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