Skip to content

Commit 8729ac9

Browse files
committed
Use renderers to render push button form field appearance
DEVSIX-7359
1 parent 30a539f commit 8729ac9

File tree

53 files changed

+946
-464
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+946
-464
lines changed

forms/src/main/java/com/itextpdf/forms/fields/PdfFormAnnotation.java

Lines changed: 110 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ This file is part of the iText (R) project.
3333
import com.itextpdf.forms.form.element.InputField;
3434
import com.itextpdf.forms.form.element.Radio;
3535
import com.itextpdf.forms.form.element.TextArea;
36+
import com.itextpdf.forms.form.element.Button;
3637
import com.itextpdf.forms.logs.FormsLogMessageConstants;
3738
import com.itextpdf.forms.util.DrawingUtil;
3839
import com.itextpdf.forms.util.FontSizeUtil;
@@ -68,6 +69,7 @@ This file is part of the iText (R) project.
6869
import com.itextpdf.layout.borders.Border;
6970
import com.itextpdf.layout.borders.SolidBorder;
7071
import com.itextpdf.layout.element.Div;
72+
import com.itextpdf.layout.element.Image;
7173
import com.itextpdf.layout.element.Paragraph;
7274
import com.itextpdf.layout.element.Text;
7375
import 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
}

forms/src/main/java/com/itextpdf/forms/fields/PushButtonFormFieldBuilder.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ This file is part of the iText (R) project.
3030
import com.itextpdf.kernel.pdf.PdfString;
3131
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
3232
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
33-
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
3433

3534
/**
3635
* Builder for push button form field.
@@ -94,19 +93,13 @@ public PdfButtonFormField createPushButton() {
9493

9594
if (annotation != null) {
9695
field.getFirstFormAnnotation().backgroundColor = ColorConstants.LIGHT_GRAY;
97-
PdfFormXObject xObject = field.getFirstFormAnnotation().drawPushButtonAppearance(
98-
getWidgetRectangle().getWidth(), getWidgetRectangle().getHeight(),
99-
caption, getDocument().getDefaultFont(), AbstractPdfFormField.DEFAULT_FONT_SIZE);
100-
annotation.setNormalAppearance(xObject.getPdfObject());
96+
field.getFirstFormAnnotation().drawPushButtonFieldAndSaveAppearance();
10197

10298
PdfDictionary mk = new PdfDictionary();
10399
mk.put(PdfName.CA, new PdfString(caption));
104100
mk.put(PdfName.BG, new PdfArray(field.getFirstFormAnnotation().backgroundColor.getColorValue()));
105101
annotation.setAppearanceCharacteristics(mk);
106102

107-
if (getConformanceLevel() != null) {
108-
PdfFormAnnotation.createPushButtonAppearanceState(annotation.getPdfObject());
109-
}
110103
setPageToField(field);
111104
}
112105

0 commit comments

Comments
 (0)