1<?php 2 3namespace Mpdf\Writer; 4 5use Mpdf\Strict; 6use Mpdf\Mpdf; 7use Mpdf\Form; 8 9final class PageWriter 10{ 11 12 use Strict; 13 14 /** 15 * @var \Mpdf\Mpdf 16 */ 17 private $mpdf; 18 19 /** 20 * @var \Mpdf\Form 21 */ 22 private $form; 23 24 /** 25 * @var \Mpdf\Writer\BaseWriter 26 */ 27 private $writer; 28 29 /** 30 * @var \Mpdf\Writer\MetadataWriter 31 */ 32 private $metadataWriter; 33 34 public function __construct(Mpdf $mpdf, Form $form, BaseWriter $writer, MetadataWriter $metadataWriter) 35 { 36 $this->mpdf = $mpdf; 37 $this->form = $form; 38 $this->writer = $writer; 39 $this->metadataWriter = $metadataWriter; 40 } 41 42 public function writePages() // _putpages 43 { 44 $nb = $this->mpdf->page; 45 $filter = $this->mpdf->compress ? '/Filter /FlateDecode ' : ''; 46 47 if ($this->mpdf->DefOrientation === 'P') { 48 $defwPt = $this->mpdf->fwPt; 49 $defhPt = $this->mpdf->fhPt; 50 } else { 51 $defwPt = $this->mpdf->fhPt; 52 $defhPt = $this->mpdf->fwPt; 53 } 54 55 $annotid = (3 + 2 * $nb); 56 57 // Active Forms 58 $totaladdnum = 0; 59 for ($n = 1; $n <= $nb; $n++) { 60 if (isset($this->mpdf->PageLinks[$n])) { 61 $totaladdnum += count($this->mpdf->PageLinks[$n]); 62 } 63 64 /* -- ANNOTATIONS -- */ 65 if (isset($this->mpdf->PageAnnots[$n])) { 66 foreach ($this->mpdf->PageAnnots[$n] as $k => $pl) { 67 if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) { 68 $totaladdnum += 2; 69 } else { 70 $totaladdnum++; 71 } 72 } 73 } 74 /* -- END ANNOTATIONS -- */ 75 76 /* -- FORMS -- */ 77 if (count($this->form->forms) > 0) { 78 $this->form->countPageForms($n, $totaladdnum); 79 } 80 /* -- END FORMS -- */ 81 } 82 83 /* -- FORMS -- */ 84 // Make a note in the radio button group of the obj_id it will have 85 $ctr = 0; 86 if (count($this->form->form_radio_groups)) { 87 foreach ($this->form->form_radio_groups as $name => $frg) { 88 $this->form->form_radio_groups[$name]['obj_id'] = $annotid + $totaladdnum + $ctr; 89 $ctr++; 90 } 91 } 92 /* -- END FORMS -- */ 93 94 // Select unused fonts (usually default font) 95 $unused = []; 96 foreach ($this->mpdf->fonts as $fk => $font) { 97 if (isset($font['type']) && $font['type'] === 'TTF' && !$font['used']) { 98 $unused[] = $fk; 99 } 100 } 101 102 for ($n = 1; $n <= $nb; $n++) { 103 104 $thispage = $this->mpdf->pages[$n]; 105 106 if (isset($this->mpdf->OrientationChanges[$n])) { 107 $hPt = $this->mpdf->pageDim[$n]['w'] * Mpdf::SCALE; 108 $wPt = $this->mpdf->pageDim[$n]['h'] * Mpdf::SCALE; 109 $owidthPt_LR = $this->mpdf->pageDim[$n]['outer_width_TB'] * Mpdf::SCALE; 110 $owidthPt_TB = $this->mpdf->pageDim[$n]['outer_width_LR'] * Mpdf::SCALE; 111 } else { 112 $wPt = $this->mpdf->pageDim[$n]['w'] * Mpdf::SCALE; 113 $hPt = $this->mpdf->pageDim[$n]['h'] * Mpdf::SCALE; 114 $owidthPt_LR = $this->mpdf->pageDim[$n]['outer_width_LR'] * Mpdf::SCALE; 115 $owidthPt_TB = $this->mpdf->pageDim[$n]['outer_width_TB'] * Mpdf::SCALE; 116 } 117 118 // Remove references to unused fonts (usually default font) 119 foreach ($unused as $fk) { 120 if ($this->mpdf->fonts[$fk]['sip'] || $this->mpdf->fonts[$fk]['smp']) { 121 foreach ($this->mpdf->fonts[$fk]['subsetfontids'] as $k => $fid) { 122 $thispage = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $thispage); 123 } 124 } else { 125 $thispage = preg_replace('/\s\/F' . $this->mpdf->fonts[$fk]['i'] . ' \d[\d.]* Tf\s/is', ' ', $thispage); 126 } 127 } 128 129 // Clean up repeated /GS1 gs statements 130 // For some reason using + for repetition instead of {2,20} crashes PHP Script Interpreter ??? 131 $thispage = preg_replace('/(\/GS1 gs\n){2,20}/', "/GS1 gs\n", $thispage); 132 133 $thispage = preg_replace('/(\s*___BACKGROUND___PATTERNS' . $this->mpdf->uniqstr . '\s*)/', ' ', $thispage); 134 $thispage = preg_replace('/(\s*___HEADER___MARKER' . $this->mpdf->uniqstr . '\s*)/', ' ', $thispage); 135 $thispage = preg_replace('/(\s*___PAGE___START' . $this->mpdf->uniqstr . '\s*)/', ' ', $thispage); 136 $thispage = preg_replace('/(\s*___TABLE___BACKGROUNDS' . $this->mpdf->uniqstr . '\s*)/', ' ', $thispage); 137 138 // mPDF 5.7.3 TRANSFORMS 139 while (preg_match('/(\% BTR(.*?)\% ETR)/is', $thispage, $m)) { 140 $thispage = preg_replace('/(\% BTR.*?\% ETR)/is', '', $thispage, 1) . "\n" . $m[2]; 141 } 142 143 // Page 144 $this->writer->object(); 145 $this->writer->write('<</Type /Page'); 146 $this->writer->write('/Parent 1 0 R'); 147 148 if (isset($this->mpdf->OrientationChanges[$n])) { 149 150 $this->writer->write(sprintf('/MediaBox [0 0 %.3F %.3F]', $hPt, $wPt)); 151 152 // If BleedBox is defined, it must be larger than the TrimBox, but smaller than the MediaBox 153 $bleedMargin = $this->mpdf->pageDim[$n]['bleedMargin'] * Mpdf::SCALE; 154 155 if ($bleedMargin && ($owidthPt_TB || $owidthPt_LR)) { 156 $x0 = $owidthPt_TB - $bleedMargin; 157 $y0 = $owidthPt_LR - $bleedMargin; 158 $x1 = $hPt - $owidthPt_TB + $bleedMargin; 159 $y1 = $wPt - $owidthPt_LR + $bleedMargin; 160 $this->writer->write(sprintf('/BleedBox [%.3F %.3F %.3F %.3F]', $x0, $y0, $x1, $y1)); 161 } 162 163 $this->writer->write(sprintf('/TrimBox [%.3F %.3F %.3F %.3F]', $owidthPt_TB, $owidthPt_LR, $hPt - $owidthPt_TB, $wPt - $owidthPt_LR)); 164 165 if ($this->mpdf->displayDefaultOrientation) { 166 if ($this->mpdf->DefOrientation === 'P') { 167 $this->writer->write('/Rotate 270'); 168 } else { 169 $this->writer->write('/Rotate 90'); 170 } 171 } 172 173 } else { // elseif($wPt != $defwPt || $hPt != $defhPt) { 174 175 $this->writer->write(sprintf('/MediaBox [0 0 %.3F %.3F]', $wPt, $hPt)); 176 $bleedMargin = $this->mpdf->pageDim[$n]['bleedMargin'] * Mpdf::SCALE; 177 178 if ($bleedMargin && ($owidthPt_TB || $owidthPt_LR)) { 179 $x0 = $owidthPt_LR - $bleedMargin; 180 $y0 = $owidthPt_TB - $bleedMargin; 181 $x1 = $wPt - $owidthPt_LR + $bleedMargin; 182 $y1 = $hPt - $owidthPt_TB + $bleedMargin; 183 $this->writer->write(sprintf('/BleedBox [%.3F %.3F %.3F %.3F]', $x0, $y0, $x1, $y1)); 184 } 185 186 $this->writer->write(sprintf('/TrimBox [%.3F %.3F %.3F %.3F]', $owidthPt_LR, $owidthPt_TB, $wPt - $owidthPt_LR, $hPt - $owidthPt_TB)); 187 } 188 $this->writer->write('/Resources 2 0 R'); 189 190 // Important to keep in RGB colorSpace when using transparency 191 if (!$this->mpdf->PDFA && !$this->mpdf->PDFX) { 192 if ($this->mpdf->restrictColorSpace === 3) { 193 $this->writer->write('/Group << /Type /Group /S /Transparency /CS /DeviceCMYK >> '); 194 } elseif ($this->mpdf->restrictColorSpace === 1) { 195 $this->writer->write('/Group << /Type /Group /S /Transparency /CS /DeviceGray >> '); 196 } else { 197 $this->writer->write('/Group << /Type /Group /S /Transparency /CS /DeviceRGB >> '); 198 } 199 } 200 201 $annotsnum = 0; 202 $embeddedfiles = []; // mPDF 5.7.2 /EmbeddedFiles 203 204 if (isset($this->mpdf->PageLinks[$n])) { 205 $annotsnum += count($this->mpdf->PageLinks[$n]); 206 } 207 208 if (isset($this->mpdf->PageAnnots[$n])) { 209 foreach ($this->mpdf->PageAnnots[$n] as $k => $pl) { 210 if (!empty($pl['opt']['file'])) { 211 $embeddedfiles[$annotsnum + 1] = true; 212 } // mPDF 5.7.2 /EmbeddedFiles 213 if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) { 214 $annotsnum += 2; 215 } else { 216 $annotsnum++; 217 } 218 $this->mpdf->PageAnnots[$n][$k]['pageobj'] = $this->mpdf->n; 219 } 220 } 221 222 // Active Forms 223 $formsnum = 0; 224 if (count($this->form->forms) > 0) { 225 foreach ($this->form->forms as $val) { 226 if ($val['page'] == $n) { 227 $formsnum++; 228 } 229 } 230 } 231 232 if ($annotsnum || $formsnum) { 233 234 $s = '/Annots [ '; 235 236 for ($i = 0; $i < $annotsnum; $i++) { 237 if (!isset($embeddedfiles[$i])) { 238 $s .= ($annotid + $i) . ' 0 R '; 239 } // mPDF 5.7.2 /EmbeddedFiles 240 } 241 242 $annotid += $annotsnum; 243 244 /* -- FORMS -- */ 245 if (count($this->form->forms) > 0) { 246 $this->form->addFormIds($n, $s, $annotid); 247 } 248 /* -- END FORMS -- */ 249 250 $s .= '] '; 251 $this->writer->write($s); 252 } 253 254 $this->writer->write('/Contents ' . ($this->mpdf->n + 1) . ' 0 R>>'); 255 $this->writer->write('endobj'); 256 257 // Page content 258 $this->writer->object(); 259 $p = $this->mpdf->compress ? gzcompress($thispage) : $thispage; 260 $this->writer->write('<<' . $filter . '/Length ' . strlen($p) . '>>'); 261 $this->writer->stream($p); 262 $this->writer->write('endobj'); 263 } 264 265 $this->metadataWriter->writeAnnotations(); // mPDF 5.7.2 266 267 // Pages root 268 $this->mpdf->offsets[1] = strlen($this->mpdf->buffer); 269 $this->writer->write('1 0 obj'); 270 $this->writer->write('<</Type /Pages'); 271 272 $kids = '/Kids ['; 273 274 for ($i = 0; $i < $nb; $i++) { 275 $kids .= (3 + 2 * $i) . ' 0 R '; 276 } 277 278 $this->writer->write($kids . ']'); 279 $this->writer->write('/Count ' . $nb); 280 $this->writer->write(sprintf('/MediaBox [0 0 %.3F %.3F]', $defwPt, $defhPt)); 281 $this->writer->write('>>'); 282 $this->writer->write('endobj'); 283 } 284 285} 286