Showing
100 changed files
with
1670 additions
and
180 deletions
Too many changes to show.
To preserve performance only 100 of 100+ files are displayed.
@@ -15,16 +15,23 @@ | @@ -15,16 +15,23 @@ | ||
15 | DART_SRC=$(shell find . -name '*.dart') | 15 | DART_SRC=$(shell find . -name '*.dart') |
16 | CLNG_SRC=$(shell find printing/ios -name '*.java' -o -name '*.m' -o -name '*.h') $(shell find printing/android -name '*.java' -o -name '*.m' -o -name '*.h') | 16 | CLNG_SRC=$(shell find printing/ios -name '*.java' -o -name '*.m' -o -name '*.h') $(shell find printing/android -name '*.java' -o -name '*.m' -o -name '*.h') |
17 | SWFT_SRC=$(shell find . -name '*.swift') | 17 | SWFT_SRC=$(shell find . -name '*.swift') |
18 | - FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf | 18 | + FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf |
19 | COV_PORT=9292 | 19 | COV_PORT=9292 |
20 | 20 | ||
21 | -all: $(FONTS) format | 21 | +all: $(FONTS) demo/assets/logo.png demo/assets/profile.jpg format |
22 | 22 | ||
23 | pdf/open-sans.ttf: | 23 | pdf/open-sans.ttf: |
24 | curl -L "https://github.com/google/fonts/raw/master/apache/opensans/OpenSans-Regular.ttf" > $@ | 24 | curl -L "https://github.com/google/fonts/raw/master/apache/opensans/OpenSans-Regular.ttf" > $@ |
25 | 25 | ||
26 | +demo/assets/open-sans.ttf: pdf/open-sans.ttf | ||
27 | + cp $^ $@ | ||
28 | + | ||
26 | pdf/open-sans-bold.ttf: | 29 | pdf/open-sans-bold.ttf: |
27 | curl -L "https://github.com/google/fonts/raw/master/apache/opensans/OpenSans-Bold.ttf" > $@ | 30 | curl -L "https://github.com/google/fonts/raw/master/apache/opensans/OpenSans-Bold.ttf" > $@ |
31 | + cp $@ demo/assets/ | ||
32 | + | ||
33 | +demo/assets/open-sans-bold.ttf: pdf/open-sans-bold.ttf | ||
34 | + cp $^ $@ | ||
28 | 35 | ||
29 | pdf/roboto.ttf: | 36 | pdf/roboto.ttf: |
30 | curl -L "https://github.com/google/fonts/raw/master/apache/robotomono/RobotoMono-Regular.ttf" > $@ | 37 | curl -L "https://github.com/google/fonts/raw/master/apache/robotomono/RobotoMono-Regular.ttf" > $@ |
@@ -35,6 +42,21 @@ pdf/noto-sans.ttf: | @@ -35,6 +42,21 @@ pdf/noto-sans.ttf: | ||
35 | pdf/genyomintw.ttf: | 42 | pdf/genyomintw.ttf: |
36 | curl -L "https://github.com/ButTaiwan/genyo-font/raw/bc2fa246196fefc1ef9e9843bc8cdba22523a39d/TW/GenYoMinTW-Heavy.ttf" > $@ | 43 | curl -L "https://github.com/ButTaiwan/genyo-font/raw/bc2fa246196fefc1ef9e9843bc8cdba22523a39d/TW/GenYoMinTW-Heavy.ttf" > $@ |
37 | 44 | ||
45 | +demo/assets/roboto1.ttf: | ||
46 | + curl -L "https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5vAw.ttf" > $@ | ||
47 | + | ||
48 | +demo/assets/roboto2.ttf: | ||
49 | + curl -L "https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlvAw.ttf" > $@ | ||
50 | + | ||
51 | +demo/assets/roboto3.ttf: | ||
52 | + curl -L "https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgWxP.ttf" > $@ | ||
53 | + | ||
54 | +demo/assets/logo.png: | ||
55 | + curl -L "https://pigment.github.io/fake-logos/logos/medium/color/auto-speed.png" > $@ | ||
56 | + | ||
57 | +demo/assets/profile.jpg: | ||
58 | + curl -L "https://www.fakepersongenerator.com/Face/female/female20151024334209870.jpg" > $@ | ||
59 | + | ||
38 | format: format-dart format-clang format-swift | 60 | format: format-dart format-clang format-swift |
39 | 61 | ||
40 | format-dart: $(DART_SRC) | 62 | format-dart: $(DART_SRC) |
@@ -58,15 +80,14 @@ get-pdf: | @@ -58,15 +80,14 @@ get-pdf: | ||
58 | 80 | ||
59 | get-printing: | 81 | get-printing: |
60 | cd printing; flutter packages get | 82 | cd printing; flutter packages get |
61 | - cd printing/example; flutter packages get | ||
62 | 83 | ||
63 | -get-web: | ||
64 | - cd pdf/web_example; pub get | 84 | +get-demo: |
85 | + cd demo; flutter packages get | ||
65 | 86 | ||
66 | get-readme: | 87 | get-readme: |
67 | cd test; flutter packages get | 88 | cd test; flutter packages get |
68 | 89 | ||
69 | -get: $(FONTS) get-pdf get-printing get-web get-readme | 90 | +get: $(FONTS) get-pdf get-printing get-demo get-readme |
70 | 91 | ||
71 | test-pdf: $(FONTS) get-pdf .coverage | 92 | test-pdf: $(FONTS) get-pdf .coverage |
72 | cd pdf; pub global run coverage:collect_coverage --port=$(COV_PORT) -o coverage.json --resume-isolates --wait-paused &\ | 93 | cd pdf; pub global run coverage:collect_coverage --port=$(COV_PORT) -o coverage.json --resume-isolates --wait-paused &\ |
@@ -77,18 +98,16 @@ test-pdf: $(FONTS) get-pdf .coverage | @@ -77,18 +98,16 @@ test-pdf: $(FONTS) get-pdf .coverage | ||
77 | 98 | ||
78 | test-printing: $(FONTS) get-printing .coverage | 99 | test-printing: $(FONTS) get-printing .coverage |
79 | cd printing; flutter test --coverage --coverage-path lcov.info | 100 | cd printing; flutter test --coverage --coverage-path lcov.info |
80 | - cd printing/example; flutter test --coverage --coverage-path lcov.info | 101 | + |
102 | +test-demo: $(FONTS) get-printing .coverage | ||
103 | + cd demo; flutter test --coverage --coverage-path lcov.info | ||
81 | 104 | ||
82 | test-readme: $(FONTS) get-readme | 105 | test-readme: $(FONTS) get-readme |
83 | cd test; dart extract_readme.dart | 106 | cd test; dart extract_readme.dart |
84 | cd test; dartanalyzer readme-*.dart | 107 | cd test; dartanalyzer readme-*.dart |
85 | 108 | ||
86 | -test-web: | ||
87 | - cd pdf/web_example; pub get | ||
88 | - cd pdf/web_example; pub run webdev build | ||
89 | - | ||
90 | -test: test-pdf test-printing node_modules test-web | ||
91 | - cat pdf/lcov.info printing/lcov.info printing/example/lcov.info | node_modules/.bin/lcov-summary | 109 | +test: test-pdf test-printing test-demo node_modules |
110 | + cat pdf/lcov.info printing/lcov.info demo/lcov.info | node_modules/.bin/lcov-summary | ||
92 | 111 | ||
93 | clean: | 112 | clean: |
94 | git clean -fdx -e .vscode -e ref | 113 | git clean -fdx -e .vscode -e ref |
@@ -148,9 +167,9 @@ ref: | @@ -148,9 +167,9 @@ ref: | ||
148 | cd $@; curl -OL 'https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf' | 167 | cd $@; curl -OL 'https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf' |
149 | 168 | ||
150 | gh-pages: | 169 | gh-pages: |
151 | - cd printing/example; flutter build web | 170 | + cd demo; flutter build web |
152 | git checkout gh-pages | 171 | git checkout gh-pages |
153 | rm -rf assets icons | 172 | rm -rf assets icons |
154 | - mv -fv printing/example/build/web/* . | 173 | + mv -fv demo/build/web/* . |
155 | 174 | ||
156 | .PHONY: test format format-dart format-clang clean publish-pdf publish-printing analyze ref | 175 | .PHONY: test format format-dart format-clang clean publish-pdf publish-printing analyze ref |
@@ -34,3 +34,7 @@ lib/generated_plugin_registrant.dart | @@ -34,3 +34,7 @@ lib/generated_plugin_registrant.dart | ||
34 | 34 | ||
35 | # Exceptions to above rules. | 35 | # Exceptions to above rules. |
36 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | 36 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages |
37 | + | ||
38 | +macos/Podfile.lock | ||
39 | +assets/*.png | ||
40 | +assets/*.jpg |
@@ -14,7 +14,9 @@ | @@ -14,7 +14,9 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | -// ignore_for_file: omit_local_variable_types | 17 | +// ignore_for_file: always_specify_types |
18 | + | ||
19 | +import 'dart:typed_data'; | ||
18 | 20 | ||
19 | import 'package:intl/intl.dart'; | 21 | import 'package:intl/intl.dart'; |
20 | import 'package:pdf/pdf.dart'; | 22 | import 'package:pdf/pdf.dart'; |
@@ -39,6 +41,7 @@ class Calendar extends StatelessWidget { | @@ -39,6 +41,7 @@ class Calendar extends StatelessWidget { | ||
39 | ) { | 41 | ) { |
40 | return Container( | 42 | return Container( |
41 | width: double.infinity, | 43 | width: double.infinity, |
44 | + margin: const EdgeInsets.only(bottom: 8), | ||
42 | child: Text( | 45 | child: Text( |
43 | DateFormat.yMMMM().format(date), | 46 | DateFormat.yMMMM().format(date), |
44 | style: const TextStyle( | 47 | style: const TextStyle( |
@@ -122,7 +125,7 @@ class Calendar extends StatelessWidget { | @@ -122,7 +125,7 @@ class Calendar extends StatelessWidget { | ||
122 | child: Container( | 125 | child: Container( |
123 | foregroundDecoration: BoxDecoration( | 126 | foregroundDecoration: BoxDecoration( |
124 | border: BoxBorder( | 127 | border: BoxBorder( |
125 | - color: PdfColors.black, | 128 | + color: PdfColors.grey, |
126 | top: true, | 129 | top: true, |
127 | left: true, | 130 | left: true, |
128 | right: index % 7 == 6, | 131 | right: index % 7 == 6, |
@@ -146,7 +149,7 @@ class Calendar extends StatelessWidget { | @@ -146,7 +149,7 @@ class Calendar extends StatelessWidget { | ||
146 | return Container( | 149 | return Container( |
147 | foregroundDecoration: BoxDecoration( | 150 | foregroundDecoration: BoxDecoration( |
148 | border: BoxBorder( | 151 | border: BoxBorder( |
149 | - color: PdfColors.black, | 152 | + color: PdfColors.grey, |
150 | left: true, | 153 | left: true, |
151 | right: index % 7 == 6, | 154 | right: index % 7 == 6, |
152 | bottom: true, | 155 | bottom: true, |
@@ -158,16 +161,32 @@ class Calendar extends StatelessWidget { | @@ -158,16 +161,32 @@ class Calendar extends StatelessWidget { | ||
158 | ); | 161 | ); |
159 | 162 | ||
160 | return Container( | 163 | return Container( |
161 | - padding: const EdgeInsets.all(20), | ||
162 | child: Column( | 164 | child: Column( |
163 | mainAxisAlignment: MainAxisAlignment.start, | 165 | mainAxisAlignment: MainAxisAlignment.start, |
164 | mainAxisSize: MainAxisSize.min, | 166 | mainAxisSize: MainAxisSize.min, |
165 | children: <Widget>[ | 167 | children: <Widget>[ |
166 | title(context, DateTime(_year, _month)), | 168 | title(context, DateTime(_year, _month)), |
167 | head, | 169 | head, |
168 | - Flexible(flex: 1, child: body), | 170 | + Expanded(child: body), |
169 | ], | 171 | ], |
170 | ), | 172 | ), |
171 | ); | 173 | ); |
172 | } | 174 | } |
173 | } | 175 | } |
176 | + | ||
177 | +Future<Uint8List> generateCalendar(PdfPageFormat pageFormat) async { | ||
178 | + //Create a PDF document. | ||
179 | + final document = Document(); | ||
180 | + | ||
181 | + document.addPage( | ||
182 | + Page( | ||
183 | + pageFormat: pageFormat, | ||
184 | + orientation: PageOrientation.landscape, | ||
185 | + build: (context) => Calendar( | ||
186 | + date: DateTime.now(), | ||
187 | + ), | ||
188 | + ), | ||
189 | + ); | ||
190 | + | ||
191 | + return document.save(); | ||
192 | +} |
demo/lib/document.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 | +// ignore_for_file: always_specify_types | ||
18 | + | ||
19 | +import 'dart:typed_data'; | ||
20 | + | ||
21 | +import 'package:pdf/pdf.dart'; | ||
22 | +import 'package:pdf/widgets.dart' as pw; | ||
23 | + | ||
24 | +Future<Uint8List> generateDocument(PdfPageFormat format) async { | ||
25 | + final pw.Document doc = pw.Document(); | ||
26 | + | ||
27 | + doc.addPage(pw.MultiPage( | ||
28 | + pageFormat: | ||
29 | + PdfPageFormat.letter.copyWith(marginBottom: 1.5 * PdfPageFormat.cm), | ||
30 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
31 | + header: (pw.Context context) { | ||
32 | + if (context.pageNumber == 1) { | ||
33 | + return null; | ||
34 | + } | ||
35 | + return pw.Container( | ||
36 | + alignment: pw.Alignment.centerRight, | ||
37 | + margin: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm), | ||
38 | + padding: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm), | ||
39 | + decoration: const pw.BoxDecoration( | ||
40 | + border: pw.BoxBorder( | ||
41 | + bottom: true, width: 0.5, color: PdfColors.grey)), | ||
42 | + child: pw.Text('Portable Document Format', | ||
43 | + style: pw.Theme.of(context) | ||
44 | + .defaultTextStyle | ||
45 | + .copyWith(color: PdfColors.grey))); | ||
46 | + }, | ||
47 | + footer: (pw.Context context) { | ||
48 | + return pw.Container( | ||
49 | + alignment: pw.Alignment.centerRight, | ||
50 | + margin: const pw.EdgeInsets.only(top: 1.0 * PdfPageFormat.cm), | ||
51 | + child: pw.Text( | ||
52 | + 'Page ${context.pageNumber} of ${context.pagesCount}', | ||
53 | + style: pw.Theme.of(context) | ||
54 | + .defaultTextStyle | ||
55 | + .copyWith(color: PdfColors.grey))); | ||
56 | + }, | ||
57 | + build: (pw.Context context) => <pw.Widget>[ | ||
58 | + pw.Header( | ||
59 | + level: 0, | ||
60 | + child: pw.Row( | ||
61 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
62 | + children: <pw.Widget>[ | ||
63 | + pw.Text('Portable Document Format', textScaleFactor: 2), | ||
64 | + pw.PdfLogo() | ||
65 | + ])), | ||
66 | + pw.Paragraph( | ||
67 | + text: | ||
68 | + 'The Portable Document Format (PDF) is a file format developed by Adobe in the 1990s to present documents, including text formatting and images, in a manner independent of application software, hardware, and operating systems. Based on the PostScript language, each PDF file encapsulates a complete description of a fixed-layout flat document, including the text, fonts, vector graphics, raster images and other information needed to display it. PDF was standardized as an open format, ISO 32000, in 2008, and no longer requires any royalties for its implementation.'), | ||
69 | + pw.Paragraph( | ||
70 | + text: | ||
71 | + 'Today, PDF files may contain a variety of content besides flat text and graphics including logical structuring elements, interactive elements such as annotations and form-fields, layers, rich media (including video content) and three dimensional objects using U3D or PRC, and various other data formats. The PDF specification also provides for encryption and digital signatures, file attachments and metadata to enable workflows requiring these features.'), | ||
72 | + pw.Header(level: 1, text: 'History and standardization'), | ||
73 | + pw.Paragraph( | ||
74 | + text: | ||
75 | + "Adobe Systems made the PDF specification available free of charge in 1993. In the early years PDF was popular mainly in desktop publishing workflows, and competed with a variety of formats such as DjVu, Envoy, Common Ground Digital Paper, Farallon Replica and even Adobe's own PostScript format."), | ||
76 | + pw.Paragraph( | ||
77 | + text: | ||
78 | + 'PDF was a proprietary format controlled by Adobe until it was released as an open standard on July 1, 2008, and published by the International Organization for Standardization as ISO 32000-1:2008, at which time control of the specification passed to an ISO Committee of volunteer industry experts. In 2008, Adobe published a Public Patent License to ISO 32000-1 granting royalty-free rights for all patents owned by Adobe that are necessary to make, use, sell, and distribute PDF compliant implementations.'), | ||
79 | + pw.Paragraph( | ||
80 | + text: | ||
81 | + "PDF 1.7, the sixth edition of the PDF specification that became ISO 32000-1, includes some proprietary technologies defined only by Adobe, such as Adobe XML Forms Architecture (XFA) and JavaScript extension for Acrobat, which are referenced by ISO 32000-1 as normative and indispensable for the full implementation of the ISO 32000-1 specification. These proprietary technologies are not standardized and their specification is published only on Adobe's website. Many of them are also not supported by popular third-party implementations of PDF."), | ||
82 | + pw.Paragraph( | ||
83 | + text: | ||
84 | + 'On July 28, 2017, ISO 32000-2:2017 (PDF 2.0) was published. ISO 32000-2 does not include any proprietary technologies as normative references.'), | ||
85 | + pw.Header(level: 1, text: 'Technical foundations'), | ||
86 | + pw.Paragraph(text: 'The PDF combines three technologies:'), | ||
87 | + pw.Bullet( | ||
88 | + text: | ||
89 | + 'A subset of the PostScript page description programming language, for generating the layout and graphics.'), | ||
90 | + pw.Bullet( | ||
91 | + text: | ||
92 | + 'A font-embedding/replacement system to allow fonts to travel with the documents.'), | ||
93 | + pw.Bullet( | ||
94 | + text: | ||
95 | + 'A structured storage system to bundle these elements and any associated content into a single file, with data compression where appropriate.'), | ||
96 | + pw.Header(level: 2, text: 'PostScript'), | ||
97 | + pw.Paragraph( | ||
98 | + text: | ||
99 | + 'PostScript is a page description language run in an interpreter to generate an image, a process requiring many resources. It can handle graphics and standard features of programming languages such as if and loop commands. PDF is largely based on PostScript but simplified to remove flow control features like these, while graphics commands such as lineto remain.'), | ||
100 | + pw.Paragraph( | ||
101 | + text: | ||
102 | + 'Often, the PostScript-like PDF code is generated from a source PostScript file. The graphics commands that are output by the PostScript code are collected and tokenized. Any files, graphics, or fonts to which the document refers also are collected. Then, everything is compressed to a single file. Therefore, the entire PostScript world (fonts, layout, measurements) remains intact.'), | ||
103 | + pw.Column( | ||
104 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
105 | + children: <pw.Widget>[ | ||
106 | + pw.Paragraph( | ||
107 | + text: | ||
108 | + 'As a document format, PDF has several advantages over PostScript:'), | ||
109 | + pw.Bullet( | ||
110 | + text: | ||
111 | + 'PDF contains tokenized and interpreted results of the PostScript source code, for direct correspondence between changes to items in the PDF page description and changes to the resulting page appearance.'), | ||
112 | + pw.Bullet( | ||
113 | + text: | ||
114 | + 'PDF (from version 1.4) supports graphic transparency; PostScript does not.'), | ||
115 | + pw.Bullet( | ||
116 | + text: | ||
117 | + 'PostScript is an interpreted programming language with an implicit global state, so instructions accompanying the description of one page can affect the appearance of any following page. Therefore, all preceding pages in a PostScript document must be processed to determine the correct appearance of a given page, whereas each page in a PDF document is unaffected by the others. As a result, PDF viewers allow the user to quickly jump to the final pages of a long document, whereas a PostScript viewer needs to process all pages sequentially before being able to display the destination page (unless the optional PostScript Document Structuring Conventions have been carefully complied with).'), | ||
118 | + ]), | ||
119 | + pw.Header(level: 1, text: 'Content'), | ||
120 | + pw.Paragraph( | ||
121 | + text: | ||
122 | + 'A PDF file is often a combination of vector graphics, text, and bitmap graphics. The basic types of content in a PDF are:'), | ||
123 | + pw.Bullet( | ||
124 | + text: | ||
125 | + 'Text stored as content streams (i.e., not encoded in plain text)'), | ||
126 | + pw.Bullet( | ||
127 | + text: | ||
128 | + 'Vector graphics for illustrations and designs that consist of shapes and lines'), | ||
129 | + pw.Bullet( | ||
130 | + text: | ||
131 | + 'Raster graphics for photographs and other types of image'), | ||
132 | + pw.Bullet(text: 'Multimedia objects in the document'), | ||
133 | + pw.Paragraph( | ||
134 | + text: | ||
135 | + 'In later PDF revisions, a PDF document can also support links (inside document or web page), forms, JavaScript (initially available as plugin for Acrobat 3.0), or any other types of embedded contents that can be handled using plug-ins.'), | ||
136 | + pw.Paragraph( | ||
137 | + text: | ||
138 | + 'PDF 1.6 supports interactive 3D documents embedded in the PDF - 3D drawings can be embedded using U3D or PRC and various other data formats.'), | ||
139 | + pw.Paragraph( | ||
140 | + text: | ||
141 | + 'Two PDF files that look similar on a computer screen may be of very different sizes. For example, a high resolution raster image takes more space than a low resolution one. Typically higher resolution is needed for printing documents than for displaying them on screen. Other things that may increase the size of a file is embedding full fonts, especially for Asiatic scripts, and storing text as graphics. '), | ||
142 | + pw.Header( | ||
143 | + level: 1, text: 'File formats and Adobe Acrobat versions'), | ||
144 | + pw.Paragraph( | ||
145 | + text: | ||
146 | + 'The PDF file format has changed several times, and continues to evolve, along with the release of new versions of Adobe Acrobat. There have been nine versions of PDF and the corresponding version of the software:'), | ||
147 | + pw.Table.fromTextArray(context: context, data: const <List<String>>[ | ||
148 | + <String>['Date', 'PDF Version', 'Acrobat Version'], | ||
149 | + <String>['1993', 'PDF 1.0', 'Acrobat 1'], | ||
150 | + <String>['1994', 'PDF 1.1', 'Acrobat 2'], | ||
151 | + <String>['1996', 'PDF 1.2', 'Acrobat 3'], | ||
152 | + <String>['1999', 'PDF 1.3', 'Acrobat 4'], | ||
153 | + <String>['2001', 'PDF 1.4', 'Acrobat 5'], | ||
154 | + <String>['2003', 'PDF 1.5', 'Acrobat 6'], | ||
155 | + <String>['2005', 'PDF 1.6', 'Acrobat 7'], | ||
156 | + <String>['2006', 'PDF 1.7', 'Acrobat 8'], | ||
157 | + <String>['2008', 'PDF 1.7', 'Acrobat 9'], | ||
158 | + <String>['2009', 'PDF 1.7', 'Acrobat 9.1'], | ||
159 | + <String>['2010', 'PDF 1.7', 'Acrobat X'], | ||
160 | + <String>['2012', 'PDF 1.7', 'Acrobat XI'], | ||
161 | + <String>['2017', 'PDF 2.0', 'Acrobat DC'], | ||
162 | + ]), | ||
163 | + pw.Padding(padding: const pw.EdgeInsets.all(10)), | ||
164 | + pw.Paragraph( | ||
165 | + text: | ||
166 | + 'Text is available under the Creative Commons Attribution Share Alike License.') | ||
167 | + ])); | ||
168 | + | ||
169 | + return doc.save(); | ||
170 | +} |
demo/lib/invoice.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 | +// ignore_for_file: always_specify_types | ||
18 | + | ||
19 | +import 'dart:typed_data'; | ||
20 | + | ||
21 | +import 'package:flutter/services.dart'; | ||
22 | +import 'package:intl/intl.dart'; | ||
23 | +import 'package:pdf/pdf.dart'; | ||
24 | +import 'package:pdf/widgets.dart' as pw; | ||
25 | + | ||
26 | +Future<Uint8List> generateInvoice(PdfPageFormat pageFormat) async { | ||
27 | + final lorem = pw.LoremText(); | ||
28 | + | ||
29 | + final products = <Product>[ | ||
30 | + Product('19874', lorem.sentence(4), 3.99, 2), | ||
31 | + Product('98452', lorem.sentence(6), 15, 2), | ||
32 | + Product('28375', lorem.sentence(4), 6.95, 3), | ||
33 | + Product('95673', lorem.sentence(3), 49.99, 4), | ||
34 | + Product('23763', lorem.sentence(2), 560.03, 1), | ||
35 | + Product('55209', lorem.sentence(5), 26, 1), | ||
36 | + Product('09853', lorem.sentence(5), 26, 1), | ||
37 | + Product('23463', lorem.sentence(5), 34, 1), | ||
38 | + Product('56783', lorem.sentence(5), 7, 4), | ||
39 | + Product('78256', lorem.sentence(5), 23, 1), | ||
40 | + Product('23745', lorem.sentence(5), 94, 1), | ||
41 | + Product('07834', lorem.sentence(5), 12, 1), | ||
42 | + Product('23547', lorem.sentence(5), 34, 1), | ||
43 | + Product('98387', lorem.sentence(5), 7.99, 2), | ||
44 | + ]; | ||
45 | + | ||
46 | + final invoice = Invoice( | ||
47 | + invoiceNumber: '982347', | ||
48 | + products: products, | ||
49 | + customerName: 'Abraham Swearegin', | ||
50 | + customerAddress: '54 rue de Rivoli\n75001 Paris, France', | ||
51 | + paymentInfo: | ||
52 | + '4509 Wiseman Street\nKnoxville, Tennessee(TN), 37929\n865-372-0425', | ||
53 | + tax: .15, | ||
54 | + baseColor: PdfColors.teal, | ||
55 | + accentColor: PdfColors.blueGrey900, | ||
56 | + ); | ||
57 | + | ||
58 | + return await invoice.buildPdf(pageFormat); | ||
59 | +} | ||
60 | + | ||
61 | +class Invoice { | ||
62 | + Invoice({ | ||
63 | + this.products, | ||
64 | + this.customerName, | ||
65 | + this.customerAddress, | ||
66 | + this.invoiceNumber, | ||
67 | + this.tax, | ||
68 | + this.paymentInfo, | ||
69 | + this.baseColor, | ||
70 | + this.accentColor, | ||
71 | + }); | ||
72 | + | ||
73 | + final List<Product> products; | ||
74 | + final String customerName; | ||
75 | + final String customerAddress; | ||
76 | + final String invoiceNumber; | ||
77 | + final double tax; | ||
78 | + final String paymentInfo; | ||
79 | + final PdfColor baseColor; | ||
80 | + final PdfColor accentColor; | ||
81 | + | ||
82 | + static const _darkColor = PdfColors.blueGrey800; | ||
83 | + static const _lightColor = PdfColors.white; | ||
84 | + | ||
85 | + PdfColor get _baseTextColor => | ||
86 | + baseColor.luminance < 0.5 ? _lightColor : _darkColor; | ||
87 | + | ||
88 | + PdfColor get _accentTextColor => | ||
89 | + baseColor.luminance < 0.5 ? _lightColor : _darkColor; | ||
90 | + | ||
91 | + double get _total => | ||
92 | + products.map<double>((p) => p.total).reduce((a, b) => a + b); | ||
93 | + | ||
94 | + double get _grandTotal => _total * (1 + tax); | ||
95 | + | ||
96 | + PdfImage _logo; | ||
97 | + | ||
98 | + Future<Uint8List> buildPdf(PdfPageFormat pageFormat) async { | ||
99 | + // Create a PDF document. | ||
100 | + final doc = pw.Document(); | ||
101 | + | ||
102 | + final font1 = await rootBundle.load('assets/roboto1.ttf'); | ||
103 | + final font2 = await rootBundle.load('assets/roboto2.ttf'); | ||
104 | + final font3 = await rootBundle.load('assets/roboto3.ttf'); | ||
105 | + | ||
106 | + _logo = PdfImage.file( | ||
107 | + doc.document, | ||
108 | + bytes: (await rootBundle.load('assets/logo.png')).buffer.asUint8List(), | ||
109 | + ); | ||
110 | + | ||
111 | + // Add page to the PDF | ||
112 | + doc.addPage( | ||
113 | + pw.MultiPage( | ||
114 | + pageTheme: _buildTheme( | ||
115 | + pageFormat, | ||
116 | + font1 != null ? pw.Font.ttf(font1) : null, | ||
117 | + font2 != null ? pw.Font.ttf(font2) : null, | ||
118 | + font3 != null ? pw.Font.ttf(font3) : null, | ||
119 | + ), | ||
120 | + header: _buildHeader, | ||
121 | + footer: _buildFooter, | ||
122 | + build: (context) => [ | ||
123 | + _contentHeader(context), | ||
124 | + _contentTable(context), | ||
125 | + pw.SizedBox(height: 20), | ||
126 | + _contentFooter(context), | ||
127 | + pw.SizedBox(height: 20), | ||
128 | + _termsAndConditions(context), | ||
129 | + ], | ||
130 | + ), | ||
131 | + ); | ||
132 | + | ||
133 | + // Return the PDF file content | ||
134 | + return doc.save(); | ||
135 | + } | ||
136 | + | ||
137 | + pw.Widget _buildHeader(pw.Context context) { | ||
138 | + return pw.Column( | ||
139 | + children: [ | ||
140 | + pw.Row( | ||
141 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
142 | + children: [ | ||
143 | + pw.Expanded( | ||
144 | + child: pw.Column( | ||
145 | + children: [ | ||
146 | + pw.Container( | ||
147 | + height: 50, | ||
148 | + padding: const pw.EdgeInsets.only(left: 20), | ||
149 | + alignment: pw.Alignment.centerLeft, | ||
150 | + child: pw.Text( | ||
151 | + 'INVOICE', | ||
152 | + style: pw.TextStyle( | ||
153 | + color: baseColor, | ||
154 | + fontWeight: pw.FontWeight.bold, | ||
155 | + fontSize: 40, | ||
156 | + ), | ||
157 | + ), | ||
158 | + ), | ||
159 | + pw.Container( | ||
160 | + decoration: pw.BoxDecoration( | ||
161 | + borderRadius: 2, | ||
162 | + color: accentColor, | ||
163 | + ), | ||
164 | + padding: const pw.EdgeInsets.only( | ||
165 | + left: 40, top: 10, bottom: 10, right: 20), | ||
166 | + alignment: pw.Alignment.centerLeft, | ||
167 | + height: 50, | ||
168 | + child: pw.DefaultTextStyle( | ||
169 | + style: pw.TextStyle( | ||
170 | + color: _accentTextColor, | ||
171 | + fontSize: 12, | ||
172 | + ), | ||
173 | + child: pw.GridView( | ||
174 | + crossAxisCount: 2, | ||
175 | + children: [ | ||
176 | + pw.Text('Invoice #'), | ||
177 | + pw.Text(invoiceNumber), | ||
178 | + pw.Text('Date:'), | ||
179 | + pw.Text(_formatDate(DateTime.now())), | ||
180 | + ], | ||
181 | + ), | ||
182 | + ), | ||
183 | + ), | ||
184 | + ], | ||
185 | + ), | ||
186 | + ), | ||
187 | + pw.Expanded( | ||
188 | + child: pw.Column( | ||
189 | + mainAxisSize: pw.MainAxisSize.min, | ||
190 | + children: [ | ||
191 | + pw.Container( | ||
192 | + alignment: pw.Alignment.topRight, | ||
193 | + padding: const pw.EdgeInsets.only(bottom: 8, left: 30), | ||
194 | + height: 72, | ||
195 | + child: _logo != null ? pw.Image(_logo) : pw.PdfLogo(), | ||
196 | + ), | ||
197 | + // pw.Container( | ||
198 | + // color: baseColor, | ||
199 | + // padding: pw.EdgeInsets.only(top: 3), | ||
200 | + // ), | ||
201 | + ], | ||
202 | + ), | ||
203 | + ), | ||
204 | + ], | ||
205 | + ), | ||
206 | + if (context.pageNumber > 1) pw.SizedBox(height: 20) | ||
207 | + ], | ||
208 | + ); | ||
209 | + } | ||
210 | + | ||
211 | + pw.Widget _buildFooter(pw.Context context) { | ||
212 | + return pw.Row( | ||
213 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
214 | + crossAxisAlignment: pw.CrossAxisAlignment.end, | ||
215 | + children: [ | ||
216 | + pw.Container( | ||
217 | + height: 20, | ||
218 | + width: 100, | ||
219 | + child: pw.BarcodeWidget( | ||
220 | + barcode: pw.Barcode.pdf417(), | ||
221 | + data: 'Invoice# $invoiceNumber', | ||
222 | + ), | ||
223 | + ), | ||
224 | + pw.Text( | ||
225 | + 'Page ${context.pageNumber}/${context.pagesCount}', | ||
226 | + style: const pw.TextStyle( | ||
227 | + fontSize: 12, | ||
228 | + color: PdfColors.grey, | ||
229 | + ), | ||
230 | + ), | ||
231 | + ], | ||
232 | + ); | ||
233 | + } | ||
234 | + | ||
235 | + pw.PageTheme _buildTheme( | ||
236 | + PdfPageFormat pageFormat, pw.Font base, pw.Font bold, pw.Font italic) { | ||
237 | + return pw.PageTheme( | ||
238 | + pageFormat: pageFormat, | ||
239 | + theme: pw.ThemeData.withFont( | ||
240 | + base: base, | ||
241 | + bold: bold, | ||
242 | + italic: italic, | ||
243 | + ), | ||
244 | + buildBackground: (context) => pw.FullPage( | ||
245 | + ignoreMargins: true, | ||
246 | + child: pw.Stack( | ||
247 | + children: [ | ||
248 | + pw.Positioned( | ||
249 | + bottom: 0, | ||
250 | + left: 0, | ||
251 | + child: pw.Container( | ||
252 | + height: 20, | ||
253 | + width: pageFormat.width / 2, | ||
254 | + decoration: pw.BoxDecoration( | ||
255 | + gradient: pw.LinearGradient( | ||
256 | + colors: [baseColor, PdfColors.white], | ||
257 | + ), | ||
258 | + ), | ||
259 | + ), | ||
260 | + ), | ||
261 | + pw.Positioned( | ||
262 | + bottom: 20, | ||
263 | + left: 0, | ||
264 | + child: pw.Container( | ||
265 | + height: 20, | ||
266 | + width: pageFormat.width / 4, | ||
267 | + decoration: pw.BoxDecoration( | ||
268 | + gradient: pw.LinearGradient( | ||
269 | + colors: [accentColor, PdfColors.white], | ||
270 | + ), | ||
271 | + ), | ||
272 | + ), | ||
273 | + ), | ||
274 | + pw.Positioned( | ||
275 | + top: pageFormat.marginTop + 72, | ||
276 | + left: 0, | ||
277 | + right: 0, | ||
278 | + child: pw.Container( | ||
279 | + height: 3, | ||
280 | + color: baseColor, | ||
281 | + ), | ||
282 | + ), | ||
283 | + ], | ||
284 | + ), | ||
285 | + ), | ||
286 | + ); | ||
287 | + } | ||
288 | + | ||
289 | + pw.Widget _contentHeader(pw.Context context) { | ||
290 | + return pw.Row( | ||
291 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
292 | + children: [ | ||
293 | + pw.Expanded( | ||
294 | + child: pw.Container( | ||
295 | + margin: const pw.EdgeInsets.symmetric(horizontal: 20), | ||
296 | + height: 70, | ||
297 | + child: pw.FittedBox( | ||
298 | + child: pw.Text( | ||
299 | + 'Total: ${_formatCurrency(_grandTotal)}', | ||
300 | + style: pw.TextStyle( | ||
301 | + color: baseColor, | ||
302 | + fontStyle: pw.FontStyle.italic, | ||
303 | + ), | ||
304 | + ), | ||
305 | + ), | ||
306 | + ), | ||
307 | + ), | ||
308 | + pw.Expanded( | ||
309 | + child: pw.Row( | ||
310 | + children: [ | ||
311 | + pw.Container( | ||
312 | + margin: const pw.EdgeInsets.only(left: 10, right: 10), | ||
313 | + height: 70, | ||
314 | + child: pw.Text( | ||
315 | + 'Invoice to:', | ||
316 | + style: pw.TextStyle( | ||
317 | + color: _darkColor, | ||
318 | + fontWeight: pw.FontWeight.bold, | ||
319 | + fontSize: 12, | ||
320 | + ), | ||
321 | + ), | ||
322 | + ), | ||
323 | + pw.Expanded( | ||
324 | + child: pw.Container( | ||
325 | + height: 70, | ||
326 | + child: pw.RichText( | ||
327 | + text: pw.TextSpan( | ||
328 | + text: '$customerName\n', | ||
329 | + style: pw.TextStyle( | ||
330 | + color: _darkColor, | ||
331 | + fontWeight: pw.FontWeight.bold, | ||
332 | + fontSize: 12, | ||
333 | + ), | ||
334 | + children: [ | ||
335 | + const pw.TextSpan( | ||
336 | + text: '\n', | ||
337 | + style: pw.TextStyle( | ||
338 | + fontSize: 5, | ||
339 | + ), | ||
340 | + ), | ||
341 | + pw.TextSpan( | ||
342 | + text: customerAddress, | ||
343 | + style: pw.TextStyle( | ||
344 | + fontWeight: pw.FontWeight.normal, | ||
345 | + fontSize: 10, | ||
346 | + ), | ||
347 | + ), | ||
348 | + ])), | ||
349 | + ), | ||
350 | + ), | ||
351 | + ], | ||
352 | + ), | ||
353 | + ), | ||
354 | + ], | ||
355 | + ); | ||
356 | + } | ||
357 | + | ||
358 | + pw.Widget _contentFooter(pw.Context context) { | ||
359 | + return pw.Row( | ||
360 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
361 | + children: [ | ||
362 | + pw.Expanded( | ||
363 | + flex: 2, | ||
364 | + child: pw.Column( | ||
365 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
366 | + children: [ | ||
367 | + pw.Text( | ||
368 | + 'Thank you for your business', | ||
369 | + style: pw.TextStyle( | ||
370 | + color: _darkColor, | ||
371 | + fontWeight: pw.FontWeight.bold, | ||
372 | + ), | ||
373 | + ), | ||
374 | + pw.Container( | ||
375 | + margin: const pw.EdgeInsets.only(top: 20, bottom: 8), | ||
376 | + child: pw.Text( | ||
377 | + 'Payment Info:', | ||
378 | + style: pw.TextStyle( | ||
379 | + color: baseColor, | ||
380 | + fontWeight: pw.FontWeight.bold, | ||
381 | + ), | ||
382 | + ), | ||
383 | + ), | ||
384 | + pw.Text( | ||
385 | + paymentInfo, | ||
386 | + style: const pw.TextStyle( | ||
387 | + fontSize: 8, | ||
388 | + lineSpacing: 5, | ||
389 | + color: _darkColor, | ||
390 | + ), | ||
391 | + ), | ||
392 | + ], | ||
393 | + ), | ||
394 | + ), | ||
395 | + pw.Expanded( | ||
396 | + flex: 1, | ||
397 | + child: pw.DefaultTextStyle( | ||
398 | + style: const pw.TextStyle( | ||
399 | + fontSize: 10, | ||
400 | + color: _darkColor, | ||
401 | + ), | ||
402 | + child: pw.Column( | ||
403 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
404 | + children: [ | ||
405 | + pw.Row( | ||
406 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
407 | + children: [ | ||
408 | + pw.Text('Sub Total:'), | ||
409 | + pw.Text(_formatCurrency(_total)), | ||
410 | + ], | ||
411 | + ), | ||
412 | + pw.SizedBox(height: 5), | ||
413 | + pw.Row( | ||
414 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
415 | + children: [ | ||
416 | + pw.Text('Tax:'), | ||
417 | + pw.Text('${(tax * 100).toStringAsFixed(1)}%'), | ||
418 | + ], | ||
419 | + ), | ||
420 | + pw.Divider(color: accentColor), | ||
421 | + pw.DefaultTextStyle( | ||
422 | + style: pw.TextStyle( | ||
423 | + color: baseColor, | ||
424 | + fontSize: 14, | ||
425 | + fontWeight: pw.FontWeight.bold, | ||
426 | + ), | ||
427 | + child: pw.Row( | ||
428 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
429 | + children: [ | ||
430 | + pw.Text('Total:'), | ||
431 | + pw.Text(_formatCurrency(_grandTotal)), | ||
432 | + ], | ||
433 | + ), | ||
434 | + ), | ||
435 | + ], | ||
436 | + ), | ||
437 | + ), | ||
438 | + ), | ||
439 | + ], | ||
440 | + ); | ||
441 | + } | ||
442 | + | ||
443 | + pw.Widget _termsAndConditions(pw.Context context) { | ||
444 | + return pw.Row( | ||
445 | + crossAxisAlignment: pw.CrossAxisAlignment.end, | ||
446 | + children: [ | ||
447 | + pw.Expanded( | ||
448 | + child: pw.Column( | ||
449 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
450 | + children: [ | ||
451 | + pw.Container( | ||
452 | + decoration: pw.BoxDecoration( | ||
453 | + border: pw.BoxBorder( | ||
454 | + top: true, | ||
455 | + color: accentColor, | ||
456 | + ), | ||
457 | + ), | ||
458 | + padding: const pw.EdgeInsets.only(top: 10, bottom: 4), | ||
459 | + child: pw.Text( | ||
460 | + 'Terms & Conditions', | ||
461 | + style: pw.TextStyle( | ||
462 | + fontSize: 12, | ||
463 | + color: baseColor, | ||
464 | + fontWeight: pw.FontWeight.bold, | ||
465 | + ), | ||
466 | + ), | ||
467 | + ), | ||
468 | + pw.Text( | ||
469 | + pw.LoremText().paragraph(40), | ||
470 | + textAlign: pw.TextAlign.justify, | ||
471 | + style: const pw.TextStyle( | ||
472 | + fontSize: 6, | ||
473 | + lineSpacing: 2, | ||
474 | + color: _darkColor, | ||
475 | + ), | ||
476 | + ), | ||
477 | + ], | ||
478 | + ), | ||
479 | + ), | ||
480 | + pw.Expanded( | ||
481 | + child: pw.SizedBox(), | ||
482 | + ), | ||
483 | + ], | ||
484 | + ); | ||
485 | + } | ||
486 | + | ||
487 | + pw.Widget _contentTable(pw.Context context) { | ||
488 | + const tableHeaders = [ | ||
489 | + 'SKU#', | ||
490 | + 'Item Description', | ||
491 | + 'Price', | ||
492 | + 'Quantity', | ||
493 | + 'Total' | ||
494 | + ]; | ||
495 | + | ||
496 | + return pw.Table.fromTextArray( | ||
497 | + border: null, | ||
498 | + cellAlignment: pw.Alignment.centerLeft, | ||
499 | + headerDecoration: pw.BoxDecoration( | ||
500 | + borderRadius: 2, | ||
501 | + color: baseColor, | ||
502 | + ), | ||
503 | + headerHeight: 25, | ||
504 | + cellHeight: 40, | ||
505 | + cellAlignments: { | ||
506 | + 0: pw.Alignment.centerLeft, | ||
507 | + 1: pw.Alignment.centerLeft, | ||
508 | + 2: pw.Alignment.centerRight, | ||
509 | + 3: pw.Alignment.center, | ||
510 | + 4: pw.Alignment.centerRight, | ||
511 | + }, | ||
512 | + headerStyle: pw.TextStyle( | ||
513 | + color: _baseTextColor, | ||
514 | + fontSize: 10, | ||
515 | + fontWeight: pw.FontWeight.bold, | ||
516 | + ), | ||
517 | + cellStyle: const pw.TextStyle( | ||
518 | + color: _darkColor, | ||
519 | + fontSize: 10, | ||
520 | + ), | ||
521 | + rowDecoration: pw.BoxDecoration( | ||
522 | + border: pw.BoxBorder( | ||
523 | + bottom: true, | ||
524 | + color: accentColor, | ||
525 | + width: .5, | ||
526 | + ), | ||
527 | + ), | ||
528 | + headers: List<String>.generate( | ||
529 | + tableHeaders.length, | ||
530 | + (col) => tableHeaders[col], | ||
531 | + ), | ||
532 | + data: List<List<String>>.generate( | ||
533 | + products.length, | ||
534 | + (row) => List<String>.generate( | ||
535 | + tableHeaders.length, | ||
536 | + (col) => products[row].getIndex(col), | ||
537 | + ), | ||
538 | + ), | ||
539 | + ); | ||
540 | + } | ||
541 | +} | ||
542 | + | ||
543 | +String _formatCurrency(double amount) { | ||
544 | + return '\$${amount.toStringAsFixed(2)}'; | ||
545 | +} | ||
546 | + | ||
547 | +String _formatDate(DateTime date) { | ||
548 | + final format = DateFormat.yMMMMd('en_US'); | ||
549 | + return format.format(date); | ||
550 | +} | ||
551 | + | ||
552 | +class Product { | ||
553 | + const Product( | ||
554 | + this.sku, | ||
555 | + this.productName, | ||
556 | + this.price, | ||
557 | + this.quantity, | ||
558 | + ); | ||
559 | + | ||
560 | + final String sku; | ||
561 | + final String productName; | ||
562 | + final double price; | ||
563 | + final int quantity; | ||
564 | + double get total => price * quantity; | ||
565 | + | ||
566 | + String getIndex(int index) { | ||
567 | + switch (index) { | ||
568 | + case 0: | ||
569 | + return sku; | ||
570 | + case 1: | ||
571 | + return productName; | ||
572 | + case 2: | ||
573 | + return _formatCurrency(price); | ||
574 | + case 3: | ||
575 | + return quantity.toString(); | ||
576 | + case 4: | ||
577 | + return _formatCurrency(total); | ||
578 | + } | ||
579 | + return ''; | ||
580 | + } | ||
581 | +} |
demo/lib/main.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 | +// ignore_for_file: always_specify_types | ||
18 | + | ||
19 | +import 'dart:async'; | ||
20 | +import 'dart:io'; | ||
21 | +import 'dart:typed_data'; | ||
22 | + | ||
23 | +import 'package:flutter/foundation.dart'; | ||
24 | +import 'package:flutter/material.dart'; | ||
25 | +import 'package:open_file/open_file.dart'; | ||
26 | +import 'package:path_provider/path_provider.dart'; | ||
27 | +import 'package:pdf/pdf.dart'; | ||
28 | +import 'package:pdf/widgets.dart' as pw; | ||
29 | +import 'package:printing/printing.dart'; | ||
30 | +import 'package:url_launcher/url_launcher.dart' as ul; | ||
31 | + | ||
32 | +import 'calendar.dart'; | ||
33 | +import 'document.dart'; | ||
34 | +import 'invoice.dart'; | ||
35 | +import 'report.dart'; | ||
36 | +import 'resume.dart'; | ||
37 | + | ||
38 | +void main() { | ||
39 | + debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; | ||
40 | + runApp(MaterialApp(home: MyApp())); | ||
41 | +} | ||
42 | + | ||
43 | +class MyApp extends StatefulWidget { | ||
44 | + @override | ||
45 | + MyAppState createState() { | ||
46 | + return MyAppState(); | ||
47 | + } | ||
48 | +} | ||
49 | + | ||
50 | +class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin { | ||
51 | + List<Tab> _myTabs; | ||
52 | + List<LayoutCallback> _tabGen; | ||
53 | + List<String> _tabUrl; | ||
54 | + int _tab = 0; | ||
55 | + TabController _tabController; | ||
56 | + | ||
57 | + PrintingInfo printingInfo; | ||
58 | + | ||
59 | + @override | ||
60 | + void initState() { | ||
61 | + super.initState(); | ||
62 | + _init(); | ||
63 | + } | ||
64 | + | ||
65 | + Future<void> _init() async { | ||
66 | + final PrintingInfo info = await Printing.info(); | ||
67 | + | ||
68 | + _myTabs = const <Tab>[ | ||
69 | + Tab(text: 'RÉSUMÉ'), | ||
70 | + Tab(text: 'DOCUMENT'), | ||
71 | + Tab(text: 'INVOICE'), | ||
72 | + Tab(text: 'REPORT'), | ||
73 | + Tab(text: 'CALENDAR'), | ||
74 | + ]; | ||
75 | + | ||
76 | + _tabGen = const <LayoutCallback>[ | ||
77 | + generateResume, | ||
78 | + generateDocument, | ||
79 | + generateInvoice, | ||
80 | + generateReport, | ||
81 | + generateCalendar, | ||
82 | + ]; | ||
83 | + | ||
84 | + _tabUrl = const <String>[ | ||
85 | + 'resume.dart', | ||
86 | + 'document.dart', | ||
87 | + 'invoice.dart', | ||
88 | + 'report.dart', | ||
89 | + 'calendar.dart', | ||
90 | + ]; | ||
91 | + | ||
92 | + _tabController = TabController( | ||
93 | + vsync: this, | ||
94 | + length: _myTabs.length, | ||
95 | + initialIndex: _tab, | ||
96 | + ); | ||
97 | + _tabController.addListener(() { | ||
98 | + setState(() { | ||
99 | + _tab = _tabController.index; | ||
100 | + }); | ||
101 | + }); | ||
102 | + | ||
103 | + setState(() { | ||
104 | + printingInfo = info; | ||
105 | + }); | ||
106 | + } | ||
107 | + | ||
108 | + void _showPrintedToast(BuildContext context) { | ||
109 | + final ScaffoldState scaffold = Scaffold.of(context); | ||
110 | + | ||
111 | + scaffold.showSnackBar( | ||
112 | + const SnackBar( | ||
113 | + content: Text('Document printed successfully'), | ||
114 | + ), | ||
115 | + ); | ||
116 | + } | ||
117 | + | ||
118 | + void _showSharedToast(BuildContext context) { | ||
119 | + final ScaffoldState scaffold = Scaffold.of(context); | ||
120 | + | ||
121 | + scaffold.showSnackBar( | ||
122 | + const SnackBar( | ||
123 | + content: Text('Document shared successfully'), | ||
124 | + ), | ||
125 | + ); | ||
126 | + } | ||
127 | + | ||
128 | + Future<void> _saveAsFile( | ||
129 | + BuildContext context, | ||
130 | + LayoutCallback build, | ||
131 | + PdfPageFormat pageFormat, | ||
132 | + ) async { | ||
133 | + final Uint8List bytes = await build(pageFormat); | ||
134 | + | ||
135 | + final Directory appDocDir = await getApplicationDocumentsDirectory(); | ||
136 | + final String appDocPath = appDocDir.path; | ||
137 | + final File file = File(appDocPath + '/' + 'document.pdf'); | ||
138 | + print('Save as file ${file.path} ...'); | ||
139 | + await file.writeAsBytes(bytes); | ||
140 | + OpenFile.open(file.path); | ||
141 | + } | ||
142 | + | ||
143 | + @override | ||
144 | + Widget build(BuildContext context) { | ||
145 | + pw.RichText.debug = true; | ||
146 | + | ||
147 | + if (_tabController == null) { | ||
148 | + return const Center(child: CircularProgressIndicator()); | ||
149 | + } | ||
150 | + | ||
151 | + final actions = <PdfPreviewAction>[ | ||
152 | + if (!kIsWeb) | ||
153 | + PdfPreviewAction( | ||
154 | + icon: const Icon(Icons.save), | ||
155 | + onPressed: _saveAsFile, | ||
156 | + ) | ||
157 | + ]; | ||
158 | + | ||
159 | + return Scaffold( | ||
160 | + appBar: AppBar( | ||
161 | + title: const Text('Pdf Printing Example'), | ||
162 | + bottom: TabBar( | ||
163 | + controller: _tabController, | ||
164 | + tabs: _myTabs, | ||
165 | + ), | ||
166 | + ), | ||
167 | + body: PdfPreview( | ||
168 | + maxPageWidth: 700, | ||
169 | + build: _tabGen[_tab], | ||
170 | + actions: actions, | ||
171 | + onPrinted: _showPrintedToast, | ||
172 | + onShared: _showSharedToast, | ||
173 | + ), | ||
174 | + floatingActionButton: FloatingActionButton( | ||
175 | + backgroundColor: Colors.deepOrange, | ||
176 | + onPressed: _showSources, | ||
177 | + child: Icon(Icons.code), | ||
178 | + ), | ||
179 | + ); | ||
180 | + } | ||
181 | + | ||
182 | + void _showSources() { | ||
183 | + ul.launch( | ||
184 | + 'https://github.com/DavBfr/dart_pdf/blob/master/demo/lib/${_tabUrl[_tab]}', | ||
185 | + ); | ||
186 | + } | ||
187 | +} |
demo/lib/report.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 | +// ignore_for_file: always_specify_types | ||
18 | + | ||
19 | +import 'dart:math'; | ||
20 | +import 'dart:typed_data'; | ||
21 | + | ||
22 | +import 'package:flutter/services.dart'; | ||
23 | +import 'package:pdf/pdf.dart'; | ||
24 | +import 'package:pdf/widgets.dart' as pw; | ||
25 | + | ||
26 | +Future<Uint8List> generateReport(PdfPageFormat pageFormat) async { | ||
27 | + const tableHeaders = ['Category', 'Budget', 'Expense', 'Result']; | ||
28 | + | ||
29 | + const dataTable = [ | ||
30 | + ['Phone', 80, 95, -15], | ||
31 | + ['Internet', 250, 230, 20], | ||
32 | + ['Electricity', 300, 375, -75], | ||
33 | + ['Movies', 85, 80, 5], | ||
34 | + ['Food', 300, 350, -50], | ||
35 | + ['Fuel', 650, 550, 100], | ||
36 | + ['Insurance', 250, 310, -60], | ||
37 | + ]; | ||
38 | + | ||
39 | + final baseColor = PdfColors.cyan; | ||
40 | + | ||
41 | + // Create a PDF document. | ||
42 | + final document = pw.Document(); | ||
43 | + | ||
44 | + // Add page to the PDF | ||
45 | + document.addPage( | ||
46 | + pw.Page( | ||
47 | + pageFormat: pageFormat, | ||
48 | + theme: pw.ThemeData.withFont( | ||
49 | + base: pw.Font.ttf(await rootBundle.load('assets/open-sans.ttf')), | ||
50 | + bold: pw.Font.ttf(await rootBundle.load('assets/open-sans-bold.ttf')), | ||
51 | + ), | ||
52 | + build: (context) { | ||
53 | + final chart1 = pw.Chart( | ||
54 | + left: pw.Container( | ||
55 | + alignment: pw.Alignment.topCenter, | ||
56 | + margin: const pw.EdgeInsets.only(right: 5, top: 10), | ||
57 | + child: pw.Transform.rotateBox( | ||
58 | + angle: pi / 2, | ||
59 | + child: pw.Text('Amount'), | ||
60 | + ), | ||
61 | + ), | ||
62 | + overlay: pw.ChartLegend( | ||
63 | + position: const pw.Alignment(-.7, 1), | ||
64 | + decoration: const pw.BoxDecoration( | ||
65 | + color: PdfColors.white, | ||
66 | + border: pw.BoxBorder( | ||
67 | + bottom: true, | ||
68 | + top: true, | ||
69 | + left: true, | ||
70 | + right: true, | ||
71 | + color: PdfColors.black, | ||
72 | + width: .5, | ||
73 | + ), | ||
74 | + ), | ||
75 | + ), | ||
76 | + grid: pw.CartesianGrid( | ||
77 | + xAxis: pw.FixedAxis.fromStrings( | ||
78 | + List<String>.generate( | ||
79 | + dataTable.length, (index) => dataTable[index][0]), | ||
80 | + marginStart: 30, | ||
81 | + marginEnd: 30, | ||
82 | + ticks: true, | ||
83 | + ), | ||
84 | + yAxis: pw.FixedAxis( | ||
85 | + [0, 100, 200, 300, 400, 500, 600, 700], | ||
86 | + format: (v) => '\$$v', | ||
87 | + divisions: true, | ||
88 | + ), | ||
89 | + ), | ||
90 | + datasets: [ | ||
91 | + pw.BarDataSet( | ||
92 | + color: PdfColors.blue100, | ||
93 | + legend: tableHeaders[2], | ||
94 | + width: 15, | ||
95 | + offset: -10, | ||
96 | + borderColor: baseColor, | ||
97 | + data: List<pw.LineChartValue>.generate( | ||
98 | + dataTable.length, | ||
99 | + (i) { | ||
100 | + final num v = dataTable[i][2]; | ||
101 | + return pw.LineChartValue(i.toDouble(), v.toDouble()); | ||
102 | + }, | ||
103 | + ), | ||
104 | + ), | ||
105 | + pw.BarDataSet( | ||
106 | + color: PdfColors.amber100, | ||
107 | + legend: tableHeaders[1], | ||
108 | + width: 15, | ||
109 | + offset: 10, | ||
110 | + borderColor: PdfColors.amber, | ||
111 | + data: List<pw.LineChartValue>.generate( | ||
112 | + dataTable.length, | ||
113 | + (i) { | ||
114 | + final num v = dataTable[i][1]; | ||
115 | + return pw.LineChartValue(i.toDouble(), v.toDouble()); | ||
116 | + }, | ||
117 | + ), | ||
118 | + ), | ||
119 | + ], | ||
120 | + ); | ||
121 | + | ||
122 | + final chart2 = pw.Chart( | ||
123 | + grid: pw.CartesianGrid( | ||
124 | + xAxis: pw.FixedAxis([0, 1, 2, 3, 4, 5, 6]), | ||
125 | + yAxis: pw.FixedAxis( | ||
126 | + [0, 200, 400, 600], | ||
127 | + divisions: true, | ||
128 | + ), | ||
129 | + ), | ||
130 | + datasets: [ | ||
131 | + pw.LineDataSet( | ||
132 | + drawSurface: true, | ||
133 | + isCurved: true, | ||
134 | + drawPoints: false, | ||
135 | + color: baseColor, | ||
136 | + data: List<pw.LineChartValue>.generate( | ||
137 | + dataTable.length, | ||
138 | + (i) { | ||
139 | + final num v = dataTable[i][2]; | ||
140 | + return pw.LineChartValue(i.toDouble(), v.toDouble()); | ||
141 | + }, | ||
142 | + ), | ||
143 | + ), | ||
144 | + ], | ||
145 | + ); | ||
146 | + | ||
147 | + final table = pw.Table.fromTextArray( | ||
148 | + border: null, | ||
149 | + headers: tableHeaders, | ||
150 | + data: dataTable, | ||
151 | + headerStyle: pw.TextStyle( | ||
152 | + color: PdfColors.white, | ||
153 | + fontWeight: pw.FontWeight.bold, | ||
154 | + ), | ||
155 | + headerDecoration: pw.BoxDecoration( | ||
156 | + color: baseColor, | ||
157 | + ), | ||
158 | + rowDecoration: pw.BoxDecoration( | ||
159 | + border: pw.BoxBorder( | ||
160 | + bottom: true, | ||
161 | + color: baseColor, | ||
162 | + width: .5, | ||
163 | + ), | ||
164 | + ), | ||
165 | + ); | ||
166 | + | ||
167 | + return pw.Column( | ||
168 | + children: [ | ||
169 | + pw.Text('Budget Report', | ||
170 | + style: pw.TextStyle( | ||
171 | + color: baseColor, | ||
172 | + fontSize: 40, | ||
173 | + )), | ||
174 | + pw.Divider(thickness: 4), | ||
175 | + pw.Expanded(flex: 3, child: chart1), | ||
176 | + pw.Divider(), | ||
177 | + pw.Expanded( | ||
178 | + flex: 2, | ||
179 | + child: pw.Row( | ||
180 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
181 | + children: [ | ||
182 | + pw.Expanded(child: chart2), | ||
183 | + pw.SizedBox(width: 10), | ||
184 | + pw.Expanded(child: table), | ||
185 | + ], | ||
186 | + ), | ||
187 | + ), | ||
188 | + pw.SizedBox(height: 10), | ||
189 | + pw.Row( | ||
190 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
191 | + children: [ | ||
192 | + pw.Expanded( | ||
193 | + child: pw.Column(children: [ | ||
194 | + pw.Container( | ||
195 | + alignment: pw.Alignment.centerLeft, | ||
196 | + padding: const pw.EdgeInsets.only(bottom: 10), | ||
197 | + child: pw.Text( | ||
198 | + 'Expense By Sub-Categories', | ||
199 | + style: pw.TextStyle( | ||
200 | + color: baseColor, | ||
201 | + fontSize: 16, | ||
202 | + ), | ||
203 | + ), | ||
204 | + ), | ||
205 | + pw.Text( | ||
206 | + 'Total expenses are broken into different categories for closer look into where the money was spent.', | ||
207 | + textAlign: pw.TextAlign.justify, | ||
208 | + ) | ||
209 | + ])), | ||
210 | + pw.SizedBox(width: 10), | ||
211 | + pw.Expanded( | ||
212 | + child: pw.Column( | ||
213 | + children: [ | ||
214 | + pw.Container( | ||
215 | + alignment: pw.Alignment.centerLeft, | ||
216 | + padding: const pw.EdgeInsets.only(bottom: 10), | ||
217 | + child: pw.Text( | ||
218 | + 'Spent vs. Saved', | ||
219 | + style: pw.TextStyle( | ||
220 | + color: baseColor, | ||
221 | + fontSize: 16, | ||
222 | + ), | ||
223 | + ), | ||
224 | + ), | ||
225 | + pw.Text( | ||
226 | + 'Budget was originally \$1915. A total of \$1990 was spent on the month os January which exceeded the overall budget by \$75', | ||
227 | + textAlign: pw.TextAlign.justify, | ||
228 | + ) | ||
229 | + ], | ||
230 | + ), | ||
231 | + ), | ||
232 | + ], | ||
233 | + ), | ||
234 | + ], | ||
235 | + ); | ||
236 | + }, | ||
237 | + ), | ||
238 | + ); | ||
239 | + | ||
240 | + // Return the PDF file content | ||
241 | + return document.save(); | ||
242 | +} |
demo/lib/resume.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 | +// ignore_for_file: always_specify_types | ||
18 | + | ||
19 | +import 'dart:async'; | ||
20 | +import 'dart:typed_data'; | ||
21 | + | ||
22 | +import 'package:flutter/services.dart'; | ||
23 | +import 'package:meta/meta.dart'; | ||
24 | +import 'package:pdf/pdf.dart'; | ||
25 | +import 'package:pdf/widgets.dart' as pw; | ||
26 | + | ||
27 | +const PdfColor green = PdfColor.fromInt(0xff9ce5d0); | ||
28 | +const PdfColor lightGreen = PdfColor.fromInt(0xffcdf1e7); | ||
29 | + | ||
30 | +Future<Uint8List> generateResume(PdfPageFormat format) async { | ||
31 | + final pw.Document doc = | ||
32 | + pw.Document(title: 'My Résumé', author: 'David PHAM-VAN'); | ||
33 | + | ||
34 | + final PdfImage profileImage = PdfImage.file( | ||
35 | + doc.document, | ||
36 | + bytes: (await rootBundle.load('assets/profile.jpg')).buffer.asUint8List(), | ||
37 | + ); | ||
38 | + | ||
39 | + final pw.PageTheme pageTheme = await _myPageTheme(format); | ||
40 | + | ||
41 | + doc.addPage(pw.Page( | ||
42 | + pageTheme: pageTheme, | ||
43 | + build: (pw.Context context) => pw.Row(children: <pw.Widget>[ | ||
44 | + pw.Expanded( | ||
45 | + child: pw.Column( | ||
46 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
47 | + children: <pw.Widget>[ | ||
48 | + pw.Container( | ||
49 | + padding: const pw.EdgeInsets.only(left: 30, bottom: 20), | ||
50 | + child: pw.Column( | ||
51 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
52 | + children: <pw.Widget>[ | ||
53 | + pw.Text('Parnella Charlesbois', | ||
54 | + textScaleFactor: 2, | ||
55 | + style: pw.Theme.of(context) | ||
56 | + .defaultTextStyle | ||
57 | + .copyWith(fontWeight: pw.FontWeight.bold)), | ||
58 | + pw.Padding(padding: const pw.EdgeInsets.only(top: 10)), | ||
59 | + pw.Text('Electrotyper', | ||
60 | + textScaleFactor: 1.2, | ||
61 | + style: pw.Theme.of(context).defaultTextStyle.copyWith( | ||
62 | + fontWeight: pw.FontWeight.bold, color: green)), | ||
63 | + pw.Padding(padding: const pw.EdgeInsets.only(top: 20)), | ||
64 | + pw.Row( | ||
65 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
66 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
67 | + children: <pw.Widget>[ | ||
68 | + pw.Column( | ||
69 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
70 | + children: <pw.Widget>[ | ||
71 | + pw.Text('568 Port Washington Road'), | ||
72 | + pw.Text('Nordegg, AB T0M 2H0'), | ||
73 | + pw.Text('Canada, ON'), | ||
74 | + ]), | ||
75 | + pw.Column( | ||
76 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
77 | + children: <pw.Widget>[ | ||
78 | + pw.Text('+1 403-721-6898'), | ||
79 | + _UrlText('p.charlesbois@yahoo.com', | ||
80 | + 'mailto:p.charlesbois@yahoo.com'), | ||
81 | + _UrlText('wholeprices.ca', | ||
82 | + 'https://wholeprices.ca'), | ||
83 | + ]), | ||
84 | + pw.Padding(padding: pw.EdgeInsets.zero) | ||
85 | + ]), | ||
86 | + ])), | ||
87 | + _Category(title: 'Work Experience'), | ||
88 | + _Block(title: 'Tour bus driver'), | ||
89 | + _Block(title: 'Logging equipment operator'), | ||
90 | + _Block(title: 'Foot doctor'), | ||
91 | + _Category(title: 'Education'), | ||
92 | + _Block(title: 'Bachelor Of Commerce'), | ||
93 | + _Block(title: 'Bachelor Interior Design'), | ||
94 | + ])), | ||
95 | + pw.Container( | ||
96 | + height: double.infinity, | ||
97 | + width: 2, | ||
98 | + margin: const pw.EdgeInsets.symmetric(horizontal: 5), | ||
99 | + color: green, | ||
100 | + ), | ||
101 | + pw.Column( | ||
102 | + crossAxisAlignment: pw.CrossAxisAlignment.center, | ||
103 | + mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
104 | + children: <pw.Widget>[ | ||
105 | + pw.ClipOval( | ||
106 | + child: pw.Container( | ||
107 | + width: 100, | ||
108 | + height: 100, | ||
109 | + color: lightGreen, | ||
110 | + child: pw.Image(profileImage), | ||
111 | + ), | ||
112 | + ), | ||
113 | + pw.Column(children: <pw.Widget>[ | ||
114 | + _Percent(size: 60, value: .7, title: pw.Text('Word')), | ||
115 | + _Percent(size: 60, value: .4, title: pw.Text('Excel')), | ||
116 | + ]), | ||
117 | + pw.BarcodeWidget( | ||
118 | + data: 'Parnella Charlesbois', | ||
119 | + width: 60, | ||
120 | + height: 60, | ||
121 | + barcode: pw.Barcode.qrCode(), | ||
122 | + ), | ||
123 | + ], | ||
124 | + ) | ||
125 | + ]), | ||
126 | + )); | ||
127 | + return doc.save(); | ||
128 | +} | ||
129 | + | ||
130 | +Future<pw.PageTheme> _myPageTheme(PdfPageFormat format) async { | ||
131 | + return pw.PageTheme( | ||
132 | + pageFormat: format.applyMargin( | ||
133 | + left: 2.0 * PdfPageFormat.cm, | ||
134 | + top: 4.0 * PdfPageFormat.cm, | ||
135 | + right: 2.0 * PdfPageFormat.cm, | ||
136 | + bottom: 2.0 * PdfPageFormat.cm), | ||
137 | + theme: pw.ThemeData.withFont( | ||
138 | + base: pw.Font.ttf(await rootBundle.load('assets/open-sans.ttf')), | ||
139 | + bold: pw.Font.ttf(await rootBundle.load('assets/open-sans-bold.ttf')), | ||
140 | + ), | ||
141 | + buildBackground: (pw.Context context) { | ||
142 | + return pw.FullPage( | ||
143 | + ignoreMargins: true, | ||
144 | + child: pw.CustomPaint( | ||
145 | + size: PdfPoint(format.width, format.height), | ||
146 | + painter: (PdfGraphics canvas, PdfPoint size) { | ||
147 | + context.canvas | ||
148 | + ..setColor(lightGreen) | ||
149 | + ..moveTo(0, size.y) | ||
150 | + ..lineTo(0, size.y - 230) | ||
151 | + ..lineTo(60, size.y) | ||
152 | + ..fillPath() | ||
153 | + ..setColor(green) | ||
154 | + ..moveTo(0, size.y) | ||
155 | + ..lineTo(0, size.y - 100) | ||
156 | + ..lineTo(100, size.y) | ||
157 | + ..fillPath() | ||
158 | + ..setColor(lightGreen) | ||
159 | + ..moveTo(30, size.y) | ||
160 | + ..lineTo(110, size.y - 50) | ||
161 | + ..lineTo(150, size.y) | ||
162 | + ..fillPath() | ||
163 | + ..moveTo(size.x, 0) | ||
164 | + ..lineTo(size.x, 230) | ||
165 | + ..lineTo(size.x - 60, 0) | ||
166 | + ..fillPath() | ||
167 | + ..setColor(green) | ||
168 | + ..moveTo(size.x, 0) | ||
169 | + ..lineTo(size.x, 100) | ||
170 | + ..lineTo(size.x - 100, 0) | ||
171 | + ..fillPath() | ||
172 | + ..setColor(lightGreen) | ||
173 | + ..moveTo(size.x - 30, 0) | ||
174 | + ..lineTo(size.x - 110, 50) | ||
175 | + ..lineTo(size.x - 150, 0) | ||
176 | + ..fillPath(); | ||
177 | + }, | ||
178 | + ), | ||
179 | + ); | ||
180 | + }, | ||
181 | + ); | ||
182 | +} | ||
183 | + | ||
184 | +class _Block extends pw.StatelessWidget { | ||
185 | + _Block({this.title}); | ||
186 | + | ||
187 | + final String title; | ||
188 | + | ||
189 | + @override | ||
190 | + pw.Widget build(pw.Context context) { | ||
191 | + return pw.Column( | ||
192 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
193 | + children: <pw.Widget>[ | ||
194 | + pw.Row( | ||
195 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
196 | + children: <pw.Widget>[ | ||
197 | + pw.Container( | ||
198 | + width: 6, | ||
199 | + height: 6, | ||
200 | + margin: const pw.EdgeInsets.only(top: 2.5, left: 2, right: 5), | ||
201 | + decoration: const pw.BoxDecoration( | ||
202 | + color: green, shape: pw.BoxShape.circle), | ||
203 | + ), | ||
204 | + pw.Text(title, | ||
205 | + style: pw.Theme.of(context) | ||
206 | + .defaultTextStyle | ||
207 | + .copyWith(fontWeight: pw.FontWeight.bold)), | ||
208 | + ]), | ||
209 | + pw.Container( | ||
210 | + decoration: const pw.BoxDecoration( | ||
211 | + border: pw.BoxBorder(left: true, color: green, width: 2)), | ||
212 | + padding: const pw.EdgeInsets.only(left: 10, top: 5, bottom: 5), | ||
213 | + margin: const pw.EdgeInsets.only(left: 5), | ||
214 | + child: pw.Column( | ||
215 | + crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
216 | + children: <pw.Widget>[ | ||
217 | + pw.Lorem(length: 20), | ||
218 | + ]), | ||
219 | + ), | ||
220 | + ]); | ||
221 | + } | ||
222 | +} | ||
223 | + | ||
224 | +class _Category extends pw.StatelessWidget { | ||
225 | + _Category({this.title}); | ||
226 | + | ||
227 | + final String title; | ||
228 | + | ||
229 | + @override | ||
230 | + pw.Widget build(pw.Context context) { | ||
231 | + return pw.Container( | ||
232 | + decoration: const pw.BoxDecoration(color: lightGreen, borderRadius: 6), | ||
233 | + margin: const pw.EdgeInsets.only(bottom: 10, top: 20), | ||
234 | + padding: const pw.EdgeInsets.fromLTRB(10, 7, 10, 4), | ||
235 | + child: pw.Text(title, textScaleFactor: 1.5)); | ||
236 | + } | ||
237 | +} | ||
238 | + | ||
239 | +class _Percent extends pw.StatelessWidget { | ||
240 | + _Percent({ | ||
241 | + @required this.size, | ||
242 | + @required this.value, | ||
243 | + this.title, | ||
244 | + this.fontSize = 1.2, | ||
245 | + this.color = green, | ||
246 | + this.backgroundColor = PdfColors.grey300, | ||
247 | + this.strokeWidth = 5, | ||
248 | + }) : assert(size != null); | ||
249 | + | ||
250 | + final double size; | ||
251 | + | ||
252 | + final double value; | ||
253 | + | ||
254 | + final pw.Widget title; | ||
255 | + | ||
256 | + final double fontSize; | ||
257 | + | ||
258 | + final PdfColor color; | ||
259 | + | ||
260 | + final PdfColor backgroundColor; | ||
261 | + | ||
262 | + final double strokeWidth; | ||
263 | + | ||
264 | + @override | ||
265 | + pw.Widget build(pw.Context context) { | ||
266 | + final List<pw.Widget> widgets = <pw.Widget>[ | ||
267 | + pw.Container( | ||
268 | + width: size, | ||
269 | + height: size, | ||
270 | + child: pw.Stack( | ||
271 | + alignment: pw.Alignment.center, | ||
272 | + fit: pw.StackFit.expand, | ||
273 | + children: <pw.Widget>[ | ||
274 | + pw.Center( | ||
275 | + child: pw.Text( | ||
276 | + '${(value * 100).round().toInt()}%', | ||
277 | + textScaleFactor: fontSize, | ||
278 | + ), | ||
279 | + ), | ||
280 | + pw.CircularProgressIndicator( | ||
281 | + value: value, | ||
282 | + backgroundColor: backgroundColor, | ||
283 | + color: color, | ||
284 | + strokeWidth: strokeWidth, | ||
285 | + ), | ||
286 | + ], | ||
287 | + ), | ||
288 | + ) | ||
289 | + ]; | ||
290 | + | ||
291 | + if (title != null) { | ||
292 | + widgets.add(title); | ||
293 | + } | ||
294 | + | ||
295 | + return pw.Column(children: widgets); | ||
296 | + } | ||
297 | +} | ||
298 | + | ||
299 | +class _UrlText extends pw.StatelessWidget { | ||
300 | + _UrlText(this.text, this.url); | ||
301 | + | ||
302 | + final String text; | ||
303 | + final String url; | ||
304 | + | ||
305 | + @override | ||
306 | + pw.Widget build(pw.Context context) { | ||
307 | + return pw.UrlLink( | ||
308 | + destination: url, | ||
309 | + child: pw.Text(text, | ||
310 | + style: const pw.TextStyle( | ||
311 | + decoration: pw.TextDecoration.underline, | ||
312 | + color: PdfColors.blue, | ||
313 | + )), | ||
314 | + ); | ||
315 | + } | ||
316 | +} |
@@ -28,6 +28,7 @@ | @@ -28,6 +28,7 @@ | ||
28 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; | 28 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; |
29 | 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; | 29 | 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; |
30 | 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; | 30 | 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; |
31 | + C5167F8E3334BB79DCB28275 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AAEE3AC203DE6402636AB66 /* Pods_Runner.framework */; }; | ||
31 | D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; }; | 32 | D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; }; |
32 | D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; | 33 | D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; |
33 | /* End PBXBuildFile section */ | 34 | /* End PBXBuildFile section */ |
@@ -60,7 +61,7 @@ | @@ -60,7 +61,7 @@ | ||
60 | /* Begin PBXFileReference section */ | 61 | /* Begin PBXFileReference section */ |
61 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; | 62 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; |
62 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; | 63 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; |
63 | - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; | 64 | + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; |
64 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | 65 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; |
65 | 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; | 66 | 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; |
66 | 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; | 67 | 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; |
@@ -73,8 +74,12 @@ | @@ -73,8 +74,12 @@ | ||
73 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; | 74 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; |
74 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; | 75 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; |
75 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; | 76 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; |
77 | + 43ED5ED41657603B2845C03F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | ||
78 | + 4AAEE3AC203DE6402636AB66 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
79 | + 65AA7E24FA1AB2B70A493F17 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | ||
76 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; | 80 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; |
77 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; | 81 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; |
82 | + AFF841D258A6267ADE7CF49D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | ||
78 | D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; }; | 83 | D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; }; |
79 | /* End PBXFileReference section */ | 84 | /* End PBXFileReference section */ |
80 | 85 | ||
@@ -85,12 +90,24 @@ | @@ -85,12 +90,24 @@ | ||
85 | files = ( | 90 | files = ( |
86 | D73912F022F37F9E000D13A0 /* App.framework in Frameworks */, | 91 | D73912F022F37F9E000D13A0 /* App.framework in Frameworks */, |
87 | 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, | 92 | 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, |
93 | + C5167F8E3334BB79DCB28275 /* Pods_Runner.framework in Frameworks */, | ||
88 | ); | 94 | ); |
89 | runOnlyForDeploymentPostprocessing = 0; | 95 | runOnlyForDeploymentPostprocessing = 0; |
90 | }; | 96 | }; |
91 | /* End PBXFrameworksBuildPhase section */ | 97 | /* End PBXFrameworksBuildPhase section */ |
92 | 98 | ||
93 | /* Begin PBXGroup section */ | 99 | /* Begin PBXGroup section */ |
100 | + 0D30096D3D852E9C057D6E9C /* Pods */ = { | ||
101 | + isa = PBXGroup; | ||
102 | + children = ( | ||
103 | + 65AA7E24FA1AB2B70A493F17 /* Pods-Runner.debug.xcconfig */, | ||
104 | + AFF841D258A6267ADE7CF49D /* Pods-Runner.release.xcconfig */, | ||
105 | + 43ED5ED41657603B2845C03F /* Pods-Runner.profile.xcconfig */, | ||
106 | + ); | ||
107 | + name = Pods; | ||
108 | + path = Pods; | ||
109 | + sourceTree = "<group>"; | ||
110 | + }; | ||
94 | 33BA886A226E78AF003329D5 /* Configs */ = { | 111 | 33BA886A226E78AF003329D5 /* Configs */ = { |
95 | isa = PBXGroup; | 112 | isa = PBXGroup; |
96 | children = ( | 113 | children = ( |
@@ -109,6 +126,7 @@ | @@ -109,6 +126,7 @@ | ||
109 | 33CEB47122A05771004F2AC0 /* Flutter */, | 126 | 33CEB47122A05771004F2AC0 /* Flutter */, |
110 | 33CC10EE2044A3C60003C045 /* Products */, | 127 | 33CC10EE2044A3C60003C045 /* Products */, |
111 | D73912EC22F37F3D000D13A0 /* Frameworks */, | 128 | D73912EC22F37F3D000D13A0 /* Frameworks */, |
129 | + 0D30096D3D852E9C057D6E9C /* Pods */, | ||
112 | ); | 130 | ); |
113 | sourceTree = "<group>"; | 131 | sourceTree = "<group>"; |
114 | }; | 132 | }; |
@@ -160,6 +178,7 @@ | @@ -160,6 +178,7 @@ | ||
160 | D73912EC22F37F3D000D13A0 /* Frameworks */ = { | 178 | D73912EC22F37F3D000D13A0 /* Frameworks */ = { |
161 | isa = PBXGroup; | 179 | isa = PBXGroup; |
162 | children = ( | 180 | children = ( |
181 | + 4AAEE3AC203DE6402636AB66 /* Pods_Runner.framework */, | ||
163 | ); | 182 | ); |
164 | name = Frameworks; | 183 | name = Frameworks; |
165 | sourceTree = "<group>"; | 184 | sourceTree = "<group>"; |
@@ -171,11 +190,13 @@ | @@ -171,11 +190,13 @@ | ||
171 | isa = PBXNativeTarget; | 190 | isa = PBXNativeTarget; |
172 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; | 191 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; |
173 | buildPhases = ( | 192 | buildPhases = ( |
193 | + 7A774E49F208BB56B0F75B23 /* [CP] Check Pods Manifest.lock */, | ||
174 | 33CC10E92044A3C60003C045 /* Sources */, | 194 | 33CC10E92044A3C60003C045 /* Sources */, |
175 | 33CC10EA2044A3C60003C045 /* Frameworks */, | 195 | 33CC10EA2044A3C60003C045 /* Frameworks */, |
176 | 33CC10EB2044A3C60003C045 /* Resources */, | 196 | 33CC10EB2044A3C60003C045 /* Resources */, |
177 | 33CC110E2044A8840003C045 /* Bundle Framework */, | 197 | 33CC110E2044A8840003C045 /* Bundle Framework */, |
178 | 3399D490228B24CF009A79C7 /* ShellScript */, | 198 | 3399D490228B24CF009A79C7 /* ShellScript */, |
199 | + 198EB137690A6B5A377F607D /* [CP] Embed Pods Frameworks */, | ||
179 | ); | 200 | ); |
180 | buildRules = ( | 201 | buildRules = ( |
181 | ); | 202 | ); |
@@ -245,6 +266,21 @@ | @@ -245,6 +266,21 @@ | ||
245 | /* End PBXResourcesBuildPhase section */ | 266 | /* End PBXResourcesBuildPhase section */ |
246 | 267 | ||
247 | /* Begin PBXShellScriptBuildPhase section */ | 268 | /* Begin PBXShellScriptBuildPhase section */ |
269 | + 198EB137690A6B5A377F607D /* [CP] Embed Pods Frameworks */ = { | ||
270 | + isa = PBXShellScriptBuildPhase; | ||
271 | + buildActionMask = 2147483647; | ||
272 | + files = ( | ||
273 | + ); | ||
274 | + inputFileListPaths = ( | ||
275 | + ); | ||
276 | + name = "[CP] Embed Pods Frameworks"; | ||
277 | + outputFileListPaths = ( | ||
278 | + ); | ||
279 | + runOnlyForDeploymentPostprocessing = 0; | ||
280 | + shellPath = /bin/sh; | ||
281 | + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | ||
282 | + showEnvVarsInLog = 0; | ||
283 | + }; | ||
248 | 3399D490228B24CF009A79C7 /* ShellScript */ = { | 284 | 3399D490228B24CF009A79C7 /* ShellScript */ = { |
249 | isa = PBXShellScriptBuildPhase; | 285 | isa = PBXShellScriptBuildPhase; |
250 | buildActionMask = 2147483647; | 286 | buildActionMask = 2147483647; |
@@ -282,6 +318,28 @@ | @@ -282,6 +318,28 @@ | ||
282 | shellPath = /bin/sh; | 318 | shellPath = /bin/sh; |
283 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; | 319 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; |
284 | }; | 320 | }; |
321 | + 7A774E49F208BB56B0F75B23 /* [CP] Check Pods Manifest.lock */ = { | ||
322 | + isa = PBXShellScriptBuildPhase; | ||
323 | + buildActionMask = 2147483647; | ||
324 | + files = ( | ||
325 | + ); | ||
326 | + inputFileListPaths = ( | ||
327 | + ); | ||
328 | + inputPaths = ( | ||
329 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
330 | + "${PODS_ROOT}/Manifest.lock", | ||
331 | + ); | ||
332 | + name = "[CP] Check Pods Manifest.lock"; | ||
333 | + outputFileListPaths = ( | ||
334 | + ); | ||
335 | + outputPaths = ( | ||
336 | + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | ||
337 | + ); | ||
338 | + runOnlyForDeploymentPostprocessing = 0; | ||
339 | + shellPath = /bin/sh; | ||
340 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
341 | + showEnvVarsInLog = 0; | ||
342 | + }; | ||
285 | /* End PBXShellScriptBuildPhase section */ | 343 | /* End PBXShellScriptBuildPhase section */ |
286 | 344 | ||
287 | /* Begin PBXSourcesBuildPhase section */ | 345 | /* Begin PBXSourcesBuildPhase section */ |
demo/pubspec.yaml
0 → 100644
1 | +name: printing_example | ||
2 | +description: Pdf Printing Example | ||
3 | + | ||
4 | +version: 1.0.0+1 | ||
5 | + | ||
6 | +environment: | ||
7 | + sdk: ">=2.7.0 <3.0.0" | ||
8 | + | ||
9 | +dependencies: | ||
10 | + flutter: | ||
11 | + sdk: flutter | ||
12 | + printing: | ||
13 | + path_provider: | ||
14 | + open_file: | ||
15 | + url_launcher: | ||
16 | + | ||
17 | +dev_dependencies: | ||
18 | + flutter_test: | ||
19 | + sdk: flutter | ||
20 | + flutter_driver: | ||
21 | + sdk: flutter | ||
22 | + test: | ||
23 | + | ||
24 | +dependency_overrides: | ||
25 | + printing: | ||
26 | + path: ../printing | ||
27 | + pdf: | ||
28 | + path: ../pdf | ||
29 | + | ||
30 | +flutter: | ||
31 | + uses-material-design: true | ||
32 | + assets: | ||
33 | + - assets/ |
1 | import 'dart:io'; | 1 | import 'dart:io'; |
2 | +import 'dart:typed_data'; | ||
2 | 3 | ||
3 | import 'package:flutter_test/flutter_test.dart'; | 4 | import 'package:flutter_test/flutter_test.dart'; |
4 | import 'package:pdf/pdf.dart'; | 5 | import 'package:pdf/pdf.dart'; |
5 | -import 'package:pdf/widgets.dart' as pw; | ||
6 | import 'package:printing_example/document.dart'; | 6 | import 'package:printing_example/document.dart'; |
7 | 7 | ||
8 | void main() { | 8 | void main() { |
9 | testWidgets('Pdf Generate the document', (WidgetTester tester) async { | 9 | testWidgets('Pdf Generate the document', (WidgetTester tester) async { |
10 | - final pw.Document doc = await generateDocument(PdfPageFormat.a4); | 10 | + final Uint8List doc = await generateDocument(PdfPageFormat.a4); |
11 | 11 | ||
12 | final File file = File('document.pdf'); | 12 | final File file = File('document.pdf'); |
13 | - file.writeAsBytesSync(doc.save()); | 13 | + file.writeAsBytesSync(doc); |
14 | }); | 14 | }); |
15 | } | 15 | } |
@@ -8,7 +8,11 @@ This library is divided into two parts: | @@ -8,7 +8,11 @@ This library is divided into two parts: | ||
8 | It can create a full multi-pages document with graphics, | 8 | It can create a full multi-pages document with graphics, |
9 | images, and text using TrueType fonts. With the ease of use you already know. | 9 | images, and text using TrueType fonts. With the ease of use you already know. |
10 | 10 | ||
11 | +See an interactive demo here: <https://davbfr.github.io/dart_pdf/>. | ||
12 | + | ||
13 | +<a href="https://davbfr.github.io/dart_pdf/"> | ||
11 | <img alt="Example document" src="https://raw.githubusercontent.com/DavBfr/dart_pdf/master/pdf/example.jpg"> | 14 | <img alt="Example document" src="https://raw.githubusercontent.com/DavBfr/dart_pdf/master/pdf/example.jpg"> |
15 | +</a> | ||
12 | 16 | ||
13 | Use the `printing` package <https://pub.dev/packages/printing> | 17 | Use the `printing` package <https://pub.dev/packages/printing> |
14 | for full flutter print and share operation. | 18 | for full flutter print and share operation. |
1 | -// ignore_for_file: omit_local_variable_types | 1 | +// ignore_for_file: always_specify_types |
2 | 2 | ||
3 | import 'dart:io'; | 3 | import 'dart:io'; |
4 | 4 | ||
5 | -import 'package:pdf/pdf.dart'; | ||
6 | import 'package:pdf/widgets.dart' as pw; | 5 | import 'package:pdf/widgets.dart' as pw; |
7 | 6 | ||
8 | void main() { | 7 | void main() { |
9 | - final pw.Document doc = pw.Document(); | 8 | + final doc = pw.Document(); |
10 | 9 | ||
11 | - doc.addPage(pw.MultiPage( | ||
12 | - pageFormat: | ||
13 | - PdfPageFormat.letter.copyWith(marginBottom: 1.5 * PdfPageFormat.cm), | ||
14 | - crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
15 | - header: (pw.Context context) { | ||
16 | - if (context.pageNumber == 1) { | ||
17 | - return null; | ||
18 | - } | ||
19 | - return pw.Container( | ||
20 | - alignment: pw.Alignment.centerRight, | ||
21 | - margin: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm), | ||
22 | - padding: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm), | ||
23 | - decoration: const pw.BoxDecoration( | ||
24 | - border: pw.BoxBorder( | ||
25 | - bottom: true, width: 0.5, color: PdfColors.grey)), | ||
26 | - child: pw.Text('Portable Document Format', | ||
27 | - style: pw.Theme.of(context) | ||
28 | - .defaultTextStyle | ||
29 | - .copyWith(color: PdfColors.grey))); | ||
30 | - }, | ||
31 | - footer: (pw.Context context) { | ||
32 | - return pw.Container( | ||
33 | - alignment: pw.Alignment.centerRight, | ||
34 | - margin: const pw.EdgeInsets.only(top: 1.0 * PdfPageFormat.cm), | ||
35 | - child: pw.Text( | ||
36 | - 'Page ${context.pageNumber} of ${context.pagesCount}', | ||
37 | - style: pw.Theme.of(context) | ||
38 | - .defaultTextStyle | ||
39 | - .copyWith(color: PdfColors.grey))); | ||
40 | - }, | ||
41 | - build: (pw.Context context) => <pw.Widget>[ | ||
42 | - pw.Header( | ||
43 | - level: 0, | ||
44 | - child: pw.Row( | ||
45 | - mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, | ||
46 | - children: <pw.Widget>[ | ||
47 | - pw.Text('Portable Document Format', textScaleFactor: 2), | ||
48 | - pw.PdfLogo() | ||
49 | - ])), | ||
50 | - pw.Paragraph( | ||
51 | - text: | ||
52 | - 'The Portable Document Format (PDF) is a file format developed by Adobe in the 1990s to present documents, including text formatting and images, in a manner independent of application software, hardware, and operating systems. Based on the PostScript language, each PDF file encapsulates a complete description of a fixed-layout flat document, including the text, fonts, vector graphics, raster images and other information needed to display it. PDF was standardized as an open format, ISO 32000, in 2008, and no longer requires any royalties for its implementation.'), | ||
53 | - pw.Paragraph( | ||
54 | - text: | ||
55 | - 'Today, PDF files may contain a variety of content besides flat text and graphics including logical structuring elements, interactive elements such as annotations and form-fields, layers, rich media (including video content) and three dimensional objects using U3D or PRC, and various other data formats. The PDF specification also provides for encryption and digital signatures, file attachments and metadata to enable workflows requiring these features.'), | ||
56 | - pw.Header(level: 1, text: 'History and standardization'), | ||
57 | - pw.Paragraph( | ||
58 | - text: | ||
59 | - "Adobe Systems made the PDF specification available free of charge in 1993. In the early years PDF was popular mainly in desktop publishing workflows, and competed with a variety of formats such as DjVu, Envoy, Common Ground Digital Paper, Farallon Replica and even Adobe's own PostScript format."), | ||
60 | - pw.Paragraph( | ||
61 | - text: | ||
62 | - 'PDF was a proprietary format controlled by Adobe until it was released as an open standard on July 1, 2008, and published by the International Organization for Standardization as ISO 32000-1:2008, at which time control of the specification passed to an ISO Committee of volunteer industry experts. In 2008, Adobe published a Public Patent License to ISO 32000-1 granting royalty-free rights for all patents owned by Adobe that are necessary to make, use, sell, and distribute PDF compliant implementations.'), | ||
63 | - pw.Paragraph( | ||
64 | - text: | ||
65 | - "PDF 1.7, the sixth edition of the PDF specification that became ISO 32000-1, includes some proprietary technologies defined only by Adobe, such as Adobe XML Forms Architecture (XFA) and JavaScript extension for Acrobat, which are referenced by ISO 32000-1 as normative and indispensable for the full implementation of the ISO 32000-1 specification. These proprietary technologies are not standardized and their specification is published only on Adobe's website. Many of them are also not supported by popular third-party implementations of PDF."), | ||
66 | - pw.Paragraph( | ||
67 | - text: | ||
68 | - 'On July 28, 2017, ISO 32000-2:2017 (PDF 2.0) was published. ISO 32000-2 does not include any proprietary technologies as normative references.'), | ||
69 | - pw.Header(level: 1, text: 'Technical foundations'), | ||
70 | - pw.Paragraph(text: 'The PDF combines three technologies:'), | ||
71 | - pw.Bullet( | ||
72 | - text: | ||
73 | - 'A subset of the PostScript page description programming language, for generating the layout and graphics.'), | ||
74 | - pw.Bullet( | ||
75 | - text: | ||
76 | - 'A font-embedding/replacement system to allow fonts to travel with the documents.'), | ||
77 | - pw.Bullet( | ||
78 | - text: | ||
79 | - 'A structured storage system to bundle these elements and any associated content into a single file, with data compression where appropriate.'), | ||
80 | - pw.Header(level: 2, text: 'PostScript'), | ||
81 | - pw.Paragraph( | ||
82 | - text: | ||
83 | - 'PostScript is a page description language run in an interpreter to generate an image, a process requiring many resources. It can handle graphics and standard features of programming languages such as if and loop commands. PDF is largely based on PostScript but simplified to remove flow control features like these, while graphics commands such as lineto remain.'), | ||
84 | - pw.Paragraph( | ||
85 | - text: | ||
86 | - 'Often, the PostScript-like PDF code is generated from a source PostScript file. The graphics commands that are output by the PostScript code are collected and tokenized. Any files, graphics, or fonts to which the document refers also are collected. Then, everything is compressed to a single file. Therefore, the entire PostScript world (fonts, layout, measurements) remains intact.'), | ||
87 | - pw.Column( | ||
88 | - crossAxisAlignment: pw.CrossAxisAlignment.start, | ||
89 | - children: <pw.Widget>[ | ||
90 | - pw.Paragraph( | ||
91 | - text: | ||
92 | - 'As a document format, PDF has several advantages over PostScript:'), | ||
93 | - pw.Bullet( | ||
94 | - text: | ||
95 | - 'PDF contains tokenized and interpreted results of the PostScript source code, for direct correspondence between changes to items in the PDF page description and changes to the resulting page appearance.'), | ||
96 | - pw.Bullet( | ||
97 | - text: | ||
98 | - 'PDF (from version 1.4) supports graphic transparency; PostScript does not.'), | ||
99 | - pw.Bullet( | ||
100 | - text: | ||
101 | - 'PostScript is an interpreted programming language with an implicit global state, so instructions accompanying the description of one page can affect the appearance of any following page. Therefore, all preceding pages in a PostScript document must be processed to determine the correct appearance of a given page, whereas each page in a PDF document is unaffected by the others. As a result, PDF viewers allow the user to quickly jump to the final pages of a long document, whereas a PostScript viewer needs to process all pages sequentially before being able to display the destination page (unless the optional PostScript Document Structuring Conventions have been carefully complied with).'), | ||
102 | - ]), | ||
103 | - pw.Header(level: 1, text: 'Content'), | ||
104 | - pw.Paragraph( | ||
105 | - text: | ||
106 | - 'A PDF file is often a combination of vector graphics, text, and bitmap graphics. The basic types of content in a PDF are:'), | ||
107 | - pw.Bullet( | ||
108 | - text: | ||
109 | - 'Text stored as content streams (i.e., not encoded in plain text)'), | ||
110 | - pw.Bullet( | ||
111 | - text: | ||
112 | - 'Vector graphics for illustrations and designs that consist of shapes and lines'), | ||
113 | - pw.Bullet( | ||
114 | - text: | ||
115 | - 'Raster graphics for photographs and other types of image'), | ||
116 | - pw.Bullet(text: 'Multimedia objects in the document'), | ||
117 | - pw.Paragraph( | ||
118 | - text: | ||
119 | - 'In later PDF revisions, a PDF document can also support links (inside document or web page), forms, JavaScript (initially available as plugin for Acrobat 3.0), or any other types of embedded contents that can be handled using plug-ins.'), | ||
120 | - pw.Paragraph( | ||
121 | - text: | ||
122 | - 'PDF 1.6 supports interactive 3D documents embedded in the PDF - 3D drawings can be embedded using U3D or PRC and various other data formats.'), | ||
123 | - pw.Paragraph( | ||
124 | - text: | ||
125 | - 'Two PDF files that look similar on a computer screen may be of very different sizes. For example, a high resolution raster image takes more space than a low resolution one. Typically higher resolution is needed for printing documents than for displaying them on screen. Other things that may increase the size of a file is embedding full fonts, especially for Asiatic scripts, and storing text as graphics. '), | ||
126 | - pw.Header( | ||
127 | - level: 1, text: 'File formats and Adobe Acrobat versions'), | ||
128 | - pw.Paragraph( | ||
129 | - text: | ||
130 | - 'The PDF file format has changed several times, and continues to evolve, along with the release of new versions of Adobe Acrobat. There have been nine versions of PDF and the corresponding version of the software:'), | ||
131 | - pw.Table.fromTextArray(context: context, data: const <List<String>>[ | ||
132 | - <String>['Date', 'PDF Version', 'Acrobat Version'], | ||
133 | - <String>['1993', 'PDF 1.0', 'Acrobat 1'], | ||
134 | - <String>['1994', 'PDF 1.1', 'Acrobat 2'], | ||
135 | - <String>['1996', 'PDF 1.2', 'Acrobat 3'], | ||
136 | - <String>['1999', 'PDF 1.3', 'Acrobat 4'], | ||
137 | - <String>['2001', 'PDF 1.4', 'Acrobat 5'], | ||
138 | - <String>['2003', 'PDF 1.5', 'Acrobat 6'], | ||
139 | - <String>['2005', 'PDF 1.6', 'Acrobat 7'], | ||
140 | - <String>['2006', 'PDF 1.7', 'Acrobat 8'], | ||
141 | - <String>['2008', 'PDF 1.7', 'Acrobat 9'], | ||
142 | - <String>['2009', 'PDF 1.7', 'Acrobat 9.1'], | ||
143 | - <String>['2010', 'PDF 1.7', 'Acrobat X'], | ||
144 | - <String>['2012', 'PDF 1.7', 'Acrobat XI'], | ||
145 | - <String>['2017', 'PDF 2.0', 'Acrobat DC'], | ||
146 | - ]), | ||
147 | - pw.Padding(padding: const pw.EdgeInsets.all(10)), | ||
148 | - pw.Paragraph( | ||
149 | - text: | ||
150 | - 'Text is available under the Creative Commons Attribution Share Alike License.') | ||
151 | - ])); | 10 | + doc.addPage( |
11 | + pw.Page( | ||
12 | + build: (pw.Context context) => pw.Center( | ||
13 | + child: pw.Text('Hello World!'), | ||
14 | + ), | ||
15 | + ), | ||
16 | + ); | ||
152 | 17 | ||
153 | - final File file = File('example.pdf'); | 18 | + final file = File('example.pdf'); |
154 | file.writeAsBytesSync(doc.save()); | 19 | file.writeAsBytesSync(doc.save()); |
155 | } | 20 | } |
pdf/web_example/.gitignore
deleted
100644 → 0
-
Please register or login to post a comment