Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
dart_pdf
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
David PHAM-VAN
2018-08-11 06:48:10 -0400
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
c11fb7e86afc07dd8be8bcf275c5fc01eb394a10
c11fb7e8
1 parent
c6d0636a
Uses an internal TTF parser
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
318 additions
and
30 deletions
.gitignore
CHANGELOG.md
README.md
lib/pdf.dart
lib/src/font_descriptor.dart
lib/src/ttf_parser.dart
lib/src/ttffont.dart
pubspec.yaml
test/pdf_test.dart
test/ttf_test.dart
.gitignore
View file @
c11fb7e
...
...
@@ -9,3 +9,5 @@ pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
*.pdf
...
...
CHANGELOG.md
View file @
c11fb7e
# 1.0.3
*
Remove dependency to ttf_parser
# 1.0.2
*
Update sdk support for 2.0.0
# 1.0.1
*
Add example
*
Lower vector_math dependency version
*
Uses better page format object
...
...
README.md
View file @
c11fb7e
# Pdf creation library for dart / flutter
This is a low-level Pdf creation library.
It can create a full multi-pages document with graphics,
images and text using TrueType fonts.
The coordinate system is using the internal Pdf system:
*
(0.0, 0.0) is bottom-left
*
1.0 is defined as 1 / 72.0 inch
*
you can use the constants for centimeters, milimeters and inch defined in PDFPageFormat
Example:
```
dart
final
pdf
=
new
PDFDocument
();
...
...
@@ -12,8 +21,30 @@ g.drawRect(50.0, 30.0, 100.0, 50.0);
g
.
fillPath
();
g
.
setColor
(
new
PDFColor
(
0.3
,
0.3
,
0.3
));
g
.
drawString
(
font
,
12.0
,
"Hello World!"
,
5
0.0
,
300.0
);
g
.
drawString
(
font
,
12.0
,
"Hello World!"
,
5
.0
*
PDFPageFormat
.
MM
,
300.0
);
var
file
=
new
File
(
'file.pdf'
);
file
.
writeAsBytesSync
(
pdf
.
save
());
```
To load an image it is possible to use the dart library
`image`
```
dart
Image
image
=
decodeImage
(
new
Io
.
File
(
'test.webp'
).
readAsBytesSync
());
PDFImage
image
=
new
PDFImage
(
pdf
,
image:
img
.
data
.
buffer
.
asUint8List
(),
width:
img
.
width
,
height:
img
.
height
);
g
.
drawImage
(
image
,
100.0
,
100.0
,
80.0
);
```
To use a TrueType font:
```
dart
PDFTTFFont
ttf
=
new
PDFTTFFont
(
pdf
,
(
new
File
(
"open-sans.ttf"
).
readAsBytesSync
()
as
Uint8List
).
buffer
.
asByteData
());
g
.
setColor
(
new
PDFColor
(
0.3
,
0.3
,
0.3
));
g
.
drawString
(
ttf
,
20.0
,
"Dart is awesome"
,
50.0
,
30.0
);
```
...
...
lib/pdf.dart
View file @
c11fb7e
...
...
@@ -23,7 +23,6 @@ import 'dart:io';
import
'dart:typed_data'
;
import
'package:meta/meta.dart'
;
import
'package:ttf_parser/ttf_parser.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
part
'src/annotation.dart'
;
...
...
@@ -50,6 +49,7 @@ part 'src/point.dart';
part
'src/polygon.dart'
;
part
'src/rect.dart'
;
part
'src/stream.dart'
;
part
'src/ttf_parser.dart'
;
part
'src/ttffont.dart'
;
part
'src/xobject.dart'
;
part
'src/xref.dart'
;
...
...
lib/src/font_descriptor.dart
View file @
c11fb7e
...
...
@@ -20,11 +20,9 @@ part of pdf;
class
PDFFontDescriptor
extends
PDFObject
{
final
PDFObjectStream
file
;
final
TtfFont
font
;
final
PDFTTFFont
ttfFont
;
PDFFontDescriptor
(
this
.
ttfFont
,
this
.
file
,
this
.
font
)
:
super
(
ttfFont
.
pdfDocument
,
"/FontDescriptor"
);
PDFFontDescriptor
(
this
.
ttfFont
,
this
.
file
)
:
super
(
ttfFont
.
pdfDocument
,
"/FontDescriptor"
);
@override
void
prepare
()
{
...
...
@@ -34,9 +32,10 @@ class PDFFontDescriptor extends PDFObject {
params
[
"/FontFile2"
]
=
file
.
ref
();
params
[
"/Flags"
]
=
PDFStream
.
intNum
(
32
);
params
[
"/FontBBox"
]
=
new
PDFStream
()
..
putStringArray
([
font
.
head
.
xMin
,
font
.
head
.
yMin
,
font
.
head
.
xMax
,
font
.
head
.
yMax
]);
params
[
"/Ascent"
]
=
PDFStream
.
intNum
(
font
.
hhea
.
ascent
);
params
[
"/Descent"
]
=
PDFStream
.
intNum
(
font
.
hhea
.
descent
);
..
putStringArray
(
[
ttfFont
.
font
.
xMin
,
ttfFont
.
font
.
yMin
,
ttfFont
.
font
.
xMax
,
ttfFont
.
font
.
yMax
]);
params
[
"/Ascent"
]
=
PDFStream
.
intNum
(
ttfFont
.
font
.
ascent
);
params
[
"/Descent"
]
=
PDFStream
.
intNum
(
ttfFont
.
font
.
descent
);
params
[
"/ItalicAngle"
]
=
PDFStream
.
intNum
(
0
);
params
[
"/CapHeight"
]
=
PDFStream
.
intNum
(
10
);
params
[
"/StemV"
]
=
PDFStream
.
intNum
(
79
);
...
...
lib/src/ttf_parser.dart
0 → 100644
View file @
c11fb7e
/*
* Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
part of
pdf
;
class
TTFParser
{
static
const
_HEAD
=
"head"
;
static
const
_NAME
=
"name"
;
static
const
_HMTX
=
"hmtx"
;
static
const
_HHEA
=
"hhea"
;
static
const
_CMAP
=
"cmap"
;
static
const
_MAXP
=
"maxp"
;
static
const
_LOCA
=
"loca"
;
static
const
_GLYF
=
"glyf"
;
final
ByteData
bytes
;
final
_tableOffsets
=
new
Map
<
String
,
int
>();
String
_fontName
;
final
advanceWidth
=
new
List
<
double
>();
final
charToGlyphIndexMap
=
new
Map
<
int
,
int
>();
final
glyphOffsets
=
new
List
<
int
>();
final
glyphInfoMap
=
new
Map
<
int
,
PDFRect
>();
TTFParser
(
this
.
bytes
)
{
final
numTables
=
bytes
.
getUint16
(
4
);
for
(
var
i
=
0
;
i
<
numTables
;
i
++)
{
final
name
=
utf8
.
decode
(
bytes
.
buffer
.
asUint8List
(
i
*
16
+
12
,
4
));
final
offset
=
bytes
.
getUint32
(
i
*
16
+
20
);
_tableOffsets
[
name
]
=
offset
;
}
_parseFontName
();
_parseHmtx
();
_parseCMap
();
_parseIndexes
();
_parseGlyf
();
}
get
unitsPerEm
=>
bytes
.
getUint16
(
_tableOffsets
[
_HEAD
]
+
18
);
get
xMin
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
36
);
get
yMin
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
38
);
get
xMax
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
40
);
get
yMax
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
42
);
get
indexToLocFormat
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
50
);
get
ascent
=>
bytes
.
getInt16
(
_tableOffsets
[
_HHEA
]
+
4
);
get
descent
=>
bytes
.
getInt16
(
_tableOffsets
[
_HHEA
]
+
6
);
get
numOfLongHorMetrics
=>
bytes
.
getInt16
(
_tableOffsets
[
_HHEA
]
+
34
);
get
numGlyphs
=>
bytes
.
getInt16
(
_tableOffsets
[
_MAXP
]
+
4
);
get
fontName
=>
_fontName
;
void
_parseFontName
()
{
final
basePosition
=
_tableOffsets
[
_NAME
];
final
count
=
bytes
.
getUint16
(
basePosition
+
2
);
final
stringOffset
=
bytes
.
getUint16
(
basePosition
+
4
);
int
pos
=
basePosition
+
6
;
for
(
var
i
=
0
;
i
<
count
;
i
++)
{
int
platformID
=
bytes
.
getUint16
(
pos
);
int
nameID
=
bytes
.
getUint16
(
pos
+
6
);
int
length
=
bytes
.
getUint16
(
pos
+
8
);
int
offset
=
bytes
.
getUint16
(
pos
+
10
);
pos
+=
12
;
if
(
platformID
==
1
&&
nameID
==
6
)
{
_fontName
=
utf8
.
decode
(
bytes
.
buffer
.
asUint8List
(
basePosition
+
stringOffset
+
offset
,
length
));
}
}
}
void
_parseHmtx
()
{
final
offset
=
_tableOffsets
[
_HMTX
];
final
unitsPerEm
=
this
.
unitsPerEm
;
for
(
var
i
=
0
;
i
<
numOfLongHorMetrics
;
i
++)
{
advanceWidth
.
add
(
bytes
.
getInt16
(
offset
+
i
*
4
).
toDouble
()
/
unitsPerEm
);
}
}
void
_parseCMap
()
{
final
basePosition
=
_tableOffsets
[
_CMAP
];
final
numSubTables
=
bytes
.
getUint16
(
basePosition
+
2
);
for
(
var
i
=
0
;
i
<
numSubTables
;
i
++)
{
final
offset
=
bytes
.
getUint32
(
basePosition
+
i
*
8
+
8
);
final
format
=
bytes
.
getUint16
(
basePosition
+
offset
);
final
length
=
bytes
.
getUint16
(
basePosition
+
offset
+
2
);
switch
(
format
)
{
case
0
:
_parseCMapFormat0
(
basePosition
+
offset
+
4
,
length
);
break
;
case
4
:
_parseCMapFormat4
(
basePosition
+
offset
+
4
,
length
);
break
;
case
6
:
_parseCMapFormat6
(
basePosition
+
offset
+
4
,
length
);
break
;
}
}
}
void
_parseCMapFormat0
(
int
basePosition
,
int
length
)
{
assert
(
length
==
262
);
for
(
var
i
=
0
;
i
<
256
;
i
++)
{
int
charCode
=
i
;
int
glyphIndex
=
bytes
.
getUint8
(
basePosition
+
i
);
if
(
glyphIndex
>
0
)
{
charToGlyphIndexMap
[
charCode
]
=
glyphIndex
;
}
}
}
void
_parseCMapFormat4
(
int
basePosition
,
int
length
)
{
final
segCount
=
bytes
.
getUint16
(
basePosition
+
2
)
~/
2
;
final
endCodes
=
new
List
<
int
>();
for
(
var
i
=
0
;
i
<
segCount
;
i
++)
{
endCodes
.
add
(
bytes
.
getUint16
(
basePosition
+
i
*
2
+
10
));
}
final
startCodes
=
new
List
<
int
>();
for
(
var
i
=
0
;
i
<
segCount
;
i
++)
{
startCodes
.
add
(
bytes
.
getUint16
(
basePosition
+
(
segCount
+
i
)
*
2
+
12
));
}
final
idDeltas
=
new
List
<
int
>();
for
(
var
i
=
0
;
i
<
segCount
;
i
++)
{
idDeltas
.
add
(
bytes
.
getUint16
(
basePosition
+
(
segCount
*
2
+
i
)
*
2
+
12
));
}
final
idRangeOffsetBasePos
=
basePosition
+
segCount
*
6
+
12
;
final
idRangeOffsets
=
new
List
<
int
>();
for
(
var
i
=
0
;
i
<
segCount
;
i
++)
{
idRangeOffsets
.
add
(
bytes
.
getUint16
(
idRangeOffsetBasePos
+
i
*
2
));
}
for
(
var
s
=
0
;
s
<
segCount
-
1
;
s
++)
{
final
startCode
=
startCodes
[
s
];
final
endCode
=
endCodes
[
s
];
final
idDelta
=
idDeltas
[
s
];
final
idRangeOffset
=
idRangeOffsets
[
s
];
final
idRangeOffsetAddress
=
idRangeOffsetBasePos
+
s
*
2
;
for
(
var
c
=
startCode
;
c
<=
endCode
;
c
++)
{
var
glyphIndex
;
if
(
idRangeOffset
==
0
)
{
glyphIndex
=
(
idDelta
+
c
)
%
65536
;
}
else
{
final
glyphIndexAddress
=
idRangeOffset
+
2
*
(
c
-
startCode
)
+
idRangeOffsetAddress
;
glyphIndex
=
bytes
.
getUint16
(
glyphIndexAddress
);
}
charToGlyphIndexMap
[
c
]
=
glyphIndex
;
}
}
}
void
_parseCMapFormat6
(
int
basePosition
,
int
length
)
{
final
firstCode
=
bytes
.
getUint16
(
basePosition
+
2
);
final
entryCount
=
bytes
.
getUint16
(
basePosition
+
4
);
for
(
var
i
=
0
;
i
<
entryCount
;
i
++)
{
final
charCode
=
firstCode
+
i
;
final
glyphIndex
=
bytes
.
getUint16
(
basePosition
+
i
*
2
+
6
);
if
(
glyphIndex
>
0
)
{
charToGlyphIndexMap
[
charCode
]
=
glyphIndex
;
}
}
}
void
_parseIndexes
()
{
final
basePosition
=
_tableOffsets
[
_LOCA
];
final
numGlyphs
=
this
.
numGlyphs
;
if
(
indexToLocFormat
==
0
)
{
for
(
var
i
=
0
;
i
<
numGlyphs
;
i
++)
{
glyphOffsets
.
add
(
bytes
.
getUint16
(
basePosition
+
i
*
2
)
*
2
);
}
}
else
{
for
(
var
i
=
0
;
i
<
numGlyphs
;
i
++)
{
glyphOffsets
.
add
(
bytes
.
getUint32
(
basePosition
+
i
*
4
));
}
}
}
void
_parseGlyf
()
{
final
baseOffset
=
_tableOffsets
[
_GLYF
];
final
unitsPerEm
=
this
.
unitsPerEm
;
int
glyphIndex
=
0
;
for
(
var
offset
in
glyphOffsets
)
{
final
xMin
=
bytes
.
getInt16
(
baseOffset
+
offset
+
2
);
// 2
final
yMin
=
bytes
.
getInt16
(
baseOffset
+
offset
+
4
);
// 4
final
xMax
=
bytes
.
getInt16
(
baseOffset
+
offset
+
6
);
// 6
final
yMax
=
bytes
.
getInt16
(
baseOffset
+
offset
+
8
);
// 8
glyphInfoMap
[
glyphIndex
]
=
new
PDFRect
(
xMin
.
toDouble
()
/
unitsPerEm
,
yMin
.
toDouble
()
/
unitsPerEm
,
xMax
.
toDouble
()
/
unitsPerEm
,
yMax
.
toDouble
()
/
unitsPerEm
);
glyphIndex
++;
}
}
}
...
...
lib/src/ttffont.dart
View file @
c11fb7e
...
...
@@ -23,19 +23,20 @@ class PDFTTFFont extends PDFFont {
PDFFontDescriptor
descriptor
;
PDFArrayObject
widthsObject
;
final
widths
=
new
List
<
String
>();
TtfFont
_
font
;
final
TTFParser
font
;
int
_charMin
;
int
_charMax
;
/// Constructs a PDFTTFFont
PDFTTFFont
(
PDFDocument
pdfDocument
,
Uint8List
bytes
)
:
super
(
pdfDocument
,
subtype:
"/TrueType"
)
{
_font
=
new
TtfParser
().
parse
(
bytes
);
baseFont
=
"/"
+
_font
.
name
.
fontName
.
replaceAll
(
" "
,
""
);
PDFTTFFont
(
PDFDocument
pdfDocument
,
ByteData
bytes
)
:
font
=
new
TTFParser
(
bytes
),
super
(
pdfDocument
,
subtype:
"/TrueType"
)
{
baseFont
=
"/"
+
font
.
fontName
.
replaceAll
(
" "
,
""
);
PDFObjectStream
file
=
new
PDFObjectStream
(
pdfDocument
,
isBinary:
true
);
file
.
buf
.
putBytes
(
bytes
);
file
.
params
[
"/Length1"
]
=
PDFStream
.
intNum
(
bytes
.
length
);
final
data
=
bytes
.
buffer
.
asUint8List
();
file
.
buf
.
putBytes
(
data
);
file
.
params
[
"/Length1"
]
=
PDFStream
.
intNum
(
data
.
length
);
_charMin
=
32
;
_charMax
=
255
;
...
...
@@ -45,35 +46,30 @@ class PDFTTFFont extends PDFFont {
}
unicodeCMap
=
new
PDFObject
(
pdfDocument
);
descriptor
=
new
PDFFontDescriptor
(
this
,
file
,
_font
);
descriptor
=
new
PDFFontDescriptor
(
this
,
file
);
widthsObject
=
new
PDFArrayObject
(
pdfDocument
,
widths
);
}
@override
double
glyphAdvance
(
int
charCode
)
{
var
g
=
_font
.
cmap
.
charToGlyphIndexMap
[
charCode
];
var
g
=
font
.
charToGlyphIndexMap
[
charCode
];
if
(
g
==
null
)
{
return
super
.
glyphAdvance
(
charCode
);
}
return
_font
.
hmtx
.
metrics
[
g
].
advanceWidth
/
_font
.
head
.
unitsPerEm
;
return
(
font
.
advanceWidth
[
g
])
??
super
.
glyphAdvance
(
charCode
)
;
}
@override
PDFRect
glyphBounds
(
int
charCode
)
{
var
g
=
_font
.
cmap
.
charToGlyphIndexMap
[
charCode
];
var
g
=
font
.
charToGlyphIndexMap
[
charCode
];
if
(
g
==
null
)
{
return
super
.
glyphBounds
(
charCode
);
}
var
info
=
_font
.
glyf
.
glyphInfoMap
[
g
];
return
new
PDFRect
(
info
.
xMin
.
toDouble
()
/
_font
.
head
.
unitsPerEm
,
info
.
yMin
.
toDouble
()
/
_font
.
head
.
unitsPerEm
,
(
info
.
xMax
-
info
.
xMin
).
toDouble
()
/
_font
.
head
.
unitsPerEm
,
(
info
.
yMax
-
info
.
yMin
).
toDouble
()
/
_font
.
head
.
unitsPerEm
);
return
font
.
glyphInfoMap
[
g
]
??
super
.
glyphBounds
(
charCode
);
}
@override
...
...
pubspec.yaml
View file @
c11fb7e
...
...
@@ -2,14 +2,13 @@ name: pdf
author
:
David PHAM-VAN <dev.nfet.net@gmail.com>
description
:
A pdf producer for Dart. It can create pdf files for both web or flutter.
homepage
:
https://github.com/davbfr/dart_pdf
version
:
1.0.
2
version
:
1.0.
3
environment
:
sdk
:
"
>=1.8.0
<3.0.0"
dependencies
:
meta
:
"
^1.1.5"
ttf_parser
:
"
^1.0.0"
vector_math
:
"
^2.0.0"
dev_dependencies
:
...
...
test/pdf_test.dart
View file @
c11fb7e
import
'dart:io'
;
import
'dart:math'
;
import
'dart:typed_data'
;
import
'package:pdf/pdf.dart'
;
import
'package:test/test.dart'
;
...
...
@@ -27,8 +28,11 @@ void main() {
g
.
restoreContext
();
var
font1
=
new
PDFFont
(
pdf
);
var
font2
=
new
PDFTTFFont
(
pdf
,
new
File
(
"../assets/Nunito-Regular.ttf"
).
readAsBytesSync
());
var
font2
=
new
PDFTTFFont
(
pdf
,
(
new
File
(
"../assets/Nunito-Regular.ttf"
).
readAsBytesSync
()
as
Uint8List
)
.
buffer
.
asByteData
());
var
s
=
"Hello World!"
;
var
r
=
font2
.
stringBounds
(
s
);
const
FS
=
20.0
;
...
...
test/ttf_test.dart
0 → 100644
View file @
c11fb7e
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:pdf/pdf.dart'
;
import
'package:test/test.dart'
;
void
main
(
)
{
test
(
'Pdf'
,
()
{
var
pdf
=
new
PDFDocument
(
deflate:
false
);
var
i
=
pdf
.
info
;
i
.
author
=
"David PHAM-VAN"
;
i
.
creator
=
i
.
author
;
i
.
title
=
"My Title"
;
i
.
subject
=
"My Subject"
;
var
page
=
new
PDFPage
(
pdf
,
pageFormat:
const
PDFPageFormat
(
500.0
,
300.0
));
var
g
=
page
.
getGraphics
();
var
ttf
=
new
PDFTTFFont
(
pdf
,
(
new
File
(
"../assets/Nunito-Regular.ttf"
).
readAsBytesSync
()
as
Uint8List
)
.
buffer
.
asByteData
());
var
s
=
"Hello World!"
;
var
r
=
ttf
.
stringBounds
(
s
);
print
(
r
);
const
FS
=
20.0
;
g
.
setColor
(
new
PDFColor
(
0.0
,
1.0
,
1.0
));
g
.
drawRect
(
50.0
+
r
.
x
*
FS
,
30.0
+
r
.
y
*
FS
,
r
.
w
*
FS
,
r
.
h
*
FS
);
g
.
fillPath
();
g
.
setColor
(
new
PDFColor
(
0.3
,
0.3
,
0.3
));
g
.
drawString
(
ttf
,
FS
,
s
,
50.0
,
30.0
);
var
file
=
new
File
(
'file.pdf'
);
file
.
writeAsBytesSync
(
pdf
.
save
());
});
}
...
...
Please
register
or
login
to post a comment