001/* 002 * $Id: BaseField.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.ArrayList; 048import java.util.HashMap; 049import java.util.Iterator; 050 051import com.itextpdf.text.BaseColor; 052import com.itextpdf.text.DocumentException; 053import com.itextpdf.text.Element; 054import com.itextpdf.text.Rectangle; 055import com.itextpdf.text.error_messages.MessageLocalization; 056 057/** Common field variables. 058 * @author Paulo Soares 059 */ 060public abstract class BaseField { 061 062 /** A thin border with 1 point width. */ 063 public static final float BORDER_WIDTH_THIN = 1; 064 /** A medium border with 2 point width. */ 065 public static final float BORDER_WIDTH_MEDIUM = 2; 066 /** A thick border with 3 point width. */ 067 public static final float BORDER_WIDTH_THICK = 3; 068 /** The field is visible. */ 069 public static final int VISIBLE = 0; 070 /** The field is hidden. */ 071 public static final int HIDDEN = 1; 072 /** The field is visible but does not print. */ 073 public static final int VISIBLE_BUT_DOES_NOT_PRINT = 2; 074 /** The field is hidden but is printable. */ 075 public static final int HIDDEN_BUT_PRINTABLE = 3; 076 /** The user may not change the value of the field. */ 077 public static final int READ_ONLY = PdfFormField.FF_READ_ONLY; 078 /** The field must have a value at the time it is exported by a submit-form 079 * action. 080 */ 081 public static final int REQUIRED = PdfFormField.FF_REQUIRED; 082 /** The field may contain multiple lines of text. 083 * This flag is only meaningful with text fields. 084 */ 085 public static final int MULTILINE = PdfFormField.FF_MULTILINE; 086 /** The field will not scroll (horizontally for single-line 087 * fields, vertically for multiple-line fields) to accommodate more text 088 * than will fit within its annotation rectangle. Once the field is full, no 089 * further text will be accepted. 090 */ 091 public static final int DO_NOT_SCROLL = PdfFormField.FF_DONOTSCROLL; 092 /** The field is intended for entering a secure password that should 093 * not be echoed visibly to the screen. 094 */ 095 public static final int PASSWORD = PdfFormField.FF_PASSWORD; 096 /** The text entered in the field represents the pathname of 097 * a file whose contents are to be submitted as the value of the field. 098 */ 099 public static final int FILE_SELECTION = PdfFormField.FF_FILESELECT; 100 /** The text entered in the field will not be spell-checked. 101 * This flag is meaningful only in text fields and in combo 102 * fields with the <CODE>EDIT</CODE> flag set. 103 */ 104 public static final int DO_NOT_SPELL_CHECK = PdfFormField.FF_DONOTSPELLCHECK; 105 /** If set the combo box includes an editable text box as well as a drop list; if 106 * clear, it includes only a drop list. 107 * This flag is only meaningful with combo fields. 108 */ 109 public static final int EDIT = PdfFormField.FF_EDIT; 110 111 /** whether or not a list may have multiple selections. Only applies to /CH LIST 112 * fields, not combo boxes. 113 */ 114 public static final int MULTISELECT = PdfFormField.FF_MULTISELECT; 115 116 /** 117 * combo box flag. 118 */ 119 public static final int COMB = PdfFormField.FF_COMB; 120 121 protected float borderWidth = BORDER_WIDTH_THIN; 122 protected int borderStyle = PdfBorderDictionary.STYLE_SOLID; 123 protected BaseColor borderColor; 124 protected BaseColor backgroundColor; 125 protected BaseColor textColor; 126 protected BaseFont font; 127 protected float fontSize = 0; 128 protected int alignment = Element.ALIGN_LEFT; 129 protected PdfWriter writer; 130 protected String text; 131 protected Rectangle box; 132 133 /** Holds value of property rotation. */ 134 protected int rotation = 0; 135 136 /** Holds value of property visibility. */ 137 protected int visibility; 138 139 /** Holds value of property fieldName. */ 140 protected String fieldName; 141 142 /** Holds value of property options. */ 143 protected int options; 144 145 /** Holds value of property maxCharacterLength. */ 146 protected int maxCharacterLength; 147 148 private final static HashMap<PdfName, Integer> fieldKeys = new HashMap<PdfName, Integer>(); 149 150 static { 151 fieldKeys.putAll(PdfCopyFieldsImp.fieldKeys); 152 fieldKeys.put(PdfName.T, Integer.valueOf(1)); 153 } 154 /** Creates a new <CODE>TextField</CODE>. 155 * @param writer the document <CODE>PdfWriter</CODE> 156 * @param box the field location and dimensions 157 * @param fieldName the field name. If <CODE>null</CODE> only the widget keys 158 * will be included in the field allowing it to be used as a kid field. 159 */ 160 public BaseField(PdfWriter writer, Rectangle box, String fieldName) { 161 this.writer = writer; 162 setBox(box); 163 this.fieldName = fieldName; 164 } 165 166 protected BaseFont getRealFont() throws IOException, DocumentException { 167 if (font == null) 168 return BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, false); 169 else 170 return font; 171 } 172 173 protected PdfAppearance getBorderAppearance() { 174 PdfAppearance app = PdfAppearance.createAppearance(writer, box.getWidth(), box.getHeight()); 175 switch (rotation) { 176 case 90: 177 app.setMatrix(0, 1, -1, 0, box.getHeight(), 0); 178 break; 179 case 180: 180 app.setMatrix(-1, 0, 0, -1, box.getWidth(), box.getHeight()); 181 break; 182 case 270: 183 app.setMatrix(0, -1, 1, 0, 0, box.getWidth()); 184 break; 185 } 186 app.saveState(); 187 // background 188 if (backgroundColor != null) { 189 app.setColorFill(backgroundColor); 190 app.rectangle(0, 0, box.getWidth(), box.getHeight()); 191 app.fill(); 192 } 193 // border 194 if (borderStyle == PdfBorderDictionary.STYLE_UNDERLINE) { 195 if (borderWidth != 0 && borderColor != null) { 196 app.setColorStroke(borderColor); 197 app.setLineWidth(borderWidth); 198 app.moveTo(0, borderWidth / 2); 199 app.lineTo(box.getWidth(), borderWidth / 2); 200 app.stroke(); 201 } 202 } 203 else if (borderStyle == PdfBorderDictionary.STYLE_BEVELED) { 204 if (borderWidth != 0 && borderColor != null) { 205 app.setColorStroke(borderColor); 206 app.setLineWidth(borderWidth); 207 app.rectangle(borderWidth / 2, borderWidth / 2, box.getWidth() - borderWidth, box.getHeight() - borderWidth); 208 app.stroke(); 209 } 210 // beveled 211 BaseColor actual = backgroundColor; 212 if (actual == null) 213 actual = BaseColor.WHITE; 214 app.setGrayFill(1); 215 drawTopFrame(app); 216 app.setColorFill(actual.darker()); 217 drawBottomFrame(app); 218 } 219 else if (borderStyle == PdfBorderDictionary.STYLE_INSET) { 220 if (borderWidth != 0 && borderColor != null) { 221 app.setColorStroke(borderColor); 222 app.setLineWidth(borderWidth); 223 app.rectangle(borderWidth / 2, borderWidth / 2, box.getWidth() - borderWidth, box.getHeight() - borderWidth); 224 app.stroke(); 225 } 226 // inset 227 app.setGrayFill(0.5f); 228 drawTopFrame(app); 229 app.setGrayFill(0.75f); 230 drawBottomFrame(app); 231 } 232 else { 233 if (borderWidth != 0 && borderColor != null) { 234 if (borderStyle == PdfBorderDictionary.STYLE_DASHED) 235 app.setLineDash(3, 0); 236 app.setColorStroke(borderColor); 237 app.setLineWidth(borderWidth); 238 app.rectangle(borderWidth / 2, borderWidth / 2, box.getWidth() - borderWidth, box.getHeight() - borderWidth); 239 app.stroke(); 240 if ((options & COMB) != 0 && maxCharacterLength > 1) { 241 float step = box.getWidth() / maxCharacterLength; 242 float yb = borderWidth / 2; 243 float yt = box.getHeight() - borderWidth / 2; 244 for (int k = 1; k < maxCharacterLength; ++k) { 245 float x = step * k; 246 app.moveTo(x, yb); 247 app.lineTo(x, yt); 248 } 249 app.stroke(); 250 } 251 } 252 } 253 app.restoreState(); 254 return app; 255 } 256 257 protected static ArrayList<String> getHardBreaks(String text) { 258 ArrayList<String> arr = new ArrayList<String>(); 259 char cs[] = text.toCharArray(); 260 int len = cs.length; 261 StringBuffer buf = new StringBuffer(); 262 for (int k = 0; k < len; ++k) { 263 char c = cs[k]; 264 if (c == '\r') { 265 if (k + 1 < len && cs[k + 1] == '\n') 266 ++k; 267 arr.add(buf.toString()); 268 buf = new StringBuffer(); 269 } 270 else if (c == '\n') { 271 arr.add(buf.toString()); 272 buf = new StringBuffer(); 273 } 274 else 275 buf.append(c); 276 } 277 arr.add(buf.toString()); 278 return arr; 279 } 280 281 protected static void trimRight(StringBuffer buf) { 282 int len = buf.length(); 283 while (true) { 284 if (len == 0) 285 return; 286 if (buf.charAt(--len) != ' ') 287 return; 288 buf.setLength(len); 289 } 290 } 291 292 protected static ArrayList<String> breakLines(ArrayList<String> breaks, BaseFont font, float fontSize, float width) { 293 ArrayList<String> lines = new ArrayList<String>(); 294 StringBuffer buf = new StringBuffer(); 295 for (int ck = 0; ck < breaks.size(); ++ck) { 296 buf.setLength(0); 297 float w = 0; 298 char cs[] = breaks.get(ck).toCharArray(); 299 int len = cs.length; 300 // 0 inline first, 1 inline, 2 spaces 301 int state = 0; 302 int lastspace = -1; 303 char c = 0; 304 int refk = 0; 305 for (int k = 0; k < len; ++k) { 306 c = cs[k]; 307 switch (state) { 308 case 0: 309 w += font.getWidthPoint(c, fontSize); 310 buf.append(c); 311 if (w > width) { 312 w = 0; 313 if (buf.length() > 1) { 314 --k; 315 buf.setLength(buf.length() - 1); 316 } 317 lines.add(buf.toString()); 318 buf.setLength(0); 319 refk = k; 320 if (c == ' ') 321 state = 2; 322 else 323 state = 1; 324 } 325 else { 326 if (c != ' ') 327 state = 1; 328 } 329 break; 330 case 1: 331 w += font.getWidthPoint(c, fontSize); 332 buf.append(c); 333 if (c == ' ') 334 lastspace = k; 335 if (w > width) { 336 w = 0; 337 if (lastspace >= 0) { 338 k = lastspace; 339 buf.setLength(lastspace - refk); 340 trimRight(buf); 341 lines.add(buf.toString()); 342 buf.setLength(0); 343 refk = k; 344 lastspace = -1; 345 state = 2; 346 } 347 else { 348 if (buf.length() > 1) { 349 --k; 350 buf.setLength(buf.length() - 1); 351 } 352 lines.add(buf.toString()); 353 buf.setLength(0); 354 refk = k; 355 if (c == ' ') 356 state = 2; 357 } 358 } 359 break; 360 case 2: 361 if (c != ' ') { 362 w = 0; 363 --k; 364 state = 1; 365 } 366 break; 367 } 368 } 369 trimRight(buf); 370 lines.add(buf.toString()); 371 } 372 return lines; 373 } 374 375 private void drawTopFrame(PdfAppearance app) { 376 app.moveTo(borderWidth, borderWidth); 377 app.lineTo(borderWidth, box.getHeight() - borderWidth); 378 app.lineTo(box.getWidth() - borderWidth, box.getHeight() - borderWidth); 379 app.lineTo(box.getWidth() - 2 * borderWidth, box.getHeight() - 2 * borderWidth); 380 app.lineTo(2 * borderWidth, box.getHeight() - 2 * borderWidth); 381 app.lineTo(2 * borderWidth, 2 * borderWidth); 382 app.lineTo(borderWidth, borderWidth); 383 app.fill(); 384 } 385 386 private void drawBottomFrame(PdfAppearance app) { 387 app.moveTo(borderWidth, borderWidth); 388 app.lineTo(box.getWidth() - borderWidth, borderWidth); 389 app.lineTo(box.getWidth() - borderWidth, box.getHeight() - borderWidth); 390 app.lineTo(box.getWidth() - 2 * borderWidth, box.getHeight() - 2 * borderWidth); 391 app.lineTo(box.getWidth() - 2 * borderWidth, 2 * borderWidth); 392 app.lineTo(2 * borderWidth, 2 * borderWidth); 393 app.lineTo(borderWidth, borderWidth); 394 app.fill(); 395 } 396 /** Gets the border width in points. 397 * @return the border width in points 398 */ 399 public float getBorderWidth() { 400 return this.borderWidth; 401 } 402 403 /** Sets the border width in points. To eliminate the border 404 * set the border color to <CODE>null</CODE>. 405 * @param borderWidth the border width in points 406 */ 407 public void setBorderWidth(float borderWidth) { 408 this.borderWidth = borderWidth; 409 } 410 411 /** Gets the border style. 412 * @return the border style 413 */ 414 public int getBorderStyle() { 415 return this.borderStyle; 416 } 417 418 /** Sets the border style. The styles are found in <CODE>PdfBorderDictionary</CODE> 419 * and can be <CODE>STYLE_SOLID</CODE>, <CODE>STYLE_DASHED</CODE>, 420 * <CODE>STYLE_BEVELED</CODE>, <CODE>STYLE_INSET</CODE> and 421 * <CODE>STYLE_UNDERLINE</CODE>. 422 * @param borderStyle the border style 423 */ 424 public void setBorderStyle(int borderStyle) { 425 this.borderStyle = borderStyle; 426 } 427 428 /** Gets the border color. 429 * @return the border color 430 */ 431 public BaseColor getBorderColor() { 432 return this.borderColor; 433 } 434 435 /** Sets the border color. Set to <CODE>null</CODE> to remove 436 * the border. 437 * @param borderColor the border color 438 */ 439 public void setBorderColor(BaseColor borderColor) { 440 this.borderColor = borderColor; 441 } 442 443 /** Gets the background color. 444 * @return the background color 445 */ 446 public BaseColor getBackgroundColor() { 447 return this.backgroundColor; 448 } 449 450 /** Sets the background color. Set to <CODE>null</CODE> for 451 * transparent background. 452 * @param backgroundColor the background color 453 */ 454 public void setBackgroundColor(BaseColor backgroundColor) { 455 this.backgroundColor = backgroundColor; 456 } 457 458 /** Gets the text color. 459 * @return the text color 460 */ 461 public BaseColor getTextColor() { 462 return this.textColor; 463 } 464 465 /** Sets the text color. If <CODE>null</CODE> the color used 466 * will be black. 467 * @param textColor the text color 468 */ 469 public void setTextColor(BaseColor textColor) { 470 this.textColor = textColor; 471 } 472 473 /** Gets the text font. 474 * @return the text font 475 */ 476 public BaseFont getFont() { 477 return this.font; 478 } 479 480 /** Sets the text font. If <CODE>null</CODE> then Helvetica 481 * will be used. 482 * @param font the text font 483 */ 484 public void setFont(BaseFont font) { 485 this.font = font; 486 } 487 488 /** Gets the font size. 489 * @return the font size 490 */ 491 public float getFontSize() { 492 return this.fontSize; 493 } 494 495 /** Sets the font size. If 0 then auto-sizing will be used but 496 * only for text fields. 497 * @param fontSize the font size 498 */ 499 public void setFontSize(float fontSize) { 500 this.fontSize = fontSize; 501 } 502 503 /** Gets the text horizontal alignment. 504 * @return the text horizontal alignment 505 */ 506 public int getAlignment() { 507 return this.alignment; 508 } 509 510 /** Sets the text horizontal alignment. It can be <CODE>Element.ALIGN_LEFT</CODE>, 511 * <CODE>Element.ALIGN_CENTER</CODE> and <CODE>Element.ALIGN_RIGHT</CODE>. 512 * @param alignment the text horizontal alignment 513 */ 514 public void setAlignment(int alignment) { 515 this.alignment = alignment; 516 } 517 518 /** Gets the text. 519 * @return the text 520 */ 521 public String getText() { 522 return this.text; 523 } 524 525 /** Sets the text for text fields. 526 * @param text the text 527 */ 528 public void setText(String text) { 529 this.text = text; 530 } 531 532 /** Gets the field dimension and position. 533 * @return the field dimension and position 534 */ 535 public Rectangle getBox() { 536 return this.box; 537 } 538 539 /** Sets the field dimension and position. 540 * @param box the field dimension and position 541 */ 542 public void setBox(Rectangle box) { 543 if (box == null) { 544 this.box = null; 545 } 546 else { 547 this.box = new Rectangle(box); 548 this.box.normalize(); 549 } 550 } 551 552 /** Gets the field rotation. 553 * @return the field rotation 554 */ 555 public int getRotation() { 556 return this.rotation; 557 } 558 559 /** Sets the field rotation. This value should be the same as 560 * the page rotation where the field will be shown. 561 * @param rotation the field rotation 562 */ 563 public void setRotation(int rotation) { 564 if (rotation % 90 != 0) 565 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("rotation.must.be.a.multiple.of.90")); 566 rotation %= 360; 567 if (rotation < 0) 568 rotation += 360; 569 this.rotation = rotation; 570 } 571 572 /** Convenience method to set the field rotation the same as the 573 * page rotation. 574 * @param page the page 575 */ 576 public void setRotationFromPage(Rectangle page) { 577 setRotation(page.getRotation()); 578 } 579 580 /** Gets the field visibility flag. 581 * @return the field visibility flag 582 */ 583 public int getVisibility() { 584 return this.visibility; 585 } 586 587 /** Sets the field visibility flag. This flags can be one of 588 * <CODE>VISIBLE</CODE>, <CODE>HIDDEN</CODE>, <CODE>VISIBLE_BUT_DOES_NOT_PRINT</CODE> 589 * and <CODE>HIDDEN_BUT_PRINTABLE</CODE>. 590 * @param visibility field visibility flag 591 */ 592 public void setVisibility(int visibility) { 593 this.visibility = visibility; 594 } 595 596 /** Gets the field name. 597 * @return the field name 598 */ 599 public String getFieldName() { 600 return this.fieldName; 601 } 602 603 /** Sets the field name. 604 * @param fieldName the field name. If <CODE>null</CODE> only the widget keys 605 * will be included in the field allowing it to be used as a kid field. 606 */ 607 public void setFieldName(String fieldName) { 608 this.fieldName = fieldName; 609 } 610 611 /** Gets the option flags. 612 * @return the option flags 613 */ 614 public int getOptions() { 615 return this.options; 616 } 617 618 /** Sets the option flags. The option flags can be a combination by oring of 619 * <CODE>READ_ONLY</CODE>, <CODE>REQUIRED</CODE>, 620 * <CODE>MULTILINE</CODE>, <CODE>DO_NOT_SCROLL</CODE>, 621 * <CODE>PASSWORD</CODE>, <CODE>FILE_SELECTION</CODE>, 622 * <CODE>DO_NOT_SPELL_CHECK</CODE> and <CODE>EDIT</CODE>. 623 * @param options the option flags 624 */ 625 public void setOptions(int options) { 626 this.options = options; 627 } 628 629 /** Gets the maximum length of the field's text, in characters. 630 * @return the maximum length of the field's text, in characters. 631 */ 632 public int getMaxCharacterLength() { 633 return this.maxCharacterLength; 634 } 635 636 /** Sets the maximum length of the field's text, in characters. 637 * It is only meaningful for text fields. 638 * @param maxCharacterLength the maximum length of the field's text, in characters 639 */ 640 public void setMaxCharacterLength(int maxCharacterLength) { 641 this.maxCharacterLength = maxCharacterLength; 642 } 643 644 /** 645 * Getter for property writer. 646 * @return Value of property writer. 647 */ 648 public PdfWriter getWriter() { 649 return writer; 650 } 651 652 /** 653 * Setter for property writer. 654 * @param writer New value of property writer. 655 */ 656 public void setWriter(PdfWriter writer) { 657 this.writer = writer; 658 } 659 660 /** 661 * Moves the field keys from <CODE>from</CODE> to <CODE>to</CODE>. The moved keys 662 * are removed from <CODE>from</CODE>. 663 * @param from the source 664 * @param to the destination. It may be <CODE>null</CODE> 665 */ 666 public static void moveFields(PdfDictionary from, PdfDictionary to) { 667 for (Iterator<PdfName> i = from.getKeys().iterator(); i.hasNext();) { 668 PdfName key = i.next(); 669 if (fieldKeys.containsKey(key)) { 670 if (to != null) 671 to.put(key, from.get(key)); 672 i.remove(); 673 } 674 } 675 } 676}