Author: lehmi
Date: Sun Feb 3 14:45:37 2013
New Revision: 1441918
URL: http://svn.apache.org/viewvc?rev=1441918&view=rev
Log:
PDFBOX-1505: improved/fixed rendering of type 2 cff fonts as proposed by Robert
Meyer
Modified:
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java
Modified:
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java
URL:
http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java?rev=1441918&r1=1441917&r2=1441918&view=diff
==============================================================================
---
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java
(original)
+++
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java
Sun Feb 3 14:45:37 2013
@@ -19,8 +19,6 @@ package org.apache.fontbox.cff;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
-import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -30,29 +28,40 @@ import java.util.List;
*/
public class CharStringRenderer extends CharStringHandler
{
- // TODO CharStringRenderer as abstract Class with two inherited
classes according to the Charsstring type....
+ // TODO CharStringRenderer as abstract Class with two inherited classes
according to the Charsstring type....
private boolean isCharstringType1 = true;
private boolean isFirstCommand = true;
-
+
private GeneralPath path = null;
private Point2D sidebearingPoint = null;
private Point2D referencePoint = null;
private int width = 0;
-
- public CharStringRenderer() {
+ private boolean hasNonEndCharOp = false;
+
+ /**
+ * Constructor for the char string renderer.
+ */
+ public CharStringRenderer()
+ {
isCharstringType1 = true;
}
-
- public CharStringRenderer(boolean isType1) {
+
+ /**
+ * Constructor for the char string renderer with a parameter
+ * to determine whether the rendered CharString is type 1.
+ * @param isType1 Determines wheher the charstring is type 1
+ */
+ public CharStringRenderer(boolean isType1)
+ {
isCharstringType1 = isType1;
}
-
+
/**
* Renders the given sequence and returns the result as a GeneralPath.
* @param sequence the given charstring sequence
- * @return the rendered GeneralPath
+ * @return the rendered GeneralPath
*/
- public GeneralPath render(List<Object> sequence) throws IOException
+ public GeneralPath render(List<Object> sequence)
{
path = new GeneralPath();
sidebearingPoint = new Point2D.Float(0, 0);
@@ -61,240 +70,354 @@ public class CharStringRenderer extends
handleSequence(sequence);
return path;
}
-
+
/**
* {@inheritDoc}
*/
public List<Integer> handleCommand(List<Integer> numbers,
CharStringCommand command)
{
- if (isCharstringType1) {
+ if (isCharstringType1)
+ {
handleCommandType1(numbers, command);
- } else {
+ }
+ else
+ {
handleCommandType2(numbers, command);
}
return null;
}
-
+
/**
- *
+ *
* @param numbers
* @param command
*/
- private void handleCommandType2(List<Integer> numbers, CharStringCommand
command) {
+ private void handleCommandType2(List<Integer> numbers, CharStringCommand
command)
+ {
String name = CharStringCommand.TYPE2_VOCABULARY.get(command.getKey());
-
+
+ if (!hasNonEndCharOp)
+ {
+ hasNonEndCharOp = !"endchar".equals(name);
+ }
if ("vmoveto".equals(name)) //
{
- if (isFirstCommand && numbers.size() == 2) {
+ if (path.getCurrentPoint() != null)
+ {
+ closePath();
+ }
+ if (isFirstCommand && numbers.size() == 2)
+ {
setWidth(numbers.get(0));
- rmoveTo(Integer.valueOf(0), numbers.get(1));
- } else {
+ rmoveTo(Integer.valueOf(0), numbers.get(1));
+ }
+ else
+ {
rmoveTo(Integer.valueOf(0), numbers.get(0));
}
- }
+ }
else if ("rlineto".equals(name)) //
{
- if (isFirstCommand && numbers.size() == 3) {
+ if (isFirstCommand && numbers.size() == 3)
+ {
setWidth(numbers.get(0));
- rlineTo(numbers.get(1), numbers.get(2));
- } else {
- rlineTo(numbers.get(0), numbers.get(1));
}
- }
+ rrlineTo(numbers);
+ }
else if ("hlineto".equals(name))//
{
- if (isFirstCommand && numbers.size() == 2) {
- setWidth(numbers.get(0));
- rlineTo(numbers.get(1), Integer.valueOf(0));
- } else {
- rlineTo(numbers.get(0), Integer.valueOf(0));
+ if (isFirstCommand && numbers.size() == 2)
+ {
+ setWidth(numbers.get(0));
}
- }
+ hlineTo(numbers);
+ }
else if ("vlineto".equals(name))//
{
- if (isFirstCommand && numbers.size() == 2) {
+ if (isFirstCommand && numbers.size() == 2)
+ {
setWidth(numbers.get(0));
- rlineTo(Integer.valueOf(0), numbers.get(1));
- } else {
- rlineTo(Integer.valueOf(0), numbers.get(0));
}
- }
+ vlineTo(numbers);
+ }
else if ("rrcurveto".equals(name))//
{
- if (isFirstCommand && numbers.size() == 7) {
+ if (isFirstCommand && numbers.size() == 7)
+ {
setWidth(numbers.get(0));
- rrcurveTo(numbers.get(1), numbers.get(2), numbers.get(3),
numbers
- .get(4), numbers.get(5), numbers.get(6));
- } else {
- rrcurveTo(numbers.get(0), numbers.get(1), numbers.get(2),
numbers
- .get(3), numbers.get(4), numbers.get(5));
}
+ rrCurveTo(numbers);
+ }
+ else if ("rlinecurve".equals(name))
+ {
+ rlineCurve(numbers);
+ }
+ else if ("rcurveline".equals(name))
+ {
+ rcurveLine(numbers);
}
else if ("closepath".equals(name))
{
closePath();
- }
+ }
else if ("rmoveto".equals(name))//
{
- if (isFirstCommand && numbers.size() == 3) {
+ if (path.getCurrentPoint() != null)
+ {
+ closePath();
+ }
+ if (isFirstCommand && numbers.size() == 3)
+ {
setWidth(numbers.get(0));
rmoveTo(numbers.get(1), numbers.get(2));
- } else {
+ }
+ else
+ {
rmoveTo(numbers.get(0), numbers.get(1));
}
- }
+ }
else if ("hmoveto".equals(name)) //
{
- if (isFirstCommand && numbers.size() == 2) {
+ if (path.getCurrentPoint() != null)
+ {
+ closePath();
+ }
+ if (isFirstCommand && numbers.size() == 2)
+ {
setWidth(numbers.get(0));
rmoveTo(numbers.get(1), Integer.valueOf(0));
- } else {
+ }
+ else
+ {
rmoveTo(numbers.get(0), Integer.valueOf(0));
- }
- }
+ }
+ }
else if ("vhcurveto".equals(name))
{
- if (isFirstCommand && numbers.size() == 5) {
+ if (isFirstCommand && numbers.size() == 5)
+ {
setWidth(numbers.get(0));
- rrcurveTo(Integer.valueOf(0), numbers.get(1), numbers.get(2),
- numbers.get(3), numbers.get(4), Integer.valueOf(0));
- } else {
- rrcurveTo(Integer.valueOf(0), numbers.get(0), numbers.get(1),
- numbers.get(2), numbers.get(3), Integer.valueOf(0));
}
-
- }
+ rvhCurveTo(numbers);
+ }
else if ("hvcurveto".equals(name))
{
- if (isFirstCommand && numbers.size() == 5) {
- setWidth(numbers.get(0));
- rrcurveTo(numbers.get(1), Integer.valueOf(0), numbers.get(2),
- numbers.get(3), Integer.valueOf(0), numbers.get(4));
- } else {
- rrcurveTo(numbers.get(0), Integer.valueOf(0), numbers.get(1),
- numbers.get(2), Integer.valueOf(0), numbers.get(3));
+ if (isFirstCommand && numbers.size() == 5)
+ {
+ setWidth(numbers.get(0));
}
+ rhvCurveTo(numbers);
+ }
+ else if ("hhcurveto".equals(name))
+ {
+ rhhCurveTo(numbers);
+ }
+ else if ("vvcurveto".equals(name))
+ {
+ rvvCurveTo(numbers);
}
- else if ("hstem".equals(name)) {
- if (numbers.size() % 2 == 1 ) {
+ else if ("hstem".equals(name))
+ {
+ if (numbers.size() % 2 == 1 )
+ {
setWidth(numbers.get(0));
}
}
- else if ("vstem".equals(name)) {
- if (numbers.size() % 2 == 1 ) {
+ else if ("vstem".equals(name))
+ {
+ if (numbers.size() % 2 == 1 )
+ {
setWidth(numbers.get(0));
- }
+ }
}
- else if ("hstemhm".equals(name)) {
- if (numbers.size() % 2 == 1 ) {
+ else if ("hstemhm".equals(name))
+ {
+ if (numbers.size() % 2 == 1 )
+ {
setWidth(numbers.get(0));
}
}
- else if ("hstemhm".equals(name)) {
- if (numbers.size() % 2 == 1) {
+ else if ("vstemhm".equals(name))
+ {
+ if (numbers.size() % 2 == 1)
+ {
setWidth(numbers.get(0));
}
}
- else if ("cntrmask".equals(name)) {
- if (numbers.size() == 1 ) {
+ else if ("cntrmask".equals(name))
+ {
+ if (numbers.size() == 1 )
+ {
setWidth(numbers.get(0));
}
}
- else if ("hintmask".equals(name)) {
- if (numbers.size() == 1 ) {
+ else if ("hintmask".equals(name))
+ {
+ if (numbers.size() == 1 )
+ {
setWidth(numbers.get(0));
}
- }else if ("endchar".equals(name)) {
- if (numbers.size() == 1 ) {
+ }
+ else if ("endchar".equals(name))
+ {
+ if (hasNonEndCharOp)
+ {
+ closePath();
+ }
+ if (numbers.size() == 1 )
+ {
setWidth(numbers.get(0));
}
}
-
- if (isFirstCommand) { isFirstCommand = false; }
+
+ if (isFirstCommand)
+ {
+ isFirstCommand = false;
+ }
}
-
+
/**
- *
+ *
* @param numbers
* @param command
*/
- private void handleCommandType1(List<Integer> numbers, CharStringCommand
command) {
+ private void handleCommandType1(List<Integer> numbers, CharStringCommand
command)
+ {
String name = CharStringCommand.TYPE1_VOCABULARY.get(command.getKey());
-
+
if ("vmoveto".equals(name))
{
rmoveTo(Integer.valueOf(0), numbers.get(0));
- }
+ }
else if ("rlineto".equals(name))
{
rlineTo(numbers.get(0), numbers.get(1));
- }
+ }
else if ("hlineto".equals(name))
{
rlineTo(numbers.get(0), Integer.valueOf(0));
- }
+ }
else if ("vlineto".equals(name))
{
rlineTo(Integer.valueOf(0), numbers.get(0));
- }
+ }
else if ("rrcurveto".equals(name))
{
rrcurveTo(numbers.get(0), numbers.get(1), numbers.get(2), numbers
.get(3), numbers.get(4), numbers.get(5));
- }
+ }
else if ("closepath".equals(name))
{
closePath();
- }
+ }
else if ("sbw".equals(name))
{
pointSb(numbers.get(0), numbers.get(1));
setWidth(numbers.get(2).intValue());
- }
+ }
else if ("hsbw".equals(name))
{
pointSb(numbers.get(0), Integer.valueOf(0));
setWidth(numbers.get(1).intValue());
- }
+ }
else if ("rmoveto".equals(name))
{
rmoveTo(numbers.get(0), numbers.get(1));
- }
+ }
else if ("hmoveto".equals(name))
{
rmoveTo(numbers.get(0), Integer.valueOf(0));
- }
+ }
else if ("vhcurveto".equals(name))
{
rrcurveTo(Integer.valueOf(0), numbers.get(0), numbers.get(1),
numbers.get(2), numbers.get(3), Integer.valueOf(0));
- }
+ }
else if ("hvcurveto".equals(name))
{
rrcurveTo(numbers.get(0), Integer.valueOf(0), numbers.get(1),
numbers.get(2), Integer.valueOf(0), numbers.get(3));
}
}
-
+
private void rmoveTo(Number dx, Number dy)
{
Point2D point = referencePoint;
if (point == null)
{
- point = sidebearingPoint;
+ point = path.getCurrentPoint();
+ if (point == null)
+ {
+ point = sidebearingPoint;
+ }
}
referencePoint = null;
path.moveTo((float)(point.getX() + dx.doubleValue()),
(float)(point.getY() + dy.doubleValue()));
}
-
+
+ private void hlineTo(List<Integer> numbers)
+ {
+ for (int i = 0;i < numbers.size();i++)
+ {
+ if (i % 2 == 0)
+ {
+ rlineTo(numbers.get(i), Integer.valueOf(0));
+ }
+ else
+ {
+ rlineTo(Integer.valueOf(0), numbers.get(i));
+ }
+ }
+ }
+
+ private void vlineTo(List<Integer> numbers)
+ {
+ for (int i = 0;i < numbers.size();i++)
+ {
+ if (i % 2 == 0)
+ {
+ rlineTo(Integer.valueOf(0), numbers.get(i));
+ }
+ else
+ {
+ rlineTo(numbers.get(i), Integer.valueOf(0));
+ }
+ }
+ }
+
private void rlineTo(Number dx, Number dy)
{
Point2D point = path.getCurrentPoint();
path.lineTo((float)(point.getX() + dx.doubleValue()),
(float)(point.getY() + dy.doubleValue()));
}
-
+
+ private void rrlineTo(List<Integer> numbers)
+ {
+ for (int i = 0;i < numbers.size();i += 2)
+ {
+ rlineTo(numbers.get(i), numbers.get(i + 1));
+ }
+ }
+
+ private void rrCurveTo(List<Integer> numbers)
+ {
+ if (numbers.size() >= 6)
+ {
+ for (int i = 0;i < numbers.size();i += 6)
+ {
+ float x1 = numbers.get(i);
+ float y1 = numbers.get(i + 1);
+ float x2 = numbers.get(i + 2);
+ float y2 = numbers.get(i + 3);
+ float x3 = numbers.get(i + 4);
+ float y3 = numbers.get(i + 5);
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ }
+ }
+ }
+
private void rrcurveTo(Number dx1, Number dy1, Number dx2, Number dy2,
Number dx3, Number dy3)
{
@@ -307,18 +430,222 @@ public class CharStringRenderer extends
float y3 = y2 + dy3.floatValue();
path.curveTo(x1, y1, x2, y2, x3, y3);
}
-
+
+
+ private void rlineCurve(List<Integer> numbers)
+ {
+ if (numbers.size() >= 6)
+ {
+ if (numbers.size() - 6 > 0)
+ {
+ for (int i = 0;i < numbers.size() - 6;i += 2)
+ {
+ if (i + 1 >= numbers.size())
+ {
+ break;
+ }
+ rlineTo(numbers.get(i), numbers.get(i + 1));
+ }
+ }
+ float x1 = numbers.get(numbers.size() - 6);
+ float y1 = numbers.get(numbers.size() - 5);
+ float x2 = numbers.get(numbers.size() - 4);
+ float y2 = numbers.get(numbers.size() - 3);
+ float x3 = numbers.get(numbers.size() - 2);
+ float y3 = numbers.get(numbers.size() - 1);
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ }
+ }
+
+ private void rcurveLine(List<Integer> numbers)
+ {
+ for (int i = 0;i < numbers.size();i += 6)
+ {
+ if (numbers.size() - i < 6)
+ {
+ break;
+ }
+ float x1 = numbers.get(i);
+ float y1 = numbers.get(i + 1);
+ float x2 = numbers.get(i + 2);
+ float y2 = numbers.get(i + 3);
+ float x3 = numbers.get(i + 4);
+ float y3 = numbers.get(i + 5);
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ if (numbers.size() - (i + 6) == 2)
+ {
+ rlineTo(numbers.get(i + 6), numbers.get(i + 7));
+ }
+ }
+ }
+
+ private void rvhCurveTo(List<Integer> numbers)
+ {
+ boolean smallCase = numbers.size() <= 5;
+ boolean odd = numbers.size() % 2 != 0;
+ if ((!odd) ? numbers.size() % 4 == 0 : (numbers.size() -1) % 4 == 0)
+ {
+ float lastY = -1;
+ for (int i = 0;i < numbers.size();i += 4)
+ {
+ if ((numbers.size() - i) < 4)
+ {
+ break;
+ }
+ float x1 = (lastY != -1) ? numbers.get(i) : 0;
+ float y1 = (lastY != -1) ? 0 : numbers.get(i);
+ float x2 = numbers.get(i + 1);
+ float y2 = numbers.get(i + 2);
+ float x3 = (lastY != -1) ? 0 : numbers.get(i + 3);
+ float y3 = (lastY != -1) ? numbers.get(i + 3) : 0;
+ if (odd && (numbers.size() - i) == 5)
+ {
+ if (smallCase)
+ {
+ y3 = numbers.get(i + 4);
+ }
+ else
+ {
+ x3 = numbers.get(i + 4);
+ }
+ }
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ if (lastY == -1)
+ {
+ lastY = 0;
+ }
+ else
+ {
+ if (numbers.size() - (i + 4) > 0)
+ {
+ rvhCurveTo(numbers.subList(i + 4, numbers.size()));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private void rhvCurveTo(List<Integer> numbers)
+ {
+ boolean smallCase = numbers.size() <= 5;
+ boolean odd = numbers.size() % 2 != 0;
+ if ((!odd) ? numbers.size() % 4 == 0 : (numbers.size() -1) % 4 == 0)
+ {
+ float lastX = -1;
+ for (int i = 0;i < numbers.size();i += 4)
+ {
+ if ((numbers.size() - i) < 4)
+ {
+ break;
+ }
+ float x1 = (lastX != -1) ? 0 : numbers.get(i);
+ float y1 = (lastX != -1) ? numbers.get(i) : 0;
+ float x2 = numbers.get(i + 1);
+ float y2 = numbers.get(i + 2);
+ float x3 = (lastX != -1) ? numbers.get(i + 3) : 0;
+ float y3 = (lastX != -1) ? 0 : numbers.get(i + 3);
+ if (odd && (numbers.size() - i) == 5)
+ {
+ if (smallCase)
+ {
+ x3 = numbers.get(i + 4);
+ }
+ else
+ {
+ y3 = numbers.get(i + 4);
+ }
+ }
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ if (lastX == -1)
+ {
+ lastX = 0;
+ }
+ else
+ {
+ if (numbers.size() - (i + 4) > 0)
+ {
+ rhvCurveTo(numbers.subList(i + 4, numbers.size()));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private void rhhCurveTo(List<Integer> numbers)
+ {
+ boolean odd = numbers.size() % 2 != 0;
+ if ((!odd) ? numbers.size() % 4 == 0 : (numbers.size() -1) % 4 == 0)
+ {
+ float lastY = -1;
+ boolean bHandled = false;
+ int increment = (odd) ? 1 : 0;
+ for (int i = 0;i < numbers.size();i += 4)
+ {
+ if ((numbers.size() - i) < 4)
+ {
+ break;
+ }
+ float x1 = (odd && !bHandled) ? numbers.get(i + increment) :
numbers.get(i);
+ float y1 = (lastY != -1) ? lastY : (odd && !bHandled) ?
numbers.get(i) : 0;
+ float x2 = numbers.get(i + 1 + increment);
+ float y2 = numbers.get(i + 2 + increment);
+ float x3 = numbers.get(i + 3 + increment);
+ float y3 = 0;
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ lastY = 0;
+ if (odd && !bHandled)
+ {
+ i++;
+ bHandled = true;
+ }
+ increment = 0;
+ }
+ }
+ }
+
+ private void rvvCurveTo(List<Integer> numbers)
+ {
+ boolean odd = numbers.size() % 2 != 0;
+ if ((!odd) ? numbers.size() % 4 == 0 : (numbers.size() -1) % 4 == 0)
+ {
+ boolean bHandled = false;
+ int increment = (odd) ? 1 : 0;
+ for (int i = 0;i < numbers.size();i += 4)
+ {
+ if ((numbers.size() - i) < 4)
+ {
+ break;
+ }
+ float x1 = (odd && !bHandled) ? numbers.get(i) : 0;
+ float y1 = numbers.get(i + increment);
+ float x2 = numbers.get(i + 1 + increment);
+ float y2 = numbers.get(i + 2 + increment);
+ float x3 = 0;
+ float y3 = numbers.get(i + 3 + increment);
+ rrcurveTo(x1, y1, x2, y2, x3, y3);
+ if (odd && !bHandled)
+ {
+ i++;
+ bHandled = true;
+ }
+ increment = 0;
+ }
+ }
+ }
+
private void closePath()
{
referencePoint = path.getCurrentPoint();
path.closePath();
}
-
+
private void pointSb(Number x, Number y)
{
sidebearingPoint = new Point2D.Float(x.floatValue(), y.floatValue());
}
-
+
/**
* Returns the bounds of the renderer path.
* @return the bounds as Rectangle2D
@@ -327,7 +654,7 @@ public class CharStringRenderer extends
{
return path.getBounds2D();
}
-
+
/**
* Returns the width of the current command.
* @return the width
@@ -336,9 +663,9 @@ public class CharStringRenderer extends
{
return width;
}
-
- private void setWidth(int width)
+
+ private void setWidth(int aWidth)
{
- this.width = width;
+ this.width = aWidth;
}
}
\ No newline at end of file