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