Showing
10 changed files
with
318 additions
and
30 deletions
| @@ -9,3 +9,5 @@ pubspec.lock | @@ -9,3 +9,5 @@ pubspec.lock | ||
| 9 | # Directory created by dartdoc | 9 | # Directory created by dartdoc |
| 10 | # If you don't generate documentation locally you can remove this line. | 10 | # If you don't generate documentation locally you can remove this line. |
| 11 | doc/api/ | 11 | doc/api/ |
| 12 | + | ||
| 13 |
| 1 | +# 1.0.3 | ||
| 2 | +* Remove dependency to ttf_parser | ||
| 3 | + | ||
| 1 | # 1.0.2 | 4 | # 1.0.2 |
| 2 | * Update sdk support for 2.0.0 | 5 | * Update sdk support for 2.0.0 |
| 6 | + | ||
| 3 | # 1.0.1 | 7 | # 1.0.1 |
| 4 | * Add example | 8 | * Add example |
| 5 | * Lower vector_math dependency version | 9 | * Lower vector_math dependency version |
| 6 | * Uses better page format object | 10 | * Uses better page format object |
| 7 | - |
| 1 | # Pdf creation library for dart / flutter | 1 | # Pdf creation library for dart / flutter |
| 2 | 2 | ||
| 3 | +This is a low-level Pdf creation library. | ||
| 4 | +It can create a full multi-pages document with graphics, | ||
| 5 | +images and text using TrueType fonts. | ||
| 6 | + | ||
| 7 | +The coordinate system is using the internal Pdf system: | ||
| 8 | + * (0.0, 0.0) is bottom-left | ||
| 9 | + * 1.0 is defined as 1 / 72.0 inch | ||
| 10 | + * you can use the constants for centimeters, milimeters and inch defined in PDFPageFormat | ||
| 11 | + | ||
| 3 | Example: | 12 | Example: |
| 4 | ```dart | 13 | ```dart |
| 5 | final pdf = new PDFDocument(); | 14 | final pdf = new PDFDocument(); |
| @@ -12,8 +21,30 @@ g.drawRect(50.0, 30.0, 100.0, 50.0); | @@ -12,8 +21,30 @@ g.drawRect(50.0, 30.0, 100.0, 50.0); | ||
| 12 | g.fillPath(); | 21 | g.fillPath(); |
| 13 | 22 | ||
| 14 | g.setColor(new PDFColor(0.3, 0.3, 0.3)); | 23 | g.setColor(new PDFColor(0.3, 0.3, 0.3)); |
| 15 | -g.drawString(font, 12.0, "Hello World!", 50.0, 300.0); | 24 | +g.drawString(font, 12.0, "Hello World!", 5.0 * PDFPageFormat.MM, 300.0); |
| 16 | 25 | ||
| 17 | var file = new File('file.pdf'); | 26 | var file = new File('file.pdf'); |
| 18 | file.writeAsBytesSync(pdf.save()); | 27 | file.writeAsBytesSync(pdf.save()); |
| 19 | ``` | 28 | ``` |
| 29 | + | ||
| 30 | +To load an image it is possible to use the dart library `image` | ||
| 31 | + | ||
| 32 | +```dart | ||
| 33 | +Image image = decodeImage(new Io.File('test.webp').readAsBytesSync()); | ||
| 34 | +PDFImage image = new PDFImage( | ||
| 35 | + pdf, | ||
| 36 | + image: img.data.buffer.asUint8List(), | ||
| 37 | + width: img.width, | ||
| 38 | + height: img.height); | ||
| 39 | +g.drawImage(image, 100.0, 100.0, 80.0); | ||
| 40 | +``` | ||
| 41 | + | ||
| 42 | +To use a TrueType font: | ||
| 43 | + | ||
| 44 | +```dart | ||
| 45 | +PDFTTFFont ttf = new PDFTTFFont( | ||
| 46 | + pdf, | ||
| 47 | + (new File("open-sans.ttf").readAsBytesSync() as Uint8List).buffer.asByteData()); | ||
| 48 | +g.setColor(new PDFColor(0.3, 0.3, 0.3)); | ||
| 49 | +g.drawString(ttf, 20.0, "Dart is awesome", 50.0, 30.0); | ||
| 50 | +``` |
| @@ -23,7 +23,6 @@ import 'dart:io'; | @@ -23,7 +23,6 @@ import 'dart:io'; | ||
| 23 | import 'dart:typed_data'; | 23 | import 'dart:typed_data'; |
| 24 | 24 | ||
| 25 | import 'package:meta/meta.dart'; | 25 | import 'package:meta/meta.dart'; |
| 26 | -import 'package:ttf_parser/ttf_parser.dart'; | ||
| 27 | import 'package:vector_math/vector_math_64.dart'; | 26 | import 'package:vector_math/vector_math_64.dart'; |
| 28 | 27 | ||
| 29 | part 'src/annotation.dart'; | 28 | part 'src/annotation.dart'; |
| @@ -50,6 +49,7 @@ part 'src/point.dart'; | @@ -50,6 +49,7 @@ part 'src/point.dart'; | ||
| 50 | part 'src/polygon.dart'; | 49 | part 'src/polygon.dart'; |
| 51 | part 'src/rect.dart'; | 50 | part 'src/rect.dart'; |
| 52 | part 'src/stream.dart'; | 51 | part 'src/stream.dart'; |
| 52 | +part 'src/ttf_parser.dart'; | ||
| 53 | part 'src/ttffont.dart'; | 53 | part 'src/ttffont.dart'; |
| 54 | part 'src/xobject.dart'; | 54 | part 'src/xobject.dart'; |
| 55 | part 'src/xref.dart'; | 55 | part 'src/xref.dart'; |
| @@ -20,11 +20,9 @@ part of pdf; | @@ -20,11 +20,9 @@ part of pdf; | ||
| 20 | 20 | ||
| 21 | class PDFFontDescriptor extends PDFObject { | 21 | class PDFFontDescriptor extends PDFObject { |
| 22 | final PDFObjectStream file; | 22 | final PDFObjectStream file; |
| 23 | - final TtfFont font; | ||
| 24 | final PDFTTFFont ttfFont; | 23 | final PDFTTFFont ttfFont; |
| 25 | 24 | ||
| 26 | - PDFFontDescriptor(this.ttfFont, this.file, this.font) | ||
| 27 | - : super(ttfFont.pdfDocument, "/FontDescriptor"); | 25 | + PDFFontDescriptor(this.ttfFont, this.file) : super(ttfFont.pdfDocument, "/FontDescriptor"); |
| 28 | 26 | ||
| 29 | @override | 27 | @override |
| 30 | void prepare() { | 28 | void prepare() { |
| @@ -34,9 +32,10 @@ class PDFFontDescriptor extends PDFObject { | @@ -34,9 +32,10 @@ class PDFFontDescriptor extends PDFObject { | ||
| 34 | params["/FontFile2"] = file.ref(); | 32 | params["/FontFile2"] = file.ref(); |
| 35 | params["/Flags"] = PDFStream.intNum(32); | 33 | params["/Flags"] = PDFStream.intNum(32); |
| 36 | params["/FontBBox"] = new PDFStream() | 34 | params["/FontBBox"] = new PDFStream() |
| 37 | - ..putStringArray([font.head.xMin, font.head.yMin, font.head.xMax, font.head.yMax]); | ||
| 38 | - params["/Ascent"] = PDFStream.intNum(font.hhea.ascent); | ||
| 39 | - params["/Descent"] = PDFStream.intNum(font.hhea.descent); | 35 | + ..putStringArray( |
| 36 | + [ttfFont.font.xMin, ttfFont.font.yMin, ttfFont.font.xMax, ttfFont.font.yMax]); | ||
| 37 | + params["/Ascent"] = PDFStream.intNum(ttfFont.font.ascent); | ||
| 38 | + params["/Descent"] = PDFStream.intNum(ttfFont.font.descent); | ||
| 40 | params["/ItalicAngle"] = PDFStream.intNum(0); | 39 | params["/ItalicAngle"] = PDFStream.intNum(0); |
| 41 | params["/CapHeight"] = PDFStream.intNum(10); | 40 | params["/CapHeight"] = PDFStream.intNum(10); |
| 42 | params["/StemV"] = PDFStream.intNum(79); | 41 | params["/StemV"] = PDFStream.intNum(79); |
lib/src/ttf_parser.dart
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This library is free software; you can redistribute it and/or | ||
| 5 | + * modify it under the terms of the GNU Lesser General Public | ||
| 6 | + * License as published by the Free Software Foundation; either | ||
| 7 | + * version 2.1 of the License, or (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This library is distributed in the hope that it will be useful, | ||
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | + * Lesser General Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser General Public | ||
| 15 | + * License along with this library; if not, write to the Free Software | ||
| 16 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 17 | + */ | ||
| 18 | + | ||
| 19 | +part of pdf; | ||
| 20 | + | ||
| 21 | +class TTFParser { | ||
| 22 | + static const _HEAD = "head"; | ||
| 23 | + static const _NAME = "name"; | ||
| 24 | + static const _HMTX = "hmtx"; | ||
| 25 | + static const _HHEA = "hhea"; | ||
| 26 | + static const _CMAP = "cmap"; | ||
| 27 | + static const _MAXP = "maxp"; | ||
| 28 | + static const _LOCA = "loca"; | ||
| 29 | + static const _GLYF = "glyf"; | ||
| 30 | + | ||
| 31 | + final ByteData bytes; | ||
| 32 | + final _tableOffsets = new Map<String, int>(); | ||
| 33 | + String _fontName; | ||
| 34 | + final advanceWidth = new List<double>(); | ||
| 35 | + final charToGlyphIndexMap = new Map<int, int>(); | ||
| 36 | + final glyphOffsets = new List<int>(); | ||
| 37 | + final glyphInfoMap = new Map<int, PDFRect>(); | ||
| 38 | + | ||
| 39 | + TTFParser(this.bytes) { | ||
| 40 | + final numTables = bytes.getUint16(4); | ||
| 41 | + | ||
| 42 | + for (var i = 0; i < numTables; i++) { | ||
| 43 | + final name = utf8.decode(bytes.buffer.asUint8List(i * 16 + 12, 4)); | ||
| 44 | + final offset = bytes.getUint32(i * 16 + 20); | ||
| 45 | + _tableOffsets[name] = offset; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + _parseFontName(); | ||
| 49 | + _parseHmtx(); | ||
| 50 | + _parseCMap(); | ||
| 51 | + _parseIndexes(); | ||
| 52 | + _parseGlyf(); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + get unitsPerEm => bytes.getUint16(_tableOffsets[_HEAD] + 18); | ||
| 56 | + | ||
| 57 | + get xMin => bytes.getInt16(_tableOffsets[_HEAD] + 36); | ||
| 58 | + | ||
| 59 | + get yMin => bytes.getInt16(_tableOffsets[_HEAD] + 38); | ||
| 60 | + | ||
| 61 | + get xMax => bytes.getInt16(_tableOffsets[_HEAD] + 40); | ||
| 62 | + | ||
| 63 | + get yMax => bytes.getInt16(_tableOffsets[_HEAD] + 42); | ||
| 64 | + | ||
| 65 | + get indexToLocFormat => bytes.getInt16(_tableOffsets[_HEAD] + 50); | ||
| 66 | + | ||
| 67 | + get ascent => bytes.getInt16(_tableOffsets[_HHEA] + 4); | ||
| 68 | + | ||
| 69 | + get descent => bytes.getInt16(_tableOffsets[_HHEA] + 6); | ||
| 70 | + | ||
| 71 | + get numOfLongHorMetrics => bytes.getInt16(_tableOffsets[_HHEA] + 34); | ||
| 72 | + | ||
| 73 | + get numGlyphs => bytes.getInt16(_tableOffsets[_MAXP] + 4); | ||
| 74 | + | ||
| 75 | + get fontName => _fontName; | ||
| 76 | + | ||
| 77 | + void _parseFontName() { | ||
| 78 | + final basePosition = _tableOffsets[_NAME]; | ||
| 79 | + final count = bytes.getUint16(basePosition + 2); | ||
| 80 | + final stringOffset = bytes.getUint16(basePosition + 4); | ||
| 81 | + int pos = basePosition + 6; | ||
| 82 | + for (var i = 0; i < count; i++) { | ||
| 83 | + int platformID = bytes.getUint16(pos); | ||
| 84 | + int nameID = bytes.getUint16(pos + 6); | ||
| 85 | + int length = bytes.getUint16(pos + 8); | ||
| 86 | + int offset = bytes.getUint16(pos + 10); | ||
| 87 | + pos += 12; | ||
| 88 | + if (platformID == 1 && nameID == 6) { | ||
| 89 | + _fontName = utf8 | ||
| 90 | + .decode(bytes.buffer.asUint8List(basePosition + stringOffset + offset, length)); | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + void _parseHmtx() { | ||
| 96 | + final offset = _tableOffsets[_HMTX]; | ||
| 97 | + final unitsPerEm = this.unitsPerEm; | ||
| 98 | + for (var i = 0; i < numOfLongHorMetrics; i++) { | ||
| 99 | + advanceWidth.add(bytes.getInt16(offset + i * 4).toDouble() / unitsPerEm); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + void _parseCMap() { | ||
| 104 | + final basePosition = _tableOffsets[_CMAP]; | ||
| 105 | + final numSubTables = bytes.getUint16(basePosition + 2); | ||
| 106 | + for (var i = 0; i < numSubTables; i++) { | ||
| 107 | + final offset = bytes.getUint32(basePosition + i * 8 + 8); | ||
| 108 | + final format = bytes.getUint16(basePosition + offset); | ||
| 109 | + final length = bytes.getUint16(basePosition + offset + 2); | ||
| 110 | + | ||
| 111 | + switch (format) { | ||
| 112 | + case 0: | ||
| 113 | + _parseCMapFormat0(basePosition + offset + 4, length); | ||
| 114 | + break; | ||
| 115 | + | ||
| 116 | + case 4: | ||
| 117 | + _parseCMapFormat4(basePosition + offset + 4, length); | ||
| 118 | + break; | ||
| 119 | + case 6: | ||
| 120 | + _parseCMapFormat6(basePosition + offset + 4, length); | ||
| 121 | + break; | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + void _parseCMapFormat0(int basePosition, int length) { | ||
| 127 | + assert(length == 262); | ||
| 128 | + for (var i = 0; i < 256; i++) { | ||
| 129 | + int charCode = i; | ||
| 130 | + int glyphIndex = bytes.getUint8(basePosition + i); | ||
| 131 | + if (glyphIndex > 0) { | ||
| 132 | + charToGlyphIndexMap[charCode] = glyphIndex; | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + void _parseCMapFormat4(int basePosition, int length) { | ||
| 138 | + final segCount = bytes.getUint16(basePosition + 2) ~/ 2; | ||
| 139 | + final endCodes = new List<int>(); | ||
| 140 | + for (var i = 0; i < segCount; i++) { | ||
| 141 | + endCodes.add(bytes.getUint16(basePosition + i * 2 + 10)); | ||
| 142 | + } | ||
| 143 | + final startCodes = new List<int>(); | ||
| 144 | + for (var i = 0; i < segCount; i++) { | ||
| 145 | + startCodes.add(bytes.getUint16(basePosition + (segCount + i) * 2 + 12)); | ||
| 146 | + } | ||
| 147 | + final idDeltas = new List<int>(); | ||
| 148 | + for (var i = 0; i < segCount; i++) { | ||
| 149 | + idDeltas.add(bytes.getUint16(basePosition + (segCount * 2 + i) * 2 + 12)); | ||
| 150 | + } | ||
| 151 | + final idRangeOffsetBasePos = basePosition + segCount * 6 + 12; | ||
| 152 | + final idRangeOffsets = new List<int>(); | ||
| 153 | + for (var i = 0; i < segCount; i++) { | ||
| 154 | + idRangeOffsets.add(bytes.getUint16(idRangeOffsetBasePos + i * 2)); | ||
| 155 | + } | ||
| 156 | + for (var s = 0; s < segCount - 1; s++) { | ||
| 157 | + final startCode = startCodes[s]; | ||
| 158 | + final endCode = endCodes[s]; | ||
| 159 | + final idDelta = idDeltas[s]; | ||
| 160 | + final idRangeOffset = idRangeOffsets[s]; | ||
| 161 | + final idRangeOffsetAddress = idRangeOffsetBasePos + s * 2; | ||
| 162 | + for (var c = startCode; c <= endCode; c++) { | ||
| 163 | + var glyphIndex; | ||
| 164 | + if (idRangeOffset == 0) { | ||
| 165 | + glyphIndex = (idDelta + c) % 65536; | ||
| 166 | + } else { | ||
| 167 | + final glyphIndexAddress = idRangeOffset + 2 * (c - startCode) + idRangeOffsetAddress; | ||
| 168 | + glyphIndex = bytes.getUint16(glyphIndexAddress); | ||
| 169 | + } | ||
| 170 | + charToGlyphIndexMap[c] = glyphIndex; | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + void _parseCMapFormat6(int basePosition, int length) { | ||
| 176 | + final firstCode = bytes.getUint16(basePosition + 2); | ||
| 177 | + final entryCount = bytes.getUint16(basePosition + 4); | ||
| 178 | + for (var i = 0; i < entryCount; i++) { | ||
| 179 | + final charCode = firstCode + i; | ||
| 180 | + final glyphIndex = bytes.getUint16(basePosition + i * 2 + 6); | ||
| 181 | + if (glyphIndex > 0) { | ||
| 182 | + charToGlyphIndexMap[charCode] = glyphIndex; | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + void _parseIndexes() { | ||
| 188 | + final basePosition = _tableOffsets[_LOCA]; | ||
| 189 | + final numGlyphs = this.numGlyphs; | ||
| 190 | + if (indexToLocFormat == 0) { | ||
| 191 | + for (var i = 0; i < numGlyphs; i++) { | ||
| 192 | + glyphOffsets.add(bytes.getUint16(basePosition + i * 2) * 2); | ||
| 193 | + } | ||
| 194 | + } else { | ||
| 195 | + for (var i = 0; i < numGlyphs; i++) { | ||
| 196 | + glyphOffsets.add(bytes.getUint32(basePosition + i * 4)); | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + void _parseGlyf() { | ||
| 202 | + final baseOffset = _tableOffsets[_GLYF]; | ||
| 203 | + final unitsPerEm = this.unitsPerEm; | ||
| 204 | + int glyphIndex = 0; | ||
| 205 | + for (var offset in glyphOffsets) { | ||
| 206 | + final xMin = bytes.getInt16(baseOffset + offset + 2); // 2 | ||
| 207 | + final yMin = bytes.getInt16(baseOffset + offset + 4); // 4 | ||
| 208 | + final xMax = bytes.getInt16(baseOffset + offset + 6); // 6 | ||
| 209 | + final yMax = bytes.getInt16(baseOffset + offset + 8); // 8 | ||
| 210 | + glyphInfoMap[glyphIndex] = new PDFRect( | ||
| 211 | + xMin.toDouble() / unitsPerEm, | ||
| 212 | + yMin.toDouble() / unitsPerEm, | ||
| 213 | + xMax.toDouble() / unitsPerEm, | ||
| 214 | + yMax.toDouble() / unitsPerEm); | ||
| 215 | + glyphIndex++; | ||
| 216 | + } | ||
| 217 | + } | ||
| 218 | +} |
| @@ -23,19 +23,20 @@ class PDFTTFFont extends PDFFont { | @@ -23,19 +23,20 @@ class PDFTTFFont extends PDFFont { | ||
| 23 | PDFFontDescriptor descriptor; | 23 | PDFFontDescriptor descriptor; |
| 24 | PDFArrayObject widthsObject; | 24 | PDFArrayObject widthsObject; |
| 25 | final widths = new List<String>(); | 25 | final widths = new List<String>(); |
| 26 | - TtfFont _font; | 26 | + final TTFParser font; |
| 27 | int _charMin; | 27 | int _charMin; |
| 28 | int _charMax; | 28 | int _charMax; |
| 29 | 29 | ||
| 30 | /// Constructs a PDFTTFFont | 30 | /// Constructs a PDFTTFFont |
| 31 | - PDFTTFFont(PDFDocument pdfDocument, Uint8List bytes) | ||
| 32 | - : super(pdfDocument, subtype: "/TrueType") { | ||
| 33 | - _font = new TtfParser().parse(bytes); | ||
| 34 | - baseFont = "/" + _font.name.fontName.replaceAll(" ", ""); | 31 | + PDFTTFFont(PDFDocument pdfDocument, ByteData bytes) |
| 32 | + : font = new TTFParser(bytes), | ||
| 33 | + super(pdfDocument, subtype: "/TrueType") { | ||
| 34 | + baseFont = "/" + font.fontName.replaceAll(" ", ""); | ||
| 35 | 35 | ||
| 36 | PDFObjectStream file = new PDFObjectStream(pdfDocument, isBinary: true); | 36 | PDFObjectStream file = new PDFObjectStream(pdfDocument, isBinary: true); |
| 37 | - file.buf.putBytes(bytes); | ||
| 38 | - file.params["/Length1"] = PDFStream.intNum(bytes.length); | 37 | + final data = bytes.buffer.asUint8List(); |
| 38 | + file.buf.putBytes(data); | ||
| 39 | + file.params["/Length1"] = PDFStream.intNum(data.length); | ||
| 39 | 40 | ||
| 40 | _charMin = 32; | 41 | _charMin = 32; |
| 41 | _charMax = 255; | 42 | _charMax = 255; |
| @@ -45,35 +46,30 @@ class PDFTTFFont extends PDFFont { | @@ -45,35 +46,30 @@ class PDFTTFFont extends PDFFont { | ||
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | unicodeCMap = new PDFObject(pdfDocument); | 48 | unicodeCMap = new PDFObject(pdfDocument); |
| 48 | - descriptor = new PDFFontDescriptor(this, file, _font); | 49 | + descriptor = new PDFFontDescriptor(this, file); |
| 49 | widthsObject = new PDFArrayObject(pdfDocument, widths); | 50 | widthsObject = new PDFArrayObject(pdfDocument, widths); |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | @override | 53 | @override |
| 53 | double glyphAdvance(int charCode) { | 54 | double glyphAdvance(int charCode) { |
| 54 | - var g = _font.cmap.charToGlyphIndexMap[charCode]; | 55 | + var g = font.charToGlyphIndexMap[charCode]; |
| 55 | 56 | ||
| 56 | if (g == null) { | 57 | if (g == null) { |
| 57 | return super.glyphAdvance(charCode); | 58 | return super.glyphAdvance(charCode); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | - return _font.hmtx.metrics[g].advanceWidth / _font.head.unitsPerEm; | 61 | + return (font.advanceWidth[g]) ?? super.glyphAdvance(charCode); |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | @override | 64 | @override |
| 64 | PDFRect glyphBounds(int charCode) { | 65 | PDFRect glyphBounds(int charCode) { |
| 65 | - var g = _font.cmap.charToGlyphIndexMap[charCode]; | 66 | + var g = font.charToGlyphIndexMap[charCode]; |
| 66 | 67 | ||
| 67 | if (g == null) { | 68 | if (g == null) { |
| 68 | return super.glyphBounds(charCode); | 69 | return super.glyphBounds(charCode); |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | - var info = _font.glyf.glyphInfoMap[g]; | ||
| 72 | - return new PDFRect( | ||
| 73 | - info.xMin.toDouble() / _font.head.unitsPerEm, | ||
| 74 | - info.yMin.toDouble() / _font.head.unitsPerEm, | ||
| 75 | - (info.xMax - info.xMin).toDouble() / _font.head.unitsPerEm, | ||
| 76 | - (info.yMax - info.yMin).toDouble() / _font.head.unitsPerEm); | 72 | + return font.glyphInfoMap[g] ?? super.glyphBounds(charCode); |
| 77 | } | 73 | } |
| 78 | 74 | ||
| 79 | @override | 75 | @override |
| @@ -2,14 +2,13 @@ name: pdf | @@ -2,14 +2,13 @@ name: pdf | ||
| 2 | author: David PHAM-VAN <dev.nfet.net@gmail.com> | 2 | author: David PHAM-VAN <dev.nfet.net@gmail.com> |
| 3 | description: A pdf producer for Dart. It can create pdf files for both web or flutter. | 3 | description: A pdf producer for Dart. It can create pdf files for both web or flutter. |
| 4 | homepage: https://github.com/davbfr/dart_pdf | 4 | homepage: https://github.com/davbfr/dart_pdf |
| 5 | -version: 1.0.2 | 5 | +version: 1.0.3 |
| 6 | 6 | ||
| 7 | environment: | 7 | environment: |
| 8 | sdk: ">=1.8.0 <3.0.0" | 8 | sdk: ">=1.8.0 <3.0.0" |
| 9 | 9 | ||
| 10 | dependencies: | 10 | dependencies: |
| 11 | meta: "^1.1.5" | 11 | meta: "^1.1.5" |
| 12 | - ttf_parser: "^1.0.0" | ||
| 13 | vector_math: "^2.0.0" | 12 | vector_math: "^2.0.0" |
| 14 | 13 | ||
| 15 | dev_dependencies: | 14 | dev_dependencies: |
| 1 | import 'dart:io'; | 1 | import 'dart:io'; |
| 2 | import 'dart:math'; | 2 | import 'dart:math'; |
| 3 | +import 'dart:typed_data'; | ||
| 3 | 4 | ||
| 4 | import 'package:pdf/pdf.dart'; | 5 | import 'package:pdf/pdf.dart'; |
| 5 | import 'package:test/test.dart'; | 6 | import 'package:test/test.dart'; |
| @@ -27,8 +28,11 @@ void main() { | @@ -27,8 +28,11 @@ void main() { | ||
| 27 | g.restoreContext(); | 28 | g.restoreContext(); |
| 28 | var font1 = new PDFFont(pdf); | 29 | var font1 = new PDFFont(pdf); |
| 29 | 30 | ||
| 30 | - var font2 = | ||
| 31 | - new PDFTTFFont(pdf, new File("../assets/Nunito-Regular.ttf").readAsBytesSync()); | 31 | + var font2 = new PDFTTFFont( |
| 32 | + pdf, | ||
| 33 | + (new File("../assets/Nunito-Regular.ttf").readAsBytesSync() as Uint8List) | ||
| 34 | + .buffer | ||
| 35 | + .asByteData()); | ||
| 32 | var s = "Hello World!"; | 36 | var s = "Hello World!"; |
| 33 | var r = font2.stringBounds(s); | 37 | var r = font2.stringBounds(s); |
| 34 | const FS = 20.0; | 38 | const FS = 20.0; |
test/ttf_test.dart
0 → 100644
| 1 | +import 'dart:io'; | ||
| 2 | +import 'dart:typed_data'; | ||
| 3 | + | ||
| 4 | +import 'package:pdf/pdf.dart'; | ||
| 5 | +import 'package:test/test.dart'; | ||
| 6 | + | ||
| 7 | +void main() { | ||
| 8 | + test('Pdf', () { | ||
| 9 | + var pdf = new PDFDocument(deflate: false); | ||
| 10 | + var i = pdf.info; | ||
| 11 | + i.author = "David PHAM-VAN"; | ||
| 12 | + i.creator = i.author; | ||
| 13 | + i.title = "My Title"; | ||
| 14 | + i.subject = "My Subject"; | ||
| 15 | + var page = new PDFPage(pdf, pageFormat: const PDFPageFormat(500.0, 300.0)); | ||
| 16 | + | ||
| 17 | + var g = page.getGraphics(); | ||
| 18 | + var ttf = new PDFTTFFont( | ||
| 19 | + pdf, | ||
| 20 | + (new File("../assets/Nunito-Regular.ttf").readAsBytesSync() as Uint8List) | ||
| 21 | + .buffer | ||
| 22 | + .asByteData()); | ||
| 23 | + var s = "Hello World!"; | ||
| 24 | + var r = ttf.stringBounds(s); | ||
| 25 | + print(r); | ||
| 26 | + const FS = 20.0; | ||
| 27 | + g.setColor(new PDFColor(0.0, 1.0, 1.0)); | ||
| 28 | + g.drawRect(50.0 + r.x * FS, 30.0 + r.y * FS, r.w * FS, r.h * FS); | ||
| 29 | + g.fillPath(); | ||
| 30 | + g.setColor(new PDFColor(0.3, 0.3, 0.3)); | ||
| 31 | + g.drawString(ttf, FS, s, 50.0, 30.0); | ||
| 32 | + | ||
| 33 | + var file = new File('file.pdf'); | ||
| 34 | + file.writeAsBytesSync(pdf.save()); | ||
| 35 | + }); | ||
| 36 | +} |
-
Please register or login to post a comment