001: /*
002: * Copyright 2005 by Paulo Soares.
003: *
004: * The contents of this file are subject to the Mozilla Public License Version 1.1
005: * (the "License"); you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the License.
011: *
012: * The Original Code is 'iText, a free JAVA-PDF library'.
013: *
014: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
015: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
016: * All Rights Reserved.
017: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
018: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
019: *
020: * Contributor(s): all the names of the contributors are added in the source code
021: * where applicable.
022: *
023: * Alternatively, the contents of this file may be used under the terms of the
024: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
025: * provisions of LGPL are applicable instead of those above. If you wish to
026: * allow use of your version of this file only under the terms of the LGPL
027: * License and not to allow others to use your version of this file under
028: * the MPL, indicate your decision by deleting the provisions above and
029: * replace them with the notice and other provisions required by the LGPL.
030: * If you do not delete the provisions above, a recipient may use your version
031: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
032: *
033: * This library is free software; you can redistribute it and/or modify it
034: * under the terms of the MPL as stated above or under the terms of the GNU
035: * Library General Public License as published by the Free Software Foundation;
036: * either version 2 of the License, or any later version.
037: *
038: * This library is distributed in the hope that it will be useful, but WITHOUT
039: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
040: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
041: * details.
042: *
043: * If you didn't download this code from the following link, you should check if
044: * you aren't using an obsolete version:
045: * http://www.lowagie.com/iText/
046: */
047:
048: package com.lowagie.text.pdf;
049:
050: import java.awt.Color;
051: import java.io.IOException;
052: import java.util.ArrayList;
053: import java.util.HashMap;
054: import java.util.Iterator;
055:
056: import com.lowagie.text.DocumentException;
057: import com.lowagie.text.Element;
058: import com.lowagie.text.Rectangle;
059:
060: /** Common field variables.
061: * @author Paulo Soares (psoares@consiste.pt)
062: */
063: public abstract class BaseField {
064:
065: /** A thin border with 1 point width. */
066: public static final float BORDER_WIDTH_THIN = 1;
067: /** A medium border with 2 point width. */
068: public static final float BORDER_WIDTH_MEDIUM = 2;
069: /** A thick border with 3 point width. */
070: public static final float BORDER_WIDTH_THICK = 3;
071: /** The field is visible. */
072: public static final int VISIBLE = 0;
073: /** The field is hidden. */
074: public static final int HIDDEN = 1;
075: /** The field is visible but does not print. */
076: public static final int VISIBLE_BUT_DOES_NOT_PRINT = 2;
077: /** The field is hidden but is printable. */
078: public static final int HIDDEN_BUT_PRINTABLE = 3;
079: /** The user may not change the value of the field. */
080: public static final int READ_ONLY = PdfFormField.FF_READ_ONLY;
081: /** The field must have a value at the time it is exported by a submit-form
082: * action.
083: */
084: public static final int REQUIRED = PdfFormField.FF_REQUIRED;
085: /** The field may contain multiple lines of text.
086: * This flag is only meaningful with text fields.
087: */
088: public static final int MULTILINE = PdfFormField.FF_MULTILINE;
089: /** The field will not scroll (horizontally for single-line
090: * fields, vertically for multiple-line fields) to accommodate more text
091: * than will fit within its annotation rectangle. Once the field is full, no
092: * further text will be accepted.
093: */
094: public static final int DO_NOT_SCROLL = PdfFormField.FF_DONOTSCROLL;
095: /** The field is intended for entering a secure password that should
096: * not be echoed visibly to the screen.
097: */
098: public static final int PASSWORD = PdfFormField.FF_PASSWORD;
099: /** The text entered in the field represents the pathname of
100: * a file whose contents are to be submitted as the value of the field.
101: */
102: public static final int FILE_SELECTION = PdfFormField.FF_FILESELECT;
103: /** The text entered in the field will not be spell-checked.
104: * This flag is meaningful only in text fields and in combo
105: * fields with the <CODE>EDIT</CODE> flag set.
106: */
107: public static final int DO_NOT_SPELL_CHECK = PdfFormField.FF_DONOTSPELLCHECK;
108: /** If set the combo box includes an editable text box as well as a drop list; if
109: * clear, it includes only a drop list.
110: * This flag is only meaningful with combo fields.
111: */
112: public static final int EDIT = PdfFormField.FF_EDIT;
113:
114: /**
115: * combo box flag.
116: */
117: public static final int COMB = PdfFormField.FF_COMB;
118:
119: protected float borderWidth = BORDER_WIDTH_THIN;
120: protected int borderStyle = PdfBorderDictionary.STYLE_SOLID;
121: protected Color borderColor;
122: protected Color backgroundColor;
123: protected Color textColor;
124: protected BaseFont font;
125: protected float fontSize = 0;
126: protected int alignment = Element.ALIGN_LEFT;
127: protected PdfWriter writer;
128: protected String text;
129: protected Rectangle box;
130:
131: /** Holds value of property rotation. */
132: protected int rotation = 0;
133:
134: /** Holds value of property visibility. */
135: protected int visibility;
136:
137: /** Holds value of property fieldName. */
138: protected String fieldName;
139:
140: /** Holds value of property options. */
141: protected int options;
142:
143: /** Holds value of property maxCharacterLength. */
144: protected int maxCharacterLength;
145:
146: private final static HashMap fieldKeys = new HashMap();
147:
148: static {
149: fieldKeys.putAll(PdfCopyFieldsImp.fieldKeys);
150: fieldKeys.put(PdfName.T, new Integer(1));
151: }
152:
153: /** Creates a new <CODE>TextField</CODE>.
154: * @param writer the document <CODE>PdfWriter</CODE>
155: * @param box the field location and dimensions
156: * @param fieldName the field name. If <CODE>null</CODE> only the widget keys
157: * will be included in the field allowing it to be used as a kid field.
158: */
159: public BaseField(PdfWriter writer, Rectangle box, String fieldName) {
160: this .writer = writer;
161: setBox(box);
162: this .fieldName = fieldName;
163: }
164:
165: protected BaseFont getRealFont() throws IOException,
166: DocumentException {
167: if (font == null)
168: return BaseFont.createFont(BaseFont.HELVETICA,
169: BaseFont.WINANSI, false);
170: else
171: return font;
172: }
173:
174: protected PdfAppearance getBorderAppearance() {
175: PdfAppearance app = PdfAppearance.createAppearance(writer, box
176: .getWidth(), box.getHeight());
177: switch (rotation) {
178: case 90:
179: app.setMatrix(0, 1, -1, 0, box.getHeight(), 0);
180: break;
181: case 180:
182: app
183: .setMatrix(-1, 0, 0, -1, box.getWidth(), box
184: .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: } else if (borderStyle == PdfBorderDictionary.STYLE_BEVELED) {
207: if (borderWidth != 0 && borderColor != null) {
208: app.setColorStroke(borderColor);
209: app.setLineWidth(borderWidth);
210: app.rectangle(borderWidth / 2, borderWidth / 2, box
211: .getWidth()
212: - borderWidth, box.getHeight() - borderWidth);
213: app.stroke();
214: }
215: // beveled
216: Color actual = backgroundColor;
217: if (actual == null)
218: actual = Color.white;
219: app.setGrayFill(1);
220: drawTopFrame(app);
221: app.setColorFill(actual.darker());
222: drawBottomFrame(app);
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
228: .getWidth()
229: - borderWidth, box.getHeight() - borderWidth);
230: app.stroke();
231: }
232: // inset
233: app.setGrayFill(0.5f);
234: drawTopFrame(app);
235: app.setGrayFill(0.75f);
236: drawBottomFrame(app);
237: } else {
238: if (borderWidth != 0 && borderColor != null) {
239: if (borderStyle == PdfBorderDictionary.STYLE_DASHED)
240: app.setLineDash(3, 0);
241: app.setColorStroke(borderColor);
242: app.setLineWidth(borderWidth);
243: app.rectangle(borderWidth / 2, borderWidth / 2, box
244: .getWidth()
245: - borderWidth, box.getHeight() - borderWidth);
246: app.stroke();
247: if ((options & COMB) != 0 && maxCharacterLength > 1) {
248: float step = box.getWidth() / maxCharacterLength;
249: float yb = borderWidth / 2;
250: float yt = box.getHeight() - borderWidth / 2;
251: for (int k = 1; k < maxCharacterLength; ++k) {
252: float x = step * k;
253: app.moveTo(x, yb);
254: app.lineTo(x, yt);
255: }
256: app.stroke();
257: }
258: }
259: }
260: app.restoreState();
261: return app;
262: }
263:
264: protected static ArrayList getHardBreaks(String text) {
265: ArrayList arr = new ArrayList();
266: char cs[] = text.toCharArray();
267: int len = cs.length;
268: StringBuffer buf = new StringBuffer();
269: for (int k = 0; k < len; ++k) {
270: char c = cs[k];
271: if (c == '\r') {
272: if (k + 1 < len && cs[k + 1] == '\n')
273: ++k;
274: arr.add(buf.toString());
275: buf = new StringBuffer();
276: } else if (c == '\n') {
277: arr.add(buf.toString());
278: buf = new StringBuffer();
279: } else
280: buf.append(c);
281: }
282: arr.add(buf.toString());
283: return arr;
284: }
285:
286: protected static void trimRight(StringBuffer buf) {
287: int len = buf.length();
288: while (true) {
289: if (len == 0)
290: return;
291: if (buf.charAt(--len) != ' ')
292: return;
293: buf.setLength(len);
294: }
295: }
296:
297: protected static ArrayList breakLines(ArrayList breaks,
298: BaseFont font, float fontSize, float width) {
299: ArrayList lines = new ArrayList();
300: StringBuffer buf = new StringBuffer();
301: for (int ck = 0; ck < breaks.size(); ++ck) {
302: buf.setLength(0);
303: float w = 0;
304: char cs[] = ((String) breaks.get(ck)).toCharArray();
305: int len = cs.length;
306: // 0 inline first, 1 inline, 2 spaces
307: int state = 0;
308: int lastspace = -1;
309: char c = 0;
310: int refk = 0;
311: for (int k = 0; k < len; ++k) {
312: c = cs[k];
313: switch (state) {
314: case 0:
315: w += font.getWidthPoint(c, fontSize);
316: buf.append(c);
317: if (w > width) {
318: w = 0;
319: if (buf.length() > 1) {
320: --k;
321: buf.setLength(buf.length() - 1);
322: }
323: lines.add(buf.toString());
324: buf.setLength(0);
325: refk = k;
326: if (c == ' ')
327: state = 2;
328: else
329: state = 1;
330: } else {
331: if (c != ' ')
332: state = 1;
333: }
334: break;
335: case 1:
336: w += font.getWidthPoint(c, fontSize);
337: buf.append(c);
338: if (c == ' ')
339: lastspace = k;
340: if (w > width) {
341: w = 0;
342: if (lastspace >= 0) {
343: k = lastspace;
344: buf.setLength(lastspace - refk);
345: trimRight(buf);
346: lines.add(buf.toString());
347: buf.setLength(0);
348: refk = k;
349: lastspace = -1;
350: state = 2;
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:
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()
383: - borderWidth);
384: app.lineTo(box.getWidth() - 2 * borderWidth, box.getHeight()
385: - 2 * borderWidth);
386: app.lineTo(2 * borderWidth, box.getHeight() - 2 * borderWidth);
387: app.lineTo(2 * borderWidth, 2 * borderWidth);
388: app.lineTo(borderWidth, borderWidth);
389: app.fill();
390: }
391:
392: private void drawBottomFrame(PdfAppearance app) {
393: app.moveTo(borderWidth, borderWidth);
394: app.lineTo(box.getWidth() - borderWidth, borderWidth);
395: app.lineTo(box.getWidth() - borderWidth, box.getHeight()
396: - borderWidth);
397: app.lineTo(box.getWidth() - 2 * borderWidth, box.getHeight()
398: - 2 * borderWidth);
399: app.lineTo(box.getWidth() - 2 * borderWidth, 2 * borderWidth);
400: app.lineTo(2 * borderWidth, 2 * borderWidth);
401: app.lineTo(borderWidth, borderWidth);
402: app.fill();
403: }
404:
405: /** Gets the border width in points.
406: * @return the border width in points
407: */
408: public float getBorderWidth() {
409: return this .borderWidth;
410: }
411:
412: /** Sets the border width in points. To eliminate the border
413: * set the border color to <CODE>null</CODE>.
414: * @param borderWidth the border width in points
415: */
416: public void setBorderWidth(float borderWidth) {
417: this .borderWidth = borderWidth;
418: }
419:
420: /** Gets the border style.
421: * @return the border style
422: */
423: public int getBorderStyle() {
424: return this .borderStyle;
425: }
426:
427: /** Sets the border style. The styles are found in <CODE>PdfBorderDictionary</CODE>
428: * and can be <CODE>STYLE_SOLID</CODE>, <CODE>STYLE_DASHED</CODE>,
429: * <CODE>STYLE_BEVELED</CODE>, <CODE>STYLE_INSET</CODE> and
430: * <CODE>STYLE_UNDERLINE</CODE>.
431: * @param borderStyle the border style
432: */
433: public void setBorderStyle(int borderStyle) {
434: this .borderStyle = borderStyle;
435: }
436:
437: /** Gets the border color.
438: * @return the border color
439: */
440: public Color getBorderColor() {
441: return this .borderColor;
442: }
443:
444: /** Sets the border color. Set to <CODE>null</CODE> to remove
445: * the border.
446: * @param borderColor the border color
447: */
448: public void setBorderColor(Color borderColor) {
449: this .borderColor = borderColor;
450: }
451:
452: /** Gets the background color.
453: * @return the background color
454: */
455: public Color getBackgroundColor() {
456: return this .backgroundColor;
457: }
458:
459: /** Sets the background color. Set to <CODE>null</CODE> for
460: * transparent background.
461: * @param backgroundColor the background color
462: */
463: public void setBackgroundColor(Color backgroundColor) {
464: this .backgroundColor = backgroundColor;
465: }
466:
467: /** Gets the text color.
468: * @return the text color
469: */
470: public Color getTextColor() {
471: return this .textColor;
472: }
473:
474: /** Sets the text color. If <CODE>null</CODE> the color used
475: * will be black.
476: * @param textColor the text color
477: */
478: public void setTextColor(Color textColor) {
479: this .textColor = textColor;
480: }
481:
482: /** Gets the text font.
483: * @return the text font
484: */
485: public BaseFont getFont() {
486: return this .font;
487: }
488:
489: /** Sets the text font. If <CODE>null</CODE> then Helvetica
490: * will be used.
491: * @param font the text font
492: */
493: public void setFont(BaseFont font) {
494: this .font = font;
495: }
496:
497: /** Gets the font size.
498: * @return the font size
499: */
500: public float getFontSize() {
501: return this .fontSize;
502: }
503:
504: /** Sets the font size. If 0 then auto-sizing will be used but
505: * only for text fields.
506: * @param fontSize the font size
507: */
508: public void setFontSize(float fontSize) {
509: this .fontSize = fontSize;
510: }
511:
512: /** Gets the text horizontal alignment.
513: * @return the text horizontal alignment
514: */
515: public int getAlignment() {
516: return this .alignment;
517: }
518:
519: /** Sets the text horizontal alignment. It can be <CODE>Element.ALIGN_LEFT</CODE>,
520: * <CODE>Element.ALIGN_CENTER</CODE> and <CODE>Element.ALIGN_RIGHT</CODE>.
521: * @param alignment the text horizontal alignment
522: */
523: public void setAlignment(int alignment) {
524: this .alignment = alignment;
525: }
526:
527: /** Gets the text.
528: * @return the text
529: */
530: public String getText() {
531: return this .text;
532: }
533:
534: /** Sets the text for text fields.
535: * @param text the text
536: */
537: public void setText(String text) {
538: this .text = text;
539: }
540:
541: /** Gets the field dimension and position.
542: * @return the field dimension and position
543: */
544: public Rectangle getBox() {
545: return this .box;
546: }
547:
548: /** Sets the field dimension and position.
549: * @param box the field dimension and position
550: */
551: public void setBox(Rectangle box) {
552: if (box == null) {
553: this .box = null;
554: } else {
555: this .box = new Rectangle(box);
556: this .box.normalize();
557: }
558: }
559:
560: /** Gets the field rotation.
561: * @return the field rotation
562: */
563: public int getRotation() {
564: return this .rotation;
565: }
566:
567: /** Sets the field rotation. This value should be the same as
568: * the page rotation where the field will be shown.
569: * @param rotation the field rotation
570: */
571: public void setRotation(int rotation) {
572: if (rotation % 90 != 0)
573: throw new IllegalArgumentException(
574: "Rotation must be a multiple of 90.");
575: rotation %= 360;
576: if (rotation < 0)
577: rotation += 360;
578: this .rotation = rotation;
579: }
580:
581: /** Convenience method to set the field rotation the same as the
582: * page rotation.
583: * @param page the page
584: */
585: public void setRotationFromPage(Rectangle page) {
586: setRotation(page.getRotation());
587: }
588:
589: /** Gets the field visibility flag.
590: * @return the field visibility flag
591: */
592: public int getVisibility() {
593: return this .visibility;
594: }
595:
596: /** Sets the field visibility flag. This flags can be one of
597: * <CODE>VISIBLE</CODE>, <CODE>HIDDEN</CODE>, <CODE>VISIBLE_BUT_DOES_NOT_PRINT</CODE>
598: * and <CODE>HIDDEN_BUT_PRINTABLE</CODE>.
599: * @param visibility field visibility flag
600: */
601: public void setVisibility(int visibility) {
602: this .visibility = visibility;
603: }
604:
605: /** Gets the field name.
606: * @return the field name
607: */
608: public String getFieldName() {
609: return this .fieldName;
610: }
611:
612: /** Sets the field name.
613: * @param fieldName the field name. If <CODE>null</CODE> only the widget keys
614: * will be included in the field allowing it to be used as a kid field.
615: */
616: public void setFieldName(String fieldName) {
617: this .fieldName = fieldName;
618: }
619:
620: /** Gets the option flags.
621: * @return the option flags
622: */
623: public int getOptions() {
624: return this .options;
625: }
626:
627: /** Sets the option flags. The option flags can be a combination by oring of
628: * <CODE>READ_ONLY</CODE>, <CODE>REQUIRED</CODE>,
629: * <CODE>MULTILINE</CODE>, <CODE>DO_NOT_SCROLL</CODE>,
630: * <CODE>PASSWORD</CODE>, <CODE>FILE_SELECTION</CODE>,
631: * <CODE>DO_NOT_SPELL_CHECK</CODE> and <CODE>EDIT</CODE>.
632: * @param options the option flags
633: */
634: public void setOptions(int options) {
635: this .options = options;
636: }
637:
638: /** Gets the maximum length of the field's text, in characters.
639: * @return the maximum length of the field's text, in characters.
640: */
641: public int getMaxCharacterLength() {
642: return this .maxCharacterLength;
643: }
644:
645: /** Sets the maximum length of the field's text, in characters.
646: * It is only meaningful for text fields.
647: * @param maxCharacterLength the maximum length of the field's text, in characters
648: */
649: public void setMaxCharacterLength(int maxCharacterLength) {
650: this .maxCharacterLength = maxCharacterLength;
651: }
652:
653: /**
654: * Getter for property writer.
655: * @return Value of property writer.
656: */
657: public PdfWriter getWriter() {
658: return writer;
659: }
660:
661: /**
662: * Setter for property writer.
663: * @param writer New value of property writer.
664: */
665: public void setWriter(PdfWriter writer) {
666: this .writer = writer;
667: }
668:
669: /**
670: * Moves the field keys from <CODE>from</CODE> to <CODE>to</CODE>. The moved keys
671: * are removed from <CODE>from</CODE>.
672: * @param from the source
673: * @param to the destination. It may be <CODE>null</CODE>
674: */
675: public static void moveFields(PdfDictionary from, PdfDictionary to) {
676: for (Iterator i = from.getKeys().iterator(); i.hasNext();) {
677: PdfName key = (PdfName) i.next();
678: if (fieldKeys.containsKey(key)) {
679: if (to != null)
680: to.put(key, from.get(key));
681: i.remove();
682: }
683: }
684: }
685: }
|