You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pdfbox-1645.patch 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. Index: fontbox/src/test/java/org/apache/fontbox/cff/CharStringRendererTest.java
  2. ===================================================================
  3. --- fontbox/src/test/java/org/apache/fontbox/cff/CharStringRendererTest.java (revision 0)
  4. +++ fontbox/src/test/java/org/apache/fontbox/cff/CharStringRendererTest.java (revision 0)
  5. @@ -0,0 +1,28 @@
  6. +package org.apache.fontbox.cff;
  7. +
  8. +import java.util.ArrayList;
  9. +import java.util.List;
  10. +
  11. +import org.junit.Test;
  12. +
  13. +import static org.junit.Assert.assertTrue;
  14. +
  15. +public class CharStringRendererTest {
  16. +
  17. + @Test
  18. + public void testArgumentValidation() {
  19. + CharStringRenderer renderer = new CharStringRenderer();
  20. + List<Integer> numbers = new ArrayList<Integer>();
  21. + for (int i = 0;i < 4;i++) {
  22. + numbers.add(1);
  23. + }
  24. + assertTrue(renderer.hasValidArguments("vhcurveto", numbers));
  25. + numbers.add(1);
  26. + assertTrue(renderer.hasValidArguments("vvcurveto", numbers));
  27. + for (int i = 0;i < 3;i++) {
  28. + numbers.add(1);
  29. + }
  30. + assertTrue(renderer.hasValidArguments("rcurveline", numbers));
  31. + }
  32. +
  33. +}
  34. Index: fontbox/src/main/java/org/apache/fontbox/cff/AFMFormatter.java
  35. ===================================================================
  36. --- fontbox/src/main/java/org/apache/fontbox/cff/AFMFormatter.java (revision 1546564)
  37. +++ fontbox/src/main/java/org/apache/fontbox/cff/AFMFormatter.java (working copy)
  38. @@ -27,7 +27,7 @@
  39. /**
  40. * This class creates all needed AFM font metric data from a CFFFont ready to be read from a AFMPaser.
  41. - *
  42. + *
  43. * @author Villu Ruusmann
  44. * @version $Revision$
  45. */
  46. @@ -125,7 +125,7 @@
  47. metric.name = mapping.getName();
  48. renderer.render(mapping.toType1Sequence());
  49. metric.width = renderer.getWidth();
  50. - metric.bounds = renderer.getBounds();
  51. + metric.bounds = renderer.getBounds2D();
  52. metrics.add(metric);
  53. }
  54. return metrics;
  55. @@ -150,7 +150,7 @@
  56. }
  57. /**
  58. - * This class represents the metric of one single character.
  59. + * This class represents the metric of one single character.
  60. *
  61. */
  62. private static class CharMetric implements Comparable<CharMetric>
  63. Index: fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java
  64. ===================================================================
  65. --- fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java (revision 1546564)
  66. +++ fontbox/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java (working copy)
  67. @@ -16,9 +16,11 @@
  68. */
  69. package org.apache.fontbox.cff;
  70. +import java.awt.Point;
  71. import java.awt.geom.GeneralPath;
  72. import java.awt.geom.Point2D;
  73. import java.awt.geom.Rectangle2D;
  74. +import java.util.HashMap;
  75. import java.util.List;
  76. import org.apache.commons.logging.Log;
  77. @@ -33,7 +35,7 @@
  78. {
  79. // TODO CharStringRenderer as abstract Class with two inherited classes according to the Charsstring type....
  80. private static final Log LOG = LogFactory.getLog(CharStringRenderer.class);
  81. -
  82. +
  83. private boolean isCharstringType1 = true;
  84. private boolean isFirstCommand = true;
  85. @@ -42,6 +44,8 @@
  86. private Point2D referencePoint = null;
  87. private int width = 0;
  88. private boolean hasNonEndCharOp = false;
  89. + private int[] bbox = {0,0,0,0};
  90. + private HashMap<String, String> vStrings;
  91. /**
  92. * Constructor for the char string renderer.
  93. @@ -100,7 +104,7 @@
  94. private void handleCommandType2(List<Integer> numbers, CharStringCommand command)
  95. {
  96. String name = CharStringCommand.TYPE2_VOCABULARY.get(command.getKey());
  97. -
  98. + checkArguments(name, numbers);
  99. if (!hasNonEndCharOp)
  100. {
  101. hasNonEndCharOp = !"endchar".equals(name);
  102. @@ -176,7 +180,7 @@
  103. setWidth(numbers.get(0));
  104. rmoveTo(numbers.get(1), numbers.get(2));
  105. }
  106. - else
  107. + else if (numbers.size() == 2)
  108. {
  109. rmoveTo(numbers.get(0), numbers.get(1));
  110. }
  111. @@ -192,7 +196,7 @@
  112. setWidth(numbers.get(0));
  113. rmoveTo(numbers.get(1), Integer.valueOf(0));
  114. }
  115. - else
  116. + else if (numbers.size() == 1)
  117. {
  118. rmoveTo(numbers.get(0), Integer.valueOf(0));
  119. }
  120. @@ -284,6 +288,61 @@
  121. }
  122. }
  123. + void checkArguments(String name, List<Integer> numbers) {
  124. + if (name == null) {
  125. + return;
  126. + }
  127. + boolean valid = hasValidArguments(name, numbers);
  128. + if (!valid) {
  129. + //Initialize the validation strings if not already done
  130. + if (vStrings == null) {
  131. + vStrings = new HashMap<String, String>();
  132. + vStrings.put("vmoveto", "1 || 2");
  133. + vStrings.put("rlineto", "3 || % 2");
  134. + vStrings.put("rrcurveto", "7 || % 6");
  135. + vStrings.put("rlinecurve", "% 2 || 6 + % 2");
  136. + vStrings.put("rcurveline", "% 6 || 2 + % 6");
  137. + vStrings.put("rmoveto", "2 || 3");
  138. + vStrings.put("hmoveto", "1 || 2");
  139. + vStrings.put("vhcurveto", "% 4 || 1 + % 4");
  140. + vStrings.put("hvcurveto", "% 4 || 1 + % 4");
  141. + vStrings.put("vvcurveto", "% 4 || 1 + % 4");
  142. + }
  143. + LOG.info(String.format("Font has an unexpected number of parameters for operator '%s'. Arguments "+
  144. + "size %d did not match pattern '%s'", name, numbers.size(),
  145. + vStrings.get(name)));
  146. + }
  147. + }
  148. +
  149. + boolean hasValidArguments(String name, List<Integer> numbers) {
  150. + boolean valid = true;
  151. + if (name.equals("vmoveto")) {
  152. + valid = (numbers.size() == 1 || numbers.size() == 2);
  153. + }
  154. + if (name.equals("rlineto")) {
  155. + valid = (numbers.size() == 3 || numbers.size() % 2 == 0);
  156. + }
  157. + if (name.equals("rrcurveto")) {
  158. + valid = (numbers.size() == 7 || numbers.size() % 6 == 0);
  159. + }
  160. + if (name.equals("rlinecurve")) {
  161. + valid = (numbers.size() % 2 == 0 || (numbers.size() - 6) % 2 == 0);
  162. + }
  163. + if (name.equals("rcurveline")) {
  164. + valid = (numbers.size() % 6 == 0 || (numbers.size() - 2) % 6 == 0);
  165. + }
  166. + if (name.equals("rmoveto")) {
  167. + valid = (numbers.size() == 2 || numbers.size() == 3);
  168. + }
  169. + if (name.equals("hmoveto")) {
  170. + valid = (numbers.size() == 1 || numbers.size() == 2);
  171. + }
  172. + if (name.equals("vvcurveto") || name.equals("vhcurveto") || name.equals("hvcurveto")) {
  173. + valid = (numbers.size() % 4 == 0 || (numbers.size() - 1) % 4 == 0);
  174. + }
  175. + return valid;
  176. + }
  177. +
  178. /**
  179. *
  180. * @param numbers
  181. @@ -353,11 +412,14 @@
  182. Point2D point = referencePoint;
  183. if (point == null)
  184. {
  185. - point = path.getCurrentPoint();
  186. - if (point == null)
  187. + if (path.getCurrentPoint() == null)
  188. {
  189. point = sidebearingPoint;
  190. }
  191. + else
  192. + {
  193. + point = path.getCurrentPoint();
  194. + }
  195. }
  196. referencePoint = null;
  197. path.moveTo((float)(point.getX() + dx.doubleValue()),
  198. @@ -397,15 +459,20 @@
  199. private void rlineTo(Number dx, Number dy)
  200. {
  201. Point2D point = path.getCurrentPoint();
  202. - path.lineTo((float)(point.getX() + dx.doubleValue()),
  203. - (float)(point.getY() + dy.doubleValue()));
  204. + if (point != null) {
  205. + updateBBox(dx.intValue(), dy.intValue());
  206. + path.lineTo((float)(point.getX() + dx.doubleValue()),
  207. + (float)(point.getY() + dy.doubleValue()));
  208. + }
  209. }
  210. private void rrlineTo(List<Integer> numbers)
  211. {
  212. for (int i = 0;i < numbers.size();i += 2)
  213. {
  214. - rlineTo(numbers.get(i), numbers.get(i + 1));
  215. + if (numbers.size() - i >= 2) {
  216. + rlineTo(numbers.get(i), numbers.get(i + 1));
  217. + }
  218. }
  219. }
  220. @@ -415,13 +482,15 @@
  221. {
  222. for (int i = 0;i < numbers.size();i += 6)
  223. {
  224. - float x1 = numbers.get(i);
  225. - float y1 = numbers.get(i + 1);
  226. - float x2 = numbers.get(i + 2);
  227. - float y2 = numbers.get(i + 3);
  228. - float x3 = numbers.get(i + 4);
  229. - float y3 = numbers.get(i + 5);
  230. - rrcurveTo(x1, y1, x2, y2, x3, y3);
  231. + if (numbers.size() - i >= 6) {
  232. + float x1 = numbers.get(i);
  233. + float y1 = numbers.get(i + 1);
  234. + float x2 = numbers.get(i + 2);
  235. + float y2 = numbers.get(i + 3);
  236. + float x3 = numbers.get(i + 4);
  237. + float y3 = numbers.get(i + 5);
  238. + rrcurveTo(x1, y1, x2, y2, x3, y3);
  239. + }
  240. }
  241. }
  242. }
  243. @@ -429,14 +498,42 @@
  244. private void rrcurveTo(Number dx1, Number dy1, Number dx2, Number dy2,
  245. Number dx3, Number dy3)
  246. {
  247. - Point2D point = path.getCurrentPoint();
  248. - float x1 = (float) point.getX() + dx1.floatValue();
  249. - float y1 = (float) point.getY() + dy1.floatValue();
  250. - float x2 = x1 + dx2.floatValue();
  251. - float y2 = y1 + dy2.floatValue();
  252. - float x3 = x2 + dx3.floatValue();
  253. - float y3 = y2 + dy3.floatValue();
  254. - path.curveTo(x1, y1, x2, y2, x3, y3);
  255. + Point2D p0 = path.getCurrentPoint();
  256. + if (p0 != null) {
  257. + float x1 = (float) p0.getX() + dx1.floatValue();
  258. + float y1 = (float) p0.getY() + dy1.floatValue();
  259. + float x2 = x1 + dx2.floatValue();
  260. + float y2 = y1 + dy2.floatValue();
  261. + float x3 = x2 + dx3.floatValue();
  262. + float y3 = y2 + dy3.floatValue( );
  263. +
  264. + Point p1 = new Point((int)x1, (int)y1);
  265. + Point p2 = new Point((int)x2, (int)y2);
  266. + Point p3 = new Point((int)x3, (int)y3);
  267. +
  268. + updateBBox((int)p0.getX(), (int)p0.getY());
  269. + updateBBox((int)p3.getX(), (int)p3.getY());
  270. +
  271. + int[] abc = calculateABC((int)p0.getX(), p1.x, p2.x, p3.x);
  272. + double[] txs = getT(abc);
  273. + for (double tx : txs) {
  274. + if (tx > 0 && tx < 1) {
  275. + int[] XandY = getXandY(tx, new Point((int)p0.getX(), (int)p0.getY()), p1, p2, p3);
  276. + updateBBox(XandY[0], XandY[1]);
  277. + }
  278. + }
  279. +
  280. + abc = calculateABC((int)p0.getY(), p1.y, p2.y, p3.y);
  281. + double[] tys = getT(abc);
  282. + for (double ty : tys) {
  283. + if (ty > 0 && ty < 1) {
  284. + int[] XandY = getXandY(ty, new Point((int)p0.getX(), (int)p0.getY()), p1, p2, p3);
  285. + updateBBox(XandY[0], XandY[1]);
  286. + }
  287. + }
  288. +
  289. + path.curveTo(x1, y1, x2, y2, x3, y3);
  290. + }
  291. }
  292. @@ -646,7 +743,9 @@
  293. private void closePath()
  294. {
  295. referencePoint = path.getCurrentPoint();
  296. - path.closePath();
  297. + if (referencePoint != null) {
  298. + path.closePath();
  299. + }
  300. }
  301. private void pointSb(Number x, Number y)
  302. @@ -658,11 +757,15 @@
  303. * Returns the bounds of the renderer path.
  304. * @return the bounds as Rectangle2D
  305. */
  306. - public Rectangle2D getBounds()
  307. + public int[] getBounds()
  308. {
  309. - return path.getBounds2D();
  310. + return bbox;
  311. }
  312. + public Rectangle2D getBounds2D() {
  313. + return path.getBounds2D();
  314. + }
  315. +
  316. /**
  317. * Returns the width of the current command.
  318. * @return the width
  319. @@ -676,4 +779,61 @@
  320. {
  321. this.width = aWidth;
  322. }
  323. -}
  324. \ No newline at end of file
  325. +
  326. + private int[] calculateABC(int p0, int p1, int p2, int p3) {
  327. + int[] abc = new int[3];
  328. + abc[0] = p0 - 3 * p1 + 3 * p2 - p3;
  329. + abc[1] = 2 * (-p0 + 2 * p1 - p2);
  330. + abc[2] = p0 - p1;
  331. + return abc;
  332. + }
  333. +
  334. + private double[] getT(int[] abc) {
  335. + double[] t = {-1, -1};
  336. + int a = abc[0];
  337. + int b = abc[1];
  338. + int c = abc[2];
  339. + double s = Math.pow(b, 2) - 4 * a * c;
  340. + if (a == 0) {
  341. + if (b != 0) {
  342. + t[0] = -c / b;
  343. + }
  344. + return t;
  345. + } else if (s > 0) {
  346. + t[0] = (-b + Math.sqrt(s)) / 2 / a;
  347. + t[1] = (-b - Math.sqrt(s)) / 2 / a;
  348. + return t;
  349. + } else if (s == 0) {
  350. + t[0] = -b / 2 / a;
  351. + return t;
  352. + } else {
  353. + return t;
  354. + }
  355. + }
  356. +
  357. + private int[] getXandY(double t, Point p0, Point p1, Point p2, Point p3) {
  358. + int[] XandY = new int[2];
  359. + double p0Coeff = Math.pow(1 - t, 3);
  360. + double p1Coeff = 3 * t * Math.pow(1 - t, 2);
  361. + double p2Coeff = 3 * Math.pow(t, 2) * (1 - t);
  362. + double p3Coeff = Math.pow(t, 3);
  363. + double x = p0Coeff * p0.x + p1Coeff * p1.x + p2Coeff * p2.x + p3Coeff * p3.x;
  364. + double y = p0Coeff * p0.y + p1Coeff * p1.y + p2Coeff * p2.y + p3Coeff * p3.y;
  365. + XandY[0] = (int)x;
  366. + XandY[1] = (int)y;
  367. + return XandY;
  368. + }
  369. +
  370. + private void updateBBox(int x, int y) {
  371. + if (x < bbox[0]) {
  372. + bbox[0] = x;
  373. + } else if (x > bbox[2]) {
  374. + bbox[2] = x;
  375. + }
  376. + if (y < bbox[1]) {
  377. + bbox[1] = y;
  378. + } else if (y > bbox[3]) {
  379. + bbox[3] = y;
  380. + }
  381. + }
  382. +}