David PHAM-VAN

Support 64k glyphs per TTF font

1 # 1.3.7 1 # 1.3.7
2 * Add Pdf Creation date 2 * Add Pdf Creation date
  3 +* Support 64k glyphs per TTF font
3 4
4 # 1.3.6 5 # 1.3.6
5 * Fix TTF Font SubSetting 6 * Fix TTF Font SubSetting
@@ -27,6 +27,9 @@ class PdfTtfFont extends PdfFont { @@ -27,6 +27,9 @@ class PdfTtfFont extends PdfFont {
27 widthsObject = PdfArrayObject(pdfDocument, <String>[]); 27 widthsObject = PdfArrayObject(pdfDocument, <String>[]);
28 } 28 }
29 29
  30 + @override
  31 + String get subtype => font.unicode ? '/Type0' : super.subtype;
  32 +
30 PdfUnicodeCmap unicodeCMap; 33 PdfUnicodeCmap unicodeCMap;
31 34
32 PdfFontDescriptor descriptor; 35 PdfFontDescriptor descriptor;
@@ -57,26 +60,60 @@ class PdfTtfFont extends PdfFont { @@ -57,26 +60,60 @@ class PdfTtfFont extends PdfFont {
57 return font.glyphInfoMap[g] ?? PdfFontMetrics.zero; 60 return font.glyphInfoMap[g] ?? PdfFontMetrics.zero;
58 } 61 }
59 62
60 - @override  
61 - void _prepare() { 63 + void _buildTrueType(Map<String, PdfStream> params) {
62 int charMin; 64 int charMin;
63 int charMax; 65 int charMax;
64 66
65 - super._prepare(); 67 + file.buf.putBytes(font.bytes.buffer.asUint8List());
  68 + file.params['/Length1'] = PdfStream.intNum(font.bytes.lengthInBytes);
  69 +
  70 + params['/BaseFont'] = PdfStream.string('/' + fontName);
  71 + params['/FontDescriptor'] = descriptor.ref();
  72 + charMin = 32;
  73 + charMax = 255;
  74 + for (int i = charMin; i <= charMax; i++) {
  75 + widthsObject.values
  76 + .add((glyphMetrics(i).advanceWidth * 1000.0).toInt().toString());
  77 + }
  78 + params['/FirstChar'] = PdfStream.intNum(charMin);
  79 + params['/LastChar'] = PdfStream.intNum(charMax);
  80 + params['/Widths'] = widthsObject.ref();
  81 + }
  82 +
  83 + void _buildType0(Map<String, PdfStream> params) {
  84 + int charMin;
  85 + int charMax;
66 86
67 final TtfWriter ttfWriter = TtfWriter(font); 87 final TtfWriter ttfWriter = TtfWriter(font);
68 final Uint8List data = ttfWriter.withChars(unicodeCMap.cmap); 88 final Uint8List data = ttfWriter.withChars(unicodeCMap.cmap);
69 -  
70 file.buf.putBytes(data); 89 file.buf.putBytes(data);
71 file.params['/Length1'] = PdfStream.intNum(data.length); 90 file.params['/Length1'] = PdfStream.intNum(data.length);
72 91
  92 + final PdfStream descendantFont = PdfStream.dictionary(<String, PdfStream>{
  93 + '/Type': PdfStream.string('/Font'),
  94 + '/BaseFont': PdfStream.string('/' + fontName),
  95 + '/FontFile2': file.ref(),
  96 + '/FontDescriptor': descriptor.ref(),
  97 + '/W': PdfStream.array(<PdfStream>[
  98 + PdfStream.intNum(0),
  99 + widthsObject.ref(),
  100 + ]),
  101 + '/CIDToGIDMap': PdfStream.string('/Identity'),
  102 + '/DW': PdfStream.string('1000'),
  103 + '/Subtype': PdfStream.string('/CIDFontType2'),
  104 + '/CIDSystemInfo': PdfStream.dictionary(<String, PdfStream>{
  105 + '/Supplement': PdfStream.intNum(0),
  106 + '/Registry': PdfStream()..putText('Adobe'),
  107 + '/Ordering': PdfStream()..putText('Identity-H'),
  108 + })
  109 + });
  110 +
73 params['/BaseFont'] = PdfStream.string('/' + fontName); 111 params['/BaseFont'] = PdfStream.string('/' + fontName);
74 - params['/FontDescriptor'] = descriptor.ref();  
75 - if (font.unicode) {  
76 - if (params.containsKey('/Encoding')) {  
77 - params.remove('/Encoding');  
78 - } 112 + params['/Encoding'] = PdfStream.string('/Identity-H');
  113 + params['/DescendantFonts'] = PdfStream()
  114 + ..putArray(<PdfStream>[descendantFont]);
79 params['/ToUnicode'] = unicodeCMap.ref(); 115 params['/ToUnicode'] = unicodeCMap.ref();
  116 +
80 charMin = 0; 117 charMin = 0;
81 charMax = unicodeCMap.cmap.length - 1; 118 charMax = unicodeCMap.cmap.length - 1;
82 for (int i = charMin; i <= charMax; i++) { 119 for (int i = charMin; i <= charMax; i++) {
@@ -85,17 +122,17 @@ class PdfTtfFont extends PdfFont { @@ -85,17 +122,17 @@ class PdfTtfFont extends PdfFont {
85 .toInt() 122 .toInt()
86 .toString()); 123 .toString());
87 } 124 }
88 - } else {  
89 - charMin = 32;  
90 - charMax = 255;  
91 - for (int i = charMin; i <= charMax; i++) {  
92 - widthsObject.values  
93 - .add((glyphMetrics(i).advanceWidth * 1000.0).toInt().toString());  
94 } 125 }
  126 +
  127 + @override
  128 + void _prepare() {
  129 + super._prepare();
  130 +
  131 + if (font.unicode) {
  132 + _buildType0(params);
  133 + } else {
  134 + _buildTrueType(params);
95 } 135 }
96 - params['/FirstChar'] = PdfStream.intNum(charMin);  
97 - params['/LastChar'] = PdfStream.intNum(charMax);  
98 - params['/Widths'] = widthsObject.ref();  
99 } 136 }
100 137
101 @override 138 @override
@@ -105,7 +142,8 @@ class PdfTtfFont extends PdfFont { @@ -105,7 +142,8 @@ class PdfTtfFont extends PdfFont {
105 } 142 }
106 143
107 final Runes runes = text.runes; 144 final Runes runes = text.runes;
108 - final List<int> bytes = List<int>(); 145 + final PdfStream stream = PdfStream();
  146 + stream.putBytes(latin1.encode('<'));
