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-11-29 08:56:10 -0500
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
ffc9a183b9825408bee451fc77fc952af9277a11
ffc9a183
1 parent
9b925db1
Add Unicode support
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
554 additions
and
57 deletions
Makefile
pdf/CHANGELOG.md
pdf/lib/pdf.dart
pdf/lib/src/font.dart
pdf/lib/src/font_descriptor.dart
pdf/lib/src/graphics.dart
pdf/lib/src/ttf_parser.dart
pdf/lib/src/ttf_writer.dart
pdf/lib/src/ttffont.dart
pdf/lib/src/unicode_cmap.dart
pdf/test/ttf_test.dart
Makefile
View file @
ffc9a18
...
...
@@ -15,7 +15,7 @@
DART_SRC
=
$(
shell
find . -name
'*.dart'
)
CLNG_SRC
=
$(
shell
find printing/ios -name
'*.java'
-o -name
'*.m'
-o -name
'*.h'
)
$(
shell
find printing/android -name
'*.java'
-o -name
'*.m'
-o -name
'*.h'
)
SWFT_SRC
=
$(
shell
find . -name
'*.swift'
)
FONTS
=
pdf/open-sans.ttf pdf/roboto.ttf
FONTS
=
pdf/open-sans.ttf pdf/roboto.ttf
pdf/noto-sans.ttf
all
:
$(FONTS) format
...
...
@@ -25,6 +25,9 @@ pdf/open-sans.ttf:
pdf/roboto.ttf
:
curl -L
"https://github.com/google/fonts/raw/master/apache/robotomono/RobotoMono-Regular.ttf"
>
$@
pdf/noto-sans.ttf
:
curl -L
"https://raw.githubusercontent.com/google/fonts/master/ofl/notosans/NotoSans-Regular.ttf"
>
$@
format
:
format-dart format-clang format-swift
format-dart
:
$(DART_SRC)
...
...
pdf/CHANGELOG.md
View file @
ffc9a18
# 1.3.5
*
Add some color functions
*
Remove color constants from PdfColor, use PdfColors
*
Add TTF Font SubSetting
*
Add Unicode support for TTF Fonts
# 1.3.4
*
Add available dimensions for PdfPageFormat
...
...
pdf/lib/pdf.dart
View file @
ffc9a18
...
...
@@ -54,8 +54,10 @@ part 'src/polygon.dart';
part
'src/rect.dart'
;
part
'src/stream.dart'
;
part
'src/ttf_parser.dart'
;
part
'src/ttf_writer.dart'
;
part
'src/ttffont.dart'
;
part
'src/type1_font.dart'
;
part
'src/type1_fonts.dart'
;
part
'src/unicode_cmap.dart'
;
part
'src/xobject.dart'
;
part
'src/xref.dart'
;
...
...
pdf/lib/src/font.dart
View file @
ffc9a18
...
...
@@ -153,4 +153,11 @@ abstract class PdfFont extends PdfObject {
@override
String
toString
()
=>
'Font(
$fontName
)'
;
PdfStream
putText
(
String
text
)
{
return
PdfStream
()
..
putBytes
(
latin1
.
encode
(
'('
))
..
putTextBytes
(
latin1
.
encode
(
text
))
..
putBytes
(
latin1
.
encode
(
')'
));
}
}
...
...
pdf/lib/src/font_descriptor.dart
View file @
ffc9a18
...
...
@@ -30,7 +30,7 @@ class PdfFontDescriptor extends PdfObject {
params
[
'/FontName'
]
=
PdfStream
.
string
(
'/'
+
ttfFont
.
fontName
);
params
[
'/FontFile2'
]
=
file
.
ref
();
params
[
'/Flags'
]
=
PdfStream
.
intNum
(
32
);
params
[
'/Flags'
]
=
PdfStream
.
intNum
(
ttfFont
.
font
.
unicode
?
4
:
32
);
params
[
'/FontBBox'
]
=
PdfStream
()
..
putIntArray
(<
int
>[
(
ttfFont
.
font
.
xMin
/
ttfFont
.
font
.
unitsPerEm
*
1000
).
toInt
(),
...
...
pdf/lib/src/graphics.dart
View file @
ffc9a18
...
...
@@ -195,7 +195,7 @@ class PdfGraphics {
buf
.
putString
(
' Td
${font.name}
'
);
buf
.
putNum
(
size
);
buf
.
putString
(
' Tf '
);
buf
.
put
Text
(
s
);
buf
.
put
Stream
(
font
.
putText
(
s
)
);
buf
.
putString
(
' Tj ET
\n
'
);
}
...
...
pdf/lib/src/ttf_parser.dart
View file @
ffc9a18
...
...
@@ -16,6 +16,17 @@
part of
pdf
;
class
TtfGlyphInfo
{
TtfGlyphInfo
(
this
.
index
,
this
.
data
,
this
.
compounds
);
final
int
index
;
final
Uint8List
data
;
final
List
<
int
>
compounds
;
@override
String
toString
()
=>
'Glyph
$index
$compounds
'
;
}
class
TtfParser
{
TtfParser
(
this
.
bytes
)
{
final
int
numTables
=
bytes
.
getUint16
(
4
);
...
...
@@ -23,56 +34,61 @@ class TtfParser {
for
(
int
i
=
0
;
i
<
numTables
;
i
++)
{
final
String
name
=
utf8
.
decode
(
bytes
.
buffer
.
asUint8List
(
i
*
16
+
12
,
4
));
final
int
offset
=
bytes
.
getUint32
(
i
*
16
+
20
);
_tableOffsets
[
name
]
=
offset
;
final
int
size
=
bytes
.
getUint32
(
i
*
16
+
24
);
tableOffsets
[
name
]
=
offset
;
tableSize
[
name
]
=
size
;
}
_parseFontName
();
_parseCMap
();
_parseIndexes
();
_parseGly
f
();
_parseGly
phs
();
}
static
const
String
_HEAD
=
'head'
;
static
const
String
_NAME
=
'name'
;
static
const
String
_HMTX
=
'hmtx'
;
static
const
String
_HHEA
=
'hhea'
;
static
const
String
_CMAP
=
'cmap'
;
static
const
String
_MAXP
=
'maxp'
;
static
const
String
_LOCA
=
'loca'
;
static
const
String
_GLYF
=
'glyf'
;
static
const
String
head_table
=
'head'
;
static
const
String
name_table
=
'name'
;
static
const
String
hmtx_table
=
'hmtx'
;
static
const
String
hhea_table
=
'hhea'
;
static
const
String
cmap_table
=
'cmap'
;
static
const
String
maxp_table
=
'maxp'
;
static
const
String
loca_table
=
'loca'
;
static
const
String
glyf_table
=
'glyf'
;
final
ByteData
bytes
;
final
Map
<
String
,
int
>
_tableOffsets
=
<
String
,
int
>{};
final
Map
<
String
,
int
>
tableOffsets
=
<
String
,
int
>{};
final
Map
<
String
,
int
>
tableSize
=
<
String
,
int
>{};
String
_fontName
;
final
Map
<
int
,
int
>
charToGlyphIndexMap
=
<
int
,
int
>{};
final
List
<
int
>
glyphOffsets
=
<
int
>[];
final
Map
<
int
,
PdfFontMetrics
>
glyphInfoMap
=
<
int
,
PdfFontMetrics
>{};
int
get
unitsPerEm
=>
bytes
.
getUint16
(
_tableOffsets
[
_HEAD
]
+
18
);
int
get
unitsPerEm
=>
bytes
.
getUint16
(
tableOffsets
[
head_table
]
+
18
);
int
get
xMin
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
36
);
int
get
xMin
=>
bytes
.
getInt16
(
tableOffsets
[
head_table
]
+
36
);
int
get
yMin
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
38
);
int
get
yMin
=>
bytes
.
getInt16
(
tableOffsets
[
head_table
]
+
38
);
int
get
xMax
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
40
);
int
get
xMax
=>
bytes
.
getInt16
(
tableOffsets
[
head_table
]
+
40
);
int
get
yMax
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
42
);
int
get
yMax
=>
bytes
.
getInt16
(
tableOffsets
[
head_table
]
+
42
);
int
get
indexToLocFormat
=>
bytes
.
getInt16
(
_tableOffsets
[
_HEAD
]
+
50
);
int
get
indexToLocFormat
=>
bytes
.
getInt16
(
tableOffsets
[
head_table
]
+
50
);
int
get
ascent
=>
bytes
.
getInt16
(
_tableOffsets
[
_HHEA
]
+
4
);
int
get
ascent
=>
bytes
.
getInt16
(
tableOffsets
[
hhea_table
]
+
4
);
int
get
descent
=>
bytes
.
getInt16
(
_tableOffsets
[
_HHEA
]
+
6
);
int
get
descent
=>
bytes
.
getInt16
(
tableOffsets
[
hhea_table
]
+
6
);
int
get
numOfLongHorMetrics
=>
bytes
.
getInt16
(
_tableOffsets
[
_HHEA
]
+
34
);
int
get
numOfLongHorMetrics
=>
bytes
.
getInt16
(
tableOffsets
[
hhea_table
]
+
34
);
int
get
numGlyphs
=>
bytes
.
getInt16
(
_tableOffsets
[
_MAXP
]
+
4
);
int
get
numGlyphs
=>
bytes
.
getInt16
(
tableOffsets
[
maxp_table
]
+
4
);
String
get
fontName
=>
_fontName
;
bool
get
unicode
=>
bytes
.
getUint32
(
0
)
==
0x10000
;
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
void
_parseFontName
()
{
final
int
basePosition
=
_tableOffsets
[
_NAME
];
final
int
basePosition
=
tableOffsets
[
name_table
];
final
int
count
=
bytes
.
getUint16
(
basePosition
+
2
);
final
int
stringOffset
=
bytes
.
getUint16
(
basePosition
+
4
);
int
pos
=
basePosition
+
6
;
...
...
@@ -105,7 +121,7 @@ class TtfParser {
}
void
_parseCMap
()
{
final
int
basePosition
=
_tableOffsets
[
_CMAP
];
final
int
basePosition
=
tableOffsets
[
cmap_table
];
final
int
numSubTables
=
bytes
.
getUint16
(
basePosition
+
2
);
for
(
int
i
=
0
;
i
<
numSubTables
;
i
++)
{
final
int
offset
=
bytes
.
getUint32
(
basePosition
+
i
*
8
+
8
);
...
...
@@ -190,7 +206,7 @@ class TtfParser {
}
void
_parseIndexes
()
{
final
int
basePosition
=
_tableOffsets
[
_LOCA
];
final
int
basePosition
=
tableOffsets
[
loca_table
];
final
int
numGlyphs
=
this
.
numGlyphs
;
if
(
indexToLocFormat
==
0
)
{
for
(
int
i
=
0
;
i
<
numGlyphs
;
i
++)
{
...
...
@@ -204,9 +220,9 @@ class TtfParser {
}
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
void
_parseGlyf
()
{
final
int
baseOffset
=
_tableOffsets
[
_GLYF
];
final
int
hmtxOffset
=
_tableOffsets
[
_HMTX
];
void
_parseGlyphs
()
{
final
int
baseOffset
=
tableOffsets
[
glyf_table
];
final
int
hmtxOffset
=
tableOffsets
[
hmtx_table
];
final
int
unitsPerEm
=
this
.
unitsPerEm
;
int
glyphIndex
=
0
;
for
(
int
offset
in
glyphOffsets
)
{
...
...
@@ -228,4 +244,117 @@ class TtfParser {
glyphIndex
++;
}
}
/// http://stevehanov.ca/blog/?id=143
TtfGlyphInfo
readGlyph
(
int
index
)
{
assert
(
index
!=
null
);
assert
(
index
<
glyphOffsets
.
length
);
final
int
start
=
tableOffsets
[
glyf_table
]
+
glyphOffsets
[
index
];
final
int
numberOfContours
=
bytes
.
getInt16
(
start
);
assert
(
numberOfContours
>=
-
1
);
if
(
numberOfContours
==
-
1
)
{
return
_readCompoundGlyph
(
index
,
start
,
start
+
10
);
}
else
{
return
_readSimpleGlyph
(
index
,
start
,
start
+
10
,
numberOfContours
);
}
}
TtfGlyphInfo
_readSimpleGlyph
(
int
glyph
,
int
start
,
int
offset
,
int
numberOfContours
)
{
const
int
X_IS_BYTE
=
2
;
const
int
Y_IS_BYTE
=
4
;
const
int
REPEAT
=
8
;
const
int
X_DELTA
=
16
;
const
int
Y_DELTA
=
32
;
int
numPoints
=
1
;
for
(
int
i
=
0
;
i
<
numberOfContours
;
i
++)
{
numPoints
=
math
.
max
(
numPoints
,
bytes
.
getUint16
(
offset
)
+
1
);
offset
+=
2
;
}
// skip over intructions
offset
+=
bytes
.
getUint16
(
offset
)
+
2
;
if
(
numberOfContours
==
0
)
{
return
TtfGlyphInfo
(
glyph
,
Uint8List
.
view
(
bytes
.
buffer
,
start
,
offset
-
start
),
<
int
>[],
);
}
final
List
<
int
>
flags
=
<
int
>[];
for
(
int
i
=
0
;
i
<
numPoints
;
i
++)
{
final
int
flag
=
bytes
.
getUint8
(
offset
++);
flags
.
add
(
flag
);
if
(
flag
&
REPEAT
!=
0
)
{
int
repeatCount
=
bytes
.
getUint8
(
offset
++);
assert
(
repeatCount
>
0
);
i
+=
repeatCount
;
while
(
repeatCount
--
>
0
)
{
flags
.
add
(
flag
);
}
}
}
int
byteFlag
=
X_IS_BYTE
;
int
deltaFlag
=
X_DELTA
;
for
(
int
a
=
0
;
a
<
2
;
a
++)
{
for
(
int
i
=
0
;
i
<
numPoints
;
i
++)
{
final
int
flag
=
flags
[
i
];
if
(
flag
&
byteFlag
!=
0
)
{
offset
++;
}
else
if
(~
flag
&
deltaFlag
!=
0
)
{
offset
+=
2
;
}
}
byteFlag
=
Y_IS_BYTE
;
deltaFlag
=
Y_DELTA
;
}
return
TtfGlyphInfo
(
glyph
,
Uint8List
.
view
(
bytes
.
buffer
,
start
,
offset
-
start
),
<
int
>[],
);
}
TtfGlyphInfo
_readCompoundGlyph
(
int
glyph
,
int
start
,
int
offset
)
{
const
int
ARG_1_AND_2_ARE_WORDS
=
1
;
const
int
MORE_COMPONENTS
=
32
;
const
int
WE_HAVE_INSTRUCTIONS
=
256
;
final
List
<
int
>
components
=
<
int
>[];
bool
hasInstructions
=
false
;
int
flags
=
MORE_COMPONENTS
;
while
(
flags
&
MORE_COMPONENTS
!=
0
)
{
flags
=
bytes
.
getUint16
(
offset
);
final
int
glyphIndex
=
bytes
.
getUint16
(
offset
+
2
);
offset
+=
(
flags
&
ARG_1_AND_2_ARE_WORDS
!=
0
)
?
8
:
6
;
components
.
add
(
glyphIndex
);
if
(
flags
&
WE_HAVE_INSTRUCTIONS
!=
0
)
{
assert
(!
hasInstructions
);
// Not already set
hasInstructions
=
true
;
}
}
if
(
hasInstructions
)
{
offset
+=
bytes
.
getUint16
(
offset
)
+
2
;
}
return
TtfGlyphInfo
(
glyph
,
Uint8List
.
view
(
bytes
.
buffer
,
start
,
offset
-
start
),
components
,
);
}
}
...
...
pdf/lib/src/ttf_writer.dart
0 → 100644
View file @
ffc9a18
/*
* Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of
pdf
;
/// https://opentype.js.org/
class
TtfWriter
{
TtfWriter
(
this
.
ttf
);
final
TtfParser
ttf
;
int
_calcTableChecksum
(
ByteData
table
)
{
assert
(
table
.
lengthInBytes
%
4
==
0
);
int
sum
=
0
;
for
(
int
i
=
0
;
i
<
table
.
lengthInBytes
-
3
;
i
+=
4
)
{
sum
=
(
sum
+
table
.
getUint32
(
i
))
&
0xffffffff
;
}
return
sum
;
}
void
_updateCompoundGlyph
(
TtfGlyphInfo
glyph
,
Map
<
int
,
int
>
compoundMap
)
{
const
int
ARG_1_AND_2_ARE_WORDS
=
1
;
const
int
MORE_COMPONENTS
=
32
;
int
offset
=
10
;
final
ByteData
bytes
=
glyph
.
data
.
buffer
.
asByteData
(
glyph
.
data
.
offsetInBytes
,
glyph
.
data
.
lengthInBytes
);
int
flags
=
MORE_COMPONENTS
;
while
(
flags
&
MORE_COMPONENTS
!=
0
)
{
flags
=
bytes
.
getUint16
(
offset
);
final
int
glyphIndex
=
bytes
.
getUint16
(
offset
+
2
);
bytes
.
setUint16
(
offset
+
2
,
compoundMap
[
glyphIndex
]);
offset
+=
(
flags
&
ARG_1_AND_2_ARE_WORDS
!=
0
)
?
8
:
6
;
}
}
int
_wordAlign
(
int
offset
,
[
int
align
=
2
])
{
return
offset
+
((
align
-
(
offset
%
align
))
%
align
);
}
Uint8List
withChars
(
List
<
int
>
chars
)
{
final
Map
<
String
,
Uint8List
>
tables
=
<
String
,
Uint8List
>{};
final
Map
<
String
,
int
>
tablesLength
=
<
String
,
int
>{};
// Create the glyphs table
final
List
<
TtfGlyphInfo
>
glyphsInfo
=
<
TtfGlyphInfo
>[];
final
Map
<
int
,
int
>
compounds
=
<
int
,
int
>{};
for
(
int
index
=
0
;
index
<
chars
.
length
;
index
++)
{
if
(
chars
[
index
]
==
32
)
{
final
TtfGlyphInfo
glyph
=
TtfGlyphInfo
(
32
,
Uint8List
(
0
),
<
int
>[]);
glyphsInfo
.
add
(
glyph
);
continue
;
}
final
TtfGlyphInfo
glyph
=
ttf
.
readGlyph
(
chars
[
index
]
==
0
?
0
:
ttf
.
charToGlyphIndexMap
[
chars
[
index
]]);
for
(
int
g
in
glyph
.
compounds
)
{
compounds
[
g
]
=
null
;
}
glyphsInfo
.
add
(
glyph
);
}
// Add compound glyphs
for
(
int
compound
in
compounds
.
keys
)
{
final
int
index
=
chars
.
indexOf
(
compound
);
if
(
index
>=
0
)
{
compounds
[
compound
]
=
index
;
}
else
{
compounds
[
compound
]
=
glyphsInfo
.
length
;
final
TtfGlyphInfo
glyph
=
ttf
.
readGlyph
(
compound
);
assert
(
glyph
.
compounds
.
isEmpty
);
// This is a simple glyph
glyphsInfo
.
add
(
glyph
);
}
}
// Add one last empty glyph
final
TtfGlyphInfo
glyph
=
TtfGlyphInfo
(
32
,
Uint8List
(
0
),
<
int
>[]);
glyphsInfo
.
add
(
glyph
);
// update compound indices
for
(
TtfGlyphInfo
glyph
in
glyphsInfo
)
{
if
(
glyph
.
compounds
.
isNotEmpty
)
{
_updateCompoundGlyph
(
glyph
,
compounds
);
}
}
int
glyphsTableLength
=
0
;
for
(
TtfGlyphInfo
glyph
in
glyphsInfo
)
{
glyphsTableLength
=
_wordAlign
(
glyphsTableLength
+
glyph
.
data
.
lengthInBytes
);
}
int
offset
=
0
;
final
Uint8List
glyphsTable
=
Uint8List
(
_wordAlign
(
glyphsTableLength
,
4
));
tables
[
TtfParser
.
glyf_table
]
=
glyphsTable
;
tablesLength
[
TtfParser
.
glyf_table
]
=
glyphsTableLength
;
// Loca
if
(
ttf
.
indexToLocFormat
==
0
)
{
tables
[
TtfParser
.
loca_table
]
=
Uint8List
(
_wordAlign
(
glyphsInfo
.
length
*
2
,
4
));
// uint16
tablesLength
[
TtfParser
.
loca_table
]
=
glyphsInfo
.
length
*
2
;
}
else
{
tables
[
TtfParser
.
loca_table
]
=
Uint8List
(
_wordAlign
(
glyphsInfo
.
length
*
4
,
4
));
// uint32
tablesLength
[
TtfParser
.
loca_table
]
=
glyphsInfo
.
length
*
4
;
}
{
final
ByteData
loca
=
tables
[
TtfParser
.
loca_table
].
buffer
.
asByteData
();
int
index
=
0
;
for
(
TtfGlyphInfo
glyph
in
glyphsInfo
)
{
if
(
ttf
.
indexToLocFormat
==
0
)
{
loca
.
setUint16
(
index
,
offset
~/
2
);
index
+=
2
;
}
else
{
loca
.
setUint32
(
index
,
offset
);
index
+=
4
;
}
glyphsTable
.
setAll
(
offset
,
glyph
.
data
);
offset
=
_wordAlign
(
offset
+
glyph
.
data
.
lengthInBytes
);
}
}
{
// Head table
final
int
start
=
ttf
.
tableOffsets
[
TtfParser
.
head_table
];
final
int
len
=
ttf
.
tableSize
[
TtfParser
.
head_table
];
final
Uint8List
head
=
Uint8List
.
fromList
(
ttf
.
bytes
.
buffer
.
asUint8List
(
start
,
_wordAlign
(
len
,
4
)));
head
.
buffer
.
asByteData
().
setUint32
(
8
,
0
);
// checkSumAdjustment
tables
[
TtfParser
.
head_table
]
=
head
;
tablesLength
[
TtfParser
.
head_table
]
=
len
;
}
{
// MaxP table
final
int
start
=
ttf
.
tableOffsets
[
TtfParser
.
maxp_table
];
final
int
len
=
ttf
.
tableSize
[
TtfParser
.
maxp_table
];
final
Uint8List
maxp
=
Uint8List
.
fromList
(
ttf
.
bytes
.
buffer
.
asUint8List
(
start
,
_wordAlign
(
len
,
4
)));
maxp
.
buffer
.
asByteData
().
setUint16
(
4
,
glyphsInfo
.
length
);
tables
[
TtfParser
.
maxp_table
]
=
maxp
;
tablesLength
[
TtfParser
.
maxp_table
]
=
len
;
}
{
// HHEA table
final
int
start
=
ttf
.
tableOffsets
[
TtfParser
.
hhea_table
];
final
int
len
=
ttf
.
tableSize
[
TtfParser
.
hhea_table
];
final
Uint8List
hhea
=
Uint8List
.
fromList
(
ttf
.
bytes
.
buffer
.
asUint8List
(
start
,
_wordAlign
(
len
,
4
)));
hhea
.
buffer
.
asByteData
()
.
setUint16
(
34
,
glyphsInfo
.
length
);
// numOfLongHorMetrics
tables
[
TtfParser
.
hhea_table
]
=
hhea
;
tablesLength
[
TtfParser
.
hhea_table
]
=
len
;
}
{
// HMTX table
final
int
len
=
4
*
glyphsInfo
.
length
;
final
Uint8List
hmtx
=
Uint8List
(
_wordAlign
(
len
,
4
));
final
int
hmtxOffset
=
ttf
.
tableOffsets
[
TtfParser
.
hmtx_table
];
final
ByteData
hmtxData
=
hmtx
.
buffer
.
asByteData
();
int
index
=
0
;
for
(
TtfGlyphInfo
glyph
in
glyphsInfo
)
{
hmtxData
.
setUint32
(
index
,
ttf
.
bytes
.
getInt32
(
hmtxOffset
+
glyph
.
index
*
4
));
index
+=
4
;
}
tables
[
TtfParser
.
hmtx_table
]
=
hmtx
;
tablesLength
[
TtfParser
.
hmtx_table
]
=
len
;
}
{
// CMAP table
final
Uint8List
cmap
=
Uint8List
(
_wordAlign
(
0x112
,
4
));
cmap
.
setAll
(
3
,
<
int
>[
1
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
12
,
0
,
0
,
1
,
6
]);
final
ByteData
cmapData
=
cmap
.
buffer
.
asByteData
();
for
(
int
i
=
1
;
i
<
chars
.
length
;
i
++)
{
cmapData
.
setUint8
(
i
+
18
,
i
);
}
tables
[
TtfParser
.
cmap_table
]
=
cmap
;
tablesLength
[
TtfParser
.
cmap_table
]
=
0x112
;
}
{
final
List
<
int
>
bytes
=
<
int
>[];
final
int
numTables
=
tables
.
length
;
// Create the file header
final
ByteData
start
=
ByteData
(
12
+
numTables
*
16
);
start
.
setUint32
(
0
,
0x00010000
);
start
.
setUint16
(
4
,
numTables
);
int
pot
=
numTables
;
while
(
pot
&
(
pot
-
1
)
!=
0
)
{
pot
++;
}
start
.
setUint16
(
6
,
pot
*
16
);
start
.
setUint16
(
8
,
math
.
log
(
pot
).
toInt
());
start
.
setUint16
(
10
,
pot
*
16
-
numTables
*
16
);
// Create the table directory
int
count
=
0
;
int
offset
=
12
+
numTables
*
16
;
tables
.
forEach
((
String
name
,
Uint8List
data
)
{
final
List
<
int
>
runes
=
name
.
runes
.
toList
();
start
.
setUint8
(
12
+
count
*
16
,
runes
[
0
]);
start
.
setUint8
(
12
+
count
*
16
+
1
,
runes
[
1
]);
start
.
setUint8
(
12
+
count
*
16
+
2
,
runes
[
2
]);
start
.
setUint8
(
12
+
count
*
16
+
3
,
runes
[
3
]);
start
.
setUint32
(
12
+
count
*
16
+
4
,
_calcTableChecksum
(
data
.
buffer
.
asByteData
()));
// checkSum
start
.
setUint32
(
12
+
count
*
16
+
8
,
offset
);
// offset
start
.
setUint32
(
12
+
count
*
16
+
12
,
tablesLength
[
name
]);
// length
offset
+=
data
.
lengthInBytes
;
count
++;
});
bytes
.
addAll
(
start
.
buffer
.
asUint8List
());
tables
.
forEach
((
String
name
,
Uint8List
data
)
{
bytes
.
addAll
(
data
.
buffer
.
asUint8List
());
});
return
Uint8List
.
fromList
(
bytes
);
}
}
}
...
...
pdf/lib/src/ttffont.dart
View file @
ffc9a18
...
...
@@ -21,37 +21,22 @@ class PdfTtfFont extends PdfFont {
PdfTtfFont
(
PdfDocument
pdfDocument
,
ByteData
bytes
)
:
font
=
TtfParser
(
bytes
),
super
.
_create
(
pdfDocument
,
subtype:
'/TrueType'
)
{
final
PdfObjectStream
file
=
PdfObjectStream
(
pdfDocument
,
isBinary:
true
);
final
Uint8List
data
=
bytes
.
buffer
.
asUint8List
();
file
.
buf
.
putBytes
(
data
);
file
.
params
[
'/Length1'
]
=
PdfStream
.
intNum
(
data
.
length
);
_charMin
=
32
;
_charMax
=
255
;
final
List
<
String
>
widths
=
<
String
>[];
for
(
int
i
=
_charMin
;
i
<=
_charMax
;
i
++)
{
widths
.
add
((
glyphMetrics
(
i
).
advanceWidth
*
1000.0
).
toInt
().
toString
());
}
unicodeCMap
=
PdfObject
(
pdfDocument
);
file
=
PdfObjectStream
(
pdfDocument
,
isBinary:
true
);
unicodeCMap
=
PdfUnicodeCmap
(
pdfDocument
);
descriptor
=
PdfFontDescriptor
(
this
,
file
);
widthsObject
=
PdfArrayObject
(
pdfDocument
,
widths
);
widthsObject
=
PdfArrayObject
(
pdfDocument
,
<
String
>[]
);
}
Pdf
Object
unicodeCMap
;
Pdf
UnicodeCmap
unicodeCMap
;
PdfFontDescriptor
descriptor
;
PdfObjectStream
file
;
PdfArrayObject
widthsObject
;
final
TtfParser
font
;
int
_charMin
;
int
_charMax
;
@override
String
get
fontName
=>
font
.
fontName
;
...
...
@@ -74,14 +59,80 @@ class PdfTtfFont extends PdfFont {
@override
void
_prepare
()
{
int
charMin
;
int
charMax
;
super
.
_prepare
();
final
TtfWriter
ttfWriter
=
TtfWriter
(
font
);
final
Uint8List
data
=
ttfWriter
.
withChars
(
unicodeCMap
.
cmap
);
file
.
buf
.
putBytes
(
data
);
file
.
params
[
'/Length1'
]
=
PdfStream
.
intNum
(
data
.
length
);
params
[
'/BaseFont'
]
=
PdfStream
.
string
(
'/'
+
fontName
);
params
[
'/FirstChar'
]
=
PdfStream
.
intNum
(
_charMin
);
params
[
'/LastChar'
]
=
PdfStream
.
intNum
(
_charMax
);
params
[
'/Widths'
]
=
widthsObject
.
ref
();
params
[
'/FontDescriptor'
]
=
descriptor
.
ref
();
// params['/Encoding'] = PdfStream.string('/Identity-H');
// params['/ToUnicode'] = unicodeCMap.ref();
if
(
font
.
unicode
)
{
if
(
params
.
containsKey
(
'/Encoding'
))
{
params
.
remove
(
'/Encoding'
);
}
params
[
'/ToUnicode'
]
=
unicodeCMap
.
ref
();
charMin
=
0
;
charMax
=
unicodeCMap
.
cmap
.
length
-
1
;
for
(
int
i
=
charMin
;
i
<=
charMax
;
i
++)
{
widthsObject
.
values
.
add
(
(
glyphMetrics
(
unicodeCMap
.
cmap
[
i
]).
advanceWidth
*
1000.0
)
.
toInt
()
.
toString
());
}
}
else
{
charMin
=
32
;
charMax
=
255
;
for
(
int
i
=
charMin
;
i
<=
charMax
;
i
++)
{
widthsObject
.
values
.
add
((
glyphMetrics
(
i
).
advanceWidth
*
1000.0
).
toInt
().
toString
());
}
}
params
[
'/FirstChar'
]
=
PdfStream
.
intNum
(
charMin
);
params
[
'/LastChar'
]
=
PdfStream
.
intNum
(
charMax
);
params
[
'/Widths'
]
=
widthsObject
.
ref
();
}
@override
PdfStream
putText
(
String
text
)
{
if
(!
font
.
unicode
)
{
return
super
.
putText
(
text
);
}
final
Runes
runes
=
text
.
runes
;
final
List
<
int
>
bytes
=
List
<
int
>();
for
(
int
rune
in
runes
)
{
int
char
=
unicodeCMap
.
cmap
.
indexOf
(
rune
);
if
(
char
==
-
1
)
{
char
=
unicodeCMap
.
cmap
.
length
;
unicodeCMap
.
cmap
.
add
(
rune
);
}
bytes
.
add
(
char
);
}
return
PdfStream
()
..
putBytes
(
latin1
.
encode
(
'('
))
..
putTextBytes
(
bytes
)
..
putBytes
(
latin1
.
encode
(
')'
));
}
@override
PdfFontMetrics
stringMetrics
(
String
s
)
{
if
(
s
.
isEmpty
||
!
font
.
unicode
)
{
return
super
.
stringMetrics
(
s
);
}
final
Runes
runes
=
s
.
runes
;
final
List
<
int
>
bytes
=
List
<
int
>();
runes
.
forEach
(
bytes
.
add
);
final
Iterable
<
PdfFontMetrics
>
metrics
=
bytes
.
map
(
glyphMetrics
);
return
PdfFontMetrics
.
append
(
metrics
);
}
}
...
...
pdf/lib/src/unicode_cmap.dart
0 → 100644
View file @
ffc9a18
/*
* Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of
pdf
;
class
PdfUnicodeCmap
extends
PdfObjectStream
{
PdfUnicodeCmap
(
PdfDocument
pdfDocument
)
:
super
(
pdfDocument
);
final
List
<
int
>
cmap
=
<
int
>[
0
];
@override
void
_prepare
()
{
buf
.
putString
(
'/CIDInit/ProcSet findresource begin
\n
'
'12 dict begin
\n
'
'begincmap
\n
'
'/CIDSystemInfo<<
\n
'
'/Registry (Adobe)
\n
'
'/Ordering (UCS)
\n
'
'/Supplement 0
\n
'
'>> def
\n
'
'/CMapName/Adobe-Identity-UCS def
\n
'
'/CMapType 2 def
\n
'
'1 begincodespacerange
\n
'
'<00> <FF>
\n
'
'endcodespacerange
\n
'
'
${cmap.length}
beginbfchar
\n
'
);
for
(
int
key
=
0
;
key
<
cmap
.
length
;
key
++)
{
final
int
value
=
cmap
[
key
];
buf
.
putString
(
'<'
+
key
.
toRadixString
(
16
).
toUpperCase
().
padLeft
(
2
,
'0'
)
+
'> <'
+
value
.
toRadixString
(
16
).
toUpperCase
().
padLeft
(
4
,
'0'
)
+
'>
\n
'
);
}
buf
.
putString
(
'endbfchar
\n
'
'endcmap
\n
'
'CMapName currentdict /CMap defineresource pop
\n
'
'end
\n
'
'end'
);
super
.
_prepare
();
}
}
...
...
pdf/test/ttf_test.dart
View file @
ffc9a18
...
...
@@ -65,10 +65,11 @@ void main() {
final
PdfGraphics
g
=
page
.
getGraphics
();
int
top
=
0
;
const
String
s
=
'Hello '
;
const
String
s
=
'Hello
Lukáča
'
;
printTextTtf
(
g
,
s
,
File
(
'open-sans.ttf'
),
30.0
+
30.0
*
top
++);
printTextTtf
(
g
,
s
,
File
(
'roboto.ttf'
),
30.0
+
30.0
*
top
++);
printTextTtf
(
g
,
s
,
File
(
'noto-sans.ttf'
),
30.0
+
30.0
*
top
++);
final
File
file
=
File
(
'ttf.pdf'
);
file
.
writeAsBytesSync
(
pdf
.
save
());
...
...
Please
register
or
login
to post a comment