David PHAM-VAN

Add support for Icon Fonts

... ... @@ -15,7 +15,7 @@
DART_SRC=$(shell find . -name '*.dart')
CLNG_SRC=$(shell find printing/ios printing/macos printing/windows printing/android -name '*.cpp' -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 demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf pdf/hacen-tunisia.ttf
FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf pdf/hacen-tunisia.ttf pdf/material.ttf
COV_PORT=9292
all: $(FONTS) demo/assets/logo.png demo/assets/profile.jpg format printing/example/.metadata get
... ... @@ -42,6 +42,9 @@ pdf/noto-sans.ttf:
pdf/genyomintw.ttf:
curl -L "https://github.com/ButTaiwan/genyo-font/raw/bc2fa246196fefc1ef9e9843bc8cdba22523a39d/TW/GenYoMinTW-Heavy.ttf" > $@
pdf/material.ttf:
curl -L "https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.ttf" > $@
demo/assets/roboto1.ttf:
curl -L "https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5vAw.ttf" > $@
... ...
... ... @@ -9,6 +9,7 @@
- Fix the line cap and joint enums
- Fix PdfOutlineMode enum
- Improve API documentation
- Add support for Icon Fonts (MaterialIcons)
## 1.12.0
... ...
... ... @@ -138,6 +138,9 @@ See https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management
/// Default width of a glyph
static const double defaultGlyphWidth = 0.600;
/// Internal units per
int get unitsPerEm;
@override
void _prepare() {
super._prepare();
... ...
... ... @@ -20,17 +20,19 @@ part of pdf;
@immutable
class PdfFontMetrics {
/// Create a PdfFontMetrics object
const PdfFontMetrics(
{@required this.left,
@required this.top,
@required this.right,
@required this.bottom,
double ascent,
double descent,
double advanceWidth})
: ascent = ascent ?? bottom,
const PdfFontMetrics({
@required this.left,
@required this.top,
@required this.right,
@required this.bottom,
double ascent,
double descent,
double advanceWidth,
double leftBearing,
}) : ascent = ascent ?? bottom,
descent = descent ?? top,
advanceWidth = advanceWidth ?? right - left,
leftBearing = leftBearing ?? left,
assert(left != null),
assert(top != null),
assert(right != null),
... ... @@ -55,9 +57,11 @@ class PdfFontMetrics {
double ascent;
double descent;
double lastBearing;
double firstBearing;
double spacing;
for (var metric in metrics) {
firstBearing ??= metric.leftBearing;
left ??= metric.left;
spacing = metric.advanceWidth > 0 ? letterSpacing : 0.0;
right += metric.advanceWidth + spacing;
... ... @@ -70,13 +74,15 @@ class PdfFontMetrics {
}
return PdfFontMetrics(
left: left,
top: top,
right: right - lastBearing - spacing,
bottom: bottom,
ascent: ascent,
descent: descent,
advanceWidth: right - spacing);
left: left,
top: top,
right: right - lastBearing - spacing,
bottom: bottom,
ascent: ascent,
descent: descent,
advanceWidth: right - spacing,
leftBearing: firstBearing,
);
}
/// Zero-sized dimensions
... ... @@ -122,32 +128,36 @@ class PdfFontMetrics {
double get effectiveLeft => math.min(leftBearing, 0);
/// Starting point
double get leftBearing => left;
final double leftBearing;
/// Ending point
double get rightBearing => advanceWidth - right;
@override
String toString() =>
'PdfFontMetrics(left:$left, top:$top, right:$right, bottom:$bottom, ascent:$ascent, descent:$descent, advanceWidth:$advanceWidth)';
'PdfFontMetrics(left:$left, top:$top, right:$right, bottom:$bottom, ascent:$ascent, descent:$descent, advanceWidth:$advanceWidth, leftBearing:$leftBearing, rightBearing:$rightBearing)';
/// Make a copy of this object
PdfFontMetrics copyWith(
{double left,
double top,
double right,
double bottom,
double ascent,
double descent,
double advanceWidth}) {
PdfFontMetrics copyWith({
double left,
double top,
double right,
double bottom,
double ascent,
double descent,
double advanceWidth,
double leftBearing,
}) {
return PdfFontMetrics(
left: left ?? this.left,
top: top ?? this.top,
right: right ?? this.right,
bottom: bottom ?? this.bottom,
ascent: ascent ?? this.ascent,
descent: descent ?? this.descent,
advanceWidth: advanceWidth ?? this.advanceWidth);
left: left ?? this.left,
top: top ?? this.top,
right: right ?? this.right,
bottom: bottom ?? this.bottom,
ascent: ascent ?? this.ascent,
descent: descent ?? this.descent,
advanceWidth: advanceWidth ?? this.advanceWidth,
leftBearing: leftBearing ?? this.leftBearing,
);
}
/// Multiply this metrics object with a font size
... ... @@ -160,6 +170,7 @@ class PdfFontMetrics {
ascent: ascent * factor,
descent: descent * factor,
advanceWidth: advanceWidth * factor,
leftBearing: leftBearing * factor,
);
}
... ...
... ... @@ -253,6 +253,9 @@ class TtfParser {
final baseOffset = tableOffsets[glyf_table];
final hmtxOffset = tableOffsets[hmtx_table];
final unitsPerEm = this.unitsPerEm;
final numOfLongHorMetrics = this.numOfLongHorMetrics;
final defaultadvanceWidth =
bytes.getUint16(hmtxOffset + (numOfLongHorMetrics - 1) * 4);
var glyphIndex = 0;
for (var offset in glyphOffsets) {
final xMin = bytes.getInt16(baseOffset + offset + 2); // 2
... ... @@ -260,16 +263,23 @@ class TtfParser {
final xMax = bytes.getInt16(baseOffset + offset + 6); // 6
final yMax = bytes.getInt16(baseOffset + offset + 8); // 8
final advanceWidth = glyphIndex < numOfLongHorMetrics
? bytes.getInt16(hmtxOffset + glyphIndex * 4).toDouble() / unitsPerEm
: null;
? bytes.getUint16(hmtxOffset + glyphIndex * 4)
: defaultadvanceWidth;
final leftBearing = glyphIndex < numOfLongHorMetrics
? bytes.getInt16(hmtxOffset + glyphIndex * 4 + 2)
: bytes.getInt16(hmtxOffset +
numOfLongHorMetrics * 4 +
(glyphIndex - numOfLongHorMetrics) * 2);
glyphInfoMap[glyphIndex] = PdfFontMetrics(
left: xMin.toDouble() / unitsPerEm,
top: yMin.toDouble() / unitsPerEm,
right: xMax.toDouble() / unitsPerEm,
bottom: yMax.toDouble() / unitsPerEm,
ascent: ascent.toDouble() / unitsPerEm,
descent: descent.toDouble() / unitsPerEm,
advanceWidth: advanceWidth);
left: xMin.toDouble() / unitsPerEm,
top: yMin.toDouble() / unitsPerEm,
right: xMax.toDouble() / unitsPerEm,
bottom: yMax.toDouble() / unitsPerEm,
ascent: ascent.toDouble() / unitsPerEm,
descent: descent.toDouble() / unitsPerEm,
advanceWidth: advanceWidth.toDouble() / unitsPerEm,
leftBearing: leftBearing.toDouble() / unitsPerEm,
);
glyphIndex++;
}
}
... ...
... ... @@ -187,10 +187,21 @@ class TtfWriter {
final hmtx = Uint8List(_wordAlign(len, 4));
final hmtxOffset = ttf.tableOffsets[TtfParser.hmtx_table];
final hmtxData = hmtx.buffer.asByteData();
final numOfLongHorMetrics = ttf.numOfLongHorMetrics;
final defaultadvanceWidth =
ttf.bytes.getUint16(hmtxOffset + (numOfLongHorMetrics - 1) * 4);
var index = 0;
for (var glyph in glyphsInfo) {
hmtxData.setUint32(
index, ttf.bytes.getInt32(hmtxOffset + glyph.index * 4));
final advanceWidth = glyph.index < numOfLongHorMetrics
? ttf.bytes.getUint16(hmtxOffset + glyph.index * 4)
: defaultadvanceWidth;
final leftBearing = glyph.index < numOfLongHorMetrics
? ttf.bytes.getInt16(hmtxOffset + glyph.index * 4 + 2)
: ttf.bytes.getInt16(hmtxOffset +
numOfLongHorMetrics * 4 +
(glyph.index - numOfLongHorMetrics) * 2);
hmtxData.setUint16(index, advanceWidth);
hmtxData.setInt16(index + 2, leftBearing);
index += 4;
}
tables[TtfParser.hmtx_table] = hmtx;
... ...
... ... @@ -50,6 +50,9 @@ class PdfTtfFont extends PdfFont {
double get descent => font.descent.toDouble() / font.unitsPerEm;
@override
int get unitsPerEm => font.unitsPerEm;
@override
PdfFontMetrics glyphMetrics(int charCode) {
final g = font.charToGlyphIndexMap[charCode];
... ...
... ... @@ -44,6 +44,9 @@ class PdfType1Font extends PdfFont {
@override
final double descent;
@override
int get unitsPerEm => 1000;
/// Width of each glyph
final List<double> widths;
... ...
... ... @@ -20,9 +20,9 @@ import 'dart:collection';
import 'dart:convert';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:image/image.dart' as im;
import 'package:barcode/barcode.dart';
import 'package:image/image.dart' as im;
import 'package:meta/meta.dart';
import 'package:pdf/pdf.dart';
import 'package:vector_math/vector_math_64.dart';
... ... @@ -48,6 +48,7 @@ part 'widgets/font.dart';
part 'widgets/forms.dart';
part 'widgets/geometry.dart';
part 'widgets/grid_view.dart';
part 'widgets/icon.dart';
part 'widgets/image.dart';
part 'widgets/multi_page.dart';
part 'widgets/page.dart';
... ...
... ... @@ -565,12 +565,12 @@ class AspectRatio extends SingleChildWidget {
typedef CustomPainter = Function(PdfGraphics canvas, PdfPoint size);
class CustomPaint extends SingleChildWidget {
CustomPaint(
{this.painter,
this.foregroundPainter,
this.size = PdfPoint.zero,
Widget child})
: super(child: child);
CustomPaint({
this.painter,
this.foregroundPainter,
this.size = PdfPoint.zero,
Widget child,
}) : super(child: child);
final CustomPainter painter;
final CustomPainter foregroundPainter;
... ...
/*
* 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 widget;
/// A description of an icon fulfilled by a font glyph.
@immutable
class IconData {
/// Creates icon data.
const IconData(
this.codePoint, {
this.matchTextDirection = false,
});
/// The Unicode code point at which this icon is stored in the icon font.
final int codePoint;
/// Whether this icon should be automatically mirrored in right-to-left
/// environments.
final bool matchTextDirection;
}
/// Defines the color, opacity, and size of icons.
@immutable
class IconThemeData {
/// Creates an icon theme data.
const IconThemeData({this.color, this.opacity, this.size, this.font});
/// Creates an icon them with some reasonable default values.
const IconThemeData.fallback(this.font)
: color = PdfColors.black,
opacity = 1.0,
size = 24.0;
/// Creates a copy of this icon theme but with the given fields replaced with
/// the new values.
IconThemeData copyWith(
{PdfColor color, double opacity, double size, Font font}) {
return IconThemeData(
color: color ?? this.color,
opacity: opacity ?? this.opacity,
size: size ?? this.size,
font: font ?? this.font,
);
}
/// The default color for icons.
final PdfColor color;
/// An opacity to apply to both explicit and default icon colors.
final double opacity;
/// The default size for icons.
final double size;
/// The font to use
final Font font;
}
/// A graphical icon widget drawn with a glyph from a font described in
/// an [IconData] such as material's predefined [IconData]s in [Icons].
class Icon extends StatelessWidget {
/// Creates an icon.
Icon(
this.icon, {
this.size,
this.color,
this.textDirection,
this.font,
}) : assert(icon != null),
super();
/// The icon to display. The available icons are described in [Icons].
final IconData icon;
/// The size of the icon in logical pixels.
final double size;
/// The color to use when drawing the icon.
final PdfColor color;
/// The text direction to use for rendering the icon.
final TextDirection textDirection;
/// Font to use to draw the icon
final Font font;
@override
Widget build(Context context) {
final textDirection = this.textDirection ?? Directionality.of(context);
final iconTheme = Theme.of(context).iconTheme;
final iconSize = size ?? iconTheme.size;
final iconColor = color ?? iconTheme.color;
final iconOpacity = iconColor.alpha;
final iconFont = font ?? iconTheme.font;
Widget iconWidget = RichText(
textDirection: textDirection,
text: TextSpan(
text: String.fromCharCode(icon.codePoint),
style: TextStyle.defaultStyle().copyWith(
color: iconColor,
fontSize: iconSize,
fontNormal: iconFont,
),
),
);
if (icon.matchTextDirection) {
switch (textDirection) {
case TextDirection.rtl:
iconWidget = Transform(
transform: Matrix4.identity()..scale(-1.0, 1.0, 1.0),
alignment: Alignment.center,
child: iconWidget,
);
break;
case TextDirection.ltr:
break;
}
}
if (iconOpacity < 1.0) {
iconWidget = Opacity(
opacity: iconOpacity,
child: iconWidget,
);
}
return iconWidget;
}
}
... ...
... ... @@ -33,6 +33,7 @@ class ThemeData extends Inherited {
bool softWrap,
TextAlign textAlign,
int maxLines,
IconThemeData iconTheme,
}) {
final base = ThemeData.base();
return base.copyWith(
... ... @@ -50,6 +51,7 @@ class ThemeData extends Inherited {
softWrap: softWrap,
textAlign: textAlign,
maxLines: maxLines,
iconTheme: iconTheme,
);
}
... ... @@ -67,6 +69,7 @@ class ThemeData extends Inherited {
@required this.tableCell,
@required this.softWrap,
@required this.textAlign,
@required this.iconTheme,
this.maxLines,
}) : assert(defaultTextStyle.inherit == false),
assert(paragraphStyle.inherit == false),
... ... @@ -80,16 +83,23 @@ class ThemeData extends Inherited {
assert(tableHeader.inherit == false),
assert(tableCell.inherit == false),
assert(softWrap != null),
assert(maxLines == null || maxLines > 0);
assert(maxLines == null || maxLines > 0),
assert(iconTheme != null);
factory ThemeData.withFont(
{Font base, Font bold, Font italic, Font boldItalic}) {
factory ThemeData.withFont({
Font base,
Font bold,
Font italic,
Font boldItalic,
Font icons,
}) {
final defaultStyle = TextStyle.defaultStyle().copyWith(
font: base,
fontNormal: base,
fontBold: bold,
fontItalic: italic,
fontBoldItalic: boldItalic);
font: base,
fontNormal: base,
fontBold: bold,
fontItalic: italic,
fontBoldItalic: boldItalic,
);
final fontSize = defaultStyle.fontSize;
return ThemeData._(
... ... @@ -107,6 +117,7 @@ class ThemeData extends Inherited {
tableCell: defaultStyle.copyWith(fontSize: fontSize * 0.8),
softWrap: true,
textAlign: TextAlign.left,
iconTheme: IconThemeData.fallback(icons),
);
}
... ... @@ -127,6 +138,7 @@ class ThemeData extends Inherited {
bool softWrap,
TextAlign textAlign,
int maxLines,
IconThemeData iconTheme,
}) =>
ThemeData._(
defaultTextStyle: this.defaultTextStyle.merge(defaultTextStyle),
... ... @@ -143,6 +155,7 @@ class ThemeData extends Inherited {
softWrap: softWrap ?? this.softWrap,
textAlign: textAlign ?? this.textAlign,
maxLines: maxLines ?? this.maxLines,
iconTheme: iconTheme ?? this.iconTheme,
);
final TextStyle defaultTextStyle;
... ... @@ -165,6 +178,8 @@ class ThemeData extends Inherited {
final TextAlign textAlign;
final bool softWrap;
final int maxLines;
final IconThemeData iconTheme;
}
class Theme extends StatelessWidget {
... ...
... ... @@ -38,6 +38,7 @@ import 'widget_container_test.dart' as widget_container;
import 'widget_flex_test.dart' as widget_flex;
import 'widget_form_test.dart' as widget_form;
import 'widget_grid_view_test.dart' as widget_grid_view;
import 'widget_icon_test.dart' as widget_icon;
import 'widget_multipage_test.dart' as widget_multipage;
import 'widget_opacity_test.dart' as widget_opacity;
import 'widget_outline_test.dart' as widget_outline;
... ... @@ -72,6 +73,7 @@ void main() {
widget_flex.main();
widget_form.main();
widget_grid_view.main();
widget_icon.main();
widget_multipage.main();
widget_opacity.main();
widget_outline.main();
... ...
... ... @@ -22,50 +22,94 @@ import 'package:pdf/widgets.dart';
import 'package:test/test.dart';
void printMetrics(
PdfGraphics canvas, String text, PdfFont font, PdfPoint size) {
final metricsUnscales = font.stringMetrics(text);
final fontSizeW = size.x / metricsUnscales.maxWidth;
final fontSizeH = size.y / metricsUnscales.maxHeight;
PdfGraphics canvas,
int codeUnit,
PdfFont font,
PdfPoint size,
) {
final metricsUnscaled = font.glyphMetrics(codeUnit);
final fontSizeW = size.x / metricsUnscaled.maxWidth;
final fontSizeH = size.y / metricsUnscaled.maxHeight;
final fontSize = min(fontSizeW, fontSizeH);
final metrics = metricsUnscales * fontSize;
final metrics = metricsUnscaled * fontSize;
final m = metricsUnscaled * font.unitsPerEm.toDouble();
const deb = 20;
const s = 5.0;
final x = (size.x - metrics.maxWidth) / 2.0;
final y = (size.y - metrics.maxHeight) / 2.0 - metrics.descent;
int index;
if (font is PdfTtfFont) {
index = font.font.charToGlyphIndexMap[codeUnit];
}
canvas
..setLineWidth(0.5)
// Glyph maximum size
..drawRect(x, y + metrics.descent, metrics.advanceWidth, metrics.maxHeight)
..setStrokeColor(PdfColors.green)
..strokePath()
// Glyph bounding box
..drawRect(x + metrics.left, y + metrics.top, metrics.width, metrics.height)
..setStrokeColor(PdfColors.amber)
..strokePath()
// Glyph baseline
..drawLine(x + metrics.effectiveLeft - deb, y,
x + metrics.maxWidth + metrics.effectiveLeft + deb, y)
..setColor(PdfColors.blue)
..strokePath()
// Drawing Start
..drawEllipse(x, y, 5, 5)
..setFillColor(PdfColors.black)
..fillPath()
// Next glyph
..drawEllipse(x + metrics.advanceWidth, y, 5, 5)
..setFillColor(PdfColors.red)
..fillPath()
// Left Bearing
..saveContext()
..setGraphicState(const PdfGraphicState(opacity: 0.5))
..drawEllipse(x + metrics.leftBearing, y, 5, 5)
..setFillColor(PdfColors.purple)
..fillPath()
..restoreContext()
// The glyph
..setFillColor(PdfColors.grey)
..drawString(font, fontSize, text, x, y);
..drawString(font, fontSize, String.fromCharCode(codeUnit), x, y)
// Metrics information
..setFillColor(PdfColors.black)
..drawString(canvas.defaultFont, s,
'unicode: 0x${codeUnit.toRadixString(16)}', 10, size.y - 20 - s * 0)
..drawString(canvas.defaultFont, s, 'index: 0x${index.toRadixString(16)}',
10, size.y - 20 - s * 1)
..drawString(canvas.defaultFont, s, 'left: ${m.left.toInt()}', 10,
size.y - 20 - s * 2)
..drawString(canvas.defaultFont, s, 'right: ${m.right.toInt()}', 10,
size.y - 20 - s * 3)
..drawString(
canvas.defaultFont, s, 'top: ${m.top.toInt()}', 10, size.y - 20 - s * 4)
..drawString(canvas.defaultFont, s, 'bottom: ${m.bottom.toInt()}', 10,
size.y - 20 - s * 5)
..drawString(canvas.defaultFont, s,
'advanceWidth: ${m.advanceWidth.toInt()}', 10, size.y - 20 - s * 6)
..drawString(canvas.defaultFont, s, 'leftBearing: ${m.leftBearing.toInt()}',
10, size.y - 20 - s * 7);
}
void main() {
test('Pdf Font Metrics', () {
final pdf = Document();
PdfFont.courier(pdf.document);
final ttfFont = File('open-sans.ttf');
final fontData = ttfFont.readAsBytesSync();
final font = PdfTtfFont(pdf.document, fontData.buffer.asByteData());
for (var letter
in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&%!?0123456789'
for (var letter in
//font.font.charToGlyphIndexMap.keys
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&%!?0123456789'
.codeUnits) {
pdf.addPage(Page(
pageFormat: const PdfPageFormat(500, 500, marginAll: 20),
... ... @@ -76,8 +120,7 @@ void main() {
child: CustomPaint(
size: const PdfPoint(200, 200),
painter: (PdfGraphics canvas, PdfPoint size) {
printMetrics(
canvas, String.fromCharCode(letter), font, size);
printMetrics(canvas, letter, font, size);
})));
}));
}
... ...
... ... @@ -81,3 +81,55 @@ Font loadFont(String filename) {
final data = File(filename).readAsBytesSync();
return Font.ttf(data.buffer.asByteData());
}
void hexDump(
ByteData bytes,
int offset,
int length, [
int highlight,
int highlightLength,
]) {
const reset = '\x1B[0m';
const red = '\x1B[1;31m';
var s = '';
var t = '';
var n = 0;
var hl = false;
for (var i = 0; i < length; i++) {
final b = bytes.getUint8(offset + i);
if (highlight != null && highlightLength != null) {
if (offset + i >= highlight && offset + i < highlight + highlightLength) {
if (!hl) {
hl = true;
s += red;
t += red;
}
} else {
if (hl) {
hl = false;
s += reset;
t += reset;
}
}
}
s += b.toRadixString(16).padLeft(2, '0') + ' ';
if (b > 31 && b < 128) {
t += String.fromCharCode(b);
} else {
t += '.';
}
n++;
if (n % 16 == 0) {
if (hl) {
s += reset;
t += reset;
hl = false;
}
print('$s $t');
s = '';
t = '';
}
}
print('$s $t');
}
... ...
/*
* 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.
*/
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart';
import 'package:test/test.dart';
import 'utils.dart';
Document pdf;
Font icons;
void main() {
setUpAll(() {
Document.debug = true;
pdf = Document();
icons = loadFont('material.ttf');
});
test('Icon Widgets', () {
pdf.addPage(
MultiPage(
theme: ThemeData.withFont(icons: icons),
build: (Context context) {
final iconList = List<IconData>();
final pdfFont = icons.getFont(context);
if (pdfFont is PdfTtfFont) {
iconList.addAll(
pdfFont.font.charToGlyphIndexMap.keys
.where((e) => e > 0x7f && e < 0xe05d)
.map((e) => IconData(e)),
);
}
return <Widget>[
Wrap(
spacing: 10,
runSpacing: 10,
children: <Widget>[
...iconList.map<Widget>(
(e) => Column(children: [
Icon(e, size: 50, color: PdfColors.blueGrey),
Text('0x${e.codePoint.toRadixString(16)}'),
]),
),
],
),
];
},
),
);
});
tearDownAll(() {
final file = File('widgets-icons.pdf');
file.writeAsBytesSync(pdf.save());
});
}
... ...
No preview for this file type
No preview for this file type