You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2012/12/26 16:36:46 UTC

svn commit: r1425954 - in /pdfbox/trunk: fontbox/src/main/java/org/apache/fontbox/cff/ fontbox/src/test/java/org/apache/fontbox/cff/ preflight/src/main/java/org/apache/pdfbox/preflight/font/util/

Author: lehmi
Date: Wed Dec 26 15:36:45 2012
New Revision: 1425954

URL: http://svn.apache.org/viewvc?rev=1425954&view=rev
Log:
PDFBOX-1473: fixed the handling of "callsubr" and "callgsubr" commands based on a proposal by Juraj Lonc

Modified:
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringConverter.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java
    pdfbox/trunk/fontbox/src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java
    pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/util/Type1Parser.java

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java?rev=1425954&r1=1425953&r2=1425954&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CFFFont.java Wed Dec 26 15:36:45 2012
@@ -248,12 +248,12 @@ public class CFFFont
 		byte[] glyphDesc = this.getCharStringsDict().get(".notdef");
 		if (((Number)getProperty("CharstringType")).intValue() == 2 ) {
 			Type2CharStringParser parser = new Type2CharStringParser();
-			List<Object> lSeq = parser.parse(glyphDesc);
+			List<Object> lSeq = parser.parse(glyphDesc, getGlobalSubrIndex(), getLocalSubrIndex());
 			csr = new CharStringRenderer(false);
 			csr.render(lSeq);
 		} else {
 			Type1CharStringParser parser = new Type1CharStringParser();
-			List<Object> lSeq = parser.parse(glyphDesc);
+			List<Object> lSeq = parser.parse(glyphDesc, getLocalSubrIndex());
 			csr = new CharStringRenderer();
 			csr.render(lSeq);
 		}
@@ -313,8 +313,7 @@ public class CFFFont
 	{
 		Number defaultWidthX = (Number) getProperty("defaultWidthX");
 		Number nominalWidthX = (Number) getProperty("nominalWidthX");
-		return new CharStringConverter(defaultWidthX.intValue(), nominalWidthX
-				.intValue(), getGlobalSubrIndex(), getLocalSubrIndex());
+		return new CharStringConverter(defaultWidthX.intValue(), nominalWidthX.intValue());
 	}
 
 	/**
@@ -400,7 +399,7 @@ public class CFFFont
 		public List<Object> toType2Sequence() throws IOException
 		{
 			Type2CharStringParser parser = new Type2CharStringParser();
-			return parser.parse(getBytes());
+			return parser.parse(getBytes(), getGlobalSubrIndex(), getLocalSubrIndex());
 		}
 
 		/**

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringConverter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringConverter.java?rev=1425954&r1=1425953&r2=1425954&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringConverter.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringConverter.java Wed Dec 26 15:36:45 2012
@@ -16,7 +16,6 @@
  */
 package org.apache.fontbox.cff;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -35,24 +34,34 @@ public class CharStringConverter extends
     private int nominalWidthX = 0;
     private List<Object> sequence = null;
     private int pathCount = 0;
