Various phpdoc fixes
[civicrm-core.git] / CRM / Utils / PDF / Document.php
CommitLineData
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
18require_once 'TbsZip/tbszip.php';
19
8246bca4 20/**
21 * Class CRM_Utils_PDF_Document.
22 */
0aeb5a1e
CW
23class 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 ];
cf56c730
EM
71 if (CIVICRM_UF === 'UnitTests' && headers_sent()) {
72 // Streaming content will 'die' in unit tests unless ob_start()
73 // has been called.
74 throw new CRM_Core_Exception_PrematureExitException('_html2doc called', [
75 'html' => $pages,
76 'fileName' => $fileName,
77 'pageStyle' => $pageStyle,
78 ]);
79 }
0aeb5a1e
CW
80
81 $ext = pathinfo($fileName, PATHINFO_EXTENSION);
82
83 $phpWord = new \PhpOffice\PhpWord\PhpWord();
84
85 $phpWord->getDocInfo()
86 ->setCreator(CRM_Core_DAO::getFieldValue('CRM_Contact_BAO_Contact', CRM_Core_Session::getLoggedInContactID(), 'display_name'));
87
88 foreach ((array) $pages as $page => $html) {
be2fb01f 89 $section = $phpWord->addSection($pageStyle + ['breakType' => 'nextPage']);
0aeb5a1e
CW
90 \PhpOffice\PhpWord\Shared\Html::addHtml($section, $html);
91 }
ad7fdc34 92
93 self::printDoc($phpWord, $ext, $fileName);
94 }
95
96 /**
97 * @param object|string $phpWord
98 * @param string $ext
3ed92c14
TO
99 * File extension/type.
100 * Ex: docx, odt, html.
ad7fdc34 101 * @param string $fileName
3ed92c14
TO
102 * The logical filename to return to client.
103 * Ex: "HelloWorld.odt".
104 * Alternatively, a full path of a file to display. This seems sketchy.
105 * Ex: "/var/lib/data/HelloWorld.odt".
ad7fdc34 106 */
107 public static function printDoc($phpWord, $ext, $fileName) {
be2fb01f 108 $formats = [
0aeb5a1e
CW
109 'docx' => 'Word2007',
110 'odt' => 'ODText',
111 'html' => 'HTML',
112 // todo
113 'pdf' => 'PDF',
be2fb01f 114 ];
2c8a6e63 115
59d861cb 116 if (realpath($fileName)) {
117 $phpWord = \PhpOffice\PhpWord\IOFactory::load($fileName, $formats[$ext]);
ad7fdc34 118 }
119
6714d8d2
SL
120 //CRM-20015
121 \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(TRUE);
0aeb5a1e
CW
122 $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $formats[$ext]);
123
0aeb5a1e
CW
124 CRM_Utils_System::setHttpHeader('Content-Type', "application/$ext");
125 CRM_Utils_System::setHttpHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
126 $objWriter->save("php://output");
127 }
128
129 /**
a2f24340
BT
130 * @param int $value
131 * @param string $metric
132 * @return float
0aeb5a1e
CW
133 */
134 public static function toTwip($value, $metric) {
135 $point = CRM_Utils_PDF_Utils::convertMetric($value, $metric, 'pt');
136 return \PhpOffice\PhpWord\Shared\Converter::pointToTwip($point);
137 }
138
04a76231 139 /**
140 * @param array $path docx/odt file path
141 * @param string $type File type
04a76231 142 *
00eb08cf 143 * @return array
6714d8d2 144 * Return extracted content of document in HTML and document type
04a76231 145 */
00eb08cf 146 public static function docReader($path, $type) {
147 $type = array_search($type, CRM_Core_SelectValues::documentApplicationType());
04a76231 148 $fileType = ($type == 'docx') ? 'Word2007' : 'ODText';
00eb08cf 149
77a80c4e 150 $phpWord = \PhpOffice\PhpWord\IOFactory::load($path, $fileType);
9cbcd63f 151 $phpWordHTML = new \PhpOffice\PhpWord\Writer\HTML($phpWord);
04a76231 152
9cbcd63f 153 // return the html content for tokenreplacment and eventually used for document download
be2fb01f 154 return [$phpWordHTML->getWriterPart('Body')->write(), $type];
04a76231 155 }
156
157 /**
9eae09f1
CW
158 * Extract content of docx/odt file
159 *
ad7fdc34 160 * @param string $filePath Document file path
161 * @param string $docType File type of document
04a76231 162 *
9eae09f1
CW
163 * @return array
164 * [string, clsTbsZip]
04a76231 165 */
9eae09f1 166 public static function unzipDoc($filePath, $docType) {
1410eae7 167 $dataFile = self::$ooxmlMap[$docType]['dataFile'];
2c8a6e63 168
ad7fdc34 169 $zip = new clsTbsZip();
170 $zip->Open($filePath);
171 $content = $zip->FileRead($dataFile);
172
be2fb01f 173 return [$content, $zip];
ad7fdc34 174 }
175
176 /**
177 * Modify contents of docx/odt file(s) and later merged into one final document
178 *
9eae09f1 179 * @param array $contents
4f308883
TO
180 * Content of formatted/token-replaced document.
181 * List of HTML snippets.
63c8a9ba 182 * @param string $fileName
3ed92c14
TO
183 * The logical filename to return to client.
184 * Ex: "HelloWorld.odt".
9eae09f1
CW
185 * @param string $docType
186 * Document type e.g. odt/docx
187 * @param clsTbsZip $zip
188 * Zip archive
2bd6b6cf 189 * @param bool $returnFinalContent
190 * Return the content of file document as a string used in unit test
191 *
192 * @return string
ad7fdc34 193 */
63c8a9ba 194 public static function printDocuments($contents, $fileName, $docType, $zip, $returnFinalContent = FALSE) {
1410eae7 195 $dataMap = self::$ooxmlMap[$docType];
007590da 196
9eae09f1 197 $finalContent = $zip->FileRead($dataMap['dataFile']);
90a73810 198
ad7fdc34 199 // token-replaced document contents of each contact will be merged into final document
200 foreach ($contents as $key => $content) {
201 if ($key == 0) {
202 $finalContent = $content;
04a76231 203 continue;
204 }
ad7fdc34 205
206 // 1. fetch the start position of document body
207 // 2. later fetch only the body part starting from position $start
208 // 3. replace closing body tag with pageBreak
209 // 4. append the $content to the finalContent
210 $start = strpos($content, $dataMap['startTag']);
211 $content = substr($content, $start);
212 $content = str_replace($dataMap['startTag'], $dataMap['pageBreak'], $content);
213 $finalContent = str_replace($dataMap['endTag'], $content, $finalContent);
04a76231 214 }
215
2bd6b6cf 216 if ($returnFinalContent) {
217 return $finalContent;
218 }
219
007590da 220 // Replace the loaded document file content located at $filePath with $finaContent
ad7fdc34 221 $zip->FileReplace($dataMap['dataFile'], $finalContent, TBSZIP_STRING);
222
007590da 223 $zip->Flush(TBSZIP_DOWNLOAD, $fileName);
90a73810 224 }
04a76231 225
0aeb5a1e 226}