David PHAM-VAN

Add document outline support

@@ -22,7 +22,7 @@ import 'package:pdf/pdf.dart'; @@ -22,7 +22,7 @@ import 'package:pdf/pdf.dart';
22 import 'package:pdf/widgets.dart' as pw; 22 import 'package:pdf/widgets.dart' as pw;
23 23
24 Future<Uint8List> generateDocument(PdfPageFormat format) async { 24 Future<Uint8List> generateDocument(PdfPageFormat format) async {
25 - final pw.Document doc = pw.Document(); 25 + final pw.Document doc = pw.Document(pageMode: PdfPageMode.outlines);
26 26
27 doc.addPage(pw.MultiPage( 27 doc.addPage(pw.MultiPage(
28 pageFormat: 28 pageFormat:
@@ -57,6 +57,7 @@ Future<Uint8List> generateDocument(PdfPageFormat format) async { @@ -57,6 +57,7 @@ Future<Uint8List> generateDocument(PdfPageFormat format) async {
57 build: (pw.Context context) => <pw.Widget>[ 57 build: (pw.Context context) => <pw.Widget>[
58 pw.Header( 58 pw.Header(
59 level: 0, 59 level: 0,
  60 + title: 'Portable Document Format',
60 child: pw.Row( 61 child: pw.Row(
61 mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, 62 mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
62 children: <pw.Widget>[ 63 children: <pw.Widget>[
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 4
5 - Implement different border radius on all corners 5 - Implement different border radius on all corners
6 - Add AcroForm widgets 6 - Add AcroForm widgets
  7 +- Add document outline support
7 8
8 ## 1.12.0 9 ## 1.12.0
9 10
@@ -207,20 +207,27 @@ class PdfSecString extends PdfString { @@ -207,20 +207,27 @@ class PdfSecString extends PdfString {
207 [PdfStringFormat format = PdfStringFormat.binary]) 207 [PdfStringFormat format = PdfStringFormat.binary])
208 : super(value, format); 208 : super(value, format);
209 209
210 - factory PdfSecString.fromString(PdfObject object, String value) { 210 + factory PdfSecString.fromString(
  211 + PdfObject object,
  212 + String value, [
  213 + PdfStringFormat format = PdfStringFormat.litteral,
  214 + ]) {
211 return PdfSecString( 215 return PdfSecString(
212 object, 216 object,
213 PdfString._string(value), 217 PdfString._string(value),
214 - PdfStringFormat.litteral, 218 + format,
215 ); 219 );
216 } 220 }
217 221
218 - factory PdfSecString.fromStream(PdfObject object, PdfStream value,  
219 - [PdfStringFormat format = PdfStringFormat.litteral]) { 222 + factory PdfSecString.fromStream(
  223 + PdfObject object,
  224 + PdfStream value, [
  225 + PdfStringFormat format = PdfStringFormat.litteral,
  226 + ]) {
220 return PdfSecString( 227 return PdfSecString(
221 object, 228 object,
222 value.output(), 229 value.output(),
223 - PdfStringFormat.litteral, 230 + format,
224 ); 231 );
225 } 232 }
226 233
@@ -14,13 +14,15 @@ @@ -14,13 +14,15 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
  17 +// ignore_for_file: omit_local_variable_types
  18 +
17 part of pdf; 19 part of pdf;
18 20
19 class PdfNames extends PdfObject { 21 class PdfNames extends PdfObject {
20 /// This constructs a Pdf Name object 22 /// This constructs a Pdf Name object
21 PdfNames(PdfDocument pdfDocument) : super(pdfDocument); 23 PdfNames(PdfDocument pdfDocument) : super(pdfDocument);
22 24
23 - final PdfArray _dests = PdfArray(); 25 + final Map<String, PdfDataType> _dests = <String, PdfDataType>{};
24 26
25 void addDest( 27 void addDest(
26 String name, 28 String name,
@@ -32,8 +34,7 @@ class PdfNames extends PdfObject { @@ -32,8 +34,7 @@ class PdfNames extends PdfObject {
32 assert(page.pdfDocument == pdfDocument); 34 assert(page.pdfDocument == pdfDocument);
33 assert(name != null); 35 assert(name != null);
34 36
35 - _dests.add(PdfSecString.fromString(this, name));  
36 - _dests.add(PdfDict(<String, PdfDataType>{ 37 + _dests[name] = PdfDict(<String, PdfDataType>{
37 '/D': PdfArray(<PdfDataType>[ 38 '/D': PdfArray(<PdfDataType>[
38 page.ref(), 39 page.ref(),
39 const PdfName('/XYZ'), 40 const PdfName('/XYZ'),
@@ -41,13 +42,32 @@ class PdfNames extends PdfObject { @@ -41,13 +42,32 @@ class PdfNames extends PdfObject {
41 if (posY == null) const PdfNull() else PdfNum(posY), 42 if (posY == null) const PdfNull() else PdfNum(posY),
42 if (posZ == null) const PdfNull() else PdfNum(posZ), 43 if (posZ == null) const PdfNull() else PdfNum(posZ),
43 ]), 44 ]),
44 - })); 45 + });
45 } 46 }
46 47
47 @override 48 @override
48 void _prepare() { 49 void _prepare() {
49 super._prepare(); 50 super._prepare();
50 51
51 - params['/Dests'] = PdfDict(<String, PdfDataType>{'/Names': _dests}); 52 + if (_dests.isEmpty) {
  53 + return;
  54 + }
  55 +
  56 + final PdfArray dests = PdfArray();
  57 +
  58 + final List<String> keys = _dests.keys.toList()..sort();
  59 +
  60 + for (String name in keys) {
  61 + dests.add(PdfSecString.fromString(this, name));
  62 + dests.add(_dests[name]);
  63 + }
  64 +
  65 + params['/Dests'] = PdfDict(<String, PdfDataType>{
  66 + '/Names': dests,
  67 + '/Limits': PdfArray(<PdfDataType>[
  68 + PdfSecString.fromString(this, keys.first),
  69 + PdfSecString.fromString(this, keys.last),
  70 + ])
  71 + });
52 } 72 }
53 } 73 }
@@ -26,15 +26,36 @@ enum PdfOutlineMode { @@ -26,15 +26,36 @@ enum PdfOutlineMode {
26 fitrect 26 fitrect
27 } 27 }
28 28
  29 +enum PdfOutlineStyle {
  30 + /// Normal
  31 + normal,
  32 +
  33 + /// Italic
  34 + italic,
  35 +
  36 + // Bold
  37 + bold,
  38 +
  39 + /// Italic and Bold
  40 + italicBold,
  41 +}
  42 +
29 class PdfOutline extends PdfObject { 43 class PdfOutline extends PdfObject {
30 /// Constructs a Pdf Outline object. When selected, the specified region 44 /// Constructs a Pdf Outline object. When selected, the specified region
31 /// is displayed. 45 /// is displayed.
32 - ///  
33 - /// @param title Title of the outline  
34 - /// @param dest The destination page  
35 - /// @param rect coordinate  
36 - PdfOutline(PdfDocument pdfDocument, {this.title, this.dest, this.rect})  
37 - : super(pdfDocument, '/Outlines'); 46 + PdfOutline(
  47 + PdfDocument pdfDocument, {
  48 + this.title,
  49 + this.dest,
  50 + this.rect,
  51 + this.anchor,
  52 + this.color,
  53 + this.destMode = PdfOutlineMode.fitpage,
  54 + this.style = PdfOutlineStyle.normal,
  55 + }) : assert(anchor == null || (dest == null && rect == null)),
  56 + assert(destMode != null),
  57 + assert(style != null),
  58 + super(pdfDocument);
38 59
39 /// This holds any outlines below us 60 /// This holds any outlines below us
40 List<PdfOutline> outlines = <PdfOutline>[]; 61 List<PdfOutline> outlines = <PdfOutline>[];
@@ -51,33 +72,25 @@ class PdfOutline extends PdfObject { @@ -51,33 +72,25 @@ class PdfOutline extends PdfObject {
51 /// The region on the destination page 72 /// The region on the destination page
52 final PdfRect rect; 73 final PdfRect rect;
53 74
  75 + /// Named destination
  76 + final String anchor;
  77 +
  78 + /// Color of the outline text
  79 + final PdfColor color;
  80 +
54 /// How the destination is handled 81 /// How the destination is handled
55 - PdfOutlineMode destMode = PdfOutlineMode.fitpage; 82 + final PdfOutlineMode destMode;
  83 +
  84 + /// How to display the outline text
  85 + final PdfOutlineStyle style;
  86 +
  87 + int effectiveLevel;
56 88
57 /// This method creates an outline, and attaches it to this one. 89 /// This method creates an outline, and attaches it to this one.
58 /// When the outline is selected, the supplied region is displayed. 90 /// When the outline is selected, the supplied region is displayed.
59 - ///  
60 - /// Note: the coordinates are in User space. They are converted to User  
61 - /// space.  
62 - ///  
63 - /// This allows you to have an outline for say a Chapter,  
64 - /// then under the chapter, one for each section. You are not really  
65 - /// limited on how deep you go, but it's best not to go below say 6 levels,  
66 - /// for the reader's sake.  
67 - ///  
68 - /// @param title Title of the outline  
69 - /// @param dest The destination page  
70 - /// @param x coordinate of region in User space  
71 - /// @param y coordinate of region in User space  
72 - /// @param w width of region in User space  
73 - /// @param h height of region in User space  
74 - /// @return [PdfOutline] object created, for creating sub-outlines  
75 - PdfOutline add({String title, PdfPage dest, PdfRect rect}) {  
76 - final PdfOutline outline =  
77 - PdfOutline(pdfDocument, title: title, dest: dest, rect: rect);  
78 - // Tell the outline of ourselves 91 + void add(PdfOutline outline) {
79 outline.parent = this; 92 outline.parent = this;
80 - return outline; 93 + outlines.add(outline);
81 } 94 }
82 95
83 /// @param os OutputStream to send the object to 96 /// @param os OutputStream to send the object to
@@ -88,20 +101,33 @@ class PdfOutline extends PdfObject { @@ -88,20 +101,33 @@ class PdfOutline extends PdfObject {
88 // These are for kids only 101 // These are for kids only
89 if (parent != null) { 102 if (parent != null) {
90 params['/Title'] = PdfSecString.fromString(this, title); 103 params['/Title'] = PdfSecString.fromString(this, title);
91 - final PdfArray dests = PdfArray();  
92 - dests.add(dest.ref());  
93 104
94 - if (destMode == PdfOutlineMode.fitpage) {  
95 - dests.add(const PdfName('/Fit')); 105 + if (color != null) {
  106 + params['/C'] = PdfColorType(color);
  107 + }
  108 +
  109 + if (style != PdfOutlineStyle.normal) {
  110 + params['/F'] = PdfNum(style.index);
  111 + }
  112 +
  113 + if (anchor != null) {
  114 + params['/Dest'] = PdfSecString.fromString(this, anchor);
96 } else { 115 } else {
97 - dests.add(const PdfName('/FitR'));  
98 - dests.add(PdfNum(rect.left));  
99 - dests.add(PdfNum(rect.bottom));  
100 - dests.add(PdfNum(rect.right));  
101 - dests.add(PdfNum(rect.top)); 116 + final PdfArray dests = PdfArray();
  117 + dests.add(dest.ref());
  118 +
  119 + if (destMode == PdfOutlineMode.fitpage) {
  120 + dests.add(const PdfName('/Fit'));
  121 + } else {
  122 + dests.add(const PdfName('/FitR'));
  123 + dests.add(PdfNum(rect.left));
  124 + dests.add(PdfNum(rect.bottom));
  125 + dests.add(PdfNum(rect.right));
  126 + dests.add(PdfNum(rect.top));
  127 + }
  128 + params['/Dest'] = dests;
102 } 129 }
103 params['/Parent'] = parent.ref(); 130 params['/Parent'] = parent.ref();
104 - params['/Dest'] = dests;  
105 131
106 // were a descendent, so by default we are closed. Find out how many 132 // were a descendent, so by default we are closed. Find out how many
107 // entries are below us 133 // entries are below us
@@ -19,22 +19,38 @@ @@ -19,22 +19,38 @@
19 part of widget; 19 part of widget;
20 20
21 class Anchor extends SingleChildWidget { 21 class Anchor extends SingleChildWidget {
22 - Anchor({Widget child, @required this.name, this.description})  
23 - : assert(name != null), 22 + Anchor({
  23 + Widget child,
  24 + @required this.name,
  25 + this.description,
  26 + this.zoom,
  27 + this.setX = false,
  28 + }) : assert(name != null),
  29 + assert(setX != null),
24 super(child: child); 30 super(child: child);
25 31
26 final String name; 32 final String name;
27 33
28 final String description; 34 final String description;
29 35
  36 + final double zoom;
  37 +
  38 + final bool setX;
  39 +
30 @override 40 @override
31 void paint(Context context) { 41 void paint(Context context) {
32 super.paint(context); 42 super.paint(context);
33 paintChild(context); 43 paintChild(context);
34 44
35 final Matrix4 mat = context.canvas.getTransform(); 45 final Matrix4 mat = context.canvas.getTransform();
36 - final Vector3 lt = mat.transform3(Vector3(box.left, box.bottom, 0));  
37 - context.document.pdfNames.addDest(name, context.page, posY: lt.y); 46 + final Vector3 lt = mat.transform3(Vector3(box.left, box.top, 0));
  47 + context.document.pdfNames.addDest(
  48 + name,
  49 + context.page,
  50 + posX: setX ? lt.x : null,
  51 + posY: lt.y,
  52 + posZ: zoom,
  53 + );
38 54
39 if (description != null) { 55 if (description != null) {
40 final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0)); 56 final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0));
@@ -307,3 +323,74 @@ class TextField extends Annotation { @@ -307,3 +323,74 @@ class TextField extends Annotation {
307 textStyle: textStyle, 323 textStyle: textStyle,
308 )); 324 ));
309 } 325 }
  326 +
  327 +class Outline extends Anchor {
  328 + Outline({
  329 + Widget child,
  330 + @required String name,
  331 + @required this.title,
  332 + this.level = 0,
  333 + this.color,
  334 + this.style = PdfOutlineStyle.normal,
  335 + }) : assert(title != null),
  336 + assert(level != null && level >= 0),
  337 + assert(style != null),
  338 + super(child: child, name: name, setX: true);
  339 +
  340 + final String title;
  341 +
  342 + final int level;
  343 +
  344 + final PdfColor color;
  345 +
  346 + final PdfOutlineStyle style;
  347 +
  348 + PdfOutline _outline;
  349 +
  350 + @override
  351 + void layout(Context context, BoxConstraints constraints,
  352 + {bool parentUsesSize = false}) {
  353 + super.layout(context, constraints, parentUsesSize: parentUsesSize);
  354 + _buildOutline(context);
  355 + }
  356 +
  357 + @override
  358 + void debugPaint(Context context) {
  359 + context.canvas
  360 + ..setFillColor(PdfColors.pink100)
  361 + ..drawRect(box.x, box.y, box.width, box.height)
  362 + ..fillPath();
  363 + }
  364 +
  365 + void _buildOutline(Context context) {
  366 + if (_outline != null) {
  367 + return;
  368 + }
  369 +
  370 + _outline = PdfOutline(
  371 + context.document,
  372 + title: title,
  373 + anchor: name,
  374 + color: color,
  375 + style: style,
  376 + );
  377 +
  378 + PdfOutline parent = context.document.outline;
  379 + int l = level;
  380 +
  381 + while (l > 0) {
  382 + if (parent.effectiveLevel == l) {
  383 + break;
  384 + }
  385 +
  386 + if (parent.outlines.isEmpty) {
  387 + parent.effectiveLevel = level;
  388 + break;
  389 + }
  390 + parent = parent.outlines.last;
  391 + l--;
  392 + }
  393 +
  394 + parent.add(_outline);
  395 + }
  396 +}
@@ -19,15 +19,23 @@ @@ -19,15 +19,23 @@
19 part of widget; 19 part of widget;
20 20
21 class Header extends StatelessWidget { 21 class Header extends StatelessWidget {
22 - Header(  
23 - {this.level = 1,  
24 - this.text,  
25 - this.child,  
26 - this.decoration,  
27 - this.margin,  
28 - this.padding,  
29 - this.textStyle})  
30 - : assert(level >= 0 && level <= 5); 22 + Header({
  23 + this.level = 1,
  24 + this.text,
  25 + this.child,
  26 + this.decoration,
  27 + this.margin,
  28 + this.padding,
  29 + this.textStyle,
  30 + String title,
  31 + this.outlineColor,
  32 + this.outlineStyle = PdfOutlineStyle.normal,
  33 + }) : assert(level != null, level >= 0 && level <= 5),
  34 + assert(text != null || child != null),
  35 + assert(outlineStyle != null),
  36 + title = title ?? text;
  37 +
  38 + final String title;
31 39
32 final String text; 40 final String text;
33 41
@@ -43,6 +51,10 @@ class Header extends StatelessWidget { @@ -43,6 +51,10 @@ class Header extends StatelessWidget {
43 51
44 final TextStyle textStyle; 52 final TextStyle textStyle;
45 53
  54 + final PdfColor outlineColor;
  55 +
  56 + final PdfOutlineStyle outlineStyle;
  57 +
46 @override 58 @override
47 Widget build(Context context) { 59 Widget build(Context context) {
48 BoxDecoration _decoration = decoration; 60 BoxDecoration _decoration = decoration;
@@ -85,13 +97,27 @@ class Header extends StatelessWidget { @@ -85,13 +97,27 @@ class Header extends StatelessWidget {
85 _textStyle ??= Theme.of(context).header5; 97 _textStyle ??= Theme.of(context).header5;
86 break; 98 break;
87 } 99 }
88 - return Container( 100 +
  101 + final Widget container = Container(
89 alignment: Alignment.topLeft, 102 alignment: Alignment.topLeft,
90 margin: _margin, 103 margin: _margin,
91 padding: _padding, 104 padding: _padding,
92 decoration: _decoration, 105 decoration: _decoration,
93 child: child ?? Text(text, style: _textStyle), 106 child: child ?? Text(text, style: _textStyle),
94 ); 107 );
  108 +
  109 + if (title == null) {
  110 + return container;
  111 + }
  112 +
  113 + return Outline(
  114 + name: text.hashCode.toString(),
  115 + title: title,
  116 + child: container,
  117 + level: level,
  118 + color: outlineColor,
  119 + style: outlineStyle,
  120 + );
95 } 121 }
96 } 122 }
97 123
@@ -40,6 +40,7 @@ import 'widget_form_test.dart' as widget_form; @@ -40,6 +40,7 @@ import 'widget_form_test.dart' as widget_form;
40 import 'widget_grid_view_test.dart' as widget_grid_view; 40 import 'widget_grid_view_test.dart' as widget_grid_view;
41 import 'widget_multipage_test.dart' as widget_multipage; 41 import 'widget_multipage_test.dart' as widget_multipage;
42 import 'widget_opacity_test.dart' as widget_opacity; 42 import 'widget_opacity_test.dart' as widget_opacity;
  43 +import 'widget_outline_test.dart' as widget_outline;
43 import 'widget_partitions_test.dart' as widget_partitions; 44 import 'widget_partitions_test.dart' as widget_partitions;
44 import 'widget_table_test.dart' as widget_table; 45 import 'widget_table_test.dart' as widget_table;
45 import 'widget_test.dart' as widget; 46 import 'widget_test.dart' as widget;
@@ -73,6 +74,7 @@ void main() { @@ -73,6 +74,7 @@ void main() {
73 widget_grid_view.main(); 74 widget_grid_view.main();
74 widget_multipage.main(); 75 widget_multipage.main();
75 widget_opacity.main(); 76 widget_opacity.main();
  77 + widget_outline.main();
76 widget_partitions.main(); 78 widget_partitions.main();
77 widget_table.main(); 79 widget_table.main();
78 widget_text.main(); 80 widget_text.main();
@@ -175,14 +175,14 @@ Document pdf; @@ -175,14 +175,14 @@ Document pdf;
175 void main() { 175 void main() {
176 setUpAll(() { 176 setUpAll(() {
177 Document.debug = true; 177 Document.debug = true;
178 - pdf = Document(); 178 + pdf = Document(pageMode: PdfPageMode.outlines);
179 }); 179 });
180 180
181 test('Pdf Colors', () { 181 test('Pdf Colors', () {
182 pdf.addPage(MultiPage( 182 pdf.addPage(MultiPage(
183 pageFormat: PdfPageFormat.standard, 183 pageFormat: PdfPageFormat.standard,
184 build: (Context context) => <Widget>[ 184 build: (Context context) => <Widget>[
185 - Header(text: 'Red'), 185 + Header(text: 'Red', outlineColor: PdfColors.red),
186 GridView( 186 GridView(
187 crossAxisCount: 4, 187 crossAxisCount: 4,
188 direction: Axis.vertical, 188 direction: Axis.vertical,
@@ -207,7 +207,7 @@ void main() { @@ -207,7 +207,7 @@ void main() {
207 Color(PdfColors.redAccent700, 'Red', 'Accent 700'), 207 Color(PdfColors.redAccent700, 'Red', 'Accent 700'),
208 ]), 208 ]),
209 NewPage(), 209 NewPage(),
210 - Header(text: 'Pink'), 210 + Header(text: 'Pink', outlineColor: PdfColors.pink),
211 GridView( 211 GridView(
212 crossAxisCount: 4, 212 crossAxisCount: 4,
213 direction: Axis.vertical, 213 direction: Axis.vertical,
@@ -232,7 +232,7 @@ void main() { @@ -232,7 +232,7 @@ void main() {
232 Color(PdfColors.pinkAccent700, 'Pink', 'Accent 700'), 232 Color(PdfColors.pinkAccent700, 'Pink', 'Accent 700'),
233 ]), 233 ]),
234 NewPage(), 234 NewPage(),
235 - Header(text: 'Purple'), 235 + Header(text: 'Purple', outlineColor: PdfColors.purple),
236 GridView( 236 GridView(
237 crossAxisCount: 4, 237 crossAxisCount: 4,
238 direction: Axis.vertical, 238 direction: Axis.vertical,
@@ -257,7 +257,7 @@ void main() { @@ -257,7 +257,7 @@ void main() {
257 Color(PdfColors.purpleAccent700, 'Purple', 'Accent 700'), 257 Color(PdfColors.purpleAccent700, 'Purple', 'Accent 700'),
258 ]), 258 ]),
259 NewPage(), 259 NewPage(),
260 - Header(text: 'Deep Purple'), 260 + Header(text: 'Deep Purple', outlineColor: PdfColors.deepPurple),
261 GridView( 261 GridView(
262 crossAxisCount: 4, 262 crossAxisCount: 4,
263 direction: Axis.vertical, 263 direction: Axis.vertical,
@@ -286,7 +286,7 @@ void main() { @@ -286,7 +286,7 @@ void main() {
286 'Accent 700'), 286 'Accent 700'),
287 ]), 287 ]),
288 NewPage(), 288 NewPage(),
289 - Header(text: 'Indigo'), 289 + Header(text: 'Indigo', outlineColor: PdfColors.indigo),
290 GridView( 290 GridView(
291 crossAxisCount: 4, 291 crossAxisCount: 4,
292 direction: Axis.vertical, 292 direction: Axis.vertical,
@@ -311,7 +311,7 @@ void main() { @@ -311,7 +311,7 @@ void main() {
311 Color(PdfColors.indigoAccent700, 'Indigo', 'Accent 700'), 311 Color(PdfColors.indigoAccent700, 'Indigo', 'Accent 700'),
312 ]), 312 ]),
313 NewPage(), 313 NewPage(),
314 - Header(text: 'Blue'), 314 + Header(text: 'Blue', outlineColor: PdfColors.blue),
315 GridView( 315 GridView(
316 crossAxisCount: 4, 316 crossAxisCount: 4,
317 direction: Axis.vertical, 317 direction: Axis.vertical,
@@ -336,7 +336,7 @@ void main() { @@ -336,7 +336,7 @@ void main() {
336 Color(PdfColors.blueAccent700, 'Blue', 'Accent 700'), 336 Color(PdfColors.blueAccent700, 'Blue', 'Accent 700'),
337 ]), 337 ]),
338 NewPage(), 338 NewPage(),
339 - Header(text: 'Light Blue'), 339 + Header(text: 'Light Blue', outlineColor: PdfColors.lightBlue),
340 GridView( 340 GridView(
341 crossAxisCount: 4, 341 crossAxisCount: 4,
342 direction: Axis.vertical, 342 direction: Axis.vertical,
@@ -365,7 +365,7 @@ void main() { @@ -365,7 +365,7 @@ void main() {
365 'Accent 700'), 365 'Accent 700'),
366 ]), 366 ]),
367 NewPage(), 367 NewPage(),
368 - Header(text: 'Cyan'), 368 + Header(text: 'Cyan', outlineColor: PdfColors.cyan),
369 GridView( 369 GridView(
370 crossAxisCount: 4, 370 crossAxisCount: 4,
371 direction: Axis.vertical, 371 direction: Axis.vertical,
@@ -390,7 +390,7 @@ void main() { @@ -390,7 +390,7 @@ void main() {
390 Color(PdfColors.cyanAccent700, 'Cyan', 'Accent 700'), 390 Color(PdfColors.cyanAccent700, 'Cyan', 'Accent 700'),
391 ]), 391 ]),
392 NewPage(), 392 NewPage(),
393 - Header(text: 'Teal'), 393 + Header(text: 'Teal', outlineColor: PdfColors.teal),
394 GridView( 394 GridView(
395 crossAxisCount: 4, 395 crossAxisCount: 4,
396 direction: Axis.vertical, 396 direction: Axis.vertical,
@@ -415,7 +415,7 @@ void main() { @@ -415,7 +415,7 @@ void main() {
415 Color(PdfColors.tealAccent700, 'Teal', 'Accent 700'), 415 Color(PdfColors.tealAccent700, 'Teal', 'Accent 700'),
416 ]), 416 ]),
417 NewPage(), 417 NewPage(),
418 - Header(text: 'Green'), 418 + Header(text: 'Green', outlineColor: PdfColors.green),
419 GridView( 419 GridView(
420 crossAxisCount: 4, 420 crossAxisCount: 4,
421 direction: Axis.vertical, 421 direction: Axis.vertical,
@@ -440,7 +440,7 @@ void main() { @@ -440,7 +440,7 @@ void main() {
440 Color(PdfColors.greenAccent700, 'Green', 'Accent 700'), 440 Color(PdfColors.greenAccent700, 'Green', 'Accent 700'),
441 ]), 441 ]),
442 NewPage(), 442 NewPage(),
443 - Header(text: 'Light Green'), 443 + Header(text: 'Light Green', outlineColor: PdfColors.lightGreen),
444 GridView( 444 GridView(
445 crossAxisCount: 4, 445 crossAxisCount: 4,
446 direction: Axis.vertical, 446 direction: Axis.vertical,
@@ -469,7 +469,7 @@ void main() { @@ -469,7 +469,7 @@ void main() {
469 'Accent700'), 469 'Accent700'),
470 ]), 470 ]),
471 NewPage(), 471 NewPage(),
472 - Header(text: 'Lime'), 472 + Header(text: 'Lime', outlineColor: PdfColors.lime),
473 GridView( 473 GridView(
474 crossAxisCount: 4, 474 crossAxisCount: 4,
475 direction: Axis.vertical, 475 direction: Axis.vertical,
@@ -494,7 +494,7 @@ void main() { @@ -494,7 +494,7 @@ void main() {
494 Color(PdfColors.limeAccent700, 'Lime', 'Accent 700'), 494 Color(PdfColors.limeAccent700, 'Lime', 'Accent 700'),
495 ]), 495 ]),
496 NewPage(), 496 NewPage(),
497 - Header(text: 'Yellow'), 497 + Header(text: 'Yellow', outlineColor: PdfColors.yellow),
498 GridView( 498 GridView(
499 crossAxisCount: 4, 499 crossAxisCount: 4,
500 direction: Axis.vertical, 500 direction: Axis.vertical,
@@ -519,7 +519,7 @@ void main() { @@ -519,7 +519,7 @@ void main() {
519 Color(PdfColors.yellowAccent700, 'Yellow', 'Accent 700'), 519 Color(PdfColors.yellowAccent700, 'Yellow', 'Accent 700'),
520 ]), 520 ]),
521 NewPage(), 521 NewPage(),
522 - Header(text: 'Amber'), 522 + Header(text: 'Amber', outlineColor: PdfColors.amber),
523 GridView( 523 GridView(
524 crossAxisCount: 4, 524 crossAxisCount: 4,
525 direction: Axis.vertical, 525 direction: Axis.vertical,
@@ -544,7 +544,7 @@ void main() { @@ -544,7 +544,7 @@ void main() {
544 Color(PdfColors.amberAccent700, 'Amber', 'Accent 700'), 544 Color(PdfColors.amberAccent700, 'Amber', 'Accent 700'),
545 ]), 545 ]),
546 NewPage(), 546 NewPage(),
547 - Header(text: 'Orange'), 547 + Header(text: 'Orange', outlineColor: PdfColors.orange),
548 GridView( 548 GridView(
549 crossAxisCount: 4, 549 crossAxisCount: 4,
550 direction: Axis.vertical, 550 direction: Axis.vertical,
@@ -569,7 +569,7 @@ void main() { @@ -569,7 +569,7 @@ void main() {
569 Color(PdfColors.orangeAccent700, 'Orange', 'Accent 700'), 569 Color(PdfColors.orangeAccent700, 'Orange', 'Accent 700'),
570 ]), 570 ]),
571 NewPage(), 571 NewPage(),
572 - Header(text: 'Deep Orange'), 572 + Header(text: 'Deep Orange', outlineColor: PdfColors.deepOrange),
573 GridView( 573 GridView(
574 crossAxisCount: 4, 574 crossAxisCount: 4,
575 direction: Axis.vertical, 575 direction: Axis.vertical,
@@ -598,7 +598,7 @@ void main() { @@ -598,7 +598,7 @@ void main() {
598 'Accent 700'), 598 'Accent 700'),
599 ]), 599 ]),
600 NewPage(), 600 NewPage(),
601 - Header(text: 'Brown'), 601 + Header(text: 'Brown', outlineColor: PdfColors.brown),
602 GridView( 602 GridView(
603 crossAxisCount: 4, 603 crossAxisCount: 4,
604 direction: Axis.vertical, 604 direction: Axis.vertical,
@@ -619,7 +619,7 @@ void main() { @@ -619,7 +619,7 @@ void main() {
619 Color(PdfColors.brown900, 'Brown', '900'), 619 Color(PdfColors.brown900, 'Brown', '900'),
620 ]), 620 ]),
621 NewPage(), 621 NewPage(),
622 - Header(text: 'Blue Grey'), 622 + Header(text: 'Blue Grey', outlineColor: PdfColors.blueGrey),
623 GridView( 623 GridView(
624 crossAxisCount: 4, 624 crossAxisCount: 4,
625 direction: Axis.vertical, 625 direction: Axis.vertical,
@@ -640,7 +640,7 @@ void main() { @@ -640,7 +640,7 @@ void main() {
640 Color(PdfColors.blueGrey900, 'Blue Grey', '900'), 640 Color(PdfColors.blueGrey900, 'Blue Grey', '900'),
641 ]), 641 ]),
642 NewPage(), 642 NewPage(),
643 - Header(text: 'Grey'), 643 + Header(text: 'Grey', outlineColor: PdfColors.grey),
644 GridView( 644 GridView(
645 crossAxisCount: 4, 645 crossAxisCount: 4,
646 direction: Axis.vertical, 646 direction: Axis.vertical,
@@ -677,7 +677,7 @@ void main() { @@ -677,7 +677,7 @@ void main() {
677 pdf.addPage(Page( 677 pdf.addPage(Page(
678 build: (Context context) => Column( 678 build: (Context context) => Column(
679 children: <Widget>[ 679 children: <Widget>[
680 - Header(text: name), 680 + Header(text: name, outlineStyle: PdfOutlineStyle.italic),
681 SizedBox( 681 SizedBox(
682 height: context.page.pageFormat.availableWidth, 682 height: context.page.pageFormat.availableWidth,
683 child: ColorWheel( 683 child: ColorWheel(
  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 +// ignore_for_file: omit_local_variable_types
  18 +
  19 +import 'dart:io';
  20 +import 'dart:math';
  21 +
  22 +import 'package:pdf/pdf.dart';
  23 +import 'package:pdf/widgets.dart';
  24 +import 'package:test/test.dart';
  25 +
  26 +Document pdf;
  27 +
  28 +final LoremText lorem = LoremText(random: Random(42));
  29 +
  30 +Iterable<Widget> level(int i) sync* {
  31 + final String text = lorem.sentence(5);
  32 + int p = 0;
  33 + PdfColor color;
  34 + PdfOutlineStyle style = PdfOutlineStyle.normal;
  35 +
  36 + if (i >= 3 && i <= 6) {
  37 + p++;
  38 + }
  39 +
  40 + if (i >= 5 && i <= 6) {
  41 + p++;
  42 + }
  43 +
  44 + if (i == 15) {
  45 + p = 10;
  46 + color = PdfColors.amber;
  47 + style = PdfOutlineStyle.bold;
  48 + }
  49 +
  50 + if (i == 17) {
  51 + color = PdfColors.red;
  52 + style = PdfOutlineStyle.italic;
  53 + }
  54 +
  55 + if (i == 18) {
  56 + color = PdfColors.blue;
  57 + style = PdfOutlineStyle.italicBold;
  58 + }
  59 +
  60 + yield Outline(
  61 + child: Text(text),
  62 + name: 'anchor$i',
  63 + title: text,
  64 + level: p,
  65 + color: color,
  66 + style: style,
  67 + );
  68 +
  69 + yield SizedBox(height: 300);
  70 +}
  71 +
  72 +void main() {
  73 + setUpAll(() {
  74 + Document.debug = true;
  75 + pdf = Document(pageMode: PdfPageMode.outlines);
  76 + });
  77 +
  78 + test('Outline Widget', () {
  79 + pdf.addPage(
  80 + MultiPage(
  81 + build: (Context context) => <Widget>[
  82 + for (int i = 0; i < 20; i++) ...level(i),
  83 + ],
  84 + ),
  85 + );
  86 + });
  87 +
  88 + tearDownAll(() {
  89 + final File file = File('widgets-outline.pdf');
  90 + file.writeAsBytesSync(pdf.save());
  91 + });
  92 +}
No preview for this file type
No preview for this file type