Showing
11 changed files
with
251 additions
and
12 deletions
@@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
5 | * Improve MultiPage Widget | 5 | * Improve MultiPage Widget |
6 | * Convert GridView to a SpanningWidget | 6 | * Convert GridView to a SpanningWidget |
7 | * Add all Material Colors | 7 | * Add all Material Colors |
8 | +* Add Hyperlink widgets | ||
8 | 9 | ||
9 | # 1.3.3 | 10 | # 1.3.3 |
10 | * Fix a bug with the RichText Widget | 11 | * Fix a bug with the RichText Widget |
@@ -41,6 +41,7 @@ part 'src/formxobject.dart'; | @@ -41,6 +41,7 @@ part 'src/formxobject.dart'; | ||
41 | part 'src/graphics.dart'; | 41 | part 'src/graphics.dart'; |
42 | part 'src/image.dart'; | 42 | part 'src/image.dart'; |
43 | part 'src/info.dart'; | 43 | part 'src/info.dart'; |
44 | +part 'src/names.dart'; | ||
44 | part 'src/object.dart'; | 45 | part 'src/object.dart'; |
45 | part 'src/object_stream.dart'; | 46 | part 'src/object_stream.dart'; |
46 | part 'src/outline.dart'; | 47 | part 'src/outline.dart'; |
@@ -24,7 +24,9 @@ class PdfAnnot extends PdfObject { | @@ -24,7 +24,9 @@ class PdfAnnot extends PdfObject { | ||
24 | @required this.subtype, | 24 | @required this.subtype, |
25 | this.dest, | 25 | this.dest, |
26 | this.destRect, | 26 | this.destRect, |
27 | - this.border}) | 27 | + this.border, |
28 | + this.url, | ||
29 | + this.name}) | ||
28 | : super(pdfPage.pdfDocument, type ?? '/Annot') { | 30 | : super(pdfPage.pdfDocument, type ?? '/Annot') { |
29 | pdfPage.annotations.add(this); | 31 | pdfPage.annotations.add(this); |
30 | } | 32 | } |
@@ -56,6 +58,18 @@ class PdfAnnot extends PdfObject { | @@ -56,6 +58,18 @@ class PdfAnnot extends PdfObject { | ||
56 | destRect: destRect, | 58 | destRect: destRect, |
57 | border: border); | 59 | border: border); |
58 | 60 | ||
61 | + /// Creates an external link annotation | ||
62 | + factory PdfAnnot.urlLink(PdfPage pdfPage, | ||
63 | + {@required PdfRect rect, @required String dest, PdfBorder border}) => | ||
64 | + PdfAnnot._create(pdfPage, | ||
65 | + subtype: '/Link', srcRect: rect, url: dest, border: border); | ||
66 | + | ||
67 | + /// Creates a link annotation to a named destination | ||
68 | + factory PdfAnnot.namedLink(PdfPage pdfPage, | ||
69 | + {@required PdfRect rect, @required String dest, PdfBorder border}) => | ||
70 | + PdfAnnot._create(pdfPage, | ||
71 | + subtype: '/Link', srcRect: rect, name: dest, border: border); | ||
72 | + | ||
59 | /// The subtype of the outline, ie text, note, etc | 73 | /// The subtype of the outline, ie text, note, etc |
60 | final String subtype; | 74 | final String subtype; |
61 | 75 | ||
@@ -75,6 +89,12 @@ class PdfAnnot extends PdfObject { | @@ -75,6 +89,12 @@ class PdfAnnot extends PdfObject { | ||
75 | /// the border for this annotation | 89 | /// the border for this annotation |
76 | final PdfBorder border; | 90 | final PdfBorder border; |
77 | 91 | ||
92 | + /// The external url for a link | ||
93 | + final String url; | ||
94 | + | ||
95 | + /// The internal name for a link | ||
96 | + final String name; | ||
97 | + | ||
78 | /// Output the annotation | 98 | /// Output the annotation |
79 | /// | 99 | /// |
80 | /// @param os OutputStream to send the object to | 100 | /// @param os OutputStream to send the object to |
@@ -83,8 +103,9 @@ class PdfAnnot extends PdfObject { | @@ -83,8 +103,9 @@ class PdfAnnot extends PdfObject { | ||
83 | super._prepare(); | 103 | super._prepare(); |
84 | 104 | ||
85 | params['/Subtype'] = PdfStream.string(subtype); | 105 | params['/Subtype'] = PdfStream.string(subtype); |
86 | - params['/Rect'] = PdfStream.string( | ||
87 | - '[${srcRect.left} ${srcRect.bottom} ${srcRect.right} ${srcRect.top}]'); | 106 | + params['/Rect'] = PdfStream() |
107 | + ..putNumArray( | ||
108 | + <double>[srcRect.left, srcRect.bottom, srcRect.right, srcRect.top]); | ||
88 | 109 | ||
89 | // handle the border | 110 | // handle the border |
90 | if (border == null) { | 111 | if (border == null) { |
@@ -97,15 +118,35 @@ class PdfAnnot extends PdfObject { | @@ -97,15 +118,35 @@ class PdfAnnot extends PdfObject { | ||
97 | if (subtype == '/Text') { | 118 | if (subtype == '/Text') { |
98 | params['/Contents'] = PdfStream()..putLiteral(content); | 119 | params['/Contents'] = PdfStream()..putLiteral(content); |
99 | } else if (subtype == '/Link') { | 120 | } else if (subtype == '/Link') { |
121 | + if (url != null) { | ||
122 | + params['/A'] = PdfStream() | ||
123 | + ..putDictionary(<String, PdfStream>{ | ||
124 | + '/S': PdfStream()..putString('/URI'), | ||
125 | + '/URI': PdfStream()..putText(url), | ||
126 | + }); | ||
127 | + } else if (name != null) { | ||
128 | + params['/A'] = PdfStream() | ||
129 | + ..putDictionary(<String, PdfStream>{ | ||
130 | + '/S': PdfStream()..putString('/GoTo'), | ||
131 | + '/D': PdfStream()..putText(name), | ||
132 | + }); | ||
133 | + } else { | ||
100 | final List<PdfStream> dests = <PdfStream>[]; | 134 | final List<PdfStream> dests = <PdfStream>[]; |
101 | dests.add(dest.ref()); | 135 | dests.add(dest.ref()); |
102 | if (destRect == null) | 136 | if (destRect == null) |
103 | dests.add(PdfStream.string('/Fit')); | 137 | dests.add(PdfStream.string('/Fit')); |
104 | else { | 138 | else { |
105 | - dests.add(PdfStream.string( | ||
106 | - '/FitR ${destRect.left} ${destRect.bottom} ${destRect.right} ${destRect.top}')); | 139 | + dests.add(PdfStream.string('/FitR ')); |
140 | + dests.add(PdfStream() | ||
141 | + ..putNumList(<double>[ | ||
142 | + destRect.left, | ||
143 | + destRect.bottom, | ||
144 | + destRect.right, | ||
145 | + destRect.top | ||
146 | + ])); | ||
107 | } | 147 | } |
108 | params['/Dest'] = PdfStream.array(dests); | 148 | params['/Dest'] = PdfStream.array(dests); |
109 | } | 149 | } |
110 | } | 150 | } |
151 | + } | ||
111 | } | 152 | } |
@@ -22,7 +22,8 @@ class PdfCatalog extends PdfObject { | @@ -22,7 +22,8 @@ class PdfCatalog extends PdfObject { | ||
22 | /// @param pdfPageList The [PdfPageList] object that's the root of the documents page tree | 22 | /// @param pdfPageList The [PdfPageList] object that's the root of the documents page tree |
23 | /// @param pagemode How the document should appear when opened. | 23 | /// @param pagemode How the document should appear when opened. |
24 | /// Allowed values are usenone, useoutlines, usethumbs or fullscreen. | 24 | /// Allowed values are usenone, useoutlines, usethumbs or fullscreen. |
25 | - PdfCatalog(PdfDocument pdfDocument, this.pdfPageList, this.pageMode) | 25 | + PdfCatalog( |
26 | + PdfDocument pdfDocument, this.pdfPageList, this.pageMode, this.names) | ||
26 | : super(pdfDocument, '/Catalog'); | 27 | : super(pdfDocument, '/Catalog'); |
27 | 28 | ||
28 | /// The pages of the document | 29 | /// The pages of the document |
@@ -34,6 +35,9 @@ class PdfCatalog extends PdfObject { | @@ -34,6 +35,9 @@ class PdfCatalog extends PdfObject { | ||
34 | /// The initial page mode | 35 | /// The initial page mode |
35 | final PdfPageMode pageMode; | 36 | final PdfPageMode pageMode; |
36 | 37 | ||
38 | + /// The initial page mode | ||
39 | + final PdfNames names; | ||
40 | + | ||
37 | /// @param os OutputStream to send the object to | 41 | /// @param os OutputStream to send the object to |
38 | @override | 42 | @override |
39 | void _prepare() { | 43 | void _prepare() { |
@@ -46,6 +50,9 @@ class PdfCatalog extends PdfObject { | @@ -46,6 +50,9 @@ class PdfCatalog extends PdfObject { | ||
46 | params['/Outlines'] = outlines.ref(); | 50 | params['/Outlines'] = outlines.ref(); |
47 | } | 51 | } |
48 | 52 | ||
53 | + // the Names object | ||
54 | + params['/Names'] = names.ref(); | ||
55 | + | ||
49 | // the /PageMode setting | 56 | // the /PageMode setting |
50 | params['/PageMode'] = | 57 | params['/PageMode'] = |
51 | PdfStream.string(PdfDocument._PdfPageModes[pageMode.index]); | 58 | PdfStream.string(PdfDocument._PdfPageModes[pageMode.index]); |
@@ -89,9 +89,9 @@ class PDFBorder extends PdfBorder { | @@ -89,9 +89,9 @@ class PDFBorder extends PdfBorder { | ||
89 | 89 | ||
90 | @deprecated | 90 | @deprecated |
91 | class PDFCatalog extends PdfCatalog { | 91 | class PDFCatalog extends PdfCatalog { |
92 | - PDFCatalog( | ||
93 | - PdfDocument pdfDocument, PdfPageList pdfPageList, PdfPageMode pageMode) | ||
94 | - : super(pdfDocument, pdfPageList, pageMode); | 92 | + PDFCatalog(PdfDocument pdfDocument, PdfPageList pdfPageList, |
93 | + PdfPageMode pageMode, PdfNames names) | ||
94 | + : super(pdfDocument, pdfPageList, pageMode, names); | ||
95 | } | 95 | } |
96 | 96 | ||
97 | @deprecated | 97 | @deprecated |
@@ -51,7 +51,8 @@ class PdfDocument { | @@ -51,7 +51,8 @@ class PdfDocument { | ||
51 | 51 | ||
52 | // Now create some standard objects | 52 | // Now create some standard objects |
53 | pdfPageList = PdfPageList(this); | 53 | pdfPageList = PdfPageList(this); |
54 | - catalog = PdfCatalog(this, pdfPageList, pageMode); | 54 | + pdfNames = PdfNames(this); |
55 | + catalog = PdfCatalog(this, pdfPageList, pageMode, pdfNames); | ||
55 | } | 56 | } |
56 | 57 | ||
57 | /// This is used to allocate objects a unique serial number in the document. | 58 | /// This is used to allocate objects a unique serial number in the document. |
@@ -70,6 +71,9 @@ class PdfDocument { | @@ -70,6 +71,9 @@ class PdfDocument { | ||
70 | /// This is the Pages object, which is required by each Pdf Document | 71 | /// This is the Pages object, which is required by each Pdf Document |
71 | PdfPageList pdfPageList; | 72 | PdfPageList pdfPageList; |
72 | 73 | ||
74 | + /// The name dictionary | ||
75 | + PdfNames pdfNames; | ||
76 | + | ||
73 | /// This is the Outline object, which is optional | 77 | /// This is the Outline object, which is optional |
74 | PdfOutline _outline; | 78 | PdfOutline _outline; |
75 | 79 |
pdf/lib/src/names.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 pdf; | ||
18 | + | ||
19 | +class PdfNames extends PdfObject { | ||
20 | + /// This constructs a Pdf Name object | ||
21 | + PdfNames(PdfDocument pdfDocument) : super(pdfDocument); | ||
22 | + | ||
23 | + final List<PdfStream> _dests = <PdfStream>[]; | ||
24 | + | ||
25 | + void addDest( | ||
26 | + String name, | ||
27 | + PdfPage page, { | ||
28 | + double posX, | ||
29 | + double posY, | ||
30 | + double posZ, | ||
31 | + }) { | ||
32 | + assert(page.pdfDocument == pdfDocument); | ||
33 | + assert(name != null); | ||
34 | + | ||
35 | + _dests.add(PdfStream()..putText(name)); | ||
36 | + _dests.add(PdfStream() | ||
37 | + ..putDictionary(<String, PdfStream>{ | ||
38 | + '/D': PdfStream() | ||
39 | + ..putArray(<PdfStream>[ | ||
40 | + page.ref(), | ||
41 | + PdfStream.string('/XYZ'), | ||
42 | + posX == null ? PdfStream.string('null') : PdfStream.num(posX), | ||
43 | + posY == null ? PdfStream.string('null') : PdfStream.num(posY), | ||
44 | + posZ == null ? PdfStream.string('null') : PdfStream.num(posZ), | ||
45 | + ]), | ||
46 | + })); | ||
47 | + } | ||
48 | + | ||
49 | + @override | ||
50 | + void _prepare() { | ||
51 | + super._prepare(); | ||
52 | + | ||
53 | + params['/Dests'] = PdfStream() | ||
54 | + ..putDictionary(<String, PdfStream>{ | ||
55 | + '/Names': PdfStream()..putArray(_dests), | ||
56 | + }); | ||
57 | + } | ||
58 | +} |
@@ -23,6 +23,7 @@ import 'package:meta/meta.dart'; | @@ -23,6 +23,7 @@ import 'package:meta/meta.dart'; | ||
23 | import 'package:pdf/pdf.dart'; | 23 | import 'package:pdf/pdf.dart'; |
24 | import 'package:vector_math/vector_math_64.dart'; | 24 | import 'package:vector_math/vector_math_64.dart'; |
25 | 25 | ||
26 | +part 'widgets/annotations.dart'; | ||
26 | part 'widgets/basic.dart'; | 27 | part 'widgets/basic.dart'; |
27 | part 'widgets/clip.dart'; | 28 | part 'widgets/clip.dart'; |
28 | part 'widgets/container.dart'; | 29 | part 'widgets/container.dart'; |
pdf/lib/widgets/annotations.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 | +class Anchor extends SingleChildWidget { | ||
20 | + Anchor({Widget child, @required this.name, this.description}) | ||
21 | + : assert(name != null), | ||
22 | + super(child: child); | ||
23 | + | ||
24 | + final String name; | ||
25 | + | ||
26 | + final String description; | ||
27 | + | ||
28 | + @override | ||
29 | + void paint(Context context) { | ||
30 | + super.paint(context); | ||
31 | + paintChild(context); | ||
32 | + | ||
33 | + final Matrix4 mat = context.canvas.getTransform(); | ||
34 | + final Vector3 lt = mat.transform3(Vector3(box.left, box.bottom, 0)); | ||
35 | + context.document.pdfNames.addDest(name, context.page, posY: lt.y); | ||
36 | + | ||
37 | + if (description != null) { | ||
38 | + final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0)); | ||
39 | + final PdfRect ibox = PdfRect.fromLTRB(lt.x, lt.y, rb.x, rb.y); | ||
40 | + PdfAnnot.text(context.page, content: description, rect: ibox); | ||
41 | + } | ||
42 | + } | ||
43 | +} | ||
44 | + | ||
45 | +class _Annotation extends SingleChildWidget { | ||
46 | + _Annotation({Widget child}) : super(child: child); | ||
47 | + | ||
48 | + @override | ||
49 | + void debugPaint(Context context) { | ||
50 | + context.canvas | ||
51 | + ..setFillColor(PdfColors.pink) | ||
52 | + ..drawRect(box.x, box.y, box.width, box.height) | ||
53 | + ..fillPath(); | ||
54 | + } | ||
55 | + | ||
56 | + @override | ||
57 | + void paint(Context context) { | ||
58 | + super.paint(context); | ||
59 | + paintChild(context); | ||
60 | + } | ||
61 | +} | ||
62 | + | ||
63 | +class Link extends _Annotation { | ||
64 | + Link({@required Widget child, this.destination}) | ||
65 | + : assert(child != null), | ||
66 | + super(child: child); | ||
67 | + | ||
68 | + final String destination; | ||
69 | + | ||
70 | + @override | ||
71 | + void paint(Context context) { | ||
72 | + super.paint(context); | ||
73 | + | ||
74 | + if (destination != null) { | ||
75 | + final Matrix4 mat = context.canvas.getTransform(); | ||
76 | + final Vector3 lt = mat.transform3(Vector3(box.left, box.bottom, 0)); | ||
77 | + final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0)); | ||
78 | + final PdfRect ibox = PdfRect.fromLTRB(lt.x, lt.y, rb.x, rb.y); | ||
79 | + PdfAnnot.namedLink( | ||
80 | + context.page, | ||
81 | + rect: ibox, | ||
82 | + dest: destination, | ||
83 | + ); | ||
84 | + } | ||
85 | + } | ||
86 | +} | ||
87 | + | ||
88 | +class UrlLink extends _Annotation { | ||
89 | + UrlLink({@required Widget child, @required this.destination}) | ||
90 | + : assert(child != null), | ||
91 | + assert(destination != null), | ||
92 | + super(child: child); | ||
93 | + | ||
94 | + final String destination; | ||
95 | + | ||
96 | + @override | ||
97 | + void paint(Context context) { | ||
98 | + super.paint(context); | ||
99 | + | ||
100 | + final Matrix4 mat = context.canvas.getTransform(); | ||
101 | + final Vector3 lt = mat.transform3(Vector3(box.left, box.bottom, 0)); | ||
102 | + final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0)); | ||
103 | + final PdfRect ibox = PdfRect.fromLTRB(lt.x, lt.y, rb.x, rb.y); | ||
104 | + PdfAnnot.urlLink( | ||
105 | + context.page, | ||
106 | + rect: ibox, | ||
107 | + dest: destination, | ||
108 | + ); | ||
109 | + } | ||
110 | +} |
@@ -36,6 +36,12 @@ void main() { | @@ -36,6 +36,12 @@ void main() { | ||
36 | g.drawRect(100, 150, 50, 50); | 36 | g.drawRect(100, 150, 50, 50); |
37 | g.strokePath(); | 37 | g.strokePath(); |
38 | 38 | ||
39 | + PdfAnnot.urlLink(page, | ||
40 | + rect: const PdfRect(100, 250, 50, 50), | ||
41 | + dest: 'https://github.com/DavBfr/dart_pdf/'); | ||
42 | + g.drawRect(100, 250, 50, 50); | ||
43 | + g.strokePath(); | ||
44 | + | ||
39 | final File file = File('annotations.pdf'); | 45 | final File file = File('annotations.pdf'); |
40 | file.writeAsBytesSync(pdf.save()); | 46 | file.writeAsBytesSync(pdf.save()); |
41 | }); | 47 | }); |
@@ -51,7 +51,10 @@ void main() { | @@ -51,7 +51,10 @@ void main() { | ||
51 | width: 2)), | 51 | width: 2)), |
52 | child: Text('Hello World', | 52 | child: Text('Hello World', |
53 | textScaleFactor: 2, textAlign: TextAlign.center)), | 53 | textScaleFactor: 2, textAlign: TextAlign.center)), |
54 | - Align(alignment: Alignment.topLeft, child: Text('Left align')), | 54 | + Align( |
55 | + alignment: Alignment.topLeft, | ||
56 | + child: | ||
57 | + Link(destination: 'anchor', child: Text('Left align'))), | ||
55 | Padding(padding: const EdgeInsets.all(5)), | 58 | Padding(padding: const EdgeInsets.all(5)), |
56 | Row( | 59 | Row( |
57 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, | 60 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
@@ -133,6 +136,7 @@ void main() { | @@ -133,6 +136,7 @@ void main() { | ||
133 | <String>['York Steak House', 'Outi Vuorinen', 'Finland'], | 136 | <String>['York Steak House', 'Outi Vuorinen', 'Finland'], |
134 | <String>['Weathervane', 'Else Jeremiassen', 'Iceland'], | 137 | <String>['Weathervane', 'Else Jeremiassen', 'Iceland'], |
135 | ]), | 138 | ]), |
139 | + Anchor(name: 'anchor', child: Text('Anchor')), | ||
136 | ])); | 140 | ])); |
137 | 141 | ||
138 | pdf.addPage(Page( | 142 | pdf.addPage(Page( |
@@ -172,7 +176,13 @@ void main() { | @@ -172,7 +176,13 @@ void main() { | ||
172 | ), | 176 | ), |
173 | ], | 177 | ], |
174 | ), | 178 | ), |
175 | - )) | 179 | + )), |
180 | + Positioned( | ||
181 | + right: 10, | ||
182 | + bottom: 10, | ||
183 | + child: UrlLink( | ||
184 | + child: Text('dart_pdf'), | ||
185 | + destination: 'https://github.com/DavBfr/dart_pdf/')) | ||
176 | ]))); | 186 | ]))); |
177 | 187 | ||
178 | final File file = File('widgets.pdf'); | 188 | final File file = File('widgets.pdf'); |
-
Please register or login to post a comment