source-class-Com.Tecnick.Pdf.Font.Subset

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:  * Subset.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;
 17: 
 18: use \Com\Tecnick\File\Byte;
 19: use \Com\Tecnick\Pdf\Font\Import\TrueType;
 20: use \Com\Tecnick\Pdf\Font\Exception as FontException;
 21: 
 22: /**
 23:  * Com\Tecnick\Pdf\Font\Subset
 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 Subset
 34: {
 35:     /**
 36:      * Content of the input font file
 37:      *
 38:      * @var string
 39:      */
 40:     protected $font = '';
 41: 
 42:     /**
 43:      * Object used to read font bytes
 44:      *
 45:      * @var \Com\Tecnick\File\Byte
 46:      */
 47:     protected $fbyte;
 48: 
 49:     /**
 50:      * Extracted font metrics
 51:      *
 52:      * @var array
 53:      */
 54:     protected $fdt = array();
 55: 
 56:     /**
 57:      * Array containing subset glyphs indexes of chars from cmap table
 58:      *
 59:      * @var array
 60:      */
 61:     protected $subglyphs = array();
 62: 
 63:     /**
 64:      * Subset font
 65:      *
 66:      * @var string
 67:      */
 68:     protected $subfont = '';
 69: 
 70:     /**
 71:      * Process TrueType font
 72:      *
 73:      * @param string $font     Content of the input font file
 74:      * @param array  $fdt      Extracted font metrics
 75:      * @param array  $subchars Array containing subset chars
 76:      *
 77:      * @throws FontException in case of error
 78:      */
 79:     public function __construct($font, $fdt, $subchars = array())
 80:     {
 81:         $this->fbyte = new Byte($font);
 82:         $processor = new TrueType($font, $fdt, $this->fbyte, $subchars);
 83:         $this->fdt = $processor->getFontMetrics();
 84:         $this->subglyphs = $processor->getSubGlyphs();
 85:         $this->addCompositeGlyphs();
 86:         $this->addProcessedTables();
 87:         $this->removeUnusedTables();
 88:         $this->buildSubsetFont();
 89:     }
 90: 
 91:     /**
 92:      * Get all the extracted font metrics
 93:      *
 94:      * @return string
 95:      */
 96:     public function getSubsetFont()
 97:     {
 98:         return $this->subfont;
 99:     }
