001/* 002 * $Id: TextField.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; 048 049import com.itextpdf.text.BaseColor; 050import com.itextpdf.text.Chunk; 051import com.itextpdf.text.DocumentException; 052import com.itextpdf.text.Element; 053import com.itextpdf.text.Font; 054import com.itextpdf.text.Phrase; 055import com.itextpdf.text.Rectangle; 056 057/** 058 * Supports text, combo and list fields generating the correct appearances. 059 * All the option in the Acrobat GUI are supported in an easy to use API. 060 * @author Paulo Soares 061 */ 062public class TextField extends BaseField { 063 064 /** Holds value of property defaultText. */ 065 private String defaultText; 066 067 /** Holds value of property choices. */ 068 private String[] choices; 069 070 /** Holds value of property choiceExports. */ 071 private String[] choiceExports; 072 073 /** Holds value of property choiceSelection. */ 074 private ArrayList<Integer> choiceSelections = new ArrayList<Integer>(); 075 076 private int topFirst; 077 078 private float extraMarginLeft; 079 private float extraMarginTop; 080 081 /** 082 * Creates a new <CODE>TextField</CODE>. 083 * @param writer the document <CODE>PdfWriter</CODE> 084 * @param box the field location and dimensions 085 * @param fieldName the field name. If <CODE>null</CODE> only the widget keys 086 * will be included in the field allowing it to be used as a kid field. 087 */ 088 public TextField(PdfWriter writer, Rectangle box, String fieldName) { 089 super(writer, box, fieldName); 090 } 091 092 private static boolean checkRTL(String text) { 093 if (text == null || text.length() == 0) 094 return false; 095 char[] cc = text.toCharArray(); 096 for (int k = 0; k < cc.length; ++k) { 097 int c = cc[k]; 098 if (c >= 0x590 && c < 0x0780) 099 return true; 100 } 101 return false; 102 } 103 104 private static void changeFontSize(Phrase p, float size) { 105 for (int k = 0; k < p.size(); ++k) 106 ((Chunk)p.get(k)).getFont().setSize(size); 107 } 108 109 private Phrase composePhrase(String text, BaseFont ufont, BaseColor color, float fontSize) { 110 Phrase phrase = null; 111 if (extensionFont == null && (substitutionFonts == null || substitutionFonts.isEmpty())) 112 phrase = new Phrase(new Chunk(text, new Font(ufont, fontSize, 0, color))); 113 else { 114 FontSelector fs = new FontSelector(); 115 fs.addFont(new Font(ufont, fontSize, 0, color)); 116 if (extensionFont != null) 117 fs.addFont(new Font(extensionFont, fontSize, 0, color)); 118 if (substitutionFonts != null) { 119 for (int k = 0; k < substitutionFonts.size(); ++k) 120 fs.addFont(new Font(substitutionFonts.get(k), fontSize, 0, color)); 121 } 122 phrase = fs.process(text); 123 } 124 return phrase; 125 } 126 127 /** 128 * Removes CRLF from a <code>String</code>. 129 * 130 * @param text 131 * @return String 132 * @since 2.1.5 133 */ 134 public static String removeCRLF(String text) { 135 if (text.indexOf('\n') >= 0 || text.indexOf('\r') >= 0) { 136 char[] p = text.toCharArray(); 137 StringBuffer sb = new StringBuffer(p.length); 138 for (int k = 0; k < p.length; ++k) { 139 char c = p[k]; 140 if (c == '\n') 141 sb.append(' '); 142 else if (c == '\r') { 143 sb.append(' '); 144 if (k < p.length - 1 && p[k + 1] == '\n') 145 ++k; 146 } 147 else 148 sb.append(c); 149 } 150 return sb.toString(); 151 } 152 return text; 153 } 154 155 /** 156 * Obfuscates a password <code>String</code>. 157 * Every character is replaced by an asterisk (*). 158 * 159 * @param text 160 * @return String 161 * @since 2.1.5 162 */ 163 public static String obfuscatePassword(String text) { 164 char[] pchar = new char[text.length()]; 165 for (int i = 0; i < text.length(); i++) 166 pchar[i] = '*'; 167 return new String(pchar); 168 } 169 170 /** 171 * Get the <code>PdfAppearance</code> of a text or combo field 172 * @throws IOException on error 173 * @throws DocumentException on error 174 * @return A <code>PdfAppearance</code> 175 */ 176 public PdfAppearance getAppearance() throws IOException, DocumentException { 177 PdfAppearance app = getBorderAppearance(); 178 app.beginVariableText(); 179 if (text == null || text.length() == 0) { 180 app.endVariableText(); 181 return app; 182 } 183 184 boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET; 185 float h = box.getHeight() - borderWidth * 2 - extraMarginTop; 186 float bw2 = borderWidth; 187 if (borderExtra) { 188 h -= borderWidth * 2; 189 bw2 *= 2; 190 } 191 float offsetX = Math.max(bw2, 1); 192 float offX = Math.min(bw2, offsetX); 193 app.saveState(); 194 app.rectangle(offX, offX, box.getWidth() - 2 * offX, box.getHeight() - 2 * offX); 195 app.clip(); 196 app.newPath(); 197 String ptext; 198 if ((options & PASSWORD) != 0) 199 ptext = obfuscatePassword(text); 200 else if ((options & MULTILINE) == 0) 201 ptext = removeCRLF(text); 202 else 203 ptext = text; //fixed by Kazuya Ujihara (ujihara.jp) 204 BaseFont ufont = getRealFont(); 205 BaseColor fcolor = textColor == null ? GrayColor.GRAYBLACK : textColor; 206 int rtl = checkRTL(ptext) ? PdfWriter.RUN_DIRECTION_LTR : PdfWriter.RUN_DIRECTION_NO_BIDI; 207 float usize = fontSize; 208 Phrase phrase = composePhrase(ptext, ufont, fcolor, usize); 209 if ((options & MULTILINE) != 0) { 210 float width = box.getWidth() - 4 * offsetX - extraMarginLeft; 211 float factor = ufont.getFontDescriptor(BaseFont.BBOXURY, 1) - ufont.getFontDescriptor(BaseFont.BBOXLLY, 1); 212 ColumnText ct = new ColumnText(null); 213 if (usize == 0) { 214 usize = h / factor; 215 if (usize > 4) { 216 if (usize > 12) 217 usize = 12; 218 float step = Math.max((usize - 4) / 10, 0.2f); 219 ct.setSimpleColumn(0, -h, width, 0); 220 ct.setAlignment(alignment); 221 ct.setRunDirection(rtl); 222 for (; usize > 4; usize -= step) { 223 ct.setYLine(0); 224 changeFontSize(phrase, usize); 225 ct.setText(phrase); 226 ct.setLeading(factor * usize); 227 int status = ct.go(true); 228 if ((status & ColumnText.NO_MORE_COLUMN) == 0) 229 break; 230 } 231 } 232 if (usize < 4) 233 usize = 4; 234 } 235 changeFontSize(phrase, usize); 236 ct.setCanvas(app); 237 float leading = usize * factor; 238 float offsetY = offsetX + h - ufont.getFontDescriptor(BaseFont.BBOXURY, usize); 239 ct.setSimpleColumn(extraMarginLeft + 2 * offsetX, -20000, box.getWidth() - 2 * offsetX, offsetY + leading); 240 ct.setLeading(leading); 241 ct.setAlignment(alignment); 242 ct.setRunDirection(rtl); 243 ct.setText(phrase); 244 ct.go(); 245 } 246 else { 247 if (usize == 0) { 248 float maxCalculatedSize = h / (ufont.getFontDescriptor(BaseFont.BBOXURX, 1) - ufont.getFontDescriptor(BaseFont.BBOXLLY, 1)); 249 changeFontSize(phrase, 1); 250 float wd = ColumnText.getWidth(phrase, rtl, 0); 251 if (wd == 0) 252 usize = maxCalculatedSize; 253 else 254 usize = Math.min(maxCalculatedSize, (box.getWidth() - extraMarginLeft - 4 * offsetX) / wd); 255 if (usize < 4) 256 usize = 4; 257 } 258 changeFontSize(phrase, usize); 259 float offsetY = offX + (box.getHeight() - 2*offX - ufont.getFontDescriptor(BaseFont.ASCENT, usize)) / 2; 260 if (offsetY < offX) 261 offsetY = offX; 262 if (offsetY - offX < -ufont.getFontDescriptor(BaseFont.DESCENT, usize)) { 263 float ny = -ufont.getFontDescriptor(BaseFont.DESCENT, usize) + offX; 264 float dy = box.getHeight() - offX - ufont.getFontDescriptor(BaseFont.ASCENT, usize); 265 offsetY = Math.min(ny, Math.max(offsetY, dy)); 266 } 267 if ((options & COMB) != 0 && maxCharacterLength > 0) { 268 int textLen = Math.min(maxCharacterLength, ptext.length()); 269 int position = 0; 270 if (alignment == Element.ALIGN_RIGHT) 271 position = maxCharacterLength - textLen; 272 else if (alignment == Element.ALIGN_CENTER) 273 position = (maxCharacterLength - textLen) / 2; 274 float step = (box.getWidth() - extraMarginLeft) / maxCharacterLength; 275 float start = step / 2 + position * step; 276 if (textColor == null) 277 app.setGrayFill(0); 278 else 279 app.setColorFill(textColor); 280 app.beginText(); 281 for (int k = 0; k < phrase.size(); ++k) { 282 Chunk ck = (Chunk)phrase.get(k); 283 BaseFont bf = ck.getFont().getBaseFont(); 284 app.setFontAndSize(bf, usize); 285 StringBuffer sb = ck.append(""); 286 for (int j = 0; j < sb.length(); ++j) { 287 String c = sb.substring(j, j + 1); 288 float wd = bf.getWidthPoint(c, usize); 289 app.setTextMatrix(extraMarginLeft + start - wd / 2, offsetY - extraMarginTop); 290 app.showText(c); 291 start += step; 292 } 293 } 294 app.endText(); 295 } 296 else { 297 float x; 298 switch (alignment) { 299 case Element.ALIGN_RIGHT: 300 x = extraMarginLeft + box.getWidth() - 2 * offsetX; 301 break; 302 case Element.ALIGN_CENTER: 303 x = extraMarginLeft + box.getWidth() / 2; 304 break; 305 default: 306 x = extraMarginLeft + 2 * offsetX; 307 } 308 ColumnText.showTextAligned(app, alignment, phrase, x, offsetY - extraMarginTop, 0, rtl, 0); 309 } 310 } 311 app.restoreState(); 312 app.endVariableText(); 313 return app; 314 } 315 316 /** 317 * Get the <code>PdfAppearance</code> of a list field 318 * @throws IOException on error 319 * @throws DocumentException on error 320 * @return A <code>PdfAppearance</code> 321 */ 322 PdfAppearance getListAppearance() throws IOException, DocumentException { 323 PdfAppearance app = getBorderAppearance(); 324 if (choices == null || choices.length == 0) { 325 return app; 326 } 327 app.beginVariableText(); 328 329 int topChoice = getTopChoice(); 330 331 BaseFont ufont = getRealFont(); 332 float usize = fontSize; 333 if (usize == 0) 334 usize = 12; 335 336 boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET; 337 float h = box.getHeight() - borderWidth * 2; 338 float offsetX = borderWidth; 339 if (borderExtra) { 340 h -= borderWidth * 2; 341 offsetX *= 2; 342 } 343 344 float leading = ufont.getFontDescriptor(BaseFont.BBOXURY, usize) - ufont.getFontDescriptor(BaseFont.BBOXLLY, usize); 345 int maxFit = (int)(h / leading) + 1; 346 int first = 0; 347 int last = 0; 348 first = topChoice; 349 last = first + maxFit; 350 if (last > choices.length) 351 last = choices.length; 352 topFirst = first; 353 app.saveState(); 354 app.rectangle(offsetX, offsetX, box.getWidth() - 2 * offsetX, box.getHeight() - 2 * offsetX); 355 app.clip(); 356 app.newPath(); 357 BaseColor fcolor = textColor == null ? GrayColor.GRAYBLACK : textColor; 358 359 360 // background boxes for selected value[s] 361 app.setColorFill(new BaseColor(10, 36, 106)); 362 for (int curVal = 0; curVal < choiceSelections.size(); ++curVal) { 363 int curChoice = (choiceSelections.get( curVal )).intValue(); 364 // only draw selections within our display range... not strictly necessary with 365 // that clipping rect from above, but it certainly doesn't hurt either 366 if (curChoice >= first && curChoice <= last) { 367 app.rectangle(offsetX, offsetX + h - (curChoice - first + 1) * leading, box.getWidth() - 2 * offsetX, leading); 368 app.fill(); 369 } 370 } 371 float xp = offsetX * 2; 372 float yp = offsetX + h - ufont.getFontDescriptor(BaseFont.BBOXURY, usize); 373 for (int idx = first; idx < last; ++idx, yp -= leading) { 374 String ptext = choices[idx]; 375 int rtl = checkRTL(ptext) ? PdfWriter.RUN_DIRECTION_LTR : PdfWriter.RUN_DIRECTION_NO_BIDI; 376 ptext = removeCRLF(ptext); 377 // highlight selected values against their (presumably) darker background 378 BaseColor textCol = choiceSelections.contains( Integer.valueOf( idx )) ? GrayColor.GRAYWHITE : fcolor; 379 Phrase phrase = composePhrase(ptext, ufont, textCol, usize); 380 ColumnText.showTextAligned(app, Element.ALIGN_LEFT, phrase, xp, yp, 0, rtl, 0); 381 } 382 app.restoreState(); 383 app.endVariableText(); 384 return app; 385 } 386 387 /** 388 * Gets a new text field. 389 * @throws IOException on error 390 * @throws DocumentException on error 391 * @return a new text field 392 */ 393 public PdfFormField getTextField() throws IOException, DocumentException { 394 if (maxCharacterLength <= 0) 395 options &= ~COMB; 396 if ((options & COMB) != 0) 397 options &= ~MULTILINE; 398 PdfFormField field = PdfFormField.createTextField(writer, false, false, maxCharacterLength); 399 field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT); 400 switch (alignment) { 401 case Element.ALIGN_CENTER: 402 field.setQuadding(PdfFormField.Q_CENTER); 403 break; 404 case Element.ALIGN_RIGHT: 405 field.setQuadding(PdfFormField.Q_RIGHT); 406 break; 407 } 408 if (rotation != 0) 409 field.setMKRotation(rotation); 410 if (fieldName != null) { 411 field.setFieldName(fieldName); 412 if (!"".equals(text)) 413 field.setValueAsString(text); 414 if (defaultText != null) 415 field.setDefaultValueAsString(defaultText); 416 if ((options & READ_ONLY) != 0) 417 field.setFieldFlags(PdfFormField.FF_READ_ONLY); 418 if ((options & REQUIRED) != 0) 419 field.setFieldFlags(PdfFormField.FF_REQUIRED); 420 if ((options & MULTILINE) != 0) 421 field.setFieldFlags(PdfFormField.FF_MULTILINE); 422 if ((options & DO_NOT_SCROLL) != 0) 423 field.setFieldFlags(PdfFormField.FF_DONOTSCROLL); 424 if ((options & PASSWORD) != 0) 425 field.setFieldFlags(PdfFormField.FF_PASSWORD); 426 if ((options & FILE_SELECTION) != 0) 427 field.setFieldFlags(PdfFormField.FF_FILESELECT); 428 if ((options & DO_NOT_SPELL_CHECK) != 0) 429 field.setFieldFlags(PdfFormField.FF_DONOTSPELLCHECK); 430 if ((options & COMB) != 0) 431 field.setFieldFlags(PdfFormField.FF_COMB); 432 } 433 field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3))); 434 PdfAppearance tp = getAppearance(); 435 field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp); 436 PdfAppearance da = (PdfAppearance)tp.getDuplicate(); 437 da.setFontAndSize(getRealFont(), fontSize); 438 if (textColor == null) 439 da.setGrayFill(0); 440 else 441 da.setColorFill(textColor); 442 field.setDefaultAppearanceString(da); 443 if (borderColor != null) 444 field.setMKBorderColor(borderColor); 445 if (backgroundColor != null) 446 field.setMKBackgroundColor(backgroundColor); 447 switch (visibility) { 448 case HIDDEN: 449 field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN); 450 break; 451 case VISIBLE_BUT_DOES_NOT_PRINT: 452 break; 453 case HIDDEN_BUT_PRINTABLE: 454 field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW); 455 break; 456 default: 457 field.setFlags(PdfAnnotation.FLAGS_PRINT); 458 break; 459 } 460 return field; 461 } 462 463 /** 464 * Gets a new combo field. 465 * @throws IOException on error 466 * @throws DocumentException on error 467 * @return a new combo field 468 */ 469 public PdfFormField getComboField() throws IOException, DocumentException { 470 return getChoiceField(false); 471 } 472 473 /** 474 * Gets a new list field. 475 * @throws IOException on error 476 * @throws DocumentException on error 477 * @return a new list field 478 */ 479 public PdfFormField getListField() throws IOException, DocumentException { 480 return getChoiceField(true); 481 } 482 483 private int getTopChoice() { 484 if (choiceSelections == null || choiceSelections.size() ==0) { 485 return 0; 486 } 487 488 Integer firstValue = choiceSelections.get(0); 489 490 if (firstValue == null) { 491 return 0; 492 } 493 494 int topChoice = 0; 495 if (choices != null) { 496 topChoice = firstValue.intValue(); 497 topChoice = Math.min( topChoice, choices.length ); 498 topChoice = Math.max( 0, topChoice); 499 } // else topChoice still 0 500 return topChoice; 501 } 502 503 protected PdfFormField getChoiceField(boolean isList) throws IOException, DocumentException { 504 options &= ~MULTILINE & ~COMB; 505 String uchoices[] = choices; 506 if (uchoices == null) 507 uchoices = new String[0]; 508 509 int topChoice = getTopChoice(); 510 511 if (text == null) 512 text = ""; //fixed by Kazuya Ujihara (ujihara.jp) 513 514 if (topChoice >= 0) 515 text = uchoices[topChoice]; 516 517 PdfFormField field = null; 518 String mix[][] = null; 519 520 if (choiceExports == null) { 521 if (isList) 522 field = PdfFormField.createList(writer, uchoices, topChoice); 523 else 524 field = PdfFormField.createCombo(writer, (options & EDIT) != 0, uchoices, topChoice); 525 } 526 else { 527 mix = new String[uchoices.length][2]; 528 for (int k = 0; k < mix.length; ++k) 529 mix[k][0] = mix[k][1] = uchoices[k]; 530 int top = Math.min(uchoices.length, choiceExports.length); 531 for (int k = 0; k < top; ++k) { 532 if (choiceExports[k] != null) 533 mix[k][0] = choiceExports[k]; 534 } 535 if (isList) 536 field = PdfFormField.createList(writer, mix, topChoice); 537 else 538 field = PdfFormField.createCombo(writer, (options & EDIT) != 0, mix, topChoice); 539 } 540 field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT); 541 if (rotation != 0) 542 field.setMKRotation(rotation); 543 if (fieldName != null) { 544 field.setFieldName(fieldName); 545 if (uchoices.length > 0) { 546 if (mix != null) { 547 if (choiceSelections.size() < 2) { 548 field.setValueAsString(mix[topChoice][0]); 549 field.setDefaultValueAsString(mix[topChoice][0]); 550 } else { 551 writeMultipleValues( field, mix); 552 } 553 } else { 554 if (choiceSelections.size() < 2) { 555 field.setValueAsString(text); 556 field.setDefaultValueAsString(text); 557 } else { 558 writeMultipleValues( field, null ); 559 } 560 } 561 } 562 if ((options & READ_ONLY) != 0) 563 field.setFieldFlags(PdfFormField.FF_READ_ONLY); 564 if ((options & REQUIRED) != 0) 565 field.setFieldFlags(PdfFormField.FF_REQUIRED); 566 if ((options & DO_NOT_SPELL_CHECK) != 0) 567 field.setFieldFlags(PdfFormField.FF_DONOTSPELLCHECK); 568 if ((options & MULTISELECT) != 0) { 569 field.setFieldFlags( PdfFormField.FF_MULTISELECT ); 570 } 571 } 572 field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3))); 573 PdfAppearance tp; 574 if (isList) { 575 tp = getListAppearance(); 576 if (topFirst > 0) 577 field.put(PdfName.TI, new PdfNumber(topFirst)); 578 } 579 else 580 tp = getAppearance(); 581 field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp); 582 PdfAppearance da = (PdfAppearance)tp.getDuplicate(); 583 da.setFontAndSize(getRealFont(), fontSize); 584 if (textColor == null) 585 da.setGrayFill(0); 586 else 587 da.setColorFill(textColor); 588 field.setDefaultAppearanceString(da); 589 if (borderColor != null) 590 field.setMKBorderColor(borderColor); 591 if (backgroundColor != null) 592 field.setMKBackgroundColor(backgroundColor); 593 switch (visibility) { 594 case HIDDEN: 595 field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN); 596 break; 597 case VISIBLE_BUT_DOES_NOT_PRINT: 598 break; 599 case HIDDEN_BUT_PRINTABLE: 600 field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW); 601 break; 602 default: 603 field.setFlags(PdfAnnotation.FLAGS_PRINT); 604 break; 605 } 606 return field; 607 } 608 609 private void writeMultipleValues( PdfFormField field, String mix[][] ) { 610 PdfArray indexes = new PdfArray(); 611 PdfArray values = new PdfArray(); 612 for (int i = 0; i < choiceSelections.size(); ++i) { 613 int idx = (choiceSelections.get( i )).intValue(); 614 indexes.add( new PdfNumber( idx ) ); 615 616 if (mix != null) 617 values.add( new PdfString( mix[idx][0] ) ); 618 else if (choices != null) 619 values.add( new PdfString( choices[ idx ] ) ); 620 } 621 622 field.put( PdfName.V, values ); 623 field.put( PdfName.I, indexes ); 624 625 } 626 627 /** 628 * Gets the default text. 629 * @return the default text 630 */ 631 public String getDefaultText() { 632 return this.defaultText; 633 } 634 635 /** 636 * Sets the default text. It is only meaningful for text fields. 637 * @param defaultText the default text 638 */ 639 public void setDefaultText(String defaultText) { 640 this.defaultText = defaultText; 641 } 642 643 /** 644 * Gets the choices to be presented to the user in list/combo fields. 645 * @return the choices to be presented to the user 646 */ 647 public String[] getChoices() { 648 return this.choices; 649 } 650 651 /** 652 * Sets the choices to be presented to the user in list/combo fields. 653 * @param choices the choices to be presented to the user 654 */ 655 public void setChoices(String[] choices) { 656 this.choices = choices; 657 } 658 659 /** 660 * Gets the export values in list/combo fields. 661 * @return the export values in list/combo fields 662 */ 663 public String[] getChoiceExports() { 664 return this.choiceExports; 665 } 666 667 /** 668 * Sets the export values in list/combo fields. If this array 669 * is <CODE>null</CODE> then the choice values will also be used 670 * as the export values. 671 * @param choiceExports the export values in list/combo fields 672 */ 673 public void setChoiceExports(String[] choiceExports) { 674 this.choiceExports = choiceExports; 675 } 676 677 /** 678 * Gets the zero based index of the selected item. 679 * @return the zero based index of the selected item 680 */ 681 public int getChoiceSelection() { 682 return getTopChoice(); 683 } 684 685 /** 686 * Gets the selected items. 687 * @return the selected items 688 * 689 * @since 5.0.1 690 */ 691 public ArrayList<Integer> getChoiceSelections() { 692 return choiceSelections; 693 } 694 695 /** 696 * Sets the zero based index of the selected item. 697 * @param choiceSelection the zero based index of the selected item 698 */ 699 public void setChoiceSelection(int choiceSelection) { 700 choiceSelections = new ArrayList<Integer>(); 701 choiceSelections.add( Integer.valueOf( choiceSelection ) ); 702 } 703 704 /** 705 * adds another (or a first I suppose) selection to a MULTISELECT list. 706 * This doesn't do anything unless this.options & MUTLISELECT != 0 707 * @param selection new selection 708 */ 709 public void addChoiceSelection( int selection) { 710 if ((this.options & BaseField.MULTISELECT) != 0) { 711 choiceSelections.add( Integer.valueOf( selection ) ); 712 } 713 } 714 715 /** 716 * replaces the existing selections with the param. If this field isn't a MULTISELECT 717 * list, all but the first element will be removed. 718 * @param selections new selections. If null, it clear()s the underlying ArrayList. 719 */ 720 public void setChoiceSelections( ArrayList<Integer> selections ) { 721 if (selections != null) { 722 choiceSelections = new ArrayList<Integer>( selections ); 723 if (choiceSelections.size() > 1 && (options & BaseField.MULTISELECT) == 0 ) { 724 // can't have multiple selections in a single-select field 725 while (choiceSelections.size() > 1) { 726 choiceSelections.remove( 1 ); 727 } 728 } 729 730 } else { 731 choiceSelections.clear(); 732 } 733 } 734 735 int getTopFirst() { 736 return topFirst; 737 } 738 739 /** 740 * Sets extra margins in text fields to better mimic the Acrobat layout. 741 * @param extraMarginLeft the extra margin left 742 * @param extraMarginTop the extra margin top 743 */ 744 public void setExtraMargin(float extraMarginLeft, float extraMarginTop) { 745 this.extraMarginLeft = extraMarginLeft; 746 this.extraMarginTop = extraMarginTop; 747 } 748 749 /** 750 * Holds value of property substitutionFonts. 751 */ 752 private ArrayList<BaseFont> substitutionFonts; 753 754 /** 755 * Gets the list of substitution fonts. The list is composed of <CODE>BaseFont</CODE> and can be <CODE>null</CODE>. The fonts in this list will be used if the original 756 * font doesn't contain the needed glyphs. 757 * @return the list 758 */ 759 public ArrayList<BaseFont> getSubstitutionFonts() { 760 return this.substitutionFonts; 761 } 762 763 /** 764 * Sets a list of substitution fonts. The list is composed of <CODE>BaseFont</CODE> and can also be <CODE>null</CODE>. The fonts in this list will be used if the original 765 * font doesn't contain the needed glyphs. 766 * @param substitutionFonts the list 767 */ 768 public void setSubstitutionFonts(ArrayList<BaseFont> substitutionFonts) { 769 this.substitutionFonts = substitutionFonts; 770 } 771 772 /** 773 * Holds value of property extensionFont. 774 */ 775 private BaseFont extensionFont; 776 777 /** 778 * Gets the extensionFont. This font will be searched before the 779 * substitution fonts. It may be <code>null</code>. 780 * @return the extensionFont 781 */ 782 public BaseFont getExtensionFont() { 783 return this.extensionFont; 784 } 785 786 /** 787 * Sets the extensionFont. This font will be searched before the 788 * substitution fonts. It may be <code>null</code>. 789 * @param extensionFont New value of property extensionFont. 790 */ 791 public void setExtensionFont(BaseFont extensionFont) { 792 this.extensionFont = extensionFont; 793 } 794}