]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2985: Reset character spacing by Dave Roxburgh
authorSimon Steiner <ssteiner@apache.org>
Thu, 20 Jul 2023 11:52:09 +0000 (12:52 +0100)
committerSimon Steiner <ssteiner@apache.org>
Thu, 20 Jul 2023 11:52:09 +0000 (12:52 +0100)
fop-core/src/main/java/org/apache/fop/afp/ptoca/PtocaBuilder.java
fop-core/src/main/java/org/apache/fop/render/afp/AFPPainter.java
fop-core/src/main/java/org/apache/fop/render/pdf/PDFContentGenerator.java
fop-core/src/main/java/org/apache/fop/render/pdf/PDFPainter.java
fop-core/src/test/java/org/apache/fop/render/afp/AFPPainterTestCase.java
fop-core/src/test/java/org/apache/fop/render/pdf/PDFPainterTestCase.java

index 084ba5daad75b76f2a52bc64d334a7190b79f5de..63026ee84e22b8ccee9dbeb90978e2536f275853 100644 (file)
@@ -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.
+     * <p>
+     * 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
index 9a4a7b93e3245940daeb9b3bd65db38d4bba8eae..66ada9facb1f0cd657688a6331e60b8b4b5307a3 100644 (file)
@@ -1102,6 +1102,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
                     }
                 }
             }
+            builder.resetInterCharacterAdjustment();
             flushText(builder, sb, charSet);
             if (pto != null) {
                 bytesAvailable = pto.getBytesAvailable();
index 668734bdebc570f87e0f82e85f4115ddc93ed718..0896ae8c304b9ea70113b23541f3fd421b64205c 100644 (file)
@@ -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
index fc137c330bdb2857e0b3bb5ce1c5b61d3463ec57..cebd083a8de562c570e9d5c0e55b5e915d2e4632 100644 (file)
@@ -526,6 +526,7 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
 
         }
         textutil.writeTJ();
+        generator.resetCharacterSpacing();
         endSimulateStyle(tf, triplet);
     }
 
@@ -599,6 +600,7 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
                 xoLast = xo;
                 yoLast = yo;
             }
+            generator.resetCharacterSpacing();
             endSimulateStyle(tf, triplet);
         }
     }
index 3add2a49550bdb7d0343a10190bd034519329242..b396d6d67d2ab09f45338c5fdae2135eec6ba5db 100644 (file)
@@ -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<String> strings = new ArrayList<String>();
         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<String> strings = new ArrayList<String>();
         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<String> 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<String> 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<String> 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<String> text) throws Exception {
+        return getDocResultInputStream(text, 0);
+    }
+
+    private static InputStream getDocResultInputStream(List<String> 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
index 7b5d34bdcc2dcf95cbdc84597043f090cd76de35..c2d9a325ba672975ce667375f8ac590e69026ac7 100644 (file)
@@ -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());