001/*
002 * $Id: PdfAcroForm.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.util.HashSet;
047
048import com.itextpdf.text.BaseColor;
049import com.itextpdf.text.ExceptionConverter;
050import com.itextpdf.text.Rectangle;
051
052/**
053 * Each PDF document can contain maximum 1 AcroForm.
054 */
055
056public class PdfAcroForm extends PdfDictionary {
057
058    private PdfWriter writer;
059
060
061    /** This is a map containing FieldTemplates. */
062    private HashSet<PdfTemplate> fieldTemplates = new HashSet<PdfTemplate>();
063
064    /** This is an array containing DocumentFields. */
065    private PdfArray documentFields = new PdfArray();
066
067    /** This is an array containing the calculationorder of the fields. */
068    private PdfArray calculationOrder = new PdfArray();
069
070    /** Contains the signature flags. */
071    private int sigFlags = 0;
072
073    /** Creates new PdfAcroForm
074     * @param writer
075     */
076    public PdfAcroForm(PdfWriter writer) {
077        super();
078        this.writer = writer;
079    }
080
081    public void setNeedAppearances(boolean value) {
082        put(PdfName.NEEDAPPEARANCES, new PdfBoolean(value));
083    }
084
085    /**
086     * Adds fieldTemplates.
087     * @param ft
088     */
089
090    public void addFieldTemplates(HashSet<PdfTemplate> ft) {
091        fieldTemplates.addAll(ft);
092    }
093
094    /**
095     * Adds documentFields.
096     * @param ref
097     */
098
099    public void addDocumentField(PdfIndirectReference ref) {
100        documentFields.add(ref);
101    }
102
103    /**
104     * Checks if the Acroform is valid
105     * @return true if the Acroform is valid
106     */
107
108    public boolean isValid() {
109        if (documentFields.size() == 0) return false;
110        put(PdfName.FIELDS, documentFields);
111        if (sigFlags != 0)
112            put(PdfName.SIGFLAGS, new PdfNumber(sigFlags));
113        if (calculationOrder.size() > 0)
114            put(PdfName.CO, calculationOrder);
115        if (fieldTemplates.isEmpty()) return true;
116        PdfDictionary dic = new PdfDictionary();
117        for (PdfTemplate template: fieldTemplates) {
118            PdfFormField.mergeResources(dic, (PdfDictionary)template.getResources());
119        }
120        put(PdfName.DR, dic);
121        put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g "));
122        PdfDictionary fonts = (PdfDictionary)dic.get(PdfName.FONT);
123        if (fonts != null) {
124            writer.eliminateFontSubset(fonts);
125        }
126        return true;
127    }
128
129    /**
130     * Adds an object to the calculationOrder.
131     * @param formField
132     */
133
134    public void addCalculationOrder(PdfFormField formField) {
135        calculationOrder.add(formField.getIndirectReference());
136    }
137
138    /**
139     * Sets the signature flags.
140     * @param f
141     */
142
143    public void setSigFlags(int f) {
144        sigFlags |= f;
145    }
146
147    /**
148     * Adds a formfield to the AcroForm.
149     * @param formField
150     */
151
152    public void addFormField(PdfFormField formField) {
153        writer.addAnnotation(formField);
154    }
155
156    /**
157     * @param name
158     * @param caption
159     * @param value
160     * @param url
161     * @param font
162     * @param fontSize
163     * @param llx
164     * @param lly
165     * @param urx
166     * @param ury
167     * @return a PdfFormField
168     */
169    public PdfFormField addHtmlPostButton(String name, String caption, String value, String url, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
170        PdfAction action = PdfAction.createSubmitForm(url, null, PdfAction.SUBMIT_HTML_FORMAT);
171        PdfFormField button = new PdfFormField(writer, llx, lly, urx, ury, action);
172        setButtonParams(button, PdfFormField.FF_PUSHBUTTON, name, value);
173        drawButton(button, caption, font, fontSize, llx, lly, urx, ury);
174        addFormField(button);
175        return button;
176    }
177
178    /**
179     * @param name
180     * @param caption
181     * @param value
182     * @param font
183     * @param fontSize
184     * @param llx
185     * @param lly
186     * @param urx
187     * @param ury
188     * @return a PdfFormField
189     */
190    public PdfFormField addResetButton(String name, String caption, String value, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
191        PdfAction action = PdfAction.createResetForm(null, 0);
192        PdfFormField button = new PdfFormField(writer, llx, lly, urx, ury, action);
193        setButtonParams(button, PdfFormField.FF_PUSHBUTTON, name, value);
194        drawButton(button, caption, font, fontSize, llx, lly, urx, ury);
195        addFormField(button);
196        return button;
197    }
198
199    /**
200     * @param name
201     * @param value
202     * @param url
203     * @param appearance
204     * @param llx
205     * @param lly
206     * @param urx
207     * @param ury
208     * @return a PdfFormField
209     */
210    public PdfFormField addMap(String name, String value, String url, PdfContentByte appearance, float llx, float lly, float urx, float ury) {
211        PdfAction action = PdfAction.createSubmitForm(url, null, PdfAction.SUBMIT_HTML_FORMAT | PdfAction.SUBMIT_COORDINATES);
212        PdfFormField button = new PdfFormField(writer, llx, lly, urx, ury, action);
213        setButtonParams(button, PdfFormField.FF_PUSHBUTTON, name, null);
214        PdfAppearance pa = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
215        pa.add(appearance);
216        button.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, pa);
217        addFormField(button);
218        return button;
219    }
220
221    /**
222     * @param button
223     * @param characteristics
224     * @param name
225     * @param value
226     */
227    public void setButtonParams(PdfFormField button, int characteristics, String name, String value) {
228        button.setButton(characteristics);
229        button.setFlags(PdfAnnotation.FLAGS_PRINT);
230        button.setPage();
231        button.setFieldName(name);
232        if (value != null) button.setValueAsString(value);
233    }
234
235    /**
236     * @param button
237     * @param caption
238     * @param font
239     * @param fontSize
240     * @param llx
241     * @param lly
242     * @param urx
243     * @param ury
244     */
245    public void drawButton(PdfFormField button, String caption, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
246        PdfAppearance pa = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
247        pa.drawButton(0f, 0f, urx - llx, ury - lly, caption, font, fontSize);
248        button.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, pa);
249    }
250
251    /**
252     * @param name
253     * @param value
254     * @return a PdfFormField
255     */
256    public PdfFormField addHiddenField(String name, String value) {
257        PdfFormField hidden = PdfFormField.createEmpty(writer);
258        hidden.setFieldName(name);
259        hidden.setValueAsName(value);
260        addFormField(hidden);
261        return hidden;
262    }
263
264    /**
265     * @param name
266     * @param text
267     * @param font
268     * @param fontSize
269     * @param llx
270     * @param lly
271     * @param urx
272     * @param ury
273     * @return a PdfFormField
274     */
275    public PdfFormField addSingleLineTextField(String name, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
276        PdfFormField field = PdfFormField.createTextField(writer, PdfFormField.SINGLELINE, PdfFormField.PLAINTEXT, 0);
277        setTextFieldParams(field, text, name, llx, lly, urx, ury);
278        drawSingleLineOfText(field, text, font, fontSize, llx, lly, urx, ury);
279        addFormField(field);
280        return field;
281    }
282
283    /**
284     * @param name
285     * @param text
286     * @param font
287     * @param fontSize
288     * @param llx
289     * @param lly
290     * @param urx
291     * @param ury
292     * @return a PdfFormField
293     */
294    public PdfFormField addMultiLineTextField(String name, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
295        PdfFormField field = PdfFormField.createTextField(writer, PdfFormField.MULTILINE, PdfFormField.PLAINTEXT, 0);
296        setTextFieldParams(field, text, name, llx, lly, urx, ury);
297        drawMultiLineOfText(field, text, font, fontSize, llx, lly, urx, ury);
298        addFormField(field);
299        return field;
300    }
301
302    /**
303     * @param name
304     * @param text
305     * @param font
306     * @param fontSize
307     * @param llx
308     * @param lly
309     * @param urx
310     * @param ury
311     * @return PdfFormField
312     */
313    public PdfFormField addSingleLinePasswordField(String name, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
314        PdfFormField field = PdfFormField.createTextField(writer, PdfFormField.SINGLELINE, PdfFormField.PASSWORD, 0);
315        setTextFieldParams(field, text, name, llx, lly, urx, ury);
316        drawSingleLineOfText(field, text, font, fontSize, llx, lly, urx, ury);
317        addFormField(field);
318        return field;
319    }
320
321    /**
322     * @param field
323     * @param text
324     * @param name
325     * @param llx
326     * @param lly
327     * @param urx
328     * @param ury
329     */
330    public void setTextFieldParams(PdfFormField field, String text, String name, float llx, float lly, float urx, float ury) {
331        field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_INVERT);
332        field.setValueAsString(text);
333        field.setDefaultValueAsString(text);
334        field.setFieldName(name);
335        field.setFlags(PdfAnnotation.FLAGS_PRINT);
336        field.setPage();
337    }
338
339    /**
340     * @param field
341     * @param text
342     * @param font
343     * @param fontSize
344     * @param llx
345     * @param lly
346     * @param urx
347     * @param ury
348     */
349    public void drawSingleLineOfText(PdfFormField field, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
350        PdfAppearance tp = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
351        PdfAppearance tp2 = (PdfAppearance)tp.getDuplicate();
352        tp2.setFontAndSize(font, fontSize);
353        tp2.resetRGBColorFill();
354        field.setDefaultAppearanceString(tp2);
355        tp.drawTextField(0f, 0f, urx - llx, ury - lly);
356        tp.beginVariableText();
357        tp.saveState();
358        tp.rectangle(3f, 3f, urx - llx - 6f, ury - lly - 6f);
359        tp.clip();
360        tp.newPath();
361        tp.beginText();
362        tp.setFontAndSize(font, fontSize);
363        tp.resetRGBColorFill();
364        tp.setTextMatrix(4, (ury - lly) / 2 - fontSize * 0.3f);
365        tp.showText(text);
366        tp.endText();
367        tp.restoreState();
368        tp.endVariableText();
369        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
370    }
371
372    /**
373     * @param field
374     * @param text
375     * @param font
376     * @param fontSize
377     * @param llx
378     * @param lly
379     * @param urx
380     * @param ury
381     */
382    public void drawMultiLineOfText(PdfFormField field, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
383        PdfAppearance tp = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
384        PdfAppearance tp2 = (PdfAppearance)tp.getDuplicate();
385        tp2.setFontAndSize(font, fontSize);
386        tp2.resetRGBColorFill();
387        field.setDefaultAppearanceString(tp2);
388        tp.drawTextField(0f, 0f, urx - llx, ury - lly);
389        tp.beginVariableText();
390        tp.saveState();
391        tp.rectangle(3f, 3f, urx - llx - 6f, ury - lly - 6f);
392        tp.clip();
393        tp.newPath();
394        tp.beginText();
395        tp.setFontAndSize(font, fontSize);
396        tp.resetRGBColorFill();
397        tp.setTextMatrix(4, 5);
398        java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(text, "\n");
399        float yPos = ury - lly;
400        while (tokenizer.hasMoreTokens()) {
401            yPos -= fontSize * 1.2f;
402            tp.showTextAligned(PdfContentByte.ALIGN_LEFT, tokenizer.nextToken(), 3, yPos, 0);
403        }
404        tp.endText();
405        tp.restoreState();
406        tp.endVariableText();
407        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
408    }
409
410    /**
411     * @param name
412     * @param value
413     * @param status
414     * @param llx
415     * @param lly
416     * @param urx
417     * @param ury
418     * @return a PdfFormField
419     */
420    public PdfFormField addCheckBox(String name, String value, boolean status, float llx, float lly, float urx, float ury) {
421        PdfFormField field = PdfFormField.createCheckBox(writer);
422        setCheckBoxParams(field, name, value, status, llx, lly, urx, ury);
423        drawCheckBoxAppearences(field, value, llx, lly, urx, ury);
424        addFormField(field);
425        return field;
426    }
427
428    /**
429     * @param field
430     * @param name
431     * @param value
432     * @param status
433     * @param llx
434     * @param lly
435     * @param urx
436     * @param ury
437     */
438    public void setCheckBoxParams(PdfFormField field, String name, String value, boolean status, float llx, float lly, float urx, float ury) {
439        field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_TOGGLE);
440        field.setFieldName(name);
441        if (status) {
442            field.setValueAsName(value);
443            field.setAppearanceState(value);
444        }
445        else {
446            field.setValueAsName("Off");
447            field.setAppearanceState("Off");
448        }
449        field.setFlags(PdfAnnotation.FLAGS_PRINT);
450        field.setPage();
451        field.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
452    }
453
454    /**
455     * @param field
456     * @param value
457     * @param llx
458     * @param lly
459     * @param urx
460     * @param ury
461     */
462    public void drawCheckBoxAppearences(PdfFormField field, String value, float llx, float lly, float urx, float ury) {
463        BaseFont font = null;
464        try {
465            font = BaseFont.createFont(BaseFont.ZAPFDINGBATS, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
466        }
467        catch(Exception e) {
468            throw new ExceptionConverter(e);
469        }
470        float size = ury - lly;
471        PdfAppearance tpOn = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
472        PdfAppearance tp2 = (PdfAppearance)tpOn.getDuplicate();
473        tp2.setFontAndSize(font, size);
474        tp2.resetRGBColorFill();
475        field.setDefaultAppearanceString(tp2);
476        tpOn.drawTextField(0f, 0f, urx - llx, ury - lly);
477        tpOn.saveState();
478        tpOn.resetRGBColorFill();
479        tpOn.beginText();
480        tpOn.setFontAndSize(font, size);
481        tpOn.showTextAligned(PdfContentByte.ALIGN_CENTER, "4", (urx - llx) / 2, (ury - lly) / 2 - size * 0.3f, 0);
482        tpOn.endText();
483        tpOn.restoreState();
484        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, value, tpOn);
485        PdfAppearance tpOff = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
486        tpOff.drawTextField(0f, 0f, urx - llx, ury - lly);
487        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, "Off", tpOff);
488    }
489
490    /**
491     * @param name
492     * @param defaultValue
493     * @param noToggleToOff
494     * @return a PdfFormField
495     */
496    public PdfFormField getRadioGroup(String name, String defaultValue, boolean noToggleToOff) {
497        PdfFormField radio = PdfFormField.createRadioButton(writer, noToggleToOff);
498        radio.setFieldName(name);
499        radio.setValueAsName(defaultValue);
500        return radio;
501    }
502
503    /**
504     * @param radiogroup
505     */
506    public void addRadioGroup(PdfFormField radiogroup) {
507        addFormField(radiogroup);
508    }
509
510    /**
511     * @param radiogroup
512     * @param value
513     * @param llx
514     * @param lly
515     * @param urx
516     * @param ury
517     * @return a PdfFormField
518     */
519    public PdfFormField addRadioButton(PdfFormField radiogroup, String value, float llx, float lly, float urx, float ury) {
520        PdfFormField radio = PdfFormField.createEmpty(writer);
521        radio.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_TOGGLE);
522        String name = ((PdfName)radiogroup.get(PdfName.V)).toString().substring(1);
523        if (name.equals(value)) {
524            radio.setAppearanceState(value);
525        }
526        else {
527            radio.setAppearanceState("Off");
528        }
529        drawRadioAppearences(radio, value, llx, lly, urx, ury);
530        radiogroup.addKid(radio);
531        return radio;
532    }
533
534    /**
535     * @param field
536     * @param value
537     * @param llx
538     * @param lly
539     * @param urx
540     * @param ury
541     */
542    public void drawRadioAppearences(PdfFormField field, String value, float llx, float lly, float urx, float ury) {
543        PdfAppearance tpOn = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
544        tpOn.drawRadioField(0f, 0f, urx - llx, ury - lly, true);
545        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, value, tpOn);
546        PdfAppearance tpOff = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
547        tpOff.drawRadioField(0f, 0f, urx - llx, ury - lly, false);
548        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, "Off", tpOff);
549    }
550
551    /**
552     * @param name
553     * @param options
554     * @param defaultValue
555     * @param font
556     * @param fontSize
557     * @param llx
558     * @param lly
559     * @param urx
560     * @param ury
561     * @return a PdfFormField
562     */
563    public PdfFormField addSelectList(String name, String[] options, String defaultValue, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
564        PdfFormField choice = PdfFormField.createList(writer, options, 0);
565        setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
566        StringBuffer text = new StringBuffer();
567        for (String option : options) {
568            text.append(option).append('\n');
569        }
570        drawMultiLineOfText(choice, text.toString(), font, fontSize, llx, lly, urx, ury);
571        addFormField(choice);
572        return choice;
573    }
574
575    /**
576     * @param name
577     * @param options
578     * @param defaultValue
579     * @param font
580     * @param fontSize
581     * @param llx
582     * @param lly
583     * @param urx
584     * @param ury
585     * @return a PdfFormField
586     */
587    public PdfFormField addSelectList(String name, String[][] options, String defaultValue, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
588        PdfFormField choice = PdfFormField.createList(writer, options, 0);
589        setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
590        StringBuffer text = new StringBuffer();
591        for (String[] option : options) {
592            text.append(option[1]).append('\n');
593        }
594        drawMultiLineOfText(choice, text.toString(), font, fontSize, llx, lly, urx, ury);
595        addFormField(choice);
596        return choice;
597    }
598
599    /**
600     * @param name
601     * @param options
602     * @param defaultValue
603     * @param editable
604     * @param font
605     * @param fontSize
606     * @param llx
607     * @param lly
608     * @param urx
609     * @param ury
610     * @return a PdfFormField
611     */
612    public PdfFormField addComboBox(String name, String[] options, String defaultValue, boolean editable, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
613        PdfFormField choice = PdfFormField.createCombo(writer, editable, options, 0);
614        setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
615        if (defaultValue == null) {
616            defaultValue = options[0];
617        }
618        drawSingleLineOfText(choice, defaultValue, font, fontSize, llx, lly, urx, ury);
619        addFormField(choice);
620        return choice;
621    }
622
623    /**
624     * @param name
625     * @param options
626     * @param defaultValue
627     * @param editable
628     * @param font
629     * @param fontSize
630     * @param llx
631     * @param lly
632     * @param urx
633     * @param ury
634     * @return a PdfFormField
635     */
636    public PdfFormField addComboBox(String name, String[][] options, String defaultValue, boolean editable, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
637        PdfFormField choice = PdfFormField.createCombo(writer, editable, options, 0);
638        setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
639        String value = null;
640        for (String[] option : options) {
641            if (option[0].equals(defaultValue)) {
642                value = option[1];
643                break;
644            }
645        }
646        if (value == null) {
647            value = options[0][1];
648        }
649        drawSingleLineOfText(choice, value, font, fontSize, llx, lly, urx, ury);
650        addFormField(choice);
651        return choice;
652    }
653
654    /**
655     * @param field
656     * @param name
657     * @param defaultValue
658     * @param llx
659     * @param lly
660     * @param urx
661     * @param ury
662     */
663    public void setChoiceParams(PdfFormField field, String name, String defaultValue, float llx, float lly, float urx, float ury) {
664        field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_INVERT);
665        if (defaultValue != null) {
666            field.setValueAsString(defaultValue);
667            field.setDefaultValueAsString(defaultValue);
668        }
669        field.setFieldName(name);
670        field.setFlags(PdfAnnotation.FLAGS_PRINT);
671        field.setPage();
672        field.setBorderStyle(new PdfBorderDictionary(2, PdfBorderDictionary.STYLE_SOLID));
673    }
674
675    /**
676     * @param name
677     * @param llx
678     * @param lly
679     * @param urx
680     * @param ury
681     * @return a PdfFormField
682     */
683    public PdfFormField addSignature(String name,
684                    float llx, float lly, float urx, float ury) {
685        PdfFormField signature = PdfFormField.createSignature(writer);
686        setSignatureParams(signature, name, llx, lly, urx, ury);
687        drawSignatureAppearences(signature, llx, lly, urx, ury);
688        addFormField(signature);
689        return signature;
690    }
691
692    /**
693     * @param field
694     * @param name
695     * @param llx
696     * @param lly
697     * @param urx
698     * @param ury
699     */
700    public void setSignatureParams(PdfFormField field, String name,
701                    float llx, float lly, float urx, float ury) {
702        field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_INVERT);
703        field.setFieldName(name);
704        field.setFlags(PdfAnnotation.FLAGS_PRINT);
705        field.setPage();
706        field.setMKBorderColor(BaseColor.BLACK);
707        field.setMKBackgroundColor(BaseColor.WHITE);
708    }
709
710    /**
711     * @param field
712     * @param llx
713     * @param lly
714     * @param urx
715     * @param ury
716     */
717    public void drawSignatureAppearences(PdfFormField field,
718                    float llx, float lly, float urx, float ury) {
719        PdfAppearance tp = PdfAppearance.createAppearance(writer, urx - llx, ury - lly);
720        tp.setGrayFill(1.0f);
721        tp.rectangle(0, 0, urx - llx, ury - lly);
722        tp.fill();
723        tp.setGrayStroke(0);
724        tp.setLineWidth(1);
725        tp.rectangle(0.5f, 0.5f, urx - llx - 0.5f, ury - lly - 0.5f);
726        tp.closePathStroke();
727        tp.saveState();
728        tp.rectangle(1, 1, urx - llx - 2, ury - lly - 2);
729        tp.clip();
730        tp.newPath();
731        tp.restoreState();
732        field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
733    }
734}