David PHAM-VAN

Improve Theme override

@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 library widget; 17 library widget;
18 18
19 import 'dart:math' as math; 19 import 'dart:math' as math;
  20 +import 'dart:typed_data';
20 21
21 import 'package:meta/meta.dart'; 22 import 'package:meta/meta.dart';
22 import 'package:pdf/pdf.dart'; 23 import 'package:pdf/pdf.dart';
@@ -28,6 +29,7 @@ part 'widgets/container.dart'; @@ -28,6 +29,7 @@ part 'widgets/container.dart';
28 part 'widgets/content.dart'; 29 part 'widgets/content.dart';
29 part 'widgets/document.dart'; 30 part 'widgets/document.dart';
30 part 'widgets/flex.dart'; 31 part 'widgets/flex.dart';
  32 +part 'widgets/font.dart';
31 part 'widgets/geometry.dart'; 33 part 'widgets/geometry.dart';
32 part 'widgets/grid_view.dart'; 34 part 'widgets/grid_view.dart';
33 part 'widgets/image.dart'; 35 part 'widgets/image.dart';
@@ -17,53 +17,70 @@ @@ -17,53 +17,70 @@
17 part of widget; 17 part of widget;
18 18
19 class Header extends StatelessWidget { 19 class Header extends StatelessWidget {
20 - Header({this.level = 1, this.text, this.child}) 20 + Header(
  21 + {this.level = 1,
  22 + this.text,
  23 + this.child,
  24 + this.decoration,
  25 + this.margin,
  26 + this.padding,
  27 + this.textStyle})
21 : assert(level >= 0 && level <= 5); 28 : assert(level >= 0 && level <= 5);
22 29
23 final String text; 30 final String text;
  31 +
24 final Widget child; 32 final Widget child;
  33 +
25 final int level; 34 final int level;
26 35
  36 + final BoxDecoration decoration;
  37 +
  38 + final EdgeInsets margin;
  39 +
  40 + final EdgeInsets padding;
  41 +
  42 + final TextStyle textStyle;
  43 +
27 @override 44 @override
28 Widget build(Context context) { 45 Widget build(Context context) {
29 - BoxDecoration _decoration;  
30 - EdgeInsets _margin;  
31 - EdgeInsets _padding;  
32 - double _textSize; 46 + BoxDecoration _decoration = decoration;
  47 + EdgeInsets _margin = margin;
  48 + EdgeInsets _padding = padding;
  49 + TextStyle _textStyle = textStyle;
33 switch (level) { 50 switch (level) {
34 case 0: 51 case 0:
35 - _margin = const EdgeInsets.only(bottom: 5.0 * PdfPageFormat.mm);  
36 - _padding = const EdgeInsets.only(bottom: 1.0 * PdfPageFormat.mm);  
37 - _decoration = 52 + _margin ??= const EdgeInsets.only(bottom: 5.0 * PdfPageFormat.mm);
  53 + _padding ??= const EdgeInsets.only(bottom: 1.0 * PdfPageFormat.mm);
  54 + _decoration ??=
38 const BoxDecoration(border: BoxBorder(bottom: true, width: 1.0)); 55 const BoxDecoration(border: BoxBorder(bottom: true, width: 1.0));
39 - _textSize = 2.0; 56 + _textStyle ??= Theme.of(context).header0;
40 break; 57 break;
41 case 1: 58 case 1:
42 - _margin = const EdgeInsets.only( 59 + _margin ??= const EdgeInsets.only(
43 top: 3.0 * PdfPageFormat.mm, bottom: 5.0 * PdfPageFormat.mm); 60 top: 3.0 * PdfPageFormat.mm, bottom: 5.0 * PdfPageFormat.mm);
44 - _decoration = 61 + _decoration ??=
45 const BoxDecoration(border: BoxBorder(bottom: true, width: 0.2)); 62 const BoxDecoration(border: BoxBorder(bottom: true, width: 0.2));
46 - _textSize = 1.5; 63 + _textStyle ??= Theme.of(context).header1;
47 break; 64 break;
48 case 2: 65 case 2:
49 - _margin = const EdgeInsets.only( 66 + _margin ??= const EdgeInsets.only(
50 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm); 67 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm);
51 - _textSize = 1.4; 68 + _textStyle ??= Theme.of(context).header2;
52 break; 69 break;
53 case 3: 70 case 3:
54 - _margin = const EdgeInsets.only( 71 + _margin ??= const EdgeInsets.only(
55 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm); 72 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm);
56 - _textSize = 1.3; 73 + _textStyle ??= Theme.of(context).header3;
57 break; 74 break;
58 case 4: 75 case 4:
59 - _margin = const EdgeInsets.only( 76 + _margin ??= const EdgeInsets.only(
60 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm); 77 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm);
61 - _textSize = 1.2; 78 + _textStyle ??= Theme.of(context).header4;
62 break; 79 break;
63 case 5: 80 case 5:
64 - _margin = const EdgeInsets.only( 81 + _margin ??= const EdgeInsets.only(
65 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm); 82 top: 2.0 * PdfPageFormat.mm, bottom: 4.0 * PdfPageFormat.mm);
66 - _textSize = 1.1; 83 + _textStyle ??= Theme.of(context).header5;
67 break; 84 break;
68 } 85 }
69 return Container( 86 return Container(
@@ -71,53 +88,96 @@ class Header extends StatelessWidget { @@ -71,53 +88,96 @@ class Header extends StatelessWidget {
71 margin: _margin, 88 margin: _margin,
72 padding: _padding, 89 padding: _padding,
73 decoration: _decoration, 90 decoration: _decoration,
74 - child: child ?? Text(text, textScaleFactor: _textSize), 91 + child: child ?? Text(text, style: _textStyle),
75 ); 92 );
76 } 93 }
77 } 94 }
78 95
79 class Paragraph extends StatelessWidget { 96 class Paragraph extends StatelessWidget {
80 - Paragraph({this.text}); 97 + Paragraph(
  98 + {this.text,
  99 + this.textAlign = TextAlign.justify,
  100 + this.style,
  101 + this.margin = const EdgeInsets.only(bottom: 5.0 * PdfPageFormat.mm),
  102 + this.padding});
81 103
82 final String text; 104 final String text;
83 105
  106 + final TextAlign textAlign;
  107 +
  108 + final TextStyle style;
  109 +
  110 + final EdgeInsets margin;
  111 +
  112 + final EdgeInsets padding;
  113 +
84 @override 114 @override
85 Widget build(Context context) { 115 Widget build(Context context) {
86 return Container( 116 return Container(
87 - margin: const EdgeInsets.only(bottom: 5.0 * PdfPageFormat.mm), 117 + margin: margin,
  118 + padding: padding,
88 child: Text( 119 child: Text(
89 text, 120 text,
90 - textAlign: TextAlign.justify,  
91 - style: Theme.of(context).paragraphStyle, 121 + textAlign: textAlign,
  122 + style: style ?? Theme.of(context).paragraphStyle,
92 ), 123 ),
93 ); 124 );
94 } 125 }
95 } 126 }
96 127
97 class Bullet extends StatelessWidget { 128 class Bullet extends StatelessWidget {
98 - Bullet({this.text}); 129 + Bullet(
  130 + {this.text,
  131 + this.textAlign = TextAlign.left,
  132 + this.style,
  133 + this.margin = const EdgeInsets.only(bottom: 2.0 * PdfPageFormat.mm),
  134 + this.padding,
  135 + this.bulletSize = 2.0 * PdfPageFormat.mm,
  136 + this.bulletMargin = const EdgeInsets.only(
  137 + top: 1.5 * PdfPageFormat.mm,
  138 + left: 5.0 * PdfPageFormat.mm,
  139 + right: 2.0 * PdfPageFormat.mm,
  140 + ),
  141 + this.bulletShape = BoxShape.circle,
  142 + this.bulletColor = PdfColor.black});
99 143
100 final String text; 144 final String text;
101 145
  146 + final TextAlign textAlign;
  147 +
  148 + final TextStyle style;
  149 +
  150 + final EdgeInsets margin;
  151 +
  152 + final EdgeInsets padding;
  153 +
  154 + final EdgeInsets bulletMargin;
  155 +
  156 + final double bulletSize;
  157 +
  158 + final BoxShape bulletShape;
  159 +
  160 + final PdfColor bulletColor;
  161 +
102 @override 162 @override
103 Widget build(Context context) { 163 Widget build(Context context) {
104 return Container( 164 return Container(
105 - margin: const EdgeInsets.only(bottom: 2.0 * PdfPageFormat.mm), 165 + margin: margin,
  166 + padding: padding,
106 child: Row( 167 child: Row(
107 crossAxisAlignment: CrossAxisAlignment.start, 168 crossAxisAlignment: CrossAxisAlignment.start,
108 children: <Widget>[ 169 children: <Widget>[
109 Container( 170 Container(
110 - width: 2.0 * PdfPageFormat.mm,  
111 - height: 2.0 * PdfPageFormat.mm,  
112 - margin: const EdgeInsets.only(  
113 - top: 0.5 * PdfPageFormat.mm,  
114 - left: 5.0 * PdfPageFormat.mm,  
115 - right: 2.0 * PdfPageFormat.mm,  
116 - ),  
117 - decoration: const BoxDecoration(  
118 - color: PdfColor.black, shape: BoxShape.circle), 171 + width: bulletSize,
  172 + height: bulletSize,
  173 + margin: bulletMargin,
  174 + decoration:
  175 + BoxDecoration(color: bulletColor, shape: bulletShape),
119 ), 176 ),
120 - Expanded(child: Text(text, style: Theme.of(context).bulletStyle)) 177 + Expanded(
  178 + child: Text(text,
  179 + textAlign: textAlign,
  180 + style: Theme.of(context).bulletStyle))
121 ])); 181 ]));
122 } 182 }
123 } 183 }
@@ -85,8 +85,7 @@ class Page extends BasePage { @@ -85,8 +85,7 @@ class Page extends BasePage {
85 final BoxConstraints constraints = BoxConstraints( 85 final BoxConstraints constraints = BoxConstraints(
86 maxWidth: pageFormat.width, maxHeight: pageFormat.height); 86 maxWidth: pageFormat.width, maxHeight: pageFormat.height);
87 87
88 - final Theme calculatedTheme =  
89 - theme ?? document.theme ?? Theme(document.document); 88 + final Theme calculatedTheme = theme ?? document.theme ?? Theme.base();
90 final Map<Type, Inherited> inherited = <Type, Inherited>{}; 89 final Map<Type, Inherited> inherited = <Type, Inherited>{};
91 inherited[calculatedTheme.runtimeType] = calculatedTheme; 90 inherited[calculatedTheme.runtimeType] = calculatedTheme;
92 final Context context = 91 final Context context =
@@ -142,9 +141,10 @@ class MultiPage extends Page { @@ -142,9 +141,10 @@ class MultiPage extends Page {
142 this.crossAxisAlignment = CrossAxisAlignment.start, 141 this.crossAxisAlignment = CrossAxisAlignment.start,
143 this.header, 142 this.header,
144 this.footer, 143 this.footer,
  144 + Theme theme,
145 EdgeInsets margin}) 145 EdgeInsets margin})
146 : _buildList = build, 146 : _buildList = build,
147 - super(pageFormat: pageFormat, margin: margin); 147 + super(pageFormat: pageFormat, margin: margin, theme: theme);
148 148
149 final BuildListCallback _buildList; 149 final BuildListCallback _buildList;
150 150
@@ -164,8 +164,7 @@ class MultiPage extends Page { @@ -164,8 +164,7 @@ class MultiPage extends Page {
164 maxWidth: pageFormat.width, maxHeight: pageFormat.height); 164 maxWidth: pageFormat.width, maxHeight: pageFormat.height);
165 final BoxConstraints childConstraints = 165 final BoxConstraints childConstraints =
166 BoxConstraints(maxWidth: constraints.maxWidth - margin.horizontal); 166 BoxConstraints(maxWidth: constraints.maxWidth - margin.horizontal);
167 - final Theme calculatedTheme =  
168 - theme ?? document.theme ?? Theme(document.document); 167 + final Theme calculatedTheme = theme ?? document.theme ?? Theme.base();
169 final Map<Type, Inherited> inherited = <Type, Inherited>{}; 168 final Map<Type, Inherited> inherited = <Type, Inherited>{};
170 inherited[calculatedTheme.runtimeType] = calculatedTheme; 169 inherited[calculatedTheme.runtimeType] = calculatedTheme;
171 Context context; 170 Context context;
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +part of widget;
  18 +
  19 +enum Type1Fonts {
  20 + courier,
  21 + courierBold,
  22 + courierBoldOblique,
  23 + courierOblique,
  24 + helvetica,
  25 + helveticaBold,
  26 + helveticaBoldOblique,
  27 + helveticaOblique,
  28 + times,
  29 + timesBold,
  30 + timesBoldItalic,
  31 + timesItalic,
  32 + symbol,
  33 + zapfDingbats
  34 +}
  35 +
  36 +/// Lazy font declaration, registers the font in the document only if needed.
  37 +/// Tries to register a font only once
  38 +class Font {
  39 + Font() : font = null;
  40 +
  41 + Font.type1(this.font) : assert(font != null);
  42 +
  43 + factory Font.courier() => Font.type1(Type1Fonts.courier);
  44 + factory Font.courierBold() => Font.type1(Type1Fonts.courierBold);
  45 + factory Font.courierBoldOblique() =>
  46 + Font.type1(Type1Fonts.courierBoldOblique);
  47 + factory Font.courierOblique() => Font.type1(Type1Fonts.courierOblique);
  48 + factory Font.helvetica() => Font.type1(Type1Fonts.helvetica);
  49 + factory Font.helveticaBold() => Font.type1(Type1Fonts.helveticaBold);
  50 + factory Font.helveticaBoldOblique() =>
  51 + Font.type1(Type1Fonts.helveticaBoldOblique);
  52 + factory Font.helveticaOblique() => Font.type1(Type1Fonts.helveticaOblique);
  53 + factory Font.times() => Font.type1(Type1Fonts.times);
  54 + factory Font.timesBold() => Font.type1(Type1Fonts.timesBold);
  55 + factory Font.timesBoldItalic() => Font.type1(Type1Fonts.timesBoldItalic);
  56 + factory Font.timesItalic() => Font.type1(Type1Fonts.timesItalic);
  57 + factory Font.symbol() => Font.type1(Type1Fonts.symbol);
  58 + factory Font.zapfDingbats() => Font.type1(Type1Fonts.zapfDingbats);
  59 +
  60 + final Type1Fonts font;
  61 +
  62 + @protected
  63 + PdfFont buildFont(PdfDocument pdfDocument) {
  64 + const Map<Type1Fonts, String> type1Map = <Type1Fonts, String>{
  65 + Type1Fonts.courier: 'Courier',
  66 + Type1Fonts.courierBold: 'Courier-Bold',
  67 + Type1Fonts.courierBoldOblique: 'Courier-BoldOblique',
  68 + Type1Fonts.courierOblique: 'Courier-Oblique',
  69 + Type1Fonts.helvetica: 'Helvetica',
  70 + Type1Fonts.helveticaBold: 'Helvetica-Bold',
  71 + Type1Fonts.helveticaBoldOblique: 'Helvetica-BoldOblique',
  72 + Type1Fonts.helveticaOblique: 'Helvetica-Oblique',
  73 + Type1Fonts.times: 'Times-Roman',
  74 + Type1Fonts.timesBold: 'Times-Bold',
  75 + Type1Fonts.timesBoldItalic: 'Times-BoldItalic',
  76 + Type1Fonts.timesItalic: 'Times-Italic',
  77 + Type1Fonts.symbol: 'Symbol',
  78 + Type1Fonts.zapfDingbats: 'ZapfDingbats'
  79 + };
  80 +
  81 + final String fontName = type1Map[font];
  82 + final PdfFont existing = pdfDocument.fonts.firstWhere(
  83 + (PdfFont font) => font.subtype == '/Type1' && font.fontName == fontName,
  84 + orElse: () => null,
  85 + );
  86 +
  87 + if (existing != null) {
  88 + return existing;
  89 + }
  90 +
  91 + switch (font) {
  92 + case Type1Fonts.courier:
  93 + return PdfFont.courier(pdfDocument);
  94 + case Type1Fonts.courierBold:
  95 + return PdfFont.courierBold(pdfDocument);
  96 + case Type1Fonts.courierBoldOblique:
  97 + return PdfFont.courierBoldOblique(pdfDocument);
  98 + case Type1Fonts.courierOblique:
  99 + return PdfFont.courierOblique(pdfDocument);
  100 + case Type1Fonts.helvetica:
  101 + return PdfFont.helvetica(pdfDocument);
  102 + case Type1Fonts.helveticaBold:
  103 + return PdfFont.helveticaBold(pdfDocument);
  104 + case Type1Fonts.helveticaBoldOblique:
  105 + return PdfFont.helveticaBoldOblique(pdfDocument);
  106 + case Type1Fonts.helveticaOblique:
  107 + return PdfFont.helveticaOblique(pdfDocument);
  108 + case Type1Fonts.times:
  109 + return PdfFont.times(pdfDocument);
  110 + case Type1Fonts.timesBold:
  111 + return PdfFont.timesBold(pdfDocument);
  112 + case Type1Fonts.timesBoldItalic:
  113 + return PdfFont.timesBoldItalic(pdfDocument);
  114 + case Type1Fonts.timesItalic:
  115 + return PdfFont.timesItalic(pdfDocument);
  116 + case Type1Fonts.symbol:
  117 + return PdfFont.symbol(pdfDocument);
  118 + case Type1Fonts.zapfDingbats:
  119 + return PdfFont.zapfDingbats(pdfDocument);
  120 + }
  121 + return PdfFont.helvetica(pdfDocument);
  122 + }
  123 +
  124 + PdfFont _pdfFont;
  125 +
  126 + PdfFont getFont(Context context) {
  127 + if (_pdfFont == null) {
  128 + final PdfDocument pdfDocument = context.page.pdfDocument;
  129 + _pdfFont = buildFont(pdfDocument);
  130 + }
  131 +
  132 + return _pdfFont;
  133 + }
  134 +}
  135 +
  136 +class TtfFont extends Font {
  137 + TtfFont(this.data);
  138 +
  139 + final ByteData data;
  140 + @override
  141 + PdfFont buildFont(PdfDocument pdfDocument) {
  142 + return PdfTtfFont(pdfDocument, data);
  143 + }
  144 +}
@@ -16,69 +16,6 @@ @@ -16,69 +16,6 @@
16 16
17 part of widget; 17 part of widget;
18 18
19 -@immutable  
20 -class TextStyle {  
21 - const TextStyle({  
22 - this.color = PdfColor.black,  
23 - @required this.font,  
24 - this.fontSize = _defaultFontSize,  
25 - this.letterSpacing = 1.0,  
26 - this.wordSpacing = 1.0,  
27 - this.lineSpacing = 0.0,  
28 - this.height = 1.0,  
29 - this.background,  
30 - }) : assert(font != null),  
31 - assert(color != null);  
32 -  
33 - final PdfColor color;  
34 -  
35 - final PdfFont font;  
36 -  
37 - // font height, in pdf unit  
38 - final double fontSize;  
39 -  
40 - static const double _defaultFontSize = 12.0 * PdfPageFormat.point;  
41 -  
42 - // spacing between letters, 1.0 being natural spacing  
43 - final double letterSpacing;  
44 -  
45 - // spacing between lines, in pdf unit  
46 - final double lineSpacing;  
47 -  
48 - // spacing between words, 1.0 being natural spacing  
49 - final double wordSpacing;  
50 -  
51 - final double height;  
52 -  
53 - final PdfColor background;  
54 -  
55 - TextStyle copyWith({  
56 - PdfColor color,  
57 - PdfFont font,  
58 - double fontSize,  
59 - double letterSpacing,  
60 - double wordSpacing,  
61 - double lineSpacing,  
62 - double height,  
63 - PdfColor background,  
64 - }) {  
65 - return TextStyle(  
66 - color: color ?? this.color,  
67 - font: font ?? this.font,  
68 - fontSize: fontSize ?? this.fontSize,  
69 - letterSpacing: letterSpacing ?? this.letterSpacing,  
70 - wordSpacing: wordSpacing ?? this.wordSpacing,  
71 - lineSpacing: lineSpacing ?? this.lineSpacing,  
72 - height: height ?? this.height,  
73 - background: background ?? this.background,  
74 - );  
75 - }  
76 -  
77 - @override  
78 - String toString() =>  
79 - 'TextStyle(color:$color font:$font letterSpacing:$letterSpacing wordSpacing:$wordSpacing lineSpacing:$lineSpacing height:$height background:$background)';  
80 -}  
81 -  
82 enum TextAlign { left, right, center, justify } 19 enum TextAlign { left, right, center, justify }
83 20
84 class _Word { 21 class _Word {
@@ -235,9 +172,10 @@ class RichText extends Widget { @@ -235,9 +172,10 @@ class RichText extends Widget {
235 } 172 }
236 173
237 final TextStyle style = span.style ?? defaultstyle; 174 final TextStyle style = span.style ?? defaultstyle;
  175 + final PdfFont font = style.font.getFont(context);
238 176
239 final PdfFontMetrics space = 177 final PdfFontMetrics space =
240 - style.font.stringMetrics(' ') * (style.fontSize * textScaleFactor); 178 + font.stringMetrics(' ') * (style.fontSize * textScaleFactor);
241 179
242 for (String word in span.text.split(' ')) { 180 for (String word in span.text.split(' ')) {
243 if (word.isEmpty) { 181 if (word.isEmpty) {
@@ -246,7 +184,7 @@ class RichText extends Widget { @@ -246,7 +184,7 @@ class RichText extends Widget {
246 } 184 }
247 185
248 final PdfFontMetrics metrics = 186 final PdfFontMetrics metrics =
249 - style.font.stringMetrics(word) * (style.fontSize * textScaleFactor); 187 + font.stringMetrics(word) * (style.fontSize * textScaleFactor);
250 188
251 if (offsetX + metrics.width > constraintWidth) { 189 if (offsetX + metrics.width > constraintWidth) {
252 if (wCount == 0) { 190 if (wCount == 0) {
@@ -326,7 +264,7 @@ class RichText extends Widget { @@ -326,7 +264,7 @@ class RichText extends Widget {
326 } 264 }
327 265
328 context.canvas.drawString( 266 context.canvas.drawString(
329 - currentStyle.font, 267 + currentStyle.font.getFont(context),
330 currentStyle.fontSize * textScaleFactor, 268 currentStyle.fontSize * textScaleFactor,
331 word.text, 269 word.text,
332 box.x + word.offset.x, 270 box.x + word.offset.x,
@@ -16,55 +16,129 @@ @@ -16,55 +16,129 @@
16 16
17 part of widget; 17 part of widget;
18 18
19 -class Theme extends Inherited {  
20 - Theme(this.document);  
21 -  
22 - final PdfDocument document;  
23 -  
24 - static Theme of(Context context) {  
25 - return context.inherited[Theme]; 19 +@immutable
  20 +class TextStyle {
  21 + const TextStyle({
  22 + this.color = PdfColor.black,
  23 + @required this.font,
  24 + this.fontSize = _defaultFontSize,
  25 + this.letterSpacing = 1.0,
  26 + this.wordSpacing = 1.0,
  27 + this.lineSpacing = 0.0,
  28 + this.height = 1.0,
  29 + this.background,
  30 + }) : assert(font != null),
  31 + assert(color != null);
  32 +
  33 + final PdfColor color;
  34 +
  35 + final Font font;
  36 +
  37 + // font height, in pdf unit
  38 + final double fontSize;
  39 +
  40 + static const double _defaultFontSize = 12.0 * PdfPageFormat.point;
  41 +
  42 + // spacing between letters, 1.0 being natural spacing
  43 + final double letterSpacing;
  44 +
  45 + // spacing between lines, in pdf unit
  46 + final double lineSpacing;
  47 +
  48 + // spacing between words, 1.0 being natural spacing
  49 + final double wordSpacing;
  50 +
  51 + final double height;
  52 +
  53 + final PdfColor background;
  54 +
  55 + TextStyle copyWith({
  56 + PdfColor color,
  57 + Font font,
  58 + double fontSize,
  59 + double letterSpacing,
  60 + double wordSpacing,
  61 + double lineSpacing,
  62 + double height,
  63 + PdfColor background,
  64 + }) {
  65 + return TextStyle(
  66 + color: color ?? this.color,
  67 + font: font ?? this.font,
  68 + fontSize: fontSize ?? this.fontSize,
  69 + letterSpacing: letterSpacing ?? this.letterSpacing,
  70 + wordSpacing: wordSpacing ?? this.wordSpacing,
  71 + lineSpacing: lineSpacing ?? this.lineSpacing,
  72 + height: height ?? this.height,
  73 + background: background ?? this.background,
  74 + );
26 } 75 }
27 76
28 - TextStyle _defaultTextStyle; 77 + @override
  78 + String toString() =>
  79 + 'TextStyle(color:$color font:$font letterSpacing:$letterSpacing wordSpacing:$wordSpacing lineSpacing:$lineSpacing height:$height background:$background)';
  80 +}
29 81
30 - TextStyle get defaultTextStyle {  
31 - _defaultTextStyle ??= TextStyle(font: PdfFont.helvetica(document));  
32 - return _defaultTextStyle; 82 +@immutable
  83 +class Theme extends Inherited {
  84 + Theme({
  85 + @required this.defaultTextStyle,
  86 + @required this.defaultTextStyleBold,
  87 + @required this.paragraphStyle,
  88 + @required this.header0,
  89 + @required this.header1,
  90 + @required this.header2,
  91 + @required this.header3,
  92 + @required this.header4,
  93 + @required this.header5,
  94 + @required this.bulletStyle,
  95 + @required this.tableHeader,
  96 + @required this.tableCell,
  97 + });
  98 +
  99 + factory Theme.withFont(Font baseFont, Font baseFontBold) {
  100 + final TextStyle defaultTextStyle = TextStyle(font: baseFont);
  101 + final TextStyle defaultTextStyleBold = TextStyle(font: baseFontBold);
  102 + final double fontSize = defaultTextStyle.fontSize;
  103 +
  104 + return Theme(
  105 + defaultTextStyle: defaultTextStyle,
  106 + defaultTextStyleBold: defaultTextStyleBold,
  107 + paragraphStyle: defaultTextStyle.copyWith(lineSpacing: 5.0),
  108 + bulletStyle: defaultTextStyle.copyWith(lineSpacing: 5.0),
  109 + header0: defaultTextStyleBold.copyWith(fontSize: fontSize * 2.0),
  110 + header1: defaultTextStyleBold.copyWith(fontSize: fontSize * 1.5),
  111 + header2: defaultTextStyleBold.copyWith(fontSize: fontSize * 1.4),
  112 + header3: defaultTextStyleBold.copyWith(fontSize: fontSize * 1.3),
  113 + header4: defaultTextStyleBold.copyWith(fontSize: fontSize * 1.2),
  114 + header5: defaultTextStyleBold.copyWith(fontSize: fontSize * 1.1),
  115 + tableHeader: defaultTextStyleBold,
  116 + tableCell: defaultTextStyle);
33 } 117 }
34 118
35 - TextStyle _defaultTextStyleBold; 119 + factory Theme.base() =>
  120 + Theme.withFont(Font.helvetica(), Font.helveticaBold());
36 121
37 - TextStyle get defaultTextStyleBold {  
38 - _defaultTextStyleBold ??=  
39 - defaultTextStyle.copyWith(font: PdfFont.helveticaBold(document));  
40 - return _defaultTextStyleBold; 122 + static Theme of(Context context) {
  123 + return context.inherited[Theme];
41 } 124 }
42 125
43 - TextStyle _paragraphStyle; 126 + final TextStyle defaultTextStyle;
44 127
45 - TextStyle get paragraphStyle {  
46 - _paragraphStyle ??= defaultTextStyle.copyWith(lineSpacing: 5.0);  
47 - return _paragraphStyle;  
48 - } 128 + final TextStyle defaultTextStyleBold;
49 129
50 - TextStyle _bulletStyle; 130 + final TextStyle paragraphStyle;
51 131
52 - TextStyle get bulletStyle {  
53 - _bulletStyle ??= defaultTextStyle.copyWith(lineSpacing: 5.0);  
54 - return _bulletStyle;  
55 - } 132 + final TextStyle header0;
  133 + final TextStyle header1;
  134 + final TextStyle header2;
  135 + final TextStyle header3;
  136 + final TextStyle header4;
  137 + final TextStyle header5;
56 138
57 - TextStyle _tableHeader; 139 + final TextStyle bulletStyle;
58 140
59 - TextStyle get tableHeader {  
60 - _tableHeader ??= defaultTextStyleBold;  
61 - return _tableHeader;  
62 - }  
63 -  
64 - TextStyle _tableCell; 141 + final TextStyle tableHeader;
65 142
66 - TextStyle get tableCell {  
67 - _tableCell ??= defaultTextStyle;  
68 - return _tableCell;  
69 - } 143 + final TextStyle tableCell;
70 } 144 }
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 16
17 import 'dart:convert'; 17 import 'dart:convert';
18 import 'dart:io'; 18 import 'dart:io';
19 -import 'dart:typed_data';  
20 19
21 import 'package:pdf/pdf.dart'; 20 import 'package:pdf/pdf.dart';
22 import 'package:pdf/widgets.dart'; 21 import 'package:pdf/widgets.dart';
@@ -28,8 +27,7 @@ void main() { @@ -28,8 +27,7 @@ void main() {
28 27
29 final Document pdf = Document(); 28 final Document pdf = Document();
30 29
31 - final TextStyle symbol =  
32 - TextStyle(font: PdfFont.zapfDingbats(pdf.document)); 30 + final TextStyle symbol = TextStyle(font: Font.zapfDingbats());
33 31
34 final List<int> imData = zlib.decode(base64.decode( 32 final List<int> imData = zlib.decode(base64.decode(
35 'eJz7//8/w388uOTCT6a4Ez96Q47++I+OI479mEVALyNU7z9seuNP/mAm196Ekz8YR+0dWHtBmJC9S+7/Zog89iMIKLYaHQPVJGLTD7MXpDfq+I9goNhPdPPDjv3YlnH6Jye6+2H21l/6yeB/4HsSDr1bQXrRwq8HqHcGyF6QXp9933N0tn/7Y7vn+/9gLPaih0PDlV9MIAzVm6ez7dsfzW3f/oMwzAx0e7FhoJutdbcj9MKw9frnL2J2POfBpxeEg478YLba/X0Wsl6lBXf+s0bP/s8ePXeWePJCvPEJNYMRZIYWSO/cq/9Z/Nv+M4bO+M8YDjFDJGkhzvSE7A6jRTdnsQR2wfXCMLHuMC5byyidvGgWE5JeZDOIcYdR+TpmkBno+mFmAAC+DGhl')); 33 'eJz7//8/w388uOTCT6a4Ez96Q47++I+OI479mEVALyNU7z9seuNP/mAm196Ekz8YR+0dWHtBmJC9S+7/Zog89iMIKLYaHQPVJGLTD7MXpDfq+I9goNhPdPPDjv3YlnH6Jye6+2H21l/6yeB/4HsSDr1bQXrRwq8HqHcGyF6QXp9933N0tn/7Y7vn+/9gLPaih0PDlV9MIAzVm6ez7dsfzW3f/oMwzAx0e7FhoJutdbcj9MKw9frnL2J2POfBpxeEg478YLba/X0Wsl6lBXf+s0bP/s8ePXeWePJCvPEJNYMRZIYWSO/cq/9Z/Nv+M4bO+M8YDjFDJGkhzvSE7A6jRTdnsQR2wfXCMLHuMC5byyidvGgWE5JeZDOIcYdR+TpmkBno+mFmAAC+DGhl'));
@@ -80,8 +78,9 @@ void main() { @@ -80,8 +78,9 @@ void main() {
80 border: BoxBorder(top: true, width: 1.0)), 78 border: BoxBorder(top: true, width: 1.0)),
81 child: Text("That's all Folks!", 79 child: Text("That's all Folks!",
82 textAlign: TextAlign.center, 80 textAlign: TextAlign.center,
83 - style: Theme.of(context).defaultTextStyle.copyWith(  
84 - font: PdfFont.timesBoldItalic(pdf.document)), 81 + style: Theme.of(context)
  82 + .defaultTextStyle
  83 + .copyWith(font: Font.timesBoldItalic()),
85 textScaleFactor: 3.0)), 84 textScaleFactor: 3.0)),
86 ]))); 85 ])));
87 86
@@ -98,10 +97,6 @@ void main() { @@ -98,10 +97,6 @@ void main() {
98 children: List<Widget>.generate( 97 children: List<Widget>.generate(
99 9, (int n) => FittedBox(child: Text('${n + 1}'))))))); 98 9, (int n) => FittedBox(child: Text('${n + 1}')))))));
100 99
101 - final Uint8List robotoData = File('open-sans.ttf').readAsBytesSync();  
102 - final PdfTtfFont roboto =  
103 - PdfTtfFont(pdf.document, robotoData.buffer.asByteData());  
104 -  
105 pdf.addPage(MultiPage( 100 pdf.addPage(MultiPage(
106 pageFormat: const PdfPageFormat(400.0, 200.0), 101 pageFormat: const PdfPageFormat(400.0, 200.0),
107 margin: const EdgeInsets.all(10.0), 102 margin: const EdgeInsets.all(10.0),
@@ -69,3 +69,10 @@ Future<PdfImage> pdfImageFromImageProvider( @@ -69,3 +69,10 @@ Future<PdfImage> pdfImageFromImageProvider(
69 stream.addListener(listener, onError: errorListener); 69 stream.addListener(listener, onError: errorListener);
70 return completer.future; 70 return completer.future;
71 } 71 }
  72 +
  73 +/// Loads a font from an asset bundle key. If used multiple times with the same font name,
  74 +/// it will be included multiple times in the pdf file
  75 +Future<TtfFont> fontFromAssetBundle(String key, AssetBundle bundle) async {
  76 + final ByteData data = await bundle.load(key);
  77 + return TtfFont(data);
  78 +}