109 for (int rune in runes) { 147 for (int rune in runes) {
110 int char = unicodeCMap.cmap.indexOf(rune); 148 int char = unicodeCMap.cmap.indexOf(rune);
111 if (char == -1) { 149 if (char == -1) {
@@ -113,13 +151,10 @@ class PdfTtfFont extends PdfFont { @@ -113,13 +151,10 @@ class PdfTtfFont extends PdfFont {
113 unicodeCMap.cmap.add(rune); 151 unicodeCMap.cmap.add(rune);
114 } 152 }
115 153
116 - bytes.add(char); 154 + stream.putBytes(latin1.encode(char.toRadixString(16).padLeft(4, '0')));
117 } 155 }
118 -  
119 - return PdfStream()  
120 - ..putBytes(latin1.encode('('))  
121 - ..putTextBytes(bytes)  
122 - ..putBytes(latin1.encode(')')); 156 + stream.putBytes(latin1.encode('>'));
  157 + return stream;
123 } 158 }
124 159
125 @override 160 @override
@@ -34,14 +34,14 @@ class PdfUnicodeCmap extends PdfObjectStream { @@ -34,14 +34,14 @@ class PdfUnicodeCmap extends PdfObjectStream {
34 '/CMapName/Adobe-Identity-UCS def\n' 34 '/CMapName/Adobe-Identity-UCS def\n'
35 '/CMapType 2 def\n' 35 '/CMapType 2 def\n'
36 '1 begincodespacerange\n' 36 '1 begincodespacerange\n'
37 - '<00> <FF>\n' 37 + '<0000> <FFFF>\n'
38 'endcodespacerange\n' 38 'endcodespacerange\n'
39 '${cmap.length} beginbfchar\n'); 39 '${cmap.length} beginbfchar\n');
40 40
41 for (int key = 0; key < cmap.length; key++) { 41 for (int key = 0; key < cmap.length; key++) {
42 final int value = cmap[key]; 42 final int value = cmap[key];
43 buf.putString('<' + 43 buf.putString('<' +
44 - key.toRadixString(16).toUpperCase().padLeft(2, '0') + 44 + key.toRadixString(16).toUpperCase().padLeft(4, '0') +
45 '> <' + 45 '> <' +
46 value.toRadixString(16).toUpperCase().padLeft(4, '0') + 46 value.toRadixString(16).toUpperCase().padLeft(4, '0') +
47 '>\n'); 47 '>\n');