-    private IndexData globalSubrIndex = null;
-    private IndexData localSubrIndex = null;
 
     /**
      * Constructor.
      * 
      * @param defaultWidth default width
      * @param nominalWidth nominal width
+     * 
+     * @deprecated Use {@link CharStringConverter#CharStringConverter(int, int)} instead
      */
     public CharStringConverter(int defaultWidth, int nominalWidth, IndexData fontGlobalSubrIndex, IndexData fontLocalSubrIndex)
     {
         defaultWidthX = defaultWidth;
         nominalWidthX = nominalWidth;
-        globalSubrIndex = fontGlobalSubrIndex;
-        localSubrIndex = fontLocalSubrIndex;
     }
 
     /**
+     * Constructor.
+     * 
+     * @param defaultWidth default width
+     * @param nominalWidth nominal width
+     * 
+     */
+    public CharStringConverter(int defaultWidth, int nominalWidth)
+    {
+        defaultWidthX = defaultWidth;
+        nominalWidthX = nominalWidth;
+    }
+    /**
      * Converts a sequence of Type1/Type2 commands into a sequence of CharStringCommands.
      * @param commandSequence the type1/type2 sequence
      * @return the CHarStringCommandSequence
@@ -145,44 +154,6 @@ public class CharStringConverter extends
         {
             drawAlternatingCurve(numbers, true);
         } 
-        else if ("callsubr".equals(name))
-        {
-            //get subrbias
-            int bias = 0;
-            int nSubrs = localSubrIndex.getCount();
-            
-            if (nSubrs < 1240)
-            {
-                bias = 107;
-            }
-            else if (nSubrs < 33900) 
-            {
-                bias = 1131;
-            }
-            else 
-            {
-                bias = 32768;
-            }
-           
-            List<Integer> result = null;
-            int subrNumber = bias+numbers.get(numbers.size()-1);
-            if (subrNumber < localSubrIndex.getCount())
-            {
-                Type2CharStringParser parser = new Type2CharStringParser();
-                byte[] bytes = localSubrIndex.getBytes(subrNumber);
-                List<Object> parsed = null;
-                try {
-                    parsed = parser.parse(bytes);
-                    parsed.addAll(0,numbers.subList(0, numbers.size()-1));
-                    result = handleSequence(parsed);
-                } 
-                catch (IOException e) 
-                {
-                    e.printStackTrace();
-                }
-            }
-            return result;
-        }
         else if ("return".equals(name))
         {
             return numbers;
@@ -276,42 +247,6 @@ public class CharStringConverter extends
         {
             drawCurve(numbers, true);
         } 
-        else if ("callgsubr".equals(name))
-        {
-            //get subrbias
-            int bias = 0;
-            int nSubrs = globalSubrIndex.getCount();
-            
-            if (nSubrs < 1240)
-            {
-                bias = 107;
-            }
-            else if (nSubrs < 33900)
-            {
-                bias = 1131;
-            }
-            else 
-            {
-                bias = 32768;
-            }
-            List<Integer> result = null;
-            int subrNumber = bias+numbers.get(numbers.size()-1);
-            if (subrNumber < nSubrs)
-            {
-                Type2CharStringParser parser = new Type2CharStringParser();
-                byte[] bytes = globalSubrIndex.getBytes(subrNumber);
-                List<Object> parsed = null;
-                try {
-                    parsed = parser.parse(bytes);
-                    parsed.addAll(0,numbers.subList(0, numbers.size()-1));
-                    result = handleSequence(parsed);
-                } catch (IOException e) 
-                {
-                    e.printStackTrace();
-                }
-            }
-            return result;
-        }
         else
         {
             addCommand(numbers, command);

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java?rev=1425954&r1=1425953&r2=1425954&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java Wed Dec 26 15:36:45 2012
@@ -34,18 +34,61 @@ public class Type1CharStringParser
     /**
      * The given byte array will be parsed and converted to a Type1 sequence.
      * @param bytes the given mapping as byte array
+     * @param localSubrIndex index containing all local subroutines
+     * 
      * @return the Type1 sequence
      * @throws IOException if an error occurs during reading
      */
