bytes, new FlateDecode()); $map = (new CharacterMapFactory($program))->create($fontFile, new UnicodeText('ABCDEF éΩfi')); $descriptor = (new FontDescriptorFactory($program))->create($fontFile, new FontDescriptorFlags(4), new NumericType(80)); $descendant = new CIDFontType2($descriptor, new CIDToGIDMap($map, new FlateDecode()), new CIDWidths($map)); // This CMap uses one byte per code, so it is not Identity-H. The first // dictionary describes the codespace and six letters. The second references // this CMap through /UseCMap and adds three distinct codes A4, A5 and A6. $code = static fn (string $bytes): CMapCode => new CMapCode(new HexadecimalString($bytes)); $spaces = new CodeSpaces(new CodeSpaceRange($code("\0"), $code("\xff"))); $collection = new CMapSystemInfo(new LiteralString('Adobe'), new LiteralString('Identity'), new IntegerType(0)); $base = new EncodingCMap( new CMapName(new NameType('PDFToolkit-OneByte-Base')), $collection, $spaces, new CidMappings( new CidRange(new CMapCodeRange($code('A'), $code('F')), new IntegerType($map->character(0x41)->cid)), new CidChar($code(' '), new IntegerType($map->character(0x20)->cid)), new NotdefRange(new CMapCodeRange($code("\0"), $code("\x1f")), new IntegerType(0)), ), ); $encoding = new EncodingCMap( new CMapName(new NameType('PDFToolkit-OneByte-Extended')), $collection, new CodeSpaces(), new CidMappings(new CidRange(new CMapCodeRange($code("\xa4"), $code("\xa6")), new IntegerType($map->character(0xe9)->cid))), base: $base, ); // /Encoding selects glyphs; /ToUnicode explains their textual content. // A..F uses incremental bfrange; A4..A6 uses distinct sequences. // The ligature fi is one glyph whose extracted Unicode sequence is "fi". $unicode = new ToUnicodeCMap(new UnicodeCMap( new CMapName(new NameType('PDFToolkit-OneByte-UCS')), $spaces, new UnicodeMappings( new UnicodeRange(new CMapCodeRange($code('A'), $code('F')), (new UnicodeScalar(new IntegerType(0x41)))->sequence()), new UnicodeChar($code(' '), (new UnicodeScalar(new IntegerType(0x20)))->sequence()), new UnicodeArrayRange( new CMapCodeRange($code("\xa4"), $code("\xa6")), (new UnicodeScalar(new IntegerType(0xe9)))->sequence(), (new UnicodeScalar(new IntegerType(0x3a9)))->sequence(), new UnicodeScalars(new UnicodeScalar(new IntegerType(0x66)), new UnicodeScalar(new IntegerType(0x69))), ), ), ), new FlateDecode()); $font = new Type0Font($descendant, $unicode, encoding: $encoding); $resources = new ResourceDictionary(new Font(new DictionaryType(new Entry(new NameType('F1'), new IndirectReference(new Type1Font(StandardFont::Helvetica))), new Entry(new NameType('F2'), new IndirectReference($font))))); // Each displayed code remains an object. Concatenation is checked against // the CMap codespace; no implicit UTF-8 -> code -> CID conversion occurs here. $characters = new CMapCodeSequence($encoding->codeSpaces(), $code('A'), $code('B'), $code('C'), $code('D'), $code('E'), $code('F'), $code(' '), $code("\xa4"), $code("\xa5"), $code(' '), $code("\xa6")); $contents = new ContentStreams(new BeginTextObject(), new SetTextFontAndSize(new NameType('F1'), new NumericType(24)), new MoveTextPosition(new NumericType(54), new NumericType(738)), new ShowText(new LiteralString('PDFToolkit - CMaps explicites')), new EndTextObject()); foreach ([['Un encodage incorpore avec des codes sur un octet.', 684], ['Le second CMap herite du premier par UseCMap.', 658], ['CID : selection des glyphes dans la police incorporee.', 612], ['ToUnicode : extraction du sens de chaque code.', 586], ['Resultat ci-dessous : six lettres, accent, grec, ligature.', 540]] as [$text, $y]) { $contents = $contents->with(new BeginTextObject(), new SetTextFontAndSize(new NameType('F1'), new NumericType(13)), new MoveTextPosition(new NumericType(54), new NumericType($y)), new ShowText(new LiteralString($text)), new EndTextObject()); } $contents = $contents->with(new BeginTextObject(), new SetTextFontAndSize(new NameType('F2'), new NumericType(34)), new MoveTextPosition(new NumericType(54), new NumericType(466)), new ShowText($characters->string()), new EndTextObject()); foreach ([['A4 = e accentue ; A5 = Omega ; A6 = ligature fi.', 402], ['Les trois destinations viennent d\'un bfrange avec tableau.', 376], ['Le bfrange de A..F incremente seulement le dernier octet.', 350], ['La police et le repertoire restent des objets PDF independants.', 292], ['Source : ISO 32000-1, pages PDF 285-289 et 301-303.', 266]] as [$text, $y]) { $contents = $contents->with(new BeginTextObject(), new SetTextFontAndSize(new NameType('F1'), new NumericType(12)), new MoveTextPosition(new NumericType(54), new NumericType($y)), new ShowText(new LiteralString($text)), new EndTextObject()); } $page = new PageObject(new Rectangle(new NumericType(0), new NumericType(0), new NumericType(612), new NumericType(792)), $resources, new StreamObject($contents)); $pages = (new PageTreeNode())->addKid($page); $catalog = new DocumentCatalog($pages); $file = new FileStructure(new FileHeader('1.7'), new FileBody($catalog), new IndirectReference($catalog)); $path = $argv[1] ?? __DIR__ . '/cmap-font.pdf'; $file->writeTo($path); echo 'PDF written to ' . $path . PHP_EOL;