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.

PDFLinearizationTestCase.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.pdf;
  19. import java.awt.Dimension;
  20. import java.awt.Rectangle;
  21. import java.awt.geom.Rectangle2D;
  22. import java.io.ByteArrayInputStream;
  23. import java.io.ByteArrayOutputStream;
  24. import java.io.File;
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import java.util.ArrayList;
  28. import java.util.LinkedHashMap;
  29. import java.util.List;
  30. import java.util.Map;
  31. import javax.xml.transform.stream.StreamResult;
  32. import org.junit.Assert;
  33. import org.junit.Test;
  34. import org.apache.fop.apps.FOUserAgent;
  35. import org.apache.fop.apps.FopFactory;
  36. import org.apache.fop.fonts.FontInfo;
  37. import org.apache.fop.render.intermediate.IFContext;
  38. import org.apache.fop.render.pdf.PDFContentGenerator;
  39. import org.apache.fop.render.pdf.PDFDocumentHandler;
  40. import org.apache.fop.render.pdf.PDFPainter;
  41. public class PDFLinearizationTestCase {
  42. private int objectLeast;
  43. private int[] objects;
  44. @Test
  45. public void testPDF() throws IOException {
  46. PDFDocument doc = new PDFDocument("");
  47. doc.setLinearizationEnabled(true);
  48. PDFResources resources = new PDFResources(doc);
  49. PDFResourceContext context = new PDFResourceContext(resources);
  50. ByteArrayOutputStream out = new ByteArrayOutputStream();
  51. PDFContentGenerator gen = null;
  52. for (int i = 0; i < 2; i++) {
  53. gen = new PDFContentGenerator(doc, out, context);
  54. Rectangle2D.Float f = new Rectangle2D.Float();
  55. PDFPage page = new PDFPage(resources, i, f, f, f, f);
  56. doc.registerObject(page);
  57. doc.registerObject(gen.getStream());
  58. page.setContents(new PDFReference(gen.getStream()));
  59. }
  60. gen.flushPDFDoc();
  61. byte[] data = out.toByteArray();
  62. checkPDF(data);
  63. }
  64. @Test
  65. public void testImage() throws Exception {
  66. String fopxconf = "<fop version=\"1.0\"><renderers>"
  67. + "<renderer mime=\"application/pdf\">"
  68. + "<linearization>true</linearization>"
  69. + "</renderer></renderers></fop>";
  70. FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI(),
  71. new ByteArrayInputStream(fopxconf.getBytes()));
  72. FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
  73. IFContext ifContext = new IFContext(foUserAgent);
  74. PDFDocumentHandler documentHandler = new PDFDocumentHandler(ifContext);
  75. documentHandler.getConfigurator().configure(documentHandler);
  76. ByteArrayOutputStream out = new ByteArrayOutputStream();
  77. documentHandler.setFontInfo(new FontInfo());
  78. documentHandler.setResult(new StreamResult(out));
  79. documentHandler.startDocument();
  80. documentHandler.startPage(0, "", "", new Dimension());
  81. PDFPainter pdfPainter = new PDFPainter(documentHandler, null);
  82. pdfPainter.drawImage("test/resources/fop/svg/logo.jpg", new Rectangle());
  83. documentHandler.endPage();
  84. Assert.assertFalse(out.toString().contains("/Subtype /Image"));
  85. documentHandler.endDocument();
  86. Assert.assertTrue(out.toString().contains("/Subtype /Image"));
  87. }
  88. private void checkPDF(byte[] data) throws IOException {
  89. checkHintTable(data);
  90. InputStream is = new ByteArrayInputStream(data);
  91. Map<String, StringBuilder> objs = readObjs(is);
  92. List<String> keys = new ArrayList<String>(objs.keySet());
  93. int start = keys.indexOf("1 0 obj");
  94. Assert.assertTrue(start > 1);
  95. int j = 1;
  96. for (int i = start; i < keys.size(); i++) {
  97. Assert.assertEquals(keys.get(i), j + " 0 obj");
  98. j++;
  99. }
  100. for (int i = 0; i < start; i++) {
  101. Assert.assertEquals(keys.get(i), j + " 0 obj");
  102. j++;
  103. }
  104. checkFirstObj(data);
  105. checkTrailer(data);
  106. String firstObj = objs.values().iterator().next().toString().replace("\n", "");
  107. Assert.assertTrue(firstObj.startsWith("<< /Linearized 1 /L " + data.length));
  108. Assert.assertTrue(firstObj.endsWith("startxref0%%EOF"));
  109. int pageObjNumber = getValue("/O", firstObj);
  110. Assert.assertTrue(objs.get(pageObjNumber + " 0 obj").toString().contains("/Type /Page"));
  111. Assert.assertTrue(objs.get("5 0 obj").toString().contains("/Type /Pages"));
  112. int total = 0;
  113. for (int i : objects) {
  114. total += i;
  115. }
  116. Assert.assertEquals(total, objs.size() - 6);
  117. }
  118. private void checkFirstObj(byte[] data) throws IOException {
  119. int firstObjPos = getValue("/E", getFirstObj(data));
  120. InputStream is = new ByteArrayInputStream(data);
  121. Assert.assertEquals(is.skip(firstObjPos), firstObjPos);
  122. byte[] obj = new byte[10];
  123. Assert.assertEquals(is.read(obj), obj.length);
  124. Assert.assertTrue(new String(obj).startsWith("1 0 obj"));
  125. }
  126. private void checkTrailer(byte[] data) throws IOException {
  127. int trailerPos = getValue("/T", getFirstObj(data));
  128. InputStream is = new ByteArrayInputStream(data);
  129. Assert.assertEquals(is.skip(trailerPos), trailerPos);
  130. byte[] obj = new byte[20];
  131. Assert.assertEquals(is.read(obj), obj.length);
  132. Assert.assertTrue(new String(obj).startsWith("0000000000 65535 f"));
  133. }
  134. private int getValue(String name, String firstObj) throws IOException {
  135. String[] split = firstObj.split(" ");
  136. for (int i = 0; i < split.length; i++) {
  137. if (split[i].equals(name)) {
  138. return Integer.valueOf(split[i + 1].replace(">>", ""));
  139. }
  140. }
  141. throw new IOException(name + " not found " + firstObj);
  142. }
  143. private int[] getArrayValue(String name, String firstObj) throws IOException {
  144. String[] split = firstObj.split(" ");
  145. for (int i = 0; i < split.length; i++) {
  146. if (split[i].equals(name)) {
  147. int[] v = new int[2];
  148. v[0] = Integer.valueOf(split[i + 1].replace("[", ""));
  149. v[1] = Integer.valueOf(split[i + 2].replace("]", ""));
  150. return v;
  151. }
  152. }
  153. throw new IOException(name + " not found " + firstObj);
  154. }
  155. private String getFirstObj(byte[] out) throws IOException {
  156. InputStream data = new ByteArrayInputStream(out);
  157. Map<String, StringBuilder> objs = readObjs(data);
  158. return objs.values().iterator().next().toString().replace("\n", "");
  159. }
  160. private void checkHintTable(byte[] out) throws IOException {
  161. String firstObj = getFirstObj(out);
  162. int hintPos = getArrayValue("/H", firstObj)[0];
  163. int hintLength = getArrayValue("/H", firstObj)[1];
  164. InputStream data = new ByteArrayInputStream(out);
  165. Assert.assertEquals(data.skip(hintPos), hintPos);
  166. byte[] hintTable = new byte[hintLength];
  167. Assert.assertEquals(data.read(hintTable), hintLength);
  168. String hintTableStr = new String(hintTable);
  169. Assert.assertTrue(hintTableStr.contains("/S "));
  170. Assert.assertTrue(hintTableStr.contains("/C "));
  171. Assert.assertTrue(hintTableStr.contains("/E "));
  172. Assert.assertTrue(hintTableStr.contains("/L "));
  173. Assert.assertTrue(hintTableStr.contains("/V "));
  174. Assert.assertTrue(hintTableStr.contains("/O "));
  175. Assert.assertTrue(hintTableStr.contains("/I "));
  176. Assert.assertTrue(hintTableStr.contains("/Length "));
  177. Assert.assertTrue(hintTableStr.contains("stream"));
  178. Assert.assertTrue(hintTableStr.contains("endstream"));
  179. Assert.assertTrue(hintTableStr.endsWith("endobj\n"));
  180. data = new ByteArrayInputStream(hintTable);
  181. readStart(data);
  182. int pages = getValue("/N", firstObj);
  183. readObjectsTable(data, pages);
  184. readSharedObjectsTable(data);
  185. Assert.assertEquals(objectLeast, 1);
  186. }
  187. private void readObjectsTable(InputStream data, int pages)
  188. throws IOException {
  189. objectLeast = read32(data);
  190. read32(data);
  191. int bitsDiffObjects = read16(data);
  192. read32(data);
  193. int bitsDiffPageLength = read16(data);
  194. read32(data);
  195. read16(data);
  196. read32(data);
  197. read16(data);
  198. read16(data);
  199. read16(data);
  200. read16(data);
  201. read16(data);
  202. objects = new int[pages];
  203. for (int i = 0; i < pages; i++) {
  204. objects[i] = objectLeast + readBits(bitsDiffObjects, data);
  205. }
  206. for (int i = 0; i < pages; i++) {
  207. readBits(bitsDiffPageLength, data);
  208. }
  209. for (int i = 0; i < pages; i++) {
  210. readBits(32, data);
  211. }
  212. }
  213. private void readSharedObjectsTable(InputStream str) throws IOException {
  214. readBits(32, str);
  215. readBits(32, str);
  216. readBits(32, str);
  217. int sharedGroups = readBits(32, str);
  218. readBits(16, str);
  219. readBits(32, str);
  220. int bitsDiffGroupLength = readBits(16, str);
  221. for (int i = 0; i < sharedGroups; i++) {
  222. readBits(bitsDiffGroupLength, str);
  223. }
  224. }
  225. private int readBits(int bits, InputStream data) throws IOException {
  226. if (bits == 32) {
  227. return read32(data);
  228. }
  229. if (bits == 16) {
  230. return read16(data);
  231. }
  232. throw new IOException("Wrong bits");
  233. }
  234. private int read32(InputStream data) throws IOException {
  235. int ch1 = data.read();
  236. int ch2 = data.read();
  237. int ch3 = data.read();
  238. int ch4 = data.read();
  239. return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4));
  240. }
  241. private int read16(InputStream data) throws IOException {
  242. int ch1 = data.read();
  243. int ch2 = data.read();
  244. return (ch1 << 8) + (ch2);
  245. }
  246. private void readStart(InputStream inputStream) throws IOException {
  247. StringBuilder sb = new StringBuilder();
  248. while (inputStream.available() > 0) {
  249. int data = inputStream.read();
  250. if (data == '\n') {
  251. if (sb.toString().equals("stream")) {
  252. return;
  253. }
  254. sb.setLength(0);
  255. } else {
  256. sb.append((char)data);
  257. }
  258. }
  259. }
  260. public static Map<String, StringBuilder> readObjs(InputStream inputStream) throws IOException {
  261. Map<String, StringBuilder> objs = new LinkedHashMap<String, StringBuilder>();
  262. StringBuilder sb = new StringBuilder();
  263. String key = null;
  264. while (inputStream.available() > 0) {
  265. int data = inputStream.read();
  266. if (data == '\n') {
  267. if (sb.toString().endsWith(" 0 obj")) {
  268. key = sb.toString().trim();
  269. objs.put(key, new StringBuilder());
  270. } else if (key != null) {
  271. objs.get(key).append(sb).append("\n");
  272. }
  273. sb.setLength(0);
  274. } else {
  275. sb.append((char)data);
  276. }
  277. }
  278. return objs;
  279. }
  280. }