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
2021-12-31 13:12:26 -0400
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
dbf1706870d783f95ce379c89c8148beb8316232
dbf17068
1 parent
eefce198
Add support for Emoji
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
218 additions
and
15 deletions
Makefile
pdf/CHANGELOG.md
pdf/lib/src/pdf/font/ttf_parser.dart
pdf/lib/src/pdf/obj/unicode_cmap.dart
pdf/lib/src/widgets/text.dart
pdf/test/widget_text_test.dart
test/build_gfonts.dart
test/golden/widgets-text.pdf
Makefile
View file @
dbf1706
...
...
@@ -18,7 +18,7 @@ DART_BIN=$(FLUTTER)/bin/dart
DART_SRC
=
$(
shell
find . -name
'*.dart'
)
CLNG_SRC
=
$(
shell
find printing/ios printing/macos printing/windows printing/linux printing/android -name
'*.cpp'
-o -name
'*.cc'
-o -name
'*.m'
-o -name
'*.h'
-o -name
'*.java'
)
SWFT_SRC
=
$(
shell
find printing/ios printing/macos -name
'*.swift'
)
FONTS
=
pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf pdf/hacen-tunisia.ttf pdf/material.ttf
FONTS
=
pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf pdf/hacen-tunisia.ttf pdf/material.ttf
pdf/emoji.ttf
COV_PORT
=
9292
SVG
=
blend_and_mask blend_mode_devil clip_path clip_path_2 clip_path_2 clip_path_3 clip_path_3 dash_path ellipse empty_defs equation fill-rule-inherit group_composite_opacity group_fill_opacity group_mask group_opacity group_opacity_transform hidden href-fill image image_def implicit_fill_with_opacity linear_gradient linear_gradient_2 linear_gradient_absolute_user_space_translate linear_gradient_percentage_bounding_translate linear_gradient_percentage_user_space_translate linear_gradient_xlink male mask mask_with_gradient mask_with_use mask_with_use2 nested_group opacity_on_path radial_gradient radial_gradient_absolute_user_space_translate radial_gradient_focal radial_gradient_percentage_bounding_translate radial_gradient_percentage_user_space_translate radial_gradient_xlink radial_ref_linear_gradient rect_rrect rect_rrect_no_ry stroke_inherit_circles style_attr text text_2 text_3 use_circles use_circles_def use_emc2 use_fill use_opacity_grid width_height_viewbox flutter_logo emoji_u1f600 text_transform dart new-pause-button new-send-circle new-gif new-camera new-image numeric_25 new-mention new-gif-button new-action-expander new-play-button aa alphachannel Ghostscript_Tiger Firefox_Logo_2017 chess_knight Flag_of_the_United_States
...
...
@@ -42,6 +42,9 @@ pdf/genyomintw.ttf:
pdf/material.ttf
:
curl -L
"https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.ttf"
>
$@
pdf/emoji.ttf
:
curl -L https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf >
$@
demo/assets/logo.svg
:
curl -L
"http://pigment.github.io/fake-logos/logos/vector/color/auto-speed.svg"
>
$@
...
...
pdf/CHANGELOG.md
View file @
dbf1706
...
...
@@ -5,6 +5,7 @@
-
Move files
-
Depreciate Font.stringSize
-
Implement fallback font
-
Implement Emoji support
## 3.6.6
...
...
pdf/lib/src/pdf/font/ttf_parser.dart
View file @
dbf1706
...
...
@@ -68,6 +68,52 @@ class TtfGlyphInfo {
String
toString
()
=>
'Glyph
$index
$compounds
'
;
}
class
TtfBitmapInfo
{
const
TtfBitmapInfo
(
this
.
data
,
this
.
height
,
this
.
width
,
this
.
horiBearingX
,
this
.
horiBearingY
,
this
.
horiAdvance
,
this
.
vertBearingX
,
this
.
vertBearingY
,
this
.
vertAdvance
,
this
.
ascent
,
this
.
descent
,
);
final
Uint8List
data
;
final
int
height
;
final
int
width
;
final
int
horiBearingX
;
final
int
horiBearingY
;
final
int
horiAdvance
;
final
int
vertBearingX
;
final
int
vertBearingY
;
final
int
vertAdvance
;
final
int
ascent
;
final
int
descent
;
PdfFontMetrics
get
metrics
{
final
coef
=
1.0
/
height
;
return
PdfFontMetrics
(
bottom:
horiBearingY
*
coef
,
left:
horiBearingX
*
coef
,
top:
horiBearingY
*
coef
-
height
*
coef
,
right:
horiAdvance
*
coef
,
ascent:
ascent
*
coef
,
descent:
horiBearingY
*
coef
,
advanceWidth:
horiAdvance
*
coef
,
leftBearing:
horiBearingX
*
coef
,
);
}
@override
String
toString
()
=>
'Bitmap Glyph
${width}
x
$height
horiBearingX:
$horiBearingX
horiBearingY:
$horiBearingY
horiAdvance:
$horiAdvance
ascender:
$ascent
descender:
$descent
'
;
}
class
TtfParser
{
TtfParser
(
ByteData
bytes
)
:
bytes
=
UnmodifiableByteDataView
(
bytes
)
{
final
numTables
=
bytes
.
getUint16
(
4
);
...
...
@@ -92,15 +138,18 @@ class TtfParser {
'Unable to find the `cmap` table. This file is not a supported TTF font'
);
assert
(
tableOffsets
.
containsKey
(
maxp_table
),
'Unable to find the `maxp` table. This file is not a supported TTF font'
);
assert
(
tableOffsets
.
containsKey
(
loca_table
),
'Unable to find the `loca` table. This file is not a supported TTF font'
);
assert
(
tableOffsets
.
containsKey
(
glyf_table
),
'Unable to find the `glyf` table. This file is not a supported TTF font'
);
_parseCMap
();
if
(
tableOffsets
.
containsKey
(
loca_table
)
&&
tableOffsets
.
containsKey
(
glyf_table
))
{
_parseIndexes
();
_parseGlyphs
();
}
if
(
tableOffsets
.
containsKey
(
cblc_table
)
&&
tableOffsets
.
containsKey
(
cbdt_table
))
{
_parseBitmaps
();
}
}
static
const
String
head_table
=
'head'
;
static
const
String
name_table
=
'name'
;
...
...
@@ -110,14 +159,17 @@ class TtfParser {
static
const
String
maxp_table
=
'maxp'
;
static
const
String
loca_table
=
'loca'
;
static
const
String
glyf_table
=
'glyf'
;
static
const
String
cblc_table
=
'CBLC'
;
static
const
String
cbdt_table
=
'CBDT'
;
final
UnmodifiableByteDataView
bytes
;
final
Map
<
String
,
int
>
tableOffsets
=
<
String
,
int
>{};
final
Map
<
String
,
int
>
tableSize
=
<
String
,
int
>{};
final
tableOffsets
=
<
String
,
int
>{};
final
tableSize
=
<
String
,
int
>{};
final
Map
<
int
,
int
>
charToGlyphIndexMap
=
<
int
,
int
>{};
final
List
<
int
>
glyphOffsets
=
<
int
>[];
final
Map
<
int
,
PdfFontMetrics
>
glyphInfoMap
=
<
int
,
PdfFontMetrics
>{};
final
charToGlyphIndexMap
=
<
int
,
int
>{};
final
glyphOffsets
=
<
int
>[];
final
glyphInfoMap
=
<
int
,
PdfFontMetrics
>{};
final
bitmapOffsets
=
<
int
,
TtfBitmapInfo
>{};
int
get
unitsPerEm
=>
bytes
.
getUint16
(
tableOffsets
[
head_table
]!
+
18
);
...
...
@@ -145,9 +197,14 @@ class TtfParser {
bool
get
unicode
=>
bytes
.
getUint32
(
0
)
==
0x10000
;
bool
get
isBitmap
=>
bitmapOffsets
.
isNotEmpty
&&
glyphOffsets
.
isEmpty
;
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
String
?
getNameID
(
TtfParserName
fontNameID
)
{
final
basePosition
=
tableOffsets
[
name_table
]!;
final
basePosition
=
tableOffsets
[
name_table
];
if
(
basePosition
==
null
)
{
return
null
;
}
// final format = bytes.getUint16(basePosition);
final
count
=
bytes
.
getUint16
(
basePosition
+
2
);
final
stringOffset
=
bytes
.
getUint16
(
basePosition
+
4
);
...
...
@@ -288,15 +345,15 @@ class TtfParser {
}
void
_parseIndexes
()
{
final
basePosition
=
tableOffsets
[
loca_table
];
final
basePosition
=
tableOffsets
[
loca_table
]
!
;
final
numGlyphs
=
this
.
numGlyphs
;
if
(
indexToLocFormat
==
0
)
{
for
(
var
i
=
0
;
i
<
numGlyphs
;
i
++)
{
glyphOffsets
.
add
(
bytes
.
getUint16
(
basePosition
!
+
i
*
2
)
*
2
);
glyphOffsets
.
add
(
bytes
.
getUint16
(
basePosition
+
i
*
2
)
*
2
);
}
}
else
{
for
(
var
i
=
0
;
i
<
numGlyphs
;
i
++)
{
glyphOffsets
.
add
(
bytes
.
getUint32
(
basePosition
!
+
i
*
4
));
glyphOffsets
.
add
(
bytes
.
getUint32
(
basePosition
+
i
*
4
));
}
}
}
...
...
@@ -465,4 +522,92 @@ class TtfParser {
}
return
String
.
fromCharCodes
(
charCodes
);
}
// https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt
void
_parseBitmaps
()
{
final
baseOffset
=
tableOffsets
[
cblc_table
]!;
final
pngOffset
=
tableOffsets
[
cbdt_table
]!;
// CBLC Header
final
numSizes
=
bytes
.
getUint32
(
baseOffset
+
4
);
var
bitmapSize
=
baseOffset
+
8
;
for
(
var
bitmapSizeIndex
=
0
;
bitmapSizeIndex
<
numSizes
;
bitmapSizeIndex
++)
{
// BitmapSize Record
final
indexSubTableArrayOffset
=
baseOffset
+
bytes
.
getUint32
(
bitmapSize
);
// final indexTablesSize = bytes.getUint32(bitmapSize + 4);
final
numberOfIndexSubTables
=
bytes
.
getUint32
(
bitmapSize
+
8
);
final
ascender
=
bytes
.
getInt8
(
bitmapSize
+
12
);
final
descender
=
bytes
.
getInt8
(
bitmapSize
+
13
);
// final startGlyphIndex = bytes.getUint16(bitmapSize + 16 + 12 * 2);
// final endGlyphIndex = bytes.getUint16(bitmapSize + 16 + 12 * 2 + 2);
// final ppemX = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 4);
// final ppemY = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 5);
// final bitDepth = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 6);
// final flags = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 7);
var
subTableArrayOffset
=
indexSubTableArrayOffset
;
for
(
var
indexSubTable
=
0
;
indexSubTable
<
numberOfIndexSubTables
;
indexSubTable
++)
{
// IndexSubTableArray
final
firstGlyphIndex
=
bytes
.
getUint16
(
subTableArrayOffset
);
final
lastGlyphIndex
=
bytes
.
getUint16
(
subTableArrayOffset
+
2
);
final
additionalOffsetToIndexSubtable
=
indexSubTableArrayOffset
+
bytes
.
getUint32
(
subTableArrayOffset
+
4
);
// IndexSubHeader
final
indexFormat
=
bytes
.
getUint16
(
additionalOffsetToIndexSubtable
);
final
imageFormat
=
bytes
.
getUint16
(
additionalOffsetToIndexSubtable
+
2
);
final
imageDataOffset
=
pngOffset
+
bytes
.
getUint32
(
additionalOffsetToIndexSubtable
+
4
);
if
(
indexFormat
==
1
)
{
// IndexSubTable1
for
(
var
glyph
=
firstGlyphIndex
;
glyph
<=
lastGlyphIndex
;
glyph
++)
{
final
sbitOffset
=
imageDataOffset
+
bytes
.
getUint32
(
additionalOffsetToIndexSubtable
+
(
glyph
-
firstGlyphIndex
+
2
)
*
4
);
if
(
imageFormat
==
17
)
{
final
height
=
bytes
.
getUint8
(
sbitOffset
);
final
width
=
bytes
.
getUint8
(
sbitOffset
+
1
);
final
bearingX
=
bytes
.
getInt8
(
sbitOffset
+
2
);
final
bearingY
=
bytes
.
getInt8
(
sbitOffset
+
3
);
final
advance
=
bytes
.
getUint8
(
sbitOffset
+
4
);
final
dataLen
=
bytes
.
getUint32
(
sbitOffset
+
5
);
bitmapOffsets
[
glyph
]
=
TtfBitmapInfo
(
bytes
.
buffer
.
asUint8List
(
bytes
.
offsetInBytes
+
sbitOffset
+
9
,
dataLen
,
),
height
,
width
,
bearingX
,
bearingY
,
advance
,
0
,
0
,
0
,
ascender
,
descender
);
}
}
}
subTableArrayOffset
+=
8
;
}
bitmapSize
+=
16
+
12
*
2
+
8
;
}
}
TtfBitmapInfo
?
getBitmap
(
int
charcode
)
=>
bitmapOffsets
[
charToGlyphIndexMap
[
charcode
]];
}
...
...
pdf/lib/src/pdf/obj/unicode_cmap.dart
View file @
dbf1706
...
...
@@ -23,7 +23,7 @@ class PdfUnicodeCmap extends PdfObjectStream {
PdfUnicodeCmap
(
PdfDocument
pdfDocument
,
this
.
protect
)
:
super
(
pdfDocument
);
/// List of characters
final
List
<
int
>
cmap
=
<
int
>[
0
];
final
cmap
=
<
int
>[
];
/// Protects the text from being "seen" by the PDF reader.
final
bool
protect
;
...
...
pdf/lib/src/widgets/text.dart
View file @
dbf1706
...
...
@@ -24,6 +24,8 @@ import 'annotations.dart';
import
'basic.dart'
;
import
'document.dart'
;
import
'geometry.dart'
;
import
'image.dart'
;
import
'image_provider.dart'
;
import
'multi_page.dart'
;
import
'placeholders.dart'
;
import
'text_style.dart'
;
...
...
@@ -690,6 +692,25 @@ class RichText extends Widget with SpanningWidget {
_decorations
.
add
(
td
);
}
InlineSpan
_addEmoji
({
required
TtfBitmapInfo
bitmap
,
double
baseline
=
0
,
required
TextStyle
style
,
AnnotationBuilder
?
annotation
,
})
{
final
metrics
=
bitmap
.
metrics
*
style
.
fontSize
!;
return
WidgetSpan
(
child:
SizedBox
(
height:
style
.
fontSize
,
child:
Image
(
MemoryImage
(
bitmap
.
data
)),
),
style:
style
,
baseline:
baseline
+
metrics
.
ascent
+
metrics
.
descent
-
metrics
.
height
,
annotation:
annotation
,
);
}
InlineSpan
_addText
({
required
List
<
int
>
text
,
int
start
=
0
,
...
...
@@ -770,6 +791,19 @@ class RichText extends Widget with SpanningWidget {
for
(
final
fb
in
style
.
fontFallback
)
{
final
font
=
fb
.
getFont
(
context
);
if
(
font
.
isRuneSupported
(
rune
))
{
if
(
font
is
PdfTtfFont
)
{
final
bitmap
=
font
.
font
.
getBitmap
(
rune
);
if
(
bitmap
!=
null
)
{
spans
.
add
(
_addEmoji
(
bitmap:
bitmap
,
style:
style
,
baseline:
span
.
baseline
,
annotation:
annotation
,
));
found
=
true
;
break
;
}
}
spans
.
add
(
_addText
(
text:
[
rune
],
style:
style
.
copyWith
(
...
...
pdf/test/widget_text_test.dart
View file @
dbf1706
...
...
@@ -27,6 +27,7 @@ late Document pdf;
late
Font
ttf
;
late
Font
ttfBold
;
late
Font
asian
;
late
Font
emoji
;
Iterable
<
TextDecoration
>
permute
(
List
<
TextDecoration
>
prefix
,
List
<
TextDecoration
>
remaining
)
sync
*
{
...
...
@@ -48,6 +49,7 @@ void main() {
ttf
=
loadFont
(
'open-sans.ttf'
);
ttfBold
=
loadFont
(
'open-sans-bold.ttf'
);
asian
=
loadFont
(
'genyomintw.ttf'
);
emoji
=
loadFont
(
'emoji.ttf'
);
pdf
=
Document
();
});
...
...
@@ -357,6 +359,20 @@ void main() {
);
});
test
(
'Text Widgets Emojis'
,
()
{
pdf
.
addPage
(
Page
(
build:
(
Context
context
)
=>
Text
(
'Hello 🐈! Dancing 💃🏃'
,
style:
TextStyle
(
fontSize:
30
,
fontFallback:
[
emoji
],
),
),
),
);
});
tearDownAll
(()
async
{
final
file
=
File
(
'widgets-text.pdf'
);
await
file
.
writeAsBytes
(
await
pdf
.
save
());
...
...
test/build_gfonts.dart
View file @
dbf1706
...
...
@@ -128,8 +128,12 @@ void main(List<String> args) async {
}
for
(
final
entry
in
<
String
,
String
>{
'CupertinoIcons'
:
'https://github.com/flutter/packages/blob/master/third_party/packages/cupertino_icons/assets/CupertinoIcons.ttf'
,
'MaterialIcons'
:
'https://fonts.gstatic.com/s/materialicons/v98/flUhRq6tzZclQEJ-Vdg-IuiaDsNZ.ttf'
,
'NotoColorEmoji'
:
'https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf'
,
}.
entries
)
{
output
.
writeln
(
''
);
output
.
writeln
(
'///
${entry.key}
'
);
...
...
test/golden/widgets-text.pdf
View file @
dbf1706
No preview for this file type
Please
register
or
login
to post a comment