1<?php 2 3namespace Mpdf\Writer; 4 5use Mpdf\Strict; 6 7use Mpdf\Mpdf; 8use Mpdf\Pdf\Protection; 9 10final class BaseWriter 11{ 12 13 use Strict; 14 15 /** 16 * @var \Mpdf\Mpdf 17 */ 18 private $mpdf; 19 20 /** 21 * @var \Mpdf\Pdf\Protection 22 */ 23 private $protection; 24 25 public function __construct(Mpdf $mpdf, Protection $protection) 26 { 27 $this->mpdf = $mpdf; 28 $this->protection = $protection; 29 } 30 31 public function write($s, $ln = true) 32 { 33 if ($this->mpdf->state === 2) { 34 $this->endPage($s, $ln); 35 } else { 36 $this->mpdf->buffer .= $s . ($ln ? "\n" : ''); 37 } 38 } 39 40 public function string($s) 41 { 42 if ($this->mpdf->encrypted) { 43 $s = $this->protection->rc4($this->protection->objectKey($this->mpdf->currentObjectNumber), $s); 44 } 45 46 return '(' . $this->escape($s) . ')'; 47 } 48 49 public function object($obj_id = false, $onlynewobj = false) 50 { 51 if (!$obj_id) { 52 $obj_id = ++$this->mpdf->n; 53 } 54 55 // Begin a new object 56 if (!$onlynewobj) { 57 $this->mpdf->offsets[$obj_id] = strlen($this->mpdf->buffer); 58 $this->write($obj_id . ' 0 obj'); 59 $this->mpdf->currentObjectNumber = $obj_id; // for later use with encryption 60 } 61 } 62 63 public function stream($s) 64 { 65 if ($this->mpdf->encrypted) { 66 $s = $this->protection->rc4($this->protection->objectKey($this->mpdf->currentObjectNumber), $s); 67 } 68 69 $this->write('stream'); 70 $this->write($s); 71 $this->write('endstream'); 72 } 73 74 public function utf16BigEndianTextString($s) // _UTF16BEtextstring 75 { 76 $s = $this->utf8ToUtf16BigEndian($s, true); 77 if ($this->mpdf->encrypted) { 78 $s = $this->protection->rc4($this->protection->objectKey($this->mpdf->currentObjectNumber), $s); 79 } 80 81 return '(' . $this->escape($s) . ')'; 82 } 83 84 // Converts UTF-8 strings to UTF16-BE. 85 public function utf8ToUtf16BigEndian($str, $setbom = true) // UTF8ToUTF16BE 86 { 87 if ($this->mpdf->checkSIP && preg_match("/([\x{20000}-\x{2FFFF}])/u", $str)) { 88 if (!in_array($this->mpdf->currentfontfamily, ['gb', 'big5', 'sjis', 'uhc', 'gbB', 'big5B', 'sjisB', 'uhcB', 'gbI', 'big5I', 'sjisI', 'uhcI', 89 'gbBI', 'big5BI', 'sjisBI', 'uhcBI'])) { 90 $str = preg_replace("/[\x{20000}-\x{2FFFF}]/u", chr(0), $str); 91 } 92 } 93 if ($this->mpdf->checkSMP && preg_match("/([\x{10000}-\x{1FFFF}])/u", $str)) { 94 $str = preg_replace("/[\x{10000}-\x{1FFFF}]/u", chr(0), $str); 95 } 96 97 $outstr = ''; // string to be returned 98 if ($setbom) { 99 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) 100 } 101 102 $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8'); 103 104 return $outstr; 105 } 106 107 public function escape($s) // _escape 108 { 109 return strtr($s, [')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r']); 110 } 111 112 public function escapeSlashes($s) // _escapeName 113 { 114 return strtr($s, ['/' => '#2F']); 115 } 116 117 /** 118 * Un-escapes a PDF string 119 * 120 * @param string $s 121 * @return string 122 */ 123 public function unescape($s) 124 { 125 $out = ''; 126 for ($count = 0, $n = strlen($s); $count < $n; $count++) { 127 if ($count === $n - 1 || $s[$count] !== '\\') { 128 $out .= $s[$count]; 129 } else { 130 switch ($s[++$count]) { 131 case ')': 132 case '(': 133 case '\\': 134 $out .= $s[$count]; 135 break; 136 case 'f': 137 $out .= chr(0x0C); 138 break; 139 case 'b': 140 $out .= chr(0x08); 141 break; 142 case 't': 143 $out .= chr(0x09); 144 break; 145 case 'r': 146 $out .= chr(0x0D); 147 break; 148 case 'n': 149 $out .= chr(0x0A); 150 break; 151 case "\r": 152 if ($count !== $n - 1 && $s[$count + 1] === "\n") { 153 $count++; 154 } 155 break; 156 case "\n": 157 break; 158 default: 159 // Octal-Values 160 $ord = ord($s[$count]); 161 if ($ord >= ord('0') && $ord <= ord('9')) { 162 $oct = ''. $s[$count]; 163 $ord = ord($s[$count + 1]); 164 if ($ord >= ord('0') && $ord <= ord('9')) { 165 $oct .= $s[++$count]; 166 $ord = ord($s[$count + 1]); 167 if ($ord >= ord('0') && $ord <= ord('9')) { 168 $oct .= $s[++$count]; 169 } 170 } 171 $out .= chr(octdec($oct)); 172 } else { 173 $out .= $s[$count]; 174 } 175 } 176 } 177 } 178 179 return $out; 180 } 181 182 private function endPage($s, $ln) 183 { 184 if ($this->mpdf->bufferoutput) { 185 186 $this->mpdf->headerbuffer.= $s . "\n"; 187 188 } elseif ($this->mpdf->ColActive && !$this->mpdf->processingHeader && !$this->mpdf->processingFooter) { 189 190 // Captures everything in buffer for columns; Almost everything is sent from fn. Cell() except: 191 // Images sent from Image() or 192 // later sent as write($textto) in printbuffer 193 // Line() 194 195 if (preg_match('/q \d+\.\d\d+ 0 0 (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ cm \/(I|FO)\d+ Do Q/', $s, $m)) { // Image data 196 197 $h = ($m[1] / Mpdf::SCALE); 198 // Update/overwrite the lowest bottom of printing y value for a column 199 $this->mpdf->ColDetails[$this->mpdf->CurrCol]['bottom_margin'] = $this->mpdf->y + $h; 200 201 } elseif ($this->mpdf->tableLevel > 0 && preg_match('/\d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ ([\-]{0,1}\d+\.\d\d+) re/', $s, $m)) { // Rect in table 202 203 $h = ($m[1] / Mpdf::SCALE); 204 // Update/overwrite the lowest bottom of printing y value for a column 205 $this->mpdf->ColDetails[$this->mpdf->CurrCol]['bottom_margin'] = max($this->mpdf->ColDetails[$this->mpdf->CurrCol]['bottom_margin'], $this->mpdf->y + $h); 206 207 } elseif (isset($this->mpdf->ColDetails[$this->mpdf->CurrCol]['bottom_margin'])) { 208 209 $h = $this->mpdf->ColDetails[$this->mpdf->CurrCol]['bottom_margin'] - $this->mpdf->y; 210 211 } else { 212 213 $h = 0; 214 215 } 216 217 if ($h < 0) { 218 $h = -$h; 219 } 220 221 $this->mpdf->columnbuffer[] = [ 222 's' => $s, // Text string to output 223 'col' => $this->mpdf->CurrCol, // Column when printed 224 'x' => $this->mpdf->x, // x when printed 225 'y' => $this->mpdf->y, // this->y when printed (after column break) 226 'h' => $h // actual y at bottom when printed = y+h 227 ]; 228 229 } elseif ($this->mpdf->table_rotate && !$this->mpdf->processingHeader && !$this->mpdf->processingFooter) { 230 231 // Captures eveything in buffer for rotated tables; 232 $this->mpdf->tablebuffer .= $s . "\n"; 233 234 } elseif ($this->mpdf->kwt && !$this->mpdf->processingHeader && !$this->mpdf->processingFooter) { 235 236 // Captures eveything in buffer for keep-with-table (h1-6); 237 $this->mpdf->kwt_buffer[] = [ 238 's' => $s, // Text string to output 239 'x' => $this->mpdf->x, // x when printed 240 'y' => $this->mpdf->y, // y when printed 241 ]; 242 243 } elseif ($this->mpdf->keep_block_together && !$this->mpdf->processingHeader && !$this->mpdf->processingFooter) { 244 // do nothing 245 } else { 246 $this->mpdf->pages[$this->mpdf->page] .= $s . ($ln ? "\n" : ''); 247 } 248 } 249 250} 251