Skip to content

Commit 5e695c4

Browse files
committed
Add the ability to set font family names to support latin symbols in calligraph scripts
Approximate font size before the layout to get rid of clip_element messages DEVSIX-7787
1 parent 9b53b22 commit 5e695c4

File tree

41 files changed

+172
-78
lines changed

Some content is hidden

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

41 files changed

+172
-78
lines changed

forms/src/main/java/com/itextpdf/forms/form/renderer/AbstractTextFieldRenderer.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,23 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.forms.form.renderer;
2424

25+
import com.itextpdf.forms.fields.AbstractPdfFormField;
2526
import com.itextpdf.forms.fields.PdfFormField;
2627
import com.itextpdf.forms.form.element.IFormField;
2728
import com.itextpdf.kernel.font.PdfFont;
2829
import com.itextpdf.kernel.geom.Rectangle;
2930
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
3031
import com.itextpdf.layout.element.Paragraph;
3132
import com.itextpdf.layout.element.Text;
33+
import com.itextpdf.layout.layout.LayoutArea;
34+
import com.itextpdf.layout.layout.LayoutContext;
35+
import com.itextpdf.layout.layout.LayoutResult;
3236
import com.itextpdf.layout.properties.Background;
3337
import com.itextpdf.layout.properties.BoxSizingPropertyValue;
3438
import com.itextpdf.layout.properties.Property;
3539
import com.itextpdf.layout.properties.TextAlignment;
3640
import com.itextpdf.layout.properties.TransparentColor;
41+
import com.itextpdf.layout.properties.UnitValue;
3742
import com.itextpdf.layout.renderer.IRenderer;
3843
import com.itextpdf.layout.renderer.LineRenderer;
3944
import com.itextpdf.layout.renderer.ParagraphRenderer;
@@ -129,6 +134,49 @@ void updatePdfFont(ParagraphRenderer renderer) {
129134
}
130135
}
131136