-    public List<Object> parse(byte[] bytes) throws IOException
+    public List<Object> parse(byte[] bytes, IndexData localSubrIndex) throws IOException
     {
+        return parse(bytes, localSubrIndex, true);
+    }
+    
+    private List<Object> parse(byte[] bytes, IndexData localSubrIndex, boolean init) throws IOException
+    {
+        if (init) 
+        {
+            sequence = new ArrayList<Object>();
+        }
         input = new DataInput(bytes);
-        sequence = new ArrayList<Object>();
+        boolean localSubroutineIndexProvided = localSubrIndex != null && localSubrIndex.getCount() > 0;
         while (input.hasRemaining())
         {
             int b0 = input.readUnsignedByte();
 
-            if (b0 >= 0 && b0 <= 31)
+            if (b0 == 10 && localSubroutineIndexProvided) 
+            { // process subr command
+                Integer operand=(Integer)sequence.remove(sequence.size()-1);
+                //get subrbias
+                int bias = 0;
+                int nSubrs = localSubrIndex.getCount();
+                
+                if (nSubrs < 1240)
+                {
+                    bias = 107;
+                }
+                else if (nSubrs < 33900) 
+                {
+                    bias = 1131;
+                }
+                else 
+                {
+                    bias = 32768;
+                }
+                int subrNumber = bias+operand;
+                if (subrNumber < localSubrIndex.getCount())
+                {
+                    byte[] subrBytes = localSubrIndex.getBytes(subrNumber);
+                    parse(subrBytes, localSubrIndex, false);
+                    Object lastItem = sequence.get(sequence.size()-1);
+                    if (lastItem instanceof CharStringCommand && ((CharStringCommand)lastItem).getKey().getValue()[0] == 11)
+                    {
+                        sequence.remove(sequence.size()-1); // remove "return" command
+                    }
+                }
+                
+            } 
+            else if (b0 >= 0 && b0 <= 31)
             {
                 sequence.add(readCommand(b0));
             } 

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java?rev=1425954&r1=1425953&r2=1425954&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java Wed Dec 26 15:36:45 2012
@@ -28,7 +28,6 @@ import java.util.List;
 public class Type2CharStringParser
 {
 
-    private DataInput input = null;
     private int hstemCount = 0;
     private int vstemCount = 0;
     private List<Object> sequence = null;
@@ -36,48 +35,122 @@ public class Type2CharStringParser
     /**
      * The given byte array will be parsed and converted to a Type2 sequence.
      * @param bytes the given mapping as byte array
+     * @param globalSubrIndex index containing all global subroutines
+     * @param localSubrIndex index containing all local subroutines
+     * 
      * @return the Type2 sequence
      * @throws IOException if an error occurs during reading
      */
-    public List<Object> parse(byte[] bytes) throws IOException
+    public List<Object> parse(byte[] bytes, IndexData globalSubrIndex, IndexData localSubrIndex) throws IOException
     {
-        input = new DataInput(bytes);
-
-        hstemCount = 0;
-        vstemCount = 0;
-
-        sequence = new ArrayList<Object>();
+    	return parse(bytes, globalSubrIndex, localSubrIndex, true);
+    }
+    
+    private List<Object> parse(byte[] bytes, IndexData globalSubrIndex, IndexData localSubrIndex, boolean init) throws IOException
+    {
+        if (init) 
+        {
+	        hstemCount = 0;
+	        vstemCount = 0;
+	        sequence = new ArrayList<Object>();
+        }
+        DataInput input = new DataInput(bytes);
+        boolean localSubroutineIndexProvided = localSubrIndex != null && localSubrIndex.getCount() > 0;
+        boolean globalSubroutineIndexProvided = globalSubrIndex != null && globalSubrIndex.getCount() > 0;
 
         while (input.hasRemaining())
         {
             int b0 = input.readUnsignedByte();
-
-            if (b0 >= 0 && b0 <= 27)
+            if (b0 == 10 && localSubroutineIndexProvided) 
+            { // process subr command
+            	Integer operand=(Integer)sequence.remove(sequence.size()-1);
+            	//get subrbias
+                int bias = 0;
+                int nSubrs = localSubrIndex.getCount();
+                
+                if (nSubrs < 1240)
+                {
+                    bias = 107;
+                }
+                else if (nSubrs < 33900) 
+                {
+                    bias = 1131;
+                }
+                else 
+                {
+                    bias = 32768;
+                }
+                int subrNumber = bias+operand;
+                if (subrNumber < localSubrIndex.getCount())
+                {
+                    byte[] subrBytes = localSubrIndex.getBytes(subrNumber);
+                    parse(subrBytes, globalSubrIndex, localSubrIndex, false);
+                    Object lastItem=sequence.get(sequence.size()-1);
+                    if (lastItem instanceof CharStringCommand && ((CharStringCommand)lastItem).getKey().getValue()[0] == 11)
+                    {
+                        sequence.remove(sequence.size()-1); // remove "return" command
+                    }
+                }
+            	
+            } 
+            else if (b0 == 29 && globalSubroutineIndexProvided) 
+            { // process globalsubr command
+                Integer operand=(Integer)sequence.remove(sequence.size()-1);
+                //get subrbias
+                int bias = 0;
+                int nSubrs = globalSubrIndex.getCount();
+                
+                if (nSubrs < 1240)
+                {
+                    bias = 107;
+                }
+                else if (nSubrs < 33900) 
+                {
+                    bias = 1131;
+                }
+                else 
+                {
+                    bias = 32768;
+                }
+                
+                int subrNumber = bias+operand;
+                if (subrNumber < globalSubrIndex.getCount())
+                {
+                    byte[] subrBytes = globalSubrIndex.getBytes(subrNumber);
+                    parse(subrBytes, globalSubrIndex, localSubrIndex, false);
+                    Object lastItem=sequence.get(sequence.size()-1);
+                    if (lastItem instanceof CharStringCommand && ((CharStringCommand)lastItem).getKey().getValue()[0]==11) 
+                    {
+                        sequence.remove(sequence.size()-1); // remove "return" command
+                    }
+                }
+                	
+            } 
+            else if (b0 >= 0 && b0 <= 27)
             {
-                sequence.add(readCommand(b0));
+                sequence.add(readCommand(b0, input));
             } 
             else if (b0 == 28)
             {
-                sequence.add(readNumber(b0));
+                sequence.add(readNumber(b0, input));
             } 
             else if (b0 >= 29 && b0 <= 31)
             {
-                sequence.add(readCommand(b0));
+                sequence.add(readCommand(b0, input));
             } 
             else if (b0 >= 32 && b0 <= 255)
             {
-                sequence.add(readNumber(b0));
+                sequence.add(readNumber(b0, input));
             }
             else
             {
                 throw new IllegalArgumentException();
             }
         }
-
         return sequence;
     }
 
-    private CharStringCommand readCommand(int b0) throws IOException
+    private CharStringCommand readCommand(int b0, DataInput input) throws IOException
     {
 
         if (b0 == 1 || b0 == 18)
@@ -111,7 +184,7 @@ public class Type2CharStringParser
         return new CharStringCommand(b0);
     }
 
-    private Integer readNumber(int b0) throws IOException
+    private Integer readNumber(int b0, DataInput input) throws IOException
     {
 
         if (b0 == 28)
@@ -141,11 +214,10 @@ public class Type2CharStringParser
         {
             int b1 = input.readUnsignedByte();
             int b2 = input.readUnsignedByte();
-            int b3 = input.readUnsignedByte();
-            int b4 = input.readUnsignedByte();
-
             // The lower bytes are representing the digits after 
             // the decimal point and aren't needed in this context
+            input.readUnsignedByte();
+            input.readUnsignedByte();
             return Integer.valueOf((short)(b1 << 8 | b2));
         } 
         else

Modified: pdfbox/trunk/fontbox/src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java?rev=1425954&r1=1425953&r2=1425954&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java (original)
+++ pdfbox/trunk/fontbox/src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java Wed Dec 26 15:36:45 2012
@@ -42,7 +42,8 @@ public class Type1CharStringTest
                 new int[] { 12, 0 }, new int[] { 31 });
 
         byte[] encodedCommands = new Type1CharStringFormatter().format(commands);
-        List<Object> decodedCommands = new Type1CharStringParser().parse(encodedCommands);
+        List<Object> decodedCommands = new Type1CharStringParser()
+                .parse(encodedCommands, new IndexData(0));
 
         assertEquals(1 + 2 + 1, encodedCommands.length);
 
@@ -61,7 +62,7 @@ public class Type1CharStringTest
 
         byte[] encodedNumbers = new Type1CharStringFormatter().format(numbers);
         List<Object> decodedNumbers = new Type1CharStringParser()
-                .parse(encodedNumbers);
+                .parse(encodedNumbers, new IndexData(0));
 
         assertEquals(5 + 2 * 2 + 3 * 1 + 2 * 2 + 5, encodedNumbers.length);
 

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/util/Type1Parser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/util/Type1Parser.java?rev=1425954&r1=1425953&r2=1425954&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/util/Type1Parser.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/util/Type1Parser.java Wed Dec 26 15:36:45 2012
@@ -34,6 +34,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.fontbox.cff.IndexData;
 import org.apache.fontbox.cff.Type1CharStringParser;
 import org.apache.fontbox.cff.Type1FontUtil;
 import org.apache.pdfbox.encoding.Encoding;
@@ -281,7 +282,8 @@ public final class Type1Parser {
 		stream.read(descBinary, 0, sizeOfCharString);
 		byte[] description = Type1FontUtil.charstringDecrypt(descBinary, lenIV);
 		Type1CharStringParser t1p = new Type1CharStringParser();
-		List<Object> operations = t1p.parse(description);
+		// TODO provide the local subroutine indexes
+		List<Object> operations = t1p.parse(description, new IndexData(0));
 		type1Font.addGlyphDescription(label, new GlyphDescription(operations));
 
 		readToken(stream); // skip "ND" or "|-" token