Showing
20 changed files
with
1185 additions
and
167 deletions
@@ -50,6 +50,7 @@ part 'src/font_metrics.dart'; | @@ -50,6 +50,7 @@ part 'src/font_metrics.dart'; | ||
50 | part 'src/formxobject.dart'; | 50 | part 'src/formxobject.dart'; |
51 | part 'src/function.dart'; | 51 | part 'src/function.dart'; |
52 | part 'src/graphic_state.dart'; | 52 | part 'src/graphic_state.dart'; |
53 | +part 'src/graphic_stream.dart'; | ||
53 | part 'src/graphics.dart'; | 54 | part 'src/graphics.dart'; |
54 | part 'src/image.dart'; | 55 | part 'src/image.dart'; |
55 | part 'src/info.dart'; | 56 | part 'src/info.dart'; |
@@ -42,20 +42,45 @@ class PdfAnnot extends PdfObject { | @@ -42,20 +42,45 @@ class PdfAnnot extends PdfObject { | ||
42 | } | 42 | } |
43 | 43 | ||
44 | enum PdfAnnotFlags { | 44 | enum PdfAnnotFlags { |
45 | + /// 1 | ||
45 | invisible, | 46 | invisible, |
47 | + | ||
48 | + /// 2 | ||
46 | hidden, | 49 | hidden, |
50 | + | ||
51 | + /// 3 | ||
47 | print, | 52 | print, |
53 | + | ||
54 | + /// 4 | ||
48 | noZoom, | 55 | noZoom, |
56 | + | ||
57 | + /// 5 | ||
49 | noRotate, | 58 | noRotate, |
59 | + | ||
60 | + /// 6 | ||
50 | noView, | 61 | noView, |
62 | + | ||
63 | + /// 7 | ||
51 | readOnly, | 64 | readOnly, |
65 | + | ||
66 | + /// 8 | ||
52 | locked, | 67 | locked, |
68 | + | ||
69 | + /// 9 | ||
53 | toggleNoView, | 70 | toggleNoView, |
54 | - lockedContent | 71 | + |
72 | + /// 10 | ||
73 | + lockedContent, | ||
74 | +} | ||
75 | + | ||
76 | +enum PdfAnnotApparence { | ||
77 | + normal, | ||
78 | + rollover, | ||
79 | + down, | ||
55 | } | 80 | } |
56 | 81 | ||
57 | abstract class PdfAnnotBase { | 82 | abstract class PdfAnnotBase { |
58 | - const PdfAnnotBase({ | 83 | + PdfAnnotBase({ |
59 | @required this.subtype, | 84 | @required this.subtype, |
60 | @required this.rect, | 85 | @required this.rect, |
61 | this.border, | 86 | this.border, |
@@ -90,9 +115,66 @@ abstract class PdfAnnotBase { | @@ -90,9 +115,66 @@ abstract class PdfAnnotBase { | ||
90 | /// Color | 115 | /// Color |
91 | final PdfColor color; | 116 | final PdfColor color; |
92 | 117 | ||
93 | - int get flagValue => flags | ||
94 | - ?.map<int>((PdfAnnotFlags e) => 1 >> e.index) | ||
95 | - ?.reduce((int a, int b) => a | b); | 118 | + final Map<String, PdfDataType> _appearances = <String, PdfDataType>{}; |
119 | + | ||
120 | + int get flagValue { | ||
121 | + if (flags == null || flags.isEmpty) { | ||
122 | + return 0; | ||
123 | + } | ||
124 | + | ||
125 | + return flags | ||
126 | + .map<int>((PdfAnnotFlags e) => 1 << e.index) | ||
127 | + .reduce((int a, int b) => a | b); | ||
128 | + } | ||
129 | + | ||
130 | + PdfGraphics appearance( | ||
131 | + PdfDocument pdfDocument, | ||
132 | + PdfAnnotApparence type, { | ||
133 | + String name, | ||
134 | + Matrix4 matrix, | ||
135 | + PdfRect boundingBox, | ||
136 | + }) { | ||
137 | + final PdfGraphicXObject s = PdfGraphicXObject(pdfDocument, '/Form'); | ||
138 | + String n; | ||
139 | + switch (type) { | ||
140 | + case PdfAnnotApparence.normal: | ||
141 | + n = '/N'; | ||
142 | + break; | ||
143 | + case PdfAnnotApparence.rollover: | ||
144 | + n = '/R'; | ||
145 | + break; | ||
146 | + case PdfAnnotApparence.down: | ||
147 | + n = '/D'; | ||
148 | + break; | ||
149 | + } | ||
150 | + if (name == null) { | ||
151 | + _appearances[n] = s.ref(); | ||
152 | + } else { | ||
153 | + if (_appearances[n] is! PdfDict) { | ||
154 | + _appearances[n] = PdfDict(); | ||
155 | + } | ||
156 | + final PdfDict d = _appearances[n]; | ||
157 | + d[name] = s.ref(); | ||
158 | + } | ||
159 | + | ||
160 | + if (matrix != null) { | ||
161 | + s.params['/Matrix'] = PdfArray.fromNum(<double>[ | ||
162 | + matrix[0], | ||
163 | + matrix[1], | ||
164 | + matrix[4], | ||
165 | + matrix[5], | ||
166 | + matrix[12], | ||
167 | + matrix[13] | ||
168 | + ]); | ||
169 | + } | ||
170 | + | ||
171 | + final PdfRect bbox = | ||
172 | + boundingBox ?? PdfRect.fromPoints(PdfPoint.zero, rect.size); | ||
173 | + s.params['/BBox'] = | ||
174 | + PdfArray.fromNum(<double>[bbox.x, bbox.y, bbox.width, bbox.height]); | ||
175 | + final PdfGraphics g = PdfGraphics(s, s.buf); | ||
176 | + return g; | ||
177 | + } | ||
96 | 178 | ||
97 | @protected | 179 | @protected |
98 | @mustCallSuper | 180 | @mustCallSuper |
@@ -118,7 +200,7 @@ abstract class PdfAnnotBase { | @@ -118,7 +200,7 @@ abstract class PdfAnnotBase { | ||
118 | params['/NM'] = PdfSecString.fromString(object, name); | 200 | params['/NM'] = PdfSecString.fromString(object, name); |
119 | } | 201 | } |
120 | 202 | ||
121 | - if (flags != null) { | 203 | + if (flags != null && flags.isNotEmpty) { |
122 | params['/F'] = PdfNum(flagValue); | 204 | params['/F'] = PdfNum(flagValue); |
123 | } | 205 | } |
124 | 206 | ||
@@ -127,13 +209,14 @@ abstract class PdfAnnotBase { | @@ -127,13 +209,14 @@ abstract class PdfAnnotBase { | ||
127 | } | 209 | } |
128 | 210 | ||
129 | if (color != null) { | 211 | if (color != null) { |
130 | - if (color is PdfColorCmyk) { | ||
131 | - final PdfColorCmyk k = color; | ||
132 | - params['/C'] = | ||
133 | - PdfArray.fromNum(<double>[k.cyan, k.magenta, k.yellow, k.black]); | ||
134 | - } else { | ||
135 | - params['/C'] = | ||
136 | - PdfArray.fromNum(<double>[color.red, color.green, color.blue]); | 212 | + params['/C'] = PdfColorType(color); |
213 | + } | ||
214 | + | ||
215 | + if (_appearances.isNotEmpty) { | ||
216 | + params['/AP'] = PdfDict(_appearances); | ||
217 | + if (_appearances['/N'] is PdfDict) { | ||
218 | + final PdfDict n = _appearances['/N']; | ||
219 | + params['/AS'] = PdfName(n.values.keys.first); | ||
137 | } | 220 | } |
138 | } | 221 | } |
139 | } | 222 | } |
@@ -141,7 +224,7 @@ abstract class PdfAnnotBase { | @@ -141,7 +224,7 @@ abstract class PdfAnnotBase { | ||
141 | 224 | ||
142 | class PdfAnnotText extends PdfAnnotBase { | 225 | class PdfAnnotText extends PdfAnnotBase { |
143 | /// Create a text annotation | 226 | /// Create a text annotation |
144 | - const PdfAnnotText({ | 227 | + PdfAnnotText({ |
145 | @required PdfRect rect, | 228 | @required PdfRect rect, |
146 | @required String content, | 229 | @required String content, |
147 | PdfBorder border, | 230 | PdfBorder border, |
@@ -163,7 +246,7 @@ class PdfAnnotText extends PdfAnnotBase { | @@ -163,7 +246,7 @@ class PdfAnnotText extends PdfAnnotBase { | ||
163 | 246 | ||
164 | class PdfAnnotNamedLink extends PdfAnnotBase { | 247 | class PdfAnnotNamedLink extends PdfAnnotBase { |
165 | /// Create a named link annotation | 248 | /// Create a named link annotation |
166 | - const PdfAnnotNamedLink({ | 249 | + PdfAnnotNamedLink({ |
167 | @required PdfRect rect, | 250 | @required PdfRect rect, |
168 | @required this.dest, | 251 | @required this.dest, |
169 | PdfBorder border, | 252 | PdfBorder border, |
@@ -195,7 +278,7 @@ class PdfAnnotNamedLink extends PdfAnnotBase { | @@ -195,7 +278,7 @@ class PdfAnnotNamedLink extends PdfAnnotBase { | ||
195 | 278 | ||
196 | class PdfAnnotUrlLink extends PdfAnnotBase { | 279 | class PdfAnnotUrlLink extends PdfAnnotBase { |
197 | /// Create an url link annotation | 280 | /// Create an url link annotation |
198 | - const PdfAnnotUrlLink({ | 281 | + PdfAnnotUrlLink({ |
199 | @required PdfRect rect, | 282 | @required PdfRect rect, |
200 | @required this.url, | 283 | @required this.url, |
201 | PdfBorder border, | 284 | PdfBorder border, |
@@ -228,15 +311,16 @@ class PdfAnnotUrlLink extends PdfAnnotBase { | @@ -228,15 +311,16 @@ class PdfAnnotUrlLink extends PdfAnnotBase { | ||
228 | enum PdfAnnotHighlighting { none, invert, outline, push, toggle } | 311 | enum PdfAnnotHighlighting { none, invert, outline, push, toggle } |
229 | 312 | ||
230 | abstract class PdfAnnotWidget extends PdfAnnotBase { | 313 | abstract class PdfAnnotWidget extends PdfAnnotBase { |
231 | - /// Create an url link annotation | ||
232 | - const PdfAnnotWidget( | ||
233 | - PdfRect rect, | ||
234 | - this.fieldType, { | 314 | + /// Create a widget annotation |
315 | + PdfAnnotWidget({ | ||
316 | + @required PdfRect rect, | ||
317 | + @required this.fieldType, | ||
235 | this.fieldName, | 318 | this.fieldName, |
236 | PdfBorder border, | 319 | PdfBorder border, |
237 | Set<PdfAnnotFlags> flags, | 320 | Set<PdfAnnotFlags> flags, |
238 | DateTime date, | 321 | DateTime date, |
239 | PdfColor color, | 322 | PdfColor color, |
323 | + this.backgroundColor, | ||
240 | this.highlighting, | 324 | this.highlighting, |
241 | }) : super( | 325 | }) : super( |
242 | subtype: '/Widget', | 326 | subtype: '/Widget', |
@@ -253,6 +337,8 @@ abstract class PdfAnnotWidget extends PdfAnnotBase { | @@ -253,6 +337,8 @@ abstract class PdfAnnotWidget extends PdfAnnotBase { | ||
253 | 337 | ||
254 | final PdfAnnotHighlighting highlighting; | 338 | final PdfAnnotHighlighting highlighting; |
255 | 339 | ||
340 | + final PdfColor backgroundColor; | ||
341 | + | ||
256 | @override | 342 | @override |
257 | void build(PdfPage page, PdfObject object, PdfDict params) { | 343 | void build(PdfPage page, PdfObject object, PdfDict params) { |
258 | super.build(page, object, params); | 344 | super.build(page, object, params); |
@@ -262,12 +348,45 @@ abstract class PdfAnnotWidget extends PdfAnnotBase { | @@ -262,12 +348,45 @@ abstract class PdfAnnotWidget extends PdfAnnotBase { | ||
262 | if (fieldName != null) { | 348 | if (fieldName != null) { |
263 | params['/T'] = PdfSecString.fromString(object, fieldName); | 349 | params['/T'] = PdfSecString.fromString(object, fieldName); |
264 | } | 350 | } |
351 | + | ||
352 | + final PdfDict mk = PdfDict(); | ||
353 | + if (color != null) { | ||
354 | + mk.values['/BC'] = PdfColorType(color); | ||
355 | + } | ||
356 | + | ||
357 | + if (backgroundColor != null) { | ||
358 | + mk.values['/BG'] = PdfColorType(backgroundColor); | ||
359 | + } | ||
360 | + | ||
361 | + if (mk.values.isNotEmpty) { | ||
362 | + params['/MK'] = mk; | ||
363 | + } | ||
364 | + | ||
365 | + if (highlighting != null) { | ||
366 | + switch (highlighting) { | ||
367 | + case PdfAnnotHighlighting.none: | ||
368 | + params['/H'] = const PdfName('/N'); | ||
369 | + break; | ||
370 | + case PdfAnnotHighlighting.invert: | ||
371 | + params['/H'] = const PdfName('/I'); | ||
372 | + break; | ||
373 | + case PdfAnnotHighlighting.outline: | ||
374 | + params['/H'] = const PdfName('/O'); | ||
375 | + break; | ||
376 | + case PdfAnnotHighlighting.push: | ||
377 | + params['/H'] = const PdfName('/P'); | ||
378 | + break; | ||
379 | + case PdfAnnotHighlighting.toggle: | ||
380 | + params['/H'] = const PdfName('/T'); | ||
381 | + break; | ||
382 | + } | ||
383 | + } | ||
265 | } | 384 | } |
266 | } | 385 | } |
267 | 386 | ||
268 | class PdfAnnotSign extends PdfAnnotWidget { | 387 | class PdfAnnotSign extends PdfAnnotWidget { |
269 | - const PdfAnnotSign( | ||
270 | - PdfRect rect, { | 388 | + PdfAnnotSign({ |
389 | + @required PdfRect rect, | ||
271 | String fieldName, | 390 | String fieldName, |
272 | PdfBorder border, | 391 | PdfBorder border, |
273 | Set<PdfAnnotFlags> flags, | 392 | Set<PdfAnnotFlags> flags, |
@@ -275,8 +394,8 @@ class PdfAnnotSign extends PdfAnnotWidget { | @@ -275,8 +394,8 @@ class PdfAnnotSign extends PdfAnnotWidget { | ||
275 | PdfColor color, | 394 | PdfColor color, |
276 | PdfAnnotHighlighting highlighting, | 395 | PdfAnnotHighlighting highlighting, |
277 | }) : super( | 396 | }) : super( |
278 | - rect, | ||
279 | - '/Sig', | 397 | + rect: rect, |
398 | + fieldType: '/Sig', | ||
280 | fieldName: fieldName, | 399 | fieldName: fieldName, |
281 | border: border, | 400 | border: border, |
282 | flags: flags, | 401 | flags: flags, |
@@ -292,3 +411,284 @@ class PdfAnnotSign extends PdfAnnotWidget { | @@ -292,3 +411,284 @@ class PdfAnnotSign extends PdfAnnotWidget { | ||
292 | params['/V'] = page.pdfDocument.sign.ref(); | 411 | params['/V'] = page.pdfDocument.sign.ref(); |
293 | } | 412 | } |
294 | } | 413 | } |
414 | + | ||
415 | +enum PdfFieldFlags { | ||
416 | + /// 1 - If set, the user may not change the value of the field. | ||
417 | + readOnly, | ||
418 | + | ||
419 | + /// 2 - If set, the field shall have a value at the time it is exported by | ||
420 | + /// a submit-form action. | ||
421 | + mandatory, | ||
422 | + | ||
423 | + /// 3 - If set, the field shall not be exported by a submit-form action. | ||
424 | + noExport, | ||
425 | + | ||
426 | + /// 4 | ||
427 | + reserved4, | ||
428 | + | ||
429 | + /// 5 | ||
430 | + reserved5, | ||
431 | + | ||
432 | + /// 6 | ||
433 | + reserved6, | ||
434 | + | ||
435 | + /// 7 | ||
436 | + reserved7, | ||
437 | + | ||
438 | + /// 8 | ||
439 | + reserved8, | ||
440 | + | ||
441 | + /// 9 | ||
442 | + reserved9, | ||
443 | + | ||
444 | + /// 10 | ||
445 | + reserved10, | ||
446 | + | ||
447 | + /// 11 | ||
448 | + reserved11, | ||
449 | + | ||
450 | + /// 12 | ||
451 | + reserved12, | ||
452 | + | ||
453 | + /// 13 - If set, the field may contain multiple lines of text; if clear, | ||
454 | + /// the field’s text shall be restricted to a single line. | ||
455 | + multiline, | ||
456 | + | ||
457 | + /// 14 - If set, the field is intended for entering a secure password that | ||
458 | + /// should not be echoed visibly to the screen. Characters typed from | ||
459 | + /// the keyboard shall instead be echoed in some unreadable form, such | ||
460 | + /// as asterisks or bullet characters. | ||
461 | + password, | ||
462 | + | ||
463 | + /// 15 - If set, exactly one radio button shall be selected at all times. | ||
464 | + noToggleToOff, | ||
465 | + | ||
466 | + /// 16 - If set, the field is a set of radio buttons; if clear, | ||
467 | + /// the field is a check box. | ||
468 | + radio, | ||
469 | + | ||
470 | + /// 17 - If set, the field is a pushbutton that does not retain | ||
471 | + /// a permanent value. | ||
472 | + pushButton, | ||
473 | + | ||
474 | + /// 18 - If set, the field is a combo box; if clear, the field is a list box. | ||
475 | + combo, | ||
476 | + | ||
477 | + /// 19 - If set, the combo box shall include an editable text box as well | ||
478 | + /// as a drop-down list | ||
479 | + edit, | ||
480 | + | ||
481 | + /// 20 - If set, the field’s option items shall be sorted alphabetically. | ||
482 | + sort, | ||
483 | + | ||
484 | + /// 21 - If set, the text entered in the field represents the pathname | ||
485 | + /// of a file whose contents shall be submitted as the value of the field. | ||
486 | + fileSelect, | ||
487 | + | ||
488 | + /// 22 - If set, more than one of the field’s option items may be selected | ||
489 | + /// simultaneously | ||
490 | + multiSelect, | ||
491 | + | ||
492 | + /// 23 - If set, text entered in the field shall not be spell-checked. | ||
493 | + doNotSpellCheck, | ||
494 | + | ||
495 | + /// 24 - If set, the field shall not scroll to accommodate more text | ||
496 | + /// than fits within its annotation rectangle. | ||
497 | + doNotScroll, | ||
498 | + | ||
499 | + /// 25 - If set, the field shall be automatically divided into as many | ||
500 | + /// equally spaced positions, or combs, as the value of MaxLen, | ||
501 | + /// and the text is laid out into those combs. | ||
502 | + comb, | ||
503 | + | ||
504 | + /// 26 - If set, a group of radio buttons within a radio button field | ||
505 | + /// that use the same value for the on state will turn on and off in unison. | ||
506 | + radiosInUnison, | ||
507 | + | ||
508 | + /// 27 - If set, the new value shall be committed as soon as a selection | ||
509 | + /// is made. | ||
510 | + commitOnSelChange, | ||
511 | +} | ||
512 | + | ||
513 | +class PdfFormField extends PdfAnnotWidget { | ||
514 | + PdfFormField({ | ||
515 | + @required String fieldType, | ||
516 | + @required PdfRect rect, | ||
517 | + String fieldName, | ||
518 | + this.alternateName, | ||
519 | + this.mappingName, | ||
520 | + PdfBorder border, | ||
521 | + Set<PdfAnnotFlags> flags, | ||
522 | + DateTime date, | ||
523 | + PdfColor color, | ||
524 | + PdfColor backgroundColor, | ||
525 | + PdfAnnotHighlighting highlighting, | ||
526 | + this.fieldFlags, | ||
527 | + }) : super( | ||
528 | + rect: rect, | ||
529 | + fieldType: fieldType, | ||
530 | + fieldName: fieldName, | ||
531 | + border: border, | ||
532 | + flags: flags, | ||
533 | + date: date, | ||
534 | + backgroundColor: backgroundColor, | ||
535 | + color: color, | ||
536 | + highlighting: highlighting, | ||
537 | + ); | ||
538 | + | ||
539 | + final String alternateName; | ||
540 | + | ||
541 | + final String mappingName; | ||
542 | + | ||
543 | + final Set<PdfFieldFlags> fieldFlags; | ||
544 | + | ||
545 | + int get fieldFlagsValue { | ||
546 | + if (fieldFlags == null || fieldFlags.isEmpty) { | ||
547 | + return 0; | ||
548 | + } | ||
549 | + | ||
550 | + return fieldFlags | ||
551 | + .map<int>((PdfFieldFlags e) => 1 << e.index) | ||
552 | + .reduce((int a, int b) => a | b); | ||
553 | + } | ||
554 | + | ||
555 | + @override | ||
556 | + void build(PdfPage page, PdfObject object, PdfDict params) { | ||
557 | + super.build(page, object, params); | ||
558 | + if (alternateName != null) { | ||
559 | + params['/TU'] = PdfSecString.fromString(object, alternateName); | ||
560 | + } | ||
561 | + if (mappingName != null) { | ||
562 | + params['/TM'] = PdfSecString.fromString(object, mappingName); | ||
563 | + } | ||
564 | + | ||
565 | + params['/Ff'] = PdfNum(fieldFlagsValue); | ||
566 | + } | ||
567 | +} | ||
568 | + | ||
569 | +enum PdfTextFieldAlign { left, center, right } | ||
570 | + | ||
571 | +class PdfTextField extends PdfFormField { | ||
572 | + PdfTextField({ | ||
573 | + @required PdfRect rect, | ||
574 | + String fieldName, | ||
575 | + String alternateName, | ||
576 | + String mappingName, | ||
577 | + PdfBorder border, | ||
578 | + Set<PdfAnnotFlags> flags, | ||
579 | + DateTime date, | ||
580 | + PdfColor color, | ||
581 | + PdfColor backgroundColor, | ||
582 | + PdfAnnotHighlighting highlighting, | ||
583 | + Set<PdfFieldFlags> fieldFlags, | ||
584 | + this.value, | ||
585 | + this.defaultValue, | ||
586 | + this.maxLength, | ||
587 | + @required this.font, | ||
588 | + @required this.fontSize, | ||
589 | + @required this.textColor, | ||
590 | + this.textAlign, | ||
591 | + }) : assert(fontSize != null), | ||
592 | + assert(textColor != null), | ||
593 | + assert(font != null), | ||
594 | + super( | ||
595 | + rect: rect, | ||
596 | + fieldType: '/Tx', | ||
597 | + fieldName: fieldName, | ||
598 | + border: border, | ||
599 | + flags: flags, | ||
600 | + date: date, | ||
601 | + color: color, | ||
602 | + backgroundColor: backgroundColor, | ||
603 | + highlighting: highlighting, | ||
604 | + alternateName: alternateName, | ||
605 | + mappingName: mappingName, | ||
606 | + fieldFlags: fieldFlags, | ||
607 | + ); | ||
608 | + | ||
609 | + final int maxLength; | ||
610 | + | ||
611 | + final String value; | ||
612 | + | ||
613 | + final String defaultValue; | ||
614 | + | ||
615 | + final PdfFont font; | ||
616 | + | ||
617 | + final double fontSize; | ||
618 | + | ||
619 | + final PdfColor textColor; | ||
620 | + | ||
621 | + final PdfTextFieldAlign textAlign; | ||
622 | + | ||
623 | + @override | ||
624 | + void build(PdfPage page, PdfObject object, PdfDict params) { | ||
625 | + super.build(page, object, params); | ||
626 | + if (maxLength != null) { | ||
627 | + params['/MaxLen'] = PdfNum(maxLength); | ||
628 | + } | ||
629 | + | ||
630 | + final PdfStream buf = PdfStream(); | ||
631 | + final PdfGraphics g = PdfGraphics(page, buf); | ||
632 | + g.setFillColor(textColor); | ||
633 | + g.setFont(font, fontSize); | ||
634 | + params['/DA'] = PdfSecString.fromStream(object, buf); | ||
635 | + | ||
636 | + if (value != null) { | ||
637 | + params['/V'] = PdfSecString.fromString(object, value); | ||
638 | + } | ||
639 | + if (defaultValue != null) { | ||
640 | + params['/DV'] = PdfSecString.fromString(object, defaultValue); | ||
641 | + } | ||
642 | + if (textAlign != null) { | ||
643 | + params['/Q'] = PdfNum(textAlign.index); | ||
644 | + } | ||
645 | + } | ||
646 | +} | ||
647 | + | ||
648 | +class PdfButtonField extends PdfFormField { | ||
649 | + PdfButtonField({ | ||
650 | + @required PdfRect rect, | ||
651 | + String fieldName, | ||
652 | + String alternateName, | ||
653 | + String mappingName, | ||
654 | + PdfBorder border, | ||
655 | + Set<PdfAnnotFlags> flags, | ||
656 | + DateTime date, | ||
657 | + PdfColor color, | ||
658 | + PdfColor backgroundColor, | ||
659 | + PdfAnnotHighlighting highlighting, | ||
660 | + Set<PdfFieldFlags> fieldFlags, | ||
661 | + this.value, | ||
662 | + this.defaultValue, | ||
663 | + }) : super( | ||
664 | + rect: rect, | ||
665 | + fieldType: '/Btn', | ||
666 | + fieldName: fieldName, | ||
667 | + border: border, | ||
668 | + flags: flags, | ||
669 | + date: date, | ||
670 | + color: color, | ||
671 | + backgroundColor: backgroundColor, | ||
672 | + highlighting: highlighting, | ||
673 | + alternateName: alternateName, | ||
674 | + mappingName: mappingName, | ||
675 | + fieldFlags: fieldFlags, | ||
676 | + ); | ||
677 | + | ||
678 | + final bool value; | ||
679 | + | ||
680 | + final bool defaultValue; | ||
681 | + | ||
682 | + @override | ||
683 | + void build(PdfPage page, PdfObject object, PdfDict params) { | ||
684 | + super.build(page, object, params); | ||
685 | + | ||
686 | + if (value != null) { | ||
687 | + params['/V'] = value ? const PdfName('/Yes') : const PdfName('/Off'); | ||
688 | + } | ||
689 | + if (defaultValue != null) { | ||
690 | + params['/DV'] = | ||
691 | + defaultValue ? const PdfName('/Yes') : const PdfName('/Off'); | ||
692 | + } | ||
693 | + } | ||
694 | +} |
@@ -98,6 +98,11 @@ class PdfString extends PdfDataType { | @@ -98,6 +98,11 @@ class PdfString extends PdfDataType { | ||
98 | return PdfString(_string(value), PdfStringFormat.litteral); | 98 | return PdfString(_string(value), PdfStringFormat.litteral); |
99 | } | 99 | } |
100 | 100 | ||
101 | + factory PdfString.fromStream(PdfObject object, PdfStream value, | ||
102 | + [PdfStringFormat format = PdfStringFormat.litteral]) { | ||
103 | + return PdfString(value.output(), format); | ||
104 | + } | ||
105 | + | ||
101 | factory PdfString.fromDate(DateTime date) { | 106 | factory PdfString.fromDate(DateTime date) { |
102 | return PdfString(_date(date)); | 107 | return PdfString(_date(date)); |
103 | } | 108 | } |
@@ -203,11 +208,28 @@ class PdfSecString extends PdfString { | @@ -203,11 +208,28 @@ class PdfSecString extends PdfString { | ||
203 | : super(value, format); | 208 | : super(value, format); |
204 | 209 | ||
205 | factory PdfSecString.fromString(PdfObject object, String value) { | 210 | factory PdfSecString.fromString(PdfObject object, String value) { |
206 | - return PdfSecString(object, PdfString._string(value)); | 211 | + return PdfSecString( |
212 | + object, | ||
213 | + PdfString._string(value), | ||
214 | + PdfStringFormat.litteral, | ||
215 | + ); | ||
216 | + } | ||
217 | + | ||
218 | + factory PdfSecString.fromStream(PdfObject object, PdfStream value, | ||
219 | + [PdfStringFormat format = PdfStringFormat.litteral]) { | ||
220 | + return PdfSecString( | ||
221 | + object, | ||
222 | + value.output(), | ||
223 | + PdfStringFormat.litteral, | ||
224 | + ); | ||
207 | } | 225 | } |
208 | 226 | ||
209 | factory PdfSecString.fromDate(PdfObject object, DateTime date) { | 227 | factory PdfSecString.fromDate(PdfObject object, DateTime date) { |
210 | - return PdfSecString(object, PdfString._date(date)); | 228 | + return PdfSecString( |
229 | + object, | ||
230 | + PdfString._date(date), | ||
231 | + PdfStringFormat.litteral, | ||
232 | + ); | ||
211 | } | 233 | } |
212 | 234 | ||
213 | final PdfObject object; | 235 | final PdfObject object; |
@@ -340,3 +362,28 @@ class PdfDict extends PdfDataType { | @@ -340,3 +362,28 @@ class PdfDict extends PdfDataType { | ||
340 | return values.containsKey(key); | 362 | return values.containsKey(key); |
341 | } | 363 | } |
342 | } | 364 | } |
365 | + | ||
366 | +class PdfColorType extends PdfDataType { | ||
367 | + const PdfColorType(this.color); | ||
368 | + | ||
369 | + final PdfColor color; | ||
370 | + | ||
371 | + @override | ||
372 | + void output(PdfStream s) { | ||
373 | + if (color is PdfColorCmyk) { | ||
374 | + final PdfColorCmyk k = color; | ||
375 | + PdfArray.fromNum(<double>[ | ||
376 | + k.cyan, | ||
377 | + k.magenta, | ||
378 | + k.yellow, | ||
379 | + k.black, | ||
380 | + ]).output(s); | ||
381 | + } else { | ||
382 | + PdfArray.fromNum(<double>[ | ||
383 | + color.red, | ||
384 | + color.green, | ||
385 | + color.blue, | ||
386 | + ]).output(s); | ||
387 | + } | ||
388 | + } | ||
389 | +} |
pdf/lib/src/graphic_stream.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: omit_local_variable_types | ||
18 | + | ||
19 | +part of pdf; | ||
20 | + | ||
21 | +mixin PdfGraphicStream on PdfObject { | ||
22 | + /// Isolated transparency: If this flag is true, objects within the group | ||
23 | + /// shall be composited against a fully transparent initial backdrop; | ||
24 | + /// if false, they shall be composited against the group’s backdrop | ||
25 | + bool isolatedTransparency = false; | ||
26 | + | ||
27 | + /// Whether the transparency group is a knockout group. | ||
28 | + /// If this flag is false, later objects within the group shall be composited | ||
29 | + /// with earlier ones with which they overlap; if true, they shall be | ||
30 | + /// composited with the group’s initial backdrop and shall overwrite any | ||
31 | + /// earlier overlapping objects. | ||
32 | + bool knockoutTransparency = false; | ||
33 | + | ||
34 | + /// The fonts associated with this page | ||
35 | + final Map<String, PdfFont> fonts = <String, PdfFont>{}; | ||
36 | + | ||
37 | + /// The fonts associated with this page | ||
38 | + final Map<String, PdfShading> shading = <String, PdfShading>{}; | ||
39 | + | ||
40 | + /// The xobjects or other images in the pdf | ||
41 | + final Map<String, PdfXObject> xObjects = <String, PdfXObject>{}; | ||
42 | + | ||
43 | + void addFont(PdfFont font) { | ||
44 | + if (!fonts.containsKey(font.name)) { | ||
45 | + fonts[font.name] = font; | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | + void addShader(PdfShading shader) { | ||
50 | + if (!shading.containsKey(shader.name)) { | ||
51 | + shading[shader.name] = shader; | ||
52 | + } | ||
53 | + } | ||
54 | + | ||
55 | + void addXObject(PdfXObject object) { | ||
56 | + if (!xObjects.containsKey(object.name)) { | ||
57 | + xObjects[object.name] = object; | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + PdfFont getDefaultFont() { | ||
62 | + if (pdfDocument.fonts.isEmpty) { | ||
63 | + PdfFont.helvetica(pdfDocument); | ||
64 | + } | ||
65 | + | ||
66 | + return pdfDocument.fonts.elementAt(0); | ||
67 | + } | ||
68 | + | ||
69 | + String stateName(PdfGraphicState state) { | ||
70 | + return pdfDocument.graphicStates.stateName(state); | ||
71 | + } | ||
72 | + | ||
73 | + @override | ||
74 | + void _prepare() { | ||
75 | + super._prepare(); | ||
76 | + | ||
77 | + // This holds any resources for this page | ||
78 | + final PdfDict resources = PdfDict(); | ||
79 | + | ||
80 | + resources['/ProcSet'] = PdfArray(const <PdfName>[ | ||
81 | + PdfName('/PDF'), | ||
82 | + PdfName('/Text'), | ||
83 | + PdfName('/ImageB'), | ||
84 | + PdfName('/ImageC'), | ||
85 | + ]); | ||
86 | + | ||
87 | + // fonts | ||
88 | + if (fonts.isNotEmpty) { | ||
89 | + resources['/Font'] = PdfDict.fromObjectMap(fonts); | ||
90 | + } | ||
91 | + | ||
92 | + // shading | ||
93 | + if (shading.isNotEmpty) { | ||
94 | + resources['/Shading'] = PdfDict.fromObjectMap(shading); | ||
95 | + } | ||
96 | + | ||
97 | + // Now the XObjects | ||
98 | + if (xObjects.isNotEmpty) { | ||
99 | + resources['/XObject'] = PdfDict.fromObjectMap(xObjects); | ||
100 | + } | ||
101 | + | ||
102 | + if (pdfDocument.hasGraphicStates) { | ||
103 | + // Declare Transparency Group settings | ||
104 | + params['/Group'] = PdfDict(<String, PdfDataType>{ | ||
105 | + '/Type': const PdfName('/Group'), | ||
106 | + '/S': const PdfName('/Transparency'), | ||
107 | + '/CS': const PdfName('/DeviceRGB'), | ||
108 | + '/I': PdfBool(isolatedTransparency), | ||
109 | + '/K': PdfBool(knockoutTransparency), | ||
110 | + }); | ||
111 | + | ||
112 | + resources['/ExtGState'] = pdfDocument.graphicStates.ref(); | ||
113 | + } | ||
114 | + | ||
115 | + params['/Resources'] = resources; | ||
116 | + } | ||
117 | +} | ||
118 | + | ||
119 | +class PdfGraphicXObject extends PdfXObject with PdfGraphicStream { | ||
120 | + PdfGraphicXObject( | ||
121 | + PdfDocument pdfDocument, [ | ||
122 | + String subtype, | ||
123 | + ]) : super(pdfDocument, subtype); | ||
124 | +} |
@@ -55,7 +55,7 @@ class _PdfGraphicsContext { | @@ -55,7 +55,7 @@ class _PdfGraphicsContext { | ||
55 | } | 55 | } |
56 | 56 | ||
57 | class PdfGraphics { | 57 | class PdfGraphics { |
58 | - PdfGraphics(this.page, this.buf) { | 58 | + PdfGraphics(this._page, this.buf) { |
59 | _context = _PdfGraphicsContext(ctm: Matrix4.identity()); | 59 | _context = _PdfGraphicsContext(ctm: Matrix4.identity()); |
60 | } | 60 | } |
61 | 61 | ||
@@ -66,17 +66,11 @@ class PdfGraphics { | @@ -66,17 +66,11 @@ class PdfGraphics { | ||
66 | _PdfGraphicsContext _context; | 66 | _PdfGraphicsContext _context; |
67 | final Queue<_PdfGraphicsContext> _contextQueue = Queue<_PdfGraphicsContext>(); | 67 | final Queue<_PdfGraphicsContext> _contextQueue = Queue<_PdfGraphicsContext>(); |
68 | 68 | ||
69 | - final PdfPage page; | 69 | + final PdfGraphicStream _page; |
70 | 70 | ||
71 | final PdfStream buf; | 71 | final PdfStream buf; |
72 | 72 | ||
73 | - PdfFont get defaultFont { | ||
74 | - if (page.pdfDocument.fonts.isEmpty) { | ||
75 | - PdfFont.helvetica(page.pdfDocument); | ||
76 | - } | ||
77 | - | ||
78 | - return page.pdfDocument.fonts.elementAt(0); | ||
79 | - } | 73 | + PdfFont get defaultFont => _page.getDefaultFont(); |
80 | 74 | ||
81 | void fillPath() { | 75 | void fillPath() { |
82 | buf.putString('f\n'); | 76 | buf.putString('f\n'); |
@@ -97,7 +91,7 @@ class PdfGraphics { | @@ -97,7 +91,7 @@ class PdfGraphics { | ||
97 | /// Apply a shader | 91 | /// Apply a shader |
98 | void applyShader(PdfShading shader) { | 92 | void applyShader(PdfShading shader) { |
99 | // The shader needs to be registered in the page resources | 93 | // The shader needs to be registered in the page resources |
100 | - page.shading[shader.name] = shader; | 94 | + _page.addShader(shader); |
101 | buf.putString('${shader.name} sh\n'); | 95 | buf.putString('${shader.name} sh\n'); |
102 | } | 96 | } |
103 | 97 | ||
@@ -139,7 +133,7 @@ class PdfGraphics { | @@ -139,7 +133,7 @@ class PdfGraphics { | ||
139 | h ??= img.height.toDouble() * w / img.width.toDouble(); | 133 | h ??= img.height.toDouble() * w / img.width.toDouble(); |
140 | 134 | ||
141 | // The image needs to be registered in the page resources | 135 | // The image needs to be registered in the page resources |
142 | - page.xObjects[img.name] = img; | 136 | + _page.addXObject(img); |
143 | 137 | ||
144 | // q w 0 0 h x y cm % the coordinate matrix | 138 | // q w 0 0 h x y cm % the coordinate matrix |
145 | buf.putString('q '); | 139 | buf.putString('q '); |
@@ -232,51 +226,68 @@ class PdfGraphics { | @@ -232,51 +226,68 @@ class PdfGraphics { | ||
232 | lineTo(x, y + rv); | 226 | lineTo(x, y + rv); |
233 | } | 227 | } |
234 | 228 | ||
235 | - /// This draws a string. | ||
236 | - /// | ||
237 | - /// @param x coordinate | ||
238 | - /// @param y coordinate | ||
239 | - /// @param s String to draw | ||
240 | - void drawString( | 229 | + /// Set the current font and size |
230 | + void setFont( | ||
241 | PdfFont font, | 231 | PdfFont font, |
242 | - double size, | ||
243 | - String s, | ||
244 | - double x, | ||
245 | - double y, { | 232 | + double size, { |
246 | double charSpace = 0, | 233 | double charSpace = 0, |
247 | double wordSpace = 0, | 234 | double wordSpace = 0, |
248 | double scale = 1, | 235 | double scale = 1, |
249 | PdfTextRenderingMode mode = PdfTextRenderingMode.fill, | 236 | PdfTextRenderingMode mode = PdfTextRenderingMode.fill, |
250 | double rise = 0, | 237 | double rise = 0, |
251 | }) { | 238 | }) { |
252 | - if (!page.fonts.containsKey(font.name)) { | ||
253 | - page.fonts[font.name] = font; | ||
254 | - } | ||
255 | - | ||
256 | - buf.putString('BT '); | ||
257 | - PdfNumList(<double>[x, y]).output(buf); | ||
258 | - buf.putString(' Td ${font.name} '); | 239 | + buf.putString('${font.name} '); |
259 | PdfNum(size).output(buf); | 240 | PdfNum(size).output(buf); |
260 | - buf.putString(' Tf '); | 241 | + buf.putString(' Tf\n'); |
261 | if (charSpace != 0) { | 242 | if (charSpace != 0) { |
262 | PdfNum(charSpace).output(buf); | 243 | PdfNum(charSpace).output(buf); |
263 | - buf.putString(' Tc '); | 244 | + buf.putString(' Tc\n'); |
264 | } | 245 | } |
265 | if (wordSpace != 0) { | 246 | if (wordSpace != 0) { |
266 | PdfNum(wordSpace).output(buf); | 247 | PdfNum(wordSpace).output(buf); |
267 | - buf.putString(' Tw '); | 248 | + buf.putString(' Tw\n'); |
268 | } | 249 | } |
269 | if (scale != 1) { | 250 | if (scale != 1) { |
270 | PdfNum(scale * 100).output(buf); | 251 | PdfNum(scale * 100).output(buf); |
271 | - buf.putString(' Tz '); | 252 | + buf.putString(' Tz\n'); |
272 | } | 253 | } |
273 | if (rise != 0) { | 254 | if (rise != 0) { |
274 | PdfNum(rise).output(buf); | 255 | PdfNum(rise).output(buf); |
275 | - buf.putString(' Ts '); | 256 | + buf.putString(' Ts\n'); |
276 | } | 257 | } |
277 | if (mode != PdfTextRenderingMode.fill) { | 258 | if (mode != PdfTextRenderingMode.fill) { |
278 | - buf.putString('${mode.index} Tr '); | 259 | + buf.putString('${mode.index} Tr\n'); |
260 | + } | ||
279 | } | 261 | } |
262 | + | ||
263 | + /// This draws a string. | ||
264 | + /// | ||
265 | + /// @param x coordinate | ||
266 | + /// @param y coordinate | ||
267 | + /// @param s String to draw | ||
268 | + void drawString( | ||
269 | + PdfFont font, | ||
270 | + double size, | ||
271 | + String s, | ||
272 | + double x, | ||
273 | + double y, { | ||
274 | + double charSpace = 0, | ||
275 | + double wordSpace = 0, | ||
276 | + double scale = 1, | ||
277 | + PdfTextRenderingMode mode = PdfTextRenderingMode.fill, | ||
278 | + double rise = 0, | ||
279 | + }) { | ||
280 | + _page.addFont(font); | ||
281 | + | ||
282 | + buf.putString('BT '); | ||
283 | + PdfNumList(<double>[x, y]).output(buf); | ||
284 | + buf.putString(' Td '); | ||
285 | + setFont(font, size, | ||
286 | + charSpace: charSpace, | ||
287 | + mode: mode, | ||
288 | + rise: rise, | ||
289 | + scale: scale, | ||
290 | + wordSpace: wordSpace); | ||
280 | buf.putString('['); | 291 | buf.putString('['); |
281 | font.putText(buf, s); | 292 | font.putText(buf, s); |
282 | buf.putString(']TJ ET\n'); | 293 | buf.putString(']TJ ET\n'); |
@@ -320,7 +331,7 @@ class PdfGraphics { | @@ -320,7 +331,7 @@ class PdfGraphics { | ||
320 | 331 | ||
321 | /// Set the graphic state for drawing | 332 | /// Set the graphic state for drawing |
322 | void setGraphicState(PdfGraphicState state) { | 333 | void setGraphicState(PdfGraphicState state) { |
323 | - final String name = page.pdfDocument.graphicStates.stateName(state); | 334 | + final String name = _page.stateName(state); |
324 | buf.putString('$name gs\n'); | 335 | buf.putString('$name gs\n'); |
325 | } | 336 | } |
326 | 337 |
@@ -199,5 +199,6 @@ class PdfImage extends PdfXObject { | @@ -199,5 +199,6 @@ class PdfImage extends PdfXObject { | ||
199 | final PdfImageOrientation orientation; | 199 | final PdfImageOrientation orientation; |
200 | 200 | ||
201 | /// Name of the image | 201 | /// Name of the image |
202 | + @override | ||
202 | String get name => '/I$objser'; | 203 | String get name => '/I$objser'; |
203 | } | 204 | } |
@@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
18 | 18 | ||
19 | part of pdf; | 19 | part of pdf; |
20 | 20 | ||
21 | -class PdfPage extends PdfObject { | 21 | +class PdfPage extends PdfObject with PdfGraphicStream { |
22 | /// This constructs a Page object, which will hold any contents for this | 22 | /// This constructs a Page object, which will hold any contents for this |
23 | /// page. | 23 | /// page. |
24 | /// | 24 | /// |
@@ -41,30 +41,9 @@ class PdfPage extends PdfObject { | @@ -41,30 +41,9 @@ class PdfPage extends PdfObject { | ||
41 | /// -1 indicates no thumbnail. | 41 | /// -1 indicates no thumbnail. |
42 | PdfObject thumbnail; | 42 | PdfObject thumbnail; |
43 | 43 | ||
44 | - /// Isolated transparency: If this flag is true, objects within the group | ||
45 | - /// shall be composited against a fully transparent initial backdrop; | ||
46 | - /// if false, they shall be composited against the group’s backdrop | ||
47 | - bool isolatedTransparency = false; | ||
48 | - | ||
49 | - /// Whether the transparency group is a knockout group. | ||
50 | - /// If this flag is false, later objects within the group shall be composited | ||
51 | - /// with earlier ones with which they overlap; if true, they shall be | ||
52 | - /// composited with the group’s initial backdrop and shall overwrite any | ||
53 | - /// earlier overlapping objects. | ||
54 | - bool knockoutTransparency = false; | ||
55 | - | ||
56 | /// This holds any Annotations contained within this page. | 44 | /// This holds any Annotations contained within this page. |
57 | List<PdfAnnot> annotations = <PdfAnnot>[]; | 45 | List<PdfAnnot> annotations = <PdfAnnot>[]; |
58 | 46 | ||
59 | - /// The fonts associated with this page | ||
60 | - final Map<String, PdfFont> fonts = <String, PdfFont>{}; | ||
61 | - | ||
62 | - /// The fonts associated with this page | ||
63 | - final Map<String, PdfShading> shading = <String, PdfShading>{}; | ||
64 | - | ||
65 | - /// The xobjects or other images in the pdf | ||
66 | - final Map<String, PdfXObject> xObjects = <String, PdfXObject>{}; | ||
67 | - | ||
68 | /// This returns a [PdfGraphics] object, which can then be used to render | 47 | /// This returns a [PdfGraphics] object, which can then be used to render |
69 | /// on to this page. If a previous [PdfGraphics] object was used, this object | 48 | /// on to this page. If a previous [PdfGraphics] object was used, this object |
70 | /// is appended to the page, and will be drawn over the top of any previous | 49 | /// is appended to the page, and will be drawn over the top of any previous |
@@ -88,7 +67,6 @@ class PdfPage extends PdfObject { | @@ -88,7 +67,6 @@ class PdfPage extends PdfObject { | ||
88 | annotations.add(ob); | 67 | annotations.add(ob); |
89 | } | 68 | } |
90 | 69 | ||
91 | - /// @param os OutputStream to send the object to | ||
92 | @override | 70 | @override |
93 | void _prepare() { | 71 | void _prepare() { |
94 | super._prepare(); | 72 | super._prepare(); |
@@ -116,47 +94,6 @@ class PdfPage extends PdfObject { | @@ -116,47 +94,6 @@ class PdfPage extends PdfObject { | ||
116 | } | 94 | } |
117 | } | 95 | } |
118 | 96 | ||
119 | - // Now the resources | ||
120 | - /// This holds any resources for this page | ||
121 | - final PdfDict resources = PdfDict(); | ||
122 | - | ||
123 | - resources['/ProcSet'] = PdfArray(const <PdfName>[ | ||
124 | - PdfName('/PDF'), | ||
125 | - PdfName('/Text'), | ||
126 | - PdfName('/ImageB'), | ||
127 | - PdfName('/ImageC'), | ||
128 | - ]); | ||
129 | - | ||
130 | - // fonts | ||
131 | - if (fonts.isNotEmpty) { | ||
132 | - resources['/Font'] = PdfDict.fromObjectMap(fonts); | ||
133 | - } | ||
134 | - | ||
135 | - // shading | ||
136 | - if (shading.isNotEmpty) { | ||
137 | - resources['/Shading'] = PdfDict.fromObjectMap(shading); | ||
138 | - } | ||
139 | - | ||
140 | - // Now the XObjects | ||
141 | - if (xObjects.isNotEmpty) { | ||
142 | - resources['/XObject'] = PdfDict.fromObjectMap(xObjects); | ||
143 | - } | ||
144 | - | ||
145 | - if (pdfDocument.hasGraphicStates) { | ||
146 | - // Declare Transparency Group settings | ||
147 | - params['/Group'] = PdfDict(<String, PdfDataType>{ | ||
148 | - '/Type': const PdfName('/Group'), | ||
149 | - '/S': const PdfName('/Transparency'), | ||
150 | - '/CS': const PdfName('/DeviceRGB'), | ||
151 | - '/I': PdfBool(isolatedTransparency), | ||
152 | - '/K': PdfBool(knockoutTransparency), | ||
153 | - }); | ||
154 | - | ||
155 | - resources['/ExtGState'] = pdfDocument.graphicStates.ref(); | ||
156 | - } | ||
157 | - | ||
158 | - params['/Resources'] = resources; | ||
159 | - | ||
160 | // The thumbnail | 97 | // The thumbnail |
161 | if (thumbnail != null) { | 98 | if (thumbnail != null) { |
162 | params['/Thumb'] = thumbnail.ref(); | 99 | params['/Thumb'] = thumbnail.ref(); |
@@ -21,4 +21,6 @@ class PdfXObject extends PdfObjectStream { | @@ -21,4 +21,6 @@ class PdfXObject extends PdfObjectStream { | ||
21 | : super(pdfDocument, type: '/XObject', isBinary: isBinary) { | 21 | : super(pdfDocument, type: '/XObject', isBinary: isBinary) { |
22 | params['/Subtype'] = PdfName(subtype); | 22 | params['/Subtype'] = PdfName(subtype); |
23 | } | 23 | } |
24 | + | ||
25 | + String get name => 'X$objser'; | ||
24 | } | 26 | } |
@@ -45,6 +45,7 @@ part 'widgets/decoration.dart'; | @@ -45,6 +45,7 @@ part 'widgets/decoration.dart'; | ||
45 | part 'widgets/document.dart'; | 45 | part 'widgets/document.dart'; |
46 | part 'widgets/flex.dart'; | 46 | part 'widgets/flex.dart'; |
47 | part 'widgets/font.dart'; | 47 | part 'widgets/font.dart'; |
48 | +part 'widgets/forms.dart'; | ||
48 | part 'widgets/geometry.dart'; | 49 | part 'widgets/geometry.dart'; |
49 | part 'widgets/grid_view.dart'; | 50 | part 'widgets/grid_view.dart'; |
50 | part 'widgets/image.dart'; | 51 | part 'widgets/image.dart'; |
@@ -45,13 +45,6 @@ class Anchor extends SingleChildWidget { | @@ -45,13 +45,6 @@ class Anchor extends SingleChildWidget { | ||
45 | } | 45 | } |
46 | 46 | ||
47 | abstract class AnnotationBuilder { | 47 | abstract class AnnotationBuilder { |
48 | - PdfRect localToGlobal(Context context, PdfRect box) { | ||
49 | - final Matrix4 mat = context.canvas.getTransform(); | ||
50 | - final Vector3 lt = mat.transform3(Vector3(box.left, box.bottom, 0)); | ||
51 | - final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0)); | ||
52 | - return PdfRect.fromLTRB(lt.x, lt.y, rb.x, rb.y); | ||
53 | - } | ||
54 | - | ||
55 | void build(Context context, PdfRect box); | 48 | void build(Context context, PdfRect box); |
56 | } | 49 | } |
57 | 50 | ||
@@ -65,7 +58,7 @@ class AnnotationLink extends AnnotationBuilder { | @@ -65,7 +58,7 @@ class AnnotationLink extends AnnotationBuilder { | ||
65 | PdfAnnot( | 58 | PdfAnnot( |
66 | context.page, | 59 | context.page, |
67 | PdfAnnotNamedLink( | 60 | PdfAnnotNamedLink( |
68 | - rect: localToGlobal(context, box), | 61 | + rect: context.localToGlobal(box), |
69 | dest: destination, | 62 | dest: destination, |
70 | ), | 63 | ), |
71 | ); | 64 | ); |
@@ -82,7 +75,7 @@ class AnnotationUrl extends AnnotationBuilder { | @@ -82,7 +75,7 @@ class AnnotationUrl extends AnnotationBuilder { | ||
82 | PdfAnnot( | 75 | PdfAnnot( |
83 | context.page, | 76 | context.page, |
84 | PdfAnnotUrlLink( | 77 | PdfAnnotUrlLink( |
85 | - rect: localToGlobal(context, box), | 78 | + rect: context.localToGlobal(box), |
86 | url: destination, | 79 | url: destination, |
87 | ), | 80 | ), |
88 | ); | 81 | ); |
@@ -128,13 +121,89 @@ class AnnotationSignature extends AnnotationBuilder { | @@ -128,13 +121,89 @@ class AnnotationSignature extends AnnotationBuilder { | ||
128 | PdfAnnot( | 121 | PdfAnnot( |
129 | context.page, | 122 | context.page, |
130 | PdfAnnotSign( | 123 | PdfAnnotSign( |
131 | - localToGlobal(context, box), | 124 | + rect: context.localToGlobal(box), |
125 | + fieldName: name, | ||
126 | + border: border, | ||
127 | + flags: flags, | ||
128 | + date: date, | ||
129 | + color: color, | ||
130 | + highlighting: highlighting, | ||
131 | + ), | ||
132 | + ); | ||
133 | + } | ||
134 | +} | ||
135 | + | ||
136 | +class AnnotationTextField extends AnnotationBuilder { | ||
137 | + AnnotationTextField({ | ||
138 | + this.name, | ||
139 | + this.border, | ||
140 | + this.flags, | ||
141 | + this.date, | ||
142 | + this.color, | ||
143 | + this.backgroundColor, | ||
144 | + this.highlighting, | ||
145 | + this.maxLength, | ||
146 | + this.alternateName, | ||
147 | + this.mappingName, | ||
148 | + this.fieldFlags, | ||
149 | + this.value, | ||
150 | + this.defaultValue, | ||
151 | + this.textStyle, | ||
152 | + }); | ||
153 | + | ||
154 | + final String name; | ||
155 | + | ||
156 | + final PdfBorder border; | ||
157 | + | ||
158 | + final Set<PdfAnnotFlags> flags; | ||
159 | + | ||
160 | + final DateTime date; | ||
161 | + | ||
162 | + final PdfColor color; | ||
163 | + | ||
164 | + final PdfColor backgroundColor; | ||
165 | + | ||
166 | + final PdfAnnotHighlighting highlighting; | ||
167 | + | ||
168 | + final int maxLength; | ||
169 | + | ||
170 | + final String value; | ||
171 | + | ||
172 | + final String defaultValue; | ||
173 | + | ||
174 | + final TextStyle textStyle; | ||
175 | + | ||
176 | + final String alternateName; | ||
177 | + | ||
178 | + final String mappingName; | ||
179 | + | ||
180 | + final Set<PdfFieldFlags> fieldFlags; | ||
181 | + | ||
182 | + @override | ||
183 | + void build(Context context, PdfRect box) { | ||
184 | + final TextStyle _textStyle = | ||
185 | + Theme.of(context).defaultTextStyle.merge(textStyle); | ||
186 | + | ||
187 | + PdfAnnot( | ||
188 | + context.page, | ||
189 | + PdfTextField( | ||
190 | + rect: context.localToGlobal(box), | ||
132 | fieldName: name, | 191 | fieldName: name, |
133 | border: border, | 192 | border: border, |
134 | flags: flags, | 193 | flags: flags, |
135 | date: date, | 194 | date: date, |
136 | color: color, | 195 | color: color, |
196 | + backgroundColor: backgroundColor, | ||
137 | highlighting: highlighting, | 197 | highlighting: highlighting, |
198 | + maxLength: maxLength, | ||
199 | + alternateName: alternateName, | ||
200 | + mappingName: mappingName, | ||
201 | + fieldFlags: fieldFlags, | ||
202 | + value: value, | ||
203 | + defaultValue: defaultValue, | ||
204 | + font: _textStyle.font.getFont(context), | ||
205 | + fontSize: _textStyle.fontSize, | ||
206 | + textColor: _textStyle.color, | ||
138 | ), | 207 | ), |
139 | ); | 208 | ); |
140 | } | 209 | } |
@@ -199,3 +268,42 @@ class Signature extends Annotation { | @@ -199,3 +268,42 @@ class Signature extends Annotation { | ||
199 | highlighting: highlighting, | 268 | highlighting: highlighting, |
200 | )); | 269 | )); |
201 | } | 270 | } |
271 | + | ||
272 | +class TextField extends Annotation { | ||
273 | + TextField({ | ||
274 | + Widget child, | ||
275 | + double width = 120, | ||
276 | + double height = 13, | ||
277 | + String name, | ||
278 | + PdfBorder border, | ||
279 | + Set<PdfAnnotFlags> flags, | ||
280 | + DateTime date, | ||
281 | + PdfColor color, | ||
282 | + PdfColor backgroundColor, | ||
283 | + PdfAnnotHighlighting highlighting, | ||
284 | + int maxLength, | ||
285 | + String alternateName, | ||
286 | + String mappingName, | ||
287 | + Set<PdfFieldFlags> fieldFlags, | ||
288 | + String value, | ||
289 | + String defaultValue, | ||
290 | + TextStyle textStyle, | ||
291 | + }) : super( | ||
292 | + child: child ?? SizedBox(width: width, height: height), | ||
293 | + builder: AnnotationTextField( | ||
294 | + name: name, | ||
295 | + border: border, | ||
296 | + flags: flags, | ||
297 | + date: date, | ||
298 | + color: color, | ||
299 | + backgroundColor: backgroundColor, | ||
300 | + highlighting: highlighting, | ||
301 | + maxLength: maxLength, | ||
302 | + alternateName: alternateName, | ||
303 | + mappingName: mappingName, | ||
304 | + fieldFlags: fieldFlags, | ||
305 | + value: value, | ||
306 | + defaultValue: defaultValue, | ||
307 | + textStyle: textStyle, | ||
308 | + )); | ||
309 | +} |
pdf/lib/widgets/forms.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: omit_local_variable_types | ||
18 | + | ||
19 | +part of widget; | ||
20 | + | ||
21 | +class Checkbox extends SingleChildWidget { | ||
22 | + Checkbox({ | ||
23 | + @required this.value, | ||
24 | + this.defaultValue, | ||
25 | + this.tristate = false, | ||
26 | + this.activeColor = PdfColors.blue, | ||
27 | + this.checkColor = PdfColors.white, | ||
28 | + @required this.name, | ||
29 | + double width = 13, | ||
30 | + double height = 13, | ||
31 | + BoxDecoration decoration, | ||
32 | + }) : super( | ||
33 | + child: Container( | ||
34 | + width: width, | ||
35 | + height: height, | ||
36 | + margin: const EdgeInsets.all(1), | ||
37 | + decoration: decoration ?? | ||
38 | + const BoxDecoration( | ||
39 | + border: BoxBorder( | ||
40 | + left: true, | ||
41 | + top: true, | ||
42 | + bottom: true, | ||
43 | + right: true, | ||
44 | + color: PdfColors.grey600, | ||
45 | + width: 2, | ||
46 | + )))); | ||
47 | + | ||
48 | + final bool value; | ||
49 | + | ||
50 | + final bool defaultValue; | ||
51 | + | ||
52 | + final bool tristate; | ||
53 | + | ||
54 | + final PdfColor activeColor; | ||
55 | + | ||
56 | + final PdfColor checkColor; | ||
57 | + | ||
58 | + final String name; | ||
59 | + | ||
60 | + @override | ||
61 | + void paint(Context context) { | ||
62 | + super.paint(context); | ||
63 | + paintChild(context); | ||
64 | + | ||
65 | + final PdfButtonField bf = PdfButtonField( | ||
66 | + rect: context.localToGlobal(box), | ||
67 | + fieldName: name, | ||
68 | + value: value, | ||
69 | + defaultValue: value, | ||
70 | + flags: <PdfAnnotFlags>{PdfAnnotFlags.print}, | ||
71 | + ); | ||
72 | + | ||
73 | + final PdfGraphics g = | ||
74 | + bf.appearance(context.document, PdfAnnotApparence.normal, name: '/Yes'); | ||
75 | + g.drawRect(0, 0, bf.rect.width, bf.rect.height); | ||
76 | + g.setFillColor(activeColor); | ||
77 | + g.fillPath(); | ||
78 | + g.moveTo(2, bf.rect.height / 2); | ||
79 | + g.lineTo(bf.rect.width / 3, bf.rect.height / 4); | ||
80 | + g.lineTo(bf.rect.width - 2, bf.rect.height / 4 * 3); | ||
81 | + g.setStrokeColor(checkColor); | ||
82 | + g.setLineWidth(2); | ||
83 | + g.strokePath(); | ||
84 | + | ||
85 | + bf.appearance(context.document, PdfAnnotApparence.normal, name: '/Off'); | ||
86 | + | ||
87 | + PdfAnnot(context.page, bf); | ||
88 | + } | ||
89 | +} | ||
90 | + | ||
91 | +class FlatButton extends SingleChildWidget { | ||
92 | + FlatButton({ | ||
93 | + PdfColor textColor = PdfColors.white, | ||
94 | + PdfColor color = PdfColors.blue, | ||
95 | + PdfColor colorDown = PdfColors.red, | ||
96 | + PdfColor colorRollover = PdfColors.blueAccent, | ||
97 | + EdgeInsets padding, | ||
98 | + BoxDecoration decoration, | ||
99 | + Widget child, | ||
100 | + @required this.name, | ||
101 | + }) : _childDown = Container( | ||
102 | + child: DefaultTextStyle( | ||
103 | + style: TextStyle(color: textColor), | ||
104 | + child: child, | ||
105 | + ), | ||
106 | + decoration: decoration ?? | ||
107 | + BoxDecoration( | ||
108 | + color: colorDown, | ||
109 | + borderRadiusEx: const BorderRadius.all(Radius.circular(2)), | ||
110 | + ), | ||
111 | + padding: padding ?? | ||
112 | + const EdgeInsets.symmetric(horizontal: 20, vertical: 5), | ||
113 | + ), | ||
114 | + _childRollover = Container( | ||
115 | + child: DefaultTextStyle( | ||
116 | + style: TextStyle(color: textColor), | ||
117 | + child: child, | ||
118 | + ), | ||
119 | + decoration: decoration ?? | ||
120 | + BoxDecoration( | ||
121 | + color: colorRollover, | ||
122 | + borderRadiusEx: const BorderRadius.all(Radius.circular(2)), | ||
123 | + ), | ||
124 | + padding: padding ?? | ||
125 | + const EdgeInsets.symmetric(horizontal: 20, vertical: 5), | ||
126 | + ), | ||
127 | + super( | ||
128 | + child: Container( | ||
129 | + child: DefaultTextStyle( | ||
130 | + style: TextStyle(color: textColor), | ||
131 | + child: child, | ||
132 | + ), | ||
133 | + decoration: decoration ?? | ||
134 | + BoxDecoration( | ||
135 | + color: color, | ||
136 | + borderRadiusEx: const BorderRadius.all(Radius.circular(2)), | ||
137 | + ), | ||
138 | + padding: padding ?? | ||
139 | + const EdgeInsets.symmetric(horizontal: 20, vertical: 5), | ||
140 | + ), | ||
141 | + ); | ||
142 | + | ||
143 | + // final PdfColor textColor; | ||
144 | + | ||
145 | + // final PdfColor color; | ||
146 | + | ||
147 | + // final EdgeInsets padding; | ||
148 | + | ||
149 | + final String name; | ||
150 | + | ||
151 | + final Widget _childDown; | ||
152 | + | ||
153 | + final Widget _childRollover; | ||
154 | + | ||
155 | + @override | ||
156 | + void paint(Context context) { | ||
157 | + super.paint(context); | ||
158 | + | ||
159 | + final PdfButtonField bf = PdfButtonField( | ||
160 | + rect: context.localToGlobal(box), | ||
161 | + fieldName: name, | ||
162 | + flags: <PdfAnnotFlags>{PdfAnnotFlags.print}, | ||
163 | + fieldFlags: <PdfFieldFlags>{PdfFieldFlags.pushButton}, | ||
164 | + ); | ||
165 | + | ||
166 | + final Matrix4 mat = context.canvas.getTransform(); | ||
167 | + final Vector3 translation = Vector3(0, 0, 0); | ||
168 | + final Quaternion rotation = Quaternion(0, 0, 0, 0); | ||
169 | + final Vector3 scale = Vector3(0, 0, 0); | ||
170 | + mat | ||
171 | + ..decompose(translation, rotation, scale) | ||
172 | + ..leftTranslate(-translation.x, -translation.y) | ||
173 | + ..translate(box.x, box.y); | ||
174 | + | ||
175 | + final Context cn = context.copyWith( | ||
176 | + canvas: bf.appearance(context.document, PdfAnnotApparence.normal, | ||
177 | + matrix: mat, boundingBox: box)); | ||
178 | + child.layout( | ||
179 | + cn, BoxConstraints.tightFor(width: box.width, height: box.height)); | ||
180 | + child.paint(cn); | ||
181 | + | ||
182 | + final Context cd = context.copyWith( | ||
183 | + canvas: bf.appearance(context.document, PdfAnnotApparence.down, | ||
184 | + matrix: mat, boundingBox: box)); | ||
185 | + _childDown.layout( | ||
186 | + cd, BoxConstraints.tightFor(width: box.width, height: box.height)); | ||
187 | + _childDown.paint(cd); | ||
188 | + | ||
189 | + final Context cr = context.copyWith( | ||
190 | + canvas: bf.appearance(context.document, PdfAnnotApparence.rollover, | ||
191 | + matrix: mat, boundingBox: box)); | ||
192 | + _childRollover.layout( | ||
193 | + cr, BoxConstraints.tightFor(width: box.width, height: box.height)); | ||
194 | + _childRollover.paint(cr); | ||
195 | + | ||
196 | + PdfAnnot(context.page, bf); | ||
197 | + } | ||
198 | +} |
@@ -79,6 +79,22 @@ class Context { | @@ -79,6 +79,22 @@ class Context { | ||
79 | } | 79 | } |
80 | return copyWith(inherited: inherited); | 80 | return copyWith(inherited: inherited); |
81 | } | 81 | } |
82 | + | ||
83 | + PdfRect localToGlobal(PdfRect box) { | ||
84 | + final Matrix4 mat = canvas.getTransform(); | ||
85 | + final Vector3 lt = mat.transform3(Vector3(box.left, box.bottom, 0)); | ||
86 | + final Vector3 lb = mat.transform3(Vector3(box.left, box.top, 0)); | ||
87 | + final Vector3 rt = mat.transform3(Vector3(box.right, box.bottom, 0)); | ||
88 | + final Vector3 rb = mat.transform3(Vector3(box.right, box.top, 0)); | ||
89 | + final List<double> x = <double>[lt.x, lb.x, rt.x, rb.x]; | ||
90 | + final List<double> y = <double>[lt.y, lb.y, rt.y, rb.y]; | ||
91 | + return PdfRect.fromLTRB( | ||
92 | + x.reduce(math.min), | ||
93 | + y.reduce(math.min), | ||
94 | + x.reduce(math.max), | ||
95 | + y.reduce(math.max), | ||
96 | + ); | ||
97 | + } | ||
82 | } | 98 | } |
83 | 99 | ||
84 | class Inherited { | 100 | class Inherited { |
@@ -36,6 +36,7 @@ import 'widget_chart_test.dart' as widget_chart; | @@ -36,6 +36,7 @@ import 'widget_chart_test.dart' as widget_chart; | ||
36 | import 'widget_clip_test.dart' as widget_clip; | 36 | import 'widget_clip_test.dart' as widget_clip; |
37 | import 'widget_container_test.dart' as widget_container; | 37 | import 'widget_container_test.dart' as widget_container; |
38 | import 'widget_flex_test.dart' as widget_flex; | 38 | import 'widget_flex_test.dart' as widget_flex; |
39 | +import 'widget_form_test.dart' as widget_form; | ||
39 | import 'widget_grid_view_test.dart' as widget_grid_view; | 40 | import 'widget_grid_view_test.dart' as widget_grid_view; |
40 | import 'widget_multipage_test.dart' as widget_multipage; | 41 | import 'widget_multipage_test.dart' as widget_multipage; |
41 | import 'widget_opacity_test.dart' as widget_opacity; | 42 | import 'widget_opacity_test.dart' as widget_opacity; |
@@ -68,6 +69,7 @@ void main() { | @@ -68,6 +69,7 @@ void main() { | ||
68 | widget_clip.main(); | 69 | widget_clip.main(); |
69 | widget_container.main(); | 70 | widget_container.main(); |
70 | widget_flex.main(); | 71 | widget_flex.main(); |
72 | + widget_form.main(); | ||
71 | widget_grid_view.main(); | 73 | widget_grid_view.main(); |
72 | widget_multipage.main(); | 74 | widget_multipage.main(); |
73 | widget_opacity.main(); | 75 | widget_opacity.main(); |
@@ -35,17 +35,17 @@ void main() { | @@ -35,17 +35,17 @@ void main() { | ||
35 | 35 | ||
36 | PdfAnnot( | 36 | PdfAnnot( |
37 | page, | 37 | page, |
38 | - const PdfAnnotText( | ||
39 | - rect: PdfRect(100, 100, 50, 50), | 38 | + PdfAnnotText( |
39 | + rect: const PdfRect(100, 100, 50, 50), | ||
40 | content: 'Hello', | 40 | content: 'Hello', |
41 | ), | 41 | ), |
42 | ); | 42 | ); |
43 | 43 | ||
44 | PdfAnnot( | 44 | PdfAnnot( |
45 | page, | 45 | page, |
46 | - const PdfAnnotNamedLink( | 46 | + PdfAnnotNamedLink( |
47 | dest: 'target', | 47 | dest: 'target', |
48 | - rect: PdfRect(100, 150, 50, 50), | 48 | + rect: const PdfRect(100, 150, 50, 50), |
49 | ), | 49 | ), |
50 | ); | 50 | ); |
51 | g.drawRect(100, 150, 50, 50); | 51 | g.drawRect(100, 150, 50, 50); |
@@ -53,14 +53,27 @@ void main() { | @@ -53,14 +53,27 @@ void main() { | ||
53 | 53 | ||
54 | PdfAnnot( | 54 | PdfAnnot( |
55 | page, | 55 | page, |
56 | - const PdfAnnotUrlLink( | ||
57 | - rect: PdfRect(100, 250, 50, 50), | 56 | + PdfAnnotUrlLink( |
57 | + rect: const PdfRect(100, 250, 50, 50), | ||
58 | url: 'https://github.com/DavBfr/dart_pdf/', | 58 | url: 'https://github.com/DavBfr/dart_pdf/', |
59 | ), | 59 | ), |
60 | ); | 60 | ); |
61 | g.drawRect(100, 250, 50, 50); | 61 | g.drawRect(100, 250, 50, 50); |
62 | g.strokePath(); | 62 | g.strokePath(); |
63 | 63 | ||
64 | + PdfAnnot( | ||
65 | + page, | ||
66 | + PdfTextField( | ||
67 | + rect: const PdfRect(100, 50, 50, 20), | ||
68 | + fieldName: 'test', | ||
69 | + font: PdfFont.helvetica(pdf), | ||
70 | + fontSize: 10, | ||
71 | + textColor: PdfColors.blue, | ||
72 | + ), | ||
73 | + ); | ||
74 | + // g.drawRect(100, 50, 50, 20); | ||
75 | + g.strokePath(); | ||
76 | + | ||
64 | final File file = File('annotations.pdf'); | 77 | final File file = File('annotations.pdf'); |
65 | file.writeAsBytesSync(pdf.save()); | 78 | file.writeAsBytesSync(pdf.save()); |
66 | }); | 79 | }); |
@@ -22,7 +22,8 @@ import 'dart:typed_data'; | @@ -22,7 +22,8 @@ import 'dart:typed_data'; | ||
22 | import 'package:pdf/pdf.dart'; | 22 | import 'package:pdf/pdf.dart'; |
23 | import 'package:test/test.dart'; | 23 | import 'package:test/test.dart'; |
24 | 24 | ||
25 | -void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | 25 | +void printText( |
26 | + PdfPage page, PdfGraphics canvas, String text, PdfFont font, double top) { | ||
26 | text = text + font.fontName; | 27 | text = text + font.fontName; |
27 | const double fontSize = 20; | 28 | const double fontSize = 20; |
28 | final PdfFontMetrics metrics = font.stringMetrics(text) * fontSize; | 29 | final PdfFontMetrics metrics = font.stringMetrics(text) * fontSize; |
@@ -30,7 +31,7 @@ void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | @@ -30,7 +31,7 @@ void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | ||
30 | const double deb = 5; | 31 | const double deb = 5; |
31 | 32 | ||
32 | const double x = 50; | 33 | const double x = 50; |
33 | - final double y = canvas.page.pageFormat.height - top; | 34 | + final double y = page.pageFormat.height - top; |
34 | 35 | ||
35 | canvas | 36 | canvas |
36 | ..drawRect(x + metrics.left, y + metrics.top, metrics.width, metrics.height) | 37 | ..drawRect(x + metrics.left, y + metrics.top, metrics.width, metrics.height) |
@@ -51,12 +52,13 @@ void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | @@ -51,12 +52,13 @@ void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | ||
51 | ..drawString(font, fontSize, text, x, y); | 52 | ..drawString(font, fontSize, text, x, y); |
52 | } | 53 | } |
53 | 54 | ||
54 | -void printTextTtf(PdfGraphics canvas, String text, File ttfFont, double top) { | 55 | +void printTextTtf( |
56 | + PdfPage page, PdfGraphics canvas, String text, File ttfFont, double top) { | ||
55 | final Uint8List fontData = ttfFont.readAsBytesSync(); | 57 | final Uint8List fontData = ttfFont.readAsBytesSync(); |
56 | final PdfTtfFont font = | 58 | final PdfTtfFont font = |
57 | - PdfTtfFont(canvas.page.pdfDocument, fontData.buffer.asByteData()); | 59 | + PdfTtfFont(page.pdfDocument, fontData.buffer.asByteData()); |
58 | 60 | ||
59 | - printText(canvas, text, font, top); | 61 | + printText(page, canvas, text, font, top); |
60 | } | 62 | } |
61 | 63 | ||
62 | void main() { | 64 | void main() { |
@@ -69,11 +71,12 @@ void main() { | @@ -69,11 +71,12 @@ void main() { | ||
69 | int top = 0; | 71 | int top = 0; |
70 | const String s = 'Hello Lukáča '; | 72 | const String s = 'Hello Lukáča '; |
71 | 73 | ||
72 | - printTextTtf(g, s, File('open-sans.ttf'), 30.0 + 30.0 * top++); | ||
73 | - printTextTtf(g, s, File('open-sans-bold.ttf'), 30.0 + 30.0 * top++); | ||
74 | - printTextTtf(g, s, File('roboto.ttf'), 30.0 + 30.0 * top++); | ||
75 | - printTextTtf(g, s, File('noto-sans.ttf'), 30.0 + 30.0 * top++); | ||
76 | - printTextTtf(g, '你好 檯號 ', File('genyomintw.ttf'), 30.0 + 30.0 * top++); | 74 | + printTextTtf(page, g, s, File('open-sans.ttf'), 30.0 + 30.0 * top++); |
75 | + printTextTtf(page, g, s, File('open-sans-bold.ttf'), 30.0 + 30.0 * top++); | ||
76 | + printTextTtf(page, g, s, File('roboto.ttf'), 30.0 + 30.0 * top++); | ||
77 | + printTextTtf(page, g, s, File('noto-sans.ttf'), 30.0 + 30.0 * top++); | ||
78 | + printTextTtf( | ||
79 | + page, g, '你好 檯號 ', File('genyomintw.ttf'), 30.0 + 30.0 * top++); | ||
77 | 80 | ||
78 | final File file = File('ttf.pdf'); | 81 | final File file = File('ttf.pdf'); |
79 | file.writeAsBytesSync(pdf.save()); | 82 | file.writeAsBytesSync(pdf.save()); |
@@ -21,7 +21,8 @@ import 'dart:io'; | @@ -21,7 +21,8 @@ import 'dart:io'; | ||
21 | import 'package:pdf/pdf.dart'; | 21 | import 'package:pdf/pdf.dart'; |
22 | import 'package:test/test.dart'; | 22 | import 'package:test/test.dart'; |
23 | 23 | ||
24 | -void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | 24 | +void printText( |
25 | + PdfPage page, PdfGraphics canvas, String text, PdfFont font, double top) { | ||
25 | text = text + font.fontName; | 26 | text = text + font.fontName; |
26 | const double fontSize = 20; | 27 | const double fontSize = 20; |
27 | final PdfFontMetrics metrics = font.stringMetrics(text) * fontSize; | 28 | final PdfFontMetrics metrics = font.stringMetrics(text) * fontSize; |
@@ -29,7 +30,7 @@ void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | @@ -29,7 +30,7 @@ void printText(PdfGraphics canvas, String text, PdfFont font, double top) { | ||
29 | const double deb = 5; | 30 | const double deb = 5; |
30 | 31 | ||
31 | const double x = 50; | 32 | const double x = 50; |
32 | - final double y = canvas.page.pageFormat.height - top; | 33 | + final double y = page.pageFormat.height - top; |
33 | 34 | ||
34 | canvas | 35 | canvas |
35 | ..drawRect(x + metrics.left, y + metrics.top, metrics.width, metrics.height) | 36 | ..drawRect(x + metrics.left, y + metrics.top, metrics.width, metrics.height) |
@@ -60,23 +61,24 @@ void main() { | @@ -60,23 +61,24 @@ void main() { | ||
60 | int top = 0; | 61 | int top = 0; |
61 | const String s = 'Hello '; | 62 | const String s = 'Hello '; |
62 | 63 | ||
63 | - printText(g, s, PdfFont.courier(pdf), 20.0 + 30.0 * top++); | ||
64 | - printText(g, s, PdfFont.courierBold(pdf), 20.0 + 30.0 * top++); | ||
65 | - printText(g, s, PdfFont.courierOblique(pdf), 20.0 + 30.0 * top++); | ||
66 | - printText(g, s, PdfFont.courierBoldOblique(pdf), 20.0 + 30.0 * top++); | 64 | + printText(page, g, s, PdfFont.courier(pdf), 20.0 + 30.0 * top++); |
65 | + printText(page, g, s, PdfFont.courierBold(pdf), 20.0 + 30.0 * top++); | ||
66 | + printText(page, g, s, PdfFont.courierOblique(pdf), 20.0 + 30.0 * top++); | ||
67 | + printText(page, g, s, PdfFont.courierBoldOblique(pdf), 20.0 + 30.0 * top++); | ||
67 | 68 | ||
68 | - printText(g, s, PdfFont.helvetica(pdf), 20.0 + 30.0 * top++); | ||
69 | - printText(g, s, PdfFont.helveticaBold(pdf), 20.0 + 30.0 * top++); | ||
70 | - printText(g, s, PdfFont.helveticaOblique(pdf), 20.0 + 30.0 * top++); | ||
71 | - printText(g, s, PdfFont.helveticaBoldOblique(pdf), 20.0 + 30.0 * top++); | 69 | + printText(page, g, s, PdfFont.helvetica(pdf), 20.0 + 30.0 * top++); |
70 | + printText(page, g, s, PdfFont.helveticaBold(pdf), 20.0 + 30.0 * top++); | ||
71 | + printText(page, g, s, PdfFont.helveticaOblique(pdf), 20.0 + 30.0 * top++); | ||
72 | + printText( | ||
73 | + page, g, s, PdfFont.helveticaBoldOblique(pdf), 20.0 + 30.0 * top++); | ||
72 | 74 | ||
73 | - printText(g, s, PdfFont.times(pdf), 20.0 + 30.0 * top++); | ||
74 | - printText(g, s, PdfFont.timesBold(pdf), 20.0 + 30.0 * top++); | ||
75 | - printText(g, s, PdfFont.timesItalic(pdf), 20.0 + 30.0 * top++); | ||
76 | - printText(g, s, PdfFont.timesBoldItalic(pdf), 20.0 + 30.0 * top++); | 75 | + printText(page, g, s, PdfFont.times(pdf), 20.0 + 30.0 * top++); |
76 | + printText(page, g, s, PdfFont.timesBold(pdf), 20.0 + 30.0 * top++); | ||
77 | + printText(page, g, s, PdfFont.timesItalic(pdf), 20.0 + 30.0 * top++); | ||
78 | + printText(page, g, s, PdfFont.timesBoldItalic(pdf), 20.0 + 30.0 * top++); | ||
77 | 79 | ||
78 | - printText(g, s, PdfFont.symbol(pdf), 20.0 + 30.0 * top++); | ||
79 | - printText(g, s, PdfFont.zapfDingbats(pdf), 20.0 + 30.0 * top++); | 80 | + printText(page, g, s, PdfFont.symbol(pdf), 20.0 + 30.0 * top++); |
81 | + printText(page, g, s, PdfFont.zapfDingbats(pdf), 20.0 + 30.0 * top++); | ||
80 | 82 | ||
81 | final File file = File('type1.pdf'); | 83 | final File file = File('type1.pdf'); |
82 | file.writeAsBytesSync(pdf.save()); | 84 | file.writeAsBytesSync(pdf.save()); |
pdf/test/widget_form_test.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: omit_local_variable_types | ||
18 | + | ||
19 | +import 'dart:io'; | ||
20 | + | ||
21 | +import 'package:test/test.dart'; | ||
22 | +import 'package:pdf/pdf.dart'; | ||
23 | +import 'package:pdf/widgets.dart'; | ||
24 | + | ||
25 | +Document pdf; | ||
26 | + | ||
27 | +class Label extends StatelessWidget { | ||
28 | + Label({this.label, this.width}); | ||
29 | + | ||
30 | + final String label; | ||
31 | + | ||
32 | + final double width; | ||
33 | + | ||
34 | + @override | ||
35 | + Widget build(Context context) { | ||
36 | + return Container( | ||
37 | + child: Text(label), | ||
38 | + width: width, | ||
39 | + alignment: Alignment.centerRight, | ||
40 | + margin: const EdgeInsets.only(right: 5), | ||
41 | + ); | ||
42 | + } | ||
43 | +} | ||
44 | + | ||
45 | +class Decorated extends StatelessWidget { | ||
46 | + Decorated({this.child, this.color}); | ||
47 | + | ||
48 | + final Widget child; | ||
49 | + | ||
50 | + final PdfColor color; | ||
51 | + | ||
52 | + @override | ||
53 | + Widget build(Context context) { | ||
54 | + return Container( | ||
55 | + child: child, | ||
56 | + padding: const EdgeInsets.all(2), | ||
57 | + decoration: BoxDecoration( | ||
58 | + color: color ?? PdfColors.yellow100, | ||
59 | + border: const BoxBorder( | ||
60 | + left: true, | ||
61 | + top: true, | ||
62 | + right: true, | ||
63 | + bottom: true, | ||
64 | + color: PdfColors.grey, | ||
65 | + width: .5, | ||
66 | + ), | ||
67 | + ), | ||
68 | + ); | ||
69 | + } | ||
70 | +} | ||
71 | + | ||
72 | +void main() { | ||
73 | + setUpAll(() { | ||
74 | + // Document.debug = true; | ||
75 | + pdf = Document(); | ||
76 | + }); | ||
77 | + | ||
78 | + test( | ||
79 | + 'Form', | ||
80 | + () { | ||
81 | + pdf.addPage( | ||
82 | + Page( | ||
83 | + build: (Context context) => Wrap( | ||
84 | + crossAxisAlignment: WrapCrossAlignment.center, | ||
85 | + children: <Widget>[ | ||
86 | + Label(label: 'Given Name:', width: 100), | ||
87 | + Decorated( | ||
88 | + child: TextField( | ||
89 | + name: 'Given Name', | ||
90 | + textStyle: const TextStyle(color: PdfColors.amber), | ||
91 | + )), | ||
92 | + // | ||
93 | + SizedBox(width: double.infinity, height: 10), | ||
94 | + // | ||
95 | + Label(label: 'Family Name:', width: 100), | ||
96 | + Decorated(child: TextField(name: 'Family Name')), | ||
97 | + // | ||
98 | + SizedBox(width: double.infinity, height: 10), | ||
99 | + // | ||
100 | + Label(label: 'Address:', width: 100), | ||
101 | + Decorated(child: TextField(name: 'Address')), | ||
102 | + // | ||
103 | + SizedBox(width: double.infinity, height: 10), | ||
104 | + // | ||
105 | + Label(label: 'Postcode:', width: 100), | ||
106 | + Decorated( | ||
107 | + child: TextField(name: 'Postcode', width: 60, maxLength: 6)), | ||
108 | + // | ||
109 | + Label(label: 'City:', width: 30), | ||
110 | + Decorated(child: TextField(name: 'City')), | ||
111 | + // | ||
112 | + SizedBox(width: double.infinity, height: 10), | ||
113 | + // | ||
114 | + Label(label: 'Country:', width: 100), | ||
115 | + Decorated( | ||
116 | + child: TextField( | ||
117 | + name: 'Country', | ||
118 | + color: PdfColors.blue, | ||
119 | + )), | ||
120 | + // | ||
121 | + SizedBox(width: double.infinity, height: 10), | ||
122 | + // | ||
123 | + Label(label: 'Checkbox:', width: 100), | ||
124 | + Checkbox( | ||
125 | + name: 'Checkbox', | ||
126 | + value: true, | ||
127 | + defaultValue: true, | ||
128 | + ), | ||
129 | + // | ||
130 | + SizedBox(width: double.infinity, height: 10), | ||
131 | + // | ||
132 | + Transform.rotateBox( | ||
133 | + angle: .7, | ||
134 | + child: FlatButton( | ||
135 | + name: 'submit', | ||
136 | + child: Text('Submit'), | ||
137 | + ), | ||
138 | + ) | ||
139 | + ], | ||
140 | + ), | ||
141 | + ), | ||
142 | + ); | ||
143 | + }, | ||
144 | + ); | ||
145 | + | ||
146 | + tearDownAll(() { | ||
147 | + final File file = File('widgets-form.pdf'); | ||
148 | + file.writeAsBytesSync(pdf.save()); | ||
149 | + }); | ||
150 | +} |
test/golden/widgets-form.pdf
0 → 100644
No preview for this file type
-
Please register or login to post a comment