From d6b5ab6517d1712b7e5c4df3e58985cb661ff2ec Mon Sep 17 00:00:00 2001 From: Maxim Date: Wed, 17 Jun 2026 12:24:29 +0300 Subject: [PATCH] Use FontMatrix of Type3 font during text chunks parsing Co-authored-by: Augusto Barbosa Villar Silva <64085472+augustovillar@users.noreply.github.com> --- .../gf/model/factory/chunks/ChunkParser.java | 17 +++--- .../factory/chunks/TextChunksHelper.java | 56 +++++++++++++------ 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/ChunkParser.java b/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/ChunkParser.java index e98a4f492..882a1e69e 100644 --- a/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/ChunkParser.java +++ b/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/ChunkParser.java @@ -827,18 +827,18 @@ private void putChunk(Long mcid, IChunk chunk) { } } - private TextPieces parseTextShowArgument(COSBase argument) { + private TextPieces parseTextShowArgument(COSBase argument, double horizontalScalingFactor) { TextPieces textPieces = new TextPieces(); if (argument.getType() == COSObjType.COS_STRING) { - parseString((COSString) argument.getDirectBase(), textPieces); + parseString((COSString) argument.getDirectBase(), textPieces, horizontalScalingFactor); } else if (argument.getType() == COSObjType.COS_ARRAY) { COSArray array = (COSArray) argument; for (COSObject obj : array) { if (obj != null) { if (obj.getType() == COSObjType.COS_STRING) { - parseString((COSString) obj.getDirectBase(), textPieces); + parseString((COSString) obj.getDirectBase(), textPieces, horizontalScalingFactor); } else if (obj.getType().isNumber()) { - double shift = - obj.getReal() / 1000 * + double shift = - obj.getReal() * horizontalScalingFactor * graphicsState.getTextState().getTextFontSize() * graphicsState.getTextState().getHorizontalScaling(); textPieces.shiftCurrentX(shift); @@ -853,7 +853,7 @@ private TextPieces parseTextShowArgument(COSBase argument) { return textPieces; } - private void parseString(COSString string, TextPieces textPieces) { + private void parseString(COSString string, TextPieces textPieces, double horizontalScalingFactor) { byte[] bytes = string.get(); try (InputStream inputStream = new ByteArrayInputStream(bytes)) { while (inputStream.available() > 0) { @@ -868,7 +868,7 @@ private void parseString(COSString string, TextPieces textPieces) { graphicsState.getTextState().getWordSpacing() : 0)) * graphicsState.getTextState().getHorizontalScaling(); width = width * - graphicsState.getTextState().getTextFontSize() / 1000 * + graphicsState.getTextState().getTextFontSize() * horizontalScalingFactor * graphicsState.getTextState().getHorizontalScaling(); String value = graphicsState.getTextState().getTextFont().toUnicode(code); String result = value; @@ -892,7 +892,8 @@ private TextChunk createTextChunk(List arguments, String operatorType, COSBase argument = TextChunksHelper.getArgument(arguments, operatorType); if (font != null && argument != null && (argument.getType() == COSObjType.COS_STRING || argument.getType() == COSObjType.COS_ARRAY) && this.textMatrix != null) { - TextPieces textPieces = parseTextShowArgument(argument); + double horizontalScalingFactor = TextChunksHelper.getHorizontalScalingFactor(font); + TextPieces textPieces = parseTextShowArgument(argument, horizontalScalingFactor); if (textPieces.isEmpty()) { textMatrix.concatenate(Matrix.getTranslateInstance(textPieces.getCurrentX(), 0)); return null; @@ -905,7 +906,7 @@ private TextChunk createTextChunk(List arguments, String operatorType, PDFontDescriptor descriptor = font.getFontDescriptor(); String fontNameWithoutSubset = font.getNameWithoutSubset(); TextChunk textChunk = new TextChunk(TextChunksHelper.calculateTextBoundingBox(textRenderingMatrixBefore, - textRenderingMatrixAfter, font, pageNumber), textPieces.getValue(), + textRenderingMatrixAfter, font, pageNumber, horizontalScalingFactor), textPieces.getValue(), fontNameWithoutSubset, TextChunksHelper.calculateTextSize(textRenderingMatrixAfter), TextChunksHelper.calculateFontWeight(graphicsState.getTextState().getRenderingMode(), font), descriptor.getItalicAngle(), TextChunksHelper.calculateTextBaseLine(textRenderingMatrixAfter), diff --git a/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/TextChunksHelper.java b/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/TextChunksHelper.java index 8692bda02..f3eaae5aa 100644 --- a/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/TextChunksHelper.java +++ b/wcag-validation/src/main/java/org/verapdf/gf/model/factory/chunks/TextChunksHelper.java @@ -23,6 +23,7 @@ import org.verapdf.cos.COSBase; import org.verapdf.model.tools.constants.Operators; import org.verapdf.pd.font.PDFont; +import org.verapdf.pd.font.type3.PDType3Font; import org.verapdf.wcag.algorithms.entities.geometry.BoundingBox; import org.verapdf.wcag.algorithms.semanticalgorithms.utils.NodeUtils; @@ -45,7 +46,7 @@ protected static COSBase getArgument(List arguments, String operatorTyp } protected static BoundingBox calculateTextBoundingBox(Matrix textRenderingMatrixBefore, Matrix textRenderingMatrixAfter, - PDFont font, Integer pageNumber) { + PDFont font, Integer pageNumber, double horizontalScalingFactor) { double[] fontBoundingBox = font.getBoundingBox(); Double descent = font.getDescent(); if (descent == null) { @@ -55,38 +56,59 @@ protected static BoundingBox calculateTextBoundingBox(Matrix textRenderingMatrix if (ascent == null) { ascent = fontBoundingBox[3]; } + double verticalScalingFactor = TextChunksHelper.getVerticalScalingFactor(font); double x1; double x2; if (textRenderingMatrixBefore.getScaleX() >= 0 && textRenderingMatrixBefore.getShearX() >= 0) { - x1 = textRenderingMatrixBefore.getTranslateX() + descent * textRenderingMatrixBefore.getShearX() / 1000; - x2 = textRenderingMatrixAfter.getTranslateX() + ascent * textRenderingMatrixAfter.getShearX() / 1000; + x1 = textRenderingMatrixBefore.getTranslateX() + descent * textRenderingMatrixBefore.getShearX() * horizontalScalingFactor; + x2 = textRenderingMatrixAfter.getTranslateX() + ascent * textRenderingMatrixAfter.getShearX() * horizontalScalingFactor; } else if (textRenderingMatrixBefore.getScaleX() < 0 && textRenderingMatrixBefore.getShearX() < 0) { - x1 = textRenderingMatrixAfter.getTranslateX() + ascent * textRenderingMatrixAfter.getShearX() / 1000; - x2 = textRenderingMatrixBefore.getTranslateX() + descent * textRenderingMatrixBefore.getShearX() / 1000; + x1 = textRenderingMatrixAfter.getTranslateX() + ascent * textRenderingMatrixAfter.getShearX() * horizontalScalingFactor; + x2 = textRenderingMatrixBefore.getTranslateX() + descent * textRenderingMatrixBefore.getShearX() * horizontalScalingFactor; } else if (textRenderingMatrixBefore.getScaleX() >= 0) { - x1 = textRenderingMatrixBefore.getTranslateX() + ascent * textRenderingMatrixBefore.getShearX() / 1000; - x2 = textRenderingMatrixAfter.getTranslateX() + descent * textRenderingMatrixAfter.getShearX() / 1000; + x1 = textRenderingMatrixBefore.getTranslateX() + ascent * textRenderingMatrixBefore.getShearX() * horizontalScalingFactor; + x2 = textRenderingMatrixAfter.getTranslateX() + descent * textRenderingMatrixAfter.getShearX() * horizontalScalingFactor; } else { - x1 = textRenderingMatrixAfter.getTranslateX() + descent * textRenderingMatrixAfter.getShearX() / 1000; - x2 = textRenderingMatrixBefore.getTranslateX() + ascent * textRenderingMatrixBefore.getShearX() / 1000; + x1 = textRenderingMatrixAfter.getTranslateX() + descent * textRenderingMatrixAfter.getShearX() * horizontalScalingFactor; + x2 = textRenderingMatrixBefore.getTranslateX() + ascent * textRenderingMatrixBefore.getShearX() * horizontalScalingFactor; } double y1; double y2; if (textRenderingMatrixBefore.getScaleY() >= 0 && textRenderingMatrixBefore.getShearY() >= 0) { - y1 = textRenderingMatrixBefore.getTranslateY() + descent * textRenderingMatrixBefore.getScaleY() / 1000; - y2 = textRenderingMatrixAfter.getTranslateY() + ascent * textRenderingMatrixAfter.getScaleY() / 1000; + y1 = textRenderingMatrixBefore.getTranslateY() + descent * textRenderingMatrixBefore.getScaleY() * verticalScalingFactor; + y2 = textRenderingMatrixAfter.getTranslateY() + ascent * textRenderingMatrixAfter.getScaleY() * verticalScalingFactor; } else if (textRenderingMatrixBefore.getScaleY() < 0 && textRenderingMatrixBefore.getShearY() < 0) { - y1 = textRenderingMatrixAfter.getTranslateY() + ascent * textRenderingMatrixAfter.getScaleY() / 1000; - y2 = textRenderingMatrixBefore.getTranslateY() + descent * textRenderingMatrixBefore.getScaleY() / 1000; + y1 = textRenderingMatrixAfter.getTranslateY() + ascent * textRenderingMatrixAfter.getScaleY() * verticalScalingFactor; + y2 = textRenderingMatrixBefore.getTranslateY() + descent * textRenderingMatrixBefore.getScaleY() * verticalScalingFactor; } else if (textRenderingMatrixBefore.getScaleY() >= 0) { - y1 = textRenderingMatrixAfter.getTranslateY() + descent * textRenderingMatrixAfter.getScaleY() / 1000; - y2 = textRenderingMatrixBefore.getTranslateY() + ascent * textRenderingMatrixBefore.getScaleY() / 1000; + y1 = textRenderingMatrixAfter.getTranslateY() + descent * textRenderingMatrixAfter.getScaleY() * verticalScalingFactor; + y2 = textRenderingMatrixBefore.getTranslateY() + ascent * textRenderingMatrixBefore.getScaleY() * verticalScalingFactor; } else { - y1 = textRenderingMatrixBefore.getTranslateY() + ascent * textRenderingMatrixBefore.getScaleY() / 1000; - y2 = textRenderingMatrixAfter.getTranslateY() + descent * textRenderingMatrixAfter.getScaleY() / 1000; + y1 = textRenderingMatrixBefore.getTranslateY() + ascent * textRenderingMatrixBefore.getScaleY() * verticalScalingFactor; + y2 = textRenderingMatrixAfter.getTranslateY() + descent * textRenderingMatrixAfter.getScaleY() * verticalScalingFactor; } return new BoundingBox(pageNumber, x1, y1, x2, y2); } + + public static double getHorizontalScalingFactor(PDFont font) { + if (font instanceof PDType3Font) { + double[] fontMatrix = ((PDType3Font) font).getFontMatrix(); + if (fontMatrix != null && fontMatrix.length > 0 && fontMatrix[0] != 0.0) { + return fontMatrix[0]; + } + } + return 1.0 / 1000.0; + } + + public static double getVerticalScalingFactor(PDFont font) { + if (font instanceof PDType3Font) { + double[] fontMatrix = ((PDType3Font) font).getFontMatrix(); + if (fontMatrix != null && fontMatrix.length > 3 && fontMatrix[3] != 0.0) { + return fontMatrix[3]; + } + } + return 1.0 / 1000.0; + } protected static double calculateTextBaseLine(Matrix textMatrix) { double rotationDegree = textMatrix.getRotationDegree();