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