Codebase list libpdfbox2-java / a5950cc
New upstream version 2.0.27 Markus Koschany 1 year, 6 months ago
100 changed file(s) with 1188 addition(s) and 1037 deletion(s). Raw diff Collapse all Expand all
0 Release Notes -- Apache PDFBox -- Version 2.0.26
0 Release Notes -- Apache PDFBox -- Version 2.0.27
11
22 Introduction
33 ------------
44
55 The Apache PDFBox library is an open source Java tool for working with PDF documents.
66
7 This is an incremental bugfix release based on the earlier 2.0.25 release. It contains
7 This is an incremental bugfix release based on the earlier 2.0.26 release. It contains
88 a couple of fixes and small improvements.
99
1010 For more details on these changes and all the other fixes and improvements
1313
1414 Bug
1515
16 [PDFBOX-4623] - COSParser: Infinite recursion
17 [PDFBOX-5203] - TestCreateSignature.testCreateSignedTimeStamp checkLTV build test fail
18 [PDFBOX-5283] - No Content - xRef / Obj Parsing
19 [PDFBOX-5305] - Pdf-A/1b Validation
20 [PDFBOX-5339] - A list of bugs found (70 bugs in total)
21 [PDFBOX-5342] - Text size option for PDFBox Debugger
22 [PDFBOX-5345] - IllegalArgumentException: Input buffer too short in StandardSecurityHandler.computeRC4key
23 [PDFBOX-5352] - ArrayIndexOutOfBoundsException in PDSeparation.tintTransform()
24 [PDFBOX-5360] - EOFException: Can't read 20 bytes
25 [PDFBOX-5361] - Wrong datatype for OPM in PDExtendedGraphicsState
26 [PDFBOX-5366] - Unhandled IOException thrown from BaseParser creates issue in PDFStreamEngine.processStreamOperators
27 [PDFBOX-5372] - *LOADS of* "WARNING: key node000xxxxx already exists in destination IDTree"
28 [PDFBOX-5373] - NullPointerException in PDRange.getMin()
29 [PDFBOX-5376] - Image interpolation when there shouldn't be
30 [PDFBOX-5377] - pDAcroForm.flatten() does not remove /SigFlags in /Catalog object
31 [PDFBOX-5380] - Could not read embedded TTF for font
32 [PDFBOX-5387] - ToUnicodeWriter.writeTo allows byte overflow in bfrange operator
33 [PDFBOX-5390] - TextToPDF appends space to each line
34 [PDFBOX-5393] - NegativeArraySizeException in pfb parser with 0 byte pfb font file
35 [PDFBOX-5395] - Hangup in COSFilterInputStream.nextRange
36 [PDFBOX-5397] - Certain PDF cannot be processed
37 [PDFBOX-5398] - Parsing fails in 2.0.26 that worked in 2.0.25
38 [PDFBOX-5399] - Object must be defined and must not be compressed object
39 [PDFBOX-5400] - Page tree root must be a dictionary
40 [PDFBOX-5401] - A carefully crafted pdf can trigger an infinite loop while parsing
41 [PDFBOX-5402] - POCIDFontType2 (Wingdings) encode throws a NullPointerException
42 [PDFBOX-5410] - Possible loop detection is triggered in 2.0.26 but file works in 2.0.25
43 [PDFBOX-5412] - IOException: object reference 112 0 R at offset 18355 in content stream
44 [PDFBOX-5413] - Field text missing
45 [PDFBOX-5418] - NPE during page render
46 [PDFBOX-5419] - Parsing shows 1 empty page with 2.0.26 and 7 with 2.0.25
16 [PDFBOX-4925] - Invalid stream Length validation in StreamValidationProcess
17 [PDFBOX-5389] - To set compressed on buffered image while creating a PDF
18 [PDFBOX-5403] - Blurry / distorted rendering
19 [PDFBOX-5424] - java.lang.IndexOutOfBoundsException (2)
20 [PDFBOX-5427] - PDFDebugger does not remove listeners for PagePane when opening new File
21 [PDFBOX-5428] - PDFRenderer.renderImageWithDPI thows EOFException in PDF
22 [PDFBOX-5429] - PDFCloneUtility.checkForRecursion breaks support for some existing PDFs
23 [PDFBOX-5430] - PDFStreamEngine.showTextStrings with font switch
24 [PDFBOX-5453] - ClassCastException (PDColor.java:66)
25 [PDFBOX-5459] - NullPointerException in PDFunctionType3.eval()
26 [PDFBOX-5460] - Deadlock in TrueTypeFont and RAFDataStream
27 [PDFBOX-5463] - illegalArgumentException for rendering PDF (image extraction)
28 [PDFBOX-5465] - NullPointerException in CmapSubtable.getCharCode
29 [PDFBOX-5470] - PDActionEmbeddedGoTo does not accept a Destination with a page number or string
30 [PDFBOX-5471] - NPE when Transparency Group is missing the BBox
31 [PDFBOX-5484] - PDFRenderer does not render letters when converting page to image
32 [PDFBOX-5488] - JPEG image rendered with wrong colors when using TwelveMonkeys
33 [PDFBOX-5499] - Performance issue since 2.0.18
34 [PDFBOX-5500] - NullPointerException in PDType0Font.readCode() if cMap is null
35 [PDFBOX-5504] - NullPointerException in CFFParser.parseFont()
36 [PDFBOX-5505] - IndexOutOfBoundsException in PDCIDFont.readWidths()
37 [PDFBOX-5506] - IndexOutOfBoundsException in Type1Parser.java
38 [PDFBOX-5507] - ClassCastException in CMapParser.parseBeginbfchar
39 [PDFBOX-5508] - ClassCastException in PDXObject.createXObject()
40 [PDFBOX-5509] - ClassCastException in PDAcroForm.getFields()
41 [PDFBOX-5510] - ClassCastException in PDDocumentCatalog.getAcroForm()
42 [PDFBOX-5511] - ClassCastException in PDResources.getIndirect()
43 [PDFBOX-5513] - getPageLayout throws IllegalArgumentException for empty mode
44 [PDFBOX-5514] - Font not found because of case issues
4745
4846 Improvement
4947
50 [PDFBOX-5347] - Create push button example
51 [PDFBOX-5348] - FontMapper should also take into account the user's font directory on Windows operating systems
52 [PDFBOX-5363] - Don't log warnings if there are not fonts to cache
53 [PDFBOX-5379] - support multiple widgets in PDTerminalField.importFDF()
54 [PDFBOX-5385] - Improve AddValidationInformation to handle exceptional situations better
55 [PDFBOX-5388] - Avoid duplicate certificates in AddValidation example
56 [PDFBOX-5394] - Render symbol for file attachment annotations
57
58 Task
59
60 [PDFBOX-5356] - Add test of PFB font
61 [PDFBOX-5396] - Add maven enforcer rule to ensure that JAVA_HOME is set
48 [PDFBOX-5448] - Clarify access permission for high / low print quality
49 [PDFBOX-5450] - Create Opaque PDFRenderer example
50 [PDFBOX-5494] - "invalid object number value" is too scary
6251
6352 Release Contents
6453 ----------------
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
6262 new Object[]{9, "can fill in form fields", ap.canFillInForm()},
6363 new Object[]{10, "can extract for accessibility", ap.canExtractForAccessibility()},
6464 new Object[]{11, "can assemble document", ap.canAssembleDocument()},
65 new Object[]{12, "can print degraded", ap.canPrintDegraded()},
65 new Object[]{12, "can print faithful", ap.canPrintFaithful()},
6666 };
6767 }
6868 }
307307
308308 PDFDebugger.allowSubsampling.setEnabled(false);
309309 PDFDebugger.allowSubsampling.removeActionListener(this);
310
311 PDFDebugger.repairAcroFormMenuItem.setEnabled(false);
312 PDFDebugger.repairAcroFormMenuItem.removeActionListener(this);
310313 }
311314
312315 @Override
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
0 /*
1 * Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.pdfbox.examples.printing;
17
18 import java.awt.print.Printable;
19 import java.awt.print.PrinterException;
20 import java.awt.print.PrinterJob;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URL;
24 import java.util.List;
25 import javax.print.PrintServiceLookup;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.pdfbox.contentstream.operator.MissingOperandException;
29 import org.apache.pdfbox.contentstream.operator.Operator;
30 import org.apache.pdfbox.contentstream.operator.OperatorName;
31 import org.apache.pdfbox.contentstream.operator.graphics.GraphicsOperatorProcessor;
32 import org.apache.pdfbox.cos.COSBase;
33 import org.apache.pdfbox.cos.COSName;
34 import org.apache.pdfbox.pdmodel.MissingResourceException;
35 import org.apache.pdfbox.pdmodel.PDDocument;
36 import org.apache.pdfbox.pdmodel.graphics.PDXObject;
37 import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
38 import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
39 import org.apache.pdfbox.printing.PDFPrintable;
40 import org.apache.pdfbox.printing.Scaling;
41 import org.apache.pdfbox.rendering.PDFRenderer;
42 import org.apache.pdfbox.rendering.PageDrawer;
43 import org.apache.pdfbox.rendering.PageDrawerParameters;
44
45 /**
46 * PDF documents with transparency groups are sometimes printed slowly and in poor quality, see
47 * <a href="https://issues.apache.org/jira/browse/PDFBOX-4123">PDFBOX-4123</a>. If the transparency
48 * groups aren't really needed (e.g. for most labels), we can use a custom PDFRenderer / PageDrawer
49 * that uses a custom DrawObject class which doesn't call showTransparencyGroup() but only
50 * showForm().
51 * <p>
52 * This OpaquePDFRenderer class object can be passed to the "long" constructor of
53 * {@link PDFPrintable#PDFPrintable(org.apache.pdfbox.pdmodel.PDDocument, org.apache.pdfbox.printing.Scaling, boolean, float, boolean, org.apache.pdfbox.rendering.PDFRenderer)}.
54 *
55 * @author Tilman Hausherr
56 */
57 public class OpaquePDFRenderer extends PDFRenderer
58 {
59
60 public static void main(String[] args) throws IOException, PrinterException
61 {
62 // PDF from the QZ Tray project, who reported this problem.
63 InputStream is = new URL("https://github.com/qzind/tray/files/1749977/test.pdf").openStream();
64 PDDocument doc = PDDocument.load(is);
65 is.close();
66 PDFRenderer renderer = new OpaquePDFRenderer(doc);
67 Printable printable = new PDFPrintable(doc, Scaling.SCALE_TO_FIT, false, 0, true, renderer);
68 PrinterJob job = PrinterJob.getPrinterJob();
69 job.setPrintService(PrintServiceLookup.lookupDefaultPrintService());
70 job.setPrintable(printable);
71 if (job.printDialog())
72 {
73 job.print();
74 }
75 doc.close();
76 }
77
78 public OpaquePDFRenderer(PDDocument document)
79 {
80 super(document);
81 }
82
83 @Override
84 protected PageDrawer createPageDrawer(PageDrawerParameters parameters) throws IOException
85 {
86 return new OpaquePageDrawer(parameters);
87 }
88
89 private class OpaquePageDrawer extends PageDrawer
90 {
91
92 public OpaquePageDrawer(PageDrawerParameters parameters) throws IOException
93 {
94 super(parameters);
95 addOperator(new OpaqueDrawObject());
96 }
97 }
98
99 // copied from org.apache.pdfbox.contentstream.operator.graphics.DrawObject()
100 // but doesn't call showTransparencyGroup
101 private static class OpaqueDrawObject extends GraphicsOperatorProcessor
102 {
103
104 private static final Log LOG = LogFactory.getLog(OpaqueDrawObject.class);
105
106 @Override
107 public void process(Operator operator, List<COSBase> operands) throws IOException
108 {
109 if (operands.isEmpty())
110 {
111 throw new MissingOperandException(operator, operands);
112 }
113 COSBase base0 = operands.get(0);
114 if (!(base0 instanceof COSName))
115 {
116 return;
117 }
118 COSName objectName = (COSName) base0;
119 PDXObject xobject = context.getResources().getXObject(objectName);
120
121 if (xobject == null)
122 {
123 throw new MissingResourceException("Missing XObject: " + objectName.getName());
124 }
125 else if (xobject instanceof PDImageXObject)
126 {
127 PDImageXObject image = (PDImageXObject) xobject;
128 context.drawImage(image);
129 }
130 else if (xobject instanceof PDFormXObject)
131 {
132 try
133 {
134 context.increaseLevel();
135 if (context.getLevel() > 50)
136 {
137 LOG.error("recursion is too deep, skipping form XObject");
138 return;
139 }
140 context.showForm((PDFormXObject) xobject);
141 }
142 finally
143 {
144 context.decreaseLevel();
145 }
146 }
147 }
148
149 @Override
150 public String getName()
151 {
152 return OperatorName.DRAW_OBJECT;
153 }
154 }
155 }
2020 <parent>
2121 <groupId>org.apache.pdfbox</groupId>
2222 <artifactId>pdfbox-parent</artifactId>
23 <version>2.0.26</version>
23 <version>2.0.27</version>
2424 <relativePath>../parent/pom.xml</relativePath>
2525 </parent>
2626
491491 int charStringsOffset = charStringsEntry.getNumber(0).intValue();
492492 input.setPosition(charStringsOffset);
493493 byte[][] charStringsIndex = readIndexData(input);
494
494 if (charStringsIndex == null)
495 {
496 throw new IOException("CharStringsIndex is missing");
497 }
498
495499 // charset
496500 DictData.Entry charsetEntry = topDict.getEntry("charset");
497501 CFFCharset charset;
273273 checkExpectedOperator((Operator) nextToken, "endcodespacerange", "codespacerange");
274274 break;
275275 }
276 if (!(nextToken instanceof byte[]))
277 {
278 throw new IOException("start range missing");
279 }
276280 byte[] startRange = (byte[]) nextToken;
277281 byte[] endRange = (byte[]) parseNextToken(cmapStream);
278282 try
296300 checkExpectedOperator((Operator) nextToken, "endbfchar", "bfchar");
297301 break;
298302 }
303 if (!(nextToken instanceof byte[]))
304 {
305 throw new IOException("input code missing");
306 }
299307 byte[] inputCode = (byte[]) nextToken;
300308 nextToken = parseNextToken(cmapStream);
301309 if (nextToken instanceof byte[])
325333 {
326334 checkExpectedOperator((Operator) nextToken, "endcidrange", "cidrange");
327335 break;
336 }
337 if (!(nextToken instanceof byte[]))
338 {
339 throw new IOException("start range missing");
328340 }
329341 byte[] startCode = (byte[]) nextToken;
330342 int start = createIntFromBytes(startCode);
367379 checkExpectedOperator((Operator) nextToken, "endcidchar", "cidchar");
368380 break;
369381 }
382 if (!(nextToken instanceof byte[]))
383 {
384 throw new IOException("start code missing");
385 }
370386 byte[] inputCode = (byte[]) nextToken;
371387 int mappedCode = (Integer) parseNextToken(cmapStream);
372388 int mappedCID = createIntFromBytes(inputCode);
379395 for (int j = 0; j < cosCount.intValue(); j++)
380396 {
381397 Object nextToken = parseNextToken(cmapStream);
382 if (nextToken == null)
383 {
384 throw new IOException("start code missing");
385 }
386398 if (nextToken instanceof Operator)
387399 {
388400 checkExpectedOperator((Operator) nextToken, "endbfrange", "bfrange");
389401 break;
390402 }
403 if (!(nextToken instanceof byte[]))
404 {
405 throw new IOException("start code missing");
406 }
391407 byte[] startCode = (byte[]) nextToken;
392408 nextToken = parseNextToken(cmapStream);
393 if (nextToken == null)
394 {
395 throw new IOException("end code missing");
396 }
397409 if (nextToken instanceof Operator)
398410 {
399411 checkExpectedOperator((Operator) nextToken, "endbfrange", "bfrange");
400412 break;
413 }
414 if (!(nextToken instanceof byte[]))
415 {
416 throw new IOException("end code missing");
401417 }
402418 byte[] endCode = (byte[]) nextToken;
403419 int start = CMap.toInt(startCode, startCode.length);
166166 }
167167 if (size > pfbdata.length - pointer)
168168 {
169 throw new IOException("PFB record size (" + size +
170 ") doesn't fit in buffer, position: " + pointer +
171 ", total length: " + pfbdata.length);
169 throw new EOFException("attempted to read " + size + " bytes at position " + pointer +
170 " into array of size " + pfbdata.length + ", but only space for " +
171 (pfbdata.length - pointer) + " bytes left");
172172 }
173173 int got = in.read(pfbdata, pointer, size);
174174 if (got < 0)
665665
666666 private int getCharCode(int gid)
667667 {
668 if (gid < 0 || gid >= glyphIdToCharacterCode.length)
668 if (gid < 0 || glyphIdToCharacterCode == null || gid >= glyphIdToCharacterCode.length)
669669 {
670670 return -1;
671671 }
3838
3939 private int cached = 0;
4040
41 private HorizontalMetricsTable hmt = null;
42
4143 /**
4244 * Don't even bother to cache huge fonts.
4345 */
7476
7577 // we don't actually read the complete table here because it can contain tens of thousands of glyphs
7678 this.data = data;
79
80 // PDFBOX-5460: read hmtx table early to avoid deadlock if getGlyph() locks "data"
81 // and then locks TrueTypeFont to read this table, while another thread
82 // locks TrueTypeFont and then tries to lock "data"
83 hmt = font.getHorizontalMetrics();
84
7785 initialized = true;
7886 }
7987
206214 private GlyphData getGlyphData(int gid) throws IOException
207215 {
208216 GlyphData glyph = new GlyphData();
209 HorizontalMetricsTable hmt = font.getHorizontalMetrics();
210217 int leftSideBearing = hmt == null ? 0 : hmt.getLeftSideBearing(gid);
211218 glyph.initData(this, data, leftSideBearing);
212219 // resolve composite glyph
743743
744744 // RD
745745 Token charstring = read(Token.CHARSTRING);
746 font.subrs.set(index.intValue(), decrypt(charstring.getData(), CHARSTRING_KEY, lenIV));
746 int j = index.intValue();
747 if (j < font.subrs.size())
748 {
749 font.subrs.set(j, decrypt(charstring.getData(), CHARSTRING_KEY, lenIV));
750 }
747751 readPut();
748752 }
749753 readDef();
3232
3333 <groupId>org.apache.pdfbox</groupId>
3434 <artifactId>pdfbox-parent</artifactId>
35 <version>2.0.26</version>
35 <version>2.0.27</version>
3636 <packaging>pom</packaging>
3737
3838 <name>PDFBox parent</name>
202202 <plugin>
203203 <groupId>org.owasp</groupId>
204204 <artifactId>dependency-check-maven</artifactId>
205 <version>7.0.4</version>
205 <version>7.1.2</version>
206206 <configuration>
207207 <failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability>
208208 <!-- https://github.com/jeremylong/DependencyCheck/issues/1574 -->
351351 <plugin>
352352 <groupId>org.apache.rat</groupId>
353353 <artifactId>apache-rat-plugin</artifactId>
354 <version>0.15</version>
354355 <configuration>
355356 <excludes>
356357 <exclude>release.properties</exclude>
538539 </developers>
539540
540541 <scm>
541 <connection>scm:svn:http://svn.apache.org/repos/asf/maven/pom/tags/2.0.26/pdfbox-parent</connection>
542 <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/pom/tags/2.0.26/pdfbox-parent</developerConnection>
543 <url>http://svn.apache.org/viewvc/maven/pom/tags/2.0.26/pdfbox-parent</url>
542 <connection>scm:svn:http://svn.apache.org/repos/asf/maven/pom/tags/2.0.27/pdfbox-parent</connection>
543 <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/pom/tags/2.0.27/pdfbox-parent</developerConnection>
544 <url>http://svn.apache.org/viewvc/maven/pom/tags/2.0.27/pdfbox-parent</url>
544545 </scm>
545546 </project>
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
7878 <dependency>
7979 <groupId>org.mockito</groupId>
8080 <artifactId>mockito-core</artifactId>
81 <version>4.4.0</version>
81 <version>4.8.0</version>
8282 <scope>test</scope>
8383 </dependency>
8484 <!-- For legal reasons (incompatible license), these two dependencies below
715715 <sha512>37c73b41d1e00d66717c3715e6c45724c3f163d3d5b045c67e90c3713746d39eef96b4f0e6f368d0679d4c73d02ca01cfe5141d8a526e46ea15a4579ea1e75a2</sha512>
716716 </configuration>
717717 </execution>
718 <execution>
719 <id>PDFBOX-5484</id>
720 <phase>generate-test-resources</phase>
721 <goals>
722 <goal>wget</goal>
723 </goals>
724 <configuration>
725 <url>https://issues.apache.org/jira/secure/attachment/13047577/PDFBOX-5484.ttf</url>
726 <outputDirectory>${project.build.directory}/fonts</outputDirectory>
727 <outputFileName>PDFBOX-5484.ttf</outputFileName>
728 <sha512>7c3d8bbc18654315d6341a277dcd5c66218b95c43baf190b6e32f77817d17bab421ef76f2c904b46c97f84c49b00d58525449cff970897010534d6aa2812a4e2</sha512>
729 </configuration>
730 </execution>
718731 </executions>
719732 </plugin>
720733 </plugins>
681681 }
682682 else if (obj instanceof COSArray)
683683 {
684 LOG.error("Nested arrays are not allowed in an array for TJ operation:" + obj);
684 LOG.error("Nested arrays are not allowed in an array for TJ operation: " + obj);
685685 }
686686 else
687687 {
688 throw new IOException("Unknown type " + obj.getClass().getSimpleName()
689 + " in array for TJ operation:" + obj);
688 LOG.error("Unknown type " + obj.getClass().getSimpleName()
689 + " in array for TJ operation: " + obj);
690690 }
691691 }
692692 }
2121 import java.util.Arrays;
2222 import java.util.Calendar;
2323 import java.util.Collection;
24 import java.util.LinkedHashMap;
2425 import java.util.List;
2526 import java.util.Map;
2627 import java.util.Set;
3940 public class COSDictionary extends COSBase implements COSUpdateInfo
4041 {
4142 private static final String PATH_SEPARATOR = "/";
43 private static final int MAP_THRESHOLD = 1000;
4244 private boolean needToBeUpdated;
4345
4446 /**
6163 */
6264 public COSDictionary(COSDictionary dict)
6365 {
64 items.putAll(dict.items);
66 addAll(dict);
6567 }
6668
6769 /**
213215 }
214216 else
215217 {
218 if (items instanceof SmallMap && items.size() >= MAP_THRESHOLD)
219 {
220 items = new LinkedHashMap<COSName, COSBase>(items);
221 }
216222 items.put(key, value);
217223 }
218224 }
14371443 * This will add all of the dictionaries keys/values to this dictionary. Existing key/value pairs will be
14381444 * overwritten.
14391445 *
1440 * @param dic The dictionaries to get the key/value pairs from.
1441 */
1442 public void addAll(COSDictionary dic)
1443 {
1444 for (Map.Entry<COSName, COSBase> entry : dic.entrySet())
1445 {
1446 setItem(entry.getKey(), entry.getValue());
1447 }
1446 * @param dict The dictionaries to get the key/value pairs from.
1447 */
1448 public void addAll(COSDictionary dict)
1449 {
1450 if (items instanceof SmallMap && items.size() + dict.items.size() >= MAP_THRESHOLD)
1451 {
1452 items = new LinkedHashMap<COSName, COSBase>(items);
1453 }
1454 items.putAll(dict.items);
14481455 }
14491456
14501457 /**
1515 */
1616 package org.apache.pdfbox.cos;
1717
18 import java.util.Calendar;
19
20 import org.apache.pdfbox.pdmodel.common.COSObjectable;
18 import java.util.Collections;
2119
2220 /**
2321 * An unmodifiable COSDictionary.
3230 UnmodifiableCOSDictionary(COSDictionary dict)
3331 {
3432 super();
35 items = dict.items;
36 }
37
38 /**
39 * {@inheritDoc}
40 */
41 @Override
42 public void clear()
43 {
44 throw new UnsupportedOperationException();
45 }
46
47 /**
48 * {@inheritDoc}
49 */
50 @Override
51 public void setItem(COSName key, COSBase value)
52 {
53 throw new UnsupportedOperationException();
54 }
55
56 /**
57 * {@inheritDoc}
58 */
59 @Override
60 public void setItem(COSName key, COSObjectable value)
61 {
62 throw new UnsupportedOperationException();
63 }
64
65 /**
66 * {@inheritDoc}
67 */
68 @Override
69 public void setItem(String key, COSObjectable value)
70 {
71 throw new UnsupportedOperationException();
72 }
73
74 /**
75 * {@inheritDoc}
76 */
77 @Override
78 public void setBoolean(String key, boolean value)
79 {
80 throw new UnsupportedOperationException();
81 }
82
83 /**
84 * {@inheritDoc}
85 */
86 @Override
87 public void setBoolean(COSName key, boolean value)
88 {
89 throw new UnsupportedOperationException();
90 }
91
92 /**
93 * {@inheritDoc}
94 */
95 @Override
96 public void setItem(String key, COSBase value)
97 {
98 throw new UnsupportedOperationException();
99 }
100
101 /**
102 * {@inheritDoc}
103 */
104 @Override
105 public void setName(String key, String value)
106 {
107 throw new UnsupportedOperationException();
108 }
109
110 /**
111 * {@inheritDoc}
112 */
113 @Override
114 public void setName(COSName key, String value)
115 {
116 throw new UnsupportedOperationException();
117 }
118
119 /**
120 * {@inheritDoc}
121 */
122 @Override
123 public void setDate(String key, Calendar date)
124 {
125 throw new UnsupportedOperationException();
126 }
127
128 /**
129 * {@inheritDoc}
130 */
131 @Override
132 public void setDate(COSName key, Calendar date)
133 {
134 throw new UnsupportedOperationException();
135 }
136
137 /**
138 * {@inheritDoc}
139 */
140 @Override
141 public void setEmbeddedDate(String embedded, String key, Calendar date)
142 {
143 throw new UnsupportedOperationException();
144 }
145
146 /**
147 * {@inheritDoc}
148 */
149 @Override
150 public void setEmbeddedDate(String embedded, COSName key, Calendar date)
151 {
152 throw new UnsupportedOperationException();
153 }
154
155 /**
156 * {@inheritDoc}
157 */
158 @Override
159 public void setString(String key, String value)
160 {
161 throw new UnsupportedOperationException();
162 }
163
164 /**
165 * {@inheritDoc}
166 */
167 @Override
168 public void setString(COSName key, String value)
169 {
170 throw new UnsupportedOperationException();
171 }
172
173 /**
174 * {@inheritDoc}
175 */
176 @Override
177 public void setEmbeddedString(String embedded, String key, String value)
178 {
179 throw new UnsupportedOperationException();
180 }
181
182 /**
183 * {@inheritDoc}
184 */
185 @Override
186 public void setEmbeddedString(String embedded, COSName key, String value)
187 {
188 throw new UnsupportedOperationException();
189 }
190
191 /**
192 * {@inheritDoc}
193 */
194 @Override
195 public void setInt(String key, int value)
196 {
197 throw new UnsupportedOperationException();
198 }
199
200 /**
201 * {@inheritDoc}
202 */
203 @Override
204 public void setInt(COSName key, int value)
205 {
206 throw new UnsupportedOperationException();
207 }
208
209 /**
210 * {@inheritDoc}
211 */
212 @Override
213 public void setLong(String key, long value)
214 {
215 throw new UnsupportedOperationException();
216 }
217
218 /**
219 * {@inheritDoc}
220 */
221 @Override
222 public void setLong(COSName key, long value)
223 {
224 throw new UnsupportedOperationException();
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 @Override
231 public void setEmbeddedInt(String embeddedDictionary, String key, int value)
232 {
233 throw new UnsupportedOperationException();
234 }
235
236 /**
237 * {@inheritDoc}
238 */
239 @Override
240 public void setEmbeddedInt(String embeddedDictionary, COSName key, int value)
241 {
242 throw new UnsupportedOperationException();
243 }
244
245 /**
246 * {@inheritDoc}
247 */
248 @Override
249 public void setFloat(String key, float value)
250 {
251 throw new UnsupportedOperationException();
252 }
253
254 /**
255 * {@inheritDoc}
256 */
257 @Override
258 public void setFloat(COSName key, float value)
259 {
260 throw new UnsupportedOperationException();
261 }
262
263 /**
264 * {@inheritDoc}
265 */
266 @Override
267 public void removeItem(COSName key)
268 {
269 throw new UnsupportedOperationException();
270 }
271
272 /**
273 * {@inheritDoc}
274 */
275 @Override
276 public void addAll(COSDictionary dic)
277 {
278 throw new UnsupportedOperationException();
33 items = Collections.unmodifiableMap(dict.items);
27934 }
28035
28136 /**
29146 * {@inheritDoc}
29247 */
29348 @Override
294 public void setFlag(COSName field, int bitFlag, boolean value)
295 {
296 throw new UnsupportedOperationException();
297 }
298
299 /**
300 * {@inheritDoc}
301 */
302 @Override
30349 public void setNeedToBeUpdated(boolean flag)
30450 {
30551 throw new UnsupportedOperationException();
3030 import javax.imageio.metadata.IIOMetadata;
3131 import javax.imageio.metadata.IIOMetadataNode;
3232 import javax.imageio.stream.ImageInputStream;
33 import javax.xml.xpath.XPathExpression;
34 import javax.xml.xpath.XPathExpressionException;
35 import javax.xml.xpath.XPathFactory;
3336
3437 import org.apache.commons.logging.Log;
3538 import org.apache.commons.logging.LogFactory;
4952
5053 private static final int POS_TRANSFORM = 11;
5154 private static final String ADOBE = "Adobe";
55
56 private static XPathExpression xPathExpression;
57
58 static
59 {
60 try
61 {
62 xPathExpression = XPathFactory.newInstance().newXPath().compile("Chroma/ColorSpaceType/@name");
63 }
64 catch (XPathExpressionException ex)
65 {
66 // shouldn't happen unless you changed the expression
67 LOG.error(ex.getMessage(), ex);
68 }
69 }
5270
5371 @Override
5472 public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary
179197 Element adobe = (Element) app14AdobeNodeList.item(0);
180198 return Integer.parseInt(adobe.getAttribute("transform"));
181199 }
200
201 // PDFBOX-5488: plan B: use ColorSpaceType from the other metadata tree.
202 try
203 {
204 String value = xPathExpression.evaluate(metadata.getAsTree("javax_imageio_1.0"));
205 if ("YCbCr".equals(value))
206 {
207 return 1;
208 }
209 if ("YCCK".equals(value))
210 {
211 return 2;
212 }
213 }
214 catch (XPathExpressionException ex)
215 {
216 return 0;
217 }
182218 return 0;
183219 }
184220
201237 // match
202238 a = 0;
203239 long afterAdobePos = iis.getStreamPosition();
204 iis.seek(iis.getStreamPosition() - 9);
240 iis.seek(afterAdobePos - 9);
205241 int tag = iis.readUnsignedShort();
206242 if (tag != 0xFFEE)
207243 {
125125 COSBase objAtIndex = array.getObject(index);
126126 if (objAtIndex instanceof COSDictionary)
127127 {
128 return (COSDictionary)array.getObject(index);
128 return (COSDictionary) objAtIndex;
129129 }
130130 }
131131 }
286286 for (Map.Entry<COSName, COSBase> entry : orgDict.entrySet())
287287 {
288288 COSName key = entry.getKey();
289 if (!filter.contains(key.getName()))
289 if (filter.contains(key.getName()))
290290 {
291 continue;
291 targetDict.setItem(key, cloner.cloneForNewDocument(entry.getValue()));
292292 }
293 targetDict.setItem(key, cloner.cloneForNewDocument(entry.getValue()));
294293 }
295294 }
296295
2323 import java.io.OutputStream;
2424 import java.math.BigDecimal;
2525 import java.util.ArrayList;
26 import java.util.Collections;
2627 import java.util.HashMap;
2728 import java.util.HashSet;
2829 import java.util.List;
9697 /**
9798 * This will add overlays to a document.
9899 *
99 * @param specificPageOverlayFile Optional map of overlay files for specific pages. The page
100 * numbers are 1-based. The map must be empty (but not null) if no specific mappings are used.
100 * @param specificPageOverlayFile Optional map of overlay files of which the first page will be
101 * used for specific pages of the input document. The page numbers are 1-based. The map must be
102 * empty (but not null) if no specific mappings are used.
101103 *
102104 * @return The modified input PDF document, which has to be saved and closed by the caller. If
103105 * the input document was passed by {@link #setInputPDF(PDDocument) setInputPDF(PDDocument)}
107109 */
108110 public PDDocument overlay(Map<Integer, String> specificPageOverlayFile) throws IOException
109111 {
110 Map<String, PDDocument> loadedDocuments = new HashMap<String, PDDocument>();
111 Map<PDDocument, LayoutPage> layouts = new HashMap<PDDocument, LayoutPage>();
112 Map<String, LayoutPage> layouts = new HashMap<String, LayoutPage>();
113 String path;
112114 loadPDFs();
113115 for (Map.Entry<Integer, String> e : specificPageOverlayFile.entrySet())
114116 {
115 PDDocument doc = loadedDocuments.get(e.getValue());
116 if (doc == null)
117 path = e.getValue();
118 LayoutPage layoutPage = layouts.get(path);
119 if (layoutPage == null)
117120 {
118 doc = loadPDF(e.getValue());
119 loadedDocuments.put(e.getValue(), doc);
120 layouts.put(doc, getLayoutPage(doc));
121 PDDocument doc = loadPDF(path);
122 layouts.put(path, getLayoutPage(doc));
121123 openDocuments.add(doc);
122124 }
123 specificPageOverlayPage.put(e.getKey(), layouts.get(doc));
125 specificPageOverlayPage.put(e.getKey(), layoutPage);
124126 }
125127 processPages(inputPDFDocument);
126128 return inputPDFDocument;
284286 }
285287 }
286288
289 /**
290 * Create a LayoutPage object from the first page of the given document.
291 *
292 * @param doc
293 * @return
294 * @throws IOException
295 */
287296 private LayoutPage getLayoutPage(PDDocument doc) throws IOException
288297 {
289298 return createLayoutPage(doc.getPage(0));
290299 }
291300
301 /**
302 * Create a LayoutPage object from given PDPage object.
303 *
304 * @return
305 * @throws IOException
306 */
292307 private LayoutPage createLayoutPage(PDPage page) throws IOException
293308 {
294309 COSBase contents = page.getCOSObject().getDictionaryObject(COSName.CONTENTS);
333348 // get the content streams as a list
334349 private List<COSStream> createContentStreamList(COSBase contents) throws IOException
335350 {
351 if (contents == null)
352 {
353 return Collections.emptyList();
354 }
355 if (contents instanceof COSStream)
356 {
357 return Collections.singletonList((COSStream) contents);
358 }
359
336360 List<COSStream> contentStreams = new ArrayList<COSStream>();
337 if (contents == null)
338 {
339 return contentStreams;
340 }
341 else if (contents instanceof COSStream)
342 {
343 contentStreams.add((COSStream) contents);
344 }
345 else if (contents instanceof COSArray)
361 if (contents instanceof COSArray)
346362 {
347363 for (COSBase item : (COSArray) contents)
348364 {
2323 import java.util.List;
2424 import java.util.Map;
2525 import java.util.Set;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
2628 import org.apache.pdfbox.cos.COSArray;
2729 import org.apache.pdfbox.cos.COSBase;
2830 import org.apache.pdfbox.cos.COSDictionary;
4244 */
4345 public class PDFCloneUtility
4446 {
47 private static final Log LOG = LogFactory.getLog(PDFCloneUtility.class);
48
4549 private final PDDocument destination;
4650 private final Map<Object,COSBase> clonedVersion = new HashMap<Object,COSBase>();
4751 private final Set<COSBase> clonedValues = new HashSet<COSBase>();
114118 for( int i=0; i<array.size(); i++ )
115119 {
116120 COSBase value = array.get(i);
117 checkForRecursion(base, value);
118 newArray.add(cloneForNewDocument(value));
121 if (hasSelfReference(base, value))
122 {
123 newArray.add(newArray);
124 }
125 else
126 {
127 newArray.add(cloneForNewDocument(value));
128 }
119129 }
120130 retval = newArray;
121131 }
132142 for( Map.Entry<COSName, COSBase> entry : originalStream.entrySet() )
133143 {
134144 COSBase value = entry.getValue();
135 checkForRecursion(base, value);
136 stream.setItem(entry.getKey(), cloneForNewDocument(value));
145 if (hasSelfReference(base, value))
146 {
147 stream.setItem(entry.getKey(), stream);
148 }
149 else
150 {
151 stream.setItem(entry.getKey(), cloneForNewDocument(value));
152 }
137153 }
138154 retval = stream;
139155 }
145161 for( Map.Entry<COSName, COSBase> entry : dic.entrySet() )
146162 {
147163 COSBase value = entry.getValue();
148 checkForRecursion(base, value);
149 ((COSDictionary) retval).setItem(entry.getKey(), cloneForNewDocument(value));
164 if (hasSelfReference(base, value))
165 {
166 ((COSDictionary) retval).setItem(entry.getKey(), retval);
167 }
168 else
169 {
170 ((COSDictionary) retval).setItem(entry.getKey(), cloneForNewDocument(value));
171 }
150172 }
151173 }
152174 else
264286 *
265287 * @param parent COSArray or COSDictionary
266288 * @param value an element
267 * @throws IOException if value is an object reference to the parent
268289 */
269 private void checkForRecursion(Object parent, COSBase value) throws IOException
290 private boolean hasSelfReference(Object parent, COSBase value)
270291 {
271292 if (value instanceof COSObject)
272293 {
273294 COSBase actual = ((COSObject) value).getObject();
274295 if (actual == parent)
275296 {
276 throw new IOException("Loop within object " + value);
297 COSObject cosObj = ((COSObject) value);
298 LOG.warn(parent.getClass().getSimpleName() + " object has a reference to itself: " +
299 cosObj.getObjectNumber() + " " + cosObj.getGenerationNumber() + " R");
300 return true;
277301 }
278302 }
303 return false;
279304 }
280305 }
4949 import org.apache.pdfbox.pdmodel.PDDocumentNameDestinationDictionary;
5050 import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
5151 import org.apache.pdfbox.pdmodel.PDPage;
52 import org.apache.pdfbox.pdmodel.PDPageTree;
5253 import org.apache.pdfbox.pdmodel.PDResources;
5354 import org.apache.pdfbox.pdmodel.PDStructureElementNameTreeNode;
5455 import org.apache.pdfbox.pdmodel.PageMode;
357358 {
358359 destination = new PDDocument(memUsageSetting);
359360 PDFCloneUtility cloner = new PDFCloneUtility(destination);
361 PDPageTree destinationPageTree = destination.getPages(); // cache PageTree
360362
361363 for (Object sourceObject : sources)
362364 {
388390 {
389391 newPage.setResources(new PDResources());
390392 }
391 destination.addPage(newPage);
393 destinationPageTree.add(newPage);
392394 }
393395 }
394396 finally
794796
795797 Map<COSDictionary, COSDictionary> objMapping = new HashMap<COSDictionary, COSDictionary>();
796798 int pageIndex = 0;
799 PDPageTree destinationPageTree = destination.getPages(); // cache PageTree
797800 for (PDPage page : srcCatalog.getPages())
798801 {
799802 PDPage newPage = new PDPage((COSDictionary) cloner.cloneForNewDocument(page.getCOSObject()));
833836 }
834837 // TODO update mapping for XObjects
835838 }
836 destination.addPage(newPage);
839 destinationPageTree.add(newPage);
837840
838841 if (pageIndex == pageIndexOpenActionDest)
839842 {
175175 long objNumber = ((COSInteger) value).longValue();
176176 if (objNumber <= 0)
177177 {
178 LOG.error("invalid object number value =" + objNumber + " at offset " + numOffset);
178 LOG.warn("invalid object number value =" + objNumber + " at offset " + numOffset);
179179 return COSNull.NULL;
180180 }
181181 int genNumber = ((COSInteger) generationNumber).intValue();
777777 String string;
778778 if (isValidUTF8(bytes))
779779 {
780 string = new String(buffer.toByteArray(), Charsets.UTF_8);
780 string = new String(bytes, Charsets.UTF_8);
781781 }
782782 else
783783 {
784784 // some malformed PDFs don't use UTF-8 see PDFBOX-3347
785 string = new String(buffer.toByteArray(), Charsets.WINDOWS_1252);
785 string = new String(bytes, Charsets.WINDOWS_1252);
786786 }
787787 return COSName.getPDFName(string);
788788 }
11031103 {
11041104 if (seqSource.isEOF())
11051105 {
1106 throw new IOException( "Error: End-of-File, expected line");
1106 throw new IOException( "Error: End-of-File, expected line at offset " +
1107 seqSource.getPosition());
11071108 }
11081109
11091110 StringBuilder buffer = new StringBuilder( 11 );
24722472 {
24732473 return false;
24742474 }
2475 if (!dictionary.containsKey(COSName.MOD_DATE) && !dictionary.containsKey(COSName.TITLE)
2476 && !dictionary.containsKey(COSName.AUTHOR)
2477 && !dictionary.containsKey(COSName.SUBJECT)
2478 && !dictionary.containsKey(COSName.KEYWORDS)
2479 && !dictionary.containsKey(COSName.CREATOR)
2480 && !dictionary.containsKey(COSName.PRODUCER)
2481 && !dictionary.containsKey(COSName.CREATION_DATE))
2482 {
2483 return false;
2484 }
2485 return true;
2475 return dictionary.containsKey(COSName.MOD_DATE) || dictionary.containsKey(COSName.TITLE)
2476 || dictionary.containsKey(COSName.AUTHOR)
2477 || dictionary.containsKey(COSName.SUBJECT)
2478 || dictionary.containsKey(COSName.KEYWORDS)
2479 || dictionary.containsKey(COSName.CREATOR)
2480 || dictionary.containsKey(COSName.PRODUCER)
2481 || dictionary.containsKey(COSName.CREATION_DATE);
24862482 }
24872483
24882484 /**
9494 * Constructor.
9595 *
9696 * @param bytes the bytes to parse.
97 * @throws IOException If there is an error initializing the stream.
98 */
99 public PDFStreamParser(byte[] bytes) throws IOException
97 */
98 public PDFStreamParser(byte[] bytes)
10099 {
101100 super(new RandomAccessSource(new RandomAccessBuffer(bytes)));
102101 }
403403 {
404404 acroForm.getCOSObject().setItem(COSName.FIELDS, new COSArray());
405405 }
406 PDAnnotationWidget firstWidget;
406407 if (signatureField == null)
407408 {
408409 signatureField = new PDSignatureField(acroForm);
409410 // append the signature object
410411 signatureField.setValue(sigObject);
412 firstWidget = signatureField.getWidgets().get(0);
411413 // backward linking
412 signatureField.getWidgets().get(0).setPage(page);
414 firstWidget.setPage(page);
413415 }
414416 else
415417 {
418 firstWidget = signatureField.getWidgets().get(0);
416419 sigObject.getCOSObject().setNeedToBeUpdated(true);
417420 }
418421
422425 // to conform PDF/A-1 requirement:
423426 // The /F key's Print flag bit shall be set to 1 and
424427 // its Hidden, Invisible and NoView flag bits shall be set to 0
425 signatureField.getWidgets().get(0).setPrinted(true);
428 firstWidget.setPrinted(true);
426429 // This may be troublesome if several form fields are signed,
427430 // see thread from PDFBox users mailing list 17.2.2021 - 19.2.2021
428431 // https://mail-archives.apache.org/mod_mbox/pdfbox-users/202102.mbox/thread
450453 // Distinction of case for visual and non-visual signature
451454 if (visualSignature == null)
452455 {
453 prepareNonVisibleSignature(signatureField);
456 prepareNonVisibleSignature(firstWidget);
454457 return;
455458 }
456459
457 prepareVisibleSignature(signatureField, acroForm, visualSignature);
460 prepareVisibleSignature(firstWidget, acroForm, visualSignature);
458461
459462 // Create Annotation / Field for signature
460463 List<PDAnnotation> annotations = page.getAnnotations();
466469
467470 // Get the annotations of the page and append the signature-annotation to it
468471 // take care that page and acroforms do not share the same array (if so, we don't need to add it twice)
469 if (!(annotations instanceof COSArrayList &&
472 if (!(checkFields &&
473 annotations instanceof COSArrayList &&
470474 acroFormFields instanceof COSArrayList &&
471 ((COSArrayList<PDAnnotation>) annotations).toList().
472 equals(((COSArrayList<PDField>) acroFormFields).toList()) &&
473 checkFields))
474 {
475 PDAnnotationWidget widget = signatureField.getWidgets().get(0);
475 ((COSArrayList) annotations).toList().
476 equals(((COSArrayList) acroFormFields).toList())))
477 {
476478 // use check to prevent the annotation widget from appearing twice
477 if (checkSignatureAnnotation(annotations, widget))
478 {
479 widget.getCOSObject().setNeedToBeUpdated(true);
479 if (checkSignatureAnnotation(annotations, firstWidget))
480 {
481 firstWidget.getCOSObject().setNeedToBeUpdated(true);
480482 }
481483 else
482484 {
483 annotations.add(widget);
485 annotations.add(firstWidget);
484486 }
485487 }
486488 page.getCOSObject().setNeedToBeUpdated(true);
552554 return false;
553555 }
554556
555 private void prepareVisibleSignature(PDSignatureField signatureField, PDAcroForm acroForm,
557 private void prepareVisibleSignature(PDAnnotationWidget firstWidget, PDAcroForm acroForm,
556558 COSDocument visualSignature)
557559 {
558560 // Obtain visual signature object
574576 COSBase type = cosBaseDict.getDictionaryObject(COSName.TYPE);
575577 if (annotNotFound && COSName.ANNOT.equals(type))
576578 {
577 assignSignatureRectangle(signatureField, cosBaseDict);
579 assignSignatureRectangle(firstWidget, cosBaseDict);
578580 annotNotFound = false;
579581 }
580582
583585 COSBase apDict = cosBaseDict.getDictionaryObject(COSName.AP);
584586 if (sigFieldNotFound && COSName.SIG.equals(fieldType) && apDict instanceof COSDictionary)
585587 {
586 assignAppearanceDictionary(signatureField, (COSDictionary) apDict);
588 assignAppearanceDictionary(firstWidget, (COSDictionary) apDict);
587589 assignAcroFormDefaultResource(acroForm, cosBaseDict);
588590 sigFieldNotFound = false;
589591 }
596598 }
597599 }
598600
599 private void assignSignatureRectangle(PDSignatureField signatureField, COSDictionary annotDict)
601 private void assignSignatureRectangle(PDAnnotationWidget firstWidget, COSDictionary annotDict)
600602 {
601603 // Read and set the rectangle for visual signature
602 PDRectangle existingRectangle = signatureField.getWidgets().get(0).getRectangle();
604 PDRectangle existingRectangle = firstWidget.getRectangle();
603605
604606 //in case of an existing field keep the original rect
605607 if (existingRectangle == null || existingRectangle.getCOSArray().size() != 4)
606608 {
607609 COSArray rectArray = (COSArray) annotDict.getDictionaryObject(COSName.RECT);
608610 PDRectangle rect = new PDRectangle(rectArray);
609 signatureField.getWidgets().get(0).setRectangle(rect);
610 }
611 }
612
613 private void assignAppearanceDictionary(PDSignatureField signatureField, COSDictionary apDict)
611 firstWidget.setRectangle(rect);
612 }
613 }
614
615 private void assignAppearanceDictionary(PDAnnotationWidget firstWidget, COSDictionary apDict)
614616 {
615617 // read and set Appearance Dictionary
616618 PDAppearanceDictionary ap = new PDAppearanceDictionary(apDict);
617619 apDict.setDirect(true);
618 signatureField.getWidgets().get(0).setAppearance(ap);
620 firstWidget.setAppearance(ap);
619621 }
620622
621623 private void assignAcroFormDefaultResource(PDAcroForm acroForm, COSDictionary newDict)
647649 }
648650 }
649651
650 private void prepareNonVisibleSignature(PDSignatureField signatureField)
652 private void prepareNonVisibleSignature(PDAnnotationWidget firstWidget)
651653 {
652654 // "Signature fields that are not intended to be visible shall
653655 // have an annotation rectangle that has zero height and width."
654656 // Set rectangle for non-visual signature to rectangle array [ 0 0 0 0 ]
655 signatureField.getWidgets().get(0).setRectangle(new PDRectangle());
657 firstWidget.setRectangle(new PDRectangle());
656658
657659 // The visual appearance must also exist for an invisible signature but may be empty.
658660 PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary();
659661 PDAppearanceStream appearanceStream = new PDAppearanceStream(this);
660662 appearanceStream.setBBox(new PDRectangle());
661663 appearanceDictionary.setNormalAppearance(appearanceStream);
662 signatureField.getWidgets().get(0).setAppearance(appearanceDictionary);
664 firstWidget.setAppearance(appearanceDictionary);
663665 }
664666
665667 /**
139139
140140 if (cachedAcroForm == null)
141141 {
142 COSDictionary dict = (COSDictionary)root.getDictionaryObject(COSName.ACRO_FORM);
142 COSDictionary dict = root.getCOSDictionary(COSName.ACRO_FORM);
143143 cachedAcroForm = dict == null ? null : new PDAcroForm(document, dict);
144144 }
145145 return cachedAcroForm;
222222 array = new COSArray();
223223 root.setItem(COSName.THREADS, array);
224224 }
225 List<PDThread> pdObjects = new ArrayList<PDThread>();
225 List<PDThread> pdObjects = new ArrayList<PDThread>(array.size());
226226 for (int i = 0; i < array.size(); i++)
227227 {
228228 pdObjects.add(new PDThread((COSDictionary)array.getObject(i)));
509509 public PageLayout getPageLayout()
510510 {
511511 String mode = root.getNameAsString(COSName.PAGE_LAYOUT);
512 if (mode != null)
513 {
514 return PageLayout.fromString(mode);
515 }
516 else
517 {
518 return PageLayout.SINGLE_PAGE;
519 }
512 if (mode != null && !mode.isEmpty())
513 {
514 try
515 {
516 return PageLayout.fromString(mode);
517 }
518 catch (IllegalArgumentException e)
519 {
520 LOG.warn("Invalid PageLayout used '" + mode + "' - returning PageLayout.SINGLE_PAGE", e);
521 }
522 }
523 return PageLayout.SINGLE_PAGE;
520524 }
521525
522526 /**
278278 {
279279 mediaBox = new PDRectangle((COSArray) base);
280280 }
281 }
282 if (mediaBox == null)
283 {
284 LOG.debug("Can't find MediaBox, will use U.S. Letter");
285 mediaBox = PDRectangle.LETTER;
281 else
282 {
283 LOG.debug("Can't find MediaBox, will use U.S. Letter");
284 mediaBox = PDRectangle.LETTER;
285 }
286286 }
287287 return mediaBox;
288288 }
466466 */
467467 private COSObject getIndirect(COSName kind, COSName name)
468468 {
469 COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind);
469 COSDictionary dict = resources.getCOSDictionary(kind);
470470 if (dict == null)
471471 {
472472 return null;
485485 */
486486 private COSBase get(COSName kind, COSName name)
487487 {
488 COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind);
488 COSDictionary dict = resources.getCOSDictionary(kind);
489489 if (dict == null)
490490 {
491491 return null;
570570 */
571571 private Iterable<COSName> getNames(COSName kind)
572572 {
573 COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind);
573 COSDictionary dict = resources.getCOSDictionary(kind);
574574 if (dict == null)
575575 {
576576 return Collections.emptySet();
700700 private COSName add(COSName kind, String prefix, COSObjectable object)
701701 {
702702 // return the existing key if the item exists already
703 COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind);
703 COSDictionary dict = resources.getCOSDictionary(kind);
704704 if (dict != null && dict.containsValue(object.getCOSObject()))
705705 {
706706 return dict.getKeyForValue(object.getCOSObject());
731731 */
732732 private COSName createKey(COSName kind, String prefix)
733733 {
734 COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind);
734 COSDictionary dict = resources.getCOSDictionary(kind);
735735 if (dict == null)
736736 {
737737 return COSName.getPDFName(prefix + 1);
754754 */
755755 private void put(COSName kind, COSName name, COSObjectable object)
756756 {
757 COSDictionary dict = (COSDictionary)resources.getDictionaryObject(kind);
757 COSDictionary dict = resources.getCOSDictionary(kind);
758758 if (dict == null)
759759 {
760760 dict = new COSDictionary();
511511 {
512512 PDMetadata retval = null;
513513 COSBase mdStream = stream.getDictionaryObject(COSName.METADATA);
514 if (mdStream != null)
515 {
516 if (mdStream instanceof COSStream)
517 {
518 retval = new PDMetadata((COSStream) mdStream);
519 }
520 else if (mdStream instanceof COSNull)
521 {
522 // null is authorized
523 }
524 else
525 {
526 throw new IllegalStateException(
527 "Expected a COSStream but was a "
528 + mdStream.getClass().getSimpleName());
529 }
514 if (mdStream instanceof COSStream)
515 {
516 retval = new PDMetadata((COSStream) mdStream);
517 }
518 else if (mdStream instanceof COSNull)
519 {
520 // null is authorized
521 }
522 else if (mdStream != null)
523 {
524 throw new IllegalStateException("Expected a COSStream but was a "
525 + mdStream.getClass().getSimpleName());
530526 }
531527 return retval;
532528 }
154154 for (int i = 0; i < sizeValuesSize; i++)
155155 {
156156 encode.add(COSInteger.ZERO);
157 encode.add(COSInteger.get(sizeValues.getInt(i) - 1));
157 encode.add(COSInteger.get(sizeValues.getInt(i) - 1L));
158158 }
159159 }
160160 }
112112 break;
113113 }
114114 }
115 if (function == null)
116 {
117 throw new IOException("partition not found in type 3 function");
118 }
115 }
116 if (function == null)
117 {
118 throw new IOException("partition not found in type 3 function");
119119 }
120120 float[] functionValues = new float[]{x};
121121 // calculate the output values using the chosen function
1515 */
1616 package org.apache.pdfbox.pdmodel.documentinterchange.markedcontent;
1717
18 import org.apache.pdfbox.cos.COSBase;
1819 import org.apache.pdfbox.cos.COSDictionary;
1920 import org.apache.pdfbox.cos.COSName;
2021 import org.apache.pdfbox.pdmodel.common.COSObjectable;
3839 */
3940 public static PDPropertyList create(COSDictionary dict)
4041 {
41 if (COSName.OCG.equals(dict.getItem(COSName.TYPE)))
42 COSBase item = dict.getItem(COSName.TYPE);
43 if (COSName.OCG.equals(item))
4244 {
4345 return new PDOptionalContentGroup(dict);
4446 }
45 else if (COSName.OCMD.equals(dict.getItem(COSName.TYPE)))
47 else if (COSName.OCMD.equals(item))
4648 {
4749 return new PDOptionalContentMembershipDictionary(dict);
4850 }
5656 private static final int FILL_IN_FORM_BIT = 9;
5757 private static final int EXTRACT_FOR_ACCESSIBILITY_BIT = 10;
5858 private static final int ASSEMBLE_DOCUMENT_BIT = 11;
59 private static final int DEGRADED_PRINT_BIT = 12;
59 private static final int FAITHFUL_PRINT_BIT = 12;
6060
6161 private int bytes;
6262
139139 && this.canModify()
140140 && this.canModifyAnnotations()
141141 && this.canPrint()
142 && this.canPrintDegraded()
142 && this.canPrintFaithful()
143143 );
144144 }
145145
159159 ret.setCanModify(true);
160160 ret.setCanModifyAnnotations(true);
161161 ret.setCanPrint(true);
162 ret.setCanPrintDegraded(true);
162 ret.setCanPrintFaithful(true);
163163 return ret;
164164 }
165165
389389 }
390390
391391 /**
392 * This will tell if the user can print the document in a degraded format.
393 *
394 * @return true If supplied with the user password they are allowed to print the
395 * document in a degraded format.
396 */
392 * This will tell if the user can print the document in a faithful format or in a degraded
393 * format (if print is enabled).
394 *
395 * @return true If supplied with the user password they are allowed to print the document in a
396 * faithful format.
397 *
398 * @deprecated use {@link #canPrintFaithful() }
399 */
400 @Deprecated
397401 public boolean canPrintDegraded()
398402 {
399 return isPermissionBitOn( DEGRADED_PRINT_BIT );
400 }
401
402 /**
403 * Set if the user can print the document in a degraded format.
404 * <p>
405 * This method will have no effect if the object is in read only mode.
406 *
407 * @param canPrintDegraded A boolean determining if the user can print the
408 * document in a degraded format.
409 */
410 public void setCanPrintDegraded( boolean canPrintDegraded )
411 {
412 if(!readOnly)
413 {
414 setPermissionBit( DEGRADED_PRINT_BIT, canPrintDegraded );
403 return isPermissionBitOn(FAITHFUL_PRINT_BIT);
404 }
405
406 /**
407 * Set if the user can print the document in a faithful format or in a degraded format (if print
408 * is enabled). The PDF version must be 1.5 or higher.
409 * <p>
410 * This method will have no effect if the object is in read only mode.
411 *
412 * @param canPrintFaithful A boolean determining if the user can print the document in a
413 * faithful format.
414 *
415 * @deprecated use {@link #setCanPrintFaithful(boolean)}
416 */
417 @Deprecated
418 public void setCanPrintDegraded(boolean canPrintFaithful)
419 {
420 if (!readOnly)
421 {
422 setPermissionBit(FAITHFUL_PRINT_BIT, canPrintFaithful);
423 }
424 }
425
426 /**
427 * This will tell if the user can print the document in a faithful format or in a degraded
428 * format (if print is enabled).
429 *
430 * @return true If supplied with the user password they are allowed to print the document in a
431 * degraded format.
432 */
433 public boolean canPrintFaithful()
434 {
435 return isPermissionBitOn(FAITHFUL_PRINT_BIT);
436 }
437
438 /**
439 * Set if the user can print the document in a faithful format or in a degraded format (if print
440 * is enabled). The PDF version must be 1.5 or higher.
441 * <p>
442 * This method will have no effect if the object is in read only mode.
443 *
444 * @param canPrintFaithful A boolean determining if the user can print the document in a
445 * degraded format.
446 */
447 public void setCanPrintFaithful(boolean canPrintFaithful)
448 {
449 if (!readOnly)
450 {
451 setPermissionBit(FAITHFUL_PRINT_BIT, canPrintFaithful);
415452 }
416453 }
417454
418455 /**
419456 * Locks the access permission read only (ie, the setters will have no effects).
420457 * After that, the object cannot be unlocked.
421 * This method is used for the currentAccessPermssion of a document to avoid
458 * This method is used for the currentAccessPermission of a document to avoid
422459 * users to change access permission.
423460 */
424461 public void setReadOnly()
456493 {
457494 return true;
458495 }
459 return canPrintDegraded();
496 return canPrintFaithful();
460497 }
461498 }
597597 * @param objNum The object number.
598598 * @param genNum The object generation number.
599599 *
600 * @throws IOException If an error occurs writing the new string.
601 */
602 private void decryptString(COSString string, long objNum, long genNum) throws IOException
600 */
601 private void decryptString(COSString string, long objNum, long genNum)
603602 {
604603 // String encrypted with identity filter
605604 if (COSName.IDENTITY.equals(stringFilterName))
7171 String symbol = element.getAttribute("symbol");
7272 if (symbol != null && !symbol.isEmpty())
7373 {
74 setSymbol(element.getAttribute("symbol"));
74 setSymbol(symbol);
7575 }
7676 }
7777
176176 List<FDFField> retval = null;
177177 if (kids != null)
178178 {
179 List<FDFField> actuals = new ArrayList<FDFField>();
179 List<FDFField> actuals = new ArrayList<FDFField>(kids.size());
180180 for (int i = 0; i < kids.size(); i++)
181181 {
182182 actuals.add(new FDFField((COSDictionary) kids.getObject(i)));
138138 */
139139 public Map<String, PDActionJavaScript> getDoc()
140140 {
141 Map<String, PDActionJavaScript> map = new LinkedHashMap<String, PDActionJavaScript>();
142141 COSArray array = dictionary.getCOSArray(COSName.DOC);
143142 if (array == null)
144143 {
145144 return null;
146145 }
146 Map<String, PDActionJavaScript> map = new LinkedHashMap<String, PDActionJavaScript>();
147147 for (int i = 0; i + 1 < array.size(); i += 2)
148148 {
149149 String name = array.getName(i);
2020
2121 import java.io.IOException;
2222 import java.io.InputStream;
23 import java.util.Collections;
24 import java.util.HashMap;
23
2524 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
2626
2727 /**
2828 * CMap resource loader and cache.
2929 */
3030 final class CMapManager
3131 {
32 static Map<String, CMap> cMapCache =
33 Collections.synchronizedMap(new HashMap<String, CMap>());
32 private static final Map<String, CMap> CMAP_CACHE = new ConcurrentHashMap<String, CMap>();
3433
3534 private CMapManager()
3635 {
4544 */
4645 public static CMap getPredefinedCMap(String cMapName) throws IOException
4746 {
48 CMap cmap = cMapCache.get(cMapName);
47 CMap cmap = CMAP_CACHE.get(cMapName);
4948 if (cmap != null)
5049 {
5150 return cmap;
5453 CMap targetCmap = new CMapParser().parsePredefined(cMapName);
5554
5655 // limit the cache to predefined CMaps
57 cMapCache.put(targetCmap.getName(), targetCmap);
56 CMAP_CACHE.put(targetCmap.getName(), targetCmap);
5857 return targetCmap;
5958 }
6059
400400 private File getDiskCacheFile()
401401 {
402402 String path = System.getProperty("pdfbox.fontcache");
403 if (path == null || !new File(path).isDirectory() || !new File(path).canWrite())
403 if (isBadPath(path))
404404 {
405405 path = System.getProperty("user.home");
406 if (path == null || !new File(path).isDirectory() || !new File(path).canWrite())
406 if (isBadPath(path))
407407 {
408408 path = System.getProperty("java.io.tmpdir");
409409 }
410410 }
411411 return new File(path, ".pdfbox.cache");
412 }
413
414 private static boolean isBadPath(String path)
415 {
416 return path == null || !new File(path).isDirectory() || !new File(path).canWrite();
412417 }
413418
414419 /**
2525 import java.util.HashSet;
2626 import java.util.LinkedHashMap;
2727 import java.util.List;
28 import java.util.Locale;
2829 import java.util.Map;
2930 import java.util.PriorityQueue;
3031 import java.util.Set;
5657 FontMapperImpl()
5758 {
5859 // substitutes for standard 14 fonts
59 substitutes.put("Courier",
60 addSubstitutes("Courier",
6061 new ArrayList<String>(Arrays.asList("CourierNew", "CourierNewPSMT", "LiberationMono",
6162 "NimbusMonL-Regu")));
62 substitutes.put("Courier-Bold",
63 addSubstitutes("Courier-Bold",
6364 new ArrayList<String>(Arrays.asList("CourierNewPS-BoldMT", "CourierNew-Bold",
6465 "LiberationMono-Bold", "NimbusMonL-Bold")));
65 substitutes.put("Courier-Oblique",
66 addSubstitutes("Courier-Oblique",
6667 new ArrayList<String>(Arrays.asList("CourierNewPS-ItalicMT","CourierNew-Italic",
6768 "LiberationMono-Italic", "NimbusMonL-ReguObli")));
68 substitutes.put("Courier-BoldOblique",
69 new ArrayList<String>(Arrays.asList("CourierNewPS-BoldItalicMT",
69 addSubstitutes("Courier-BoldOblique",
70 new ArrayList<String>(Arrays.asList("CourierNewPS-BoldItalicMT",
7071 "CourierNew-BoldItalic", "LiberationMono-BoldItalic",
7172 "NimbusMonL-BoldObli")));
72 substitutes.put("Helvetica",
73 new ArrayList<String>(Arrays.asList("ArialMT", "Arial", "LiberationSans",
73 addSubstitutes("Helvetica",
74 new ArrayList<String>(Arrays.asList("ArialMT", "Arial", "LiberationSans",
7475 "NimbusSanL-Regu")));
75 substitutes.put("Helvetica-Bold",
76 addSubstitutes("Helvetica-Bold",
7677 new ArrayList<String>(Arrays.asList("Arial-BoldMT", "Arial-Bold",
7778 "LiberationSans-Bold", "NimbusSanL-Bold")));
78 substitutes.put("Helvetica-Oblique",
79 addSubstitutes("Helvetica-Oblique",
7980 new ArrayList<String>(Arrays.asList("Arial-ItalicMT", "Arial-Italic",
8081 "Helvetica-Italic", "LiberationSans-Italic", "NimbusSanL-ReguItal")));
81 substitutes.put("Helvetica-BoldOblique",
82 addSubstitutes("Helvetica-BoldOblique",
8283 new ArrayList<String>(Arrays.asList("Arial-BoldItalicMT", "Helvetica-BoldItalic",
8384 "LiberationSans-BoldItalic", "NimbusSanL-BoldItal")));
84 substitutes.put("Times-Roman",
85 addSubstitutes("Times-Roman",
8586 new ArrayList<String>(Arrays.asList("TimesNewRomanPSMT", "TimesNewRoman",
8687 "TimesNewRomanPS", "LiberationSerif", "NimbusRomNo9L-Regu")));
87 substitutes.put("Times-Bold",
88 addSubstitutes("Times-Bold",
8889 new ArrayList<String>(Arrays.asList("TimesNewRomanPS-BoldMT", "TimesNewRomanPS-Bold",
8990 "TimesNewRoman-Bold", "LiberationSerif-Bold",
9091 "NimbusRomNo9L-Medi")));
91 substitutes.put("Times-Italic",
92 new ArrayList<String>(Arrays.asList("TimesNewRomanPS-ItalicMT",
92 addSubstitutes("Times-Italic",
93 new ArrayList<String>(Arrays.asList("TimesNewRomanPS-ItalicMT",
9394 "TimesNewRomanPS-Italic", "TimesNewRoman-Italic", "LiberationSerif-Italic",
9495 "NimbusRomNo9L-ReguItal")));
95 substitutes.put("Times-BoldItalic",
96 new ArrayList<String>(Arrays.asList("TimesNewRomanPS-BoldItalicMT",
96 addSubstitutes("Times-BoldItalic",
97 new ArrayList<String>(Arrays.asList("TimesNewRomanPS-BoldItalicMT",
9798 "TimesNewRomanPS-BoldItalic", "TimesNewRoman-BoldItalic",
9899 "LiberationSerif-BoldItalic", "NimbusRomNo9L-MediItal")));
99 substitutes.put("Symbol",
100 addSubstitutes("Symbol",
100101 new ArrayList<String>(Arrays.asList("Symbol", "SymbolMT", "StandardSymL")));
101 substitutes.put("ZapfDingbats", new ArrayList<String>(
102 addSubstitutes("ZapfDingbats", new ArrayList<String>(
102103 Arrays.asList("ZapfDingbatsITCbyBT-Regular", "ZapfDingbatsITC", "Dingbats",
103104 "MS-Gothic")));
104105
106107 // these include names such as "Arial" and "TimesNewRoman"
107108 for (String baseName : Standard14Fonts.getNames())
108109 {
109 if (!substitutes.containsKey(baseName))
110 if (getSubstitutes(baseName).isEmpty())
110111 {
111112 String mappedName = Standard14Fonts.getMappedFontName(baseName);
112 substitutes.put(baseName, copySubstitutes(mappedName));
113 addSubstitutes(baseName, copySubstitutes(mappedName.toLowerCase(Locale.ENGLISH)));
113114 }
114115 }
115116
176177 {
177178 for (String name : getPostScriptNames(info.getPostScriptName()))
178179 {
179 map.put(name, info);
180 map.put(name.toLowerCase(Locale.ENGLISH), info);
180181 }
181182 }
182183 return map;
214215 */
215216 public void addSubstitute(String match, String replace)
216217 {
217 if (!substitutes.containsKey(match))
218 {
219 substitutes.put(match, new ArrayList<String>());
220 }
221 substitutes.get(match).add(replace);
218 String lowerCaseMatch = match.toLowerCase(Locale.ENGLISH);
219 if (!substitutes.containsKey(lowerCaseMatch))
220 {
221 substitutes.put(lowerCaseMatch, new ArrayList<String>());
222 }
223 substitutes.get(lowerCaseMatch).add(replace);
224 }
225
226 private void addSubstitutes(String match, List<String> replacements)
227 {
228 substitutes.put(match.toLowerCase(Locale.ENGLISH), replacements);
222229 }
223230
224231 /**
226233 */
227234 private List<String> getSubstitutes(String postScriptName)
228235 {
229 List<String> subs = substitutes.get(postScriptName.replace(" ", ""));
236 List<String> subs = substitutes.get(postScriptName.replace(" ", "").toLowerCase(Locale.ENGLISH));
230237 if (subs != null)
231238 {
232239 return subs;
475482 }
476483
477484 // look up the PostScript name
478 FontInfo info = fontInfoByName.get(postScriptName);
485 FontInfo info = fontInfoByName.get(postScriptName.toLowerCase(Locale.ENGLISH));
479486 if (info != null && info.getFormat() == format)
480487 {
481488 if (LOG.isDebugEnabled())
7979 COSArray wArray = (COSArray) wBase;
8080 int size = wArray.size();
8181 int counter = 0;
82 while (counter < size)
82 while (counter < size - 1)
8383 {
8484 COSBase firstCodeBase = wArray.getObject(counter++);
8585 if (!(firstCodeBase instanceof COSNumber))
110110 }
111111 else
112112 {
113 if (counter >= size)
114 {
115 LOG.warn("premature end of widths array");
116 break;
117 }
113118 COSBase secondCodeBase = next;
114119 COSBase rangeWidthBase = wArray.getObject(counter++);
115120 if (!(secondCodeBase instanceof COSNumber) || !(rangeWidthBase instanceof COSNumber))
2020 import java.io.ByteArrayOutputStream;
2121 import java.io.IOException;
2222 import java.io.InputStream;
23 import java.util.Collections;
24 import java.util.HashMap;
2523 import java.util.List;
2624 import java.util.Map;
2725 import java.util.Set;
28 import java.util.TreeSet;
26 import java.util.TreeMap;
2927
3028 import org.apache.commons.logging.Log;
3129 import org.apache.commons.logging.LogFactory;
104102 throws IOException
105103 {
106104 // build CID2GIDMap, because the content stream has been written with the old GIDs
107 Map<Integer, Integer> cidToGid = new HashMap<Integer, Integer>(gidToCid.size());
105 TreeMap<Integer, Integer> cidToGid = new TreeMap<Integer, Integer>();
108106 for (Map.Entry<Integer, Integer> entry : gidToCid.entrySet())
109107 {
110108 int newGID = entry.getKey();
235233 cidFont.setName(COSName.BASE_FONT, newName);
236234 }
237235
238 private void buildCIDToGIDMap(Map<Integer, Integer> cidToGid) throws IOException
239 {
240 ByteArrayOutputStream out = new ByteArrayOutputStream();
241 int cidMax = Collections.max(cidToGid.keySet());
236 private void buildCIDToGIDMap(TreeMap<Integer, Integer> cidToGid) throws IOException
237 {
238 int cidMax = cidToGid.lastKey();
239 byte[] buffer = new byte[cidMax * 2 + 2];
240 int bi = 0;
242241 for (int i = 0; i <= cidMax; i++)
243242 {
244 int gid;
245 if (cidToGid.containsKey(i))
246 {
247 gid = cidToGid.get(i);
248 }
249 else
250 {
251 gid = 0;
252 }
253 out.write(new byte[] { (byte)(gid >> 8 & 0xff), (byte)(gid & 0xff) });
254 }
255
256 InputStream input = new ByteArrayInputStream(out.toByteArray());
243 Integer gid = cidToGid.get(i);
244 if (gid != null)
245 {
246 buffer[bi] = (byte) (gid >> 8 & 0xff);
247 buffer[bi+1] = (byte) (gid & 0xff);
248 }
249 // else keep 0 initialization
250 bi += 2;
251 }
252
253 InputStream input = new ByteArrayInputStream(buffer);
257254 PDStream stream = new PDStream(document, input, COSName.FLATE_DECODE);
258255
259256 cidFont.setItem(COSName.CID_TO_GID_MAP, stream);
263260 * Builds the CIDSet entry, required by PDF/A. This lists all CIDs in the font, including those
264261 * that don't have a GID.
265262 */
266 private void buildCIDSet(Map<Integer, Integer> cidToGid) throws IOException
267 {
268 int cidMax = Collections.max(cidToGid.keySet());
263 private void buildCIDSet(TreeMap<Integer, Integer> cidToGid) throws IOException
264 {
265 int cidMax = cidToGid.lastKey();
269266 byte[] bytes = new byte[cidMax / 8 + 1];
270267 for (int cid = 0; cid <= cidMax; cid++)
271268 {
282279 /**
283280 * Builds widths with a custom CIDToGIDMap (for embedding font subset).
284281 */
285 private void buildWidths(Map<Integer, Integer> cidToGid) throws IOException
282 private void buildWidths(TreeMap<Integer, Integer> cidToGid) throws IOException
286283 {
287284 float scaling = 1000f / ttf.getHeader().getUnitsPerEm();
288285
290287 COSArray ws = new COSArray();
291288 int prev = Integer.MIN_VALUE;
292289 // Use a sorted list to get an optimal width array
293 Set<Integer> keys = new TreeSet<Integer>(cidToGid.keySet());
290 Set<Integer> keys = cidToGid.keySet();
291 HorizontalMetricsTable horizontalMetricsTable = ttf.getHorizontalMetrics();
294292 for (int cid : keys)
295293 {
296294 int gid = cidToGid.get(cid);
297 long width = Math.round(ttf.getHorizontalMetrics().getAdvanceWidth(gid) * scaling);
295 long width = Math.round(horizontalMetricsTable.getAdvanceWidth(gid) * scaling);
298296 if (width == 1000)
299297 {
300298 // skip default width
339337 /**
340338 * Builds vertical metrics with a custom CIDToGIDMap (for embedding font subset).
341339 */
342 private void buildVerticalMetrics(Map<Integer, Integer> cidToGid) throws IOException
340 private void buildVerticalMetrics(TreeMap<Integer, Integer> cidToGid) throws IOException
343341 {
344342 // The "vhea" and "vmtx" tables that specify vertical metrics shall never be used by a conforming
345343 // reader. The only way to specify vertical metrics in PDF shall be by means of the DW2 and W2
364362 COSArray w2 = new COSArray();
365363 int prev = Integer.MIN_VALUE;
366364 // Use a sorted list to get an optimal width array
367 Set<Integer> keys = new TreeSet<Integer>(cidToGid.keySet());
365 Set<Integer> keys = cidToGid.keySet();
368366 for (int cid : keys)
369367 {
370368 // Unlike buildWidths, we look up with cid (not gid) here because this is
404402 {
405403 int cidMax = ttf.getNumberOfGlyphs();
406404 int[] gidwidths = new int[cidMax * 2];
405 HorizontalMetricsTable horizontalMetricsTable = ttf.getHorizontalMetrics();
407406 for (int cid = 0; cid < cidMax; cid++)
408407 {
409408 gidwidths[cid * 2] = cid;
410 gidwidths[cid * 2 + 1] = ttf.getHorizontalMetrics().getAdvanceWidth(cid);
409 gidwidths[cid * 2 + 1] = horizontalMetricsTable.getAdvanceWidth(cid);
411410 }
412411
413412 cidFont.setItem(COSName.W, getWidths(gidwidths));
526525
527526 int cidMax = ttf.getNumberOfGlyphs();
528527 int[] gidMetrics = new int[cidMax * 4];
528 GlyphTable glyphTable = ttf.getGlyph();
529 VerticalMetricsTable verticalMetricsTable = ttf.getVerticalMetrics();
530 HorizontalMetricsTable horizontalMetricsTable = ttf.getHorizontalMetrics();
529531 for (int cid = 0; cid < cidMax; cid++)
530532 {
531 GlyphData glyph = ttf.getGlyph().getGlyph(cid);
533 GlyphData glyph = glyphTable.getGlyph(cid);
532534 if (glyph == null)
533535 {
534536 gidMetrics[cid * 4] = Integer.MIN_VALUE;
536538 else
537539 {
538540 gidMetrics[cid * 4] = cid;
539 gidMetrics[cid * 4 + 1] = ttf.getVerticalMetrics().getAdvanceHeight(cid);
540 gidMetrics[cid * 4 + 2] = ttf.getHorizontalMetrics().getAdvanceWidth(cid);
541 gidMetrics[cid * 4 + 3] = glyph.getYMaximum() + ttf.getVerticalMetrics().getTopSideBearing(cid);
541 gidMetrics[cid * 4 + 1] = verticalMetricsTable.getAdvanceHeight(cid);
542 gidMetrics[cid * 4 + 2] = horizontalMetricsTable.getAdvanceWidth(cid);
543 gidMetrics[cid * 4 + 3] = glyph.getYMaximum() + verticalMetricsTable.getTopSideBearing(cid);
542544 }
543545 }
544546
534534 {
535535 if (fontWidthOfSpace == -1f)
536536 {
537 COSBase toUnicode = dict.getDictionaryObject(COSName.TO_UNICODE);
538537 try
539538 {
540 if (toUnicode != null && toUnicodeCMap != null)
539 if (toUnicodeCMap != null && dict.containsKey(COSName.TO_UNICODE))
541540 {
542541 int spaceMapping = toUnicodeCMap.getSpaceMapping();
543542 if (spaceMapping > -1)
554553 if (fontWidthOfSpace <= 0)
555554 {
556555 fontWidthOfSpace = getWidthFromFont(32);
557 }
558 // use the average font width as fall back
559 if (fontWidthOfSpace <= 0)
560 {
561 fontWidthOfSpace = getAverageFontWidth();
556
557 // use the average font width as fall back
558 if (fontWidthOfSpace <= 0)
559 {
560 fontWidthOfSpace = getAverageFontWidth();
561 }
562562 }
563563 }
564564 catch (Exception e)
4646 */
4747 public int getFamilyClass()
4848 {
49 return bytes[0] << 8 | bytes[1];
49 return bytes[0] << 8 | (bytes[1] & 0xff);
5050 }
5151
5252 /**
3333 import org.apache.fontbox.util.BoundingBox;
3434 import org.apache.pdfbox.cos.COSDictionary;
3535 import org.apache.pdfbox.cos.COSName;
36 import org.apache.pdfbox.io.IOUtils;
3637 import org.apache.pdfbox.pdmodel.PDDocument;
3738 import org.apache.pdfbox.pdmodel.common.PDRectangle;
3839 import org.apache.pdfbox.pdmodel.common.PDStream;
4546 import org.apache.pdfbox.pdmodel.font.encoding.Type1Encoding;
4647 import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding;
4748
48
4949 import static org.apache.pdfbox.pdmodel.font.UniUtil.getUniNameOfCodePoint;
5050
5151 /**
191191 PDStream ff2Stream = fd.getFontFile2();
192192 if (ff2Stream != null)
193193 {
194 InputStream is = null;
194195 try
195196 {
196197 // embedded
197198 TTFParser ttfParser = new TTFParser(true);
198 ttfFont = ttfParser.parse(ff2Stream.createInputStream());
199 is = ff2Stream.createInputStream();
200 ttfFont = ttfParser.parse(is);
199201 }
200202 catch (IOException e)
201203 {
202204 LOG.warn("Could not read embedded TTF for font " + getBaseFont(), e);
203205 fontIsDamaged = true;
206 IOUtils.closeQuietly(is);
204207 }
205208 }
206209 }
686689 // PDFBOX-4755 / PDF.js #5501
687690 cmapWinUnicode = cmap;
688691 }
692 else if (CmapTable.PLATFORM_UNICODE == cmap.getPlatformId()
693 && CmapTable.ENCODING_UNICODE_2_0_BMP == cmap.getPlatformEncodingId())
694 {
695 // PDFBOX-5484
696 cmapWinUnicode = cmap;
697 }
689698 }
690699 }
691700 cmapInitialized = true;
213213 * @param closeTTF whether to close the ttf parameter after embedding. Must be true when the ttf
214214 * parameter was created in the load() method, false when the ttf parameter was passed to the
215215 * load() method.
216 * @param vertical
216 * @param vertical whether to enable vertical substitutions.
217217 * @throws IOException
218218 */
219219 private PDType0Font(PDDocument document, TrueTypeFont ttf, boolean embedSubset,
284284 // predefined CMap
285285 COSName encodingName = (COSName) encoding;
286286 cMap = CMapManager.getPredefinedCMap(encodingName.getName());
287 if (cMap != null)
288 {
289 isCMapPredefined = true;
290 }
291 else
292 {
293 throw new IOException("Missing required CMap");
294 }
287 isCMapPredefined = true;
295288 }
296289 else if (encoding != null)
297290 {
310303 PDCIDSystemInfo ros = descendantFont.getCIDSystemInfo();
311304 if (ros != null)
312305 {
306 String ordering = ros.getOrdering();
313307 isDescendantCJK = "Adobe".equals(ros.getRegistry()) &&
314 ("GB1".equals(ros.getOrdering()) ||
315 "CNS1".equals(ros.getOrdering()) ||
316 "Japan1".equals(ros.getOrdering()) ||
317 "Korea1".equals(ros.getOrdering()));
308 ("GB1".equals(ordering) ||
309 "CNS1".equals(ordering) ||
310 "Japan1".equals(ordering) ||
311 "Korea1".equals(ordering));
318312 }
319313 }
320314
340334 String strName = null;
341335 if (isDescendantCJK)
342336 {
343 strName = descendantFont.getCIDSystemInfo().getRegistry() + "-" +
344 descendantFont.getCIDSystemInfo().getOrdering() + "-" +
345 descendantFont.getCIDSystemInfo().getSupplement();
337 PDCIDSystemInfo cidSystemInfo = descendantFont.getCIDSystemInfo();
338 if (cidSystemInfo != null)
339 {
340 strName = cidSystemInfo.getRegistry() + "-" +
341 cidSystemInfo.getOrdering() + "-" +
342 cidSystemInfo.getSupplement();
343 }
346344 }
347345 else if (name != null)
348346 {
413411 @Override
414412 public boolean isVertical()
415413 {
416 return cMap.getWMode() == 1;
414 return cMap != null && cMap.getWMode() == 1;
417415 }
418416
419417 @Override
570568 @Override
571569 public int readCode(InputStream in) throws IOException
572570 {
571 if (cMap == null)
572 {
573 throw new IOException("required cmap is null");
574 }
573575 return cMap.readCode(in);
574576 }
575577
218218 PDStream fontFile3 = fd.getFontFile3();
219219 if (fontFile3 != null)
220220 {
221 throw new IOException("/FontFile3 for Type1 font not supported");
221 LOG.warn("/FontFile3 for Type1 font not supported");
222222 }
223223
224224 // or it may contain a PFB
120120 return null;
121121 }
122122 }
123 float x = ((COSNumber) arguments.get(2)).floatValue();
124 float y = ((COSNumber) arguments.get(3)).floatValue();
123125 return new PDRectangle(
124 ((COSNumber) arguments.get(2)).floatValue(),
125 ((COSNumber) arguments.get(3)).floatValue(),
126 ((COSNumber) arguments.get(4)).floatValue() - ((COSNumber) arguments.get(2)).floatValue(),
127 ((COSNumber) arguments.get(5)).floatValue() - ((COSNumber) arguments.get(3)).floatValue());
126 x,
127 y,
128 ((COSNumber) arguments.get(4)).floatValue() - x,
129 ((COSNumber) arguments.get(5)).floatValue() - y);
128130 }
129131 else
130132 {
194194 */
195195 private PDFontDescriptor createFontDescriptor(TrueTypeFont ttf) throws IOException
196196 {
197 PDFontDescriptor fd = new PDFontDescriptor();
198 fd.setFontName(ttf.getName());
199
197 String ttfName = ttf.getName();
200198 OS2WindowsMetricsTable os2 = ttf.getOS2Windows();
201199 if (os2 == null)
202200 {
203 throw new IOException("os2 table is missing in font " + ttf.getName());
201 throw new IOException("os2 table is missing in font " + ttfName);
204202 }
205203 PostScriptTable post = ttf.getPostScript();
206204 if (post == null)
207205 {
208 throw new IOException("post table is missing in font " + ttf.getName());
209 }
206 throw new IOException("post table is missing in font " + ttfName);
207 }
208
209 PDFontDescriptor fd = new PDFontDescriptor();
210 fd.setFontName(ttfName);
211
212 HorizontalHeaderTable hhea = ttf.getHorizontalHeader();
210213
211214 // Flags
212 fd.setFixedPitch(post.getIsFixedPitch() > 0 ||
213 ttf.getHorizontalHeader().getNumberOfHMetrics() == 1);
215 fd.setFixedPitch(post.getIsFixedPitch() > 0 || hhea.getNumberOfHMetrics() == 1);
214216
215217 int fsSelection = os2.getFsSelection();
216218 fd.setItalic(((fsSelection & (ITALIC | OBLIQUE)) != 0));
250252 fd.setFontBoundingBox(rect);
251253
252254 // Ascent, Descent
253 HorizontalHeaderTable hHeader = ttf.getHorizontalHeader();
254 fd.setAscent(hHeader.getAscender() * scaling);
255 fd.setDescent(hHeader.getDescender() * scaling);
255 fd.setAscent(hhea.getAscender() * scaling);
256 fd.setDescent(hhea.getDescender() * scaling);
256257
257258 // CapHeight, XHeight
258259 if (os2.getVersion() >= 1.2)
7171 else if (COSName.FORM.getName().equals(subtype))
7272 {
7373 ResourceCache cache = resources != null ? resources.getResourceCache() : null;
74 COSDictionary group = (COSDictionary)stream.getDictionaryObject(COSName.GROUP);
74 COSDictionary group = stream.getCOSDictionary(COSName.GROUP);
7575 if (group != null && COSName.TRANSPARENCY.equals(group.getCOSName(COSName.S)))
7676 {
7777 return new PDTransparencyGroup(stream, cache);
2222 import java.io.IOException;
2323 import java.util.Arrays;
2424
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.pdfbox.cos.COSBase;
28
2529 /**
2630 * A color value, consisting of one or more color components, or for pattern color spaces,
2731 * a name and optional color components.
3337 */
3438 public final class PDColor
3539 {
40 private static final Log LOG = LogFactory.getLog(PDColor.class);
41
3642 private final float[] components;
3743 private final COSName patternName;
3844 private final PDColorSpace colorSpace;
4854 {
4955 // color components (optional), for the color of an uncoloured tiling pattern
5056 components = new float[array.size() - 1];
51 for (int i = 0; i < components.length; i++)
52 {
53 components[i] = ((COSNumber)array.get(i)).floatValue();
54 }
57 initComponents(array);
5558
5659 // pattern name (required)
57 patternName = (COSName)array.get(array.size() - 1);
60 COSBase base = array.get(array.size() - 1);
61 if (base instanceof COSName)
62 {
63 patternName = (COSName) base;
64 }
65 else
66 {
67 LOG.warn("pattern name in " + array + " isn't a name, ignored");
68 patternName = COSName.getPDFName("Unknown");
69 }
5870 }
5971 else
6072 {
6173 // color components only
6274 components = new float[array.size()];
63 for (int i = 0; i < array.size(); i++)
64 {
65 components[i] = ((COSNumber)array.get(i)).floatValue();
66 }
75 initComponents(array);
6776 patternName = null;
6877 }
6978 this.colorSpace = colorSpace;
79 }
80
81 private void initComponents(COSArray array)
82 {
83 for (int i = 0; i < components.length; i++)
84 {
85 COSBase base = array.get(i);
86 if (base instanceof COSNumber)
87 {
88 components[i] = ((COSNumber) base).floatValue();
89 }
90 else
91 {
92 LOG.warn("color component " + i + " in " + array + " isn't a number, ignored");
93 }
94 }
7095 }
7196
7297 /**
131131 PDDeviceNProcess process = getProcess();
132132 if (process != null)
133133 {
134 sb.append(getProcess());
134 sb.append(process);
135135 sb.append(' ');
136136 }
137137
7777 // flip bit to avoid having to set /BlackIs1
7878 mcios.writeBits(~(image.getRGB(x, y) & 1), 1);
7979 }
80 if (mcios.getBitOffset() != 0)
81 {
82 mcios.writeBits(0, 8 - mcios.getBitOffset());
80 int bitOffset = mcios.getBitOffset();
81 if (bitOffset != 0)
82 {
83 mcios.writeBits(0, 8 - bitOffset);
8384 }
8485 }
8586 mcios.flush();
1616 package org.apache.pdfbox.pdmodel.graphics.optionalcontent;
1717
1818 import java.util.ArrayList;
19 import java.util.Collections;
1920 import java.util.List;
2021 import org.apache.pdfbox.cos.COSArray;
2122 import org.apache.pdfbox.cos.COSBase;
5960 */
6061 public List<PDPropertyList> getOCGs()
6162 {
62 List<PDPropertyList> list = new ArrayList<PDPropertyList>();
6363 COSBase base = dict.getDictionaryObject(COSName.OCGS);
6464 if (base instanceof COSDictionary)
6565 {
66 list.add(PDPropertyList.create((COSDictionary) base));
66 return Collections.singletonList(PDPropertyList.create((COSDictionary) base));
6767 }
68 else if (base instanceof COSArray)
68
69 if (base instanceof COSArray)
6970 {
7071 COSArray ar = (COSArray) base;
72 List<PDPropertyList> list = new ArrayList<PDPropertyList>();
7173 for (int i = 0; i < ar.size(); ++i)
7274 {
7375 COSBase elem = ar.getObject(i);
7678 list.add(PDPropertyList.create((COSDictionary) elem));
7779 }
7880 }
81 return list;
7982 }
80 return list;
83
84 return Collections.emptyList();
8185 }
8286
8387 /**
5959 private PDLineDashPattern lineDashPattern = new PDLineDashPattern();
6060 private RenderingIntent renderingIntent;
6161 private boolean strokeAdjustment = false;
62 private BlendMode blendMode = BlendMode.COMPATIBLE;
62 private BlendMode blendMode = BlendMode.NORMAL;
6363 private PDSoftMask softMask;
6464 private double alphaConstant = 1.0;
6565 private double nonStrokingAlphaConstant = 1.0;
2121 import org.apache.pdfbox.cos.COSBase;
2222 import org.apache.pdfbox.cos.COSBoolean;
2323 import org.apache.pdfbox.cos.COSDictionary;
24 import org.apache.pdfbox.cos.COSInteger;
2425 import org.apache.pdfbox.cos.COSName;
2526 import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
2627 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
8687 if (destArray.size() >= 1)
8788 {
8889 COSBase page = destArray.getObject(0);
89 if (!(page instanceof COSDictionary))
90 if (!(page instanceof COSInteger))
9091 {
91 throw new IllegalArgumentException("Destination of a GoToE action must be "
92 + "a page dictionary object");
92 throw new IllegalArgumentException(
93 "Destination of a GoToE action must be an integer");
9394 }
9495 }
9596 }
8585 COSBase page = destArray.getObject(0);
8686 if (!(page instanceof COSDictionary))
8787 {
88 throw new IllegalArgumentException("Destination of a GoTo action must be "
89 + "a page dictionary object");
88 throw new IllegalArgumentException(
89 "Destination of a GoTo action must be a page dictionary object");
9090 }
9191 }
9292 }
7676 this.document = document;
7777 }
7878
79 @Override
80 public void generateAppearanceStreams()
81 {
82 generateNormalAppearance();
83 generateRolloverAppearance();
84 generateDownAppearance();
85 }
86
7987 PDAnnotation getAnnotation()
8088 {
8189 return annotation;
2424 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup;
2525 import org.apache.pdfbox.pdmodel.PDAppearanceContentStream;
2626 import org.apache.pdfbox.pdmodel.PDDocument;
27 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
2728 import org.apache.pdfbox.util.Matrix;
2829
2930 /**
4647 }
4748
4849 @Override
49 public void generateAppearanceStreams()
50 {
51 generateNormalAppearance();
52 generateRolloverAppearance();
53 generateDownAppearance();
54 }
55
56 @Override
5750 public void generateNormalAppearance()
5851 {
5952 PDAnnotationMarkup annotation = (PDAnnotationMarkup) getAnnotation();
7063
7164 PDRectangle rect = getRectangle();
7265 PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
66 PDAppearanceStream pdAppearanceStream = annotation.getNormalAppearanceStream();
7367 if (!annotation.getCOSObject().containsKey(COSName.RD))
7468 {
7569 // Adobe creates the /RD entry with a number that is decided
8175 float rd = Math.min(rect.getHeight() / 10, 5);
8276 annotation.setRectDifferences(rd);
8377 bbox = new PDRectangle(-rd, -rd, rect.getWidth() + 2 * rd, rect.getHeight() + 2 * rd);
84 Matrix matrix = annotation.getNormalAppearanceStream().getMatrix();
85 matrix.transformPoint(rd, rd);
86 annotation.getNormalAppearanceStream().setMatrix(matrix.createAffineTransform());
78 Matrix matrix = pdAppearanceStream.getMatrix();
79 pdAppearanceStream.setMatrix(matrix.createAffineTransform());
8780 PDRectangle rect2 = new PDRectangle(rect.getLowerLeftX() - rd, rect.getLowerLeftY() - rd,
8881 rect.getWidth() + 2 * rd, rect.getHeight() + 2 * rd);
8982 annotation.setRectangle(rect2);
9083 }
91 annotation.getNormalAppearanceStream().setBBox(bbox);
84 pdAppearanceStream.setBBox(bbox);
9285
9386 float halfX = rect.getWidth() / 2;
9487 float halfY = rect.getHeight() / 2;
4949 public PDCircleAppearanceHandler(PDAnnotation annotation, PDDocument document)
5050 {
5151 super(annotation, document);
52 }
53
54 @Override
55 public void generateAppearanceStreams()
56 {
57 generateNormalAppearance();
58 generateRolloverAppearance();
59 generateDownAppearance();
6052 }
6153
6254 @Override
4242 public PDFileAttachmentAppearanceHandler(PDAnnotation annotation, PDDocument document)
4343 {
4444 super(annotation, document);
45 }
46
47 @Override
48 public void generateAppearanceStreams()
49 {
50 generateNormalAppearance();
51 generateRolloverAppearance();
52 generateDownAppearance();
5345 }
5446
5547 @Override
4646 import org.apache.pdfbox.pdmodel.interactive.annotation.layout.AppearanceStyle;
4747 import org.apache.pdfbox.pdmodel.interactive.annotation.layout.PlainText;
4848 import org.apache.pdfbox.pdmodel.interactive.annotation.layout.PlainTextFormatter;
49 import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
4950 import org.apache.pdfbox.util.Matrix;
5051
5152 public class PDFreeTextAppearanceHandler extends PDAbstractAppearanceHandler
6667 public PDFreeTextAppearanceHandler(PDAnnotation annotation, PDDocument document)
6768 {
6869 super(annotation, document);
69 }
70
71 @Override
72 public void generateAppearanceStreams()
73 {
74 generateNormalAppearance();
75 generateRolloverAppearance();
76 generateDownAppearance();
7770 }
7871
7972 @Override
246239 float clipHeight = rotation == 90 || rotation == 270 ?
247240 borderBox.getWidth() - ab.width * 4 : borderBox.getHeight() - ab.width * 4;
248241 extractFontDetails(annotation);
249 if (document != null && document.getDocumentCatalog().getAcroForm() != null)
250 {
251 // Try to get font from AcroForm default resources
252 // Sample file: https://gitlab.freedesktop.org/poppler/poppler/issues/6
253 PDResources defaultResources = document.getDocumentCatalog().getAcroForm().getDefaultResources();
254 if (defaultResources != null)
255 {
256 PDFont defaultResourcesFont = defaultResources.getFont(fontName);
257 if (defaultResourcesFont != null)
242 if (document != null)
243 {
244 PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
245 if (acroForm != null)
246 {
247 // Try to get font from AcroForm default resources
248 // Sample file: https://gitlab.freedesktop.org/poppler/poppler/issues/6
249 PDResources defaultResources = acroForm.getDefaultResources();
250 if (defaultResources != null)
258251 {
259 font = defaultResourcesFont;
252 PDFont defaultResourcesFont = defaultResources.getFont(fontName);
253 if (defaultResourcesFont != null)
254 {
255 font = defaultResourcesFont;
256 }
260257 }
261258 }
262259 }
440437 private void extractFontDetails(PDAnnotationMarkup annotation)
441438 {
442439 String defaultAppearance = annotation.getDefaultAppearance();
443 if (defaultAppearance == null && document != null &&
444 document.getDocumentCatalog().getAcroForm() != null)
445 {
446 defaultAppearance = document.getDocumentCatalog().getAcroForm().getDefaultAppearance();
440 if (defaultAppearance == null && document != null)
441 {
442 PDAcroForm pdAcroForm = document.getDocumentCatalog().getAcroForm();
443 if (pdAcroForm != null)
444 {
445 defaultAppearance = pdAcroForm.getDefaultAppearance();
446 }
447447 }
448448 if (defaultAppearance == null)
449449 {
5252 }
5353
5454 @Override
55 public void generateAppearanceStreams()
56 {
57 generateNormalAppearance();
58 generateRolloverAppearance();
59 generateDownAppearance();
60 }
61
62 @Override
6355 public void generateNormalAppearance()
6456 {
6557 PDAnnotationTextMarkup annotation = (PDAnnotationTextMarkup) getAnnotation();
4242 public PDInkAppearanceHandler(PDAnnotation annotation, PDDocument document)
4343 {
4444 super(annotation, document);
45 }
46
47 @Override
48 public void generateAppearanceStreams()
49 {
50 generateNormalAppearance();
51 generateRolloverAppearance();
52 generateDownAppearance();
5345 }
5446
5547 @Override
4949 }
5050
5151 @Override
52 public void generateAppearanceStreams()
53 {
54 generateNormalAppearance();
55 generateRolloverAppearance();
56 generateDownAppearance();
57 }
58
59 @Override
6052 public void generateNormalAppearance()
6153 {
6254 PDAnnotationLine annotation = (PDAnnotationLine) getAnnotation();
112104 // arrow length is 9 * width at about 30° => 10 * width seems to be enough
113105 // but need to consider /LL, /LLE and /LLO too
114106 //TODO find better way to calculate padding
115 rect.setLowerLeftX(Math.min(minX - Math.max(lineEndingSize * 10, Math.abs(llo+ll+lle)), rect.getLowerLeftX()));
116 rect.setLowerLeftY(Math.min(minY - Math.max(lineEndingSize * 10, Math.abs(llo+ll+lle)), rect.getLowerLeftY()));
117 rect.setUpperRightX(Math.max(maxX + Math.max(lineEndingSize * 10, Math.abs(llo+ll+lle)), rect.getUpperRightX()));
118 rect.setUpperRightY(Math.max(maxY + Math.max(lineEndingSize * 10, Math.abs(llo+ll+lle)), rect.getUpperRightY()));
107 float max = Math.max(lineEndingSize * 10, Math.abs(llo+ll+lle));
108 rect.setLowerLeftX(Math.min(minX - max, rect.getLowerLeftX()));
109 rect.setLowerLeftY(Math.min(minY - max, rect.getLowerLeftY()));
110 rect.setUpperRightX(Math.max(maxX + max, rect.getUpperRightX()));
111 rect.setUpperRightY(Math.max(maxY + max, rect.getUpperRightY()));
119112
120113 annotation.setRectangle(rect);
121114
4949 public PDLinkAppearanceHandler(PDAnnotation annotation, PDDocument document)
5050 {
5151 super(annotation, document);
52 }
53
54 @Override
55 public void generateAppearanceStreams()
56 {
57 generateNormalAppearance();
58 generateRolloverAppearance();
59 generateDownAppearance();
6052 }
6153
6254 @Override
4949 public PDPolygonAppearanceHandler(PDAnnotation annotation, PDDocument document)
5050 {
5151 super(annotation, document);
52 }
53
54 @Override
55 public void generateAppearanceStreams()
56 {
57 generateNormalAppearance();
58 generateRolloverAppearance();
59 generateDownAppearance();
6052 }
6153
6254 @Override
4949 public PDPolylineAppearanceHandler(PDAnnotation annotation, PDDocument document)
5050 {
5151 super(annotation, document);
52 }
53
54 @Override
55 public void generateAppearanceStreams()
56 {
57 generateNormalAppearance();
58 generateRolloverAppearance();
59 generateDownAppearance();
6052 }
6153
6254 @Override
3030 }
3131
3232 @Override
33 public void generateAppearanceStreams()
34 {
35 generateNormalAppearance();
36 generateRolloverAppearance();
37 generateDownAppearance();
38 }
39
40 @Override
4133 public void generateNormalAppearance()
4234 {
4335 // TODO to be implemented
5050 public PDSquareAppearanceHandler(PDAnnotation annotation, PDDocument document)
5151 {
5252 super(annotation, document);
53 }
54
55 @Override
56 public void generateAppearanceStreams()
57 {
58 generateNormalAppearance();
59 generateRolloverAppearance();
60 generateDownAppearance();
6153 }
6254
6355 @Override
5555 }
5656
5757 @Override
58 public void generateAppearanceStreams()
59 {
60 generateNormalAppearance();
61 generateRolloverAppearance();
62 generateDownAppearance();
63 }
64
65 @Override
6658 public void generateNormalAppearance()
6759 {
6860 PDAnnotationTextMarkup annotation = (PDAnnotationTextMarkup) getAnnotation();
4141 public PDStrikeoutAppearanceHandler(PDAnnotation annotation, PDDocument document)
4242 {
4343 super(annotation, document);
44 }
45
46 @Override
47 public void generateAppearanceStreams()
48 {
49 generateNormalAppearance();
50 generateRolloverAppearance();
51 generateDownAppearance();
5244 }
5345
5446 @Override
7373 public PDTextAppearanceHandler(PDAnnotation annotation, PDDocument document)
7474 {
7575 super(annotation, document);
76 }
77
78 @Override
79 public void generateAppearanceStreams()
80 {
81 generateNormalAppearance();
82 generateRolloverAppearance();
83 generateDownAppearance();
8476 }
8577
8678 @Override
228220
229221 contentStream.setLineCapStyle(0);
230222 contentStream.setLineWidth(0.61f); // value from Adobe
231 contentStream.addRect(1, 1, bbox.getWidth() - 2, bbox.getHeight() - 2);
232 contentStream.moveTo(bbox.getWidth() / 4, bbox.getHeight() / 7 * 2);
233 contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 2);
234 contentStream.moveTo(bbox.getWidth() / 4, bbox.getHeight() / 7 * 3);
235 contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 3);
236 contentStream.moveTo(bbox.getWidth() / 4, bbox.getHeight() / 7 * 4);
237 contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 4);
238 contentStream.moveTo(bbox.getWidth() / 4, bbox.getHeight() / 7 * 5);
239 contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 5);
223 float width = bbox.getWidth();
224 float height = bbox.getHeight();
225 contentStream.addRect(1, 1, width - 2, height - 2);
226 contentStream.moveTo(width / 4, height / 7 * 2);
227 contentStream.lineTo(width * 3 / 4 - 1, height / 7 * 2);
228 contentStream.moveTo(width / 4, height / 7 * 3);
229 contentStream.lineTo(width * 3 / 4 - 1, height / 7 * 3);
230 contentStream.moveTo(width / 4, height / 7 * 4);
231 contentStream.lineTo(width * 3 / 4 - 1, height / 7 * 4);
232 contentStream.moveTo(width / 4, height / 7 * 5);
233 contentStream.lineTo(width * 3 / 4 - 1, height / 7 * 5);
240234 contentStream.fillAndStroke();
241235 }
242236
268262 gs.setBlendMode(BlendMode.NORMAL);
269263 contentStream.setGraphicsStateParameters(gs);
270264 contentStream.setNonStrokingColor(1f);
271 drawCircle(contentStream, bbox.getWidth() / 2, bbox.getHeight() / 2, smallR);
265 float width = bbox.getWidth() / 2;
266 float height = bbox.getHeight() / 2;
267 drawCircle(contentStream, width, height, smallR);
272268 contentStream.fill();
273269 contentStream.restoreGraphicsState();
274270
275271 contentStream.setLineWidth(0.59f); // value from Adobe
276 drawCircle(contentStream, bbox.getWidth() / 2, bbox.getHeight() / 2, smallR);
277 drawCircle2(contentStream, bbox.getWidth() / 2, bbox.getHeight() / 2, largeR);
272 drawCircle(contentStream, width, height, smallR);
273 drawCircle2(contentStream, width, height, largeR);
278274 contentStream.fillAndStroke();
279275 }
280276
4141 public PDUnderlineAppearanceHandler(PDAnnotation annotation, PDDocument document)
4242 {
4343 super(annotation, document);
44 }
45
46 @Override
47 public void generateAppearanceStreams()
48 {
49 generateNormalAppearance();
50 generateRolloverAppearance();
51 generateDownAppearance();
5244 }
5345
5446 @Override
3939 import org.apache.pdfbox.pdmodel.PDPage;
4040 import org.apache.pdfbox.pdmodel.PDPageContentStream;
4141 import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
42 import org.apache.pdfbox.pdmodel.PDPageTree;
4243 import org.apache.pdfbox.pdmodel.PDResources;
4344 import org.apache.pdfbox.pdmodel.common.COSArrayList;
4445 import org.apache.pdfbox.pdmodel.common.COSObjectable;
6162 public final class PDAcroForm implements COSObjectable
6263 {
6364 private static final Log LOG = LogFactory.getLog(PDAcroForm.class);
64
65
6566 private static final int FLAG_SIGNATURES_EXIST = 1;
6667 private static final int FLAG_APPEND_ONLY = 1 << 1;
6768
6869 private final PDDocument document;
6970 private final COSDictionary dictionary;
70
71
7172 private Map<String, PDField> fieldCache;
7273
7374 private ScriptingHandler scriptingHandler;
105106 {
106107 return document;
107108 }
108
109
109110 @Override
110111 public COSDictionary getCOSObject()
111112 {
150151 FDFDictionary fdfDict = new FDFDictionary();
151152 catalog.setFDF(fdfDict);
152153
153 List<FDFField> fdfFields = new ArrayList<FDFField>();
154154 List<PDField> fields = getFields();
155 List<FDFField> fdfFields = new ArrayList<FDFField>(fields.size());
155156 for (PDField field : fields)
156157 {
157158 fdfFields.add(field.exportFDF());
158159 }
159
160
160161 fdfDict.setID(document.getDocument().getDocumentID());
161
162
162163 if (!fdfFields.isEmpty())
163164 {
164165 fdfDict.setFields(fdfFields);
188189 LOG.warn("Flatten for a dynamix XFA form is not supported");
189190 return;
190191 }
191
192
192193 List<PDField> fields = new ArrayList<PDField>();
193194 for (PDField field: getFieldTree())
194195 {
196197 }
197198 flatten(fields, false);
198199 }
199
200
200
201
201202 /**
202203 * This will flatten the specified form fields.
203204 *
218219 {
219220 return;
220221 }
221
222
222223 if (!refreshAppearances && getNeedAppearances())
223224 {
224225 LOG.warn("acroForm.getNeedAppearances() returns true, " +
234235 LOG.warn("Flatten for a dynamix XFA form is not supported");
235236 return;
236237 }
237
238
238239 // refresh the appearances if set
239240 if (refreshAppearances)
240241 {
242243 }
243244
244245 // get the widgets per page
245 Map<COSDictionary,Set<COSDictionary>> pagesWidgetsMap = buildPagesWidgetsMap(fields);
246
246 PDPageTree pages = document.getPages();
247 Map<COSDictionary,Set<COSDictionary>> pagesWidgetsMap = buildPagesWidgetsMap(fields, pages);
248
247249 // preserve all non widget annotations
248 for (PDPage page : document.getPages())
250 for (PDPage page : pages)
249251 {
250252 Set<COSDictionary> widgetsForPageMap = pagesWidgetsMap.get(page.getCOSObject());
251253
252254 // indicates if the original content stream
253255 // has been wrapped in a q...Q pair.
254256 boolean isContentStreamWrapped = false;
255
257
256258 List<PDAnnotation> annotations = new ArrayList<PDAnnotation>();
257
259
258260 for (PDAnnotation annotation: page.getAnnotations())
259261 {
260262 if (widgetsForPageMap == null || !widgetsForPageMap.contains(annotation.getCOSObject()))
261263 {
262 annotations.add(annotation);
264 annotations.add(annotation);
263265 }
264266 else if (isVisibleAnnotation(annotation))
265267 {
292294 }
293295 page.setAnnotations(annotations);
294296 }
295
297
296298 // remove the fields
297299 removeFields(fields);
298
300
299301 // remove XFA for hybrid forms
300302 dictionary.removeItem(COSName.XFA);
301303
355357 }
356358 }
357359 }
358
359
360
361
360362 /**
361363 * This will return all of the documents root fields.
362364 *
380382 List<PDField> pdFields = new ArrayList<PDField>();
381383 for (int i = 0; i < cosFields.size(); i++)
382384 {
383 COSDictionary element = (COSDictionary) cosFields.getObject(i);
384 if (element != null)
385 {
386 PDField field = PDField.fromDictionary(this, element, null);
385 COSBase element = cosFields.getObject(i);
386 if (element instanceof COSDictionary)
387 {
388 PDField field = PDField.fromDictionary(this, (COSDictionary) element, null);
387389 if (field != null)
388390 {
389391 pdFields.add(field);
402404 {
403405 dictionary.setItem(COSName.FIELDS, COSArrayList.converterToCOSArray(fields));
404406 }
405
407
406408 /**
407409 * Returns an iterator which walks all fields in the field tree, in order.
408410 */
417419 public PDFieldTree getFieldTree()
418420 {
419421 return new PDFieldTree(this);
420 }
421
422 }
423
422424 /**
423425 * This will tell this form to cache the fields into a Map structure
424426 * for fast access via the getField method. The default is false. You would
476478 return field;
477479 }
478480 }
479
481
480482 return null;
481483 }
482484
521523 {
522524 dictionary.setBoolean(COSName.NEED_APPEARANCES, value);
523525 }
524
526
525527 /**
526528 * This will get the default resources for the AcroForm.
527529 *
567569 {
568570 return hasXFA() && getFields().isEmpty();
569571 }
570
572
571573 /**
572574 * Get the XFA resource, the XFA resource is only used for PDF 1.5+ forms.
573575 *
593595 {
594596 dictionary.setItem(COSName.XFA, xfa);
595597 }
596
598
597599 /**
598600 * This will get the document-wide default value for the quadding/justification of variable text
599601 * fields.
686688 {
687689 this.scriptingHandler = scriptingHandler;
688690 }
689
691
690692 private Matrix resolveTransformationMatrix(PDAnnotation annotation, PDAppearanceStream appearanceStream)
691693 {
692694 // 1st step transform appearance stream bbox with appearance stream matrix
712714 private Rectangle2D getTransformedAppearanceBBox(PDAppearanceStream appearanceStream)
713715 {
714716 Matrix appearanceStreamMatrix = appearanceStream.getMatrix();
715 PDRectangle appearanceStreamBBox = appearanceStream.getBBox();
717 PDRectangle appearanceStreamBBox = appearanceStream.getBBox();
716718 GeneralPath transformedAppearanceBox = appearanceStreamBBox.transform(appearanceStreamMatrix);
717719 return transformedAppearanceBox.getBounds2D();
718720 }
719721
720 private Map<COSDictionary,Set<COSDictionary>> buildPagesWidgetsMap(List<PDField> fields) throws IOException
722 private Map<COSDictionary,Set<COSDictionary>> buildPagesWidgetsMap(
723 List<PDField> fields, PDPageTree pages) throws IOException
721724 {
722725 Map<COSDictionary,Set<COSDictionary>> pagesAnnotationsMap =
723726 new HashMap<COSDictionary, Set<COSDictionary>>();
724727 boolean hasMissingPageRef = false;
725
728
726729 for (PDField field : fields)
727730 {
728731 List<PDAnnotationWidget> widgets = field.getWidgets();
748751 // If there is a widget with a missing page reference we need to build the map reverse i.e.
749752 // from the annotations to the widget.
750753 LOG.warn("There has been a widget with a missing page reference, will check all page annotations");
751 for (PDPage page : document.getPages())
754 for (PDPage page : pages)
752755 {
753756 for (PDAnnotation annotation : page.getAnnotations())
754757 {
765768 private void fillPagesAnnotationMap(Map<COSDictionary, Set<COSDictionary>> pagesAnnotationsMap,
766769 PDPage page, PDAnnotationWidget widget)
767770 {
768 if (pagesAnnotationsMap.get(page.getCOSObject()) == null)
769 {
770 Set<COSDictionary> widgetsForPage = new HashSet<COSDictionary>();
771 Set<COSDictionary> widgetsForPage = pagesAnnotationsMap.get(page.getCOSObject());
772 if (widgetsForPage == null)
773 {
774 widgetsForPage = new HashSet<COSDictionary>();
771775 widgetsForPage.add(widget.getCOSObject());
772776 pagesAnnotationsMap.put(page.getCOSObject(), widgetsForPage);
773777 }
774778 else
775779 {
776 Set<COSDictionary> widgetsForPage = pagesAnnotationsMap.get(page.getCOSObject());
777780 widgetsForPage.add(widget.getCOSObject());
778781 }
779782 }
8585 super.importFDF(fdfField);
8686
8787 List<FDFField> fdfKids = fdfField.getKids();
88 if (fdfKids == null)
89 {
90 return;
91 }
8892 List<PDField> children = getChildren();
89 if (fdfKids == null)
90 {
91 return;
92 }
9393 for (int i = 0; i < fdfKids.size(); i++)
9494 {
9595 for (PDField pdChild : children)
1818 import java.io.IOException;
1919 import java.util.ArrayList;
2020 import java.util.List;
21 import java.util.Set;
2221
2322 import org.apache.pdfbox.cos.COSDictionary;
2423 import org.apache.pdfbox.cos.COSName;
123122 */
124123 public List<String> getSelectedExportValues() throws IOException
125124 {
126 Set<String> onValues = getOnValues();
127125 List<String> exportValues = getExportValues();
128126 List<String> selectedExportValues = new ArrayList<String>();
129127 if (exportValues.isEmpty())
135133 {
136134 String fieldValue = getValue();
137135 int idx = 0;
138 for (String onValue : onValues)
136 for (String onValue : getOnValues())
139137 {
140138 if (onValue.compareTo(fieldValue) == 0)
141139 {
5050 {
5151 super(acroForm);
5252 getCOSObject().setItem(COSName.FT, COSName.SIG);
53 getWidgets().get(0).setLocked(true);
54 getWidgets().get(0).setPrinted(true);
53 PDAnnotationWidget firstWidget = getWidgets().get(0);
54 firstWidget.setLocked(true);
55 firstWidget.setPrinted(true);
5556 setPartialName(generatePartialName());
5657 }
5758
100100 {
101101 super.importFDF(fdfField);
102102
103 Integer f = fdfField.getWidgetFieldFlags();
103104 for (PDAnnotationWidget widget : getWidgets())
104105 {
105 int annotFlags = widget.getAnnotationFlags();
106 Integer f = fdfField.getWidgetFieldFlags();
107106 if (f != null)
108107 {
109108 widget.setAnnotationFlags(f);
112111 {
113112 // these are supposed to be ignored if the F is set.
114113 Integer setF = fdfField.getSetWidgetFieldFlags();
114 int annotFlags = widget.getAnnotationFlags();
115115 if (setF != null)
116116 {
117117 annotFlags = annotFlags | setF;
256256 }
257257
258258 // draw to graphics using PDFRender
259 AffineTransform transform = (AffineTransform)graphics2D.getTransform().clone();
259 AffineTransform transform = graphics2D.getTransform();
260260 graphics2D.setBackground(Color.WHITE);
261261 renderer.setSubsamplingAllowed(subsamplingAllowed);
262262 renderer.setRenderingHints(renderingHints);
527527
528528 private RenderingHints createDefaultRenderingHints(Graphics2D graphics)
529529 {
530 boolean isBitonal = isBitonal(graphics);
530531 RenderingHints r = new RenderingHints(null);
531 r.put(RenderingHints.KEY_INTERPOLATION, isBitonal(graphics) ?
532 r.put(RenderingHints.KEY_INTERPOLATION, isBitonal ?
532533 RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR :
533534 RenderingHints.VALUE_INTERPOLATION_BICUBIC);
534535 r.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
535 r.put(RenderingHints.KEY_ANTIALIASING, isBitonal(graphics) ?
536 r.put(RenderingHints.KEY_ANTIALIASING, isBitonal ?
536537 RenderingHints.VALUE_ANTIALIAS_OFF :
537538 RenderingHints.VALUE_ANTIALIAS_ON);
538539 return r;
3939 import java.awt.geom.Point2D;
4040 import java.awt.geom.Rectangle2D;
4141 import java.awt.image.BufferedImage;
42 import java.awt.image.ByteLookupTable;
4243 import java.awt.image.ColorModel;
4344 import java.awt.image.ComponentColorModel;
4445 import java.awt.image.DataBuffer;
4546 import java.awt.image.DataBufferByte;
47 import java.awt.image.LookupOp;
48 import java.awt.image.LookupTable;
4649 import java.awt.image.Raster;
4750 import java.awt.image.WritableRaster;
4851 import java.io.IOException;
113116 * If you want to do custom graphics processing rather than Graphics2D rendering, then you should
114117 * subclass {@link PDFGraphicsStreamEngine} instead. Subclassing PageDrawer is only suitable for
115118 * cases where the goal is to render onto a {@link Graphics2D} surface. In that case you'll also
116 * have to subclass {@link PDFRenderer} and modify
117 * {@link PDFRenderer#createPageDrawer(PageDrawerParameters)}.
119 * have to subclass {@link PDFRenderer} and override
120 * {@link PDFRenderer#createPageDrawer(PageDrawerParameters)}. See the <i>OpaquePDFRenderer.java</i>
121 * example in the source code download on how to do this.
118122 *
119123 * @author Ben Litchfield
120124 */
166170 private final RenderDestination destination;
167171 private final RenderingHints renderingHints;
168172 private final float imageDownscalingOptimizationThreshold;
173 private LookupTable invTable = null;
169174
170175 /**
171176 * Default annotations filter, returns all annotations
488493 at.concatenate(font.getFontMatrix().createAffineTransform());
489494
490495 Glyph2D glyph2D = createGlyph2D(font);
491 drawGlyph2D(glyph2D, font, code, displacement, at);
496 try
497 {
498 drawGlyph2D(glyph2D, font, code, displacement, at);
499 }
500 catch (IOException ex)
501 {
502 LOG.error("Could not draw glyph for code " + code + " at position (" +
503 at.getTranslateX() + "," + at.getTranslateY() + ")", ex);
504 }
492505 }
493506
494507 /**
11531166
11541167 // draw the mask
11551168 BufferedImage mask = pdImage.getImage();
1169 AffineTransform imageTransform = new AffineTransform(at);
1170 imageTransform.scale(1.0 / mask.getWidth(), -1.0 / mask.getHeight());
1171 imageTransform.translate(0, -mask.getHeight());
1172 AffineTransform full = new AffineTransform(g.getTransform());
1173 full.concatenate(imageTransform);
1174 Matrix m = new Matrix(full);
1175 double scaleX = Math.abs(m.getScalingFactorX());
1176 double scaleY = Math.abs(m.getScalingFactorY());
1177
1178 boolean smallMask = mask.getWidth() <= 8 && mask.getHeight() <= 8;
1179 if (!smallMask)
1180 {
1181 // PDFBOX-5403:
1182 // The mask is copied to RGB because this supports a smooth scaling, so we
1183 // get a mask with 255 values instead of just 0 and 255.
1184 // Inverting is done because when we don't do it, the getScaledInstance() call
1185 // produces a black line in many masks. With the inversion we have a white line
1186 // which is neutral. Because of the inversion we don't have to substract from 255
1187 // in the "apply the mask" segment when rasterPixel[3] is assigned.
1188
1189 // The inversion is not done for very small ones, because of
1190 // PDFBOX-2171-002-002710-p14.pdf where the "New Harmony Consolidated" and
1191 // "Sailor Springs" patterns became almost invisible.
1192 // (We may have to decide this differently in the future, e.g. on b/w relationship)
1193 BufferedImage tmp = new BufferedImage(mask.getWidth(), mask.getHeight(), BufferedImage.TYPE_INT_RGB);
1194 mask = new LookupOp(getInvLookupTable(), graphics.getRenderingHints()).filter(mask, tmp);
1195 }
1196
11561197 BufferedImage renderedMask = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
11571198 g = (Graphics2D) renderedMask.getGraphics();
11581199 g.translate(-bounds.getMinX(), -bounds.getMinY());
1159 AffineTransform imageTransform = new AffineTransform(at);
1160 imageTransform.scale(1.0 / mask.getWidth(), -1.0 / mask.getHeight());
1161 imageTransform.translate(0, -mask.getHeight());
11621200 g.setRenderingHints(graphics.getRenderingHints());
1163 g.drawImage(mask, imageTransform, null);
1201
1202 if (smallMask)
1203 {
1204 g.drawImage(mask, imageTransform, null);
1205 }
1206 else
1207 {
1208 while (scaleX < 0.25)
1209 {
1210 scaleX *= 2.0;
1211 }
1212 while (scaleY < 0.25)
1213 {
1214 scaleY *= 2.0;
1215 }
1216 int w2 = (int) Math.round(mask.getWidth() * scaleX);
1217 int h2 = (int) Math.round(mask.getHeight() * scaleY);
1218
1219 Image scaledMask = mask.getScaledInstance(w2, h2, Image.SCALE_SMOOTH);
1220 imageTransform.scale(1f / Math.abs(scaleX), 1f / Math.abs(scaleY));
1221 g.drawImage(scaledMask, imageTransform, null);
1222 }
11641223 g.dispose();
11651224
11661225 // apply the mask
1167 final int[] transparent = new int[4];
11681226 int[] alphaPixel = null;
1227 int[] rasterPixel = null;
11691228 WritableRaster raster = renderedPaint.getRaster();
11701229 WritableRaster alpha = renderedMask.getRaster();
11711230 for (int y = 0; y < h; y++)
11731232 for (int x = 0; x < w; x++)
11741233 {
11751234 alphaPixel = alpha.getPixel(x, y, alphaPixel);
1176 if (alphaPixel[0] == 255)
1177 {
1178 raster.setPixel(x, y, transparent);
1179 }
1235 rasterPixel = raster.getPixel(x, y, rasterPixel);
1236 rasterPixel[3] = alphaPixel[0];
1237 raster.setPixel(x, y, rasterPixel);
11801238 }
11811239 }
11821240
16681726 Matrix transform = Matrix.concatenate(ctm, form.getMatrix());
16691727
16701728 // transform the bbox
1671 GeneralPath transformedBox = form.getBBox().transform(transform);
1729 PDRectangle formBBox = form.getBBox();
1730 if (formBBox == null)
1731 {
1732 // PDFBOX-5471
1733 // check done here and not in caller to avoid getBBox() creating rectangle twice
1734 LOG.warn("transparency group ignored because BBox is null");
1735 formBBox = new PDRectangle();
1736 }
1737 GeneralPath transformedBox = formBBox.transform(transform);
16721738
16731739 // clip the bbox to prevent giant bboxes from consuming all memory
16741740 Area transformed = new Area(transformedBox);
20742140 }
20752141 return true;
20762142 }
2143
2144 private LookupTable getInvLookupTable()
2145 {
2146 if (invTable == null)
2147 {
2148 byte[] inv = new byte[256];
2149 for (int i = 0; i < inv.length; i++)
2150 {
2151 inv[i] = (byte) (255 - i);
2152 }
2153 invTable = new ByteLookupTable(0, inv);
2154 }
2155 return invTable;
2156 }
20772157 }
399399 c[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
400400 c[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
401401 }
402
402403 /**
403404 * Transforms the given point by this matrix.
404405 *
422423 *
423424 * @param x x-coordinate
424425 * @param y y-coordinate
426 *
427 * @return the transformed point.
425428 */
426429 public Point2D.Float transformPoint(float x, float y)
427430 {
435438 }
436439
437440 /**
438 * Transforms the given point by this matrix.
441 * Transforms the given vector by this matrix.
439442 *
440443 * @param vector 2D vector
444 *
445 * @return the transformed vector.
441446 */
442447 public Vector transform(Vector vector)
443448 {
112112 permission1.setCanModify(false);
113113 permission1.setCanModifyAnnotations(false);
114114 permission1.setCanPrint(false);
115 permission1.setCanPrintDegraded(false);
115 permission1.setCanPrintFaithful(false);
116116
117117 permission2 = new AccessPermission();
118118 permission2.setCanAssembleDocument(false);
122122 permission2.setCanModify(false);
123123 permission2.setCanModifyAnnotations(false);
124124 permission2.setCanPrint(true); // it is true now !
125 permission2.setCanPrintDegraded(false);
125 permission2.setCanPrintFaithful(false);
126126
127127 recipient1 = getRecipient("test1.der", permission1);
128128 recipient2 = getRecipient("test2.der", permission2);
214214 Assert.assertFalse(permission.canModify());
215215 Assert.assertFalse(permission.canModifyAnnotations());
216216 Assert.assertFalse(permission.canPrint());
217 Assert.assertFalse(permission.canPrintDegraded());
217 Assert.assertFalse(permission.canPrintFaithful());
218218 }
219219 finally
220220 {
250250 Assert.assertFalse(permission.canModify());
251251 Assert.assertFalse(permission.canModifyAnnotations());
252252 Assert.assertFalse(permission.canPrint());
253 Assert.assertFalse(permission.canPrintDegraded());
253 Assert.assertFalse(permission.canPrintFaithful());
254254 }
255255 finally
256256 {
269269 Assert.assertFalse(permission.canModify());
270270 Assert.assertFalse(permission.canModifyAnnotations());
271271 Assert.assertTrue(permission.canPrint());
272 Assert.assertFalse(permission.canPrintDegraded());
272 Assert.assertFalse(permission.canPrintFaithful());
273273 }
274274 finally
275275 {
9696 permission.setCanModify(false);
9797 permission.setCanModifyAnnotations(false);
9898 permission.setCanPrint(true);
99 permission.setCanPrintDegraded(false);
99 permission.setCanPrintFaithful(false);
100100 permission.setReadOnly();
101101 }
102102
132132
133133 restrAP.setCanAssembleDocument(false);
134134 restrAP.setCanExtractForAccessibility(false);
135 restrAP.setCanPrintDegraded(false);
135 restrAP.setCanPrintFaithful(false);
136136
137137 inputFileAsByteArray = getFileResourceAsByteArray("PasswordSample-128bit.pdf");
138138 checkPerms(inputFileAsByteArray, "owner", fullAP);
181181 assertEquals(expectedPermissions.canModify(), currentAccessPermission.canModify());
182182 assertEquals(expectedPermissions.canModifyAnnotations(), currentAccessPermission.canModifyAnnotations());
183183 assertEquals(expectedPermissions.canPrint(), currentAccessPermission.canPrint());
184 assertEquals(expectedPermissions.canPrintDegraded(), currentAccessPermission.canPrintDegraded());
184 assertEquals(expectedPermissions.canPrintFaithful(), currentAccessPermission.canPrintFaithful());
185185
186186 new PDFRenderer(doc).renderImage(0);
187187
1616
1717 package org.apache.pdfbox.pdmodel.font;
1818
19 import java.awt.geom.Area;
20 import java.awt.geom.GeneralPath;
1921 import java.io.ByteArrayOutputStream;
2022 import java.io.File;
2123 import java.io.FileInputStream;
2830 import java.net.URL;
2931 import java.util.ArrayList;
3032 import java.util.List;
33
3134 import org.apache.fontbox.ttf.TTFParser;
3235 import org.apache.fontbox.ttf.TrueTypeCollection;
3336 import org.apache.fontbox.ttf.TrueTypeFont;
4043 import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding;
4144 import org.apache.pdfbox.rendering.PDFRenderer;
4245 import org.apache.pdfbox.text.PDFTextStripper;
46
4347 import org.junit.Assert;
4448 import org.junit.Assume;
4549 import org.junit.Before;
440444 Assert.assertEquals(text + "\n" + text, extractedText.trim());
441445 doc.close();
442446 }
447
448 /**
449 * Test font with an unusual cmap table combination (0, 3).
450 *
451 * @throws IOException
452 */
453 @Test
454 public void testPDFBox5484() throws IOException
455 {
456 File fontFile = new File("target/fonts", "PDFBOX-5484.ttf");
457 TrueTypeFont ttf = new TTFParser().parse(fontFile);
458 PDDocument doc = new PDDocument();
459 PDTrueTypeFont tr = PDTrueTypeFont.load(doc, ttf, WinAnsiEncoding.INSTANCE);
460 GeneralPath path1 = tr.getPath("oslash");
461 GeneralPath path2 = tr.getPath(248);
462 Assert.assertFalse(path2.getPathIterator(null).isDone()); // not empty
463 Assert.assertTrue(new Area(path1).equals(new Area(path2))); // assertEquals does not test equals()
464 doc.close();
465 }
443466 }
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>parent/pom.xml</relativePath>
2727 </parent>
2828
3333
3434 <scm>
3535 <connection>
36 scm:svn:http://svn.apache.org/repos/asf/pdfbox/tags/2.0.26
36 scm:svn:http://svn.apache.org/repos/asf/pdfbox/tags/2.0.27
3737 </connection>
3838 <developerConnection>
39 scm:svn:https://svn.apache.org/repos/asf/pdfbox/tags/2.0.26
39 scm:svn:https://svn.apache.org/repos/asf/pdfbox/tags/2.0.27
4040 </developerConnection>
41 <url>http://svn.apache.org/viewvc/pdfbox/tags/2.0.26</url>
41 <url>http://svn.apache.org/viewvc/pdfbox/tags/2.0.27</url>
4242 </scm>
4343
4444 <modules>
134134
135135 The release candidate is a zip archive of the sources in:
136136
137 http://svn.apache.org/repos/asf/pdfbox/tags/${project.version}/
137 https://svn.apache.org/repos/asf/pdfbox/tags/${project.version}/
138138
139139 The SHA-512 checksum of the archive is ${checksum}.
140140
2525 <parent>
2626 <groupId>org.apache.pdfbox</groupId>
2727 <artifactId>pdfbox-parent</artifactId>
28 <version>2.0.26</version>
28 <version>2.0.27</version>
2929 <relativePath>../parent/pom.xml</relativePath>
3030 </parent>
3131
122122 // else Filter entry is optional
123123 }
124124
125 private boolean readUntilEndOfDictionaryStream(InputStream ra) throws IOException
126 {
127 boolean search = true;
128 boolean maybe = false;
129 do
130 {
131 int c = ra.read();
132 switch (c)
133 {
134 case '>':
135 if (maybe)
136 {
137 return true;
138 }
139 maybe = true;
140 break;
141 case -1:
142 search = false;
143 break;
144 default:
145 maybe = false;
146 break;
147 }
148 } while (search);
149 return false;
150 }
151
125152 private boolean readUntilStream(InputStream ra) throws IOException
126153 {
127154 boolean search = true;
222249 return;
223250 }
224251 skipped += curSkip;
252 }
253
254 // skip (most of) the dictionary to avoid false positives, see PDFBOX-4925
255 if (!readUntilEndOfDictionaryStream(ra))
256 {
257 addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_DAMAGED,
258 "Unable to find end of dictionary"));
259 return;
225260 }
226261
227262 // ---- go to the stream key word
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
2222 <parent>
2323 <groupId>org.apache.pdfbox</groupId>
2424 <artifactId>pdfbox-parent</artifactId>
25 <version>2.0.26</version>
25 <version>2.0.27</version>
2626 <relativePath>../parent/pom.xml</relativePath>
2727 </parent>
2828
123123 {
124124 ap.setCanPrint( args[++i].equalsIgnoreCase( "true" ) );
125125 }
126 else if( key.equals( "-canPrintDegraded" ) )
127 {
128 ap.setCanPrintDegraded( args[++i].equalsIgnoreCase( "true" ) );
126 else if( key.equals( "-canPrintDegraded") || key.equals( "-canPrintFaithful") )
127 {
128 ap.setCanPrintFaithful( args[++i].equalsIgnoreCase( "true" ) );
129129 }
130130 else if( key.equals( "-certFile" ) )
131131 {
240240 + " -canModify <true|false> : Set the modify permission\n"
241241 + " -canModifyAnnotations <true|false> : Set the modify annots permission\n"
242242 + " -canPrint <true|false> : Set the print permission\n"
243 + " -canPrintDegraded <true|false> : Set the print degraded permission\n"
243 + " -canPrintFaithful <true|false> : Set the print faithful permission\n"
244244 + " -keyLength <length> : Key length in bits "
245245 + "(valid values: 40, 128 or 256, default is 256)\n"
246246 + "\nNote: By default all permissions are set to true!";
138138 {
139139 document = PDDocument.load(new File(pdfFile), password);
140140
141 int numberOfPages = document.getNumberOfPages();
142141 boolean startEndPageSet = false;
143142 if (startPage != null)
144143 {
146145 startEndPageSet = true;
147146 if (split == null)
148147 {
148 int numberOfPages = document.getNumberOfPages();
149149 splitter.setSplitAtPage(numberOfPages);
150150 }
151151 }
109109 */
110110 public void createPDFFromText( PDDocument doc, Reader text ) throws IOException
111111 {
112 try
113 {
114
115 final int margin = 40;
116 float height = font.getBoundingBox().getHeight() / FONTSCALE;
117 PDRectangle actualMediaBox =
118 landscape ? new PDRectangle(mediaBox.getHeight(), mediaBox.getWidth()) : mediaBox;
119
120 //calculate font height and increase by a factor.
121 height = height*fontSize*LINE_HEIGHT_FACTOR;
122 BufferedReader data = new BufferedReader( text );
123 String nextLine;
124 PDPage page = new PDPage(actualMediaBox);
125 PDPageContentStream contentStream = null;
126 float y = -1;
127 float maxStringLength = page.getMediaBox().getWidth() - 2*margin;
128
129 // There is a special case of creating a PDF document from an empty string.
130 boolean textIsEmpty = true;
131
132 StringBuilder nextLineToDraw = new StringBuilder();
133
134 while( (nextLine = data.readLine()) != null )
112 final int margin = 40;
113 float height = font.getBoundingBox().getHeight() / FONTSCALE;
114 PDRectangle actualMediaBox =
115 landscape ? new PDRectangle(mediaBox.getHeight(), mediaBox.getWidth()) : mediaBox;
116
117 //calculate font height and increase by a factor.
118 height = height * fontSize * LINE_HEIGHT_FACTOR;
119 BufferedReader data = new BufferedReader(text);
120 String nextLine;
121 PDPage page = new PDPage(actualMediaBox);
122 PDPageContentStream contentStream = null;
123 float y = -1;
124 float maxStringLength = page.getMediaBox().getWidth() - 2 * margin;
125
126 // There is a special case of creating a PDF document from an empty string.
127 boolean textIsEmpty = true;
128
129 StringBuilder nextLineToDraw = new StringBuilder();
130
131 while ((nextLine = data.readLine()) != null)
132 {
133 // The input text is nonEmpty. New pages will be created and added
134 // to the PDF document as they are needed, depending on the length of
135 // the text.
136 textIsEmpty = false;
137
138 String[] lineWords = nextLine.replaceAll("[\\n\\r]+$", "").split(" ", -1);
139 int lineIndex = 0;
140 while (lineIndex < lineWords.length)
135141 {
136
137 // The input text is nonEmpty. New pages will be created and added
138 // to the PDF document as they are needed, depending on the length of
139 // the text.
140 textIsEmpty = false;
141
142 String[] lineWords = nextLine.replaceAll("[\\n\\r]+$", "").split(" ", -1);
143 int lineIndex = 0;
144 while( lineIndex < lineWords.length )
142 nextLineToDraw.setLength(0);
143 boolean addSpace = false;
144 float lengthIfUsingNextWord = 0;
145 boolean ff = false;
146 do
145147 {
146 nextLineToDraw.setLength(0);
147 boolean addSpace = false;
148 float lengthIfUsingNextWord = 0;
149 boolean ff = false;
150 do
151 {
152 String word1, word2 = "";
153 String word = lineWords[lineIndex];
154 int indexFF = word.indexOf('\f');
155 if (indexFF == -1)
148 String word1, word2 = "";
149 String word = lineWords[lineIndex];
150 int indexFF = word.indexOf('\f');
151 if (indexFF == -1)
152 {
153 word1 = word;
154 }
155 else
156 {
157 ff = true;
158 word1 = word.substring(0, indexFF);
159 if (indexFF < word.length())
156160 {
157 word1 = word;
161 word2 = word.substring(indexFF + 1);
162 }
163 }
164 // word1 is the part before ff, word2 after
165 // both can be empty
166 // word1 can also be empty without ff, if a line has many spaces
167 if (word1.length() > 0 || !ff)
168 {
169 if (addSpace)
170 {
171 nextLineToDraw.append(" ");
158172 }
159173 else
160174 {
161 ff = true;
162 word1 = word.substring(0, indexFF);
163 if (indexFF < word.length())
164 {
165 word2 = word.substring(indexFF + 1);
166 }
175 addSpace = true;
167176 }
168 // word1 is the part before ff, word2 after
169 // both can be empty
170 // word1 can also be empty without ff, if a line has many spaces
171 if (word1.length() > 0 || !ff)
177 nextLineToDraw.append(word1);
178 }
179 if (!ff || word2.length() == 0)
180 {
181 lineIndex++;
182 }
183 else
184 {
185 lineWords[lineIndex] = word2;
186 }
187 if (ff)
188 {
189 break;
190 }
191 if (lineIndex < lineWords.length)
192 {
193 // need cut off at \f in next word to avoid IllegalArgumentException
194 String nextWord = lineWords[lineIndex];
195 indexFF = nextWord.indexOf('\f');
196 if (indexFF != -1)
172197 {
173 if (addSpace)
174 {
175 nextLineToDraw.append(" ");
176 }
177 else
178 {
179 addSpace = true;
180 }
181 nextLineToDraw.append(word1);
198 nextWord = nextWord.substring(0, indexFF);
182199 }
183 if (!ff || word2.length() == 0)
184 {
185 lineIndex++;
186 }
187 else
188 {
189 lineWords[lineIndex] = word2;
190 }
191 if (ff)
192 {
193 break;
194 }
195 if( lineIndex < lineWords.length )
196 {
197 // need cut off at \f in next word to avoid IllegalArgumentException
198 String nextWord = lineWords[lineIndex];
199 indexFF = nextWord.indexOf('\f');
200 if (indexFF != -1)
201 {
202 nextWord = nextWord.substring(0, indexFF);
203 }
204
205 String lineWithNextWord = nextLineToDraw + " " + nextWord;
206 lengthIfUsingNextWord =
207 (font.getStringWidth( lineWithNextWord )/FONTSCALE) * fontSize;
208 }
209 }
210 while (lineIndex < lineWords.length && lengthIfUsingNextWord < maxStringLength);
211
212 if( y < margin )
213 {
214 // We have crossed the end-of-page boundary and need to extend the
215 // document by another page.
216 page = new PDPage(actualMediaBox);
217 doc.addPage( page );
218 if( contentStream != null )
219 {
220 contentStream.endText();
221 contentStream.close();
222 }
223 contentStream = new PDPageContentStream(doc, page);
224 contentStream.setFont( font, fontSize );
225 contentStream.beginText();
226 y = page.getMediaBox().getHeight() - margin + height;
227 contentStream.newLineAtOffset(margin, y);
228 }
229
230 if( contentStream == null )
231 {
232 throw new IOException( "Error:Expected non-null content stream." );
233 }
234 contentStream.newLineAtOffset(0, -height);
235 y -= height;
236 contentStream.showText(nextLineToDraw.toString());
237 if (ff)
238 {
239 page = new PDPage(actualMediaBox);
240 doc.addPage(page);
200
201 String lineWithNextWord = nextLineToDraw + " " + nextWord;
202 lengthIfUsingNextWord
203 = (font.getStringWidth(lineWithNextWord) / FONTSCALE) * fontSize;
204 }
205 }
206 while (lineIndex < lineWords.length && lengthIfUsingNextWord < maxStringLength);
207
208 if (y < margin)
209 {
210 // We have crossed the end-of-page boundary and need to extend the
211 // document by another page.
212 page = new PDPage(actualMediaBox);
213 doc.addPage(page);
214 if (contentStream != null)
215 {
241216 contentStream.endText();
242217 contentStream.close();
243 contentStream = new PDPageContentStream(doc, page);
244 contentStream.setFont(font, fontSize);
245 contentStream.beginText();
246 y = page.getMediaBox().getHeight() - margin + height;
247 contentStream.newLineAtOffset(margin, y);
248 }
218 }
219 contentStream = new PDPageContentStream(doc, page);
220 contentStream.setFont(font, fontSize);
221 contentStream.beginText();
222 y = page.getMediaBox().getHeight() - margin + height;
223 contentStream.newLineAtOffset(margin, y);
224 }
225
226 if (contentStream == null)
227 {
228 throw new IOException("Error:Expected non-null content stream.");
229 }
230 contentStream.newLineAtOffset(0, -height);
231 y -= height;
232 contentStream.showText(nextLineToDraw.toString());
233 if (ff)
234 {
235 page = new PDPage(actualMediaBox);
236 doc.addPage(page);
237 contentStream.endText();
238 contentStream.close();
239 contentStream = new PDPageContentStream(doc, page);
240 contentStream.setFont(font, fontSize);
241 contentStream.beginText();
242 y = page.getMediaBox().getHeight() - margin + height;
243 contentStream.newLineAtOffset(margin, y);
249244 }
250245 }
251
252 // If the input text was the empty string, then the above while loop will have short-circuited
253 // and we will not have added any PDPages to the document.
254 // So in order to make the resultant PDF document readable by Adobe Reader etc, we'll add an empty page.
255 if (textIsEmpty)
256 {
257 doc.addPage(page);
258 }
259
260 if( contentStream != null )
261 {
262 contentStream.endText();
263 contentStream.close();
264 }
265 }
266 catch( IOException io )
267 {
268 if( doc != null )
269 {
270 doc.close();
271 }
272 throw io;
246 }
247
248 // If the input text was the empty string, then the above while loop will have short-circuited
249 // and we will not have added any PDPages to the document.
250 // So in order to make the resultant PDF document readable by Adobe Reader etc, we'll add an empty page.
251 if (textIsEmpty)
252 {
253 doc.addPage(page);
254 }
255
256 if (contentStream != null)
257 {
258 contentStream.endText();
259 contentStream.close();
273260 }
274261 }
275262
2626 <parent>
2727 <groupId>org.apache.pdfbox</groupId>
2828 <artifactId>pdfbox-parent</artifactId>
29 <version>2.0.26</version>
29 <version>2.0.27</version>
3030 <relativePath>../parent/pom.xml</relativePath>
3131 </parent>
3232