100: 
101:     /**
102:      * Returs the checksum of a TTF table.
103:      *
104:      * @param string $table  Table to check
105:      * @param int    $length Length of table in bytes
106:      *
107:      * @return int checksum
108:      */
109:     protected function getTableChecksum($table, $length)
110:     {
111:         $sum = 0;
112:         $tlen = ($length / 4);
113:         $offset = 0;
114:         for ($idx = 0; $idx < $tlen; ++$idx) {
115:             $val = unpack('Ni', substr($table, $offset, 4));
116:             $sum += $val['i'];
117:             $offset += 4;
118:         }
119:         $sum = unpack('Ni', pack('N', $sum));
120:         return $sum['i'];
121:     }
122: 
123:     /**
124:      * Add composite glyphs
125:      */
126:     protected function addCompositeGlyphs()
127:     {
128:         $new_sga = $this->subglyphs;
129:         while (!empty($new_sga)) {
130:             $sga = array_keys($new_sga);
131:             $new_sga = array();
132:             foreach ($sga as $key) {
133:                 $new_sga = $this->findCompositeGlyphs($new_sga, $key);
134:             }
135:             $this->subglyphs = array_merge($this->subglyphs, $new_sga);
136:         }
137:         // sort glyphs by key (and remove duplicates)
138:         ksort($this->subglyphs);
139:     }
140: 
141:     /**
142:      * Add composite glyphs
143:      *
144:      * @param array $new_sga
145:      * @param int   $key
146:      *
147:      * @return array
148:      */
149:     protected function findCompositeGlyphs($new_sga, $key)
150:     {
151:         if (isset($this->fdt['indexToLoc'][$key])) {
152:             $this->offset = ($this->fdt['table']['glyf']['offset'] + $this->fdt['indexToLoc'][$key]);
153:             $numberOfContours = $this->fbyte->getShort($this->offset);
154:             $this->offset += 2;
155:             if ($numberOfContours < 0) { // composite glyph
156:                 $this->offset += 8; // skip xMin, yMin, xMax, yMax
157:                 do {
158:                     $flags = $this->fbyte->getUShort($this->offset);
159:                     $this->offset += 2;
160:                     $glyphIndex = $this->fbyte->getUShort($this->offset);
161:                     $this->offset += 2;
162:                     if (!isset($this->subglyphs[$glyphIndex])) {
163:                         // add missing glyphs
164:                         $new_sga[$glyphIndex] = true;
165:                     }
166:                     // skip some bytes by case
167:                     if ($flags & 1) {
168:                         $this->offset += 4;
169:                     } else {
170:                         $this->offset += 2;
171:                     }
172:                     if ($flags & 8) {
173:                         $this->offset += 2;
174:                     } elseif ($flags & 64) {
175:                         $this->offset += 4;
176:                     } elseif ($flags & 128) {
177:                         $this->offset += 8;
178:                     }
179:                 } while ($flags & 32);
180:             }
181:         }
182:         return $new_sga;
183:     }
184: 
185:     /**
186:      * Remove unused tables
187:      */
188:     protected function removeUnusedTables()
189:     {
190:         // array of table names to preserve (loca and glyf tables will be added later)
191:         // the cmap table is not needed and shall not be present,
192:         // since the mapping from character codes to glyph descriptions is provided separately
193:         $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep', 'glyf', 'loca');
194:         // get the tables to preserve
195:         $this->offset = 12;
196:         $tabname = array_keys($this->fdt['table']);
197:         foreach ($tabname as $tag) {
198:             if (in_array($tag, $table_names)) {
199:                 $this->fdt['table'][$tag]['data'] = substr(
200:                     $this->font,
201:                     $this->fdt['table'][$tag]['offset'],
202:                     $this->fdt['table'][$tag]['length']
203:                 );
204:                 if ($tag == 'head') {
205:                     // set the checkSumAdjustment to 0
206:                     $this->fdt['table'][$tag]['data'] = substr($this->fdt['table'][$tag]['data'], 0, 8)
207:                         ."\x0\x0\x0\x0".substr($this->fdt['table'][$tag]['data'], 12);
208:                 }
209:                 $pad = 4 - ($this->fdt['table'][$tag]['length'] % 4);
210:                 if ($pad != 4) {
211:                     // the length of a table must be a multiple of four bytes
212:                     $this->fdt['table'][$tag]['length'] += $pad;
213:                     $this->fdt['table'][$tag]['data'] .= str_repeat("\x0", $pad);
214:                 }
215:                 $this->fdt['table'][$tag]['offset'] = $this->offset;
216:                 $this->offset += $this->fdt['table'][$tag]['length'];
217:                 // check sum is not changed
218:             } else {
219:                 // remove the table
220:                 unset($this->fdt['table'][$tag]);
221:             }
222:         }
223:     }
224: 
225:     /**
226:      * Add glyf and loca tables
227:      */
228:     protected function addProcessedTables()
229:     {
230:         // build new glyf and loca tables
231:         $glyf = '';
232:         $loca = '';
233:         $this->offset = 0;
234:         $glyf_offset = $this->fdt['table']['glyf']['offset'];
235:         for ($i = 0; $i < $this->fdt['tot_num_glyphs']; ++$i) {
236:             if (isset($this->subglyphs[$i])
237:                 && isset($this->fdt['indexToLoc'][$i])
238:                 && isset($this->fdt['indexToLoc'][($i + 1)])
239:             ) {
240:                 $length = ($this->fdt['indexToLoc'][($i + 1)] - $this->fdt['indexToLoc'][$i]);
241:                 $glyf .= substr($this->font, ($glyf_offset + $this->fdt['indexToLoc'][$i]), $length);
242:             } else {
243:                 $length = 0;
244:             }
245:             if ($this->fdt['short_offset']) {
246:                 $loca .= pack('n', floor($this->offset / 2));
247:             } else {
248:                 $loca .= pack('N', $this->offset);
249:             }
250:             $this->offset += $length;
251:         }
252:         // add loca
253:         $this->fdt['table']['loca']['data'] = $loca;
254:         $this->fdt['table']['loca']['length'] = strlen($loca);
255:         $pad = 4 - ($this->fdt['table']['loca']['length'] % 4);
256:         if ($pad != 4) {
257:             // the length of a table must be a multiple of four bytes
258:             $this->fdt['table']['loca']['length'] += $pad;
259:             $this->fdt['table']['loca']['data'] .= str_repeat("\x0", $pad);
260:         }
261:         $this->fdt['table']['loca']['offset'] = $this->offset;
262:         $this->fdt['table']['loca']['checkSum'] = $this->getTableChecksum(
263:             $this->fdt['table']['loca']['data'],
264:             $this->fdt['table']['loca']['length']
265:         );
266:         $this->offset += $this->fdt['table']['loca']['length'];
267:         // add glyf
268:         $this->fdt['table']['glyf']['data'] = $glyf;
269:         $this->fdt['table']['glyf']['length'] = strlen($glyf);
270:         $pad = 4 - ($this->fdt['table']['glyf']['length'] % 4);
271:         if ($pad != 4) {
272:             // the length of a table must be a multiple of four bytes
273:             $this->fdt['table']['glyf']['length'] += $pad;
274:             $this->fdt['table']['glyf']['data'] .= str_repeat("\x0", $pad);
275:         }
276:         $this->fdt['table']['glyf']['offset'] = $this->offset;
277:         $this->fdt['table']['glyf']['checkSum'] = $this->getTableChecksum(
278:             $this->fdt['table']['glyf']['data'],
279:             $this->fdt['table']['glyf']['length']
280:         );
281:     }
282: 
283:     /**
284:      * build new subset font
285:      */
286:     protected function buildSubsetFont()
287:     {
288:         $this->subfont = '';
289:         $this->subfont .= pack('N', 0x10000); // sfnt version
290:         $numTables = count($this->fdt['table']);
291:         $this->subfont .= pack('n', $numTables); // numTables
292:         $entrySelector = floor(log($numTables, 2));
293:         $searchRange = pow(2, $entrySelector) * 16;
294:         $rangeShift = ($numTables * 16) - $searchRange;
295:         $this->subfont .= pack('n', $searchRange); // searchRange
296:         $this->subfont .= pack('n', $entrySelector); // entrySelector
297:         $this->subfont .= pack('n', $rangeShift); // rangeShift
298:         $this->offset = ($numTables * 16);
299:         foreach ($this->fdt['table'] as $tag => $data) {
300:             $this->subfont .= $tag; // tag
301:             $this->subfont .= pack('N', $data['checkSum']); // checkSum
302:             $this->subfont .= pack('N', ($data['offset'] + $this->offset)); // offset
303:             $this->subfont .= pack('N', $data['length']); // length
304:         }
305:         foreach ($this->fdt['table'] as $data) {
306:             $this->subfont .= $data['data'];
307:         }
308:         // set checkSumAdjustment on head table
309:         $checkSumAdjustment = (0xB1B0AFBA - $this->getTableChecksum($this->subfont, strlen($this->subfont)));
310:         $this->subfont = substr($this->subfont, 0, $this->fdt['table']['head']['offset'] + 8)
311:             .pack('N', $checkSumAdjustment)
312:             .substr($this->subfont, $this->fdt['table']['head']['offset'] + 12);
313:     }
314: }
315: 
 

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