John Harris
Committed by David PHAM-VAN

Add more annotations

1 # Changelog 1 # Changelog
2 2
  3 +## 3.5.0
  4 +
  5 +- Add annotations [John Harris]
  6 +
3 ## 3.4.2 7 ## 3.4.2
4 8
5 - Revert dart format 9 - Revert dart format
@@ -99,6 +99,8 @@ abstract class PdfAnnotBase { @@ -99,6 +99,8 @@ abstract class PdfAnnotBase {
99 this.flags, 99 this.flags,
100 this.date, 100 this.date,
101 this.color, 101 this.color,
  102 + this.subject,
  103 + this.author,
102 }); 104 });
103 105
104 /// The subtype of the outline, ie text, note, etc 106 /// The subtype of the outline, ie text, note, etc
@@ -115,6 +117,12 @@ abstract class PdfAnnotBase { @@ -115,6 +117,12 @@ abstract class PdfAnnotBase {
115 /// The internal name for a link 117 /// The internal name for a link
116 final String? name; 118 final String? name;
117 119
  120 + /// The author of the annotation
  121 + final String? author;
  122 +
  123 + /// The subject of the annotation
  124 + final String? subject;
  125 +
118 /// Flags specifying various characteristics of the annotation 126 /// Flags specifying various characteristics of the annotation
119 final Set<PdfAnnotFlags>? flags; 127 final Set<PdfAnnotFlags>? flags;
120 128
@@ -223,6 +231,14 @@ abstract class PdfAnnotBase { @@ -223,6 +231,14 @@ abstract class PdfAnnotBase {
223 params['/C'] = PdfColorType(color!); 231 params['/C'] = PdfColorType(color!);
224 } 232 }
225 233
  234 + if (subject != null) {
  235 + params['/Subj'] = PdfSecString.fromString(object, subject!);
  236 + }
  237 +
  238 + if (author != null) {
  239 + params['/T'] = PdfSecString.fromString(object, author!);
  240 + }
  241 +
226 if (_appearances.isNotEmpty) { 242 if (_appearances.isNotEmpty) {
227 params['/AP'] = PdfDict(_appearances); 243 params['/AP'] = PdfDict(_appearances);
228 if (_as != null) { 244 if (_as != null) {
@@ -242,6 +258,8 @@ class PdfAnnotText extends PdfAnnotBase { @@ -242,6 +258,8 @@ class PdfAnnotText extends PdfAnnotBase {
242 Set<PdfAnnotFlags>? flags, 258 Set<PdfAnnotFlags>? flags,
243 DateTime? date, 259 DateTime? date,
244 PdfColor? color, 260 PdfColor? color,
  261 + String? subject,
  262 + String? author,
245 }) : super( 263 }) : super(
246 subtype: '/Text', 264 subtype: '/Text',
247 rect: rect, 265 rect: rect,
@@ -251,6 +269,8 @@ class PdfAnnotText extends PdfAnnotBase { @@ -251,6 +269,8 @@ class PdfAnnotText extends PdfAnnotBase {
251 flags: flags, 269 flags: flags,
252 date: date, 270 date: date,
253 color: color, 271 color: color,
  272 + subject: subject,
  273 + author: author,
254 ); 274 );
255 } 275 }
256 276
@@ -263,6 +283,8 @@ class PdfAnnotNamedLink extends PdfAnnotBase { @@ -263,6 +283,8 @@ class PdfAnnotNamedLink extends PdfAnnotBase {
263 Set<PdfAnnotFlags>? flags, 283 Set<PdfAnnotFlags>? flags,
264 DateTime? date, 284 DateTime? date,
265 PdfColor? color, 285 PdfColor? color,
  286 + String? subject,
  287 + String? author,
266 }) : super( 288 }) : super(
267 subtype: '/Link', 289 subtype: '/Link',
268 rect: rect, 290 rect: rect,
@@ -270,6 +292,8 @@ class PdfAnnotNamedLink extends PdfAnnotBase { @@ -270,6 +292,8 @@ class PdfAnnotNamedLink extends PdfAnnotBase {
270 flags: flags, 292 flags: flags,
271 date: date, 293 date: date,
272 color: color, 294 color: color,
  295 + subject: subject,
  296 + author: author,
273 ); 297 );
274 298
275 final String dest; 299 final String dest;
@@ -295,6 +319,8 @@ class PdfAnnotUrlLink extends PdfAnnotBase { @@ -295,6 +319,8 @@ class PdfAnnotUrlLink extends PdfAnnotBase {
295 Set<PdfAnnotFlags>? flags, 319 Set<PdfAnnotFlags>? flags,
296 DateTime? date, 320 DateTime? date,
297 PdfColor? color, 321 PdfColor? color,
  322 + String? subject,
  323 + String? author,
298 }) : super( 324 }) : super(
299 subtype: '/Link', 325 subtype: '/Link',
300 rect: rect, 326 rect: rect,
@@ -302,6 +328,8 @@ class PdfAnnotUrlLink extends PdfAnnotBase { @@ -302,6 +328,8 @@ class PdfAnnotUrlLink extends PdfAnnotBase {
302 flags: flags, 328 flags: flags,
303 date: date, 329 date: date,
304 color: color, 330 color: color,
  331 + subject: subject,
  332 + author: author,
305 ); 333 );
306 334
307 final String url; 335 final String url;
@@ -318,6 +346,177 @@ class PdfAnnotUrlLink extends PdfAnnotBase { @@ -318,6 +346,177 @@ class PdfAnnotUrlLink extends PdfAnnotBase {
318 } 346 }
319 } 347 }
320 348
  349 +class PdfAnnotSquare extends PdfAnnotBase {
  350 + /// Create an Square annotation
  351 + PdfAnnotSquare({
  352 + required PdfRect rect,
  353 + PdfBorder? border,
  354 + Set<PdfAnnotFlags>? flags,
  355 + DateTime? date,
  356 + PdfColor? color,
  357 + this.interiorColor,
  358 + String? subject,
  359 + String? author,
  360 + }) : super(
  361 + subtype: '/Square',
  362 + rect: rect,
  363 + border: border,
  364 + flags: flags,
  365 + date: date,
  366 + color: color,
  367 + subject: subject,
  368 + author: author,
  369 + );
  370 +
  371 + final PdfColor? interiorColor;
  372 +
  373 + @override
  374 + void build(PdfPage page, PdfObject object, PdfDict params) {
  375 + super.build(page, object, params);
  376 + if (interiorColor != null) {
  377 + params['/IC'] = PdfColorType(interiorColor!);
  378 + }
  379 + }
  380 +}
  381 +
  382 +class PdfAnnotCircle extends PdfAnnotBase {
  383 + /// Create an Circle annotation
  384 + PdfAnnotCircle({
  385 + required PdfRect rect,
  386 + PdfBorder? border,
  387 + Set<PdfAnnotFlags>? flags,
  388 + DateTime? date,
  389 + PdfColor? color,
  390 + this.interiorColor,
  391 + String? subject,
  392 + String? author,
  393 + }) : super(
  394 + subtype: '/Circle',
  395 + rect: rect,
  396 + border: border,
  397 + flags: flags,
  398 + date: date,
  399 + color: color,
  400 + subject: subject,
  401 + author: author,
  402 + );
  403 +
  404 + final PdfColor? interiorColor;
  405 +
  406 + @override
  407 + void build(PdfPage page, PdfObject object, PdfDict params) {
  408 + super.build(page, object, params);
  409 + if (interiorColor != null) {
  410 + params['/IC'] = PdfColorType(interiorColor!);
  411 + }
  412 + }
  413 +}
  414 +
  415 +class PdfAnnotPolygon extends PdfAnnotBase {
  416 + /// Create an Polygon annotation
  417 + PdfAnnotPolygon(this.document, this.points,
  418 + {required PdfRect rect,
  419 + PdfBorder? border,
  420 + Set<PdfAnnotFlags>? flags,
  421 + DateTime? date,
  422 + PdfColor? color,
  423 + this.interiorColor,
  424 + String? subject,
  425 + String? author,
  426 + bool closed = true})
  427 + : super(
  428 + subtype: closed ? '/PolyLine' : '/Polygon',
  429 + rect: rect,
  430 + border: border,
  431 + flags: flags,
  432 + date: date,
  433 + color: color,
  434 + subject: subject,
  435 + author: author,
  436 + );
  437 +
  438 + final PdfDocument document;
  439 +
  440 + final List<PdfPoint> points;
  441 +
  442 + final PdfColor? interiorColor;
  443 +
  444 + @override
  445 + void build(PdfPage page, PdfObject object, PdfDict params) {
  446 + super.build(page, object, params);
  447 +
  448 + // Flip the points on the Y axis.
  449 + final flippedPoints =
  450 + points.map((e) => PdfPoint(e.x, rect.height - e.y)).toList();
  451 +
  452 + final verticies = <num>[];
  453 + for (var i = 0; i < flippedPoints.length; i++) {
  454 + verticies.add(flippedPoints[i].x);
  455 + verticies.add(flippedPoints[i].y);
  456 + }
  457 +
  458 + params['/Vertices'] = PdfArray.fromNum(verticies);
  459 +
  460 + if (interiorColor != null) {
  461 + params['/IC'] = PdfColorType(interiorColor!);
  462 + }
  463 + }
  464 +}
  465 +
  466 +class PdfAnnotInk extends PdfAnnotBase {
  467 + /// Create an Ink List annotation
  468 + PdfAnnotInk(
  469 + this.document,
  470 + this.points, {
  471 + required PdfRect rect,
  472 + PdfBorder? border,
  473 + Set<PdfAnnotFlags>? flags,
  474 + DateTime? date,
  475 + PdfColor? color,
  476 + String? subject,
  477 + String? author,
  478 + String? content,
  479 + }) : super(
  480 + subtype: '/Ink',
  481 + rect: rect,
  482 + border: border,
  483 + flags: flags,
  484 + date: date,
  485 + color: color,
  486 + subject: subject,
  487 + author: author,
  488 + content: content,
  489 + );
  490 +
  491 + final PdfDocument document;
  492 +
  493 + final List<List<PdfPoint>> points;
  494 +
  495 + @override
  496 + void build(
  497 + PdfPage page,
  498 + PdfObject object,
  499 + PdfDict params,
  500 + ) {
  501 + super.build(page, object, params);
  502 +
  503 + final verticies = List<List<num>>.filled(points.length, <num>[]);
  504 + for (var listIndex = 0; listIndex < points.length; listIndex++) {
  505 + // Flip the points on the Y axis.
  506 + final flippedPoints = points[listIndex]
  507 + .map((e) => PdfPoint(e.x, rect.height - e.y))
  508 + .toList();
  509 + for (var i = 0; i < flippedPoints.length; i++) {
  510 + verticies[listIndex].add(flippedPoints[i].x);
  511 + verticies[listIndex].add(flippedPoints[i].y);
  512 + }
  513 + }
  514 +
  515 + params['/InkList'] =
  516 + PdfArray(verticies.map((v) => PdfArray.fromNum(v)).toList());
  517 + }
  518 +}
  519 +
321 enum PdfAnnotHighlighting { none, invert, outline, push, toggle } 520 enum PdfAnnotHighlighting { none, invert, outline, push, toggle }
322 521
323 abstract class PdfAnnotWidget extends PdfAnnotBase { 522 abstract class PdfAnnotWidget extends PdfAnnotBase {
@@ -332,6 +531,8 @@ abstract class PdfAnnotWidget extends PdfAnnotBase { @@ -332,6 +531,8 @@ abstract class PdfAnnotWidget extends PdfAnnotBase {
332 PdfColor? color, 531 PdfColor? color,
333 this.backgroundColor, 532 this.backgroundColor,
334 this.highlighting, 533 this.highlighting,
  534 + String? subject,
  535 + String? author,
335 }) : super( 536 }) : super(
336 subtype: '/Widget', 537 subtype: '/Widget',
337 rect: rect, 538 rect: rect,
@@ -339,6 +540,8 @@ abstract class PdfAnnotWidget extends PdfAnnotBase { @@ -339,6 +540,8 @@ abstract class PdfAnnotWidget extends PdfAnnotBase {
339 flags: flags, 540 flags: flags,
340 date: date, 541 date: date,
341 color: color, 542 color: color,
  543 + subject: subject,
  544 + author: author,
342 ); 545 );
343 546
344 final String fieldType; 547 final String fieldType;
@@ -531,6 +734,8 @@ class PdfFormField extends PdfAnnotWidget { @@ -531,6 +734,8 @@ class PdfFormField extends PdfAnnotWidget {
531 PdfBorder? border, 734 PdfBorder? border,
532 Set<PdfAnnotFlags>? flags, 735 Set<PdfAnnotFlags>? flags,
533 DateTime? date, 736 DateTime? date,
  737 + String? subject,
  738 + String? author,
534 PdfColor? color, 739 PdfColor? color,
535 PdfColor? backgroundColor, 740 PdfColor? backgroundColor,
536 PdfAnnotHighlighting? highlighting, 741 PdfAnnotHighlighting? highlighting,
@@ -542,6 +747,8 @@ class PdfFormField extends PdfAnnotWidget { @@ -542,6 +747,8 @@ class PdfFormField extends PdfAnnotWidget {
542 border: border, 747 border: border,
543 flags: flags, 748 flags: flags,
544 date: date, 749 date: date,
  750 + subject: subject,
  751 + author: author,
545 backgroundColor: backgroundColor, 752 backgroundColor: backgroundColor,
546 color: color, 753 color: color,
547 highlighting: highlighting, 754 highlighting: highlighting,
@@ -588,6 +795,8 @@ class PdfTextField extends PdfFormField { @@ -588,6 +795,8 @@ class PdfTextField extends PdfFormField {
588 PdfBorder? border, 795 PdfBorder? border,
589 Set<PdfAnnotFlags>? flags, 796 Set<PdfAnnotFlags>? flags,
590 DateTime? date, 797 DateTime? date,
  798 + String? subject,
  799 + String? author,
591 PdfColor? color, 800 PdfColor? color,
592 PdfColor? backgroundColor, 801 PdfColor? backgroundColor,
593 PdfAnnotHighlighting? highlighting, 802 PdfAnnotHighlighting? highlighting,
@@ -606,6 +815,8 @@ class PdfTextField extends PdfFormField { @@ -606,6 +815,8 @@ class PdfTextField extends PdfFormField {
606 border: border, 815 border: border,
607 flags: flags, 816 flags: flags,
608 date: date, 817 date: date,
  818 + subject: subject,
  819 + author: author,
609 color: color, 820 color: color,
610 backgroundColor: backgroundColor, 821 backgroundColor: backgroundColor,
611 highlighting: highlighting, 822 highlighting: highlighting,
@@ -57,4 +57,13 @@ class PdfRect { @@ -57,4 +57,13 @@ class PdfRect {
57 PdfPoint get topRight => PdfPoint(right, y); 57 PdfPoint get topRight => PdfPoint(right, y);
58 PdfPoint get bottomLeft => PdfPoint(x, top); 58 PdfPoint get bottomLeft => PdfPoint(x, top);
59 PdfPoint get bottomRight => PdfPoint(right, top); 59 PdfPoint get bottomRight => PdfPoint(right, top);
  60 +
  61 + /// Returns a new rectangle with edges moved outwards by the given delta.
  62 + PdfRect inflate(double delta) {
  63 + return PdfRect.fromLTRB(
  64 + left - delta, top - delta, right + delta, bottom + delta);
  65 + }
  66 +
  67 + /// Returns a new rectangle with edges moved inwards by the given delta.
  68 + PdfRect deflate(double delta) => inflate(-delta);
60 } 69 }
@@ -14,10 +14,13 @@ @@ -14,10 +14,13 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
  17 +import 'dart:math';
  18 +
17 import 'package:pdf/pdf.dart'; 19 import 'package:pdf/pdf.dart';
18 import 'package:vector_math/vector_math_64.dart'; 20 import 'package:vector_math/vector_math_64.dart';
19 21
20 import 'geometry.dart'; 22 import 'geometry.dart';
  23 +import 'shape.dart';
21 import 'text_style.dart'; 24 import 'text_style.dart';
22 import 'theme.dart'; 25 import 'theme.dart';
23 import 'widget.dart'; 26 import 'widget.dart';
@@ -63,7 +66,7 @@ class Anchor extends SingleChildWidget { @@ -63,7 +66,7 @@ class Anchor extends SingleChildWidget {
63 } 66 }
64 67
65 abstract class AnnotationBuilder { 68 abstract class AnnotationBuilder {
66 - void build(Context context, PdfRect? box); 69 + PdfAnnot build(Context context, PdfRect? box);
67 } 70 }
68 71
69 class AnnotationLink extends AnnotationBuilder { 72 class AnnotationLink extends AnnotationBuilder {
@@ -72,8 +75,8 @@ class AnnotationLink extends AnnotationBuilder { @@ -72,8 +75,8 @@ class AnnotationLink extends AnnotationBuilder {
72 final String destination; 75 final String destination;
73 76
74 @override 77 @override
75 - void build(Context context, PdfRect? box) {  
76 - PdfAnnot( 78 + PdfAnnot build(Context context, PdfRect? box) {
  79 + return PdfAnnot(
77 context.page, 80 context.page,
78 PdfAnnotNamedLink( 81 PdfAnnotNamedLink(
79 rect: context.localToGlobal(box!), 82 rect: context.localToGlobal(box!),
@@ -84,28 +87,244 @@ class AnnotationLink extends AnnotationBuilder { @@ -84,28 +87,244 @@ class AnnotationLink extends AnnotationBuilder {
84 } 87 }
85 88
86 class AnnotationUrl extends AnnotationBuilder { 89 class AnnotationUrl extends AnnotationBuilder {
87 - AnnotationUrl(this.destination); 90 + AnnotationUrl(
  91 + this.destination, {
  92 + this.date,
  93 + this.subject,
  94 + this.author,
  95 + });
88 96
89 final String destination; 97 final String destination;
90 98
  99 + final DateTime? date;
  100 +
  101 + final String? author;
  102 +
  103 + final String? subject;
  104 +
91 @override 105 @override
92 - void build(Context context, PdfRect? box) {  
93 - PdfAnnot( 106 + PdfAnnot build(Context context, PdfRect? box) {
  107 + return PdfAnnot(
94 context.page, 108 context.page,
95 PdfAnnotUrlLink( 109 PdfAnnotUrlLink(
96 rect: context.localToGlobal(box!), 110 rect: context.localToGlobal(box!),
97 url: destination, 111 url: destination,
  112 + date: date,
  113 + author: author,
  114 + subject: subject,
  115 + ),
  116 + );
  117 + }
  118 +}
  119 +
  120 +class AnnotationSquare extends AnnotationBuilder {
  121 + AnnotationSquare({
  122 + this.color,
  123 + this.interiorColor,
  124 + this.border,
  125 + this.author,
  126 + this.date,
  127 + this.subject,
  128 + this.content,
  129 + });
  130 +
  131 + final PdfColor? color;
  132 +
  133 + final PdfColor? interiorColor;
  134 +
  135 + final PdfBorder? border;
  136 +
  137 + final String? author;
  138 +
  139 + final DateTime? date;
  140 +
  141 + final String? subject;
  142 +
  143 + final String? content;
  144 +
  145 + @override
  146 + PdfAnnot build(Context context, PdfRect? box) {
  147 + return PdfAnnot(
  148 + context.page,
  149 + PdfAnnotSquare(
  150 + rect: context.localToGlobal(box!),
  151 + border: border,
  152 + color: color,
  153 + interiorColor: interiorColor,
  154 + date: date,
  155 + author: author,
  156 + subject: subject,
98 ), 157 ),
99 ); 158 );
100 } 159 }
101 } 160 }
102 161
  162 +class AnnotationCircle extends AnnotationBuilder {
  163 + AnnotationCircle({
  164 + this.color,
  165 + this.interiorColor,
  166 + this.border,
  167 + this.author,
  168 + this.date,
  169 + this.subject,
  170 + this.content,
  171 + });
  172 +
  173 + final PdfColor? color;
  174 +
  175 + final PdfColor? interiorColor;
  176 +
  177 + final PdfBorder? border;
  178 +
  179 + final String? author;
  180 +
  181 + final DateTime? date;
  182 +
  183 + final String? subject;
  184 +
  185 + final String? content;
  186 +
  187 + @override
  188 + PdfAnnot build(Context context, PdfRect? box) {
  189 + return PdfAnnot(
  190 + context.page,
  191 + PdfAnnotCircle(
  192 + rect: context.localToGlobal(box!),
  193 + border: border,
  194 + color: color,
  195 + interiorColor: interiorColor,
  196 + date: date,
  197 + author: author,
  198 + subject: subject,
  199 + ),
  200 + );
  201 + }
  202 +}
  203 +
  204 +class AnnotationPolygon extends AnnotationBuilder {
  205 + AnnotationPolygon(
  206 + this.points, {
  207 + this.color,
  208 + this.interiorColor,
  209 + this.border,
  210 + this.author,
  211 + this.date,
  212 + this.subject,
  213 + this.content,
  214 + });
  215 +
  216 + final List<PdfPoint> points;
  217 +
  218 + final PdfColor? color;
  219 +
  220 + final PdfColor? interiorColor;
  221 +
  222 + final PdfBorder? border;
  223 +
  224 + final String? author;
  225 +
  226 + final DateTime? date;
  227 +
  228 + final String? subject;
  229 +
  230 + final String? content;
  231 +
  232 + @override
  233 + PdfAnnot build(Context context, PdfRect? box) {
  234 + final globalPoints =
  235 + points.map((e) => context.localToGlobalPoint(e)).toList();
  236 +
  237 + final rect = context.localToGlobal(PdfRect(
  238 + points.map((point) => point.x).reduce(min),
  239 + points.map((point) => point.y).reduce(min),
  240 + points.map((point) => point.x).reduce(max) -
  241 + points.map((point) => point.x).reduce(min),
  242 + points.map((point) => point.y).reduce(max) -
  243 + points.map((point) => point.y).reduce(min)));
  244 +
  245 + final pdfAnnotPolygon = PdfAnnotPolygon(
  246 + context.document,
  247 + globalPoints,
  248 + rect: rect,
  249 + border: border,
  250 + color: color,
  251 + interiorColor: interiorColor,
  252 + date: date,
  253 + author: author,
  254 + subject: subject,
  255 + );
  256 +
  257 + return PdfAnnot(context.page, pdfAnnotPolygon);
  258 + }
  259 +}
  260 +
  261 +class AnnotationInk extends AnnotationBuilder {
  262 + AnnotationInk(
  263 + this.points, {
  264 + this.color,
  265 + this.border,
  266 + this.author,
  267 + this.date,
  268 + this.subject,
  269 + this.content,
  270 + });
  271 +
  272 + final List<List<PdfPoint>> points;
  273 +
  274 + final PdfColor? color;
  275 +
  276 + final PdfBorder? border;
  277 +
  278 + final String? author;
  279 +
  280 + final DateTime? date;
  281 +
  282 + final String? subject;
  283 +
  284 + final String? content;
  285 +
  286 + @override
  287 + PdfAnnot build(Context context, PdfRect? box) {
  288 + final globalPoints = points
  289 + .map((pList) => pList
  290 + .map((e) => context.localToGlobalPoint(e))
  291 + .toList(growable: false))
  292 + .toList(growable: false);
  293 +
  294 + final allPoints =
  295 + points.expand((pointList) => pointList).toList(growable: false);
  296 +
  297 + final minX = allPoints.map((point) => point.x).reduce(min);
  298 + final minY = allPoints.map((point) => point.y).reduce(min);
  299 + final maxX = allPoints.map((point) => point.x).reduce(max);
  300 + final maxY = allPoints.map((point) => point.y).reduce(max);
  301 + final rect =
  302 + context.localToGlobal(PdfRect(minX, minY, maxX - minX, maxY - minY));
  303 +
  304 + final pdfAnnotInk = PdfAnnotInk(
  305 + context.document,
  306 + globalPoints,
  307 + rect: rect,
  308 + border: border,
  309 + color: color,
  310 + date: date,
  311 + author: author,
  312 + subject: subject,
  313 + content: content,
  314 + );
  315 +
  316 + return PdfAnnot(context.page, pdfAnnotInk);
  317 + }
  318 +}
  319 +
103 class AnnotationTextField extends AnnotationBuilder { 320 class AnnotationTextField extends AnnotationBuilder {
104 AnnotationTextField({ 321 AnnotationTextField({
105 this.name, 322 this.name,
106 this.border, 323 this.border,
107 this.flags, 324 this.flags,
108 this.date, 325 this.date,
  326 + this.subject,
  327 + this.author,
109 this.color, 328 this.color,
110 this.backgroundColor, 329 this.backgroundColor,
111 this.highlighting, 330 this.highlighting,
@@ -146,11 +365,15 @@ class AnnotationTextField extends AnnotationBuilder { @@ -146,11 +365,15 @@ class AnnotationTextField extends AnnotationBuilder {
146 365
147 final Set<PdfFieldFlags>? fieldFlags; 366 final Set<PdfFieldFlags>? fieldFlags;
148 367
  368 + final String? author;
  369 +
  370 + final String? subject;
  371 +
149 @override 372 @override
150 - void build(Context context, PdfRect? box) { 373 + PdfAnnot build(Context context, PdfRect? box) {
151 final _textStyle = Theme.of(context).defaultTextStyle.merge(textStyle); 374 final _textStyle = Theme.of(context).defaultTextStyle.merge(textStyle);
152 375
153 - PdfAnnot( 376 + return PdfAnnot(
154 context.page, 377 context.page,
155 PdfTextField( 378 PdfTextField(
156 rect: context.localToGlobal(box!), 379 rect: context.localToGlobal(box!),
@@ -158,6 +381,8 @@ class AnnotationTextField extends AnnotationBuilder { @@ -158,6 +381,8 @@ class AnnotationTextField extends AnnotationBuilder {
158 border: border, 381 border: border,
159 flags: flags, 382 flags: flags,
160 date: date, 383 date: date,
  384 + author: author,
  385 + subject: subject,
161 color: color, 386 color: color,
162 backgroundColor: backgroundColor, 387 backgroundColor: backgroundColor,
163 highlighting: highlighting, 388 highlighting: highlighting,
@@ -208,6 +433,148 @@ class UrlLink extends Annotation { @@ -208,6 +433,148 @@ class UrlLink extends Annotation {
208 }) : super(child: child, builder: AnnotationUrl(destination)); 433 }) : super(child: child, builder: AnnotationUrl(destination));
209 } 434 }
210 435
  436 +class SquareAnnotation extends Annotation {
  437 + SquareAnnotation({
  438 + Widget? child,
  439 + PdfColor? color,
  440 + PdfColor? interiorColor,
  441 + PdfBorder? border,
  442 + String? author,
  443 + DateTime? date,
  444 + String? subject,
  445 + String? content,
  446 + }) : super(
  447 + child: child ??
  448 + Rectangle(
  449 + fillColor: interiorColor,
  450 + strokeWidth: border?.width ?? 1.0,
  451 + strokeColor: color),
  452 + builder: AnnotationSquare(
  453 + color: color,
  454 + interiorColor: interiorColor,
  455 + border: border,
  456 + author: author,
  457 + date: date,
  458 + content: content,
  459 + subject: subject,
  460 + ),
  461 + );
  462 +}
  463 +
  464 +class CircleAnnotation extends Annotation {
  465 + CircleAnnotation({
  466 + Widget? child,
  467 + PdfColor? color,
  468 + PdfColor? interiorColor,
  469 + PdfBorder? border,
  470 + String? author,
  471 + DateTime? date,
  472 + String? subject,
  473 + String? content,
  474 + }) : super(
  475 + child: child ??
  476 + Circle(
  477 + fillColor: interiorColor,
  478 + strokeWidth: border?.width ?? 1.0,
  479 + strokeColor: color),
  480 + builder: AnnotationCircle(
  481 + color: color,
  482 + interiorColor: interiorColor,
  483 + border: border,
  484 + author: author,
  485 + date: date,
  486 + content: content,
  487 + subject: subject,
  488 + ),
  489 + );
  490 +}
  491 +
  492 +class PolygonAnnotation extends Annotation {
  493 + PolygonAnnotation({
  494 + required List<PdfPoint> points,
  495 + Widget? child,
  496 + PdfColor? color,
  497 + PdfColor? interiorColor,
  498 + PdfBorder? border,
  499 + String? author,
  500 + DateTime? date,
  501 + String? subject,
  502 + String? content,
  503 + }) : super(
  504 + child: child ??
  505 + Polygon(
  506 + points: points,
  507 + strokeColor: color,
  508 + fillColor: interiorColor,
  509 + strokeWidth: border?.width ?? 1.0),
  510 + builder: AnnotationPolygon(
  511 + points,
  512 + color: color,
  513 + interiorColor: interiorColor,
  514 + border: border,
  515 + author: author,
  516 + date: date,
  517 + content: content,
  518 + subject: subject,
  519 + ),
  520 + );
  521 +}
  522 +
  523 +class PolyLineAnnotation extends Annotation {
  524 + PolyLineAnnotation({
  525 + required List<PdfPoint> points,
  526 + PdfColor? color,
  527 + PdfBorder? border,
  528 + String? author,
  529 + DateTime? date,
  530 + String? content,
  531 + String? subject,
  532 + }) : super(
  533 + child: Polygon(
  534 + points: points,
  535 + strokeColor: color,
  536 + close: false,
  537 + strokeWidth: border?.width ?? 1.0),
  538 + builder: AnnotationPolygon(
  539 + points,
  540 + color: color,
  541 + border: border,
  542 + author: author,
  543 + date: date,
  544 + content: content,
  545 + subject: subject,
  546 + ),
  547 + );
  548 +}
  549 +
  550 +class InkAnnotation extends Annotation {
  551 + InkAnnotation({
  552 + required List<List<PdfPoint>> points,
  553 + Widget? child,
  554 + PdfColor? color,
  555 + PdfBorder? border,
  556 + String? author,
  557 + DateTime? date,
  558 + String? content,
  559 + String? subject,
  560 + }) : super(
  561 + child: child ??
  562 + InkList(
  563 + points: points,
  564 + strokeColor: color,
  565 + strokeWidth: border?.width ?? 1.0),
  566 + builder: AnnotationInk(
  567 + points,
  568 + color: color,
  569 + border: border,
  570 + author: author,
  571 + date: date,
  572 + content: content,
  573 + subject: subject,
  574 + ),
  575 + );
  576 +}
  577 +
211 class Outline extends Anchor { 578 class Outline extends Anchor {
212 Outline({ 579 Outline({
213 Widget? child, 580 Widget? child,
  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 +import 'package:pdf/src/pdf/point.dart';
  18 +import 'package:pdf/src/widgets/geometry.dart';
  19 +import 'package:pdf/src/widgets/widget.dart';
  20 +
  21 +import '../../pdf.dart';
  22 +
  23 +class Circle extends Widget {
  24 + Circle({this.fillColor, this.strokeColor, this.strokeWidth = 1.0});
  25 +
  26 + final PdfColor? fillColor;
  27 + final PdfColor? strokeColor;
  28 + final double strokeWidth;
  29 +
  30 + @override
  31 + void layout(Context context, BoxConstraints constraints,
  32 + {bool parentUsesSize = false}) {
  33 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  34 + }
  35 +
  36 + @override
  37 + void paint(Context context) {
  38 + super.paint(context);
  39 +
  40 + final canvas = context.canvas;
  41 +
  42 + canvas.saveContext();
  43 +
  44 + if (fillColor != null) {
  45 + canvas.setFillColor(fillColor!);
  46 + }
  47 + if (strokeColor != null) {
  48 + canvas.setStrokeColor(strokeColor);
  49 + }
  50 +
  51 + canvas.setLineWidth(strokeWidth);
  52 +
  53 + canvas.drawEllipse(
  54 + box!.width / 2, box!.height / 2, box!.width / 2, box!.height / 2);
  55 +
  56 + if (strokeColor != null && fillColor != null) {
  57 + canvas.fillAndStrokePath();
  58 + } else if (strokeColor != null) {
  59 + canvas.strokePath();
  60 + } else {
  61 + canvas.fillPath();
  62 + }
  63 +
  64 + canvas.restoreContext();
  65 + }
  66 +}
  67 +
  68 +class Rectangle extends Widget {
  69 + Rectangle({this.fillColor, this.strokeColor, this.strokeWidth = 1.0});
  70 +
  71 + final PdfColor? fillColor;
  72 + final PdfColor? strokeColor;
  73 + final double strokeWidth;
  74 +
  75 + @override
  76 + void layout(Context context, BoxConstraints constraints,
  77 + {bool parentUsesSize = false}) {
  78 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  79 + }
  80 +
  81 + @override
  82 + void paint(Context context) {
  83 + super.paint(context);
  84 +
  85 + final canvas = context.canvas;
  86 +
  87 + canvas.saveContext();
  88 +
  89 + if (fillColor != null) {
  90 + canvas.setFillColor(fillColor!);
  91 + }
  92 + if (strokeColor != null) {
  93 + canvas.setStrokeColor(strokeColor);
  94 + }
  95 +
  96 + canvas.setLineWidth(strokeWidth);
  97 +
  98 + canvas.drawRect(0, 0, box!.width, box!.height);
  99 +
  100 + if (strokeColor != null && fillColor != null) {
  101 + canvas.fillAndStrokePath();
  102 + } else if (strokeColor != null) {
  103 + canvas.strokePath();
  104 + } else {
  105 + canvas.fillPath();
  106 + }
  107 +
  108 + canvas.restoreContext();
  109 + }
  110 +}
  111 +
  112 +class Polygon extends Widget {
  113 + Polygon(
  114 + {required this.points,
  115 + this.fillColor,
  116 + this.strokeColor,
  117 + this.strokeWidth = 1.0,
  118 + this.close = true});
  119 +
  120 + final List<PdfPoint> points;
  121 + final PdfColor? fillColor;
  122 + final PdfColor? strokeColor;
  123 + final double strokeWidth;
  124 + final bool close;
  125 +
  126 + @override
  127 + void layout(Context context, BoxConstraints constraints,
  128 + {bool parentUsesSize = false}) {
  129 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  130 + }
  131 +
  132 + @override
  133 + void paint(Context context) {
  134 + super.paint(context);
  135 +
  136 + // Make sure there are enough points to draw anything
  137 + if (points.length < 3) {
  138 + return;
  139 + }
  140 +
  141 + final canvas = context.canvas;
  142 +
  143 + canvas.saveContext();
  144 +
  145 + if (fillColor != null) {
  146 + canvas.setFillColor(fillColor!);
  147 + }
  148 + if (strokeColor != null) {
  149 + canvas.setStrokeColor(strokeColor);
  150 + }
  151 +
  152 + canvas.setLineWidth(strokeWidth);
  153 +
  154 + // Flip the points on the Y axis.
  155 + final flippedPoints =
  156 + points.map((e) => PdfPoint(e.x, box!.height - e.y)).toList();
  157 +
  158 + canvas.moveTo(flippedPoints[0].x, flippedPoints[0].y);
  159 + for (var i = 0; i < flippedPoints.length; i++) {
  160 + canvas.lineTo(flippedPoints[i].x, flippedPoints[i].y);
  161 + }
  162 +
  163 + if (close) {
  164 + canvas.closePath();
  165 + }
  166 +
  167 + if (strokeColor != null && fillColor != null) {
  168 + canvas.fillAndStrokePath();
  169 + } else if (strokeColor != null) {
  170 + canvas.strokePath();
  171 + } else {
  172 + canvas.fillPath();
  173 + }
  174 +
  175 + canvas.restoreContext();
  176 + }
  177 +}
  178 +
  179 +class InkList extends Widget {
  180 + InkList({required this.points, this.strokeColor, this.strokeWidth = 1.0});
  181 +
  182 + final List<List<PdfPoint>> points;
  183 + final PdfColor? strokeColor;
  184 + final double strokeWidth;
  185 +
  186 + @override
  187 + void layout(Context context, BoxConstraints constraints,
  188 + {bool parentUsesSize = false}) {
  189 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  190 + }
  191 +
  192 + @override
  193 + void paint(Context context) {
  194 + super.paint(context);
  195 +
  196 + final canvas = context.canvas;
  197 +
  198 + canvas.saveContext();
  199 +
  200 + if (strokeColor != null) {
  201 + canvas.setStrokeColor(strokeColor);
  202 + }
  203 +
  204 + canvas.setLineWidth(strokeWidth);
  205 +
  206 + // Flip the points on the Y axis.
  207 +
  208 + for (var subLineIndex = 0; subLineIndex < points.length; subLineIndex++) {
  209 + final flippedPoints = points[subLineIndex]
  210 + .map((e) => PdfPoint(e.x, box!.height - e.y))
  211 + .toList();
  212 + canvas.moveTo(flippedPoints[0].x, flippedPoints[0].y);
  213 + for (var i = 0; i < flippedPoints.length; i++) {
  214 + canvas.lineTo(flippedPoints[i].x, flippedPoints[i].y);
  215 + }
  216 + }
  217 +
  218 + canvas.strokePath();
  219 +
  220 + canvas.restoreContext();
  221 + }
  222 +}
@@ -112,6 +112,12 @@ class Context { @@ -112,6 +112,12 @@ class Context {
112 y.reduce(math.max), 112 y.reduce(math.max),
113 ); 113 );
114 } 114 }
  115 +
  116 + PdfPoint localToGlobalPoint(PdfPoint point) {
  117 + final mat = canvas.getTransform();
  118 + final xy = mat.transform3(Vector3(point.x, point.y, 0));
  119 + return PdfPoint(xy.x, xy.y);
  120 + }
115 } 121 }
116 122
117 class Inherited { 123 class Inherited {
@@ -49,6 +49,7 @@ export 'src/widgets/page_theme.dart'; @@ -49,6 +49,7 @@ export 'src/widgets/page_theme.dart';
49 export 'src/widgets/partitions.dart'; 49 export 'src/widgets/partitions.dart';
50 export 'src/widgets/placeholders.dart'; 50 export 'src/widgets/placeholders.dart';
51 export 'src/widgets/progress.dart'; 51 export 'src/widgets/progress.dart';
  52 +export 'src/widgets/shape.dart';
52 export 'src/widgets/stack.dart'; 53 export 'src/widgets/stack.dart';
53 export 'src/widgets/svg.dart'; 54 export 'src/widgets/svg.dart';
54 export 'src/widgets/table.dart'; 55 export 'src/widgets/table.dart';
@@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl
4 homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf 4 homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf
5 repository: https://github.com/DavBfr/dart_pdf 5 repository: https://github.com/DavBfr/dart_pdf
6 issue_tracker: https://github.com/DavBfr/dart_pdf/issues 6 issue_tracker: https://github.com/DavBfr/dart_pdf/issues
7 -version: 3.4.2 7 +version: 3.5.0
8 8
9 environment: 9 environment:
10 sdk: ">=2.12.0 <3.0.0" 10 sdk: ">=2.12.0 <3.0.0"
@@ -17,59 +17,103 @@ @@ -17,59 +17,103 @@
17 import 'dart:io'; 17 import 'dart:io';
18 18
19 import 'package:pdf/pdf.dart'; 19 import 'package:pdf/pdf.dart';
  20 +import 'package:pdf/widgets.dart';
20 import 'package:test/test.dart'; 21 import 'package:test/test.dart';
21 22
22 -void main() {  
23 - test('Pdf Annotations', () async {  
24 - final pdf = PdfDocument();  
25 - final page = PdfPage(pdf, pageFormat: const PdfPageFormat(500, 300));  
26 - final page1 = PdfPage(pdf, pageFormat: const PdfPageFormat(500, 300));  
27 -  
28 - pdf.pdfNames.addDest('target', page1, posY: 100);  
29 -  
30 - final g = page.getGraphics(); 23 +late Document pdf;
31 24
32 - PdfAnnot(  
33 - page,  
34 - PdfAnnotText(  
35 - rect: const PdfRect(100, 100, 50, 50),  
36 - content: 'Hello',  
37 - ),  
38 - ); 25 +void main() {
  26 + setUpAll(() {
  27 + Document.debug = true;
  28 + pdf = Document();
  29 + });
39 30
40 - PdfAnnot(  
41 - page,  
42 - PdfAnnotNamedLink(  
43 - dest: 'target',  
44 - rect: const PdfRect(100, 150, 50, 50), 31 + test('Pdf Link Annotations', () async {
  32 + pdf.addPage(
  33 + Page(
  34 + build: (context) => Column(
  35 + children: [
  36 + Link(child: Text('A link'), destination: 'destination'),
  37 + UrlLink(
  38 + child: Text('GitHub'),
  39 + destination: 'https://github.com/DavBfr/dart_pdf/'),
  40 + ],
  41 + ),
45 ), 42 ),
46 ); 43 );
47 - g.drawRect(100, 150, 50, 50);  
48 - g.strokePath(); 44 + });
49 45
50 - PdfAnnot(  
51 - page,  
52 - PdfAnnotUrlLink(  
53 - rect: const PdfRect(100, 250, 50, 50),  
54 - url: 'https://github.com/DavBfr/dart_pdf/', 46 + test('Pdf Shape Annotations', () async {
  47 + pdf.addPage(
  48 + Page(
  49 + build: (context) => Wrap(
  50 + spacing: 20,
  51 + runSpacing: 20,
  52 + children: [
  53 + SizedBox(
  54 + width: 200,
  55 + height: 200,
  56 + child: CircleAnnotation(
  57 + color: PdfColors.blue,
  58 + author: 'David PHAM-VAN',
  59 + ),
  60 + ),
  61 + SizedBox(
  62 + width: 200,
  63 + height: 200,
  64 + child: SquareAnnotation(
  65 + color: PdfColors.red,
  66 + ),
  67 + ),
  68 + SizedBox(
  69 + width: 200,
  70 + height: 100,
  71 + child: PolyLineAnnotation(
  72 + points: const [
  73 + PdfPoint(10, 10),
  74 + PdfPoint(10, 30),
  75 + PdfPoint(50, 70)
  76 + ],
  77 + color: PdfColors.purple,
  78 + ),
  79 + ),
  80 + SizedBox(
  81 + width: 200,
  82 + height: 100,
  83 + child: PolygonAnnotation(
  84 + points: const [
  85 + PdfPoint(10, 10),
  86 + PdfPoint(10, 30),
  87 + PdfPoint(50, 70)
  88 + ],
  89 + color: PdfColors.orange,
  90 + ),
  91 + ),
  92 + SizedBox(
  93 + width: 200,
  94 + height: 100,
  95 + child: InkAnnotation(
  96 + points: const [
  97 + [PdfPoint(10, 10), PdfPoint(10, 30), PdfPoint(50, 70)],
  98 + [PdfPoint(100, 10), PdfPoint(100, 30), PdfPoint(150, 70)],
  99 + ],
  100 + color: PdfColors.green,
  101 + ),
  102 + ),
  103 + ],
  104 + ),
55 ), 105 ),
56 ); 106 );
57 - g.drawRect(100, 250, 50, 50);  
58 - g.strokePath(); 107 + });
59 108
60 - PdfAnnot(  
61 - page,  
62 - PdfTextField(  
63 - rect: const PdfRect(100, 50, 50, 20),  
64 - fieldName: 'test',  
65 - font: PdfFont.helvetica(pdf),  
66 - fontSize: 10,  
67 - textColor: PdfColors.blue,  
68 - ),  
69 - );  
70 - // g.drawRect(100, 50, 50, 20);  
71 - g.strokePath(); 109 + test('Pdf Anchor Annotation', () async {
  110 + pdf.addPage(Page(
  111 + build: (context) =>
  112 + Anchor(child: Text('The destination'), name: 'destination'),
  113 + ));
  114 + });
72 115
  116 + tearDownAll(() async {
73 final file = File('annotations.pdf'); 117 final file = File('annotations.pdf');
74 await file.writeAsBytes(await pdf.save()); 118 await file.writeAsBytes(await pdf.save());
75 }); 119 });