1
49
50 package com.lowagie.text.pdf;
51
52 import java.awt.Color;
53 import java.io.IOException;
54 import java.util.HashMap;
55
56 import com.lowagie.text.Rectangle;
57
62
63 public class PdfAnnotation extends PdfDictionary {
64
65 public static final PdfName HIGHLIGHT_NONE = PdfName.N;
66
67 public static final PdfName HIGHLIGHT_INVERT = PdfName.I;
68
69 public static final PdfName HIGHLIGHT_OUTLINE = PdfName.O;
70
71 public static final PdfName HIGHLIGHT_PUSH = PdfName.P;
72
73 public static final PdfName HIGHLIGHT_TOGGLE = PdfName.T;
74
75 public static final int FLAGS_INVISIBLE = 1;
76
77 public static final int FLAGS_HIDDEN = 2;
78
79 public static final int FLAGS_PRINT = 4;
80
81 public static final int FLAGS_NOZOOM = 8;
82
83 public static final int FLAGS_NOROTATE = 16;
84
85 public static final int FLAGS_NOVIEW = 32;
86
87 public static final int FLAGS_READONLY = 64;
88
89 public static final int FLAGS_LOCKED = 128;
90
91 public static final int FLAGS_TOGGLENOVIEW = 256;
92
93 public static final PdfName APPEARANCE_NORMAL = PdfName.N;
94
95 public static final PdfName APPEARANCE_ROLLOVER = PdfName.R;
96
97 public static final PdfName APPEARANCE_DOWN = PdfName.D;
98
99 public static final PdfName AA_ENTER = PdfName.E;
100
101 public static final PdfName AA_EXIT = PdfName.X;
102
103 public static final PdfName AA_DOWN = PdfName.D;
104
105 public static final PdfName AA_UP = PdfName.U;
106
107 public static final PdfName AA_FOCUS = PdfName.FO;
108
109 public static final PdfName AA_BLUR = PdfName.BL;
110
111 public static final PdfName AA_JS_KEY = PdfName.K;
112
113 public static final PdfName AA_JS_FORMAT = PdfName.F;
114
115 public static final PdfName AA_JS_CHANGE = PdfName.V;
116
117 public static final PdfName AA_JS_OTHER_CHANGE = PdfName.C;
118
119 public static final int MARKUP_HIGHLIGHT = 0;
120
121 public static final int MARKUP_UNDERLINE = 1;
122
123 public static final int MARKUP_STRIKEOUT = 2;
124
128 public static final int MARKUP_SQUIGGLY = 3;
129
130 protected PdfWriter writer;
131
135 protected PdfIndirectReference reference;
136 protected HashMap templates;
137 protected boolean form = false;
138 protected boolean annotation = true;
139
140
141 protected boolean used = false;
142
143
144 private int placeInPage = -1;
145
146
147 public PdfAnnotation(PdfWriter writer, Rectangle rect) {
148 this.writer = writer;
149 if (rect != null)
150 put(PdfName.RECT, new PdfRectangle(rect));
151 }
152
153
163
164 public PdfAnnotation(PdfWriter writer, float llx, float lly, float urx, float ury, PdfString title, PdfString content) {
165 this.writer = writer;
166 put(PdfName.SUBTYPE, PdfName.TEXT);
167 put(PdfName.T, title);
168 put(PdfName.RECT, new PdfRectangle(llx, lly, urx, ury));
169 put(PdfName.CONTENTS, content);
170 }
171
172
181
182 public PdfAnnotation(PdfWriter writer, float llx, float lly, float urx, float ury, PdfAction action) {
183 this.writer = writer;
184 put(PdfName.SUBTYPE, PdfName.LINK);
185 put(PdfName.RECT, new PdfRectangle(llx, lly, urx, ury));
186 put(PdfName.A, action);
187 put(PdfName.BORDER, new PdfBorderArray(0, 0, 0));
188 put(PdfName.C, new PdfColor(0x00, 0x00, 0xFF));
189 }
190
191
202 public static PdfAnnotation createScreen(PdfWriter writer, Rectangle rect, String clipTitle, PdfFileSpecification fs,
203 String mimeType, boolean playOnDisplay) throws IOException {
204 PdfAnnotation ann = new PdfAnnotation(writer, rect);
205 ann.put(PdfName.SUBTYPE, PdfName.SCREEN);
206 ann.put (PdfName.F, new PdfNumber(FLAGS_PRINT));
207 ann.put(PdfName.TYPE, PdfName.ANNOT);
208 ann.setPage();
209 PdfIndirectReference ref = ann.getIndirectReference();
210 PdfAction action = PdfAction.rendition(clipTitle,fs,mimeType, ref);
211 PdfIndirectReference actionRef = writer.addToBody(action).getIndirectReference();
212
213 if (playOnDisplay)
214 {
215 PdfDictionary aa = new PdfDictionary();
216 aa.put(new PdfName("PV"), actionRef);
217 ann.put(PdfName.AA, aa);
218 }
219 ann.put(PdfName.A, actionRef);
220 return ann;
221 }
222
223
227 public PdfIndirectReference getIndirectReference() {
228 if (reference == null) {
229 reference = writer.getPdfIndirectReference();
230 }
231 return reference;
232 }
233
234
243 public static PdfAnnotation createText(PdfWriter writer, Rectangle rect, String title, String contents, boolean open, String icon) {
244 PdfAnnotation annot = new PdfAnnotation(writer, rect);
245 annot.put(PdfName.SUBTYPE, PdfName.TEXT);
246 if (title != null)
247 annot.put(PdfName.T, new PdfString(title, PdfObject.TEXT_UNICODE));
248 if (contents != null)
249 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
250 if (open)
251 annot.put(PdfName.OPEN, PdfBoolean.PDFTRUE);
252 if (icon != null) {
253 annot.put(PdfName.NAME, new PdfName(icon));
254 }
255 return annot;
256 }
257
258
265 protected static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight) {
266 PdfAnnotation annot = new PdfAnnotation(writer, rect);
267 annot.put(PdfName.SUBTYPE, PdfName.LINK);
268 if (!highlight.equals(HIGHLIGHT_INVERT))
269 annot.put(PdfName.H, highlight);
270 return annot;
271 }
272
273
281 public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, PdfAction action) {
282 PdfAnnotation annot = createLink(writer, rect, highlight);
283 annot.putEx(PdfName.A, action);
284 return annot;
285 }
286
287
295 public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, String namedDestination) {
296 PdfAnnotation annot = createLink(writer, rect, highlight);
297 annot.put(PdfName.DEST, new PdfString(namedDestination));
298 return annot;
299 }
300
301
310 public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, int page, PdfDestination dest) {
311 PdfAnnotation annot = createLink(writer, rect, highlight);
312 PdfIndirectReference ref = writer.getPageReference(page);
313 dest.addPage(ref);
314 annot.put(PdfName.DEST, dest);
315 return annot;
316 }
317
318
326 public static PdfAnnotation createFreeText(PdfWriter writer, Rectangle rect, String contents, PdfContentByte defaultAppearance) {
327 PdfAnnotation annot = new PdfAnnotation(writer, rect);
328 annot.put(PdfName.SUBTYPE, PdfName.FREETEXT);
329 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
330 annot.setDefaultAppearanceString(defaultAppearance);
331 return annot;
332 }
333
334
345 public static PdfAnnotation createLine(PdfWriter writer, Rectangle rect, String contents, float x1, float y1, float x2, float y2) {
346 PdfAnnotation annot = new PdfAnnotation(writer, rect);
347 annot.put(PdfName.SUBTYPE, PdfName.LINE);
348 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
349 PdfArray array = new PdfArray(new PdfNumber(x1));
350 array.add(new PdfNumber(y1));
351 array.add(new PdfNumber(x2));
352 array.add(new PdfNumber(y2));
353 annot.put(PdfName.L, array);
354 return annot;
355 }
356
357
365 public static PdfAnnotation createSquareCircle(PdfWriter writer, Rectangle rect, String contents, boolean square) {
366 PdfAnnotation annot = new PdfAnnotation(writer, rect);
367 if (square)
368 annot.put(PdfName.SUBTYPE, PdfName.SQUARE);
369 else
370 annot.put(PdfName.SUBTYPE, PdfName.CIRCLE);
371 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
372 return annot;
373 }
374
375 public static PdfAnnotation createMarkup(PdfWriter writer, Rectangle rect, String contents, int type, float quadPoints[]) {
376 PdfAnnotation annot = new PdfAnnotation(writer, rect);
377 PdfName name = PdfName.HIGHLIGHT;
378 switch (type) {
379 case MARKUP_UNDERLINE:
380 name = PdfName.UNDERLINE;
381 break;
382 case MARKUP_STRIKEOUT:
383 name = PdfName.STRIKEOUT;
384 break;
385 case MARKUP_SQUIGGLY:
386 name = PdfName.SQUIGGLY;
387 break;
388 }
389 annot.put(PdfName.SUBTYPE, name);
390 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
391 PdfArray array = new PdfArray();
392 for (int k = 0; k < quadPoints.length; ++k)
393 array.add(new PdfNumber(quadPoints[k]));
394 annot.put(PdfName.QUADPOINTS, array);
395 return annot;
396 }
397
398
406 public static PdfAnnotation createStamp(PdfWriter writer, Rectangle rect, String contents, String name) {
407 PdfAnnotation annot = new PdfAnnotation(writer, rect);
408 annot.put(PdfName.SUBTYPE, PdfName.STAMP);
409 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
410 annot.put(PdfName.NAME, new PdfName(name));
411 return annot;
412 }
413
414 public static PdfAnnotation createInk(PdfWriter writer, Rectangle rect, String contents, float inkList[][]) {
415 PdfAnnotation annot = new PdfAnnotation(writer, rect);
416 annot.put(PdfName.SUBTYPE, PdfName.INK);
417 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
418 PdfArray outer = new PdfArray();
419 for (int k = 0; k < inkList.length; ++k) {
420 PdfArray inner = new PdfArray();
421 float deep[] = inkList[k];
422 for (int j = 0; j < deep.length; ++j)
423 inner.add(new PdfNumber(deep[j]));
424 outer.add(inner);
425 }
426 annot.put(PdfName.INKLIST, outer);
427 return annot;
428 }
429
430
442 public static PdfAnnotation createFileAttachment(PdfWriter writer, Rectangle rect, String contents, byte fileStore[], String file, String fileDisplay) throws IOException {
443 return createFileAttachment(writer, rect, contents, PdfFileSpecification.fileEmbedded(writer, file, fileDisplay, fileStore));
444 }
445
446
454 public static PdfAnnotation createFileAttachment(PdfWriter writer, Rectangle rect, String contents, PdfFileSpecification fs) throws IOException {
455 PdfAnnotation annot = new PdfAnnotation(writer, rect);
456 annot.put(PdfName.SUBTYPE, PdfName.FILEATTACHMENT);
457 if (contents != null)
458 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
459 annot.put(PdfName.FS, fs.getReference());
460 return annot;
461 }
462
463
471 public static PdfAnnotation createPopup(PdfWriter writer, Rectangle rect, String contents, boolean open) {
472 PdfAnnotation annot = new PdfAnnotation(writer, rect);
473 annot.put(PdfName.SUBTYPE, PdfName.POPUP);
474 if (contents != null)
475 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
476 if (open)
477 annot.put(PdfName.OPEN, PdfBoolean.PDFTRUE);
478 return annot;
479 }
480
481 public void setDefaultAppearanceString(PdfContentByte cb) {
482 byte b[] = cb.getInternalBuffer().toByteArray();
483 int len = b.length;
484 for (int k = 0; k < len; ++k) {
485 if (b[k] == '\n')
486 b[k] = 32;
487 }
488 put(PdfName.DA, new PdfString(b));
489 }
490
491 public void setFlags(int flags) {
492 if (flags == 0)
493 remove(PdfName.F);
494 else
495 put(PdfName.F, new PdfNumber(flags));
496 }
497
498 public void setBorder(PdfBorderArray border) {
499 put(PdfName.BORDER, border);
500 }
501
502 public void setBorderStyle(PdfBorderDictionary border) {
503 put(PdfName.BS, border);
504 }
505
506
512 public void setHighlighting(PdfName highlight) {
513 if (highlight.equals(HIGHLIGHT_INVERT))
514 remove(PdfName.H);
515 else
516 put(PdfName.H, highlight);
517 }
518
519 public void setAppearance(PdfName ap, PdfTemplate template) {
520 PdfDictionary dic = (PdfDictionary)get(PdfName.AP);
521 if (dic == null)
522 dic = new PdfDictionary();
523 dic.put(ap, template.getIndirectReference());
524 put(PdfName.AP, dic);
525 if (!form)
526 return;
527 if (templates == null)
528 templates = new HashMap();
529 templates.put(template, null);
530 }
531
532 public void setAppearance(PdfName ap, String state, PdfTemplate template) {
533 PdfDictionary dicAp = (PdfDictionary)get(PdfName.AP);
534 if (dicAp == null)
535 dicAp = new PdfDictionary();
536
537 PdfDictionary dic;
538 PdfObject obj = dicAp.get(ap);
539 if (obj != null && obj.isDictionary())
540 dic = (PdfDictionary)obj;
541 else
542 dic = new PdfDictionary();
543 dic.put(new PdfName(state), template.getIndirectReference());
544 dicAp.put(ap, dic);
545 put(PdfName.AP, dicAp);
546 if (!form)
547 return;
548 if (templates == null)
549 templates = new HashMap();
550 templates.put(template, null);
551 }
552
553 public void setAppearanceState(String state) {
554 if (state == null) {
555 remove(PdfName.AS);
556 return;
557 }
558 put(PdfName.AS, new PdfName(state));
559 }
560
561 public void setColor(Color color) {
562 put(PdfName.C, new PdfColor(color));
563 }
564
565 public void setTitle(String title) {
566 if (title == null) {
567 remove(PdfName.T);
568 return;
569 }
570 put(PdfName.T, new PdfString(title, PdfObject.TEXT_UNICODE));
571 }
572
573 public void setPopup(PdfAnnotation popup) {
574 put(PdfName.POPUP, popup.getIndirectReference());
575 popup.put(PdfName.PARENT, getIndirectReference());
576 }
577
578 public void setAction(PdfAction action) {
579 put(PdfName.A, action);
580 }
581
582 public void setAdditionalActions(PdfName key, PdfAction action) {
583 PdfDictionary dic;
584 PdfObject obj = get(PdfName.AA);
585 if (obj != null && obj.isDictionary())
586 dic = (PdfDictionary)obj;
587 else
588 dic = new PdfDictionary();
589 dic.put(key, action);
590 put(PdfName.AA, dic);
591 }
592
593
596 public boolean isUsed() {
597 return used;
598 }
599
600
602 public void setUsed() {
603 used = true;
604 }
605
606 public HashMap getTemplates() {
607 return templates;
608 }
609
610
613 public boolean isForm() {
614 return form;
615 }
616
617
620 public boolean isAnnotation() {
621 return annotation;
622 }
623
624 public void setPage(int page) {
625 put(PdfName.P, writer.getPageReference(page));
626 }
627
628 public void setPage() {
629 put(PdfName.P, writer.getCurrentPage());
630 }
631
632
635 public int getPlaceInPage() {
636 return placeInPage;
637 }
638
639
644 public void setPlaceInPage(int placeInPage) {
645 this.placeInPage = placeInPage;
646 }
647
648 public void setRotate(int v) {
649 put(PdfName.ROTATE, new PdfNumber(v));
650 }
651
652 PdfDictionary getMK() {
653 PdfDictionary mk = (PdfDictionary)get(PdfName.MK);
654 if (mk == null) {
655 mk = new PdfDictionary();
656 put(PdfName.MK, mk);
657 }
658 return mk;
659 }
660
661 public void setMKRotation(int rotation) {
662 getMK().put(PdfName.R, new PdfNumber(rotation));
663 }
664
665 public static PdfArray getMKColor(Color color) {
666 PdfArray array = new PdfArray();
667 int type = ExtendedColor.getType(color);
668 switch (type) {
669 case ExtendedColor.TYPE_GRAY: {
670 array.add(new PdfNumber(((GrayColor)color).getGray()));
671 break;
672 }
673 case ExtendedColor.TYPE_CMYK: {
674 CMYKColor cmyk = (CMYKColor)color;
675 array.add(new PdfNumber(cmyk.getCyan()));
676 array.add(new PdfNumber(cmyk.getMagenta()));
677 array.add(new PdfNumber(cmyk.getYellow()));
678 array.add(new PdfNumber(cmyk.getBlack()));
679 break;
680 }
681 case ExtendedColor.TYPE_SEPARATION:
682 case ExtendedColor.TYPE_PATTERN:
683 case ExtendedColor.TYPE_SHADING:
684 throw new RuntimeException("Separations, patterns and shadings are not allowed in MK dictionary.");
685 default:
686 array.add(new PdfNumber(color.getRed() / 255f));
687 array.add(new PdfNumber(color.getGreen() / 255f));
688 array.add(new PdfNumber(color.getBlue() / 255f));
689 }
690 return array;
691 }
692
693 public void setMKBorderColor(Color color) {
694 if (color == null)
695 getMK().remove(PdfName.BC);
696 else
697 getMK().put(PdfName.BC, getMKColor(color));
698 }
699
700 public void setMKBackgroundColor(Color color) {
701 if (color == null)
702 getMK().remove(PdfName.BG);
703 else
704 getMK().put(PdfName.BG, getMKColor(color));
705 }
706
707 public void setMKNormalCaption(String caption) {
708 getMK().put(PdfName.CA, new PdfString(caption, PdfObject.TEXT_UNICODE));
709 }
710
711 public void setMKRolloverCaption(String caption) {
712 getMK().put(PdfName.RC, new PdfString(caption, PdfObject.TEXT_UNICODE));
713 }
714
715 public void setMKAlternateCaption(String caption) {
716 getMK().put(PdfName.AC, new PdfString(caption, PdfObject.TEXT_UNICODE));
717 }
718
719 public void setMKNormalIcon(PdfTemplate template) {
720 getMK().put(PdfName.I, template.getIndirectReference());
721 }
722
723 public void setMKRolloverIcon(PdfTemplate template) {
724 getMK().put(PdfName.RI, template.getIndirectReference());
725 }
726
727 public void setMKAlternateIcon(PdfTemplate template) {
728 getMK().put(PdfName.IX, template.getIndirectReference());
729 }
730
731 public void setMKIconFit(PdfName scale, PdfName scalingType, float leftoverLeft, float leftoverBottom, boolean fitInBounds) {
732 PdfDictionary dic = new PdfDictionary();
733 if (!scale.equals(PdfName.A))
734 dic.put(PdfName.SW, scale);
735 if (!scalingType.equals(PdfName.P))
736 dic.put(PdfName.S, scalingType);
737 if (leftoverLeft != 0.5f || leftoverBottom != 0.5f) {
738 PdfArray array = new PdfArray(new PdfNumber(leftoverLeft));
739 array.add(new PdfNumber(leftoverBottom));
740 dic.put(PdfName.A, array);
741 }
742 if (fitInBounds)
743 dic.put(PdfName.FB, PdfBoolean.PDFTRUE);
744 getMK().put(PdfName.IF, dic);
745 }
746
747 public void setMKTextPosition(int tp) {
748 getMK().put(PdfName.TP, new PdfNumber(tp));
749 }
750
751
755 public void setLayer(PdfOCG layer) {
756 put(PdfName.OC, layer.getRef());
757 }
758
759
764 public void setName(String name) {
765 put(PdfName.NM, new PdfString(name));
766 }
767
768
815 public static class PdfImportedLink {
816 float llx, lly, urx, ury;
817 HashMap parameters = new HashMap();
818 PdfArray destination = null;
819 int newPage=0;
820
821 PdfImportedLink(PdfDictionary annotation) {
822 parameters.putAll(annotation.hashMap);
823 try {
824 destination = (PdfArray) parameters.remove(PdfName.DEST);
825 } catch (ClassCastException ex) {
826 throw new IllegalArgumentException("You have to consolidate the named destinations of your reader.");
827 }
828 if (destination != null) {
829 destination = new PdfArray(destination);
830 }
831 PdfArray rc = (PdfArray) parameters.remove(PdfName.RECT);
832 llx = rc.getAsNumber(0).floatValue();
833 lly = rc.getAsNumber(1).floatValue();
834 urx = rc.getAsNumber(2).floatValue();
835 ury = rc.getAsNumber(3).floatValue();
836 }
837
838 public boolean isInternal() {
839 return destination != null;
840 }
841
842 public int getDestinationPage() {
843 if (!isInternal()) return 0;
844
845
846
847 PdfIndirectReference ref = destination.getAsIndirectObject(0);
848
849 PRIndirectReference pr = (PRIndirectReference) ref;
850 PdfReader r = pr.getReader();
851 for (int i = 1; i <= r.getNumberOfPages(); i++) {
852 PRIndirectReference pp = r.getPageOrigRef(i);
853 if (pp.getGeneration() == pr.getGeneration() && pp.getNumber() == pr.getNumber()) return i;
854 }
855 throw new IllegalArgumentException("Page not found.");
856 }
857
858 public void setDestinationPage(int newPage) {
859 if (!isInternal()) throw new IllegalArgumentException("Cannot change destination of external link");
860 this.newPage=newPage;
861 }
862
863 public void transformDestination(float a, float b, float c, float d, float e, float f) {
864 if (!isInternal()) throw new IllegalArgumentException("Cannot change destination of external link");
865 if (destination.getAsName(1).equals(PdfName.XYZ)) {
866 float x = destination.getAsNumber(2).floatValue();
867 float y = destination.getAsNumber(3).floatValue();
868 float xx = x * a + y * c + e;
869 float yy = x * b + y * d + f;
870 destination.set(2, new PdfNumber(xx));
871 destination.set(3, new PdfNumber(yy));
872 }
873 }
874
875 public void transformRect(float a, float b, float c, float d, float e, float f) {
876 float x = llx * a + lly * c + e;
877 float y = llx * b + lly * d + f;
878 llx = x;
879 lly = y;
880 x = urx * a + ury * c + e;
881 y = urx * b + ury * d + f;
882 urx = x;
883 ury = y;
884 }
885
886 public PdfAnnotation createAnnotation(PdfWriter writer) {
887 PdfAnnotation annotation = new PdfAnnotation(writer, new Rectangle(llx, lly, urx, ury));
888 if (newPage != 0) {
889 PdfIndirectReference ref = writer.getPageReference(newPage);
890 destination.set(0, ref);
891 }
892 if (destination != null) annotation.put(PdfName.DEST, destination);
893 annotation.hashMap.putAll(parameters);
894 return annotation;
895 }
896
897
902 public String toString() {
903 StringBuffer buf = new StringBuffer("Imported link: location [");
904 buf.append(llx);
905 buf.append(' ');
906 buf.append(lly);
907 buf.append(' ');
908 buf.append(urx);
909 buf.append(' ');
910 buf.append(ury);
911 buf.append("] destination ");
912 buf.append(destination);
913 buf.append(" parameters ");
914 buf.append(parameters);
915 return buf.toString();
916 }
917 }
918 }
919