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