source-class-Com.Tecnick.Pdf.Font.Import.TypeOne

It appears that you are using AdBlocking software. The cost of running this website is covered by advertisements. If you like it please feel free to a small amount of money to secure the future of this website.
  1: <?php
  2: /**
  3:  * TypeOne.php
  4:  *
  5:  * @since       2011-05-23
  6:  * @category    Library
  7:  * @package     PdfFont
  8:  * @author      Nicola Asuni <info@tecnick.com>
  9:  * @copyright   2011-2015 Nicola Asuni - Tecnick.com LTD
 10:  * @license     http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
 11:  * @link        https://github.com/tecnickcom/tc-lib-pdf-font
 12:  *
 13:  * This file is part of tc-lib-pdf-font software library.
 14:  */
 15: 
 16: namespace Com\Tecnick\Pdf\Font\Import;
 17: 
 18: use \Com\Tecnick\File\File;
 19: use \Com\Tecnick\Unicode\Data\Encoding;
 20: use \Com\Tecnick\Pdf\Font\Exception as FontException;
 21: 
 22: /**
 23:  * Com\Tecnick\Pdf\Font\Import\TypeOne
 24:  *
 25:  * @since       2011-05-23
 26:  * @category    Library
 27:  * @package     PdfFont
 28:  * @author      Nicola Asuni <info@tecnick.com>
 29:  * @copyright   2011-2015 Nicola Asuni - Tecnick.com LTD
 30:  * @license     http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
 31:  * @link        https://github.com/tecnickcom/tc-lib-pdf-font
 32:  */
 33: class TypeOne extends \Com\Tecnick\Pdf\Font\Import\Core
 34: {
 35:     /**
 36:      * Store font data
 37:      */
 38:     protected function storeFontData()
 39:     {
 40:         // read first segment
 41:         $dat = unpack('Cmarker/Ctype/Vsize', substr($this->font, 0, 6));
 42:         if ($dat['marker'] != 128) {
 43:             throw new FontException('Font file is not a valid binary Type1');
 44:         }
 45:         $this->fdt['size1'] = $dat['size'];
 46:         $data = substr($this->font, 6, $this->fdt['size1']);
 47:         // read second segment
 48:         $dat = unpack('Cmarker/Ctype/Vsize', substr($this->font, (6 + $this->fdt['size1']), 6));
 49:         if ($dat['marker'] != 128) {
 50:             throw new FontException('Font file is not a valid binary Type1');
 51:         }
 52:         $this->fdt['size2'] = $dat['size'];
 53:         $this->fdt['encrypted'] = substr($this->font, (12 + $this->fdt['size1']), $this->fdt['size2']);
 54:         $data .= $this->fdt['encrypted'];
 55:         // store compressed font
 56:         $this->fdt['file'] = $this->fdt['file_name'].'.z';
 57:         $file = new File();
 58:         $fpt = $file->fopenLocal($this->fdt['dir'].$this->fdt['file'], 'wb');
 59:         fwrite($fpt, gzcompress($data));
 60:         fclose($fpt);
 61:     }
 62: 
 63:     /**
 64:      * Extract Font information
 65:      */
 66:     protected function extractFontInfo()
 67:     {
 68:         if (preg_match('#/FontName[\s]*\/([^\s]*)#', $this->font, $matches) !== 1) {
 69:             preg_match('#/FullName[\s]*\(([^\)]*)#', $this->font, $matches);
 70:         }
 71:         $this->fdt['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]);
 72:         preg_match('#/FontBBox[\s]*{([^}]*)#', $this->font, $matches);
 73:         $this->fdt['bbox'] = trim($matches[1]);
 74:         $bvl = explode(' ', $this->fdt['bbox']);
 75:         $this->fdt['Ascent'] = intval($bvl[3]);
 76:         $this->fdt['Descent'] = intval($bvl[1]);
 77:         preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $this->font, $matches);
 78:         $this->fdt['italicAngle'] = intval($matches[1]);
 79:         if ($this->fdt['italicAngle'] != 0) {
 80:             $this->fdt['Flags'] |= 64;
 81:         }
 82:         preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $this->font, $matches);
 83:         $this->fdt['underlinePosition'] = intval($matches[1]);
 84:         preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $this->font, $matches);
 85:         $this->fdt['underlineThickness'] = intval($matches[1]);
 86:         preg_match('#/isFixedPitch[\s]*([^\s]*)#', $this->font, $matches);
 87:         if ($matches[1] == 'true') {
 88:             $this->fdt['Flags'] |= 1;
 89:         }
 90:         preg_match('#/Weight[\s]*\(([^\)]*)#', $this->font, $matches);
 91:         if (!empty($matches[1])) {
 92:             $this->fdt['weight'] = strtolower($matches[1]);
 93:         }
 94:         $this->fdt['weight'] = 'Book';
 95:         $this->fdt['Leading'] = 0;
 96:     }
 97: 
 98:     /**
 99:      * Extract Font information
100:      *
101:      * @return array
102:      */
103:     protected function getInternalMap()
104:     {
105:         $imap = array();
106:         if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $this->font, $fmap, PREG_SET_ORDER) > 0) {
107:             foreach ($fmap as $val) {
108:                 $imap[$val[2]] = $val[1];
109:             }
110:         }
111:         return $imap;
112:     }
113: 
114:     /**
115:      * Decrypt eexec encrypted part
116:      *
117:      * @return string
118:      */
119:     protected function getEplain()
120:     {
121:         $csr = 55665; // eexec encryption constant
122:         $cc1 = 52845;
123:         $cc2 = 22719;
124:         $elen = strlen($this->fdt['encrypted']);
125:         $eplain = '';
126:         for ($idx = 0; $idx < $elen; ++$idx) {
127:             $chr = ord($this->fdt['encrypted'][$idx]);
128:             $eplain .= chr($chr ^ ($csr >> 8));
129:             $csr = ((($chr + $csr) * $cc1 + $cc2) % 65536);
130:         }
131:         return $eplain;
132:     }
133: 
134:     /**
135:      * Extract eexec info
136:      *
137:      * @return array
138:      */
139:     protected function extractEplainInfo()
140:     {
141:         $eplain = $this->getEplain();
142:         if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) {
143:             if ($matches[1] == 'true') {
144:                 $this->fdt['Flags'] |= 0x40000;
145:             }
146:         }
147:         $this->extractStem($eplain);
148:         if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
149:             $bvl = explode(' ', $matches[1]);
150:             if (count($bvl) >= 6) {
151:                 $vl1 = intval($bvl[2]);
152:                 $vl2 = intval($bvl[4]);
153:                 $this->fdt['XHeight'] = min($vl1, $vl2);
154:                 $this->fdt['CapHeight'] = max($vl1, $vl2);
155:             }
156:         }
157:         $this->getRandomBytes($eplain);
158:         return $this->getCharstringData($eplain);
159:     }
160: 
161:     /**
162:      * Extract eexec info
163:      *
164:      * @param string $eplain Decoded eexec encrypted part
165:      *
166:      * @return array
167:      */
168:     protected function extractStem($eplain)
169:     {
170:         if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
171:             $this->fdt['StemV'] = intval($matches[1]);
172:         } elseif (($this->fdt['weight'] == 'bold') || ($this->fdt['weight'] == 'black')) {
173:             $this->fdt['StemV'] = 123;
174:         } else {
175:             $this->fdt['StemV'] = 70;
176:         }
177:         if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
178:             $this->fdt['StemH'] = intval($matches[1]);
179:         } else {
180:             $this->fdt['StemH'] = 30;
181:         }
182:         if (preg_match('#/Cap[X]?Height[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
183:             $this->fdt['CapHeight'] = intval($matches[1]);
184:         } else {
185:             $this->fdt['CapHeight'] = $this->fdt['Ascent'];
186:         }
187:         $this->fdt['XHeight'] = ($this->fdt['Ascent'] + $this->fdt['Descent']);
188:     }
189: 
190:     /**
191:      * Get the number of random bytes at the beginning of charstrings
192:      */
193:     protected function getRandomBytes($eplain)
194:     {
195:         $this->fdt['lenIV'] = 4;
196:         if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) {
197:             $this->fdt['lenIV'] = intval($matches[1]);
198:         }
199:     }
200: 
201:     /**
202:      * Get charstring data
203:      */
204:     protected function getCharstringData($eplain)
205:     {
206:         $this->fdt['enc_map'] = false;
207:         $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1));
208:         preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER);
209:         if (!empty($this->fdt['enc']) && isset(Encoding::$map[$this->fdt['enc']])) {
210:             $this->fdt['enc_map'] = Encoding::$map[$this->fdt['enc']];
211:         }
212:         return $matches;
213:     }
214: 
215:     /**
216:      * get CID
217:      *
218:      * @param array $imap
219:      * @param array $val
220:      *
221:      * @return int
222:      */
223:     protected function getCid($imap, $val)
224:     {
225:         if (isset($imap[$val[1]])) {
226:             return $imap[$val[1]];
227:         }
228:         if ($this->fdt['enc_map'] === false) {
229:             return 0;
230:         }
231:         $cid = array_search($val[1], $this->fdt['enc_map']);
232:         if ($cid === false) {
233:             return 0;
234:         }
235:         if ($cid > 1000) {
236:             return 1000;
237:         }
238:         return $cid;
239:     }
240: 
241:     /**
242:      * Decode number
243:      *
244:      * @param int   $idx
245:      * @param int   $cck
246:      * @param int   $cid
247:      * @param array $ccom
248:      * @param array $cdec
249:      * @param array $cwidths
250:      *
251:      * @return $int
252:      */
253:     protected function decodeNumber($idx, &$cck, &$cid, &$ccom, &$cdec, &$cwidths)
254:     {
255:         if ($ccom[$idx] == 255) {
256:             $sval = chr($ccom[($idx + 1)]).chr($ccom[($idx + 2)]).chr($ccom[($idx + 3)]).chr($ccom[($idx + 4)]);
257:             $vsval = unpack('li', $sval);
258:             $cdec[$cck] = $vsval['i'];
259:             return ($idx + 5);
260:         }
261:         if ($ccom[$idx] >= 251) {
262:             $cdec[$cck] = ((-($ccom[$idx] - 251) * 256) - $ccom[($idx + 1)] - 108);
263:             return ($idx + 2);
264:         }
265:         if ($ccom[$idx] >= 247) {
266:             $cdec[$cck] = ((($ccom[$idx] - 247) * 256) + $ccom[($idx + 1)] + 108);
267:             return ($idx + 2);
268:         }
269:         if ($ccom[$idx] >= 32) {
270:             $cdec[$cck] = ($ccom[$idx] - 139);
271:             return ++$idx;
272:         }
273:         $cdec[$cck] = $ccom[$idx];
274:         if (($cck > 0) && ($cdec[$cck] == 13)) {
275:             // hsbw command: update width
276:             $cwidths[$cid] = $cdec[($cck - 1)];
277:         }
278:         return ++$idx;
279:     }
280: 
281:     /**
282:      * Process Type1 font
283:      */
284:     protected function process()
285:     {
286:         $this->storeFontData();
287:         $this->extractFontInfo();
288:         $imap = $this->getInternalMap();
289:         $matches = $this->extractEplainInfo();
290:         $cwidths = array();
291:         $cc1 = 52845;
292:         $cc2 = 22719;
293:         foreach ($matches as $val) {
294:             $cid = $this->getCid($imap, $val);
295:             // decrypt charstring encrypted part
296:             $csr = 4330; // charstring encryption constant
297:             $ccd = $val[2];
298:             $clen = strlen($ccd);
299:             $ccom = array();
300:             for ($idx = 0; $idx < $clen; ++$idx) {
301:                 $chr = ord($ccd[$idx]);
302:                 $ccom[] = ($chr ^ ($csr >> 8));
303:                 $csr = ((($chr + $csr) * $cc1 + $cc2) % 65536);
304:             }
305:             // decode numbers
306:             $cdec = array();
307:             $cck = 0;
308:             $idx = $this->fdt['lenIV'];
309:             while ($idx < $clen) {
310:                 $idx = $this->decodeNumber($idx, $cck, $cid, $ccom, $cdec, $cwidths);
311:                 ++$cck;
312:             }
313:         }
314:         $this->setCharWidths($cwidths);
315:     }
316: }
317: 
 

© 2004-2017 – Nicola Asuni - Tecnick.com - All rights reserved.
about - disclaimer - privacy