1<?php 2 3require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php'; 4require_once DOKU_PLUGIN . 'odt/ODT/ODTsettings.php'; 5 6/** 7 * ODTExport: 8 * Class containing static code for exporting/generating the ODT file. 9 * 10 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 11 */ 12class ODTExport 13{ 14 /** 15 * Build the document from scratch. 16 * (code taken from old function 'document_end_scratch') 17 * 18 * @param ODTInternalParams $params 19 * @param string $meta 20 * @param string $userfields 21 * @return mixed 22 */ 23 protected static function buildFromScratch(ODTInternalParams $params, $meta=null, $userfields=null, $pagestyles=null){ 24 // add defaults 25 $settings = new ODTSettings(); 26 $params->ZIP->addData('mimetype', 'application/vnd.oasis.opendocument.text', 'mimetype'); 27 $params->ZIP->addData('meta.xml', $meta); 28 $params->ZIP->addData('settings.xml', $settings->getContent()); 29 30 $autostyles = $params->styleset->export('office:automatic-styles'); 31 $commonstyles = $params->styleset->export('office:styles'); 32 $masterstyles = $params->styleset->export('office:master-styles'); 33 34 $value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n"; 35 $value .= '<office:document-content '; 36 $value .= 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '; 37 $value .= 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" '; 38 $value .= 'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" '; 39 $value .= 'xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" '; 40 $value .= 'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" '; 41 $value .= 'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" '; 42 $value .= 'xmlns:xlink="http://www.w3.org/1999/xlink" '; 43 $value .= 'xmlns:dc="http://purl.org/dc/elements/1.1/" '; 44 $value .= 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" '; 45 $value .= 'xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" '; 46 $value .= 'xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" '; 47 $value .= 'xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" '; 48 $value .= 'xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" '; 49 $value .= 'xmlns:math="http://www.w3.org/1998/Math/MathML" '; 50 $value .= 'xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" '; 51 $value .= 'xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" '; 52 $value .= 'xmlns:ooo="http://openoffice.org/2004/office" '; 53 $value .= 'xmlns:ooow="http://openoffice.org/2004/writer" '; 54 $value .= 'xmlns:oooc="http://openoffice.org/2004/calc" '; 55 $value .= 'xmlns:dom="http://www.w3.org/2001/xml-events" '; 56 $value .= 'xmlns:xforms="http://www.w3.org/2002/xforms" '; 57 $value .= 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '; 58 $value .= 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '; 59 $value .= 'xmlns:rpt="http://openoffice.org/2005/report" '; 60 $value .= 'xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" '; 61 $value .= 'xmlns:xhtml="http://www.w3.org/1999/xhtml" '; 62 $value .= 'xmlns:grddl="http://www.w3.org/2003/g/data-view#" '; 63 $value .= 'xmlns:officeooo="http://openoffice.org/2009/office" '; 64 $value .= 'xmlns:tableooo="http://openoffice.org/2009/table" '; 65 $value .= 'xmlns:drawooo="http://openoffice.org/2010/draw" '; 66 $value .= 'xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" '; 67 $value .= 'xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" '; 68 $value .= 'xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" '; 69 $value .= 'xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" '; 70 $value .= 'xmlns:css3t="http://www.w3.org/TR/css3-text/" '; 71 $value .= 'office:version="1.2">'; 72 $value .= '<office:scripts/>'; 73 $value .= '<office:font-face-decls>'; 74 $value .= '<style:font-face style:name="OpenSymbol" svg:font-family="OpenSymbol" style:font-charset="x-symbol"/>'; 75 $value .= '<style:font-face style:name="StarSymbol1" svg:font-family="StarSymbol" style:font-charset="x-symbol"/>'; 76 $value .= '<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>'; 77 $value .= '<style:font-face style:name="Tahoma1" svg:font-family="Tahoma"/>'; 78 $value .= '<style:font-face style:name="Lucida Sans Unicode" svg:font-family="'Lucida Sans Unicode'" style:font-pitch="variable"/>'; 79 $value .= '<style:font-face style:name="Tahoma" svg:font-family="Tahoma" style:font-pitch="variable"/>'; 80 $value .= '<style:font-face style:name="Times New Roman" svg:font-family="'Times New Roman'" style:font-family-generic="roman" style:font-pitch="variable"/>'; 81 $value .= '</office:font-face-decls>'; 82 $value .= $autostyles; 83 $value .= '<office:body>'; 84 $value .= '<office:text>'; 85 $value .= '<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>'; 86 $value .= '<text:sequence-decls>'; 87 $value .= '<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>'; 88 $value .= '<text:sequence-decl text:display-outline-level="0" text:name="Table"/>'; 89 $value .= '<text:sequence-decl text:display-outline-level="0" text:name="Text"/>'; 90 $value .= '<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>'; 91 $value .= '</text:sequence-decls>'; 92 $value .= $userfields; 93 $value .= $params->content; 94 $value .= '</office:text>'; 95 $value .= '</office:body>'; 96 $value .= '</office:document-content>'; 97 98 $params->ZIP->addData('content.xml', $value); 99 100 // Edit 'styles.xml' 101 $value = io_readFile(DOKU_PLUGIN.'odt/styles.xml'); 102 103 // Insert new master page styles 104 $page = ''; 105 foreach ($pagestyles as $name => $layout_name) { 106 $page .= '<style:master-page style:name="'.$name.'" style:page-layout-name="'.$layout_name.'"/>'; 107 } 108 if ( !empty($page) ) { 109 $value = str_replace('</office:master-styles>', $page.'</office:master-styles>', $value); 110 } 111 112 // Add common styles. 113 $original = XMLUtil::getElement('office:styles', $value); 114 $value = str_replace($original, $commonstyles, $value); 115 116 // Add automatic styles. 117 $value = str_replace('<office:automatic-styles/>', $autostyles, $value); 118 $params->ZIP->addData('styles.xml', $value); 119 120 // build final manifest 121 $params->ZIP->addData('META-INF/manifest.xml', $params->manifest->getContent()); 122 } 123 124 /** 125 * Build the document from the template. 126 * (code taken from old function 'document_end_scratch') 127 * 128 * @param ODTInternalParams $params 129 * @param string $meta 130 * @param string $userfields 131 * @return mixed 132 */ 133 protected static function buildFromODTTemplate(ODTInternalParams $params, $meta=null, $userfields=null, $pagestyles=null, $template=NULL, $tempDir=NULL){ 134 // for the temp dir 135 global $ID; 136 137 if (!isset($template) || !isset($tempDir)) { 138 return; 139 } 140 141 // Temp dir 142 if (is_dir($tempDir)) { io_rmdir($tempDir,true); } 143 io_mkdir_p($tempDir); 144 145 // Extract template 146 try { 147 $ZIPextract = new \splitbrain\PHPArchive\Zip(); 148 $ZIPextract->open($template); 149 $ZIPextract->extract($tempDir); 150 $ZIPextract->open($template); 151 $templateContents = $ZIPextract->contents(); 152 } catch (\splitbrain\PHPArchive\ArchiveIOException $e) { 153 throw new Exception(' Error extracting the zip archive:'.$template.' to '.$tempDir); 154 } 155 156 // Replace metadata 157 io_saveFile($tempDir.'/meta.xml', $meta); 158 159 // Evtl. copy page format of first page to different style 160 $first_master = $params->styleset->getStyleAtIndex ('office:master-styles', 0); 161 if (isset($first_master) && 162 $first_master->getProperty('style-page-layout-name') != $params->document->getStyleName('first page')) { 163 // The master page of the template references a different page layout style 164 // then used by us for the first page. Copy the page format settings. 165 $source = $params->document->getStyle($params->document->getStyleName('first page')); 166 $dest = $params->document->getStyle($first_master->getProperty('style-page-layout-name')); 167 168 if (isset($source) && isset($dest)) { 169 $dest->setProperty('width', $source->getProperty('width')); 170 $dest->setProperty('height', $source->getProperty('height')); 171 $dest->setProperty('margin-top', $source->getProperty('margin-top')); 172 $dest->setProperty('margin-right', $source->getProperty('margin-right')); 173 $dest->setProperty('margin-bottom', $source->getProperty('margin-bottom')); 174 $dest->setProperty('margin-left', $source->getProperty('margin-left')); 175 } 176 } 177 178 $autostyles = $params->styleset->export('office:automatic-styles'); 179 $commonstyles = $params->styleset->export('office:styles'); 180 $masterstyles = $params->styleset->export('office:master-styles'); 181 182 // Prepare content 183 $missingfonts = $params->styleset->getMissingFonts($tempDir.'/styles.xml'); 184 185 // Insert content 186 $old_content = io_readFile($tempDir.'/content.xml'); 187 if (strpos($old_content, 'DOKUWIKI-ODT-INSERT') !== FALSE) { // Replace the mark 188 self::replaceInFile('/<text:p[^>]*>DOKUWIKI-ODT-INSERT<\/text:p>/', 189 $params->content, $tempDir.'/content.xml', true); 190 } else { // Append to the template 191 self::replaceInFile('</office:text>', $params->content.'</office:text>', $tempDir.'/content.xml'); 192 } 193 194 // Cut off unwanted content 195 if (strpos($old_content, 'DOKUWIKI-ODT-CUT-START') !== FALSE 196 && strpos($old_content, 'DOKUWIKI-ODT-CUT-STOP') !== FALSE) { 197 self::replaceInFile('/DOKUWIKI-ODT-CUT-START.*DOKUWIKI-ODT-CUT-STOP/', 198 '', $tempDir.'/content.xml', true); 199 } 200 201 // Insert userfields 202 if (strpos($old_content, "text:user-field-decls") === FALSE) { // no existing userfields 203 self::replaceInFile('/<office:text([^>]*)>/U', '<office:text\1>'.$userfields, $tempDir.'/content.xml', TRUE); 204 } else { 205 self::replaceInFile('</text:user-field-decls>', substr($userfields,23), $tempDir.'/content.xml'); 206 } 207 208 // Insert styles & fonts 209 $value = io_readFile($tempDir.'/content.xml'); 210 $original = XMLUtil::getElement('office:automatic-styles', $value); 211 self::replaceInFile($original, $autostyles, $tempDir.'/content.xml'); 212 213 $value = io_readFile($tempDir.'/styles.xml'); 214 $original = XMLUtil::getElement('office:automatic-styles', $value); 215 self::replaceInFile($original, $autostyles, $tempDir.'/styles.xml'); 216 217 $value = io_readFile($tempDir.'/styles.xml'); 218 $original = XMLUtil::getElement('office:styles', $value); 219 self::replaceInFile($original, $commonstyles, $tempDir.'/styles.xml'); 220 221 self::replaceInFile('</office:font-face-decls>', $missingfonts.'</office:font-face-decls>', $tempDir.'/styles.xml'); 222 223 // Insert page styles 224 $page = ''; 225 foreach ($pagestyles as $name => $layout_name) { 226 $page .= '<style:master-page style:name="'.$name.'" style:page-layout-name="'.$layout_name.'"/>'; 227 } 228 if ( !empty($page) ) { 229 self::replaceInFile('</office:master-styles>', $page.'</office:master-styles>', $tempDir.'/styles.xml'); 230 } 231 232 // Add manifest data 233 self::replaceInFile('</manifest:manifest>', $params->manifest->getExtraContent() . '</manifest:manifest>', $tempDir . '/META-INF/manifest.xml'); 234 235 // Build the Zip 236 foreach ($templateContents as $fileInfo) { 237 if (!$fileInfo->getIsdir()) { 238 $params->ZIP->addFile($tempDir.'/'.$fileInfo->getPath(), $fileInfo); 239 } 240 } 241 io_rmdir($tempDir,true); 242 } 243 244 /** 245 * Build the document from the template. 246 * (code taken from old function 'document_end_scratch') 247 * 248 * @param ODTInternalParams $params 249 * @param string $meta 250 * @param string $userfields 251 * @return mixed 252 */ 253 public static function buildZIPFile(ODTInternalParams $params, $meta=null, $userfields=null, $pagestyles=null, $template=NULL, $tempDir=NULL){ 254 if ( !isset($template) ) { 255 self::buildFromScratch($params, $meta, $userfields, $pagestyles); 256 } else { 257 self::buildFromODTTemplate($params, $meta, $userfields, $pagestyles, $template, $tempDir); 258 } 259 $params->ZIP->close(); 260 } 261 262 /** 263 * @param string $from 264 * @param string $to 265 * @param string $file 266 * @param bool $regexp 267 */ 268 protected static function replaceInFile($from, $to, $file, $regexp=FALSE) { 269 $value = io_readFile($file); 270 if ($regexp) { 271 $value = preg_replace($from, $to, $value); 272 } else { 273 $value = str_replace($from, $to, $value); 274 } 275 $file_f = fopen($file, 'w'); 276 fwrite($file_f, $value); 277 fclose($file_f); 278 } 279} 280