Showing
4 changed files
with
65 additions
and
34 deletions
@@ -372,13 +372,13 @@ class TtfParser { | @@ -372,13 +372,13 @@ class TtfParser { | ||
372 | final hmtxOffset = tableOffsets[hmtx_table]!; | 372 | final hmtxOffset = tableOffsets[hmtx_table]!; |
373 | final unitsPerEm = this.unitsPerEm; | 373 | final unitsPerEm = this.unitsPerEm; |
374 | final numOfLongHorMetrics = this.numOfLongHorMetrics; | 374 | final numOfLongHorMetrics = this.numOfLongHorMetrics; |
375 | - final defaultadvanceWidth = | 375 | + final defaultAdvanceWidth = |
376 | bytes.getUint16(hmtxOffset + (numOfLongHorMetrics - 1) * 4); | 376 | bytes.getUint16(hmtxOffset + (numOfLongHorMetrics - 1) * 4); |
377 | 377 | ||
378 | for (var glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { | 378 | for (var glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { |
379 | final advanceWidth = glyphIndex < numOfLongHorMetrics | 379 | final advanceWidth = glyphIndex < numOfLongHorMetrics |
380 | ? bytes.getUint16(hmtxOffset + glyphIndex * 4) | 380 | ? bytes.getUint16(hmtxOffset + glyphIndex * 4) |
381 | - : defaultadvanceWidth; | 381 | + : defaultAdvanceWidth; |
382 | final leftBearing = glyphIndex < numOfLongHorMetrics | 382 | final leftBearing = glyphIndex < numOfLongHorMetrics |
383 | ? bytes.getInt16(hmtxOffset + glyphIndex * 4 + 2) | 383 | ? bytes.getInt16(hmtxOffset + glyphIndex * 4 + 2) |
384 | : bytes.getInt16(hmtxOffset + | 384 | : bytes.getInt16(hmtxOffset + |
@@ -500,7 +500,7 @@ class TtfParser { | @@ -500,7 +500,7 @@ class TtfParser { | ||
500 | const HAS_SCALE = 0x008; | 500 | const HAS_SCALE = 0x008; |
501 | const MORE_COMPONENTS = 0x0020; | 501 | const MORE_COMPONENTS = 0x0020; |
502 | const HAS_X_Y_SCALE = 0x0040; | 502 | const HAS_X_Y_SCALE = 0x0040; |
503 | - const HAS_TRANFORMATION_MATRIX = 0x0080; | 503 | + const HAS_TRANSFORMATION_MATRIX = 0x0080; |
504 | const WE_HAVE_INSTRUCTIONS = 0x0100; | 504 | const WE_HAVE_INSTRUCTIONS = 0x0100; |
505 | 505 | ||
506 | final components = <int>[]; | 506 | final components = <int>[]; |
@@ -515,7 +515,7 @@ class TtfParser { | @@ -515,7 +515,7 @@ class TtfParser { | ||
515 | offset += 2; | 515 | offset += 2; |
516 | } else if (flags & HAS_X_Y_SCALE != 0) { | 516 | } else if (flags & HAS_X_Y_SCALE != 0) { |
517 | offset += 4; | 517 | offset += 4; |
518 | - } else if (flags & HAS_TRANFORMATION_MATRIX != 0) { | 518 | + } else if (flags & HAS_TRANSFORMATION_MATRIX != 0) { |
519 | offset += 8; | 519 | offset += 8; |
520 | } | 520 | } |
521 | 521 |
@@ -19,15 +19,16 @@ import 'dart:typed_data'; | @@ -19,15 +19,16 @@ import 'dart:typed_data'; | ||
19 | 19 | ||
20 | import 'ttf_parser.dart'; | 20 | import 'ttf_parser.dart'; |
21 | 21 | ||
22 | -/// Generate a TTF font copy with the minimal number of glyph to embedd | 22 | +/// Generate a TTF font copy with the minimal number of glyph to embed |
23 | /// into the PDF document | 23 | /// into the PDF document |
24 | /// | 24 | /// |
25 | /// https://opentype.js.org/ | 25 | /// https://opentype.js.org/ |
26 | +/// https://github.com/HinTak/Font-Validator | ||
26 | class TtfWriter { | 27 | class TtfWriter { |
27 | - /// Create a Truetype Writer object | 28 | + /// Create a TrueType Writer object |
28 | TtfWriter(this.ttf); | 29 | TtfWriter(this.ttf); |
29 | 30 | ||
30 | - /// original truetype file | 31 | + /// Original TrueType file |
31 | final TtfParser ttf; | 32 | final TtfParser ttf; |
32 | 33 | ||
33 | int _calcTableChecksum(ByteData table) { | 34 | int _calcTableChecksum(ByteData table) { |
@@ -56,7 +57,7 @@ class TtfWriter { | @@ -56,7 +57,7 @@ class TtfWriter { | ||
56 | } | 57 | } |
57 | } | 58 | } |
58 | 59 | ||
59 | - int _wordAlign(int offset, [int align = 2]) { | 60 | + int _wordAlign(int offset, [int align = 4]) { |
60 | return offset + ((align - (offset % align)) % align); | 61 | return offset + ((align - (offset % align)) % align); |
61 | } | 62 | } |
62 | 63 | ||
@@ -105,10 +106,6 @@ class TtfWriter { | @@ -105,10 +106,6 @@ class TtfWriter { | ||
105 | assert(compounds[compound]! >= 0, 'Unable to find the glyph'); | 106 | assert(compounds[compound]! >= 0, 'Unable to find the glyph'); |
106 | } | 107 | } |
107 | 108 | ||
108 | - // Add one last empty glyph | ||
109 | - final glyph = TtfGlyphInfo(32, Uint8List(0), const <int>[]); | ||
110 | - glyphsInfo.add(glyph); | ||
111 | - | ||
112 | // update compound indices | 109 | // update compound indices |
113 | for (final glyph in glyphsInfo) { | 110 | for (final glyph in glyphsInfo) { |
114 | if (glyph.compounds.isNotEmpty) { | 111 | if (glyph.compounds.isNotEmpty) { |
@@ -122,19 +119,19 @@ class TtfWriter { | @@ -122,19 +119,19 @@ class TtfWriter { | ||
122 | _wordAlign(glyphsTableLength + glyph.data.lengthInBytes); | 119 | _wordAlign(glyphsTableLength + glyph.data.lengthInBytes); |
123 | } | 120 | } |
124 | var offset = 0; | 121 | var offset = 0; |
125 | - final glyphsTable = Uint8List(_wordAlign(glyphsTableLength, 4)); | 122 | + final glyphsTable = Uint8List(_wordAlign(glyphsTableLength)); |
126 | tables[TtfParser.glyf_table] = glyphsTable; | 123 | tables[TtfParser.glyf_table] = glyphsTable; |
127 | tablesLength[TtfParser.glyf_table] = glyphsTableLength; | 124 | tablesLength[TtfParser.glyf_table] = glyphsTableLength; |
128 | 125 | ||
129 | // Loca | 126 | // Loca |
130 | if (ttf.indexToLocFormat == 0) { | 127 | if (ttf.indexToLocFormat == 0) { |
131 | tables[TtfParser.loca_table] = | 128 | tables[TtfParser.loca_table] = |
132 | - Uint8List(_wordAlign(glyphsInfo.length * 2, 4)); // uint16 | ||
133 | - tablesLength[TtfParser.loca_table] = glyphsInfo.length * 2; | 129 | + Uint8List(_wordAlign((glyphsInfo.length + 1) * 2)); // uint16 |
130 | + tablesLength[TtfParser.loca_table] = (glyphsInfo.length + 1) * 2; | ||
134 | } else { | 131 | } else { |
135 | tables[TtfParser.loca_table] = | 132 | tables[TtfParser.loca_table] = |
136 | - Uint8List(_wordAlign(glyphsInfo.length * 4, 4)); // uint32 | ||
137 | - tablesLength[TtfParser.loca_table] = glyphsInfo.length * 4; | 133 | + Uint8List(_wordAlign((glyphsInfo.length + 1) * 4)); // uint32 |
134 | + tablesLength[TtfParser.loca_table] = (glyphsInfo.length + 1) * 4; | ||
138 | } | 135 | } |
139 | 136 | ||
140 | { | 137 | { |
@@ -151,6 +148,11 @@ class TtfWriter { | @@ -151,6 +148,11 @@ class TtfWriter { | ||
151 | glyphsTable.setAll(offset, glyph.data); | 148 | glyphsTable.setAll(offset, glyph.data); |
152 | offset = _wordAlign(offset + glyph.data.lengthInBytes); | 149 | offset = _wordAlign(offset + glyph.data.lengthInBytes); |
153 | } | 150 | } |
151 | + if (ttf.indexToLocFormat == 0) { | ||
152 | + loca.setUint16(index, offset ~/ 2); | ||
153 | + } else { | ||
154 | + loca.setUint32(index, offset); | ||
155 | + } | ||
154 | } | 156 | } |
155 | 157 | ||
156 | { | 158 | { |
@@ -158,7 +160,7 @@ class TtfWriter { | @@ -158,7 +160,7 @@ class TtfWriter { | ||
158 | final start = ttf.tableOffsets[TtfParser.head_table]!; | 160 | final start = ttf.tableOffsets[TtfParser.head_table]!; |
159 | final len = ttf.tableSize[TtfParser.head_table]!; | 161 | final len = ttf.tableSize[TtfParser.head_table]!; |
160 | final head = Uint8List.fromList( | 162 | final head = Uint8List.fromList( |
161 | - ttf.bytes.buffer.asUint8List(start, _wordAlign(len, 4))); | 163 | + ttf.bytes.buffer.asUint8List(start, _wordAlign(len))); |
162 | head.buffer.asByteData().setUint32(8, 0); // checkSumAdjustment | 164 | head.buffer.asByteData().setUint32(8, 0); // checkSumAdjustment |
163 | tables[TtfParser.head_table] = head; | 165 | tables[TtfParser.head_table] = head; |
164 | tablesLength[TtfParser.head_table] = len; | 166 | tablesLength[TtfParser.head_table] = len; |
@@ -169,7 +171,7 @@ class TtfWriter { | @@ -169,7 +171,7 @@ class TtfWriter { | ||
169 | final start = ttf.tableOffsets[TtfParser.maxp_table]!; | 171 | final start = ttf.tableOffsets[TtfParser.maxp_table]!; |
170 | final len = ttf.tableSize[TtfParser.maxp_table]!; | 172 | final len = ttf.tableSize[TtfParser.maxp_table]!; |
171 | final maxp = Uint8List.fromList( | 173 | final maxp = Uint8List.fromList( |
172 | - ttf.bytes.buffer.asUint8List(start, _wordAlign(len, 4))); | 174 | + ttf.bytes.buffer.asUint8List(start, _wordAlign(len))); |
173 | maxp.buffer.asByteData().setUint16(4, glyphsInfo.length); | 175 | maxp.buffer.asByteData().setUint16(4, glyphsInfo.length); |
174 | tables[TtfParser.maxp_table] = maxp; | 176 | tables[TtfParser.maxp_table] = maxp; |
175 | tablesLength[TtfParser.maxp_table] = len; | 177 | tablesLength[TtfParser.maxp_table] = len; |
@@ -180,7 +182,7 @@ class TtfWriter { | @@ -180,7 +182,7 @@ class TtfWriter { | ||
180 | final start = ttf.tableOffsets[TtfParser.hhea_table]!; | 182 | final start = ttf.tableOffsets[TtfParser.hhea_table]!; |
181 | final len = ttf.tableSize[TtfParser.hhea_table]!; | 183 | final len = ttf.tableSize[TtfParser.hhea_table]!; |
182 | final hhea = Uint8List.fromList( | 184 | final hhea = Uint8List.fromList( |
183 | - ttf.bytes.buffer.asUint8List(start, _wordAlign(len, 4))); | 185 | + ttf.bytes.buffer.asUint8List(start, _wordAlign(len))); |
184 | hhea.buffer | 186 | hhea.buffer |
185 | .asByteData() | 187 | .asByteData() |
186 | .setUint16(34, glyphsInfo.length); // numOfLongHorMetrics | 188 | .setUint16(34, glyphsInfo.length); // numOfLongHorMetrics |
@@ -192,17 +194,17 @@ class TtfWriter { | @@ -192,17 +194,17 @@ class TtfWriter { | ||
192 | { | 194 | { |
193 | // HMTX table | 195 | // HMTX table |
194 | final len = 4 * glyphsInfo.length; | 196 | final len = 4 * glyphsInfo.length; |
195 | - final hmtx = Uint8List(_wordAlign(len, 4)); | 197 | + final hmtx = Uint8List(_wordAlign(len)); |
196 | final hmtxOffset = ttf.tableOffsets[TtfParser.hmtx_table]!; | 198 | final hmtxOffset = ttf.tableOffsets[TtfParser.hmtx_table]!; |
197 | final hmtxData = hmtx.buffer.asByteData(); | 199 | final hmtxData = hmtx.buffer.asByteData(); |
198 | final numOfLongHorMetrics = ttf.numOfLongHorMetrics; | 200 | final numOfLongHorMetrics = ttf.numOfLongHorMetrics; |
199 | - final defaultadvanceWidth = | 201 | + final defaultAdvanceWidth = |
200 | ttf.bytes.getUint16(hmtxOffset + (numOfLongHorMetrics - 1) * 4); | 202 | ttf.bytes.getUint16(hmtxOffset + (numOfLongHorMetrics - 1) * 4); |
201 | var index = 0; | 203 | var index = 0; |
202 | for (final glyph in glyphsInfo) { | 204 | for (final glyph in glyphsInfo) { |
203 | final advanceWidth = glyph.index < numOfLongHorMetrics | 205 | final advanceWidth = glyph.index < numOfLongHorMetrics |
204 | ? ttf.bytes.getUint16(hmtxOffset + glyph.index * 4) | 206 | ? ttf.bytes.getUint16(hmtxOffset + glyph.index * 4) |
205 | - : defaultadvanceWidth; | 207 | + : defaultAdvanceWidth; |
206 | final leftBearing = glyph.index < numOfLongHorMetrics | 208 | final leftBearing = glyph.index < numOfLongHorMetrics |
207 | ? ttf.bytes.getInt16(hmtxOffset + glyph.index * 4 + 2) | 209 | ? ttf.bytes.getInt16(hmtxOffset + glyph.index * 4 + 2) |
208 | : ttf.bytes.getInt16(hmtxOffset + | 210 | : ttf.bytes.getInt16(hmtxOffset + |
@@ -219,12 +221,12 @@ class TtfWriter { | @@ -219,12 +221,12 @@ class TtfWriter { | ||
219 | { | 221 | { |
220 | // CMAP table | 222 | // CMAP table |
221 | const len = 40; | 223 | const len = 40; |
222 | - final cmap = Uint8List(_wordAlign(len, 4)); | 224 | + final cmap = Uint8List(_wordAlign(len)); |
223 | final cmapData = cmap.buffer.asByteData(); | 225 | final cmapData = cmap.buffer.asByteData(); |
224 | cmapData.setUint16(0, 0); // Table version number | 226 | cmapData.setUint16(0, 0); // Table version number |
225 | cmapData.setUint16(2, 1); // Number of encoding tables that follow. | 227 | cmapData.setUint16(2, 1); // Number of encoding tables that follow. |
226 | cmapData.setUint16(4, 3); // Platform ID | 228 | cmapData.setUint16(4, 3); // Platform ID |
227 | - cmapData.setUint16(6, 1); // Platform-specific encoding ID | 229 | + cmapData.setUint16(6, 10); // Platform-specific encoding ID |
228 | cmapData.setUint32(8, 12); // Offset from beginning of table | 230 | cmapData.setUint32(8, 12); // Offset from beginning of table |
229 | cmapData.setUint16(12, 12); // Table format | 231 | cmapData.setUint16(12, 12); // Table format |
230 | cmapData.setUint32(16, 28); // Table length | 232 | cmapData.setUint32(16, 28); // Table length |
@@ -239,7 +241,7 @@ class TtfWriter { | @@ -239,7 +241,7 @@ class TtfWriter { | ||
239 | } | 241 | } |
240 | 242 | ||
241 | { | 243 | { |
242 | - final bytes = <int>[]; | 244 | + final bytes = BytesBuilder(); |
243 | 245 | ||
244 | final numTables = tables.length; | 246 | final numTables = tables.length; |
245 | 247 | ||
@@ -258,7 +260,20 @@ class TtfWriter { | @@ -258,7 +260,20 @@ class TtfWriter { | ||
258 | // Create the table directory | 260 | // Create the table directory |
259 | var count = 0; | 261 | var count = 0; |
260 | var offset = 12 + numTables * 16; | 262 | var offset = 12 + numTables * 16; |
261 | - tables.forEach((String name, Uint8List data) { | 263 | + var headOffset = 0; |
264 | + | ||
265 | + final tableKeys = [ | ||
266 | + TtfParser.head_table, | ||
267 | + TtfParser.hhea_table, | ||
268 | + TtfParser.maxp_table, | ||
269 | + TtfParser.hmtx_table, | ||
270 | + TtfParser.cmap_table, | ||
271 | + TtfParser.loca_table, | ||
272 | + TtfParser.glyf_table, | ||
273 | + ]; | ||
274 | + | ||
275 | + for (final name in tableKeys) { | ||
276 | + final data = tables[name]!; | ||
262 | final runes = name.runes.toList(); | 277 | final runes = name.runes.toList(); |
263 | start.setUint8(12 + count * 16, runes[0]); | 278 | start.setUint8(12 + count * 16, runes[0]); |
264 | start.setUint8(12 + count * 16 + 1, runes[1]); | 279 | start.setUint8(12 + count * 16 + 1, runes[1]); |
@@ -268,16 +283,28 @@ class TtfWriter { | @@ -268,16 +283,28 @@ class TtfWriter { | ||
268 | _calcTableChecksum(data.buffer.asByteData())); // checkSum | 283 | _calcTableChecksum(data.buffer.asByteData())); // checkSum |
269 | start.setUint32(12 + count * 16 + 8, offset); // offset | 284 | start.setUint32(12 + count * 16 + 8, offset); // offset |
270 | start.setUint32(12 + count * 16 + 12, tablesLength[name]!); // length | 285 | start.setUint32(12 + count * 16 + 12, tablesLength[name]!); // length |
286 | + | ||
287 | + if (name == 'head') { | ||
288 | + headOffset = offset; | ||
289 | + } | ||
271 | offset += data.lengthInBytes; | 290 | offset += data.lengthInBytes; |
272 | count++; | 291 | count++; |
273 | - }); | ||
274 | - bytes.addAll(start.buffer.asUint8List()); | 292 | + } |
293 | + bytes.add(start.buffer.asUint8List()); | ||
275 | 294 | ||
276 | - tables.forEach((String name, Uint8List data) { | ||
277 | - bytes.addAll(data.buffer.asUint8List()); | ||
278 | - }); | 295 | + for (final name in tableKeys) { |
296 | + final data = tables[name]!; | ||
297 | + bytes.add(data.buffer.asUint8List()); | ||
298 | + } | ||
299 | + | ||
300 | + final output = bytes.toBytes(); | ||
301 | + | ||
302 | + final crc = 0xB1B0AFBA - _calcTableChecksum(output.buffer.asByteData()); | ||
303 | + output.buffer | ||
304 | + .asByteData() | ||
305 | + .setUint32(headOffset + 8, crc & 0xffffffff); // checkSumAdjustment | ||
279 | 306 | ||
280 | - return Uint8List.fromList(bytes); | 307 | + return output; |
281 | } | 308 | } |
282 | } | 309 | } |
283 | } | 310 | } |
@@ -3,7 +3,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | @@ -3,7 +3,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | ||
3 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf | 3 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf |
4 | repository: https://github.com/DavBfr/dart_pdf | 4 | repository: https://github.com/DavBfr/dart_pdf |
5 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 5 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
6 | -version: 3.8.4 | 6 | +version: 3.8.5 |
7 | 7 | ||
8 | environment: | 8 | environment: |
9 | sdk: ">=2.12.0 <3.0.0" | 9 | sdk: ">=2.12.0 <3.0.0" |
-
Please register or login to post a comment