Showing
8 changed files
with
373 additions
and
154 deletions
@@ -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; |
pdf/lib/widgets/font.dart
0 → 100644
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 | +} |
-
Please register or login to post a comment