1 /* 2 * Copyright 2005 by Paulo Soares. 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 1.1 5 * (the "License"); you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at http://www.mozilla.org/MPL/ 7 * 8 * Software distributed under the License is distributed on an "AS IS" basis, 9 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 10 * for the specific language governing rights and limitations under the License. 11 * 12 * The Original Code is 'iText, a free JAVA-PDF library'. 13 * 14 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by 15 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. 16 * All Rights Reserved. 17 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer 18 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. 19 * 20 * Contributor(s): all the names of the contributors are added in the source code 21 * where applicable. 22 * 23 * Alternatively, the contents of this file may be used under the terms of the 24 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the 25 * provisions of LGPL are applicable instead of those above. If you wish to 26 * allow use of your version of this file only under the terms of the LGPL 27 * License and not to allow others to use your version of this file under 28 * the MPL, indicate your decision by deleting the provisions above and 29 * replace them with the notice and other provisions required by the LGPL. 30 * If you do not delete the provisions above, a recipient may use your version 31 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. 32 * 33 * This library is free software; you can redistribute it and/or modify it 34 * under the terms of the MPL as stated above or under the terms of the GNU 35 * Library General Public License as published by the Free Software Foundation; 36 * either version 2 of the License, or any later version. 37 * 38 * This library is distributed in the hope that it will be useful, but WITHOUT 39 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 40 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more 41 * details. 42 * 43 * If you didn't download this code from the following link, you should check if 44 * you aren't using an obsolete version: 45 * http://www.lowagie.com/iText/ 46 */ 47 48 package com.lowagie.text.pdf; 49 50 import java.awt.Color; 51 import java.io.IOException; 52 import java.util.ArrayList; 53 import java.util.HashMap; 54 import java.util.Iterator; 55 import com.lowagie.text.error_messages.MessageLocalization; 56 57 import com.lowagie.text.DocumentException; 58 import com.lowagie.text.Element; 59 import com.lowagie.text.Rectangle; 60 61 /** Common field variables. 62 * @author Paulo Soares (psoares@consiste.pt) 63 */ 64 public abstract class BaseField { 65 66 /** A thin border with 1 point width. */ 67 public static final float BORDER_WIDTH_THIN = 1; 68 /** A medium border with 2 point width. */ 69 public static final float BORDER_WIDTH_MEDIUM = 2; 70 /** A thick border with 3 point width. */ 71 public static final float BORDER_WIDTH_THICK = 3; 72 /** The field is visible. */ 73 public static final int VISIBLE = 0; 74 /** The field is hidden. */ 75 public static final int HIDDEN = 1; 76 /** The field is visible but does not print. */ 77 public static final int VISIBLE_BUT_DOES_NOT_PRINT = 2; 78 /** The field is hidden but is printable. */ 79 public static final int HIDDEN_BUT_PRINTABLE = 3; 80 /** The user may not change the value of the field. */ 81 public static final int READ_ONLY = PdfFormField.FF_READ_ONLY; 82 /** The field must have a value at the time it is exported by a submit-form 83 * action. 84 */ 85 public static final int REQUIRED = PdfFormField.FF_REQUIRED; 86 /** The field may contain multiple lines of text. 87 * This flag is only meaningful with text fields. 88 */ 89 public static final int MULTILINE = PdfFormField.FF_MULTILINE; 90 /** The field will not scroll (horizontally for single-line 91 * fields, vertically for multiple-line fields) to accommodate more text 92 * than will fit within its annotation rectangle. Once the field is full, no 93 * further text will be accepted. 94 */ 95 public static final int DO_NOT_SCROLL = PdfFormField.FF_DONOTSCROLL; 96 /** The field is intended for entering a secure password that should 97 * not be echoed visibly to the screen. 98 */ 99 public static final int PASSWORD = PdfFormField.FF_PASSWORD; 100 /** The text entered in the field represents the pathname of 101 * a file whose contents are to be submitted as the value of the field. 102 */ 103 public static final int FILE_SELECTION = PdfFormField.FF_FILESELECT; 104 /** The text entered in the field will not be spell-checked. 105 * This flag is meaningful only in text fields and in combo 106 * fields with the <CODE>EDIT</CODE> flag set. 107 */ 108 public static final int DO_NOT_SPELL_CHECK = PdfFormField.FF_DONOTSPELLCHECK; 109 /** If set the combo box includes an editable text box as well as a drop list; if 110 * clear, it includes only a drop list. 111 * This flag is only meaningful with combo fields. 112 */ 113 public static final int EDIT = PdfFormField.FF_EDIT; 114 115 /** whether or not a list may have multiple selections. Only applies to /CH LIST 116 * fields, not combo boxes. 117 */ 118 public static final int MULTISELECT = PdfFormField.FF_MULTISELECT; 119 120 /** 121 * combo box flag. 122 */ 123 public static final int COMB = PdfFormField.FF_COMB; 124 125 protected float borderWidth = BORDER_WIDTH_THIN; 126 protected int borderStyle = PdfBorderDictionary.STYLE_SOLID; 127 protected Color borderColor; 128 protected Color backgroundColor; 129 protected Color textColor; 130 protected BaseFont font; 131 protected float fontSize = 0; 132 protected int alignment = Element.ALIGN_LEFT; 133 protected PdfWriter writer; 134 protected String text; 135 protected Rectangle box; 136 137 /** Holds value of property rotation. */ 138 protected int rotation = 0; 139 140 /** Holds value of property visibility. */ 141 protected int visibility; 142 143 /** Holds value of property fieldName. */ 144 protected String fieldName; 145 146 /** Holds value of property options. */ 147 protected int options; 148 149 /** Holds value of property maxCharacterLength. */ 150 protected int maxCharacterLength; 151 152 private final static HashMap fieldKeys = new HashMap(); 153 154 static { 155 fieldKeys.putAll(PdfCopyFieldsImp.fieldKeys); fieldKeys.put(PdfName.T, new Integer(1))156 fieldKeys.put(PdfName.T, new Integer(1)); 157 } 158 /** Creates a new <CODE>TextField</CODE>. 159 * @param writer the document <CODE>PdfWriter</CODE> 160 * @param box the field location and dimensions 161 * @param fieldName the field name. If <CODE>null</CODE> only the widget keys 162 * will be included in the field allowing it to be used as a kid field. 163 */ BaseField(PdfWriter writer, Rectangle box, String fieldName)164 public BaseField(PdfWriter writer, Rectangle box, String fieldName) { 165 this.writer = writer; 166 setBox(box); 167 this.fieldName = fieldName; 168 } 169 getRealFont()170 protected BaseFont getRealFont() throws IOException, DocumentException { 171 if (font == null) 172 return BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, false); 173 else 174 return font; 175 } 176 getBorderAppearance()177 protected PdfAppearance getBorderAppearance() { 178 PdfAppearance app = PdfAppearance.createAppearance(writer, box.getWidth(), box.getHeight()); 179 switch (rotation) { 180 case 90: 181 app.setMatrix(0, 1, -1, 0, box.getHeight(), 0); 182 break; 183 case 180: 184 app.setMatrix(-1, 0, 0, -1, box.getWidth(), box.getHeight()); 185 break; 186 case 270: 187 app.setMatrix(0, -1, 1, 0, 0, box.getWidth()); 188 break; 189 } 190 app.saveState(); 191 // background 192 if (backgroundColor != null) { 193 app.setColorFill(backgroundColor); 194 app.rectangle(0, 0, box.getWidth(), box.getHeight()); 195 app.fill(); 196 } 197 // border 198 if (borderStyle == PdfBorderDictionary.STYLE_UNDERLINE) { 199 if (borderWidth != 0 && borderColor != null) { 200 app.setColorStroke(borderColor); 201 app.setLineWidth(borderWidth); 202 app.moveTo(0, borderWidth / 2); 203 app.lineTo(box.getWidth(), borderWidth / 2); 204 app.stroke(); 205 } 206 } 207 else if (borderStyle == PdfBorderDictionary.STYLE_BEVELED) { 208 if (borderWidth != 0 && borderColor != null) { 209 app.setColorStroke(borderColor); 210 app.setLineWidth(borderWidth); 211 app.rectangle(borderWidth / 2, borderWidth / 2, box.getWidth() - borderWidth, box.getHeight() - borderWidth); 212 app.stroke(); 213 } 214 // beveled 215 Color actual = backgroundColor; 216 if (actual == null) 217 actual = Color.white; 218 app.setGrayFill(1); 219 drawTopFrame(app); 220 app.setColorFill(actual.darker()); 221 drawBottomFrame(app); 222 } 223 else if (borderStyle == PdfBorderDictionary.STYLE_INSET) { 224 if (borderWidth != 0 && borderColor != null) { 225 app.setColorStroke(borderColor); 226 app.setLineWidth(borderWidth); 227 app.rectangle(borderWidth / 2, borderWidth / 2, box.getWidth() - borderWidth, box.getHeight() - borderWidth); 228 app.stroke(); 229 } 230 // inset 231 app.setGrayFill(0.5f); 232 drawTopFrame(app); 233 app.setGrayFill(0.75f); 234 drawBottomFrame(app); 235 } 236 else { 237 if (borderWidth != 0 && borderColor != null) { 238 if (borderStyle == PdfBorderDictionary.STYLE_DASHED) 239 app.setLineDash(3, 0); 240 app.setColorStroke(borderColor); 241 app.setLineWidth(borderWidth); 242 app.rectangle(borderWidth / 2, borderWidth / 2, box.getWidth() - borderWidth, box.getHeight() - borderWidth); 243 app.stroke(); 244 if ((options & COMB) != 0 && maxCharacterLength > 1) { 245 float step = box.getWidth() / maxCharacterLength; 246 float yb = borderWidth / 2; 247 float yt = box.getHeight() - borderWidth / 2; 248 for (int k = 1; k < maxCharacterLength; ++k) { 249 float x = step * k; 250 app.moveTo(x, yb); 251 app.lineTo(x, yt); 252 } 253 app.stroke(); 254 } 255 } 256 } 257 app.restoreState(); 258 return app; 259 } 260 getHardBreaks(String text)261 protected static ArrayList getHardBreaks(String text) { 262 ArrayList arr = new ArrayList(); 263 char cs[] = text.toCharArray(); 264 int len = cs.length; 265 StringBuffer buf = new StringBuffer(); 266 for (int k = 0; k < len; ++k) { 267 char c = cs[k]; 268 if (c == '\r') { 269 if (k + 1 < len && cs[k + 1] == '\n') 270 ++k; 271 arr.add(buf.toString()); 272 buf = new StringBuffer(); 273 } 274 else if (c == '\n') { 275 arr.add(buf.toString()); 276 buf = new StringBuffer(); 277 } 278 else 279 buf.append(c); 280 } 281 arr.add(buf.toString()); 282 return arr; 283 } 284 trimRight(StringBuffer buf)285 protected static void trimRight(StringBuffer buf) { 286 int len = buf.length(); 287 while (true) { 288 if (len == 0) 289 return; 290 if (buf.charAt(--len) != ' ') 291 return; 292 buf.setLength(len); 293 } 294 } 295 breakLines(ArrayList breaks, BaseFont font, float fontSize, float width)296 protected static ArrayList breakLines(ArrayList breaks, BaseFont font, float fontSize, float width) { 297 ArrayList lines = new ArrayList(); 298 StringBuffer buf = new StringBuffer(); 299 for (int ck = 0; ck < breaks.size(); ++ck) { 300 buf.setLength(0); 301 float w = 0; 302 char cs[] = ((String)breaks.get(ck)).toCharArray(); 303 int len = cs.length; 304 // 0 inline first, 1 inline, 2 spaces 305 int state = 0; 306 int lastspace = -1; 307 char c = 0; 308 int refk = 0; 309 for (int k = 0; k < len; ++k) { 310 c = cs[k]; 311 switch (state) { 312 case 0: 313 w += font.getWidthPoint(c, fontSize); 314 buf.append(c); 315 if (w > width) { 316 w = 0; 317 if (buf.length() > 1) { 318 --k; 319 buf.setLength(buf.length() - 1); 320 } 321 lines.add(buf.toString()); 322 buf.setLength(0); 323 refk = k; 324 if (c == ' ') 325 state = 2; 326 else 327 state = 1; 328 } 329 else { 330 if (c != ' ') 331 state = 1; 332 } 333 break; 334 case 1: 335 w += font.getWidthPoint(c, fontSize); 336 buf.append(c); 337 if (c == ' ') 338 lastspace = k; 339 if (w > width) { 340 w = 0; 341 if (lastspace >= 0) { 342 k = lastspace; 343 buf.setLength(lastspace - refk); 344 trimRight(buf); 345 lines.add(buf.toString()); 346 buf.setLength(0); 347 refk = k; 348 lastspace = -1; 349 state = 2; 350 } 351 else { 352 if (buf.length() > 1) { 353 --k; 354 buf.setLength(buf.length() - 1); 355 } 356 lines.add(buf.toString()); 357 buf.setLength(0); 358 refk = k; 359 if (c == ' ') 360 state = 2; 361 } 362 } 363 break; 364 case 2: 365 if (c != ' ') { 366 w = 0; 367 --k; 368 state = 1; 369 } 370 break; 371 } 372 } 373 trimRight(buf); 374 lines.add(buf.toString()); 375 } 376 return lines; 377 } 378 drawTopFrame(PdfAppearance app)379 private void drawTopFrame(PdfAppearance app) { 380 app.moveTo(borderWidth, borderWidth); 381 app.lineTo(borderWidth, box.getHeight() - borderWidth); 382 app.lineTo(box.getWidth() - borderWidth, box.getHeight() - borderWidth); 383 app.lineTo(box.getWidth() - 2 * borderWidth, box.getHeight() - 2 * borderWidth); 384 app.lineTo(2 * borderWidth, box.getHeight() - 2 * borderWidth); 385 app.lineTo(2 * borderWidth, 2 * borderWidth); 386 app.lineTo(borderWidth, borderWidth); 387 app.fill(); 388 } 389 drawBottomFrame(PdfAppearance app)390 private void drawBottomFrame(PdfAppearance app) { 391 app.moveTo(borderWidth, borderWidth); 392 app.lineTo(box.getWidth() - borderWidth, borderWidth); 393 app.lineTo(box.getWidth() - borderWidth, box.getHeight() - borderWidth); 394 app.lineTo(box.getWidth() - 2 * borderWidth, box.getHeight() - 2 * borderWidth); 395 app.lineTo(box.getWidth() - 2 * borderWidth, 2 * borderWidth); 396 app.lineTo(2 * borderWidth, 2 * borderWidth); 397 app.lineTo(borderWidth, borderWidth); 398 app.fill(); 399 } 400 /** Gets the border width in points. 401 * @return the border width in points 402 */ getBorderWidth()403 public float getBorderWidth() { 404 return this.borderWidth; 405 } 406 407 /** Sets the border width in points. To eliminate the border 408 * set the border color to <CODE>null</CODE>. 409 * @param borderWidth the border width in points 410 */ setBorderWidth(float borderWidth)411 public void setBorderWidth(float borderWidth) { 412 this.borderWidth = borderWidth; 413 } 414 415 /** Gets the border style. 416 * @return the border style 417 */ getBorderStyle()418 public int getBorderStyle() { 419 return this.borderStyle; 420 } 421 422 /** Sets the border style. The styles are found in <CODE>PdfBorderDictionary</CODE> 423 * and can be <CODE>STYLE_SOLID</CODE>, <CODE>STYLE_DASHED</CODE>, 424 * <CODE>STYLE_BEVELED</CODE>, <CODE>STYLE_INSET</CODE> and 425 * <CODE>STYLE_UNDERLINE</CODE>. 426 * @param borderStyle the border style 427 */ setBorderStyle(int borderStyle)428 public void setBorderStyle(int borderStyle) { 429 this.borderStyle = borderStyle; 430 } 431 432 /** Gets the border color. 433 * @return the border color 434 */ getBorderColor()435 public Color getBorderColor() { 436 return this.borderColor; 437 } 438 439 /** Sets the border color. Set to <CODE>null</CODE> to remove 440 * the border. 441 * @param borderColor the border color 442 */ setBorderColor(Color borderColor)443 public void setBorderColor(Color borderColor) { 444 this.borderColor = borderColor; 445 } 446 447 /** Gets the background color. 448 * @return the background color 449 */ getBackgroundColor()450 public Color getBackgroundColor() { 451 return this.backgroundColor; 452 } 453 454 /** Sets the background color. Set to <CODE>null</CODE> for 455 * transparent background. 456 * @param backgroundColor the background color 457 */ setBackgroundColor(Color backgroundColor)458 public void setBackgroundColor(Color backgroundColor) { 459 this.backgroundColor = backgroundColor; 460 } 461 462 /** Gets the text color. 463 * @return the text color 464 */ getTextColor()465 public Color getTextColor() { 466 return this.textColor; 467 } 468 469 /** Sets the text color. If <CODE>null</CODE> the color used 470 * will be black. 471 * @param textColor the text color 472 */ setTextColor(Color textColor)473 public void setTextColor(Color textColor) { 474 this.textColor = textColor; 475 } 476 477 /** Gets the text font. 478 * @return the text font 479 */ getFont()480 public BaseFont getFont() { 481 return this.font; 482 } 483 484 /** Sets the text font. If <CODE>null</CODE> then Helvetica 485 * will be used. 486 * @param font the text font 487 */ setFont(BaseFont font)488 public void setFont(BaseFont font) { 489 this.font = font; 490 } 491 492 /** Gets the font size. 493 * @return the font size 494 */ getFontSize()495 public float getFontSize() { 496 return this.fontSize; 497 } 498 499 /** Sets the font size. If 0 then auto-sizing will be used but 500 * only for text fields. 501 * @param fontSize the font size 502 */ setFontSize(float fontSize)503 public void setFontSize(float fontSize) { 504 this.fontSize = fontSize; 505 } 506 507 /** Gets the text horizontal alignment. 508 * @return the text horizontal alignment 509 */ getAlignment()510 public int getAlignment() { 511 return this.alignment; 512 } 513 514 /** Sets the text horizontal alignment. It can be <CODE>Element.ALIGN_LEFT</CODE>, 515 * <CODE>Element.ALIGN_CENTER</CODE> and <CODE>Element.ALIGN_RIGHT</CODE>. 516 * @param alignment the text horizontal alignment 517 */ setAlignment(int alignment)518 public void setAlignment(int alignment) { 519 this.alignment = alignment; 520 } 521 522 /** Gets the text. 523 * @return the text 524 */ getText()525 public String getText() { 526 return this.text; 527 } 528 529 /** Sets the text for text fields. 530 * @param text the text 531 */ setText(String text)532 public void setText(String text) { 533 this.text = text; 534 } 535 536 /** Gets the field dimension and position. 537 * @return the field dimension and position 538 */ getBox()539 public Rectangle getBox() { 540 return this.box; 541 } 542 543 /** Sets the field dimension and position. 544 * @param box the field dimension and position 545 */ setBox(Rectangle box)546 public void setBox(Rectangle box) { 547 if (box == null) { 548 this.box = null; 549 } 550 else { 551 this.box = new Rectangle(box); 552 this.box.normalize(); 553 } 554 } 555 556 /** Gets the field rotation. 557 * @return the field rotation 558 */ getRotation()559 public int getRotation() { 560 return this.rotation; 561 } 562 563 /** Sets the field rotation. This value should be the same as 564 * the page rotation where the field will be shown. 565 * @param rotation the field rotation 566 */ setRotation(int rotation)567 public void setRotation(int rotation) { 568 if (rotation % 90 != 0) 569 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("rotation.must.be.a.multiple.of.90")); 570 rotation %= 360; 571 if (rotation < 0) 572 rotation += 360; 573 this.rotation = rotation; 574 } 575 576 /** Convenience method to set the field rotation the same as the 577 * page rotation. 578 * @param page the page 579 */ setRotationFromPage(Rectangle page)580 public void setRotationFromPage(Rectangle page) { 581 setRotation(page.getRotation()); 582 } 583 584 /** Gets the field visibility flag. 585 * @return the field visibility flag 586 */ getVisibility()587 public int getVisibility() { 588 return this.visibility; 589 } 590 591 /** Sets the field visibility flag. This flags can be one of 592 * <CODE>VISIBLE</CODE>, <CODE>HIDDEN</CODE>, <CODE>VISIBLE_BUT_DOES_NOT_PRINT</CODE> 593 * and <CODE>HIDDEN_BUT_PRINTABLE</CODE>. 594 * @param visibility field visibility flag 595 */ setVisibility(int visibility)596 public void setVisibility(int visibility) { 597 this.visibility = visibility; 598 } 599 600 /** Gets the field name. 601 * @return the field name 602 */ getFieldName()603 public String getFieldName() { 604 return this.fieldName; 605 } 606 607 /** Sets the field name. 608 * @param fieldName the field name. If <CODE>null</CODE> only the widget keys 609 * will be included in the field allowing it to be used as a kid field. 610 */ setFieldName(String fieldName)611 public void setFieldName(String fieldName) { 612 this.fieldName = fieldName; 613 } 614 615 /** Gets the option flags. 616 * @return the option flags 617 */ getOptions()618 public int getOptions() { 619 return this.options; 620 } 621 622 /** Sets the option flags. The option flags can be a combination by oring of 623 * <CODE>READ_ONLY</CODE>, <CODE>REQUIRED</CODE>, 624 * <CODE>MULTILINE</CODE>, <CODE>DO_NOT_SCROLL</CODE>, 625 * <CODE>PASSWORD</CODE>, <CODE>FILE_SELECTION</CODE>, 626 * <CODE>DO_NOT_SPELL_CHECK</CODE> and <CODE>EDIT</CODE>. 627 * @param options the option flags 628 */ setOptions(int options)629 public void setOptions(int options) { 630 this.options = options; 631 } 632 633 /** Gets the maximum length of the field's text, in characters. 634 * @return the maximum length of the field's text, in characters. 635 */ getMaxCharacterLength()636 public int getMaxCharacterLength() { 637 return this.maxCharacterLength; 638 } 639 640 /** Sets the maximum length of the field's text, in characters. 641 * It is only meaningful for text fields. 642 * @param maxCharacterLength the maximum length of the field's text, in characters 643 */ setMaxCharacterLength(int maxCharacterLength)644 public void setMaxCharacterLength(int maxCharacterLength) { 645 this.maxCharacterLength = maxCharacterLength; 646 } 647 648 /** 649 * Getter for property writer. 650 * @return Value of property writer. 651 */ getWriter()652 public PdfWriter getWriter() { 653 return writer; 654 } 655 656 /** 657 * Setter for property writer. 658 * @param writer New value of property writer. 659 */ setWriter(PdfWriter writer)660 public void setWriter(PdfWriter writer) { 661 this.writer = writer; 662 } 663 664 /** 665 * Moves the field keys from <CODE>from</CODE> to <CODE>to</CODE>. The moved keys 666 * are removed from <CODE>from</CODE>. 667 * @param from the source 668 * @param to the destination. It may be <CODE>null</CODE> 669 */ moveFields(PdfDictionary from, PdfDictionary to)670 public static void moveFields(PdfDictionary from, PdfDictionary to) { 671 for (Iterator i = from.getKeys().iterator(); i.hasNext();) { 672 PdfName key = (PdfName)i.next(); 673 if (fieldKeys.containsKey(key)) { 674 if (to != null) 675 to.put(key, from.get(key)); 676 i.remove(); 677 } 678 } 679 } 680 } 681