From 05f8a2c9d2084b29b7ddf49a25d6a8e028243b7c Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Thu, 20 Jul 2023 12:52:09 +0100 Subject: [PATCH] FOP-2985: Reset character spacing by Dave Roxburgh --- .../apache/fop/afp/ptoca/PtocaBuilder.java | 20 ++++++++ .../org/apache/fop/render/afp/AFPPainter.java | 1 + .../fop/render/pdf/PDFContentGenerator.java | 9 ++++ .../org/apache/fop/render/pdf/PDFPainter.java | 2 + .../fop/render/afp/AFPPainterTestCase.java | 46 +++++++++++++++---- .../fop/render/pdf/PDFPainterTestCase.java | 26 +++++++++++ 6 files changed, 95 insertions(+), 9 deletions(-) diff --git a/fop-core/src/main/java/org/apache/fop/afp/ptoca/PtocaBuilder.java b/fop-core/src/main/java/org/apache/fop/afp/ptoca/PtocaBuilder.java index 084ba5daa..63026ee84 100644 --- a/fop-core/src/main/java/org/apache/fop/afp/ptoca/PtocaBuilder.java +++ b/fop-core/src/main/java/org/apache/fop/afp/ptoca/PtocaBuilder.java @@ -360,6 +360,26 @@ public abstract class PtocaBuilder implements PtocaConstants { this.currentInterCharacterAdjustment = incr; } + /** + * Resets the intercharacter adjustment (additional increment or decrement between graphic + * characters) to 0. + *

+ * This is a modal control sequence. + * + * @throws IOException if an I/O error occurs + */ + public void resetInterCharacterAdjustment() throws IOException { + if (0 == this.currentInterCharacterAdjustment) { + return; + } + newControlSequence(); + writeShort(0); //Increment + writeBytes(0); // Direction + commit(chained(SIA)); + + this.currentInterCharacterAdjustment = 0; + } + /** * A control sequence is a sequence of bytes that specifies a control * function. A control sequence consists of a control sequence introducer diff --git a/fop-core/src/main/java/org/apache/fop/render/afp/AFPPainter.java b/fop-core/src/main/java/org/apache/fop/render/afp/AFPPainter.java index 9a4a7b93e..66ada9fac 100644 --- a/fop-core/src/main/java/org/apache/fop/render/afp/AFPPainter.java +++ b/fop-core/src/main/java/org/apache/fop/render/afp/AFPPainter.java @@ -1102,6 +1102,7 @@ public class AFPPainter extends AbstractIFPainter { } } } + builder.resetInterCharacterAdjustment(); flushText(builder, sb, charSet); if (pto != null) { bytesAvailable = pto.getBytesAvailable(); diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFContentGenerator.java index 668734bde..0896ae8c3 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFContentGenerator.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@ -423,6 +423,15 @@ public class PDFContentGenerator { } } + /** + * Resets the current character spacing (Tc) value to zero. + */ + public void resetCharacterSpacing() { + if (getState().setCharacterSpacing(0)) { + getStream().add(format(0) + " Tc\n"); + } + } + /** * Establishes a new foreground or fill color. * @param col the color to apply diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java index fc137c330..cebd083a8 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java @@ -526,6 +526,7 @@ public class PDFPainter extends AbstractIFPainter { } textutil.writeTJ(); + generator.resetCharacterSpacing(); endSimulateStyle(tf, triplet); } @@ -599,6 +600,7 @@ public class PDFPainter extends AbstractIFPainter { xoLast = xo; yoLast = yo; } + generator.resetCharacterSpacing(); endSimulateStyle(tf, triplet); } } diff --git a/fop-core/src/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java b/fop-core/src/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java index 3add2a495..b396d6d67 100644 --- a/fop-core/src/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java @@ -28,7 +28,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -45,6 +44,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.apache.commons.io.IOUtils; + import org.apache.xmlgraphics.image.loader.Image; import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageFlavor; @@ -155,7 +156,7 @@ public class AFPPainterTestCase { } @Test - public void testPresentationText() throws URISyntaxException, IFException, IOException { + public void testPresentationText() throws Exception { List strings = new ArrayList(); strings.add("test"); Assert.assertEquals(writeText(strings), "BEGIN DOCUMENT DOC00001\n" @@ -190,7 +191,7 @@ public class AFPPainterTestCase { } @Test - public void testPresentationText2() throws URISyntaxException, IFException, IOException { + public void testPresentationText2() throws Exception { List strings = new ArrayList(); for (int i = 0; i < 5000; i++) { strings.add("tes"); @@ -211,7 +212,37 @@ public class AFPPainterTestCase { + "END DOCUMENT DOC00001\n"); } - private String writeText(List text) throws URISyntaxException, IOException, IFException { + /** + * Checks that letter spacing is reset to 0 after the relevant text block. + */ + @Test + public void testLetterSpacingReset() throws Exception { + List strings = new ArrayList<>(); + strings.add("xxxx"); + InputStream inputStream = getDocResultInputStream(strings, 10000); + byte[] bytes = IOUtils.toByteArray(inputStream); + // The 134th byte is incremented by 5 to account for the 5 extra bytes inserted for the reset. + Assert.assertEquals((byte)39, bytes[134]); + // And these are the 5 reset bytes. + Assert.assertEquals((byte)5, bytes[163]); + Assert.assertEquals((byte)195, bytes[164]); + Assert.assertEquals((byte)0, bytes[165]); + Assert.assertEquals((byte)0, bytes[166]); + Assert.assertEquals((byte)0, bytes[167]); + } + + private String writeText(List text) throws Exception { + InputStream bis = getDocResultInputStream(text); + StringBuilder sb = new StringBuilder(); + new AFPParser(false).read(bis, sb); + return sb.toString(); + } + + private static InputStream getDocResultInputStream(List text) throws Exception { + return getDocResultInputStream(text, 0); + } + + private static InputStream getDocResultInputStream(List text, int letterSpacing) throws Exception { FOUserAgent agent = FopFactory.newInstance(new URI(".")).newFOUserAgent(); IFContext context = new IFContext(agent); AFPDocumentHandler doc = new AFPDocumentHandler(context); @@ -232,14 +263,11 @@ public class AFPPainterTestCase { doc.startDocument(); doc.startPage(0, "", "", new Dimension()); for (String s : text) { - afpPainter.drawText(0, 0, 0, 0, null, s); + afpPainter.drawText(0, 0, letterSpacing, 0, null, s); } doc.endDocument(); - InputStream bis = new ByteArrayInputStream(outputStream.toByteArray()); - StringBuilder sb = new StringBuilder(); - new AFPParser(false).read(bis, sb); - return sb.toString(); + return new ByteArrayInputStream(outputStream.toByteArray()); } @Test diff --git a/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java b/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java index 7b5d34bdc..c2d9a325b 100644 --- a/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java @@ -184,6 +184,32 @@ public class PDFPainterTestCase { verify(pdfContentGenerator).add("0 Tr\n"); } + /** + * Tests that letter spacing is set and reset appropriately. + * @throws IFException + */ + @Test + public void testLetterSpacing() throws IFException { + final StringBuilder sb = new StringBuilder(); + pdfDocumentHandler = makePDFDocumentHandler(sb); + + FontInfo fi = new FontInfo(); + fi.addFontProperties("f1", new FontTriplet("a", "normal", 400)); + MultiByteFont font = new MultiByteFont(null, null); + fi.addMetrics("f1", font); + pdfDocumentHandler.setFontInfo(fi); + MyPDFPainter pdfPainter = new MyPDFPainter(pdfDocumentHandler, null); + pdfPainter.setFont("a", "normal", 400, null, 12, null); + pdfPainter.drawText(0, 0, 4321, 0, null, "test"); + + assertEquals("BT\n" + + "/f1 0.012 Tf\n" + + "1 0 0 -1 0 0 Tm [<0000000000000000>] TJ\n", + sb.toString()); + verify(pdfContentGenerator).updateCharacterSpacing(4.321f); + verify(pdfContentGenerator).resetCharacterSpacing(); + } + @Test public void testSimulateStyleColor() throws Exception { FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); -- 2.39.5