Skip to content

Commit f1f4961

Browse files
Revert to typo_ascender scale coefficient usage for Type1Fonts for HTML mode
In standard PDF fonts according to iText metrics typo ascender and typo descender don't fully include some of the font glyphs. Additionally, in HTML mode for AD < 1150 in case of 'line-height: normal' the line is expanded vertically a bit and according to CSS specification, this expansion doesn't preserve ascent/descent aspect ratio. So for standard PDF fonts (for which AD < 1150) the baseline appeared closer to the line center resulting in not optimal appearance. This issue is still present for other fonts, but at least not that significant for the default one. Preserving the aspect ratio even only for normal mode would result in inconsitency with CSS specification and our own behavior. Artificially increasing ascent/descent up to our normal line height (1.15) for all fonts will also lead to unexpected rendering. DEVSIX-3661
1 parent 3f8f41f commit f1f4961

File tree

5 files changed

+46
-57
lines changed

5 files changed

+46
-57
lines changed

layout/src/main/java/com/itextpdf/layout/renderer/LineHeightHelper.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.layout.renderer;
2424

25-
import com.itextpdf.io.font.FontMetrics;
2625
import com.itextpdf.io.font.FontProgram;
2726
import com.itextpdf.kernel.font.PdfFont;
2827
import com.itextpdf.layout.property.LineHeight;
@@ -49,7 +48,7 @@ static float[] getActualAscenderDescender(AbstractRenderer renderer) {
4948
static float[] getFontAscenderDescenderNormalized(AbstractRenderer renderer) {
5049
PdfFont font = renderer.resolveFirstPdfFont();
5150
float fontSize = renderer.getPropertyAsUnitValue(Property.FONT_SIZE).getValue();
52-
float[] fontAscenderDescenderFromMetrics = calculateFontAscenderDescenderFromFontMetrics(font);
51+
float[] fontAscenderDescenderFromMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
5352
float fontAscender = fontAscenderDescenderFromMetrics[0] / FontProgram.UNITS_NORMALIZATION * fontSize;
5453
float fontDescender = fontAscenderDescenderFromMetrics[1] / FontProgram.UNITS_NORMALIZATION * fontSize;
5554
return new float[] {fontAscender, fontDescender};
@@ -75,20 +74,4 @@ static float calculateLineHeight(AbstractRenderer renderer) {
7574
}
7675
return lineHeightValue;
7776
}
78-
79-
static float[] calculateFontAscenderDescenderFromFontMetrics(PdfFont font) {
80-
FontMetrics fontMetrics = font.getFontProgram().getFontMetrics();
81-
float ascender;
82-
float descender;
83-
if (fontMetrics.getWinAscender() == 0 || fontMetrics.getWinDescender() == 0 ||
84-
fontMetrics.getTypoAscender() == fontMetrics.getWinAscender()
85-
&& fontMetrics.getTypoDescender() == fontMetrics.getWinDescender()) {
86-
ascender = fontMetrics.getTypoAscender();
87-
descender = fontMetrics.getTypoDescender();
88-
} else {
89-
ascender = fontMetrics.getWinAscender();
90-
descender = fontMetrics.getWinDescender();
91-
}
92-
return new float[] {ascender, descender};
93-
}
9477
}

