Skip to content

Commit d727d41

Browse files
committed
fix font subset indexing issues
1 parent 3086153 commit d727d41

8 files changed

Lines changed: 334 additions & 13 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.9.0
1+
2.10.0

src/OutFont.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ protected function uniToCid(array &$font, int $cidoffset): void
154154
} // else unknown character
155155
}
156156

157-
$font['cw'] = \array_merge($font['cw'], $chw);
157+
foreach ($chw as $cid => $width) {
158+
$font['cw'][$cid] = $width;
159+
}
158160
}
159161

160162
/**

src/Output.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,11 @@ protected function getEncodingDiffs(): string
172172
if (empty($this->subchars[$file_key])) {
173173
$this->subchars[$file_key] = $font['subsetchars'];
174174
} else {
175-
$this->subchars[$file_key] = \array_merge($this->subchars[$file_key], $font['subsetchars']);
175+
foreach ($font['subsetchars'] as $cid => $enabled) {
176+
if ($enabled) {
177+
$this->subchars[$file_key][(int) $cid] = true;
178+
}
179+
}
176180
}
177181
}
178182
}

src/Stack.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,9 +470,15 @@ public function getOrdArrDims(array $uniarr): array
470470
$totspacewidth = 0; // total space width
471471
$words = 0; // total number of words
472472
$fact = ($this->stack[$this->index]['spacing'] * $this->stack[$this->index]['stretching']);
473+
$fkey = $this->stack[$this->index]['key'];
474+
$subset = !empty($this->font[$fkey]['subset']);
473475
$uniarr[] = 8203; // add null at the end to ensure that the last word is processed
474476
$split = [];
475477
foreach ($uniarr as $idx => $ord) {
478+
if ($subset) {
479+
$this->addSubsetChar($fkey, $ord);
480+
}
481+
476482
$unitype = UnicodeType::UNI[$ord];
477483
$chrwidth = $this->getCharWidth($ord);
478484
// 'B' Paragraph Separator

src/Subset.php

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,11 @@ protected function addCompositeGlyphs(): void
284284
$new_sga = $this->findCompositeGlyphs($new_sga, $key);
285285
}
286286

287-
$this->subglyphs = [...$this->subglyphs, ...$new_sga];
287+
foreach ($new_sga as $gid => $enabled) {
288+
if ($enabled) {
289+
$this->subglyphs[$gid] = true;
290+
}
291+
}
288292
}
289293

290294
// sort glyphs by key
@@ -424,12 +428,9 @@ protected function addProcessedTables(): void
424428
$this->offset = 0;
425429
$glyf_offset = $this->fdt['table']['glyf']['offset'];
426430
for ($i = 0; $i < $this->fdt['tot_num_glyphs']; ++$i) {
427-
if (
428-
isset($this->subglyphs[$i])
429-
&& isset($this->fdt['indexToLoc'][$i])
430-
&& isset($this->fdt['indexToLoc'][($i + 1)])
431-
) {
432-
$length = ($this->fdt['indexToLoc'][($i + 1)] - $this->fdt['indexToLoc'][$i]);
431+
$nextidx = $this->getNextLocaIndex($i + 1);
432+
if (isset($this->subglyphs[$i]) && isset($this->fdt['indexToLoc'][$i]) && ($nextidx !== null)) {
433+
$length = ($this->fdt['indexToLoc'][$nextidx] - $this->fdt['indexToLoc'][$i]);
433434
$glyf .= \substr($this->font, ($glyf_offset + $this->fdt['indexToLoc'][$i]), $length);
434435
} else {
435436
$length = 0;
@@ -497,6 +498,20 @@ protected function addProcessedTables(): void
497498
);
498499
}
499500

501+
/**
502+
* Returns the first available loca index from $start, or null if none exists.
503+
*/
504+
protected function getNextLocaIndex(int $start): ?int
505+
{
506+
for ($idx = $start; $idx <= $this->fdt['tot_num_glyphs']; ++$idx) {
507+
if (isset($this->fdt['indexToLoc'][$idx])) {
508+
return $idx;
509+
}
510+
}
511+
512+
return null;
513+
}
514+
500515
/**
501516
* build new subset font
502517
*
@@ -514,7 +529,11 @@ protected function buildSubsetFont(): void
514529
$this->subfont .= \pack('n', $searchRange); // searchRange
515530
$this->subfont .= \pack('n', $entrySelector); // entrySelector
516531
$this->subfont .= \pack('n', $rangeShift); // rangeShift
517-
$this->offset = ($numTables * 16);
532+
// Table offsets stored in $this->fdt start after the 12-byte sfnt header.
533+
// The full output adds the table directory immediately after that header,
534+
// so both directory offsets and in-buffer table positions must include this base.
535+
$tableDataBaseOffset = ($numTables * 16);
536+
$this->offset = $tableDataBaseOffset;
518537
foreach ($this->fdt['table'] as $tag => $data) {
519538
$this->subfont .= $tag; // tag
520539
$this->subfont .= \pack('N', $data['checkSum']); // checkSum
@@ -528,8 +547,9 @@ protected function buildSubsetFont(): void
528547

529548
// set checkSumAdjustment on head table
530549
$checkSumAdjustment = (0xB1B0AFBA - $this->getTableChecksum($this->subfont, \strlen($this->subfont)));
531-
$this->subfont = \substr($this->subfont, 0, $this->fdt['table']['head']['offset'] + 8)
550+
$headAdjustmentPos = $tableDataBaseOffset + $this->fdt['table']['head']['offset'] + 8;
551+
$this->subfont = \substr($this->subfont, 0, $headAdjustmentPos)
532552
. \pack('N', $checkSumAdjustment)
533-
. \substr($this->subfont, $this->fdt['table']['head']['offset'] + 12);
553+
. \substr($this->subfont, $headAdjustmentPos + 4);
534554
}
535555
}

test/OutputTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@
1616

1717
namespace Test;
1818

19+
/**
20+
* @phpstan-import-type TFontDataCidInfo from \Com\Tecnick\Pdf\Font\Load
21+
* @phpstan-import-type TFontDataDesc from \Com\Tecnick\Pdf\Font\Load
22+
* @phpstan-type TUniToCidFont array{
23+
* cidinfo: TFontDataCidInfo,
24+
* cw: array<int, int>,
25+
* desc: TFontDataDesc,
26+
* dw: int,
27+
* enc: string,
28+
* i: int,
29+
* n: int,
30+
* name: string,
31+
* subset: bool,
32+
* subsetchars: array<int, bool>
33+
* }
34+
*/
35+
class OutputTestOutFont extends \Com\Tecnick\Pdf\Font\OutFont
36+
{
37+
/**
38+
* @param TUniToCidFont $font
39+
*/
40+
public function runUniToCid(array &$font, int $cidoffset): void
41+
{
42+
$this->uniToCid($font, $cidoffset);
43+
}
44+
}
45+
1946
/**
2047
* Output Test
2148
*
@@ -28,6 +55,8 @@
2855
* @link https://github.com/tecnickcom/tc-lib-pdf-font
2956
*
3057
* @SuppressWarnings("PHPMD.LongVariable")
58+
*
59+
* @phpstan-import-type TFontData from \Com\Tecnick\Pdf\Font\Load
3160
*/
3261
class OutputTest extends TestUtil
3362
{
@@ -180,4 +209,79 @@ public function testSubsetTrueTypeUnicodeOutputUsesValidCidSystemInfoAndFontStre
180209
$this->assertNotEmpty($lengths);
181210
$this->assertGreaterThan(1000, \max($lengths));
182211
}
212+
213+
public function testSubsetCharMergePreservesUnicodeKeys(): void
214+
{
215+
$this->setupTest();
216+
$indir = \dirname(__DIR__) . '/util/vendor/tecnickcom/tc-font-mirror/';
217+
218+
$objnum = 1;
219+
$stack = new \Com\Tecnick\Pdf\Font\Stack(1);
220+
new \Com\Tecnick\Pdf\Font\Import($indir . 'freefont/FreeSans.ttf');
221+
$stack->add($objnum, 'freesans', '', '', true);
222+
223+
$fonts = $stack->getFonts();
224+
/** @var TFontData $base */
225+
$base = $fonts['freesans'];
226+
$base['key'] = 'freesans_dup';
227+
$base['i'] = $base['i'] + 1000;
228+
$base['n'] = $base['n'] + 1000;
229+
$base['subsetchars'] = [8776 => true];
230+
/** @var TFontData $primary */
231+
$primary = $fonts['freesans'];
232+
$primary['subsetchars'] = [960 => true];
233+
234+
/** @var array<string, TFontData> $fonts */
235+
$fonts =
236+
\array_replace(
237+
$fonts,
238+
[
239+
'freesans' => $primary,
240+
'freesans_dup' => $base,
241+
]
242+
);
243+
244+
$encrypt = new \Com\Tecnick\Pdf\Encrypt\Encrypt();
245+
$output = new \Com\Tecnick\Pdf\Font\Output($fonts, $objnum, $encrypt);
246+
247+
$ref = new \ReflectionClass($output);
248+
$prop = $ref->getProperty('subchars');
249+
$prop->setAccessible(true);
250+
$subchars = $prop->getValue($output);
251+
252+
$this->assertIsArray($subchars);
253+
$this->assertNotEmpty($subchars);
254+
$first = \array_values($subchars)[0];
255+
$this->assertArrayHasKey(960, $first);
256+
$this->assertArrayHasKey(8776, $first);
257+
}
258+
259+
public function testUniToCidPreservesNumericCidKeys(): void
260+
{
261+
$outfont = new OutputTestOutFont();
262+
263+
$font = [
264+
'cidinfo' => ['Ordering' => 'Identity', 'Registry' => 'Adobe', 'Supplement' => 0, 'uni2cid' => [960 => 853, 8776 => 3283]],
265+
'cw' => [32 => 250, 960 => 500, 8776 => 600],
266+
'desc' => [
267+
'Ascent' => 0, 'AvgWidth' => 0, 'CapHeight' => 0, 'Descent' => 0,
268+
'Flags' => 0, 'FontBBox' => '', 'ItalicAngle' => 0, 'Leading' => 0,
269+
'MaxWidth' => 0, 'MissingWidth' => 0, 'StemH' => 0, 'StemV' => 0, 'XHeight' => 0,
270+
],
271+
'dw' => 0,
272+
'enc' => '',
273+
'i' => 1,
274+
'n' => 1,
275+
'name' => 'test',
276+
'subset' => true,
277+
'subsetchars' => [],
278+
];
279+
280+
$outfont->runUniToCid($font, 0);
281+
282+
$this->assertArrayHasKey(853, $font['cw']);
283+
$this->assertArrayHasKey(3283, $font['cw']);
284+
$this->assertSame(500, $font['cw'][853]);
285+
$this->assertSame(600, $font['cw'][3283]);
286+
}
183287
}

test/StackTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,23 @@ public function testStackMissingFont(): void
185185
$objnum = 1;
186186
$stack->insert($objnum, 'missing');
187187
}
188+
189+
public function testUnicodeOrdAddedToSubsetChars(): void
190+
{
191+
$this->setupTest();
192+
$indir = \dirname(__DIR__) . '/util/vendor/tecnickcom/tc-font-mirror/';
193+
$objnum = 1;
194+
195+
$stack = new \Com\Tecnick\Pdf\Font\Stack(0.75, true, true, true);
196+
new \Com\Tecnick\Pdf\Font\Import($indir . 'freefont/FreeSans.ttf');
197+
$stack->insert($objnum, 'freesans', '', 12, 0, 1, '', true);
198+
199+
// Use pi and almost-equal to ensure non-latin BMP code points are tracked.
200+
$stack->getOrdArrDims([960, 8776]);
201+
202+
$fonts = $stack->getFonts();
203+
$fkey = $stack->getCurrentFontKey();
204+
$this->assertArrayHasKey(960, $fonts[$fkey]['subsetchars']);
205+
$this->assertArrayHasKey(8776, $fonts[$fkey]['subsetchars']);
206+
}
188207
}

0 commit comments

Comments
 (0)