001/* 002 * $Id: PdfAnnotation.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.IOException; 047import java.util.HashMap; 048import java.util.HashSet; 049 050import com.itextpdf.text.BaseColor; 051import com.itextpdf.text.Rectangle; 052import com.itextpdf.text.error_messages.MessageLocalization; 053/** 054 * A <CODE>PdfAnnotation</CODE> is a note that is associated with a page. 055 * 056 * @see PdfDictionary 057 */ 058 059public class PdfAnnotation extends PdfDictionary { 060 /** highlight attributename */ 061 public static final PdfName HIGHLIGHT_NONE = PdfName.N; 062 /** highlight attributename */ 063 public static final PdfName HIGHLIGHT_INVERT = PdfName.I; 064 /** highlight attributename */ 065 public static final PdfName HIGHLIGHT_OUTLINE = PdfName.O; 066 /** highlight attributename */ 067 public static final PdfName HIGHLIGHT_PUSH = PdfName.P; 068 /** highlight attributename */ 069 public static final PdfName HIGHLIGHT_TOGGLE = PdfName.T; 070 /** flagvalue */ 071 public static final int FLAGS_INVISIBLE = 1; 072 /** flagvalue */ 073 public static final int FLAGS_HIDDEN = 2; 074 /** flagvalue */ 075 public static final int FLAGS_PRINT = 4; 076 /** flagvalue */ 077 public static final int FLAGS_NOZOOM = 8; 078 /** flagvalue */ 079 public static final int FLAGS_NOROTATE = 16; 080 /** flagvalue */ 081 public static final int FLAGS_NOVIEW = 32; 082 /** flagvalue */ 083 public static final int FLAGS_READONLY = 64; 084 /** flagvalue */ 085 public static final int FLAGS_LOCKED = 128; 086 /** flagvalue */ 087 public static final int FLAGS_TOGGLENOVIEW = 256; 088 /** appearance attributename */ 089 public static final PdfName APPEARANCE_NORMAL = PdfName.N; 090 /** appearance attributename */ 091 public static final PdfName APPEARANCE_ROLLOVER = PdfName.R; 092 /** appearance attributename */ 093 public static final PdfName APPEARANCE_DOWN = PdfName.D; 094 /** attributevalue */ 095 public static final PdfName AA_ENTER = PdfName.E; 096 /** attributevalue */ 097 public static final PdfName AA_EXIT = PdfName.X; 098 /** attributevalue */ 099 public static final PdfName AA_DOWN = PdfName.D; 100 /** attributevalue */ 101 public static final PdfName AA_UP = PdfName.U; 102 /** attributevalue */ 103 public static final PdfName AA_FOCUS = PdfName.FO; 104 /** attributevalue */ 105 public static final PdfName AA_BLUR = PdfName.BL; 106 /** attributevalue */ 107 public static final PdfName AA_JS_KEY = PdfName.K; 108 /** attributevalue */ 109 public static final PdfName AA_JS_FORMAT = PdfName.F; 110 /** attributevalue */ 111 public static final PdfName AA_JS_CHANGE = PdfName.V; 112 /** attributevalue */ 113 public static final PdfName AA_JS_OTHER_CHANGE = PdfName.C; 114 /** attributevalue */ 115 public static final int MARKUP_HIGHLIGHT = 0; 116 /** attributevalue */ 117 public static final int MARKUP_UNDERLINE = 1; 118 /** attributevalue */ 119 public static final int MARKUP_STRIKEOUT = 2; 120 /** 121 * attributevalue 122 * @since 2.1.3 123 */ 124 public static final int MARKUP_SQUIGGLY = 3; 125 126 protected PdfWriter writer; 127 /** 128 * Reference to this annotation. 129 * @since 2.1.6; was removed in 2.1.5, but restored in 2.1.6 130 */ 131 protected PdfIndirectReference reference; 132 protected HashSet<PdfTemplate> templates; 133 protected boolean form = false; 134 protected boolean annotation = true; 135 136 /** Holds value of property used. */ 137 protected boolean used = false; 138 139 /** Holds value of property placeInPage. */ 140 private int placeInPage = -1; 141 142 // constructors 143 public PdfAnnotation(PdfWriter writer, Rectangle rect) { 144 this.writer = writer; 145 if (rect != null) 146 put(PdfName.RECT, new PdfRectangle(rect)); 147 } 148 149/** 150 * Constructs a new <CODE>PdfAnnotation</CODE> of subtype text. 151 * @param writer 152 * @param llx 153 * @param lly 154 * @param urx 155 * @param ury 156 * @param title 157 * @param content 158 */ 159 160 public PdfAnnotation(PdfWriter writer, float llx, float lly, float urx, float ury, PdfString title, PdfString content) { 161 this.writer = writer; 162 put(PdfName.SUBTYPE, PdfName.TEXT); 163 put(PdfName.T, title); 164 put(PdfName.RECT, new PdfRectangle(llx, lly, urx, ury)); 165 put(PdfName.CONTENTS, content); 166 } 167 168/** 169 * Constructs a new <CODE>PdfAnnotation</CODE> of subtype link (Action). 170 * @param writer 171 * @param llx 172 * @param lly 173 * @param urx 174 * @param ury 175 * @param action 176 */ 177 178 public PdfAnnotation(PdfWriter writer, float llx, float lly, float urx, float ury, PdfAction action) { 179 this.writer = writer; 180 put(PdfName.SUBTYPE, PdfName.LINK); 181 put(PdfName.RECT, new PdfRectangle(llx, lly, urx, ury)); 182 put(PdfName.A, action); 183 put(PdfName.BORDER, new PdfBorderArray(0, 0, 0)); 184 put(PdfName.C, new PdfColor(0x00, 0x00, 0xFF)); 185 } 186 187 /** 188 * Creates a screen PdfAnnotation 189 * @param writer 190 * @param rect 191 * @param clipTitle 192 * @param fs 193 * @param mimeType 194 * @param playOnDisplay 195 * @return a screen PdfAnnotation 196 * @throws IOException 197 */ 198 public static PdfAnnotation createScreen(PdfWriter writer, Rectangle rect, String clipTitle, PdfFileSpecification fs, 199 String mimeType, boolean playOnDisplay) throws IOException { 200 PdfAnnotation ann = new PdfAnnotation(writer, rect); 201 ann.put(PdfName.SUBTYPE, PdfName.SCREEN); 202 ann.put (PdfName.F, new PdfNumber(FLAGS_PRINT)); 203 ann.put(PdfName.TYPE, PdfName.ANNOT); 204 ann.setPage(); 205 PdfIndirectReference ref = ann.getIndirectReference(); 206 PdfAction action = PdfAction.rendition(clipTitle,fs,mimeType, ref); 207 PdfIndirectReference actionRef = writer.addToBody(action).getIndirectReference(); 208 // for play on display add trigger event 209 if (playOnDisplay) 210 { 211 PdfDictionary aa = new PdfDictionary(); 212 aa.put(new PdfName("PV"), actionRef); 213 ann.put(PdfName.AA, aa); 214 } 215 ann.put(PdfName.A, actionRef); 216 return ann; 217 } 218 219 /** 220 * Returns an indirect reference to the annotation 221 * @return the indirect reference 222 */ 223 public PdfIndirectReference getIndirectReference() { 224 if (reference == null) { 225 reference = writer.getPdfIndirectReference(); 226 } 227 return reference; 228 } 229 230 /** 231 * @param writer 232 * @param rect 233 * @param title 234 * @param contents 235 * @param open 236 * @param icon 237 * @return a PdfAnnotation 238 */ 239 public static PdfAnnotation createText(PdfWriter writer, Rectangle rect, String title, String contents, boolean open, String icon) { 240 PdfAnnotation annot = new PdfAnnotation(writer, rect); 241 annot.put(PdfName.SUBTYPE, PdfName.TEXT); 242 if (title != null) 243 annot.put(PdfName.T, new PdfString(title, PdfObject.TEXT_UNICODE)); 244 if (contents != null) 245 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 246 if (open) 247 annot.put(PdfName.OPEN, PdfBoolean.PDFTRUE); 248 if (icon != null) { 249 annot.put(PdfName.NAME, new PdfName(icon)); 250 } 251 return annot; 252 } 253 254 /** 255 * Creates a link. 256 * @param writer 257 * @param rect 258 * @param highlight 259 * @return A PdfAnnotation 260 */ 261 protected static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight) { 262 PdfAnnotation annot = new PdfAnnotation(writer, rect); 263 annot.put(PdfName.SUBTYPE, PdfName.LINK); 264 if (!highlight.equals(HIGHLIGHT_INVERT)) 265 annot.put(PdfName.H, highlight); 266 return annot; 267 } 268 269 /** 270 * Creates an Annotation with an Action. 271 * @param writer 272 * @param rect 273 * @param highlight 274 * @param action 275 * @return A PdfAnnotation 276 */ 277 public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, PdfAction action) { 278 PdfAnnotation annot = createLink(writer, rect, highlight); 279 annot.putEx(PdfName.A, action); 280 return annot; 281 } 282 283 /** 284 * Creates an Annotation with an local destination. 285 * @param writer 286 * @param rect 287 * @param highlight 288 * @param namedDestination 289 * @return A PdfAnnotation 290 */ 291 public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, String namedDestination) { 292 PdfAnnotation annot = createLink(writer, rect, highlight); 293 annot.put(PdfName.DEST, new PdfString(namedDestination)); 294 return annot; 295 } 296 297 /** 298 * Creates an Annotation with a PdfDestination. 299 * @param writer 300 * @param rect 301 * @param highlight 302 * @param page 303 * @param dest 304 * @return A PdfAnnotation 305 */ 306 public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, int page, PdfDestination dest) { 307 PdfAnnotation annot = createLink(writer, rect, highlight); 308 PdfIndirectReference ref = writer.getPageReference(page); 309 dest.addPage(ref); 310 annot.put(PdfName.DEST, dest); 311 return annot; 312 } 313 314 /** 315 * Add some free text to the document. 316 * @param writer 317 * @param rect 318 * @param contents 319 * @param defaultAppearance 320 * @return A PdfAnnotation 321 */ 322 public static PdfAnnotation createFreeText(PdfWriter writer, Rectangle rect, String contents, PdfContentByte defaultAppearance) { 323 PdfAnnotation annot = new PdfAnnotation(writer, rect); 324 annot.put(PdfName.SUBTYPE, PdfName.FREETEXT); 325 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 326 annot.setDefaultAppearanceString(defaultAppearance); 327 return annot; 328 } 329 330 /** 331 * Adds a line to the document. Move over the line and a tooltip is shown. 332 * @param writer 333 * @param rect 334 * @param contents 335 * @param x1 336 * @param y1 337 * @param x2 338 * @param y2 339 * @return A PdfAnnotation 340 */ 341 public static PdfAnnotation createLine(PdfWriter writer, Rectangle rect, String contents, float x1, float y1, float x2, float y2) { 342 PdfAnnotation annot = new PdfAnnotation(writer, rect); 343 annot.put(PdfName.SUBTYPE, PdfName.LINE); 344 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 345 PdfArray array = new PdfArray(new PdfNumber(x1)); 346 array.add(new PdfNumber(y1)); 347 array.add(new PdfNumber(x2)); 348 array.add(new PdfNumber(y2)); 349 annot.put(PdfName.L, array); 350 return annot; 351 } 352 353 /** 354 * Adds a circle or a square that shows a tooltip when you pass over it. 355 * @param writer 356 * @param rect 357 * @param contents The tooltip 358 * @param square true if you want a square, false if you want a circle 359 * @return A PdfAnnotation 360 */ 361 public static PdfAnnotation createSquareCircle(PdfWriter writer, Rectangle rect, String contents, boolean square) { 362 PdfAnnotation annot = new PdfAnnotation(writer, rect); 363 if (square) 364 annot.put(PdfName.SUBTYPE, PdfName.SQUARE); 365 else 366 annot.put(PdfName.SUBTYPE, PdfName.CIRCLE); 367 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 368 return annot; 369 } 370 371 public static PdfAnnotation createMarkup(PdfWriter writer, Rectangle rect, String contents, int type, float quadPoints[]) { 372 PdfAnnotation annot = new PdfAnnotation(writer, rect); 373 PdfName name = PdfName.HIGHLIGHT; 374 switch (type) { 375 case MARKUP_UNDERLINE: 376 name = PdfName.UNDERLINE; 377 break; 378 case MARKUP_STRIKEOUT: 379 name = PdfName.STRIKEOUT; 380 break; 381 case MARKUP_SQUIGGLY: 382 name = PdfName.SQUIGGLY; 383 break; 384 } 385 annot.put(PdfName.SUBTYPE, name); 386 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 387 PdfArray array = new PdfArray(); 388 for (int k = 0; k < quadPoints.length; ++k) 389 array.add(new PdfNumber(quadPoints[k])); 390 annot.put(PdfName.QUADPOINTS, array); 391 return annot; 392 } 393 394 /** 395 * Adds a Stamp to your document. Move over the stamp and a tooltip is shown 396 * @param writer 397 * @param rect 398 * @param contents 399 * @param name 400 * @return A PdfAnnotation 401 */ 402 public static PdfAnnotation createStamp(PdfWriter writer, Rectangle rect, String contents, String name) { 403 PdfAnnotation annot = new PdfAnnotation(writer, rect); 404 annot.put(PdfName.SUBTYPE, PdfName.STAMP); 405 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 406 annot.put(PdfName.NAME, new PdfName(name)); 407 return annot; 408 } 409 410 public static PdfAnnotation createInk(PdfWriter writer, Rectangle rect, String contents, float inkList[][]) { 411 PdfAnnotation annot = new PdfAnnotation(writer, rect); 412 annot.put(PdfName.SUBTYPE, PdfName.INK); 413 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 414 PdfArray outer = new PdfArray(); 415 for (int k = 0; k < inkList.length; ++k) { 416 PdfArray inner = new PdfArray(); 417 float deep[] = inkList[k]; 418 for (int j = 0; j < deep.length; ++j) 419 inner.add(new PdfNumber(deep[j])); 420 outer.add(inner); 421 } 422 annot.put(PdfName.INKLIST, outer); 423 return annot; 424 } 425 426 /** Creates a file attachment annotation. 427 * @param writer the <CODE>PdfWriter</CODE> 428 * @param rect the dimensions in the page of the annotation 429 * @param contents the file description 430 * @param fileStore an array with the file. If it's <CODE>null</CODE> 431 * the file will be read from the disk 432 * @param file the path to the file. It will only be used if 433 * <CODE>fileStore</CODE> is not <CODE>null</CODE> 434 * @param fileDisplay the actual file name stored in the pdf 435 * @throws IOException on error 436 * @return the annotation 437 */ 438 public static PdfAnnotation createFileAttachment(PdfWriter writer, Rectangle rect, String contents, byte fileStore[], String file, String fileDisplay) throws IOException { 439 return createFileAttachment(writer, rect, contents, PdfFileSpecification.fileEmbedded(writer, file, fileDisplay, fileStore)); 440 } 441 442 /** Creates a file attachment annotation 443 * @param writer 444 * @param rect 445 * @param contents 446 * @param fs 447 * @return the annotation 448 * @throws IOException 449 */ 450 public static PdfAnnotation createFileAttachment(PdfWriter writer, Rectangle rect, String contents, PdfFileSpecification fs) throws IOException { 451 PdfAnnotation annot = new PdfAnnotation(writer, rect); 452 annot.put(PdfName.SUBTYPE, PdfName.FILEATTACHMENT); 453 if (contents != null) 454 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 455 annot.put(PdfName.FS, fs.getReference()); 456 return annot; 457 } 458 459 /** 460 * Adds a popup to your document. 461 * @param writer 462 * @param rect 463 * @param contents 464 * @param open 465 * @return A PdfAnnotation 466 */ 467 public static PdfAnnotation createPopup(PdfWriter writer, Rectangle rect, String contents, boolean open) { 468 PdfAnnotation annot = new PdfAnnotation(writer, rect); 469 annot.put(PdfName.SUBTYPE, PdfName.POPUP); 470 if (contents != null) 471 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 472 if (open) 473 annot.put(PdfName.OPEN, PdfBoolean.PDFTRUE); 474 return annot; 475 } 476 477 /** 478 * Creates a polygon or -line annotation 479 * @param writer the PdfWriter 480 * @param rect the annotation position 481 * @param contents the textual content of the annotation 482 * @param polygon if true, the we're creating a polygon annotation, if false, a polyline 483 * @param vertices an array with the vertices of the polygon or -line 484 * @since 5.0.2 485 */ 486 public static PdfAnnotation createPolygonPolyline( 487 PdfWriter writer, Rectangle rect, String contents, boolean polygon, PdfArray vertices) { 488 PdfAnnotation annot = new PdfAnnotation(writer, rect); 489 if (polygon) 490 annot.put(PdfName.SUBTYPE, PdfName.POLYGON); 491 else 492 annot.put(PdfName.SUBTYPE, PdfName.POLYLINE); 493 annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE)); 494 annot.put(PdfName.VERTICES, new PdfArray(vertices)); 495 return annot; 496 } 497 498 public void setDefaultAppearanceString(PdfContentByte cb) { 499 byte b[] = cb.getInternalBuffer().toByteArray(); 500 int len = b.length; 501 for (int k = 0; k < len; ++k) { 502 if (b[k] == '\n') 503 b[k] = 32; 504 } 505 put(PdfName.DA, new PdfString(b)); 506 } 507 508 public void setFlags(int flags) { 509 if (flags == 0) 510 remove(PdfName.F); 511 else 512 put(PdfName.F, new PdfNumber(flags)); 513 } 514 515 public void setBorder(PdfBorderArray border) { 516 put(PdfName.BORDER, border); 517 } 518 519 public void setBorderStyle(PdfBorderDictionary border) { 520 put(PdfName.BS, border); 521 } 522 523 /** 524 * Sets the annotation's highlighting mode. The values can be 525 * <CODE>HIGHLIGHT_NONE</CODE>, <CODE>HIGHLIGHT_INVERT</CODE>, 526 * <CODE>HIGHLIGHT_OUTLINE</CODE> and <CODE>HIGHLIGHT_PUSH</CODE>; 527 * @param highlight the annotation's highlighting mode 528 */ 529 public void setHighlighting(PdfName highlight) { 530 if (highlight.equals(HIGHLIGHT_INVERT)) 531 remove(PdfName.H); 532 else 533 put(PdfName.H, highlight); 534 } 535 536 public void setAppearance(PdfName ap, PdfTemplate template) { 537 PdfDictionary dic = (PdfDictionary)get(PdfName.AP); 538 if (dic == null) 539 dic = new PdfDictionary(); 540 dic.put(ap, template.getIndirectReference()); 541 put(PdfName.AP, dic); 542 if (!form) 543 return; 544 if (templates == null) 545 templates = new HashSet<PdfTemplate>(); 546 templates.add(template); 547 } 548 549 public void setAppearance(PdfName ap, String state, PdfTemplate template) { 550 PdfDictionary dicAp = (PdfDictionary)get(PdfName.AP); 551 if (dicAp == null) 552 dicAp = new PdfDictionary(); 553 554 PdfDictionary dic; 555 PdfObject obj = dicAp.get(ap); 556 if (obj != null && obj.isDictionary()) 557 dic = (PdfDictionary)obj; 558 else 559 dic = new PdfDictionary(); 560 dic.put(new PdfName(state), template.getIndirectReference()); 561 dicAp.put(ap, dic); 562 put(PdfName.AP, dicAp); 563 if (!form) 564 return; 565 if (templates == null) 566 templates = new HashSet<PdfTemplate>(); 567 templates.add(template); 568 } 569 570 public void setAppearanceState(String state) { 571 if (state == null) { 572 remove(PdfName.AS); 573 return; 574 } 575 put(PdfName.AS, new PdfName(state)); 576 } 577 578 public void setColor(BaseColor color) { 579 put(PdfName.C, new PdfColor(color)); 580 } 581 582 public void setTitle(String title) { 583 if (title == null) { 584 remove(PdfName.T); 585 return; 586 } 587 put(PdfName.T, new PdfString(title, PdfObject.TEXT_UNICODE)); 588 } 589 590 public void setPopup(PdfAnnotation popup) { 591 put(PdfName.POPUP, popup.getIndirectReference()); 592 popup.put(PdfName.PARENT, getIndirectReference()); 593 } 594 595 public void setAction(PdfAction action) { 596 put(PdfName.A, action); 597 } 598 599 public void setAdditionalActions(PdfName key, PdfAction action) { 600 PdfDictionary dic; 601 PdfObject obj = get(PdfName.AA); 602 if (obj != null && obj.isDictionary()) 603 dic = (PdfDictionary)obj; 604 else 605 dic = new PdfDictionary(); 606 dic.put(key, action); 607 put(PdfName.AA, dic); 608 } 609 610 /** Getter for property used. 611 * @return Value of property used. 612 */ 613 public boolean isUsed() { 614 return used; 615 } 616 617 /** Setter for property used. 618 */ 619 public void setUsed() { 620 used = true; 621 } 622 623 public HashSet<PdfTemplate> getTemplates() { 624 return templates; 625 } 626 627 /** Getter for property form. 628 * @return Value of property form. 629 */ 630 public boolean isForm() { 631 return form; 632 } 633 634 /** Getter for property annotation. 635 * @return Value of property annotation. 636 */ 637 public boolean isAnnotation() { 638 return annotation; 639 } 640 641 public void setPage(int page) { 642 put(PdfName.P, writer.getPageReference(page)); 643 } 644 645 public void setPage() { 646 put(PdfName.P, writer.getCurrentPage()); 647 } 648 649 /** Getter for property placeInPage. 650 * @return Value of property placeInPage. 651 */ 652 public int getPlaceInPage() { 653 return placeInPage; 654 } 655 656 /** Places the annotation in a specified page that must be greater 657 * or equal to the current one. With <code>PdfStamper</code> the page 658 * can be any. The first page is 1. 659 * @param placeInPage New value of property placeInPage. 660 */ 661 public void setPlaceInPage(int placeInPage) { 662 this.placeInPage = placeInPage; 663 } 664 665 public void setRotate(int v) { 666 put(PdfName.ROTATE, new PdfNumber(v)); 667 } 668 669 PdfDictionary getMK() { 670 PdfDictionary mk = (PdfDictionary)get(PdfName.MK); 671 if (mk == null) { 672 mk = new PdfDictionary(); 673 put(PdfName.MK, mk); 674 } 675 return mk; 676 } 677 678 public void setMKRotation(int rotation) { 679 getMK().put(PdfName.R, new PdfNumber(rotation)); 680 } 681 682 public static PdfArray getMKColor(BaseColor color) { 683 PdfArray array = new PdfArray(); 684 int type = ExtendedColor.getType(color); 685 switch (type) { 686 case ExtendedColor.TYPE_GRAY: { 687 array.add(new PdfNumber(((GrayColor)color).getGray())); 688 break; 689 } 690 case ExtendedColor.TYPE_CMYK: { 691 CMYKColor cmyk = (CMYKColor)color; 692 array.add(new PdfNumber(cmyk.getCyan())); 693 array.add(new PdfNumber(cmyk.getMagenta())); 694 array.add(new PdfNumber(cmyk.getYellow())); 695 array.add(new PdfNumber(cmyk.getBlack())); 696 break; 697 } 698 case ExtendedColor.TYPE_SEPARATION: 699 case ExtendedColor.TYPE_PATTERN: 700 case ExtendedColor.TYPE_SHADING: 701 throw new RuntimeException(MessageLocalization.getComposedMessage("separations.patterns.and.shadings.are.not.allowed.in.mk.dictionary")); 702 default: 703 array.add(new PdfNumber(color.getRed() / 255f)); 704 array.add(new PdfNumber(color.getGreen() / 255f)); 705 array.add(new PdfNumber(color.getBlue() / 255f)); 706 } 707 return array; 708 } 709 710 public void setMKBorderColor(BaseColor color) { 711 if (color == null) 712 getMK().remove(PdfName.BC); 713 else 714 getMK().put(PdfName.BC, getMKColor(color)); 715 } 716 717 public void setMKBackgroundColor(BaseColor color) { 718 if (color == null) 719 getMK().remove(PdfName.BG); 720 else 721 getMK().put(PdfName.BG, getMKColor(color)); 722 } 723 724 public void setMKNormalCaption(String caption) { 725 getMK().put(PdfName.CA, new PdfString(caption, PdfObject.TEXT_UNICODE)); 726 } 727 728 public void setMKRolloverCaption(String caption) { 729 getMK().put(PdfName.RC, new PdfString(caption, PdfObject.TEXT_UNICODE)); 730 } 731 732 public void setMKAlternateCaption(String caption) { 733 getMK().put(PdfName.AC, new PdfString(caption, PdfObject.TEXT_UNICODE)); 734 } 735 736 public void setMKNormalIcon(PdfTemplate template) { 737 getMK().put(PdfName.I, template.getIndirectReference()); 738 } 739 740 public void setMKRolloverIcon(PdfTemplate template) { 741 getMK().put(PdfName.RI, template.getIndirectReference()); 742 } 743 744 public void setMKAlternateIcon(PdfTemplate template) { 745 getMK().put(PdfName.IX, template.getIndirectReference()); 746 } 747 748 public void setMKIconFit(PdfName scale, PdfName scalingType, float leftoverLeft, float leftoverBottom, boolean fitInBounds) { 749 PdfDictionary dic = new PdfDictionary(); 750 if (!scale.equals(PdfName.A)) 751 dic.put(PdfName.SW, scale); 752 if (!scalingType.equals(PdfName.P)) 753 dic.put(PdfName.S, scalingType); 754 if (leftoverLeft != 0.5f || leftoverBottom != 0.5f) { 755 PdfArray array = new PdfArray(new PdfNumber(leftoverLeft)); 756 array.add(new PdfNumber(leftoverBottom)); 757 dic.put(PdfName.A, array); 758 } 759 if (fitInBounds) 760 dic.put(PdfName.FB, PdfBoolean.PDFTRUE); 761 getMK().put(PdfName.IF, dic); 762 } 763 764 public void setMKTextPosition(int tp) { 765 getMK().put(PdfName.TP, new PdfNumber(tp)); 766 } 767 768 /** 769 * Sets the layer this annotation belongs to. 770 * @param layer the layer this annotation belongs to 771 */ 772 public void setLayer(PdfOCG layer) { 773 put(PdfName.OC, layer.getRef()); 774 } 775 776 /** 777 * Sets the name of the annotation. 778 * With this name the annotation can be identified among 779 * all the annotations on a page (it has to be unique). 780 */ 781 public void setName(String name) { 782 put(PdfName.NM, new PdfString(name)); 783 } 784 785 /** 786 * This class processes links from imported pages so that they may be active. The following example code reads a group 787 * of files and places them all on the output PDF, four pages in a single page, keeping the links active. 788 * <pre> 789 * String[] files = new String[] {"input1.pdf", "input2.pdf"}; 790 * String outputFile = "output.pdf"; 791 * int firstPage=1; 792 * Document document = new Document(); 793 * PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputFile)); 794 * document.setPageSize(PageSize.A4); 795 * float W = PageSize.A4.getWidth() / 2; 796 * float H = PageSize.A4.getHeight() / 2; 797 * document.open(); 798 * PdfContentByte cb = writer.getDirectContent(); 799 * for (int i = 0; i < files.length; i++) { 800 * PdfReader currentReader = new PdfReader(files[i]); 801 * currentReader.consolidateNamedDestinations(); 802 * for (int page = 1; page <= currentReader.getNumberOfPages(); page++) { 803 * PdfImportedPage importedPage = writer.getImportedPage(currentReader, page); 804 * float a = 0.5f; 805 * float e = (page % 2 == 0) ? W : 0; 806 * float f = (page % 4 == 1 || page % 4 == 2) ? H : 0; 807 * ArrayList links = currentReader.getLinks(page); 808 * cb.addTemplate(importedPage, a, 0, 0, a, e, f); 809 * for (int j = 0; j < links.size(); j++) { 810 * PdfAnnotation.PdfImportedLink link = (PdfAnnotation.PdfImportedLink)links.get(j); 811 * if (link.isInternal()) { 812 * int dPage = link.getDestinationPage(); 813 * int newDestPage = (dPage-1)/4 + firstPage; 814 * float ee = (dPage % 2 == 0) ? W : 0; 815 * float ff = (dPage % 4 == 1 || dPage % 4 == 2) ? H : 0; 816 * link.setDestinationPage(newDestPage); 817 * link.transformDestination(a, 0, 0, a, ee, ff); 818 * } 819 * link.transformRect(a, 0, 0, a, e, f); 820 * writer.addAnnotation(link.createAnnotation(writer)); 821 * } 822 * if (page % 4 == 0) 823 * document.newPage(); 824 * } 825 * if (i < files.length - 1) 826 * document.newPage(); 827 * firstPage += (currentReader.getNumberOfPages()+3)/4; 828 * } 829 * document.close(); 830 * </pre> 831 */ 832 public static class PdfImportedLink { 833 float llx, lly, urx, ury; 834 HashMap<PdfName, PdfObject> parameters = new HashMap<PdfName, PdfObject>(); 835 PdfArray destination = null; 836 int newPage=0; 837 838 PdfImportedLink(PdfDictionary annotation) { 839 parameters.putAll(annotation.hashMap); 840 try { 841 destination = (PdfArray) parameters.remove(PdfName.DEST); 842 } catch (ClassCastException ex) { 843 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("you.have.to.consolidate.the.named.destinations.of.your.reader")); 844 } 845 if (destination != null) { 846 destination = new PdfArray(destination); 847 } 848 PdfArray rc = (PdfArray) parameters.remove(PdfName.RECT); 849 llx = rc.getAsNumber(0).floatValue(); 850 lly = rc.getAsNumber(1).floatValue(); 851 urx = rc.getAsNumber(2).floatValue(); 852 ury = rc.getAsNumber(3).floatValue(); 853 } 854 855 public boolean isInternal() { 856 return destination != null; 857 } 858 859 public int getDestinationPage() { 860 if (!isInternal()) return 0; 861 862 // here destination is something like 863 // [132 0 R, /XYZ, 29.3898, 731.864502, null] 864 PdfIndirectReference ref = destination.getAsIndirectObject(0); 865 866 PRIndirectReference pr = (PRIndirectReference) ref; 867 PdfReader r = pr.getReader(); 868 for (int i = 1; i <= r.getNumberOfPages(); i++) { 869 PRIndirectReference pp = r.getPageOrigRef(i); 870 if (pp.getGeneration() == pr.getGeneration() && pp.getNumber() == pr.getNumber()) return i; 871 } 872 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("page.not.found")); 873 } 874 875 public void setDestinationPage(int newPage) { 876 if (!isInternal()) throw new IllegalArgumentException(MessageLocalization.getComposedMessage("cannot.change.destination.of.external.link")); 877 this.newPage=newPage; 878 } 879 880 public void transformDestination(float a, float b, float c, float d, float e, float f) { 881 if (!isInternal()) throw new IllegalArgumentException(MessageLocalization.getComposedMessage("cannot.change.destination.of.external.link")); 882 if (destination.getAsName(1).equals(PdfName.XYZ)) { 883 float x = destination.getAsNumber(2).floatValue(); 884 float y = destination.getAsNumber(3).floatValue(); 885 float xx = x * a + y * c + e; 886 float yy = x * b + y * d + f; 887 destination.set(2, new PdfNumber(xx)); 888 destination.set(3, new PdfNumber(yy)); 889 } 890 } 891 892 public void transformRect(float a, float b, float c, float d, float e, float f) { 893 float x = llx * a + lly * c + e; 894 float y = llx * b + lly * d + f; 895 llx = x; 896 lly = y; 897 x = urx * a + ury * c + e; 898 y = urx * b + ury * d + f; 899 urx = x; 900 ury = y; 901 } 902 903 public PdfAnnotation createAnnotation(PdfWriter writer) { 904 PdfAnnotation annotation = new PdfAnnotation(writer, new Rectangle(llx, lly, urx, ury)); 905 if (newPage != 0) { 906 PdfIndirectReference ref = writer.getPageReference(newPage); 907 destination.set(0, ref); 908 } 909 if (destination != null) annotation.put(PdfName.DEST, destination); 910 annotation.hashMap.putAll(parameters); 911 return annotation; 912 } 913 914 /** 915 * Returns a String representation of the link. 916 * @return a String representation of the imported link 917 * @since 2.1.6 918 */ 919 @Override 920 public String toString() { 921 StringBuffer buf = new StringBuffer("Imported link: location ["); 922 buf.append(llx); 923 buf.append(' '); 924 buf.append(lly); 925 buf.append(' '); 926 buf.append(urx); 927 buf.append(' '); 928 buf.append(ury); 929 buf.append("] destination "); 930 buf.append(destination); 931 buf.append(" parameters "); 932 buf.append(parameters); 933 return buf.toString(); 934 } 935 } 936}