@@ -33,6 +33,7 @@ This file is part of the iText (R) project.
3333import com .itextpdf .forms .form .element .InputField ;
3434import com .itextpdf .forms .form .element .Radio ;
3535import com .itextpdf .forms .form .element .TextArea ;
36+ import com .itextpdf .forms .form .element .Button ;
3637import com .itextpdf .forms .logs .FormsLogMessageConstants ;
3738import com .itextpdf .forms .util .DrawingUtil ;
3839import com .itextpdf .forms .util .FontSizeUtil ;
@@ -68,6 +69,7 @@ This file is part of the iText (R) project.
6869import com .itextpdf .layout .borders .Border ;
6970import com .itextpdf .layout .borders .SolidBorder ;
7071import com .itextpdf .layout .element .Div ;
72+ import com .itextpdf .layout .element .Image ;
7173import com .itextpdf .layout .element .Paragraph ;
7274import com .itextpdf .layout .element .Text ;
7375import com .itextpdf .layout .properties .Background ;
@@ -120,6 +122,8 @@ public class PdfFormAnnotation extends AbstractPdfFormField {
120122 protected Color backgroundColor ;
121123 protected Color borderColor ;
122124
125+ private Button formFieldElement ;
126+
123127 /**
124128 * Creates a form field annotation as a wrapper of a {@link PdfWidgetAnnotation}.
125129 *
@@ -389,6 +393,19 @@ public PdfFormAnnotation setPage(int pageNum) {
389393 return this ;
390394 }
391395
396+ /**
397+ * Sets an element associated with the current field.
398+ *
399+ * @param element model element to set.
400+ *
401+ * @return this {@link PdfFormAnnotation}.
402+ */
403+ public PdfFormAnnotation setFormFieldElement (Button element ) {
404+ this .formFieldElement = element ;
405+ regenerateWidget ();
406+ return this ;
407+ }
408+
392409 /**
393410 * Gets the appearance state names.
394411 *
@@ -536,9 +553,7 @@ protected void drawTextAppearance(Rectangle rect, PdfFont font, float fontSize,
536553
537554 Style paragraphStyle = new Style ().setFont (font ).setFontSize (fontSize );
538555 paragraphStyle .setProperty (Property .LEADING , new Leading (Leading .MULTIPLIED , 1 ));
539- if (getColor () != null ) {
540- paragraphStyle .setProperty (Property .FONT_COLOR , new TransparentColor (getColor ()));
541- }
556+ paragraphStyle .setFontColor (getColor ());
542557
543558 int maxLen = new PdfTextFormField (parent .getPdfObject ()).getMaxLen ();
544559 // check if /Comb has been set
@@ -750,73 +765,67 @@ protected void drawPdfA2CheckAppearance(float width, float height, String onStat
750765 }
751766
752767 /**
753- * Draws the appearance for a push button.
754- *
755- * @param width the width of the pushbutton
756- * @param height the width of the pushbutton
757- * @param text the text to display on the button
758- * @param font a {@link PdfFont}
759- * @param fontSize the size of the font
760- *
761- * @return a new {@link PdfFormXObject}
768+ * Draws the appearance of a push button and saves it into an appearance stream.
762769 */
763- protected PdfFormXObject drawPushButtonAppearance (float width , float height , String text ,
764- PdfFont font , float fontSize ) {
765- PdfStream stream = (PdfStream ) new PdfStream ().makeIndirect (getDocument ());
766- PdfCanvas canvas = new PdfCanvas (stream , new PdfResources (), getDocument ());
770+ protected void drawPushButtonFieldAndSaveAppearance () {
771+ Rectangle rectangle = getRect (this .getPdfObject ());
772+ if (rectangle == null ) {
773+ return ;
774+ }
775+ float width = rectangle .getWidth ();
776+ float height = rectangle .getHeight ();
777+
778+ createInputButton ();
767779
768780 PdfFormXObject xObject = new PdfFormXObject (new Rectangle (0 , 0 , width , height ));
769- drawBorder (canvas , xObject , width , height );
781+ applyRotation (xObject , height , width );
782+ Canvas canvas = new Canvas (xObject , this .getDocument ());
783+ setMetaInfoToCanvas (canvas );
784+
785+ String caption = parent .getDisplayValue ();
786+ if (caption != null && !caption .isEmpty ()) {
787+ formFieldElement .setSingleLineValue (caption );
788+ }
770789
790+ float imagePadding = borderColor == null ? 0 : borderWidth ;
771791 if (parent .img != null ) {
772- PdfImageXObject imgXObj = new PdfImageXObject (parent .img );
773- canvas .addXObjectWithTransformationMatrix (imgXObj , width - borderWidth , 0 , 0 ,
774- height - borderWidth , borderWidth / 2 , borderWidth / 2 );
775- xObject .getResources ().addImage (imgXObj );
792+ // If we got here, the button will only contain the image that the user has set into the annotation.
793+ // There is no way to pass other elements with this image.
794+ formFieldElement .getChildren ().clear ();
795+ Image image = new Image (new PdfImageXObject (parent .img ), imagePadding , imagePadding );
796+ image .setHeight (height - 2 * imagePadding );
797+ image .setWidth (width - 2 * imagePadding );
798+ formFieldElement .add (image );
776799 } else if (parent .form != null ) {
777- canvas .addXObjectWithTransformationMatrix (parent .form ,
778- (height - borderWidth ) / parent .form .getHeight (), 0 , 0 ,
779- (height - borderWidth ) / parent .form .getHeight (), borderWidth / 2 , borderWidth / 2 );
780- xObject .getResources ().addForm (parent .form );
800+ // If we got here, the button will only contain the image that the user has set as form into the annotation.
801+ // There is no way to pass other elements with this image as form.
802+ formFieldElement .getChildren ().clear ();
803+ Image image = new Image (parent .form , imagePadding , imagePadding );
804+ image .setHeight (height - 2 * imagePadding );
805+ formFieldElement .add (image );
781806 } else {
782- drawButton (canvas , 0 , 0 , width , height , text , font , fontSize );
783- xObject .getResources ().addFont (getDocument (), font );
807+ xObject .getResources ().addFont (getDocument (), getFont ());
784808 }
785- xObject .getPdfObject ().getOutputStream ().writeBytes (stream .getBytes ());
786-
787- return xObject ;
788- }
809+ canvas .add (formFieldElement );
789810
790- /**
791- * Performs the low-level drawing operations to draw a button object.
792- *
793- * @param canvas the {@link PdfCanvas} of the page to draw on.
794- * @param x will be ignored, according to spec it shall be 0
795- * @param y will be ignored, according to spec it shall be 0
796- * @param width the width of the button
797- * @param height the width of the button
798- * @param text the text to display on the button
799- * @param font a {@link PdfFont}
800- * @param fontSize the size of the font
801- */
802- protected void drawButton (PdfCanvas canvas , float x , float y , float width , float height , String text , PdfFont font ,
803- float fontSize ) {
804- if (getColor () == null ) {
805- color = ColorConstants .BLACK ;
806- }
807- if (text == null ) {
808- text = "" ;
811+ PdfDictionary ap = new PdfDictionary ();
812+ PdfStream normalAppearanceStream = xObject .getPdfObject ();
813+ if (normalAppearanceStream != null ) {
814+ PdfName stateName = getPdfObject ().getAsName (PdfName .AS );
815+ if (stateName == null ) {
816+ stateName = new PdfName ("push" );
817+ }
818+ getPdfObject ().put (PdfName .AS , stateName );
819+ PdfDictionary normalAppearance = new PdfDictionary ();
820+ normalAppearance .put (stateName , normalAppearanceStream );
821+ ap .put (PdfName .N , normalAppearance );
822+ ap .setModified ();
809823 }
810-
811- Paragraph paragraph = new Paragraph (text ).setFont (font ).setFontSize (fontSize ).setMargin (0 ).
812- setMultipliedLeading (1 ).setVerticalAlignment (VerticalAlignment .MIDDLE );
813- Canvas modelCanvas = new Canvas (canvas , new Rectangle (0 , -height , width , 2 * height ));
814- modelCanvas .setProperty (Property .APPEARANCE_STREAM_LAYOUT , Boolean .TRUE );
815-
816- setMetaInfoToCanvas (modelCanvas );
817-
818- modelCanvas .showTextAligned (paragraph , width / 2 , height / 2 , TextAlignment .CENTER ,
819- VerticalAlignment .MIDDLE );
824+ put (PdfName .AP , ap );
825+ // We need to draw waitingDrawingElements (drawn inside close method), but the close method
826+ // flushes TagTreePointer that will be used later, so set null to the corresponding property.
827+ canvas .setProperty (Property .TAGGING_HELPER , null );
828+ canvas .close ();
820829 }
821830
822831 /**
@@ -1052,47 +1061,13 @@ void drawChoiceAppearance(Rectangle rect, float fontSize, String value, PdfFormX
10521061 appearance .getPdfObject ().setData (stream .getBytes ());
10531062 }
10541063
1055- static void createPushButtonAppearanceState (PdfDictionary widget ) {
1056- PdfDictionary appearances = widget .getAsDictionary (PdfName .AP );
1057- PdfStream normalAppearanceStream = appearances .getAsStream (PdfName .N );
1058- if (normalAppearanceStream != null ) {
1059- PdfName stateName = widget .getAsName (PdfName .AS );
1060- if (stateName == null ) {
1061- stateName = new PdfName ("push" );
1062- }
1063- widget .put (PdfName .AS , stateName );
1064- PdfDictionary normalAppearance = new PdfDictionary ();
1065- normalAppearance .put (stateName , normalAppearanceStream );
1066- appearances .put (PdfName .N , normalAppearance );
1067- }
1068- }
1069-
10701064 static void setMetaInfoToCanvas (Canvas canvas ) {
10711065 MetaInfoContainer metaInfo = FormsMetaInfoStaticContainer .getMetaInfoForLayout ();
10721066 if (metaInfo != null ) {
10731067 canvas .setProperty (Property .META_INFO , metaInfo );
10741068 }
10751069 }
10761070
1077- void regeneratePushButtonField () {
1078- PdfDictionary widget = getPdfObject ();
1079- PdfFormXObject appearance ;
1080- Rectangle rect = getRect (widget );
1081- PdfDictionary apDic = widget .getAsDictionary (PdfName .AP );
1082-
1083- if (apDic == null ) {
1084- put (PdfName .AP , apDic = new PdfDictionary ());
1085- }
1086- appearance = drawPushButtonAppearance (rect .getWidth (), rect .getHeight (), parent .getDisplayValue (),
1087- getFont (), getFontSize (widget .getAsArray (PdfName .Rect ), parent .getDisplayValue ()));
1088-
1089- apDic .put (PdfName .N , appearance .getPdfObject ());
1090-
1091- if (getPdfAConformanceLevel () != null ) {
1092- createPushButtonAppearanceState (widget );
1093- }
1094- }
1095-
10961071 //TODO DEVSIX-7443 remove method
10971072 void regenerateCheckboxField (CheckBoxType checkType ) {
10981073 parent .setCheckType (checkType );
@@ -1269,7 +1244,7 @@ boolean regenerateWidget() {
12691244 return regenerateTextAndChoiceField ();
12701245 } else if (PdfName .Btn .equals (type )) {
12711246 if (parent .getFieldFlag (PdfButtonFormField .FF_PUSH_BUTTON )) {
1272- regeneratePushButtonField ();
1247+ drawPushButtonFieldAndSaveAppearance ();
12731248 } else if (parent .getFieldFlag (PdfButtonFormField .FF_RADIO )) {
12741249 drawRadioButtonAndSaveAppearance (getRadioButtonValue ());
12751250 } else {
@@ -1286,6 +1261,45 @@ boolean regenerateWidget() {
12861261 return false ;
12871262 }
12881263
1264+ void createInputButton () {
1265+ final Rectangle rect = getRect (getPdfObject ());
1266+ if (rect == null ) {
1267+ formFieldElement = null ;
1268+ return ;
1269+ }
1270+
1271+ if (formFieldElement == null ) {
1272+ // Create it one time and re-set properties during each widget regeneration.
1273+ formFieldElement = new Button (parent .getFieldName ().toUnicodeString ());
1274+ }
1275+
1276+ formFieldElement .setFont (getFont ());
1277+ formFieldElement .setFontSize (getFontSize (getPdfObject ()
1278+ .getAsArray (PdfName .Rect ), parent .getDisplayValue ()));
1279+ if (getColor () == null ) {
1280+ final TransparentColor transparentColor =
1281+ formFieldElement .<TransparentColor >getProperty (Property .FONT_COLOR );
1282+ color = transparentColor == null ? ColorConstants .BLACK : transparentColor .getColor ();
1283+ }
1284+ formFieldElement .setFontColor (color );
1285+
1286+ formFieldElement .setBackgroundColor (backgroundColor );
1287+ if (borderWidth > 0 && borderColor != null ) {
1288+ final float borderWidth = Math .max (1 , getBorderWidth ());
1289+ // Don't take border into account as it will be drawn inside
1290+ Border border = FormBorderFactory .getBorder (getWidget ().getBorderStyle (),
1291+ borderWidth , borderColor , backgroundColor );
1292+ formFieldElement .setBorder (border != null ? border : new SolidBorder (borderColor , borderWidth ));
1293+ }
1294+
1295+ // Set fixed size
1296+ formFieldElement .setProperty (Property .WIDTH , UnitValue .createPointValue (rect .getWidth ()));
1297+ formFieldElement .setProperty (Property .HEIGHT , UnitValue .createPointValue (rect .getHeight ()));
1298+
1299+ // Always flatten
1300+ formFieldElement .setInteractive (false );
1301+ }
1302+
12891303 Radio createRadio () {
12901304 final Rectangle rect = getRect (getPdfObject ());
12911305 if (rect == null ) {
@@ -1311,7 +1325,7 @@ Radio createRadio() {
13111325 radio .setProperty (Property .HEIGHT , UnitValue .createPointValue (rect .getHeight ()));
13121326
13131327 // Always flatten
1314- radio .setProperty ( FormProperty . FORM_FIELD_FLATTEN , true );
1328+ radio .setInteractive ( false );
13151329
13161330 return radio ;
13171331 }
0 commit comments