layout/src/main/java/com/itextpdf/layout/renderer/LineRenderer.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,7 @@ && hasProperty(Property.LINE_HEIGHT)) {
387387
&& childResult.getStatus() != LayoutResult.NOTHING) {
388388
if (RenderingMode.HTML_MODE.equals(childRenderer.<RenderingMode>getProperty(Property.RENDERING_MODE))
389389
&& childRenderer instanceof TextRenderer) {
390-
float[] ascenderDescender = LineHeightHelper
391-
.getActualAscenderDescender((AbstractRenderer) childRenderer);
390+
float[] ascenderDescender = LineHeightHelper.getActualAscenderDescender((TextRenderer) childRenderer);
392391
childAscent = ascenderDescender[0];
393392
childDescent = ascenderDescender[1];
394393
} else {

layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ This file is part of the iText (R) project.
5656
import com.itextpdf.kernel.colors.Color;
5757
import com.itextpdf.kernel.font.PdfFont;
5858
import com.itextpdf.kernel.font.PdfType0Font;
59+
import com.itextpdf.kernel.font.PdfType1Font;
5960
import com.itextpdf.kernel.geom.Rectangle;
6061
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
6162
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
@@ -105,9 +106,9 @@ This file is part of the iText (R) project.
105106
public class TextRenderer extends AbstractRenderer implements ILeafElementRenderer {
106107

107108
protected static final float TEXT_SPACE_COEFF = FontProgram.UNITS_NORMALIZATION;
109+
static final float TYPO_ASCENDER_SCALE_COEFF = 1.2f;
108110
private static final float ITALIC_ANGLE = 0.21256f;
109111
private static final float BOLD_SIMULATION_STROKE_COEFF = 1 / 30f;
110-
private static final float TYPO_ASCENDER_SCALE_COEFF = 1.2f;
111112

112113
protected float yLineOffset;
113114

@@ -227,17 +228,14 @@ public LayoutResult layout(LayoutContext layoutContext) {
227228
float currentLineWidth = 0;
228229
int previousCharPos = -1;
229230

230-
if(RenderingMode.HTML_MODE.equals(this.<RenderingMode>getProperty(Property.RENDERING_MODE))) {
231-
float[] ascenderDescender = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
232-
ascender = ascenderDescender[0];
233-
descender = ascenderDescender[1];
231+
RenderingMode mode = this.<RenderingMode>getProperty(Property.RENDERING_MODE);
232+
float[] ascenderDescender = calculateAscenderDescender(font, mode);
233+
ascender = ascenderDescender[0];
234+
descender = ascenderDescender[1];
235+
if (RenderingMode.HTML_MODE.equals(mode)) {
234236
currentLineAscender = ascenderDescender[0];
235237
currentLineDescender = ascenderDescender[1];
236238
currentLineHeight = (currentLineAscender - currentLineDescender) * fontSize.getValue() / TEXT_SPACE_COEFF + textRise;
237-
} else {
238-
float[] ascenderDescender = calculateAscenderDescender(font);
239-
ascender = ascenderDescender[0];
240-
descender = ascenderDescender[1];
241239
}
242240

243241
savedWordBreakAtLineEnding = null;
@@ -1070,13 +1068,21 @@ TextRenderer removeReversedRanges() {
10701068
}
10711069

10721070
static float[] calculateAscenderDescender(PdfFont font) {
1071+
return calculateAscenderDescender(font, RenderingMode.DEFAULT_LAYOUT_MODE);
1072+
}
1073+
1074+
static float[] calculateAscenderDescender(PdfFont font, RenderingMode mode) {
10731075
FontMetrics fontMetrics = font.getFontProgram().getFontMetrics();
10741076
float ascender;
10751077
float descender;
1078+
float usedTypoAscenderScaleCoeff = TYPO_ASCENDER_SCALE_COEFF;
1079+
if (RenderingMode.HTML_MODE.equals(mode) && !(font instanceof PdfType1Font)) {
1080+
usedTypoAscenderScaleCoeff = 1;
1081+
}
10761082
if (fontMetrics.getWinAscender() == 0 || fontMetrics.getWinDescender() == 0 ||
10771083
fontMetrics.getTypoAscender() == fontMetrics.getWinAscender() && fontMetrics.getTypoDescender() == fontMetrics.getWinDescender()) {
1078-
ascender = fontMetrics.getTypoAscender() * TYPO_ASCENDER_SCALE_COEFF;
1079-
descender = fontMetrics.getTypoDescender() * TYPO_ASCENDER_SCALE_COEFF;
1084+
ascender = fontMetrics.getTypoAscender() * usedTypoAscenderScaleCoeff;
1085+
descender = fontMetrics.getTypoDescender() * usedTypoAscenderScaleCoeff;
10801086
} else {
10811087
ascender = fontMetrics.getWinAscender();
10821088
descender = fontMetrics.getWinDescender();

layout/src/test/java/com/itextpdf/layout/renderer/LineHeightHelperUnitTest.java

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,13 @@ This file is part of the iText (R) project.
3030
import com.itextpdf.kernel.pdf.PdfWriter;
3131
import com.itextpdf.layout.Document;
3232
import com.itextpdf.layout.element.Text;
33-
import com.itextpdf.layout.font.FontProvider;
3433
import com.itextpdf.layout.property.LineHeight;
3534
import com.itextpdf.layout.property.Property;
3635
import com.itextpdf.layout.property.RenderingMode;
3736
import com.itextpdf.test.ExtendedITextTest;
3837
import com.itextpdf.test.annotations.type.UnitTest;
3938

4039
import java.io.IOException;
41-
4240
import org.junit.Assert;
4341
import org.junit.Test;
4442
import org.junit.experimental.categories.Category;
@@ -52,26 +50,26 @@ public class LineHeightHelperUnitTest extends ExtendedITextTest {
5250

5351
@Test
5452
public void calculateFontAscenderDescenderFromFontMetricsCourierTest() throws IOException {
55-
float[] fontAscenderDescender = LineHeightHelper
56-
.calculateFontAscenderDescenderFromFontMetrics(PdfFontFactory.createFont(StandardFonts.COURIER));
57-
Assert.assertEquals(629.0f, fontAscenderDescender[0], EPS);
58-
Assert.assertEquals(-157.0f, fontAscenderDescender[1], EPS);
53+
PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER);
54+
float[] fontAscenderDescender = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
55+
Assert.assertEquals(629.0f * TextRenderer.TYPO_ASCENDER_SCALE_COEFF, fontAscenderDescender[0], EPS);
56+
Assert.assertEquals(-157.0f * TextRenderer.TYPO_ASCENDER_SCALE_COEFF, fontAscenderDescender[1], EPS);
5957
}
6058

6159
@Test
6260
public void calculateFontAscenderDescenderFromFontMetricsTimesTest() throws IOException {
63-
float[] fontAscenderDescender = LineHeightHelper
64-
.calculateFontAscenderDescenderFromFontMetrics(PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN));
65-
Assert.assertEquals(683.0f, fontAscenderDescender[0], EPS);
66-
Assert.assertEquals(-217.0f, fontAscenderDescender[1], EPS);
61+
PdfFont font = PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN);
62+
float[] fontAscenderDescender = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
63+
Assert.assertEquals(683.0f * TextRenderer.TYPO_ASCENDER_SCALE_COEFF, fontAscenderDescender[0], EPS);
64+
Assert.assertEquals(-217.0f * TextRenderer.TYPO_ASCENDER_SCALE_COEFF, fontAscenderDescender[1], EPS);
6765
}
6866

6967
@Test
7068
public void calculateFontAscenderDescenderFromFontMetricsHelveticaTest() throws IOException {
71-
float[] fontAscenderDescender = LineHeightHelper
72-
.calculateFontAscenderDescenderFromFontMetrics(PdfFontFactory.createFont(StandardFonts.HELVETICA));
73-
Assert.assertEquals(718.0f, fontAscenderDescender[0], EPS);
74-
Assert.assertEquals(-207.0f, fontAscenderDescender[1], EPS);
69+
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA);
70+
float[] fontAscenderDescender = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
71+
Assert.assertEquals(718.0f * TextRenderer.TYPO_ASCENDER_SCALE_COEFF, fontAscenderDescender[0], EPS);
72+
Assert.assertEquals(-207.0f * TextRenderer.TYPO_ASCENDER_SCALE_COEFF, fontAscenderDescender[1], EPS);
7573
}
7674

7775
@Test
@@ -80,8 +78,8 @@ public void getFontAscenderDescenderNormalizedTextRendererTest() {
8078
TextRenderer textRenderer = new TextRenderer(new Text("Hello"));
8179
textRenderer.setParent(document.getRenderer());
8280
float[] ascenderDescender = LineHeightHelper.getFontAscenderDescenderNormalized(textRenderer);
83-
Assert.assertEquals(8.61600f, ascenderDescender[0], EPS);
84-
Assert.assertEquals(-2.483999f, ascenderDescender[1], EPS);
81+
Assert.assertEquals(10.33920f, ascenderDescender[0], EPS);
82+
Assert.assertEquals(-2.9808f, ascenderDescender[1], EPS);
8583
}
8684

8785
@Test
@@ -172,70 +170,70 @@ public void getActualAscenderDescenderTextRenderer() {
172170
TextRenderer textRenderer = new TextRenderer(new Text("Hello"));
173171
textRenderer.setParent(document.getRenderer());
174172
float[] ascenderDescender = LineHeightHelper.getActualAscenderDescender(textRenderer);
175-
Assert.assertEquals(9.96599f, ascenderDescender[0], EPS);
176-
Assert.assertEquals(-3.83399f, ascenderDescender[1], EPS);
173+
Assert.assertEquals(10.57919f, ascenderDescender[0], EPS);
174+
Assert.assertEquals(-3.22079f, ascenderDescender[1], EPS);
177175
}
178176

179177
@Test
180178
public void calculateFontAscenderDescenderFromFontMetricsNotoEmojiFontTest() throws IOException {
181179
PdfFont font = PdfFontFactory.createFont(FONTS + "NotoEmoji-Regular.ttf");
182-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
180+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
183181
Assert.assertEquals(1068.0f, ascenderDescenderFromFontMetrics[0], EPS);
184182
Assert.assertEquals(-292.0f, ascenderDescenderFromFontMetrics[1], EPS);
185183
}
186184

187185
@Test
188186
public void calculateFontAscenderDescenderFromFontMetricsNotoSansFontTest() throws IOException {
189187
PdfFont font = PdfFontFactory.createFont(FONTS + "NotoSans-Regular.ttf");
190-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
188+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
191189
Assert.assertEquals(1068.0f, ascenderDescenderFromFontMetrics[0], EPS);
192190
Assert.assertEquals(-292.0f, ascenderDescenderFromFontMetrics[1], EPS);
193191
}
194192

195193
@Test
196194
public void calculateFontAscenderDescenderFromFontMetricsNotoColorEmojiFontTest() throws IOException {
197195
PdfFont font = PdfFontFactory.createFont(FONTS + "NotoColorEmoji.ttf");
198-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
196+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
199197
System.out.println(ascenderDescenderFromFontMetrics[0]);
200198
System.out.println(ascenderDescenderFromFontMetrics[1]);
201199
}
202200

203201
@Test
204202
public void calculateFontAscenderDescenderFromFontMetricsNotoSansCJKscRegularFontTest() throws IOException {
205203
PdfFont font = PdfFontFactory.createFont(FONTS + "NotoSansCJKsc-Regular.otf");
206-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
204+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
207205
Assert.assertEquals(1160.0f, ascenderDescenderFromFontMetrics[0], EPS);
208206
Assert.assertEquals(-320.0f, ascenderDescenderFromFontMetrics[1], EPS);
209207
}
210208

211209
@Test
212210
public void calculateFontAscenderDescenderFromFontMetricsPuritan2FontTest() throws IOException {
213211
PdfFont font = PdfFontFactory.createFont(FONTS + "Puritan2.otf");
214-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
212+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
215213
Assert.assertEquals(860.0f, ascenderDescenderFromFontMetrics[0], EPS);
216214
Assert.assertEquals(-232.0f, ascenderDescenderFromFontMetrics[1], EPS);
217215
}
218216

219217
@Test
220218
public void calculateFontAscenderDescenderFromFontMetricsNotoSansCJKjpBoldFontTest() throws IOException {
221219
PdfFont font = PdfFontFactory.createFont(FONTS + "NotoSansCJKjp-Bold.otf");
222-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
220+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
223221
Assert.assertEquals(1160.0f, ascenderDescenderFromFontMetrics[0], EPS);
224222
Assert.assertEquals(-320.0f, ascenderDescenderFromFontMetrics[1], EPS);
225223
}
226224

227225
@Test
228226
public void calculateFontAscenderDescenderFromFontMetricsFreeSansFontTest() throws IOException {
229227
PdfFont font = PdfFontFactory.createFont(FONTS + "FreeSans.ttf");
230-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
228+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
231229
Assert.assertEquals(800.0f, ascenderDescenderFromFontMetrics[0], EPS);
232230
Assert.assertEquals(-200.0f, ascenderDescenderFromFontMetrics[1], EPS);
233231
}
234232

235233
@Test
236234
public void calculateFontAscenderDescenderFromFontMetricsOpenSansRegularFontTest() throws IOException {
237235
PdfFont font = PdfFontFactory.createFont(OPEN_SANS_FONTS + "OpenSans-Regular.ttf");
238-
float[] ascenderDescenderFromFontMetrics = LineHeightHelper.calculateFontAscenderDescenderFromFontMetrics(font);
236+
float[] ascenderDescenderFromFontMetrics = TextRenderer.calculateAscenderDescender(font, RenderingMode.HTML_MODE);
239237
Assert.assertEquals(1068.0f, ascenderDescenderFromFontMetrics[0], EPS);
240238
Assert.assertEquals(-292.0f, ascenderDescenderFromFontMetrics[1], EPS);
241239
}

layout/src/test/java/com/itextpdf/layout/renderer/TextRendererPositioningTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,15 @@ This file is part of the iText (R) project.
3333
import com.itextpdf.layout.property.Property;
3434
import com.itextpdf.layout.property.UnitValue;
3535
import com.itextpdf.test.ExtendedITextTest;
36+
import com.itextpdf.test.annotations.type.IntegrationTest;
3637

3738
import java.io.IOException;
3839
import org.junit.Assert;
3940
import org.junit.BeforeClass;
4041
import org.junit.Test;
42+
import org.junit.experimental.categories.Category;
4143

44+
@Category(IntegrationTest.class)
4245
public class TextRendererPositioningTest extends ExtendedITextTest {
4346
public static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/layout/TextRendererPositioningTest/";
4447
public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/layout/TextRendererPositioningTest/";

0 commit comments

Comments
 (0)