Showing
11 changed files
with
257 additions
and
18 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') { |
| 100 | - final List<PdfStream> dests = <PdfStream>[]; | ||
| 101 | - dests.add(dest.ref()); | ||
| 102 | - if (destRect == null) | ||
| 103 | - dests.add(PdfStream.string('/Fit')); | ||
| 104 | - else { | ||
| 105 | - dests.add(PdfStream.string( | ||
| 106 | - '/FitR ${destRect.left} ${destRect.bottom} ${destRect.right} ${destRect.top}')); | 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 { | ||
| 134 | + final List<PdfStream> dests = <PdfStream>[]; | ||
| 135 | + dests.add(dest.ref()); | ||
| 136 | + if (destRect == null) | ||
| 137 | + dests.add(PdfStream.string('/Fit')); | ||
| 138 | + else { | ||
| 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 | + ])); | ||
| 147 | + } | ||
| 148 | + params['/Dest'] = PdfStream.array(dests); | ||
| 107 | } | 149 | } |
| 108 | - params['/Dest'] = PdfStream.array(dests); | ||
| 109 | } | 150 | } |
| 110 | } | 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