Commit | Line | Data |
---|---|---|
0aeb5a1e CW |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
fee14197 | 4 | | CiviCRM version 5 | |
0aeb5a1e | 5 | +--------------------------------------------------------------------+ |
8c9251b3 | 6 | | Copyright CiviCRM LLC (c) 2004-2018 | |
0aeb5a1e CW |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the GNU Affero General Public License | | |
12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the GNU Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the GNU Affero General Public | | |
20 | | License and the CiviCRM Licensing Exception along | | |
21 | | with this program; if not, contact CiviCRM LLC | | |
22 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
23 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
25 | +--------------------------------------------------------------------+ | |
26 | */ | |
27 | ||
28 | /** | |
29 | * | |
30 | * @package CRM | |
8c9251b3 | 31 | * @copyright CiviCRM LLC (c) 2004-2018 |
0aeb5a1e | 32 | */ |
ad7fdc34 | 33 | |
34 | require_once 'TbsZip/tbszip.php'; | |
35 | ||
8246bca4 | 36 | /** |
37 | * Class CRM_Utils_PDF_Document. | |
38 | */ | |
0aeb5a1e CW |
39 | class CRM_Utils_PDF_Document { |
40 | ||
9eae09f1 CW |
41 | public static $ooxmlMap = array( |
42 | 'docx' => array( | |
43 | 'dataFile' => 'word/document.xml', | |
44 | 'startTag' => '<w:body>', | |
2bd6b6cf | 45 | 'pageBreak' => '<w:p><w:pPr><w:pStyle w:val="Normal"/><w:rPr></w:rPr></w:pPr><w:r><w:rPr></w:rPr></w:r><w:r><w:br w:type="page"/></w:r></w:p>', |
9eae09f1 CW |
46 | 'endTag' => '</w:body></w:document>', |
47 | ), | |
48 | 'odt' => array( | |
49 | 'dataFile' => 'content.xml', | |
50 | 'startTag' => '<office:body>', | |
51 | 'pageBreak' => '<text:p text:style-name="Standard"></text:p>', | |
52 | 'endTag' => '</office:body></office:document-content>', | |
53 | ), | |
54 | ); | |
55 | ||
0aeb5a1e | 56 | /** |
8246bca4 | 57 | * Convert html to a Doc file. |
58 | * | |
0aeb5a1e | 59 | * @param array $pages |
4f308883 | 60 | * List of HTML snippets. |
0aeb5a1e | 61 | * @param string $fileName |
3ed92c14 TO |
62 | * The logical filename to return to client. |
63 | * Ex: "HelloWorld.odt". | |
0aeb5a1e CW |
64 | * @param array|int $format |
65 | */ | |
66 | public static function html2doc($pages, $fileName, $format = array()) { | |
67 | if (is_array($format)) { | |
68 | // PDF Page Format parameters passed in - merge with defaults | |
69 | $format += CRM_Core_BAO_PdfFormat::getDefaultValues(); | |
70 | } | |
71 | else { | |
72 | // PDF Page Format ID passed in | |
73 | $format = CRM_Core_BAO_PdfFormat::getById($format); | |
74 | } | |
75 | $paperSize = CRM_Core_BAO_PaperSize::getByName($format['paper_size']); | |
76 | ||
77 | $metric = CRM_Core_BAO_PdfFormat::getValue('metric', $format); | |
78 | $pageStyle = array( | |
79 | 'orientation' => CRM_Core_BAO_PdfFormat::getValue('orientation', $format), | |
80 | 'pageSizeW' => self::toTwip($paperSize['width'], $paperSize['metric']), | |
81 | 'pageSizeH' => self::toTwip($paperSize['height'], $paperSize['metric']), | |
82 | 'marginTop' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_top', $format), $metric), | |
83 | 'marginRight' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_right', $format), $metric), | |
84 | 'marginBottom' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_bottom', $format), $metric), | |
85 | 'marginLeft' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_left', $format), $metric), | |
86 | ); | |
87 | ||
88 | $ext = pathinfo($fileName, PATHINFO_EXTENSION); | |
89 | ||
90 | $phpWord = new \PhpOffice\PhpWord\PhpWord(); | |
91 | ||
92 | $phpWord->getDocInfo() | |
93 | ->setCreator(CRM_Core_DAO::getFieldValue('CRM_Contact_BAO_Contact', CRM_Core_Session::getLoggedInContactID(), 'display_name')); | |
94 | ||
95 | foreach ((array) $pages as $page => $html) { | |
96 | $section = $phpWord->addSection($pageStyle + array('breakType' => 'nextPage')); | |
97 | \PhpOffice\PhpWord\Shared\Html::addHtml($section, $html); | |
98 | } | |
ad7fdc34 | 99 | |
100 | self::printDoc($phpWord, $ext, $fileName); | |
101 | } | |
102 | ||
103 | /** | |
104 | * @param object|string $phpWord | |
105 | * @param string $ext | |
3ed92c14 TO |
106 | * File extension/type. |
107 | * Ex: docx, odt, html. | |
ad7fdc34 | 108 | * @param string $fileName |
3ed92c14 TO |
109 | * The logical filename to return to client. |
110 | * Ex: "HelloWorld.odt". | |
111 | * Alternatively, a full path of a file to display. This seems sketchy. | |
112 | * Ex: "/var/lib/data/HelloWorld.odt". | |
ad7fdc34 | 113 | */ |
114 | public static function printDoc($phpWord, $ext, $fileName) { | |
0aeb5a1e CW |
115 | $formats = array( |
116 | 'docx' => 'Word2007', | |
117 | 'odt' => 'ODText', | |
118 | 'html' => 'HTML', | |
119 | // todo | |
120 | 'pdf' => 'PDF', | |
121 | ); | |
2c8a6e63 | 122 | |
59d861cb | 123 | if (realpath($fileName)) { |
124 | $phpWord = \PhpOffice\PhpWord\IOFactory::load($fileName, $formats[$ext]); | |
ad7fdc34 | 125 | } |
126 | ||
f0ba77f4 | 127 | \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(TRUE); //CRM-20015 |
0aeb5a1e CW |
128 | $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $formats[$ext]); |
129 | ||
0aeb5a1e CW |
130 | CRM_Utils_System::setHttpHeader('Content-Type', "application/$ext"); |
131 | CRM_Utils_System::setHttpHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"'); | |
132 | $objWriter->save("php://output"); | |
133 | } | |
134 | ||
135 | /** | |
136 | * @param $value | |
137 | * @param $metric | |
138 | * @return int | |
139 | */ | |
140 | public static function toTwip($value, $metric) { | |
141 | $point = CRM_Utils_PDF_Utils::convertMetric($value, $metric, 'pt'); | |
142 | return \PhpOffice\PhpWord\Shared\Converter::pointToTwip($point); | |
143 | } | |
144 | ||
04a76231 | 145 | /** |
146 | * @param array $path docx/odt file path | |
147 | * @param string $type File type | |
04a76231 | 148 | * |
00eb08cf | 149 | * @return array |
150 | * Return extracted content of document in HTML and document type | |
04a76231 | 151 | */ |
00eb08cf | 152 | public static function docReader($path, $type) { |
153 | $type = array_search($type, CRM_Core_SelectValues::documentApplicationType()); | |
04a76231 | 154 | $fileType = ($type == 'docx') ? 'Word2007' : 'ODText'; |
00eb08cf | 155 | |
77a80c4e | 156 | $phpWord = \PhpOffice\PhpWord\IOFactory::load($path, $fileType); |
9cbcd63f | 157 | $phpWordHTML = new \PhpOffice\PhpWord\Writer\HTML($phpWord); |
04a76231 | 158 | |
9cbcd63f | 159 | // return the html content for tokenreplacment and eventually used for document download |
00eb08cf | 160 | return array($phpWordHTML->getWriterPart('Body')->write(), $type); |
04a76231 | 161 | } |
162 | ||
163 | /** | |
9eae09f1 CW |
164 | * Extract content of docx/odt file |
165 | * | |
ad7fdc34 | 166 | * @param string $filePath Document file path |
167 | * @param string $docType File type of document | |
04a76231 | 168 | * |
9eae09f1 CW |
169 | * @return array |
170 | * [string, clsTbsZip] | |
04a76231 | 171 | */ |
9eae09f1 | 172 | public static function unzipDoc($filePath, $docType) { |
1410eae7 | 173 | $dataFile = self::$ooxmlMap[$docType]['dataFile']; |
2c8a6e63 | 174 | |
ad7fdc34 | 175 | $zip = new clsTbsZip(); |
176 | $zip->Open($filePath); | |
177 | $content = $zip->FileRead($dataFile); | |
178 | ||
9eae09f1 | 179 | return array($content, $zip); |
ad7fdc34 | 180 | } |
181 | ||
182 | /** | |
183 | * Modify contents of docx/odt file(s) and later merged into one final document | |
184 | * | |
9eae09f1 | 185 | * @param array $contents |
4f308883 TO |
186 | * Content of formatted/token-replaced document. |
187 | * List of HTML snippets. | |
63c8a9ba | 188 | * @param string $fileName |
3ed92c14 TO |
189 | * The logical filename to return to client. |
190 | * Ex: "HelloWorld.odt". | |
9eae09f1 CW |
191 | * @param string $docType |
192 | * Document type e.g. odt/docx | |
193 | * @param clsTbsZip $zip | |
194 | * Zip archive | |
2bd6b6cf | 195 | * @param bool $returnFinalContent |
196 | * Return the content of file document as a string used in unit test | |
197 | * | |
198 | * @return string | |
ad7fdc34 | 199 | */ |
63c8a9ba | 200 | public static function printDocuments($contents, $fileName, $docType, $zip, $returnFinalContent = FALSE) { |
1410eae7 | 201 | $dataMap = self::$ooxmlMap[$docType]; |
007590da | 202 | |
9eae09f1 | 203 | $finalContent = $zip->FileRead($dataMap['dataFile']); |
90a73810 | 204 | |
ad7fdc34 | 205 | // token-replaced document contents of each contact will be merged into final document |
206 | foreach ($contents as $key => $content) { | |
207 | if ($key == 0) { | |
208 | $finalContent = $content; | |
04a76231 | 209 | continue; |
210 | } | |
ad7fdc34 | 211 | |
212 | // 1. fetch the start position of document body | |
213 | // 2. later fetch only the body part starting from position $start | |
214 | // 3. replace closing body tag with pageBreak | |
215 | // 4. append the $content to the finalContent | |
216 | $start = strpos($content, $dataMap['startTag']); | |
217 | $content = substr($content, $start); | |
218 | $content = str_replace($dataMap['startTag'], $dataMap['pageBreak'], $content); | |
219 | $finalContent = str_replace($dataMap['endTag'], $content, $finalContent); | |
04a76231 | 220 | } |
221 | ||
2bd6b6cf | 222 | if ($returnFinalContent) { |
223 | return $finalContent; | |
224 | } | |
225 | ||
007590da | 226 | // Replace the loaded document file content located at $filePath with $finaContent |
ad7fdc34 | 227 | $zip->FileReplace($dataMap['dataFile'], $finalContent, TBSZIP_STRING); |
228 | ||
007590da | 229 | $zip->Flush(TBSZIP_DOWNLOAD, $fileName); |
90a73810 | 230 | } |
04a76231 | 231 | |
0aeb5a1e | 232 | } |