3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
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 |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 require_once 'TbsZip/tbszip.php';
21 * Class CRM_Utils_PDF_Document.
23 class CRM_Utils_PDF_Document
{
25 public static $ooxmlMap = [
27 'dataFile' => 'word/document.xml',
28 'startTag' => '<w:body>',
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>',
30 'endTag' => '</w:body></w:document>',
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>',
41 * Convert html to a Doc file.
44 * List of HTML snippets.
45 * @param string $fileName
46 * The logical filename to return to client.
47 * Ex: "HelloWorld.odt".
48 * @param array|int $format
50 public static function html2doc($pages, $fileName, $format = []) {
51 if (is_array($format)) {
52 // PDF Page Format parameters passed in - merge with defaults
53 $format +
= CRM_Core_BAO_PdfFormat
::getDefaultValues();
56 // PDF Page Format ID passed in
57 $format = CRM_Core_BAO_PdfFormat
::getById($format);
59 $paperSize = CRM_Core_BAO_PaperSize
::getByName($format['paper_size']);
61 $metric = CRM_Core_BAO_PdfFormat
::getValue('metric', $format);
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),
72 $ext = pathinfo($fileName, PATHINFO_EXTENSION
);
74 $phpWord = new \PhpOffice\PhpWord\
PhpWord();
76 $phpWord->getDocInfo()
77 ->setCreator(CRM_Core_DAO
::getFieldValue('CRM_Contact_BAO_Contact', CRM_Core_Session
::getLoggedInContactID(), 'display_name'));
79 foreach ((array) $pages as $page => $html) {
80 $section = $phpWord->addSection($pageStyle +
['breakType' => 'nextPage']);
81 \PhpOffice\PhpWord\Shared\Html
::addHtml($section, $html);
84 self
::printDoc($phpWord, $ext, $fileName);
88 * @param object|string $phpWord
90 * File extension/type.
91 * Ex: docx, odt, html.
92 * @param string $fileName
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".
98 public static function printDoc($phpWord, $ext, $fileName) {
100 'docx' => 'Word2007',
107 if (realpath($fileName)) {
108 $phpWord = \PhpOffice\PhpWord\IOFactory
::load($fileName, $formats[$ext]);
112 \PhpOffice\PhpWord\Settings
::setOutputEscapingEnabled(TRUE);
113 $objWriter = \PhpOffice\PhpWord\IOFactory
::createWriter($phpWord, $formats[$ext]);
115 CRM_Utils_System
::setHttpHeader('Content-Type', "application/$ext");
116 CRM_Utils_System
::setHttpHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
117 $objWriter->save("php://output");
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);
131 * @param array $path docx/odt file path
132 * @param string $type File type
135 * Return extracted content of document in HTML and document type
137 public static function docReader($path, $type) {
138 $type = array_search($type, CRM_Core_SelectValues
::documentApplicationType());
139 $fileType = ($type == 'docx') ?
'Word2007' : 'ODText';
141 $phpWord = \PhpOffice\PhpWord\IOFactory
::load($path, $fileType);
142 $phpWordHTML = new \PhpOffice\PhpWord\Writer\
HTML($phpWord);
144 // return the html content for tokenreplacment and eventually used for document download
145 return [$phpWordHTML->getWriterPart('Body')->write(), $type];
149 * Extract content of docx/odt file
151 * @param string $filePath Document file path
152 * @param string $docType File type of document
155 * [string, clsTbsZip]
157 public static function unzipDoc($filePath, $docType) {
158 $dataFile = self
::$ooxmlMap[$docType]['dataFile'];
160 $zip = new clsTbsZip();
161 $zip->Open($filePath);
162 $content = $zip->FileRead($dataFile);
164 return [$content, $zip];
168 * Modify contents of docx/odt file(s) and later merged into one final document
170 * @param array $contents
171 * Content of formatted/token-replaced document.
172 * List of HTML snippets.
173 * @param string $fileName
174 * The logical filename to return to client.
175 * Ex: "HelloWorld.odt".
176 * @param string $docType
177 * Document type e.g. odt/docx
178 * @param clsTbsZip $zip
180 * @param bool $returnFinalContent
181 * Return the content of file document as a string used in unit test
185 public static function printDocuments($contents, $fileName, $docType, $zip, $returnFinalContent = FALSE) {
186 $dataMap = self
::$ooxmlMap[$docType];
188 $finalContent = $zip->FileRead($dataMap['dataFile']);
190 // token-replaced document contents of each contact will be merged into final document
191 foreach ($contents as $key => $content) {
193 $finalContent = $content;
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);
207 if ($returnFinalContent) {
208 return $finalContent;
211 // Replace the loaded document file content located at $filePath with $finaContent
212 $zip->FileReplace($dataMap['dataFile'], $finalContent, TBSZIP_STRING
);
214 $zip->Flush(TBSZIP_DOWNLOAD
, $fileName);