137+
/**
138+
* Approximates font size to fit occupied area if width anf height are specified.
139+
*
140+
* @param layoutContext layout context that specifies layout area.
141+
* @param lFontSize minimal font size value.
142+
* @param rFontSize maximum font size value.
143+
*
144+
* @return fitting font size or -1 in case it shouldn't be approximated.
145+
*/
146+
float approximateFontSize(LayoutContext layoutContext, float lFontSize, float rFontSize) {
147+
IRenderer flatRenderer = createFlatRenderer().setParent(this);
148+
149+
Float areaWidth = retrieveWidth(layoutContext.getArea().getBBox().getWidth());
150+
Float areaHeight = retrieveHeight();
151+
if (areaWidth == null || areaHeight == null) {
152+
return -1;
153+
}
154+
flatRenderer.setProperty(Property.FONT_SIZE, UnitValue.createPointValue(AbstractPdfFormField.DEFAULT_FONT_SIZE));
155+
LayoutContext newLayoutContext = new LayoutContext(new LayoutArea(1,
156+
new Rectangle((float) areaWidth, (float) areaHeight)));
157+
if (flatRenderer.layout(newLayoutContext).getStatus() == LayoutResult.FULL) {
158+
return -1;
159+
} else {
160+
final int numberOfIterations = 6;
161+
return calculateFittingFontSize(flatRenderer, lFontSize, rFontSize, newLayoutContext, numberOfIterations);
162+
}
163+
}
164+
165+
float calculateFittingFontSize(IRenderer renderer, float lFontSize, float rFontSize,
166+
LayoutContext newLayoutContext, int numberOfIterations) {
167+
for (int i = 0; i < numberOfIterations; i++) {
168+
float mFontSize = (lFontSize + rFontSize) / 2;
169+
renderer.setProperty(Property.FONT_SIZE, UnitValue.createPointValue(mFontSize));
170+
LayoutResult result = renderer.layout(newLayoutContext);
171+
if (result.getStatus() == LayoutResult.FULL) {
172+
lFontSize = mFontSize;
173+
} else {
174+
rFontSize = mFontSize;
175+
}
176+
}
177+
return lFontSize;
178+
}
179+
132180
// The width based on cols of textarea and size of input isn't affected by box sizing, so we emulate it here.
133181
float updateHtmlColsSizeBasedWidth(float width) {
134182
if (BoxSizingPropertyValue.BORDER_BOX == this.<BoxSizingPropertyValue>getProperty(Property.BOX_SIZING)) {

forms/src/main/java/com/itextpdf/forms/form/renderer/SigFieldRenderer.java

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import com.itextpdf.commons.utils.MessageFormatUtil;
2626
import com.itextpdf.forms.PdfAcroForm;
27+
import com.itextpdf.forms.fields.AbstractPdfFormField;
2728
import com.itextpdf.forms.fields.PdfFormCreator;
2829
import com.itextpdf.forms.fields.PdfSignatureFormField;
2930
import com.itextpdf.forms.fields.SignatureFormFieldBuilder;
@@ -69,6 +70,8 @@ public class SigFieldRenderer extends AbstractTextFieldRenderer {
6970

7071
private static final float EPS = 1e-5f;
7172

73+
private boolean isFontSizeApproximated = false;
74+
7275
/**
7376
* Creates a new {@link SigFieldRenderer} instance.
7477
*
@@ -111,7 +114,8 @@ protected IRenderer createFlatRenderer() {
111114
throw new IllegalStateException("A signature image must be present when rendering mode is " +
112115
"graphic and description. Use setSignatureGraphic()");
113116
}
114-
div.add(new Image(signatureGraphic)).add(new Paragraph(description).setMultipliedLeading(0.9f));
117+
div.add(new Image(signatureGraphic))
118+
.add(new Paragraph(description).setMargin(0).setMultipliedLeading(0.9f));
115119
break;
116120
}
117121
case GRAPHIC:
@@ -123,12 +127,21 @@ protected IRenderer createFlatRenderer() {
123127
div.add(new Image(signatureGraphic));
124128
break;
125129
default:
126-
div.add(new Paragraph(description).setMultipliedLeading(0.9f));
130+
div.add(new Paragraph(description).setMargin(0).setMultipliedLeading(0.9f));
127131
break;
128132
}
129133
return div.createRendererSubTree();
130134
}
131135

136+
/**
137+
* {@inheritDoc}
138+
*/
139+
@Override
140+
public LayoutResult layout(LayoutContext layoutContext) {
141+
approximateFontSizeToFitLayoutArea(layoutContext);
142+
return super.layout(layoutContext);
143+
}
144+
132145
/**
133146
* {@inheritDoc}
134147
*
@@ -153,28 +166,33 @@ protected void adjustFieldLayout(LayoutContext layoutContext) {
153166
case GRAPHIC_AND_DESCRIPTION: {
154167
// Split the signature field into two and add the name of the signer or an image to the one side,
155168
// the description to the other side.
169+
UnitValue[] paddings = getPaddings();
156170
if (bBox.getHeight() > bBox.getWidth()) {
171+
float topPadding = paddings[0].getValue();
172+
float bottomPadding = paddings[2].getValue();
157173
signatureRect = new Rectangle(
158174
bBox.getX(),
159-
bBox.getY() + bBox.getHeight() / 2,
175+
bBox.getY() + bBox.getHeight() / 2 + bottomPadding / 2,
160176
bBox.getWidth(),
161-
bBox.getHeight() / 2);
177+
bBox.getHeight() / 2 - bottomPadding / 2);
162178
descriptionRect = new Rectangle(
163179
bBox.getX(),
164180
bBox.getY(),
165181
bBox.getWidth(),
166-
bBox.getHeight() / 2);
182+
bBox.getHeight() / 2 - topPadding / 2);
167183
} else {
168184
// origin is the bottom-left
185+
float rightPadding = paddings[1].getValue();
186+
float leftPadding = paddings[3].getValue();
169187
signatureRect = new Rectangle(
170188
bBox.getX(),
171189
bBox.getY(),
172-
bBox.getWidth() / 2,
190+
bBox.getWidth() / 2 - rightPadding / 2,
173191
bBox.getHeight());
174192
descriptionRect = new Rectangle(
175-
bBox.getX() + bBox.getWidth() / 2,
193+
bBox.getX() + bBox.getWidth() / 2 + leftPadding / 2,
176194
bBox.getY(),
177-
bBox.getWidth() / 2,
195+
bBox.getWidth() / 2 - leftPadding / 2,
178196
bBox.getHeight());
179197
}
180198
break;
@@ -185,7 +203,15 @@ protected void adjustFieldLayout(LayoutContext layoutContext) {
185203
break;
186204
default:
187205
// Default one, it just shows whatever description was defined for the signature.
188-
descriptionRect = bBox.setHeight(getOccupiedArea().getBBox().getHeight() * (1 - TOP_SECTION));
206+
if (retrieveHeight() == null) {
207+
// Adjust calculated occupied area height to keep the same font size.
208+
float calculatedHeight = getOccupiedArea().getBBox().getHeight();
209+
getOccupiedArea().getBBox().moveDown(calculatedHeight * TOP_SECTION)
210+
.setHeight(calculatedHeight * (1 + TOP_SECTION));
211+
bBox.moveDown(calculatedHeight * TOP_SECTION);
212+
}
213+
descriptionRect = bBox.setHeight(getOccupiedArea().getBBox().getHeight() * (1 - TOP_SECTION)
214+
- calculateAdditionalHeight());
189215
break;
190216
}
191217

@@ -331,10 +357,10 @@ private void relayoutImage(Rectangle signatureRect, int pageNum) {
331357
}
332358

333359
private void relayoutParagraph(IRenderer renderer, Rectangle rect, int pageNum) {
334-
UnitValue fontSize = this.hasOwnProperty(Property.FONT_SIZE) ?
360+
UnitValue fontSizeAsUV = this.hasOwnProperty(Property.FONT_SIZE) ?
335361
(UnitValue) this.<UnitValue>getOwnProperty(Property.FONT_SIZE) :
336362
(UnitValue) modelElement.<UnitValue>getOwnProperty(Property.FONT_SIZE);
337-
if (fontSize == null || fontSize.getValue() < EPS) {
363+
if (fontSizeAsUV == null || fontSizeAsUV.getValue() < EPS || isFontSizeApproximated) {
338364
// Calculate font size.
339365
IRenderer helper = ((Paragraph) renderer.getModelElement()).createRendererSubTree()
340366
.setParent(renderer.getParent());
@@ -343,19 +369,8 @@ private void relayoutParagraph(IRenderer renderer, Rectangle rect, int pageNum)
343369
float lFontSize = 0.1f, rFontSize = 100;
344370
int numberOfIterations = 15;
345371
// 15 iterations with lFontSize = 0.1 and rFontSize = 100 should result in ~0.003 precision.
346-
for (int i = 0; i < numberOfIterations; i++) {
347-
float mFontSize = (lFontSize + rFontSize) / 2;
348-
UnitValue fontSizeAsUV = UnitValue.createPointValue(mFontSize);
349-
helper.setProperty(Property.FONT_SIZE, fontSizeAsUV);
350-
LayoutResult result = helper.layout(layoutContext);
351-
if (result.getStatus() == LayoutResult.FULL) {
352-
lFontSize = mFontSize;
353-
} else {
354-
rFontSize = mFontSize;
355-
}
356-
}
357-
UnitValue fontSizeAsUV = UnitValue.createPointValue(lFontSize);
358-
renderer.getModelElement().setProperty(Property.FONT_SIZE, fontSizeAsUV);
372+
float fontSize = calculateFittingFontSize(helper, lFontSize, rFontSize, layoutContext, numberOfIterations);
373+
renderer.getModelElement().setProperty(Property.FONT_SIZE, UnitValue.createPointValue(fontSize));
359374
}
360375
// Relayout the element after font size was changed or signature was split into 2 parts.
361376
LayoutContext layoutContext = new LayoutContext(new LayoutArea(pageNum, rect));
@@ -390,4 +405,29 @@ private void applyBackgroundImage(SigField modelElement) {
390405
.build());
391406
}
392407
}
408+
409+
private float calculateAdditionalHeight() {
410+
Rectangle dummy = new Rectangle(0, 0);
411+
this.applyMargins(dummy, true);
412+
this.applyBorderBox(dummy, true);
413+
this.applyPaddings(dummy, true);
414+
return dummy.getHeight();
415+
}
416+
417+
private void approximateFontSizeToFitLayoutArea(LayoutContext layoutContext) {
418+
if (this.hasOwnProperty(Property.FONT_SIZE) || modelElement.hasOwnProperty(Property.FONT_SIZE)) {
419+
return;
420+
}
421+
if (SigField.RenderingMode.GRAPHIC == ((SigField) modelElement).getRenderingMode() ||
422+
SigField.RenderingMode.GRAPHIC_AND_DESCRIPTION == ((SigField) modelElement).getRenderingMode()) {
423+
// We can expect CLIP_ELEMENT log messages since the initial image size may be larger than the field height.
424+
// But image size will be adjusted during its relayout in #adjustFieldLayout.
425+
return;
426+
}
427+
float fontSize = approximateFontSize(layoutContext, 0.1f, AbstractPdfFormField.DEFAULT_FONT_SIZE);
428+
if (fontSize > 0) {
429+
isFontSizeApproximated = true;
430+
modelElement.setProperty(Property.FONT_SIZE, UnitValue.createPointValue(fontSize));
431+
}
432+
}
393433
}

forms/src/main/java/com/itextpdf/forms/form/renderer/TextAreaRenderer.java

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ This file is part of the iText (R) project.
3535
import com.itextpdf.kernel.pdf.PdfDocument;
3636
import com.itextpdf.kernel.pdf.PdfPage;
3737
import com.itextpdf.kernel.pdf.PdfString;
38-
import com.itextpdf.layout.layout.LayoutArea;
3938
import com.itextpdf.layout.layout.LayoutContext;
4039
import com.itextpdf.layout.layout.LayoutResult;
4140
import com.itextpdf.layout.minmaxwidth.MinMaxWidth;
@@ -267,37 +266,8 @@ private void cropContentLines(List<LineRenderer> lines, Rectangle bBox) {
267266
}
268267

269268
private void approximateFontSizeToFitMultiLine(LayoutContext layoutContext) {
270-
IRenderer flatRenderer = createFlatRenderer();
271-
flatRenderer.setParent(this);
272-
TextArea modelElement = (TextArea) this.getModelElement();
273-
274-
float lFontSize = AbstractPdfFormField.MIN_FONT_SIZE;
275-
float rFontSize = AbstractPdfFormField.DEFAULT_FONT_SIZE;
276-
flatRenderer.setProperty(
277-
Property.FONT_SIZE, UnitValue.createPointValue(AbstractPdfFormField.DEFAULT_FONT_SIZE));
278-
Float areaWidth = retrieveWidth(layoutContext.getArea().getBBox().getWidth());
279-
Float areaHeight = retrieveHeight();
280-
LayoutContext newLayoutContext;
281-
if (areaWidth == null || areaHeight == null) {
282-
modelElement.setFontSize(AbstractPdfFormField.DEFAULT_FONT_SIZE);
283-
return;
284-
}
285-
newLayoutContext = new LayoutContext(new LayoutArea(1, new Rectangle((float) areaWidth, (float) areaHeight)));
286-
if (flatRenderer.layout(newLayoutContext).getStatus() == LayoutResult.FULL) {
287-
lFontSize = AbstractPdfFormField.DEFAULT_FONT_SIZE;
288-
} else {
289-
final int numberOfIterations = 6;
290-
for (int i = 0; i < numberOfIterations; i++) {
291-
float mFontSize = (lFontSize + rFontSize) / 2;
292-
flatRenderer.setProperty(Property.FONT_SIZE, UnitValue.createPointValue(mFontSize));
293-
LayoutResult result = flatRenderer.layout(newLayoutContext);
294-
if (result.getStatus() == LayoutResult.FULL) {
295-
lFontSize = mFontSize;
296-
} else {
297-
rFontSize = mFontSize;
298-
}
299-
}
300-
}
301-
modelElement.setFontSize(lFontSize);
269+
float fontSize = approximateFontSize(layoutContext, AbstractPdfFormField.MIN_FONT_SIZE,
270+
AbstractPdfFormField.DEFAULT_FONT_SIZE);
271+
((TextArea) modelElement).setFontSize(fontSize < 0 ? AbstractPdfFormField.DEFAULT_FONT_SIZE : fontSize);
302272
}
303273
}

forms/src/test/java/com/itextpdf/forms/form/element/SigFieldTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ public static void beforeClass() {
7474
}
7575

7676
@Test
77-
// TODO DEVSIX-7787 Get rid of this logs
78-
@LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.CLIP_ELEMENT))
7977
public void basicSigFieldTest() throws IOException, InterruptedException {
8078
String outPdf = DESTINATION_FOLDER + "basicSigField.pdf";
8179
String cmpPdf = SOURCE_FOLDER + "cmp_basicSigField.pdf";
@@ -155,7 +153,6 @@ public void customizedSigFieldTest() throws IOException, InterruptedException {
155153
}
156154

157155
@Test
158-
// TODO DEVSIX-7787 Get rid of this logs
159156
@LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.CLIP_ELEMENT))
160157
public void signatureFieldVerticalAlignmentTest() throws IOException, InterruptedException {
161158
String outPdf = DESTINATION_FOLDER + "signatureFieldVerticalAlignment.pdf";
@@ -347,6 +344,7 @@ public void borderTypesTest() throws IOException, InterruptedException {
347344
sigField.setSize(100);
348345
sigField.setInteractive(true);
349346
sigField.setDescription("dashed");
347+
sigField.setProperty(Property.MARGIN_BOTTOM, UnitValue.createPointValue(30));
350348
document.add(sigField);
351349

352350
PdfDictionary bs = new PdfDictionary();
@@ -359,6 +357,8 @@ public void borderTypesTest() throws IOException, InterruptedException {
359357
sigField2.setSize(100);
360358
sigField2.setInteractive(true);
361359
sigField2.setDescription("underline");
360+
sigField2.setFontSize(18);
361+
sigField2.setProperty(Property.MARGIN_BOTTOM, UnitValue.createPointValue(30));
362362
document.add(sigField2);
363363

364364
// INSET
@@ -370,6 +370,7 @@ public void borderTypesTest() throws IOException, InterruptedException {
370370
sigField3.setSize(100);
371371
sigField3.setInteractive(true);
372372
sigField3.setDescription("inset");
373+
sigField3.setProperty(Property.MARGIN_BOTTOM, UnitValue.createPointValue(30));
373374
document.add(sigField3);
374375

375376
// BEVELLED
@@ -381,6 +382,7 @@ public void borderTypesTest() throws IOException, InterruptedException {
381382
sigField4.setSize(100);
382383
sigField4.setInteractive(true);
383384
sigField4.setDescription("bevelled");
385+
sigField4.setFontSize(18);
384386
document.add(sigField4);
385387

386388
PdfFormCreator.getAcroForm(document.getPdfDocument(), false).flattenFields();

0 commit comments

Comments
 (0)