diff --git a/.gitignore b/.gitignore
index 748b8ba931d..0dbc7ec1a4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ bin/
.settings/
.classpath
.project
+.idea/
+*.iml
diff --git a/.travis.yml b/.travis.yml
index c8039e0f336..a93a1d45958 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,10 +19,11 @@
language: java
+# https://docs.travis-ci.com/user/reference/xenial/#jvm-clojure-groovy-java-scala-support
jdk:
- - oraclejdk8
- - oraclejdk7
- - openjdk6
+ - oraclejdk12
+ - openjdk8
+ - openjdk11
before_install:
- sudo apt-get update -qq
diff --git a/README.md b/README.md
index 3df28e35bb4..6ef40069efb 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,21 @@
-Apache PDFBox
+
+
+Apache PDFBox
===================================================
The Apache PDFBox library is an open source Java tool for working with PDF
@@ -7,18 +24,18 @@ of existing documents and the ability to extract content from documents.
PDFBox also includes several command line utilities. PDFBox is published
under the Apache License, Version 2.0.
-PDFBox is a project of the Apache Software Foundation .
+PDFBox is a project of the Apache Software Foundation .
Binary Downloads
----------------
You can download binary versions for releases currently under development or older
-releases from out [Download Page](http://pdfbox.apache.org/download.cgi).
+releases from our [Download Page](https://pdfbox.apache.org/download.cgi).
Build
-----
-You need Java 6 (or higher) and Maven 2 to
+You need Java 6 (or higher) and Maven 2 to
build PDFBox. The recommended build command is:
mvn clean install
@@ -33,7 +50,7 @@ Contribute
There are various ways to help us improve PDFBox.
- look at the [Issue Tracker](https://issues.apache.org/jira/browse/PDFBOX) to help us fix bugs.
-- answer questions on our [Users Mailing List](http://pdfbox.apache.org/mailinglists.html "Subscribe to Mailing List").
+- answer questions on our [Users Mailing List](https://pdfbox.apache.org/mailinglists.html "Subscribe to Mailing List").
- help us enhance the [Examples](https://svn.apache.org/repos/asf/pdfbox/trunk/examples/)
- help us to enhance the [PDFBox Documentation](https://git-wip-us.apache.org/repos/asf/pdfbox-docs)
or on [GitHub](https://github.com/apache/pdfbox-docs).
@@ -41,7 +58,7 @@ or on [GitHub](https://github.com/apache/pdfbox-docs).
Support
-------
-**Please follow the guidelines at our [Support Page](http://pdfbox.apache.org/support.html).**
+**Please follow the guidelines at our [Support Page](https://pdfbox.apache.org/support.html).**
If you have questions about how to use PDFBox do ask on the
[Users Mailing List](/mailinglists.html "Subscribe to Mailing List").
@@ -49,8 +66,8 @@ This will get you help from the entire community.
The PDFBox examples and the test code in the sources will also provide additional information.
-And there are additonal resources available on sites such as
-[Stack Overflow](http://stackoverflow.com/search?q=pdfbox "Stack Overflow").
+And there are additional resources available on sites such as
+[Stack Overflow](https://stackoverflow.com/search?q=pdfbox "Stack Overflow").
If you are sure you have found a bug the please report the issue in our
[Issue Tracker](https://issues.apache.org/jira/browse/PDFBOX).
@@ -60,7 +77,7 @@ Known Limitations and Problems
See the issue tracker at https://issues.apache.org/jira/browse/PDFBOX for
the full list of known issues and requested features. Some of the more
-commont issues are:
+common issues are:
1. You get text like "G38G43G36G51G5" instead of what you expect when you are
extracting text. This is because the characters are a meaningless internal
@@ -91,7 +108,7 @@ The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -108,7 +125,7 @@ and/or re-export to another country, of encryption software. BEFORE using
any encryption software, please check your country's laws, regulations and
policies concerning the import, possession, or use, and re-export of
encryption software, to see if this is permitted. See
- for more information.
+ for more information.
The U.S. Government Department of Commerce, Bureau of Industry and
Security (BIS), has classified this software as Export Commodity Control
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index b756fe0379e..9bf30abe777 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,1216 +1,67 @@
-Release Notes -- Apache PDFBox -- Version 2.0.0-RC3
+Release Notes -- Apache PDFBox -- Version 2.0.24
Introduction
------------
The Apache PDFBox library is an open source Java tool for working with PDF documents.
-This is the third release candidate for the upcoming major release 2.0.0 of PDFBox.
-This release contains a lot of improvements, fixes and refactorings. The API is
-supposed to be stable, but we can't guarantee that there won't be any last changes
-to it before providing the final release candidate.
+This is an incremental bugfix release based on the earlier 2.0.23 release. It contains
+a couple of fixes and small improvements.
For more details on these changes and all the other fixes and improvements
included in this release, please refer to the following issues on the
PDFBox issue tracker at https://issues.apache.org/jira/browse/PDFBOX.
-Sub-task
-
-[PDFBOX-1869] - Implementation for ShadingType 1
-[PDFBOX-1870] - PDFunctionType0 incorrect
-[PDFBOX-2117] - AxialShadingContext is slow
-[PDFBOX-2279] - Text with gradient not shown
-[PDFBOX-2529] - Preflight: mention the page on which a problem has been found
-[PDFBOX-2531] - better error message on not yet read stream
-[PDFBOX-2535] - mention subtype in COSStream IOException
-[PDFBOX-2536] - More specific TIFFFaxDecoder exceptions
-[PDFBOX-2537] - do not discard underlying cause when creating validation error
-[PDFBOX-2611] - possibly incorrect error message "Hexa String must have only Hexadecimal Characters" in preflight
-[PDFBOX-2612] - error "Destination contains invalid page reference 'null'" is not detected by preflight
-[PDFBOX-2613] - Conflicting /N information for OutputIntent not detected by preflight
-[PDFBOX-2614] - missing /Type/FontDescriptor not detected by preflight
-[PDFBOX-2619] - XMP dates contain time zone, while document info dates do not, and this isn't detected by preflight
-[PDFBOX-2625] - Preflight error: The character with CID 0 should have a width equals to 57.0, but has 57.78
-[PDFBOX-2627] - Add block composer to handle multiline text
-[PDFBOX-2630] - "loop in destinations" not detected by preflight
-[PDFBOX-2647] - Check thumbnails in XMP metadata
-[PDFBOX-2718] - Allow to create new AcroForm fields from scratch
-[PDFBOX-2783] - Remove getCOSDictionary() method, adjust getCOSObject() return type
-[PDFBOX-2849] - fix problems with setting existing AcroForm buttons
-[PDFBOX-2863] - Support the comb flag for PDF forms
-[PDFBOX-2877] - Wrong text placement for autosize fields compared to Adobe generated
-[PDFBOX-2889] - Support appearance generation for choice fields
-[PDFBOX-2900] - PDF Debugger doesn't print inline images correctly
-[PDFBOX-2993] - Create a PDTransparencyGroup for added code clarity
-[PDFBOX-2994] - Rename PDGroup to PDTransparencyGroupAttributes
-[PDFBOX-3051] - COSArray.getObject() incorrect handling of indirect reference to COSNull
-[PDFBOX-3052] - NPE in PDFStreamEngine.ShowText when no font set
-[PDFBOX-3053] - Text extraction fails with type 3 fonts
-[PDFBOX-3057] - NPE in CFFParser.parseType1Dicts()
-[PDFBOX-3060] - Catalog cannot be found
-[PDFBOX-3061] - Word concatenation in 2.0 not in 1.8
-[PDFBOX-3062] - Text extraction and height different in 2.0
-[PDFBOX-3068] - Null metadata in 2.0 in some files that had metadata in 1.8.10 with old parser
-[PDFBOX-3112] - Avoid crazy /Length1 values in font descriptor
-[PDFBOX-3123] - Text extraction garbled in this file, was OK in 1.8
-[PDFBOX-3125] - IndexOutOfBoundsException in PDFont.getWidth()
-[PDFBOX-3126] - IndexOutOfBoundsException in PfbParser.parsePfb
-[PDFBOX-3127] - Text with vertical font not extracted correctly
-[PDFBOX-3129] - NullPointerException in PDFStreamEngine.showText()
-[PDFBOX-3186] - Parsing fails when XRef stream object is 1 byte later
-
Bug
-[PDFBOX-31] - bug with the Type3 font
-[PDFBOX-37] - Text Extraction Weirdness
-[PDFBOX-40] - Font problem when setting form value
-[PDFBOX-53] - Problem getting value from PDRadioCollection
-[PDFBOX-54] - please correct the SetField example
-[PDFBOX-62] - Incorrect (zero) character widths returned in some docs
-[PDFBOX-101] - ImportXFDF results in PDF with larger text fields
-[PDFBOX-123] - too many space made in extracted text file
-[PDFBOX-129] - Error when setting the value of a combo box to " "
-[PDFBOX-159] - Field renaming character set problem
-[PDFBOX-161] - java.util.EmptyStackException from PDFTextStripper.writeText
-[PDFBOX-166] - ConvertColorSpace RGB to CMYK
-[PDFBOX-198] - Tiff image problems
-[PDFBOX-205] - Miscellaneous errors on valid files
-[PDFBOX-239] - PDFToImage prints every word at the start of the line
-[PDFBOX-283] - Character encoding/appearance issues when filling forms
-[PDFBOX-297] - Printing fails
-[PDFBOX-308] - Unknown encoding for 'UniJIS-UCS2-H'
-[PDFBOX-317] - PDFont.getStringWidth() returns incorrect values
-[PDFBOX-326] - TrueType and characterHorizontalDisplacement
-[PDFBOX-412] - Failure to render PDFs with embedded fonts
-[PDFBOX-427] - ArrayIndexOutOfBoundsException in drawString
-[PDFBOX-447] - Image Convert Issue
-[PDFBOX-451] - PDFImageWriter does not convert chinese PDF correctly
-[PDFBOX-465] - invalid date formats
-[PDFBOX-484] - Spaces, numbers and some letters not display correctly
-[PDFBOX-488] - Invalid memory access of location 00000000 eip=968f5aa7 (MAC OS X)
-[PDFBOX-490] - Pdf Printing of text from embedded fonts
-[PDFBOX-501] - Open a trueType Font PDF, content become square box
-[PDFBOX-538] - CryptographyException on Adobe Distiller generated file
-[PDFBOX-587] - build script should support building without an internet connection
-[PDFBOX-648] - PdfBox can't be buit from behind a firewall/proxy
-[PDFBOX-649] - loading an fdf containing a file attachment throws IOException
-[PDFBOX-657] - PDFToImage does not work with certain fonts (for eg. PDF documents created by MS Office and OpenOffice)
-[PDFBOX-664] - Incorrect rendering of Slovak language PDF
-[PDFBOX-677] - Lines not showing in PrintPDF print-out (Table borders and SVG figures)
-[PDFBOX-723] - Our test hangs with custom pdf file on operation PDPage.convertToImage()
-[PDFBOX-725] - Text extraction fails due to font problem with Type0, supplement-0 font
-[PDFBOX-728] - Text extracted from a TeX-created PDF file comes in some form of hex encoding
-[PDFBOX-778] - OutOfMemory when extracting text from pdf
-[PDFBOX-785] - Spliting a PDF creates unnecessarily large files
-[PDFBOX-823] - NullPointerException in DateConverter.toISO8601(DateConverter.java:221)
-[PDFBOX-833] - Wrong encoding with Type1C font when specific encoding is defined
-[PDFBOX-837] - Wrong RevisionNumber when disabling all permissions and using 128bit encryption
-[PDFBOX-877] - processOperator breaks contract - never throws IOException
-[PDFBOX-904] - Potential issue with COSString and UTF-16-encoded Strings.
-[PDFBOX-905] - NullPointerException when writing pdf to image
-[PDFBOX-923] - pdf gets messed up when updated with xfdf data
-[PDFBOX-924] - Image not getting rendered correctly..
-[PDFBOX-932] - Swedish characters are garbled in form
-[PDFBOX-934] - ImageToPDF.createPDFFromImage causes problems for certain TIFF inputs
-[PDFBOX-940] - [pdmodel.font.PDFont] Error: Could not parse predefined CMAP file for 'PDFXC-Indentity0-0'
-[PDFBOX-962] - All sort of Problems when importing Xfdf files into PDFs -> damaged pdfs and NPEs
-[PDFBOX-965] - Printing of PDF with embedded OTF/TTF fonts is not working
-[PDFBOX-984] - When create images from PDF File with characters from PT-BR it´s printing wrong
-[PDFBOX-988] - pdmodel.font.PDSimpleFont hanging on TrueType font (ubuntu)
-[PDFBOX-989] - Scale Pdf: Fit to Printable Area
-[PDFBOX-1002] - Form field not rendered after being processed by pdfbox-1.1.0, wrong position of same field in pdfbox-1.5.0
-[PDFBOX-1007] - Maven performs textual filtering of binary resources [patch]
-[PDFBOX-1019] - PDF conversion to image crashes the JVM
-[PDFBOX-1020] - Can't read embedded font YDLRUT+ArialMT
-[PDFBOX-1036] - FDFExport/Import gives strange results
-[PDFBOX-1058] - Converting PDF to Image gives error and the image generated is of poor quality
-[PDFBOX-1060] - convertToImage includes "ghost" annotation outlines
-[PDFBOX-1069] - Ubuntu throws exceptions when fonts missing
-[PDFBOX-1071] - Can not generate chinese character PDF file
-[PDFBOX-1074] - TIFFFaxDecoder5 when using PDFImageWriter
-[PDFBOX-1086] - Error when decoding CCITT compressed data that contains EOLs, fill bits etc.
-[PDFBOX-1087] - FDF parsing is unreliable when xref are missing
-[PDFBOX-1107] - PDF created by Bullzip PDF Printer / www.bullzip.com / Freeware Edition shows weird characters
-[PDFBOX-1109] - Data corruption related to scratch file use
-[PDFBOX-1134] - fontbox not decoding font correctly for all characters
-[PDFBOX-1147] - Printing a PDF with an image inside show black.
-[PDFBOX-1148] - PDF with embedded fonts (Identity-H) not print.
-[PDFBOX-1152] - Gets scrambled japanese text while reading a PDF file
-[PDFBOX-1155] - setSuppressDuplicateOverlappingText sometimes removes characters that it shouldn't
-[PDFBOX-1164] - Inline image parsing error causes RuntimeException + FIX
-[PDFBOX-1206] - TrueType glyphs render incorrectly
-[PDFBOX-1207] - PDFPageProcessor.processStream() take 10 minutes to return
-[PDFBOX-1219] - org.apache.jempbox.impl.DateConverter unable to parse correct date value
-[PDFBOX-1231] - AcroForm appearance generator
-[PDFBOX-1234] - NPE at org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.calculateFontSize(PDAppearance.java:551)
-[PDFBOX-1242] - Handle non ISO-8859-1 chars with drawString
-[PDFBOX-1250] - CFF to Type1 Font conversion is missing/corrupting the font metrics
-[PDFBOX-1268] - OutOfMemory Error because of huge colors
-[PDFBOX-1273] - java.io.IOException: Error: Unknown annotation type null
-[PDFBOX-1276] - java.lang.NullPointerException on trying to set value for PDTextBox in pdf file.
-[PDFBOX-1278] - PDF file containing PDCIDFontType0 (PDType1CFont) does not render correctly to image
-[PDFBOX-1282] - Unicode characters displayed with wrong glyps because of interpretation as 8 bit strings
-[PDFBOX-1283] - Unicode characters displayed with wrong Advance
-[PDFBOX-1292] - Rendering of certain documents results in large tracts of blank space - even though contents can be extracted
-[PDFBOX-1296] - Warnung: Changing font on < > from to the default font
-[PDFBOX-1301] - Wrong characters in HTML/TXT file from PDF containing scanned pages/images
-[PDFBOX-1302] - Got ArrayIndexOutOfBoundsException in parsing a Chinese ttf file.
-[PDFBOX-1304] - Text extraction meets "Could not parse predefined CMAP" and returns just a small part of the content containing garbage chars.
-[PDFBOX-1307] - extracted images from a PDF sometimes come out inverted
-[PDFBOX-1321] - PDF rendered as black box
-[PDFBOX-1325] - Converting page to png creates an empty image
-[PDFBOX-1332] - Some inline font is can not parsed out
-[PDFBOX-1336] - JVM Crashes on Linux OS + Sun JVM + PDFBox
-[PDFBOX-1342] - Tags not fully preserved when merging PDFs.
-[PDFBOX-1348] - ExtractImages jpg and tiff picture from pdf but color wrong
-[PDFBOX-1351] - False paragraph caused by superscript (1.7 regression)
-[PDFBOX-1372] - NullPointerException with loadDescriptorDictionary
-[PDFBOX-1391] - Document with "embedded subset" fonts is displayed incorrect
-[PDFBOX-1403] - Retrieve FontDescription from descendant font
-[PDFBOX-1405] - Non-Ascii chars are not decoded correctly by pdfbox but works fine with pdftotext
-[PDFBOX-1412] - NullPointerException when getting fields from a PDF file
-[PDFBOX-1413] - Spaces replaced by é when exporting image
-[PDFBOX-1414] - EXCEPTION_ACCESS_VIOLATION in fontmanager.dll
-[PDFBOX-1419] - PDField.setValue is not behave correctly
-[PDFBOX-1426] - JVM crashes when trying to process the attached pdf's
-[PDFBOX-1435] - text is obscured by the Images
-[PDFBOX-1442] - bar chart converted from PDF is totally a black area.
-[PDFBOX-1452] - Greek Pdfs print out wrong characters
-[PDFBOX-1466] - Rendering of pattern colorspace fails
-[PDFBOX-1474] - PDDocument.decrypt does not throws InvalidPasswordException
-[PDFBOX-1478] - Problem with printing landscape document
-[PDFBOX-1506] - Incorrect visualization of PDF document via PageDrawer
-[PDFBOX-1511] - pdfMerger App produces Garbage
-[PDFBOX-1512] - TextPositionComparator is not compatible with Java 7
-[PDFBOX-1533] - When merging certain PDF's several odd looking empty pages occur in the result
-[PDFBOX-1541] - expected='endstream' actual='' failure to parse
-[PDFBOX-1550] - Helv vs. Helvetica font names cause PDField.setValue to fail
-[PDFBOX-1570] - PDFImageWriter creates black boxes for some images in the pdf
-[PDFBOX-1574] - ImportFDF fails to do anything
-[PDFBOX-1576] - StackOverflowError [COSDictionary.toString(COSDictionary.java:1418)]
-[PDFBOX-1585] - org.apache.pdfbox.util.PDFTextStripper.getText() causes thread to block indefinitely
-[PDFBOX-1595] - PDFMerger failed with the following exception: java.lang.NullPointerException
-[PDFBOX-1604] - FontBox is not storing all subroutines for CID-Keyed OTF CFF fonts possibly leading to rendering / width issues
-[PDFBOX-1606] - NonSequentialPDFParser produces garbage text in document info
-[PDFBOX-1607] - StringIndexOutOfBoundsException in PDFParser
-[PDFBOX-1608] - Rendering problem with Java 7 update 21
-[PDFBOX-1617] - Null pointer exception
-[PDFBOX-1618] - Split PDF file to single page files, some files are inflated in size
-[PDFBOX-1620] - Missing text in pdf reader view
-[PDFBOX-1622] - TextNormalize init not thread-safe, may lead to infinite loop
-[PDFBOX-1625] - java.lang.IndexOutOfBoundsException at writing PDF file
-[PDFBOX-1627] - Exception in thread "main" java.lang.NullPointerException
-[PDFBOX-1628] - Type 3 Fonts are not processed by PDPage.createImage
-[PDFBOX-1629] - Null PointerException
-[PDFBOX-1630] - An interesting Exception error
-[PDFBOX-1631] - Group Exception
-[PDFBOX-1632] - Exception with validation
-[PDFBOX-1633] - DateConverter needs to work
-[PDFBOX-1637] - Faulty documentation of PDStream.getInputStreamAsString()
-[PDFBOX-1638] - PDCcitt doesn't use color space
-[PDFBOX-1639] - Infinite loop with PDFParser used by tika.
-[PDFBOX-1642] - NPE when parsing XMP schema definition with "closed Choice" value type
-[PDFBOX-1643] - Check for missing validation processes does not work properly in Preflight
-[PDFBOX-1651] - PDFBox doesn't read the permission bits correct. PDDocument.getCurrentAccessPermission().canPrint() is allways returning true irrespective of the document print permissions
-[PDFBOX-1653] - Fix pdfbox eating up big chunks of memory for identical CID mappings
-[PDFBOX-1654] - Wasted work in XMLUtil.getNodeValue
-[PDFBOX-1655] - Wasted work (or incorrect behavior) in PDCIDFontType2Font.readCIDToGIDMapping
-[PDFBOX-1657] - glyph contours missing
-[PDFBOX-1658] - TTC fonts not supported for substitution
-[PDFBOX-1659] - Preflight 2.0.0 doesn't properly identify PDFs with encryption
-[PDFBOX-1660] - Error 6.2.4 results in description that looks more like the one belonging to 6.2.3
-[PDFBOX-1663] - Hello World using a TrueType font ArrayIndexOutOfBoundsException
-[PDFBOX-1664] - NullPointerException in PDType1Font.java
-[PDFBOX-1666] - Missing StemV font descriptor entry when embedding AFM fonts
-[PDFBOX-1668] - Loading a Russian PDF never finishes
-[PDFBOX-1670] - Printing pages rotated by 180 degrees is not working
-[PDFBOX-1671] - Error printing document java.lang.ArrayIndexOutOfBoundsException: 346
-[PDFBOX-1672] - Some characteres are missing after print thru PDFBox
-[PDFBOX-1674] - Preflight doesn't correctly parse PDF if obj identifier not followed by line terminator
-[PDFBOX-1678] - Convert to image problem
-[PDFBOX-1679] - java.io.IOException: Error: Expected an integer type, actual='f'
-[PDFBOX-1681] - java.lang.IllegalArgumentException: Color parameter outside of expected range: Red
-[PDFBOX-1683] - 2.0 build fails
-[PDFBOX-1688] - File with embedded subset renders no text
-[PDFBOX-1689] - Partial failure to render PDF
-[PDFBOX-1691] - "Foreign" characters are not rendered
-[PDFBOX-1692] - java.lang.OutOfMemoryError: Java heap space
-[PDFBOX-1694] - Bug in org.apache.pdfbox.io.Ascii85InputStream
-[PDFBOX-1696] - Bug in org.apache.pdfbox.io.Ascii85OutputStream
-[PDFBOX-1699] - Problem with generate jpg from pdf
-[PDFBOX-1705] - can not Write Hebrew and Chinese word into a PDF
-[PDFBOX-1708] - IndexOutOfBoundsException on convertToImage with an embedded Fax-Image
-[PDFBOX-1713] - [PATCH] Bullet character not rendered
-[PDFBOX-1714] - Merging PDFs results in java.io.IOException: expected='R' actual='0'
-[PDFBOX-1717] - Rendering to image has misplaced characters
-[PDFBOX-1718] - wrong glyphs displayed
-[PDFBOX-1719] - NPE while signing PDF - acroform without fields
-[PDFBOX-1724] - Method createColorModel not implemented for PDCalGray
-[PDFBOX-1725] - Character rendered at wrong position
-[PDFBOX-1727] - Content outside the MediaBox should not be rendered
-[PDFBOX-1730] - Image in PDF has extremely different colors when rendered
-[PDFBOX-1733] - Rectangles have one rounded edge in rendered image only
-[PDFBOX-1735] - Convert page pdf to image
-[PDFBOX-1737] - Skip whitespaces when resolving a XRef
-[PDFBOX-1740] - Umlaut not rendered correctly in TTF composite glyph
-[PDFBOX-1741] - [PATCH] Text should be in italic but is rendered upright
-[PDFBOX-1742] - type1CFont font with null encoding
-[PDFBOX-1743] - OutOfMemoryError in fontbox
-[PDFBOX-1749] - Out of memory exception when parsing TTF file
-[PDFBOX-1750] - PDTextbox and PDAnnotationWidget are not correct initialized from it's own constructor .
-[PDFBOX-1752] - Rendering PDF containing Jpeg2000 fails
-[PDFBOX-1753] - The font gets gibbrish when adding a line of text to an existing PDF with a table
-[PDFBOX-1754] - Preflight doesn't detect JavaScript for some PDFs
-[PDFBOX-1756] - ClassCastException CosString cannot be cast to COSName
-[PDFBOX-1758] - Preflight doesn't report Filespec dictionary that refers (indirectly) to an EmbeddedFile entry in some cases
-[PDFBOX-1760] - Regressions 28 Oct 2013
-[PDFBOX-1763] - Exception caused by "Invalid ICC Profile Data"
-[PDFBOX-1764] - PDFBox takes ages to render page 2 of the attached PDF
-[PDFBOX-1765] - Null pointer exception in PDFToImage
-[PDFBOX-1768] - cannot build last source code
-[PDFBOX-1770] - ExtractText gets all "?" when pdf 's font is instance of PDType1Font
-[PDFBOX-1771] - Cannot render FOP pdf with subsetted OTF CFF for both standard and CID-Keyed fonts
-[PDFBOX-1773] - Regression? Type 3 Fonts are not processed by RenderUtil.convertToImage
-[PDFBOX-1774] - StackOverflowError; Preflight->Font
-[PDFBOX-1776] - Print pdf with font embedded(SimSun TrueType(CID) Identity-H)
-[PDFBOX-1777] - memory leak in org.apache.pdfbox.cos.COSDocument
-[PDFBOX-1778] - Rounding issue in generated PDF file
-[PDFBOX-1780] - previous revision is damaged after signing
-[PDFBOX-1789] - NullPointerException at PDPageContentStream.setFont
-[PDFBOX-1790] - NPE during PDTrueTypeFont.loadTTF() on Mac TrueType font lacking Windows-platformID CMAPEncodingEntry
-[PDFBOX-1791] - Type3 glyphs with partial black background
-[PDFBOX-1794] - Rendering Problem with Type 3 Fonts
-[PDFBOX-1796] - Infiniteloop BaseParser.java:1010
-[PDFBOX-1799] - NullPointerException when constructing a PDJPeg using a BufferedImage
-[PDFBOX-1801] - xmp serializer does not generate valid xml for structured types
-[PDFBOX-1802] - COSDictionary in COSArray setDirect(true) but dic written indirect
-[PDFBOX-1804] - PDFTextStripper Issue related to word positions not correctly being parsed
-[PDFBOX-1808] - PDFTextStripper.getText - hight memory usage
-[PDFBOX-1810] - PDFToImage: Image of pdf is resized and drawn multiple times at top of output image
-[PDFBOX-1811] - java.io.IOException: Object at offset does not end with 'endobj'
-[PDFBOX-1812] - Illegal characters in XML output
-[PDFBOX-1813] - Stack overflow error in Main (no output file produced)
-[PDFBOX-1814] - In some cases PDPage converttoimage is extremely slow
-[PDFBOX-1818] - Push back buffer is full error
-[PDFBOX-1819] - Rendering problem with JPX image
-[PDFBOX-1822] - Signature byte range is Invalid
-[PDFBOX-1824] - [PATCH] CFF fonts render wrong glyphs
-[PDFBOX-1825] - [PATCH] Many pdfbox tests are never run
-[PDFBOX-1829] - PDF Extract Image Pixelmap Issue
-[PDFBOX-1830] - Grey background rectangle rendered at different position
-[PDFBOX-1831] - [PATCH] Fix: "Foreign" characters are not rendered
-[PDFBOX-1845] - PDDocument.load() give Error: Expected a long type at offset 1633
-[PDFBOX-1849] - Isartor test 6-3-5-t01-fail-a does not return the expected error code
-[PDFBOX-1860] - HTML converter escapes formatting close tags
-[PDFBOX-1861] - Line is incorrectly dashed
-[PDFBOX-1862] - Incomplete signature creation (regression in 1.8.3 with PDFBOX-1780)
-[PDFBOX-1864] - Non-embedded fonts not detected (or are they?)
-[PDFBOX-1865] - RenderUtil - rendering blank pages as images from PDF
-[PDFBOX-1868] - Garbled / distorted fonts during PDF to image conversion on recent versions
-[PDFBOX-1871] - Content appears a few px higher when rasterizing PDF
-[PDFBOX-1872] - PDMetadata.exportXMPMetadata fails when Metadata has encrypted stream
-[PDFBOX-1874] - PDFTextStripper.isParagraphSeparation(...)
-[PDFBOX-1875] - Image and some text missing in rendered file
-[PDFBOX-1876] - Incorrect color for DeviceN type 4 shading object
-[PDFBOX-1877] - Radial Shading (type 3) fails Ghent Workgroup tests
-[PDFBOX-1879] - Gibberish characters when converting pdf to image
-[PDFBOX-1880] - [PATCH] Type 1 Shading must not ignore current transformation matrix
-[PDFBOX-1882] - Negative array size exception when reading a string from a OTF font
-[PDFBOX-1884] - Avoid NPE when encountering null PDComplexFileSpecification
-[PDFBOX-1887] - Bugfixes + Optimization of Gouraud Shading
-[PDFBOX-1888] - JBIG2Filter is creating an ImageInputStream (with temp file) and not closing it
-[PDFBOX-1892] - Empty pages after rendering images: org.apache.pdfbox.util.operator.pagedrawer.Invoke
-[PDFBOX-1895] - Type0 settings /Registry and /Ordering are not decrypted when writing document
-[PDFBOX-1896] - Support MMType1 (Multiple Master) Fonts
-[PDFBOX-1900] - ConvertToImage - pdf - checkbox wrongly rendered
-[PDFBOX-1901] - null check confusing
-[PDFBOX-1908] - Drop shadow is too heavy (Transparency Groups)
-[PDFBOX-1910] - Text rendered as question marks
-[PDFBOX-1911] - Orange background from the pdf gets turned into blue in the png files.
-[PDFBOX-1916] - java.lang.ArrayIndexOutOfBoundsException in inlineimage
-[PDFBOX-1917] - Rendering hangs
-[PDFBOX-1918] - PDF with incorrect startxref
-[PDFBOX-1922] - NonSequentialParser not reading version in header and trailer
-[PDFBOX-1924] - Gouraud shading: detect empty triangles
-[PDFBOX-1925] - DeviceCMYK Colorspace: PDFToImage gives wrong output
-[PDFBOX-1928] - PDResources.getFonts() and PDresources.getXObjects() change underlying COSDictionary
-[PDFBOX-1929] - Drop shadow on text appears as a box
-[PDFBOX-1930] - TimesNewRoman font should be substituted
-[PDFBOX-1931] - Radial shading is missing
-[PDFBOX-1934] - converttoimage error and part of the pdf is not rendered
-[PDFBOX-1940] - Faulty pdf->image rendering
-[PDFBOX-1942] - Regression: java.lang.IndexOutOfBoundsException in shading
-[PDFBOX-1944] - Regression: NPE in test file
-[PDFBOX-1945] - Regression: NPE with inline image
-[PDFBOX-1948] - Regression: page renders mostly empty, text missing
-[PDFBOX-1950] - Inline image mask does not mask
-[PDFBOX-1953] - java.lang.IllegalArgumentException in SampledImageReader.getRGBImage()
-[PDFBOX-1954] - Regression: Some lines are too small / too long
-[PDFBOX-1955] - Regression: Colors much lighter
-[PDFBOX-1961] - Page with annotations renders fine with 1.8 but not with 2.0
-[PDFBOX-1965] - NPE in NonSequentialPDFParser when parseMinimal property is set to true
-[PDFBOX-1966] - Type 1, 4 and 5 shadings for shFill()
-[PDFBOX-1969] - JPEGFactory bug
-[PDFBOX-1977] - LZWFilter fails
-[PDFBOX-1978] - Type1FontUtilTest is non-deterministic
-[PDFBOX-1979] - TypeTestingHelper is non-deterministic
-[PDFBOX-1980] - TestCOSFloat is non-deterministic
-[PDFBOX-1981] - CryptographyException for file that isn't encrypted
-[PDFBOX-1983] - Unable to add TIF images, CCITTFactory not working
-[PDFBOX-1984] - PDFont documentation correction needed for getFontWidth and getFontHeight
-[PDFBOX-1988] - PDFBox ExtractText issue of PDF with no embedded fonts
-[PDFBOX-1992] - text in pdf with convertToImage not rendered
-[PDFBOX-1993] - Gray color images much lighter
-[PDFBOX-1995] - AdobePDFSchema.getProducer() returns empty string
-[PDFBOX-1997] - CIE LAB item missing in rendering
-[PDFBOX-1999] - JBIG2Filter - FlateDecoded Globals Table
-[PDFBOX-2000] - White page when converting first page to image
-[PDFBOX-2001] - Digital Signature information (parser bug?)
-[PDFBOX-2005] - JDK 1.8 build fails in TestTTFParser
-[PDFBOX-2007] - Performance regression since PDFRenderer
-[PDFBOX-2008] - Off-by-one error in BaseParser.readGenerationNumber()
-[PDFBOX-2009] - PDFStreamEngine.processEncodedText incorrectly handling UTF-16 text with BOM FEFF
-[PDFBOX-2015] - Hybrid reference pdf still contain XRefStm info in the trailer dictionary afterPDDocument#save
-[PDFBOX-2016] - Stream parsing still incorrect if length value is wrong
-[PDFBOX-2020] - PDF/A Validation raises NullPointerException for PDFs without ImageColorSpace
-[PDFBOX-2021] - PDFPrinter problem with landscape and rotated pages
-[PDFBOX-2022] - silentPrint(no args) doesn't use the printerJob field
-[PDFBOX-2023] - Text extraction gets zero font height for type3 fonts
-[PDFBOX-2024] - /Rotate 180 PDF is not displayed correctly in PDFReader app
-[PDFBOX-2026] - cannot load jpg into new pdf
-[PDFBOX-2032] - [PATCH] TTF Type12 IOException: Invalid Characters codes
-[PDFBOX-2035] - Ignore badly formatted toUnicode CMaps
-[PDFBOX-2036] - Add test with LZW fail sequence
-[PDFBOX-2037] - Glyph in type1CFont not rendered
-[PDFBOX-2038] - Method VisualSignatureParser#parse does not close COSDocument
-[PDFBOX-2042] - ColorSpace with empty Range array
-[PDFBOX-2044] - TrueType glyphs not displayed in rendering
-[PDFBOX-2045] - Merging PDFs with a Form has no effect
-[PDFBOX-2046] - [PATCH] Can't read the embedded Type1 font
-[PDFBOX-2047] - read operations alter PDLab object
-[PDFBOX-2050] - Add predictor to LZW filter
-[PDFBOX-2054] - Remove System.out.println()
-[PDFBOX-2057] - Importing BufferedImage into PDPixelMap is broken in 1.8.5
-[PDFBOX-2058] - The text of pdfs using Type1C can't be extracted correct
-[PDFBOX-2062] - Setting a PDFFormField's value with a specific font size causes the font size to change on click
-[PDFBOX-2063] - Incomplete EOF detection in ASCIIHexFilter
-[PDFBOX-2065] - Missing getCOSObject() in PDCalRGB
-[PDFBOX-2067] - Error creating JPEG image with SMask
-[PDFBOX-2070] - Filter.decode() modifies PDF if there is a filter array
-[PDFBOX-2072] - Wrong calculation of space char width in PDFStreamEngine
-[PDFBOX-2073] - PDF files with unusual Japanese font can not be rewrite correctly
-[PDFBOX-2074] - 4-bytes CMap entry causes exception
-[PDFBOX-2079] - Extra new line characters extracted in 1.8.5 for embedded files leading to ZipFile exception in Java 1.6
-[PDFBOX-2082] - signing corrupts PDF when signature exactly fits allocated space
-[PDFBOX-2091] - Some characters are not rendered (font with symbol encoding)
-[PDFBOX-2095] - Useless memory allocation in GlyfDescript
-[PDFBOX-2098] - Gouraud shading doesn't appear
-[PDFBOX-2100] - Gouraud shading doesn't work with function
-[PDFBOX-2101] - Surprising memory consumption when extracting images
-[PDFBOX-2102] - Characters swallowed on COSString.getString()
-[PDFBOX-2103] - JPXFilter fails to decode some Jpeg2000 images
-[PDFBOX-2106] - getSuffix() returns null for RLE encoding
-[PDFBOX-2108] - Type0 CFF Font with identity encoding rendered incorrectly
-[PDFBOX-2109] - CFFParser uses String constructor without encoding
-[PDFBOX-2110] - Font not found: CourierNew
-[PDFBOX-2111] - Cast error in Gouraud shadings
-[PDFBOX-2114] - ObjStm is being processed to late
-[PDFBOX-2115] - Use unfiltered stream in gouraud shadings
-[PDFBOX-2120] - Regression: Type 1 font corrupted
-[PDFBOX-2122] - FontBox's TTFDataStream doesn't set timezone in readInternationalDate
-[PDFBOX-2128] - CMYK images are not supported correctly
-[PDFBOX-2133] - Parsing of a Type1 font fails with a NumberFormatException
-[PDFBOX-2134] - Parsing of a Type1 font fails with a NPE
-[PDFBOX-2140] - non embedded Type1 symbol glyph not rendered
-[PDFBOX-2141] - Shading not applied to text
-[PDFBOX-2147] - Clean up code with "inspect and transform"
-[PDFBOX-2153] - Setting the correct clipping path for shading
-[PDFBOX-2155] - Fix JavaDocs warnings
-[PDFBOX-2156] - different shading patterns at different resolutions when ctm is null
-[PDFBOX-2158] - ExtractText missing most of text in this PDF file, due to font bounding box with minus infinity
-[PDFBOX-2160] - PDFTextStripper doesn't always write paragraph start
-[PDFBOX-2163] - inline image with EI in the middle incorrectly parsed
-[PDFBOX-2166] - AIOOBE with barcode ttf font
-[PDFBOX-2168] - Different behavior of Undo feature when form was pre filled by PDFBox
-[PDFBOX-2170] - java.lang.ClassCastException: org.apache.fontbox.cff.CharStringCommand cannot be cast to java.lang.Integer
-[PDFBOX-2171] - UnsupportedOperationException for stencil image / pattern
-[PDFBOX-2173] - Nullpointer when validating empty file
-[PDFBOX-2176] - Ignore IllegalArgumentException when reading an ICCProfile
-[PDFBOX-2177] - [PATCH] IndexOutOfBoundsException reading embedded OpenType font
-[PDFBOX-2178] - Invalid color space kind: COSName{DeviceGray}
-[PDFBOX-2179] - Regression: Some isartor tests are not passing in 2.0.0
-[PDFBOX-2181] - Regression: NPE in PreflightContentStream
-[PDFBOX-2183] - COSArray cannot be cast to COSNumber
-[PDFBOX-2184] - CMMException: Invalid profile data
-[PDFBOX-2185] - Rotation and skew not applied on rectangles
-[PDFBOX-2186] - java.io.IOException: Catalog cannot be found
-[PDFBOX-2187] - ArrayIndexOutOfBoundsException in TIFFFaxDecoder
-[PDFBOX-2188] - java.io.IOException: Expected a name or array but got: COSObject{1823, 0}
-[PDFBOX-2189] - java.awt.geom.IllegalPathStateException: missing initial moveto in path definition
-[PDFBOX-2191] - Identity function not implemented
-[PDFBOX-2192] - "unknown command" in Type1CharString.handleCommand
-[PDFBOX-2193] - ClassCastException in PDExtendedGraphicsState.getFontSetting()
-[PDFBOX-2194] - Refactor predictor
-[PDFBOX-2195] - Missing text when converting PDF to image
-[PDFBOX-2198] - ClassCastException in COSArrayList.convertIntegerCOSArrayToList for font widths
-[PDFBOX-2199] - Found Token[kind=NAME, text=dup] but expected begin
-[PDFBOX-2200] - Memory leak with org.apache.pdfbox.pdmodel.font.PDFont#cmapObjects
-[PDFBOX-2201] - getKeywords returns null although keywords are present
-[PDFBOX-2202] - java.io.IOException: Found Token[kind=NAME, text=readonly] but expected def
-[PDFBOX-2203] - java.lang.IllegalArgumentException: alpha value out of range
-[PDFBOX-2204] - Indexed color space in JPX
-[PDFBOX-2206] - Cannot save a document which has been closed
-[PDFBOX-2207] - Stream parsing still incorrect if length value is wrong
-[PDFBOX-2212] - OutOfMemoryError in GlyfCompositeDescrip
-[PDFBOX-2214] - EmptyStackException in PDFStreamEngine
-[PDFBOX-2215] - NPE in PDTrueTypeFont.makeFontDescriptor
-[PDFBOX-2216] - java.io.IOException: Found Token[kind=NAME, text= ] but expected LITERAL for type1 font
-[PDFBOX-2217] - Matrix transform ignored in axial and radial shadings (in PDFToImage output)
-[PDFBOX-2221] - Text is pink
-[PDFBOX-2222] - NPE in PDFStreamEngine
-[PDFBOX-2225] - ClassCastException in PDFMergerUtility.appendDocument
-[PDFBOX-2227] - java.io.IOException: Found Token[kind=NAME, text= ] but expected LITERAL for type1 font
-[PDFBOX-2228] - LZW EarlyChange parameter isn't supported
-[PDFBOX-2229] - NPE in GlyfCompositeDescript.getPointCount
-[PDFBOX-2234] - [PATCH] Invalid Color space preflight error on Java 8
-[PDFBOX-2237] - java.io.IOException: Image stream is empty for inline image
-[PDFBOX-2240] - ArrayIndexOutOfBoundsException PDImageXObject.applyMask
-[PDFBOX-2241] - IOException: Expected INTEGER or REAL but got NAME
-[PDFBOX-2243] - java.lang.IllegalArgumentException: negative dash phase
-[PDFBOX-2244] - java.lang.IndexOutOfBoundsException in callothersubr
-[PDFBOX-2245] - java.lang.StringIndexOutOfBoundsException in PDTrueTypeFont.getGIDForCharacterCode
-[PDFBOX-2247] - Regression in text extraction between 1.8.5 and 1.8.6
-[PDFBOX-2251] - NoSuchElementException when reading cmap format 4 subtable
-[PDFBOX-2256] - Text size renders wrong
-[PDFBOX-2257] - BufferedInputStream wrapped in BufferedInputStream
-[PDFBOX-2261] - Extremely long hang during getFields() on a few PDF files
-[PDFBOX-2265] - ArrayIndexOutOfBoundsException in PDICCBased.loadICCProfile
-[PDFBOX-2266] - NPE when converting page to image
-[PDFBOX-2267] - IOException and partial rendering and colorspace creation error
-[PDFBOX-2268] - AES-256 decryptions fails
-[PDFBOX-2270] - PDField.getFullyQualifiedName() returns name adding suffix '.null'
-[PDFBOX-2271] - Potential NPE in PDAppearanceString.java
-[PDFBOX-2275] - ClassCastException in PDResources
-[PDFBOX-2278] - Exception in thread "main" java.lang.IllegalStateException: Call to processSubStream() before processStream() or initStream()
-[PDFBOX-2280] - Text not italic
-[PDFBOX-2281] - Yellow box shown
-[PDFBOX-2283] - Incorrect transform for annotations / appearance streams
-[PDFBOX-2284] - NullPointerException in PDFieldTreeNode
-[PDFBOX-2285] - debugLogMetadata doesn't log
-[PDFBOX-2287] - [PATCH] COSStream loses contents in setFilters()
-[PDFBOX-2291] - Differences in Overlay stamping between version 1.8.2 and 1.8.6
-[PDFBOX-2292] - Saving of decrypted version of password protected document gives an error
-[PDFBOX-2293] - NonSequential parser gives an error
-[PDFBOX-2295] - Checkboxes missing
-[PDFBOX-2296] - Wrong stream length
-[PDFBOX-2298] - Wrong scaling of embedded type 1 font
-[PDFBOX-2299] - Isartor tests don't work anymore
-[PDFBOX-2300] - Glyphs rendered at wrong position
-[PDFBOX-2301] - RandomAccessBuffer consumes too much memory.
-[PDFBOX-2304] - square glyphs missing
-[PDFBOX-2306] - Error reading stream, expected='endstream' actual='endobj'
-[PDFBOX-2307] - NPE in TrueTypeFont.getWidth
-[PDFBOX-2309] - UnsupportedOperationException: not implemented: missing CFF
-[PDFBOX-2310] - codeToGID NPE
-[PDFBOX-2311] - color space 'COSName{DefaultRGB}' does not exist in the current page's resources
-[PDFBOX-2312] - IllegalArgumentException: Built-in Encoding required for symbolic font
-[PDFBOX-2313] - ExtractImages finds never-rendered images
-[PDFBOX-2314] - Restore backward compatibility between Overlay and OverlayPDF
-[PDFBOX-2315] - Found Token[kind=NAME, text=ND] but expected ND
-[PDFBOX-2317] - ZapfDingbats should use its own glyph list
-[PDFBOX-2318] - NPE in new DomXmpParser when no type is found
-[PDFBOX-2319] - Date Converter needs to handle miliseconds and other formats
-[PDFBOX-2320] - IOException: Could not read embedded TTF for font TimesNewRoman
-[PDFBOX-2323] - More flexible image caching (OOM)
-[PDFBOX-2324] - Failure to render DeviceN image
-[PDFBOX-2325] - Failure to render OpenType (TrueType)
-[PDFBOX-2326] - IllegalArgumentException: Use PDType1CFont for FontFile3
-[PDFBOX-2327] - Glyph list ligatures are decomposed too early
-[PDFBOX-2330] - Typo on usage message; "PDFDBox" instead of "PDFBox"
-[PDFBOX-2332] - Error reading stream, expected='endstream' actual='endstream8' at offset 1993
-[PDFBOX-2334] - codeToGID NPE
-[PDFBOX-2338] - IllegalStateException: recursive definition
-[PDFBOX-2339] - ArrayIndexOutOfBoundsException when type1 font is empty
-[PDFBOX-2342] - WriteDecodedDoc cant decrypt pdf form correctly
-[PDFBOX-2343] - Giving NullPoint exception when we call PDType1Font.HELVETICA_BOLD.getStringWidth("Some String")
-[PDFBOX-2344] - NegativeArraySizeException in radial shading
-[PDFBOX-2345] - IndexOutOfBoundsException reading encrypted pdf
-[PDFBOX-2347] - NPE while creating security handler for decryption
-[PDFBOX-2350] - Type1 Parser hangs indefinitely
-[PDFBOX-2351] - /XRefStm content missing in saved file
-[PDFBOX-2352] - NegativeArraySizeException in HorizontalMetricsTable.read
-[PDFBOX-2355] - newDocuments is private in Splitter
-[PDFBOX-2356] - Error Validating PDF Archive Document with half hour timezone
-[PDFBOX-2357] - PDTrueTypeFont has no method to load font from stream
-[PDFBOX-2358] - ExternalFonts uses classloader of class in font-box
-[PDFBOX-2360] - PDFont had methods removed
-[PDFBOX-2363] - wrong color in rendering
-[PDFBOX-2364] - CCITT image renders incorrectly
-[PDFBOX-2365] - NPE with file with PDFDocEncoding
-[PDFBOX-2367] - Ligature glyph widths wrong
-[PDFBOX-2372] - Trash Glyphs: Regressions 19.9.2014
-[PDFBOX-2373] - Rendering at 72 dpi crashes java
-[PDFBOX-2376] - Small regression in text extraction with PDFBox 1.8.7 vs. 1.8.6
-[PDFBOX-2379] - glyphlist_ext is not OSGI compatible
-[PDFBOX-2380] - Glyphlist .properties are not ordered
-[PDFBOX-2381] - BaseParser - IOException: Push back buffer is full
-[PDFBOX-2383] - PDFBox tests include copyright files
-[PDFBOX-2384] - ExtractText should default to UTF-8
-[PDFBOX-2385] - inline image with EI at the end incorrectly parsed
-[PDFBOX-2390] - PDExtendedGraphicsState is incorrectly named
-[PDFBOX-2395] - Signing PDF document changes documentID
-[PDFBOX-2396] - Comment on `org.apache.pdfbox.util.Splitter.createNewDocumentIfNecessary` is out of date
-[PDFBOX-2399] - font.getFontDescriptor() for PDType1Font.HELVETICA is null
-[PDFBOX-2401] - Image has wrong colors after Merge
-[PDFBOX-2402] - NonSequentialPDFParser cannot recover from spurious closing brackets
-[PDFBOX-2403] - false negative? "Font damaged, The FontFile can't be read"
-[PDFBOX-2405] - false negatives: Invalid Font definition, Some required fields are missing from the Font ... ?
-[PDFBOX-2406] - fix typo "AlpaConstant"
-[PDFBOX-2407] - false negative: 2.4.3 : Invalid Color space, The operator "f" can't be used without Color Profile ?
-[PDFBOX-2408] - false negative? 1.2.1 : Body Syntax error, Single space expected ...
-[PDFBOX-2409] - got the wrong result from Arabic text extraction
-[PDFBOX-2411] - Pushback buffer is full on seamingly small PDF
-[PDFBOX-2412] - Loading XFDF document fails with ClassCastException
-[PDFBOX-2413] - Loaded FDF document returns null fields
-[PDFBOX-2415] - java.lang.ClassCastException: org.apache.pdfbox.pdmodel.font.PDType1CFont cannot be cast to org.apache.pdfbox.pdmodel.font.PDType1Font
-[PDFBOX-2416] - xmp regression? 7.3 : Error on MetaData, Cannot find a definition for the namespace http://ns.adobe.com/xap/1.0/t/pg/
-[PDFBOX-2417] - xmp regression? 7.3 : Error on MetaData, Schema is not set in this document : http://ns.adobe.com/xap/1.0/sType/Dimensions#
-[PDFBOX-2418] - xmp regression? 7.3 : Error on MetaData, Schema is not set in this document : http://ns.adobe.com/xap/1.0/g/img/
-[PDFBOX-2419] - XFDF export is not XML compliant
-[PDFBOX-2421] - Poor text extraction and rendering of file with non embedded type1 font
-[PDFBOX-2422] - PDFont.getStringWidth results in stackoverflow
-[PDFBOX-2424] - ClassCastException in getMetaData if no real meta data
-[PDFBOX-2426] - Make ExternalFonts.getProvider public
-[PDFBOX-2428] - An error occured when reading table hmtx
-[PDFBOX-2429] - Times New Roman rendered as Arial
-[PDFBOX-2433] - PDFPrinter does not print Acroform fields
-[PDFBOX-2434] - ClassCastException in readVersionInTrailer
-[PDFBOX-2436] - Parsing error
-[PDFBOX-2437] - PDFont isSymbolic() has unexpected return value
-[PDFBOX-2439] - [PATCH] ArrayIndexOutOfBoundsException in multithreaded system
-[PDFBOX-2441] - Improve XRef self healing mechanism when more than one xref table
-[PDFBOX-2445] - Out of Memory - Extract text for Apache_Solr_4.7_Ref_Guide.pdf
-[PDFBOX-2447] - "Cannot save a document which has been closed" when encrypting
-[PDFBOX-2448] - ligatures and some glyphs missing
-[PDFBOX-2453] - Building on OpenJDK throws javax.imageio.IIOException
-[PDFBOX-2455] - NonSequentialParser does not tolerate missing %%EOF markers
-[PDFBOX-2457] - LogFactory is intialized with a wrong class
-[PDFBOX-2458] - Signing doesn't work anymore using BC 1.51 instead of 1.50
-[PDFBOX-2460] - fix TestPublicKeyEncryption.java
-[PDFBOX-2462] - NullPointerException in (PDFStreamParser.java:109)
-[PDFBOX-2465] - NPE in PdfaExtensionHelper.populateSchemaMapping
-[PDFBOX-2466] - 2.4 : Invalid Color space, Unable to read default color space : Missing color space: DefaultRGB
-[PDFBOX-2469] - javax.crypto.BadPaddingException in PDFBox 1.8.8-SNAPSHOT
-[PDFBOX-2470] - Exception in PDDocument.addSignature(PDSignature sigObject, SignatureInterface signatureInterface, SignatureOptions options))
-[PDFBOX-2471] - AES encryption failing to write Acroform field names and values
-[PDFBOX-2477] - NPE in DomXmpParser.createProperty
-[PDFBOX-2478] - NPE in XObjImageValidator.checkColorSpaceAndImageMask
-[PDFBOX-2479] - NPE in PDICCBased.getColorSpaceType
-[PDFBOX-2481] - Adding large TYPE_BYTE_BINARY image to pdf document generates distorted result
-[PDFBOX-2483] - StackOverflowError in preflight
-[PDFBOX-2484] - Cannot decrypt AES256 encrypted files with nonSeq parser
-[PDFBOX-2485] - IllegalArgumentException in TypeMapping.instanciateSimpleProperty
-[PDFBOX-2486] - ClassCastException in preflight: PDNonTerminalField cannot be cast to PDField
-[PDFBOX-2487] - ArrayIndexOutOfBoundsException in Type1CharString
-[PDFBOX-2488] - NPE in FontValidator.isSubSet in preflight
-[PDFBOX-2489] - StackOverflowError in PDSimpleFont.isFontSymbolic
-[PDFBOX-2490] - Return value of COSDocument#isEncrypted is unclear
-[PDFBOX-2491] - NPE in PDFAIdentificationValidation.checkConformanceLevel()
-[PDFBOX-2492] - Java 8u25 IllegalBlockSizeException decrypting pdf
-[PDFBOX-2495] - Black shapes in the background of some rendered pages of some PDFs
-[PDFBOX-2496] - PNG filesize is larger
-[PDFBOX-2498] - ArrayIndexOutOfBoundsException in PreflightParser.lastIndexOf
-[PDFBOX-2499] - EOF and NPE in PDType1CFont.getFontMatrix
-[PDFBOX-2500] - ClassCastException in StreamValidationProcess.checkFilters
-[PDFBOX-2501] - Page render without barcode
-[PDFBOX-2502] - false negative? 1.4.6 : Trailer Syntax error, ID is different in the first and the last trailer
-[PDFBOX-2503] - false negative? 1: 7.2 : Error on MetaData, Producer present in the document catalog dictionary doesn't match with XMP information
-[PDFBOX-2504] - ClassCastException in preflight: PDAnnotationWidget cannot be cast to PDField
-[PDFBOX-2505] - ArrayIndexOutOfBoundsException in PDColor constructor
-[PDFBOX-2507] - Annotation example not rendered to image
-[PDFBOX-2508] - Text extraction getting zero font height, bad widths, and ? for text in this PDF with Type 3 Fonts
-[PDFBOX-2509] - Korean Text font substitution issues
-[PDFBOX-2513] - false negative? RuntimeException: EOL encountered in white run
-[PDFBOX-2517] - Better error message on pdfA identification
-[PDFBOX-2519] - Regression: Box color missing
-[PDFBOX-2520] - Don't decrypt already decrypted pdfs
-[PDFBOX-2521] - Don't throw IOException if stream length is missing in lenient mode
-[PDFBOX-2523] - IOException: Error: Expected a long type at offset 1218571, instead got 'xref'
-[PDFBOX-2525] - Overlay: data black & white after import
-[PDFBOX-2526] - Arial black not black
-[PDFBOX-2527] - IOException: Negative seek offset in NonSequentialPDFParser
-[PDFBOX-2528] - IOException: Object must be defined and must not be compressed object: 0:0
-[PDFBOX-2533] - Poor rendering with non-sequential parser
-[PDFBOX-2540] - ArrayIndexOutOfBoundsException in Type1Parser.parseASCII
-[PDFBOX-2541] - ClassCastException in BaseParser.parseCOSDictionaryValue
-[PDFBOX-2542] - IllegalArgumentException: root must be of type Pages
-[PDFBOX-2543] - ClassCastException in PDFontDescriptor.getFontFile2
-[PDFBOX-2546] - IllegalArgumentException: resourceDictionary is null in PDFMerger
-[PDFBOX-2549] - TIFF-Predictor with 16 bits per component not supported
-[PDFBOX-2550] - ClassCastException in PDAnnotation.getColour
-[PDFBOX-2552] - Blank rendering when negative page rotation
-[PDFBOX-2553] - CalRGB colors different
-[PDFBOX-2557] - Yellow text not using heavy font
-[PDFBOX-2559] - TTF font cannot be loaded
-[PDFBOX-2560] - Arial Truetype CID font rendering incorrect
-[PDFBOX-2561] - Rendering of PDIndexed line incorrect
-[PDFBOX-2563] - [PATCH] Use cmap for Type0/TTF fallback
-[PDFBOX-2569] - COSNumber fails to parse numbers like "+018" in JRE <= 1.6
-[PDFBOX-2570] - ClassCastException in PDCalGray: COSFloat cannot be cast to COSArray
-[PDFBOX-2571] - IllegalStateException: Not a CIDFont
-[PDFBOX-2572] - ArrayIndexOutOfBoundsException in CmapSubtable.processSubtype12
-[PDFBOX-2573] - IllegalStateException: PDFBox bug: encoding should not be null!
-[PDFBOX-2579] - Exception in thread "main" java.io.IOException: Error: Expected a long type at offset 1029, instead got '12688(Deleted'
-[PDFBOX-2582] - Form fields missing entirely or incorrect in PDField list
-[PDFBOX-2583] - Error when rendering a PDF with annotations
-[PDFBOX-2586] - IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
-[PDFBOX-2588] - Text fields if initialy empty in AcroForms do not contain a COSName.V in its dictionary and therefore does not get rendered.
-[PDFBOX-2595] - Pdfbox always sets the second part of documentID to the same value
-[PDFBOX-2598] - IllegalArgumentException in CFFParser.readCharset
-[PDFBOX-2599] - failure to render file with utf8 CID TT fonts
-[PDFBOX-2601] - fix getHashObjectIdentifier in TSAClient
-[PDFBOX-2605] - Multiple text operations on page cause NPE in TTFSubsetter
-[PDFBOX-2606] - Support OS with no fonts
-[PDFBOX-2607] - Failed reading embedded Font
-[PDFBOX-2608] - false negative on pdf/A validation?
-[PDFBOX-2615] - IllegalArgumentException in PDPageTree constructor: root cannot be null
-[PDFBOX-2616] - JVM crashes while trying to convert PDF to JPG image (only on Windows)
-[PDFBOX-2617] - Group of Button fields treated as a Radio Button group
-[PDFBOX-2620] - Support named actions
-[PDFBOX-2621] - Files created with CreatePDFA.java are not PDF/A-1b
-[PDFBOX-2622] - PDAnnotationLink::getBorderStyle() don't understand external border style
-[PDFBOX-2629] - PDAnnotation should not use PDGamma for colors
-[PDFBOX-2632] - Lost output when mixing subset and non-subset of the same font
-[PDFBOX-2634] - Multiple text operations on multiple pages cause NPE in TTFSubsetter
-[PDFBOX-2635] - PrintImageLocations outputs utter nonsense
-[PDFBOX-2636] - Colorspaces of annotations not treated correctly
-[PDFBOX-2640] - Fields within a fields kids entry are not correctly recognized
-[PDFBOX-2641] - ArrayIndexOutOfBoundsException in PDType1Font constructor
-[PDFBOX-2646] - A text including single-quote is malformed with Embedded TTF font
-[PDFBOX-2649] - Character widths incorrect in a loaded font
-[PDFBOX-2650] - Type1Equivalent: TrueType must use 'cmap' when 'post' table is empty
-[PDFBOX-2651] - Preflight doesn't check for valid destination syntax
-[PDFBOX-2652] - Document Outlines (Bookmark) and Link Annotation validation do not validate /Dest item
-[PDFBOX-2653] - Image extraction fails with attached PDF
-[PDFBOX-2654] - NullPointerException when reading a GIF file with a transparent color
-[PDFBOX-2655] - PDCIDFontType2Embedder.buildCIDSet() ArrayOutOfBounds
-[PDFBOX-2656] - Trailer isn't written when signing a PDF
-[PDFBOX-2660] - Text missing
-[PDFBOX-2664] - PDDocumentInformation shouldn't throw IOException
-[PDFBOX-2665] - PDType1Font (HELVETICA) encode getting NullPointerException
-[PDFBOX-2667] - StandardSecurityHandler should throw InvalidPasswordException
-[PDFBOX-2668] - intersectClippingPath does a shallow copy
-[PDFBOX-2675] - PDOutlineNode.getParent uses /P item as fallback for /Parent
-[PDFBOX-2676] - PDPageLabelRange.setLabelItem() should not allow negative startPage
-[PDFBOX-2677] - Negative Outlines COUNT and various issues
-[PDFBOX-2678] - possible NPE in ExtractText tool of trunk
-[PDFBOX-2679] - Blank page rendered with wrong xref start objid
-[PDFBOX-2687] - ClassCastException when trying to get OutputIntents or add to it
-[PDFBOX-2693] - OutOfMemoryError at org.apache.fontbox.cff.IndexData.initData(IndexData.java:95)
-[PDFBOX-2698] - PDFToImage IndexOutOfBoundsException
-[PDFBOX-2702] - Merging PDFs created using "Nuance PDF Create" not possible
-[PDFBOX-2704] - PDPageTree.indexOf doesn't find page numbers
-[PDFBOX-2708] - PDDocument.removePage() deletes the last page regardless of parameter passed
-[PDFBOX-2711] - Japanese text not extracted
-[PDFBOX-2713] - Preserve the origin pdf version when splitting a pdf
-[PDFBOX-2714] - Type1Fonts working on one computer, not another
-[PDFBOX-2715] - Pages in a PDF being dropped with just an error-log message
-[PDFBOX-2717] - Keep type and subtype for PDWidgetAnnotation created from field
-[PDFBOX-2719] - The addSignature() method always set the visual signature on the last page of the PDF
-[PDFBOX-2720] - Can't sign PDF document with forms or annotations
-[PDFBOX-2723] - PDFBox*.tmp files not deleted by COSParser
-[PDFBOX-2724] - Importing a XFDF file doesn't populate the field value
-[PDFBOX-2726] - org.apache.pdfbox.cos.COSArray cannot be cast to org.apache.pdfbox.cos.COSDictionary
-[PDFBOX-2728] - java.awt.geom.IllegalPathStateException: missing initial moveto in path definition
-[PDFBOX-2730] - PDFSplit slow and keeps unused pages
-[PDFBOX-2733] - Nullpointer exception in PDFXrefStreamParser.parse
-[PDFBOX-2734] - Can't create PDF with DeviceN colorspace
-[PDFBOX-2739] - Saving merged documents causes IOException
-[PDFBOX-2741] - IndexOutOfBoundsException when calling PDSeparation.setAlternateColorSpace
-[PDFBOX-2745] - PDPageXYZDestination zoom property can't be set lower than 100%
-[PDFBOX-2746] - PDPageContentStream.saveGraphicsState() saves wrong nonStrokingColor and throws an exception
-[PDFBOX-2747] - pdfbox: garbled japanese txt output
-[PDFBOX-2750] - Rendering in poor quality in 2.0 but not in 1.8.*
-[PDFBOX-2759] - NPE in BaseParser.parseCOSDictionaryValue() due to object reference in content stream
-[PDFBOX-2760] - NPE in MoveText.process()
-[PDFBOX-2767] - ClassCastException in PDDocument.addSignature
-[PDFBOX-2769] - NPE when saving encrypted file
-[PDFBOX-2771] - COSString encodes Euro sign wrongly
-[PDFBOX-2772] - EI token lost for rewrite
-[PDFBOX-2773] - ClassCastException in PDDocumentCatalog.java:339
-[PDFBOX-2774] - Can't encode Euro with WinAnsiEncoding
-[PDFBOX-2775] - ArrayIndexOutOfBoundsException in PDFTextStripper.processTextPosition()
-[PDFBOX-2778] - PDF to Image conversion fails with "Invalid code word encountered"
-[PDFBOX-2781] - Opening pdf document after encrypting it with PDFBox throws IllegalBlockSizeException
-[PDFBOX-2786] - PDPageDestination page index off by one
-[PDFBOX-2789] - TTF encoding issues
-[PDFBOX-2792] - Text extraction ignores bookmarks
-[PDFBOX-2793] - /Dests dictionary isn't supported
-[PDFBOX-2794] - UnsupportedOperationException: not supported for Type 3 fonts
-[PDFBOX-2795] - PrintRequestAttributeSet is being ignored
-[PDFBOX-2797] - PDJavascriptNameTreeNode does not support dictionaries
-[PDFBOX-2798] - PDTextStream does not support UTF16 with BOM
-[PDFBOX-2799] - PDOptionalContentProperties.setGroupEnabled not working
-[PDFBOX-2801] - SecurityHandler does not tolerate plain-text COSString
-[PDFBOX-2802] - TestFontEmbedding sometimes fails due to non-determinism
-[PDFBOX-2803] - NullPointerException into class PDType0Font
-[PDFBOX-2808] - Can't merge to files with bookmarks
-[PDFBOX-2811] - Infinite loop within RandomAccessBuffer
-[PDFBOX-2812] - NPE in PDColorSpaceFactory.createColorSpace with PDICCBased
-[PDFBOX-2814] - Text not rendered in mode 7
-[PDFBOX-2816] - PDFBox makes disallowed changes when signing a signed document
-[PDFBOX-2819] - invalid ICC Profile when reading from a byte array
-[PDFBOX-2822] - infinite loop of searching for a key in PDResources
-[PDFBOX-2824] - ArrayIndexOutOfBoundsException in GlyfSimpleDescript.readFlags() when multithreading
-[PDFBOX-2826] - Mouse position shown when mouse outside of PDFReader window
-[PDFBOX-2829] - PDBox 2.0 Throws IndexOutOfBoundsException (severe offset errors as well)
-[PDFBOX-2830] - Can't draw color border around a PDTextBox + create example
-[PDFBOX-2832] - Remove obsolete methods from fontbox's Encoding
-[PDFBOX-2833] - Add an API to get the COSObjectKey of a given object
-[PDFBOX-2834] - Violation in PDOutputIntent.getDestOutputProfile() method
-[PDFBOX-2836] - COSName should be interpreted as UTF-8
-[PDFBOX-2837] - PDFBox creates files with EBCDIC code on z/OS
-[PDFBOX-2843] - widthOfSpace() appears wrong in TextPosition
-[PDFBOX-2844] - Printing has bigger margins than expected
-[PDFBOX-2845] - Error parsing PDF
-[PDFBOX-2846] - setValue failing with font issues.
-[PDFBOX-2847] - mergeDocumentsNonSeq does not utilize scratchFile
-[PDFBOX-2851] - getExportValue() non functional in PDRadioButton
-[PDFBOX-2856] - Markedly slower processing for particular file in 2.0.0-trunk vs 1.8.9
-[PDFBOX-2862] - GlyphList doesn't appear to be thread safe in trunk...or user error?
-[PDFBOX-2867] - Correct use of Float.NaN
-[PDFBOX-2868] - NPE in Acroform getValueAsString
-[PDFBOX-2869] - Corruption in ScratchFileBuffer
-[PDFBOX-2871] - Performance issue when filling the first PDTextField of an AcroForm
-[PDFBOX-2872] - Matrix.toCOSArray() has constant return
-[PDFBOX-2875] - Type 1 fonts are embedded incorrectly
-[PDFBOX-2876] - Better support for embedding of simple TrueType fonts
-[PDFBOX-2881] - Radial and Axial shading steps are calculated incorrectly
-[PDFBOX-2884] - NPE in FontMapper.getFont()
-[PDFBOX-2885] - NPE in PDNonTerminalField.getChildren()
-[PDFBOX-2886] - "IllegalArgumentException root cannot be null" in 2.0.0 for file that was parsed in 1.8.x
-[PDFBOX-2887] - NPE in PDFXrefStreamParser in 2.0 trunk
-[PDFBOX-2896] - XMPBox not creating valid "title" entry in DublinCoreSchema in trunk
-[PDFBOX-2898] - Incorrect key for color space in PDGroup
-[PDFBOX-2899] - Text not rendered in mode 7 (2)
-[PDFBOX-2901] - High CPU load and OutOfMemoryError when rendering shading
-[PDFBOX-2904] - IndexOutOfBoundsException in CFFType1Font.getType2CharString()
-[PDFBOX-2906] - NullPointerException in PDFStreamEngine.showText
-[PDFBOX-2908] - PDFTextStripper.writeText is slow
-[PDFBOX-2909] - NullPointerException when rendering shading with no function
-[PDFBOX-2911] - Merge does not close input streams
-[PDFBOX-2916] - ArrayIndexOutOfBoundsException in CmapSubtable.processSubtype6
-[PDFBOX-2924] - ClassCastException when doing PDFSplit
-[PDFBOX-2927] - Print with PrintRanges printRequestAttribute causing document to be cropped
-[PDFBOX-2929] - "Illegal instruction: 4" with PDFToImage
-[PDFBOX-2930] - PDFPageable does not rotate portrait document with 90°/270° rotation well
-[PDFBOX-2932] - NPE in PDSignature.getValuesAsString() when field contains no value
-[PDFBOX-2935] - Problem while extracting font from PDFontSetting (used in PDExtendedGraphicsState)
-[PDFBOX-2937] - Field duplication in PDIndexed color space
-[PDFBOX-2939] - PDFRenderer.renderImageWithDPI exception with certain PDFs
-[PDFBOX-2946] - Symbol glyphs not aligned
-[PDFBOX-2948] - NPE in PDStream.createInputStream
-[PDFBOX-2949] - Rendering to ARGB brings black background
-[PDFBOX-2950] - Chinese font substitution issue
-[PDFBOX-2951] - quotedbl causes NullPointerException
-[PDFBOX-2956] - PDFontDescriptor doesn't contain method getCIDSet.
-[PDFBOX-2958] - TIFF-Predictor with 1 bit per component not supported
-[PDFBOX-2959] - type3 font glyphs overlapped
-[PDFBOX-2960] - ClassCastException when pattern name is indirect object
-[PDFBOX-2961] - Checkbox with multiple widgets doesn't reflect check() state.
-[PDFBOX-2965] - NPE in PDAcroForm.getField() if the /Fields entry is missing
-[PDFBOX-2966] - Glyphs overlapping in rendering
-[PDFBOX-2969] - RandomAccessBuffer clone is broken for non-default chunk size
-[PDFBOX-2971] - CalGray white rendered as cyan
-[PDFBOX-2972] - Exception when RenderingIntent value is not one of the predefined.
-[PDFBOX-2976] - java.util.zip.DataFormatException: incorrect data check
-[PDFBOX-2982] - fix ClassCastExceptions in operator methods
-[PDFBOX-2985] - Potential NPE in PDMarkedContent#getMCID()
-[PDFBOX-2986] - Potential resource leak in TTFParser's use of RAFDataStream
-[PDFBOX-2989] - LZW decode filter shouldn't throw IndexOutOfBoundsException
-[PDFBOX-2990] - PDDocument.load fails to load a PDF document.
-[PDFBOX-2992] - Add .gitignore
-[PDFBOX-2995] - PDAcroForm getDefaultAppearance throws NPE if DA is not defined
-[PDFBOX-2996] - StackOverflow in Quicksort
-[PDFBOX-3001] - FileSystemFontProvider cache instability
-[PDFBOX-3002] - PDF files not closed after load fails
-[PDFBOX-3003] - Incorrect color space processing for inline images
-[PDFBOX-3005] - Incorrect property names for lists
-[PDFBOX-3008] - Memory leak in preflight
-[PDFBOX-3010] - SignatureOptions object must not be closed before calling saveIncremental in trunk's CreateVisibleSignature example
-[PDFBOX-3012] - PDAcroForm flatten() throws ClassCastException
-[PDFBOX-3013] - Incorrect accordance between attributes and properties
-[PDFBOX-3014] - ZapfDingbats not finding a substitute in Windows 8.1 Pro
-[PDFBOX-3018] - IOException "head is mandatory" when using getOriginalData() of TT font from TTC file
-[PDFBOX-3019] - Unwanted spaces in text extraction
-[PDFBOX-3021] - Class Cast Exception: COSString -> COSName
-[PDFBOX-3022] - Maven repos should be https
-[PDFBOX-3025] - Test case for unwanted spaces in text extraction
-[PDFBOX-3027] - Incorrect enumeration of conformances for PDFAIdentificationSchema
-[PDFBOX-3033] - Usage methods references incorrect package
-[PDFBOX-3034] - Newly created XRef stream has direct root objects
-[PDFBOX-3035] - Files with missing xref table must fail
-[PDFBOX-3037] - Text extraction decodes image files
-[PDFBOX-3038] - Text extraction shows glyphs with zero height
-[PDFBOX-3041] - Wrong default type in Xref stream W0 element
-[PDFBOX-3042] - Bad space calculation in text extraction
-[PDFBOX-3056] - Make PDFTextStreamEngine public
-[PDFBOX-3067] - Text strings being returned as single characters, regression from version 1.8
-[PDFBOX-3070] - Incorrect DefaultRGB color space obtain
-[PDFBOX-3073] - Change to use media box for page size instead of cropbox.
-[PDFBOX-3075] - Changed to the getHeight function for fonts so it will return a more accurate height
-[PDFBOX-3076] - Type3 Font that is getting zero height text, even in latest 2.0
-[PDFBOX-3081] - Create example to draw glyph sizes in rendered images
-[PDFBOX-3082] - High memory consumption while building font cache
-[PDFBOX-3083] - Form fields are missing when rendering
-[PDFBOX-3087] - Metadata stream should not be compressed
-[PDFBOX-3090] - ArrayIndexOutOfBoundsException in CmapSubtable.processSubtype2
-[PDFBOX-3091] - java.lang.ClassCastException: org.apache.fontbox.cff.CharStringCommand cannot be cast to java.lang.Integer
-[PDFBOX-3093] - Exception in TTFParser
-[PDFBOX-3094] - Merging PDFs with a Form is not retaining the field name values
-[PDFBOX-3095] - Space size NaN
-[PDFBOX-3097] - ClassCastException in Axial / Radial shading when object reference in extends
-[PDFBOX-3102] - getGlyphs returns empty array now
-[PDFBOX-3105] - Image with mask missing in rendering
-[PDFBOX-3106] - Allow access to font data
-[PDFBOX-3107] - Asterisk character not displaying properly in Adobe Reader
-[PDFBOX-3108] - Font cache is always rebuilt when font skipped
-[PDFBOX-3109] - DrawPrintTextLocations with incorrect coordinates when cropbox
-[PDFBOX-3110] - Extract by beads doesn't work
-[PDFBOX-3114] - Visible signatures in different pages changes previous revision
-[PDFBOX-3130] - Recent regression in PDFTextStripper, text getting garbled
-[PDFBOX-3139] - Custom FontMapper cant be used
-[PDFBOX-3140] - Different fallback font rendering first and second time
-[PDFBOX-3141] - Link annotation borders not rendered
-[PDFBOX-3143] - Added PDEmbeddedFile constructor with COSName parameter
-[PDFBOX-3144] - NullPointerException in TTFSubsetter
-[PDFBOX-3145] - Security manager fails for .pdfbox.cache
-[PDFBOX-3146] - Ink annotation borders not rendered
-[PDFBOX-3148] - Multiline fields won't get rendered correctly if there are multiple paragraphs in field value
-[PDFBOX-3149] - Failure to decrypt empty strings (AES 128)
-[PDFBOX-3151] - getStringWidth is terribly slow (and resulting document is invalid)
-[PDFBOX-3152] - NullPointerException in PDType1Font.encode() with centered dot
-[PDFBOX-3153] - Direct JPEG extraction results in invalid images in 2.0.0 releases.
-[PDFBOX-3154] - PDDocumentCatalog.getDocumentCatalog().getPages().getCount() returns 0 - first page is -1
-[PDFBOX-3155] - org.apache.pdfbox.util.PDFTextStripper class initialization throws NumberFormatException with recent Verona-enabled Java 9 JVMs
-[PDFBOX-3157] - PDOutputIntent has N=3 (RGB) hardcoded
-[PDFBOX-3160] - Problem with org.apache.xmpbox.DateConverter
-[PDFBOX-3164] - XFDF annotations partially incorrectly applied to existing PDF or exceptions when parsing
-[PDFBOX-3167] - IllegalArgumentException: dash lengths all zero
-[PDFBOX-3169] - SaveIncremental does not work without signature
-[PDFBOX-3172] - PDPage.getContentStreams() always returns empty when content stream field is an array
-[PDFBOX-3173] - Signature dictionary is not decrypted in encrypted files
-[PDFBOX-3175] - PDFTextStreamEngine probably miscalculates text height
-[PDFBOX-3179] - PDDocument.load() Error: Expected a long type at offset 2, instead got 'DF-1.4'
-[PDFBOX-3181] - java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds! in org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory.createFromImage
-[PDFBOX-3184] - Throwing in PDType1Font.encode for chars above 255 is wrong.
-[PDFBOX-3187] - NullPointerException CFFParser
+[PDFBOX-5051] - Slow rendering for specific PDF file
+[PDFBOX-5134] - Very slow rendering on PageDrawer.shadingFill
+[PDFBOX-5135] - Glyphs missed in rendering
+[PDFBOX-5137] - Wrong classification of an JPEG image leading to a blank image added to a pdf document
+[PDFBOX-5138] - Embedded files not extracted from PDF files with multilevel EmbeddedFiles tree
+[PDFBOX-5150] - 3.0.0-RC1: PDComboBox.setValue() throws IllegalArgumentException: /DA is a required entry
+[PDFBOX-5151] - Issue with COSObjectKey::fixGeneration
+[PDFBOX-5155] - Error extracting text from PDF - Can't read the embedded Type1 font FDFBJU+NewsGothic
+[PDFBOX-5156] - Error in identification of PDF comment symbol % as a token separator with PDF names
+[PDFBOX-5163] - Stack overflow when reading a corrupt dictionary
+[PDFBOX-5168] - dash pattern [0] should be invisible
+[PDFBOX-5175] - Behaviour change in 2.0.20 due to use of IOUtils.populateBuffer in SecurityHandler.prepareAESInitializationVector leading to IOException for certain PDF
+[PDFBOX-5176] - java.io.IOException: Page tree root must be a dictionary
+[PDFBOX-5180] - Snapshot Deploy not working
+[PDFBOX-5187] - TSAClient with username+password
+[PDFBOX-5188] - COSOutputStream.flush doesn't call super
+[PDFBOX-5190] - BaseParser: stack overflow when reading a corrupt pdf
+[PDFBOX-5191] - isEmbeddingPermitted() is too restrictive on TTFs with OS2 table versions 0-2
+[PDFBOX-5192] - Wild rendering when repeating truetype glyph flag is outside of range
+[PDFBOX-5193] - v2.0.22 and v3.0.0-RC1 PDF Debugger app crashes with java.lang.NullPointerException
+[PDFBOX-5194] - CreateCheckBox example draws too large, clipped checkmark
+[PDFBOX-5196] - Wrong color space detected for some Jpeg images
+[PDFBOX-5199] - Possible memory leak after calling decode filter
+[PDFBOX-5204] - Ink annotation not rendered
Improvement
-[PDFBOX-193] - Getting tiff - PDCcitt.TiffWrapper object
-[PDFBOX-408] - Optional logger calls could be added to COSDocument & PDJpeg when an error occurs.
-[PDFBOX-678] - Support missing Text Rendering Modes when rendering a PDF
-[PDFBOX-870] - PDF-To-IMAGE output is not anti-aliased
-[PDFBOX-996] - need to insert a child as the first child of an outline but you can only append to the outline.
-[PDFBOX-1083] - PDType0Font incomplete
-[PDFBOX-1094] - Pattern colorspace support
-[PDFBOX-1167] - PDFStreamEngine#processSubStream should throw original IOException instead of RuntimeException + FIX
-[PDFBOX-1182] - Create a module for the commandline tools
-[PDFBOX-1213] - Adding style information to the PDF to HTML converter
-[PDFBOX-1270] - Change internal page resolution to float everywhere
-[PDFBOX-1329] - Update PDPage to enum
-[PDFBOX-1356] - Support lucene 3.6.0
-[PDFBOX-1384] - Proposals for a new PDNameTreeNode and PDNumberTreeNode
-[PDFBOX-1402] - Improve handling of multiline text boxes
-[PDFBOX-1444] - Capability to use custom PageDrawer in PDPage.convertToImage
-[PDFBOX-1503] - Double logging of exceptions
-[PDFBOX-1523] - Manifest should support Specification entries
-[PDFBOX-1543] - Remove the ReplaceString example
-[PDFBOX-1564] - Extending COSName to produce PDF/A with correct OutputIntents
-[PDFBOX-1566] - reduce duplicated code and add caching to pdpagenode
-[PDFBOX-1587] - Update the dependency on Bouncy Castle to 1.48
-[PDFBOX-1591] - Resources should implement java.io.closeable
-[PDFBOX-1594] - Add support for AES256 Encryption
-[PDFBOX-1596] - OverlayPDF logic should be moved into a library class
-[PDFBOX-1613] - The ability to inject the time/random component into the COSWriter process to write a PDF document allows some advanced signature creation scenarios where the signature is generated on a separate server that does not hold the full PDF document.
-[PDFBOX-1621] - Add setModifiedDate(Calendar c) to PDAnnotation
-[PDFBOX-1645] - [PATCH] Improved the accuracy of the bounding box for each rendered CFF glyph
-[PDFBOX-1648] - FontBox can't load CMaps with no spaces between tokens
-[PDFBOX-1656] - Enable PDFMergeUtility to merge Encrypted PDFs
-[PDFBOX-1665] - Replace external glyphlist.txt with our onw implementation
-[PDFBOX-1667] - org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent throws Exception while it can throw IOException instead
-[PDFBOX-1669] - Update the dependency on Bouncy Castle to 1.49
-[PDFBOX-1687] - add dispose() in pdfbox\pdmodel\PDPage.convertToImage()
-[PDFBOX-1690] - Add description to embedded file
-[PDFBOX-1695] - Improve pdfbox tests
-[PDFBOX-1698] - Remove the print and the convertToImage stuff from PDPage and PDDocument
-[PDFBOX-1702] - Performance improvement in PDPageContentStream.drawString
-[PDFBOX-1707] - Add dispose() when done with graphics
-[PDFBOX-1720] - BouncyCastle 1.49: ambigous constructor usage
-[PDFBOX-1734] - ImageIoUtil.WriteImage doesn't work with tiff images
-[PDFBOX-1738] - PDF with parsing IOException
-[PDFBOX-1739] - Load document error for two RegisSTAR documents
-[PDFBOX-1744] - Be resilient to PDFs with missing version info
-[PDFBOX-1782] - Add getMaxLength() and setMaxLength() methods to PDTextbox
-[PDFBOX-1784] - Update parent pom/rat plugin version
-[PDFBOX-1798] - Performance problem with PDDocument.saveIncremental (when signing document)
-[PDFBOX-1815] - Suggestion: close files in COSStream
-[PDFBOX-1820] - Suggestion: close streams in PDIndex and PDJpeg
-[PDFBOX-1828] - Remove not needed CMaps
-[PDFBOX-1833] - BaseParser tidy up
-[PDFBOX-1834] - Remove old Overlay implementation
-[PDFBOX-1836] - Use the latest dependencies
-[PDFBOX-1839] - PDFImageWriter default BufferedImage type makes output colors look poor
-[PDFBOX-1840] - Automatically load isartor for preflight tests
-[PDFBOX-1844] - [PATCH] Parser for Type 1 Fonts
-[PDFBOX-1847] - TSA Time Signature
-[PDFBOX-1850] - Speed up TestImageIOUtils
-[PDFBOX-1851] - [PATCH] Improved CMYK color space conversion
-[PDFBOX-1852] - [PATCH] Alternative patch to speed up TestImageIOUtils
-[PDFBOX-1854] - Include AFM files for Core 14 fonts
-[PDFBOX-1889] - Remove the ConvertColorspace class
-[PDFBOX-1890] - Merge PdfDecompressor and WriteDecodedDoc
-[PDFBOX-1891] - Remove the ant module
-[PDFBOX-1893] - Refactor color spaces
-[PDFBOX-1897] - There are some errors within the source code documentation (javadocs)
-[PDFBOX-1902] - generics added to maputil
-[PDFBOX-1903] - refactor pdmodel (pdpage)
-[PDFBOX-1905] - Remove the PDPage reference from PageDrawer/PDFStreamEngine
-[PDFBOX-1906] - Don't use a src subdirectory as output directory for a test case
-[PDFBOX-1909] - Close open streams
-[PDFBOX-1914] - Shading package: Move "function" methods to base class and more refactoring
-[PDFBOX-1915] - Implement shading with Coons and tensor-product patch meshes
-[PDFBOX-1941] - Refactor PageDrawer operators
-[PDFBOX-1943] - Move pdfbox-tools to its own package
-[PDFBOX-1959] - Remove AWT Fonts
-[PDFBOX-1962] - Refactor the packages in the core pdfbox module
-[PDFBOX-1963] - PDFImageWriter doesn't make use of PDFStreamEngine
-[PDFBOX-1964] - PDFMergerUtility support merging using non sequential parser
-[PDFBOX-1972] - WrappedIOException no longer needed in Java 1.6
-[PDFBOX-1973] - Exception Refactoring (Don't wrap Exceptions with COSVisitorException)
-[PDFBOX-1976] - DocumentEncryption and PDFEncryption are deprecated and should be removed
-[PDFBOX-1982] - Standardise AcroForm Fields
-[PDFBOX-1985] - Replace List with List in PDDocument and PDPageNode
-[PDFBOX-1986] - Move SecurityHandler to PDEncryptionDictionary
-[PDFBOX-1989] - Save LZW and other encoded PDImageXObject resources
-[PDFBOX-1990] - Support creating PDF from lossless encoded images
-[PDFBOX-1991] - Shading PaintContexts should not depend on the page height
-[PDFBOX-2002] - Show deprecation in the build / fix deprecated calls / delete longtime deprecated stuff
-[PDFBOX-2034] - TestFilters is non-deterministic
-[PDFBOX-2039] - Class PDDocument should implement java.io.Closeable
-[PDFBOX-2051] - PDFPrinter does not use getPageable()
-[PDFBOX-2052] - PDFCloneUtility does not handle COSStreamArray
-[PDFBOX-2066] - RubberStampWithImage should support more image types
-[PDFBOX-2068] - Add filter parameter to PDImageXObject(document, filteredStream) constructor
-[PDFBOX-2071] - Insert inline image in page content stream
-[PDFBOX-2088] - Support Bouncycastle 1.50
-[PDFBOX-2094] - Add PrintRequestAttributeSet parameter to silentPrint()
-[PDFBOX-2097] - Remove pdfbox-war subproject
-[PDFBOX-2099] - Improve handling and writing of header and trailer versions
-[PDFBOX-2104] - Implement transparency groups
-[PDFBOX-2105] - Support for multipage TIFFs in CCITTFactory, makes PDFBox capable of doing tiff2pdf
-[PDFBOX-2107] - Make PDFBox XMP library agnostic
-[PDFBOX-2113] - Update documentation to reflect the requirement for JBIG2 decoders
-[PDFBOX-2118] - Remove ICU4J dependency
-[PDFBOX-2123] - Optimize reading of 1-bit depth images in SampleImageReader
-[PDFBOX-2126] - Optimize clipping
-[PDFBOX-2127] - Optimize calls of getPixel in SampledImageReader and PDImageXObject
-[PDFBOX-2129] - Add PDFBox version to the title
-[PDFBOX-2131] - Avoid constructing debug messages if debug log is off
-[PDFBOX-2132] - Provide a pluggable exception handler to PDFStreamEngine
-[PDFBOX-2136] - Use the Type1Parser to extract the encoding
-[PDFBOX-2144] - Provide a pluggable font manager
-[PDFBOX-2146] - remove unused imports / fix imports
-[PDFBOX-2148] - Handle the Fully Qualified Name of duplicate fields better
-[PDFBOX-2149] - Font Refactoring
-[PDFBOX-2151] - Replace log4j with commons logging
-[PDFBOX-2152] - Unable to print the PDF with Acrobat shrink to fit print
-[PDFBOX-2157] - Remove AFMFormatter
-[PDFBOX-2174] - Suppress the Dock icon on OS X
-[PDFBOX-2196] - [PATCH] Type safety in PDNameTreeNode and PDNumberTreeNode via generics
-[PDFBOX-2205] - (Graphics) Operator Refactoring
-[PDFBOX-2220] - [PATCH] Differences array without BaseEncoding (Type1C)
-[PDFBOX-2239] - Add missing values to PDComplexFileSpecification
-[PDFBOX-2250] - Improve XRef self healing mechanism
-[PDFBOX-2262] - Remove usage of AWT fonts
-[PDFBOX-2269] - Support for AES-256 Rev. 5 Decryption (Acrobat 9)
-[PDFBOX-2294] - Improve vertical text drawing as an experiment
-[PDFBOX-2302] - Make better use of RenderingHints
-[PDFBOX-2303] - Lazy loading of glyphs in TrueType fonts
-[PDFBOX-2328] - Give PDColor access to its underling PDColorSpace
-[PDFBOX-2329] - add toString method to PDRange
-[PDFBOX-2333] - Overhaul the appearance generation for PDF forms
-[PDFBOX-2362] - Remove .properties file usage in PDFStreamEngine
-[PDFBOX-2366] - Improve high-level font APIs
-[PDFBOX-2370] - Move caching outside of PDResources
-[PDFBOX-2374] - Make JavaDocs for trunk builds available via our website
-[PDFBOX-2386] - Move operators and content streams out of "pdfbox.util"
-[PDFBOX-2387] - ImageIOUtil, JPEGUtil, TIFFUtil and MetaUtil are not needed in "pdfbox"
-[PDFBOX-2388] - Move printing classes into top-level "printing" package
-[PDFBOX-2389] - Move Encoding classes into "font" package
-[PDFBOX-2391] - Use an enum for RenderingIntent
-[PDFBOX-2392] - PDPropertyList belongs in "markedcontent" package
-[PDFBOX-2394] - Add example code to extract embedded files in annotations
-[PDFBOX-2414] - Allow non-sequential parser for PDFMerger in app
-[PDFBOX-2423] - Page tree handling needs rewriting
-[PDFBOX-2430] - Make the non-sequential parser the default parser
-[PDFBOX-2440] - xref stream is saved as table
-[PDFBOX-2444] - Add radial shading example
-[PDFBOX-2452] - Continuous log "Nonsymbolic Type 0 font: SNCFYS+ARStdKai"
-[PDFBOX-2456] - create TestSymmetricKeyEncryption.java
-[PDFBOX-2459] - Share functionality between Page Tree and Field Tree
-[PDFBOX-2461] - Clear Checkstyle errors in source
-[PDFBOX-2464] - Document crypto build dependencies
-[PDFBOX-2467] - "Arial,Bold" always substituted with "Helvetica-Bold"
-[PDFBOX-2468] - Switch FDFDocument.load from PDFParser to NonSequentialParser
-[PDFBOX-2473] - Remove the CopyDoc example
-[PDFBOX-2474] - Remove the direct usage of PDFParser
-[PDFBOX-2515] - Improve the non sequential parser to be used when signing a pdf
-[PDFBOX-2516] - Further align AcroForms and Fields PDModel with PDF specification
-[PDFBOX-2530] - Improve PDFDebugger
-[PDFBOX-2565] - Subset embedded TTF fonts
-[PDFBOX-2566] - Remove logging from operator classes
-[PDFBOX-2580] - Decouple implementation specific forms handling from interactive.form PD Model
-[PDFBOX-2587] - PDF takes minutes to convert (sRGB)
-[PDFBOX-2591] - Allow using custom Filters
-[PDFBOX-2592] - Allow sharing of COS objects between different documents
-[PDFBOX-2594] - Set default params in JBIG2Filter
-[PDFBOX-2597] - Provide easier access to AcroForm field tree
-[PDFBOX-2600] - Remove old parser
-[PDFBOX-2623] - PDFPrinter.getPrintable returns Pageable instead of PDFPageable for easier extending
-[PDFBOX-2628] - XmpSerializer will never throw XmpSerializationException
-[PDFBOX-2645] - Open PDF file from byte array without temp file
-[PDFBOX-2669] - Make internal PageDrawer font classes package-private
-[PDFBOX-2670] - Move orphaned COSObjectKey class
-[PDFBOX-2680] - Move multi-pdf classes from util into their own package
-[PDFBOX-2683] - Remove SignatureInterface dependency from COSDocument
-[PDFBOX-2689] - Implement page transitions
-[PDFBOX-2695] - Iterate PDOutlineNode children
-[PDFBOX-2700] - support JPEG color space code 11 (JCS_YCCK)
-[PDFBOX-2703] - Remove javacc generated PDFParser from preflight
-[PDFBOX-2707] - Remove redundant IOUtils.closeQuietly
-[PDFBOX-2716] - Use saveIncremental() method on a document opened with an InputStream does not work
-[PDFBOX-2727] - Cache color space instances
-[PDFBOX-2735] - Keyboard shortcuts in PDFReader
-[PDFBOX-2736] - First page and last page navigation with keyboard shortcuts in PDFReader
-[PDFBOX-2744] - Add validation check for setNonStrokingColor and setStrokingColor
-[PDFBOX-2748] - Recent files in PDF reader
-[PDFBOX-2753] - Improve rendering of filled thin lines
-[PDFBOX-2758] - Support additional annotation types when importing XFDF files
-[PDFBOX-2764] - Allow setting extended graphics state in PDPageContentStream
-[PDFBOX-2777] - Create convenience method to create an XImage object
-[PDFBOX-2782] - Enhance toString() output for AcroForm fields
-[PDFBOX-2791] - Provide access to Type 1 font data
-[PDFBOX-2806] - The 'kern' table type is not supported.
-[PDFBOX-2807] - The vertical layout table types 'vhea', 'vmtx', 'VORG' are not supported.
-[PDFBOX-2838] - Please make PDPageContentStream non-final
-[PDFBOX-2841] - Make it easier to work with RadioButton Groups
-[PDFBOX-2842] - Overhaul font substitution
-[PDFBOX-2865] - Downgrade logging "Using last-resort fallback for x font" to warn in 2.0.0?
-[PDFBOX-2870] - Use animal sniffer maven plugin to detect non java 6 api usage
-[PDFBOX-2878] - Align annotation and form public API
-[PDFBOX-2880] - Allow Type 1 embedding without AFM file
-[PDFBOX-2882] - Improve performance when using scratch file
-[PDFBOX-2883] - Unify memory handling
-[PDFBOX-2888] - setAllSecurityToBeRemoved(true) before calling protect() should have no effect
-[PDFBOX-2892] - Invisible signature annotation violates PDF/A
-[PDFBOX-2893] - Simplify COSStream encoding and decoding
-[PDFBOX-2894] - Remove COSStreamArray / SequenceRandomAccessRead
-[PDFBOX-2905] - Replace PDFReader with PDFDebugger
-[PDFBOX-2922] - Printing issues with landscape pages
-[PDFBOX-2928] - Add numPages parameter of Book in Printing.printWithPaper example
-[PDFBOX-2931] - Make PDFPrintable margin-aligning and centering optional
-[PDFBOX-2933] - Drop ant build including .NET build support
-[PDFBOX-2943] - PDType3Font.getWidthFromFont not supported
-[PDFBOX-2945] - PDType1Font.getNameInFont(String) very slow when Unicode fallback is used
-[PDFBOX-2962] - Handle TIFF predictor for bpc 2 and 4 / optimize existing predictor code
-[PDFBOX-2973] - Actions shortage
-[PDFBOX-2978] - Add support for grouped checkboxes
-[PDFBOX-2997] - Make FontMapper into a singleton interface
-[PDFBOX-3072] - Allow missing page type
-[PDFBOX-3088] - Cache glyph table to optimize concurrent access
-[PDFBOX-3103] - Slow performance when printing PDF (fix provided)
-[PDFBOX-3104] - Font Cache is taking a lot of time
-[PDFBOX-3115] - Fix high memory usage during signing
-[PDFBOX-3121] - Buffer save(File)
-[PDFBOX-3122] - IllegalArgumentException: dash lengths all zero
-[PDFBOX-3131] - Reduce amount of intermediate data and objects to reduce memory footprint/complexity
-[PDFBOX-3133] - PDFBox 2.0.0-RC2 and earlier 2.0.0 SNAPSHOT Versions print performance is poor with systems having low RAM < 3GB and lower number of fonts.
-[PDFBOX-3137] - Reduce/remove dependency on commons.io in preflight/xmpbox
-[PDFBOX-3158] - Add constructor with BufferedImage to PDVisibleSignDesigner
-[PDFBOX-3161] - No glyph for U+0009 in font ArialUnicodeMS
-[PDFBOX-3163] - PDImageXObject.createFromFile should relies on header bytes
-[PDFBOX-3176] - Add a removeRegion method in PDFTextSTripperByArea class
-[PDFBOX-3178] - Make PDFTextStreamEngine class public
+[PDFBOX-5093] - Pass PDFRenderer to PDFPrintable constructor
+[PDFBOX-5141] - Create tests for HelloWorld examples
+[PDFBOX-5145] - Faster PDImageXObject.applyMask
+[PDFBOX-5154] - Custom folder for fonts in FontMapper
+[PDFBOX-5157] - allow to make timestamp only signature "LTV"
+[PDFBOX-5164] - Create portable collection PDF
+[PDFBOX-5177] - Optimize memory footprint of PDFObjectStreamParser
+[PDFBOX-5183] - Add getter/setter for suppressDuplicateOverlappingText in PDFMarkedContentExtractor
+[PDFBOX-5200] - Cache PageTree in PDFPrintable
+[PDFBOX-5201] - Add Adobe Illustrator COSNames
+[PDFBOX-5208] - Make constructors of CIDSystemInfo and PDPanoseClassification public
-Feature
+Wish
-[PDFBOX-52] - DCTFilter is not implemented yet
-[PDFBOX-149] - Update encryption algorithms
-[PDFBOX-151] - Correct calculation of Type0Font size.
-[PDFBOX-615] - shfill operator needs implementation
-[PDFBOX-830] - Setting of logical page numbers
-[PDFBOX-922] - True type PDFont subclass only supports WinAnsiEncoding (hardcoded!)
-[PDFBOX-953] - PDFBox fails to ExtractText from Adobe Acrobat X 256-bit AES encrypted documents
-[PDFBOX-1054] - DateConverter: allow for external adding of potential date parsing formats
-[PDFBOX-1209] - Add insertSiblingBefore() to PDOutlineItem
-[PDFBOX-1223] - Strange color issues with convertToImage method
-[PDFBOX-1462] - Use file backed buffer for FlateFilter?
-[PDFBOX-1494] - PDF box color distortion
-[PDFBOX-1589] - Switch to java 1.6 as minimum requirement for PDFBox
-[PDFBOX-1766] - [PATCH] Visible Signature using PDFbox
-[PDFBOX-2211] - Create sample code for creating a PDF with shading
-[PDFBOX-2276] - Remove Jempbox subproject
-[PDFBOX-2400] - Add insertPage() method
-[PDFBOX-2624] - "CIDSet entry is missing for the Composite Subset" when creating PDF/A-1b file with PDType0Font.load()
-[PDFBOX-2673] - Add output path prefix param in PDFSplit/Splitter
-[PDFBOX-2752] - Support TTC font files
-[PDFBOX-2766] - Missing PDDocument.load() overload
-[PDFBOX-2821] - Add PDDocument(boolean) constructor for creating new documents using scratch files
-[PDFBOX-3074] - Mark transparency groups
+[PDFBOX-5198] - When merging multiple pdf ua documents, Tags become nested
Task
-[PDFBOX-1600] - COSDocument and PDDocument declare throws IOException when they don't
-[PDFBOX-1675] - Preflight : improve error information
-[PDFBOX-1685] - Verify interpretation of rdf:about for PDF/A
-[PDFBOX-1975] - Improve TestImageIOUtils unit tests to check image resolution and compression
-[PDFBOX-2197] - Add sample how to import a page as PDFormXObject
-[PDFBOX-2480] - Add information about Snapshots to download section
-[PDFBOX-2576] - Improve code quality
-[PDFBOX-2610] - Expand Isartor test for Bavaria test suite and other tests
-[PDFBOX-2674] - Remove two unused methods from COSStream
-[PDFBOX-2712] - Remove commented out lines of code
-[PDFBOX-2762] - remove parseCOSStream() call from PDFStreamParser
-[PDFBOX-2768] - Remove VisualSignatureParser
-[PDFBOX-3011] - Find out why trunk CreateVisibleSignature example produces incorrect output pdf
-[PDFBOX-3020] - Set libraries to current versions for RC
-[PDFBOX-3040] - Move website to local build tool
-
-Test
-
-[PDFBOX-1584] - Add unit test for RandomAccessFileOutputStream
-[PDFBOX-1673] - Tests with selection of files from Adobe Acrobat Engineering website
-[PDFBOX-2369] - how to convert pdf to image
-
-Wish
-
-[PDFBOX-1187] - Cut dependency between pdfbox and jempbox
-[PDFBOX-1224] - Angle units are not consistent
-[PDFBOX-1450] - document how to encrypt with AES 256 with the release of 2.0
-[PDFBOX-1540] - Add XML output option to preflight
-[PDFBOX-1590] - Unify logging between preflight and other modules
-[PDFBOX-1769] - Fix crash on invalid xref
-[PDFBOX-1946] - Running within an Applet has many AccessControlException 's
-[PDFBOX-2011] - Please extend base class "Encoding" with 2 methods to access global name2char and char2name maps
-[PDFBOX-2012] - Extend CMAPEncodingEntry API
-[PDFBOX-2013] - Please extend PDTrueTypeFont API
-[PDFBOX-2190] - Disable console logging for preflight Isartor tests
-[PDFBOX-2209] - [PATCH] Restore shading API
-[PDFBOX-2692] - Possibility to use our own and/or overwrite PageDrawer class
-[PDFBOX-2738] - Make org.apache.pdfbox.pdmodel.PDDocument#getFontsToSubset public
-[PDFBOX-2770] - Provide the sources along with SNAPSHOT releases
+[PDFBOX-5133] - Failing testFlattenPDFBox2469Filled on Ubuntu
+[PDFBOX-5184] - Add test for PDFMarkedContentExtractor class
+[PDFBOX-5186] - Create test for CreateGradientShadingPDF
Release Contents
----------------
@@ -1219,10 +70,10 @@ This release consists of a single source archive packaged as a zip file.
The archive can be unpacked with the jar tool from your JDK installation.
See the README.txt file for instructions on how to build this release.
-The source archive is accompanied by SHA1 and MD5 checksums and a PGP
-signature that you can use to verify the authenticity of your download.
+The source archive is accompanied by a SHA512 checksum and a PGP signature
+that you can use to verify the authenticity of your download.
The public key used for the PGP signature can be found at
-https://svn.apache.org/repos/asf/pdfbox/KEYS.
+https://www.apache.org/dist/pdfbox/KEYS.
About Apache PDFBox
-------------------
@@ -1233,7 +84,7 @@ documents and the ability to extract content from documents. Apache PDFBox
also includes several command line utilities. Apache PDFBox is published
under the Apache License, Version 2.0.
-For more information, visit http://pdfbox.apache.org/
+For more information, visit https://pdfbox.apache.org/
About The Apache Software Foundation
------------------------------------
@@ -1245,4 +96,4 @@ enables individual and commercial users to easily deploy Apache software;
the Foundation's intellectual property framework limits the legal exposure
of its 2,500+ contributors.
-For more information, visit http://www.apache.org/
+For more information, visit https://www.apache.org/
diff --git a/app/pom.xml b/app/pom.xml
index ec6a2de6bf5..2841cb2a13a 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -23,7 +23,7 @@
org.apache.pdfboxpdfbox-parent
- 2.0.0-SNAPSHOT
+ 2.0.25-SNAPSHOT../parent/pom.xml
@@ -49,6 +49,11 @@
bcprov-jdk15onprovided
+
+ org.apache.pdfbox
+ jbig2-imageio
+ provided
+
@@ -61,9 +66,9 @@
org.apache.pdfbox.*true
- *;scope=provided;inline=org/apache/**|org/bouncycastle/**
+ *;scope=provided;inline=org/apache/**|org/bouncycastle/**|META-INF/services/**${project.url}
- !junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,org.apache.log4j;resolution:=optional,*
+ !junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,*org.apache.pdfbox.tools.PDFBox
diff --git a/app/src/main/appended-resources/META-INF/LICENSE b/app/src/main/appended-resources/META-INF/LICENSE
index 57237248ea1..f581b421846 100644
--- a/app/src/main/appended-resources/META-INF/LICENSE
+++ b/app/src/main/appended-resources/META-INF/LICENSE
@@ -134,3 +134,33 @@ Glyphlist (http://www.adobe.com/devnet/opentype/archives/glyph.html)
non-infringement of any third party rights regarding the Adobe
materials.
+Twelvemonkeys (https://github.com/haraldk/TwelveMonkeys/)
+
+ Copyright (c) 2008-2016, Harald Kuhr
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ o Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ o Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ o Neither the name "TwelveMonkeys" nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/app/src/main/appended-resources/META-INF/NOTICE b/app/src/main/appended-resources/META-INF/NOTICE
index e8fea2ebf58..a0f9eac0893 100644
--- a/app/src/main/appended-resources/META-INF/NOTICE
+++ b/app/src/main/appended-resources/META-INF/NOTICE
@@ -1,2 +1,14 @@
Based on source code originally developed in the PDFBox and FontBox projects.
Copyright (c) 2002-2007, www.pdfbox.org
+
+Includes the Adobe Glyph List
+Copyright 1997, 1998, 2002, 2007, 2010 Adobe Systems Incorporated.
+
+Includes the Zapf Dingbats Glyph List
+Copyright 2002, 2010 Adobe Systems Incorporated.
+
+Includes the Bidi Mirroring Glyph Property (BidiMirroring-8.0.0.txt)
+Copyright 1991-2015 Unicode, Inc.
+
+Includes parts of TwelveMonkeys ImageIO
+Copyright 2008-2016 Harald Kuhr
diff --git a/debugger-app/README.md b/debugger-app/README.md
new file mode 100644
index 00000000000..59db786bd31
--- /dev/null
+++ b/debugger-app/README.md
@@ -0,0 +1,29 @@
+
+
+# Apache PDFBox PDF Debugger App
+
+## Package for distribution on Windows, Linux and macOS
+
+To package the Apache PDFBox Debugger App for distribution on Windows, Linux
+and macOS [packr](https://github.com/libgdx/packr) provides a way to do so.
+
+The projects [Readme](https://github.com/libgdx/packr#readme) describes the steps needed
+for the various platforms.
+
+**NOTE:** The Apache PDFBox project provides no support to create such packages.
+The Information shall be treated as a help for the interested developer.
diff --git a/debugger-app/pom.xml b/debugger-app/pom.xml
index 1b6432bfb95..83476346f2e 100644
--- a/debugger-app/pom.xml
+++ b/debugger-app/pom.xml
@@ -23,7 +23,7 @@
org.apache.pdfboxpdfbox-parent
- 2.0.0-SNAPSHOT
+ 2.0.25-SNAPSHOT../parent/pom.xml
@@ -49,6 +49,11 @@
bcprov-jdk15onprovided
+
+ org.apache.pdfbox
+ jbig2-imageio
+ provided
+
@@ -61,9 +66,9 @@
org.apache.pdfbox.*true
- *;scope=provided;inline=org/apache/**|org/bouncycastle/**
+ *;scope=provided;inline=org/apache/**|org/bouncycastle/**|META-INF/services/**${project.url}
- !junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,org.apache.log4j;resolution:=optional,*
+ !junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,*org.apache.pdfbox.debugger.PDFDebugger
diff --git a/debugger-app/src/main/appended-resources/META-INF/LICENSE b/debugger-app/src/main/appended-resources/META-INF/LICENSE
index 57237248ea1..f581b421846 100644
--- a/debugger-app/src/main/appended-resources/META-INF/LICENSE
+++ b/debugger-app/src/main/appended-resources/META-INF/LICENSE
@@ -134,3 +134,33 @@ Glyphlist (http://www.adobe.com/devnet/opentype/archives/glyph.html)
non-infringement of any third party rights regarding the Adobe
materials.
+Twelvemonkeys (https://github.com/haraldk/TwelveMonkeys/)
+
+ Copyright (c) 2008-2016, Harald Kuhr
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ o Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ o Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ o Neither the name "TwelveMonkeys" nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/debugger-app/src/main/appended-resources/META-INF/NOTICE b/debugger-app/src/main/appended-resources/META-INF/NOTICE
index e8fea2ebf58..a0f9eac0893 100644
--- a/debugger-app/src/main/appended-resources/META-INF/NOTICE
+++ b/debugger-app/src/main/appended-resources/META-INF/NOTICE
@@ -1,2 +1,14 @@
Based on source code originally developed in the PDFBox and FontBox projects.
Copyright (c) 2002-2007, www.pdfbox.org
+
+Includes the Adobe Glyph List
+Copyright 1997, 1998, 2002, 2007, 2010 Adobe Systems Incorporated.
+
+Includes the Zapf Dingbats Glyph List
+Copyright 2002, 2010 Adobe Systems Incorporated.
+
+Includes the Bidi Mirroring Glyph Property (BidiMirroring-8.0.0.txt)
+Copyright 1991-2015 Unicode, Inc.
+
+Includes parts of TwelveMonkeys ImageIO
+Copyright 2008-2016 Harald Kuhr
diff --git a/debugger/pom.xml b/debugger/pom.xml
index 35cf888b2a6..a0588121ffd 100644
--- a/debugger/pom.xml
+++ b/debugger/pom.xml
@@ -23,7 +23,7 @@
org.apache.pdfboxpdfbox-parent
- 2.0.0-SNAPSHOT
+ 2.0.25-SNAPSHOT../parent/pom.xml
@@ -48,14 +48,14 @@
pdfbox${project.version}
-
- com.levigo.jbig2
- levigo-jbig2-imageio
+ org.apache.pdfbox
+ jbig2-imageiotest
+
com.github.jai-imageiojai-imageio-core
@@ -75,6 +75,19 @@
true
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.apache.pdfbox.debugger
+
+
+
+
+
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java b/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
index 11bc176ab45..e64a363594c 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
@@ -18,8 +18,10 @@
import java.awt.BorderLayout;
import java.awt.Component;
+import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FileDialog;
+import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
@@ -31,7 +33,6 @@
import java.awt.event.WindowEvent;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
@@ -42,9 +43,14 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import javax.imageio.spi.IIORegistry;
+import javax.print.attribute.HashPrintRequestAttributeSet;
+import javax.print.attribute.PrintRequestAttributeSet;
+import javax.print.attribute.standard.Sides;
import javax.swing.AbstractAction;
import javax.swing.Action;
+import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
@@ -90,17 +96,30 @@
import org.apache.pdfbox.debugger.ui.ErrorDialog;
import org.apache.pdfbox.debugger.ui.ExtensionFileFilter;
import org.apache.pdfbox.debugger.ui.FileOpenSaveDialog;
+import org.apache.pdfbox.debugger.ui.ImageTypeMenu;
+import org.apache.pdfbox.debugger.ui.LogDialog;
import org.apache.pdfbox.debugger.ui.MapEntry;
import org.apache.pdfbox.debugger.ui.OSXAdapter;
import org.apache.pdfbox.debugger.ui.PDFTreeCellRenderer;
import org.apache.pdfbox.debugger.ui.PDFTreeModel;
import org.apache.pdfbox.debugger.ui.PageEntry;
+import org.apache.pdfbox.debugger.ui.ReaderBottomPanel;
import org.apache.pdfbox.debugger.ui.RecentFiles;
+import org.apache.pdfbox.debugger.ui.RenderDestinationMenu;
import org.apache.pdfbox.debugger.ui.RotationMenu;
import org.apache.pdfbox.debugger.ui.Tree;
+import org.apache.pdfbox.debugger.ui.WindowPrefs;
import org.apache.pdfbox.debugger.ui.ZoomMenu;
+import org.apache.pdfbox.filter.FilterFactory;
+import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.common.PDPageLabels;
+import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences;
import org.apache.pdfbox.printing.PDFPageable;
/**
@@ -110,6 +129,7 @@
* @author Ben Litchfield
* @author Khyrul Bashar
*/
+@SuppressWarnings({"serial","squid:MaximumInheritanceDepth","squid:S1948"})
public class PDFDebugger extends JFrame
{
private static final Set SPECIALCOLORSPACES =
@@ -119,6 +139,7 @@ public class PDFDebugger extends JFrame
new HashSet(Arrays.asList(COSName.ICCBASED, COSName.PATTERN, COSName.CALGRAY,
COSName.CALRGB, COSName.LAB));
+ @SuppressWarnings({"squid:S2068"})
private static final String PASSWORD = "-password";
private static final String VIEW_STRUCTURE = "-viewstructure";
@@ -127,6 +148,7 @@ public class PDFDebugger extends JFrame
private TreeStatusPane statusPane;
private RecentFiles recentFiles;
+ private WindowPrefs windowPrefs;
private boolean isPageMode;
private PDDocument document;
@@ -135,10 +157,10 @@ public class PDFDebugger extends JFrame
private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
private static final boolean IS_MAC_OS = OS_NAME.startsWith("mac os x");
- private JScrollPane jScrollPane1;
- private JScrollPane jScrollPane2;
- private javax.swing.JSplitPane jSplitPane1;
- private javax.swing.JTextPane jTextPane1;
+ private JScrollPane jScrollPaneRight;
+ private javax.swing.JSplitPane jSplitPane;
+ private javax.swing.JTextPane jTextPane;
+ private ReaderBottomPanel statusBar;
private Tree tree;
private final JPanel documentPanel = new JPanel();
@@ -147,6 +169,7 @@ public class PDFDebugger extends JFrame
private JMenuItem saveMenuItem;
private JMenu recentFilesMenu;
private JMenuItem printMenuItem;
+ private JMenuItem reopenMenuItem;
// edit > find menu
private JMenu findMenu;
@@ -157,6 +180,9 @@ public class PDFDebugger extends JFrame
// view menu
private JMenuItem viewModeItem;
+ public static JCheckBoxMenuItem allowSubsampling;
+ public static JCheckBoxMenuItem repairAcroFormMenuItem;
+
/**
* Constructor.
*/
@@ -179,16 +205,16 @@ public PDFDebugger(boolean viewPages)
*/
private void initComponents()
{
- jSplitPane1 = new javax.swing.JSplitPane();
- jScrollPane1 = new JScrollPane();
+ jSplitPane = new javax.swing.JSplitPane();
+ JScrollPane jScrollPaneLeft = new JScrollPane();
tree = new Tree(this);
- jScrollPane2 = new JScrollPane();
- jTextPane1 = new javax.swing.JTextPane();
+ jScrollPaneRight = new JScrollPane();
+ jTextPane = new javax.swing.JTextPane();
tree.setCellRenderer(new PDFTreeCellRenderer());
tree.setModel(null);
- setTitle("PDFBox Debugger");
+ setTitle("Apache PDFBox Debugger");
addWindowListener(new java.awt.event.WindowAdapter()
{
@@ -202,12 +228,14 @@ public void windowOpened(WindowEvent windowEvent)
@Override
public void windowClosing(WindowEvent evt)
{
- exitForm(evt);
+ exitMenuItemActionPerformed(null);
}
});
-
- jScrollPane1.setBorder(new BevelBorder(BevelBorder.RAISED));
- jScrollPane1.setPreferredSize(new Dimension(300, 500));
+
+ windowPrefs = new WindowPrefs(this.getClass());
+
+ jScrollPaneLeft.setBorder(new BevelBorder(BevelBorder.RAISED));
+ jSplitPane.setDividerLocation(windowPrefs.getDividerLocation());
tree.addTreeSelectionListener(new TreeSelectionListener()
{
@Override
@@ -217,15 +245,14 @@ public void valueChanged(TreeSelectionEvent evt)
}
});
- jScrollPane1.setViewportView(tree);
+ jScrollPaneLeft.setViewportView(tree);
- jSplitPane1.setRightComponent(jScrollPane2);
- jSplitPane1.setDividerSize(3);
-
- jScrollPane2.setPreferredSize(new Dimension(300, 500));
- jScrollPane2.setViewportView(jTextPane1);
+ jSplitPane.setRightComponent(jScrollPaneRight);
+ jSplitPane.setDividerSize(3);
- jSplitPane1.setLeftComponent(jScrollPane1);
+ jScrollPaneRight.setViewportView(jTextPane);
+
+ jSplitPane.setLeftComponent(jScrollPaneLeft);
JScrollPane documentScroller = new JScrollPane();
documentScroller.setViewportView(documentPanel);
@@ -235,7 +262,10 @@ public void valueChanged(TreeSelectionEvent evt)
statusPane.getPanel().setPreferredSize(new Dimension(300, 25));
getContentPane().add(statusPane.getPanel(), BorderLayout.PAGE_START);
- getContentPane().add(jSplitPane1, BorderLayout.CENTER);
+ getContentPane().add(jSplitPane, BorderLayout.CENTER);
+
+ statusBar = new ReaderBottomPanel();
+ getContentPane().add(statusBar, BorderLayout.SOUTH);
// create menus
JMenuBar menuBar = new JMenuBar();
@@ -244,8 +274,8 @@ public void valueChanged(TreeSelectionEvent evt)
menuBar.add(createViewMenu());
setJMenuBar(menuBar);
- Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- setBounds((screenSize.width-700)/2, (screenSize.height-600)/2, 700, 600);
+ setExtendedState(windowPrefs.getExtendedState());
+ setBounds(windowPrefs.getBounds());
// drag and drop to open files
setTransferHandler(new TransferHandler()
@@ -266,19 +296,30 @@ public boolean importData(TransferSupport transferSupport)
List files = (List) transferable.getTransferData(
DataFlavor.javaFileListFlavor);
readPDFFile(files.get(0), "");
- return true;
}
catch (IOException e)
{
- throw new RuntimeException(e);
+ new ErrorDialog(e).setVisible(true);
}
catch (UnsupportedFlavorException e)
{
throw new RuntimeException(e);
}
+ return true;
}
});
+ initGlobalEventHandlers();
+ }
+
+ /**
+ * Initialize application global event handlers. Protected to allow
+ * subclasses to override this method if they don't want the global event
+ * handler overridden.
+ */
+ @SuppressWarnings("WeakerAccess")
+ protected void initGlobalEventHandlers()
+ {
// Mac OS X file open/quit handler
if (IS_MAC_OS)
{
@@ -314,6 +355,7 @@ public void actionPerformed(ActionEvent evt)
JMenu fileMenu = new JMenu("File");
fileMenu.add(openMenuItem);
+ fileMenu.setMnemonic('F');
JMenuItem openUrlMenuItem = new JMenuItem("Open URL...");
openUrlMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, SHORCUT_KEY_MASK));
@@ -338,6 +380,33 @@ public void actionPerformed(ActionEvent evt)
}
});
fileMenu.add(openUrlMenuItem);
+
+ reopenMenuItem = new JMenuItem("Reopen");
+ reopenMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, SHORCUT_KEY_MASK));
+ reopenMenuItem.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent evt)
+ {
+ try
+ {
+ if (currentFilePath.startsWith("http"))
+ {
+ readPDFurl(currentFilePath, "");
+ }
+ else
+ {
+ readPDFFile(currentFilePath, "");
+ }
+ }
+ catch (IOException e)
+ {
+ new ErrorDialog(e).setVisible(true);
+ }
+ }
+ });
+ reopenMenuItem.setEnabled(false);
+ fileMenu.add(reopenMenuItem);
try
{
@@ -354,6 +423,7 @@ public void actionPerformed(ActionEvent evt)
fileMenu.add(recentFilesMenu);
printMenuItem = new JMenuItem("Print");
+ printMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, SHORCUT_KEY_MASK));
printMenuItem.setEnabled(false);
printMenuItem.addActionListener(new ActionListener()
{
@@ -364,25 +434,22 @@ public void actionPerformed(ActionEvent evt)
}
});
- if (!IS_MAC_OS)
- {
- fileMenu.addSeparator();
- fileMenu.add(printMenuItem);
- }
+ fileMenu.addSeparator();
+ fileMenu.add(printMenuItem);
- JMenuItem exitMenuItem = new JMenuItem("Exit");
- exitMenuItem.setAccelerator(KeyStroke.getKeyStroke("alt F4"));
- exitMenuItem.addActionListener(new ActionListener()
+ if (!IS_MAC_OS)
{
- @Override
- public void actionPerformed(ActionEvent evt)
+ JMenuItem exitMenuItem = new JMenuItem("Exit");
+ exitMenuItem.setAccelerator(KeyStroke.getKeyStroke("alt F4"));
+ exitMenuItem.addActionListener(new ActionListener()
{
- exitMenuItemActionPerformed(evt);
- }
- });
+ @Override
+ public void actionPerformed(ActionEvent evt)
+ {
+ exitMenuItemActionPerformed(evt);
+ }
+ });
- if (!IS_MAC_OS)
- {
fileMenu.addSeparator();
fileMenu.add(exitMenuItem);
}
@@ -393,6 +460,7 @@ public void actionPerformed(ActionEvent evt)
private JMenu createEditMenu()
{
JMenu editMenu = new JMenu("Edit");
+ editMenu.setMnemonic('E');
JMenuItem cutMenuItem = new JMenuItem("Cut");
cutMenuItem.setEnabled(false);
@@ -418,6 +486,7 @@ private JMenu createEditMenu()
private JMenu createViewMenu()
{
JMenu viewMenu = new JMenu("View");
+ viewMenu.setMnemonic('V');
if (isPageMode)
{
viewModeItem = new JMenuItem("Show Internal Structure");
@@ -456,7 +525,27 @@ public void actionPerformed(ActionEvent actionEvent)
RotationMenu rotationMenu = RotationMenu.getInstance();
rotationMenu.setEnableMenu(false);
viewMenu.add(rotationMenu.getMenu());
+
+ ImageTypeMenu imageTypeMenu = ImageTypeMenu.getInstance();
+ imageTypeMenu.setEnableMenu(false);
+ viewMenu.add(imageTypeMenu.getMenu());
+
+ RenderDestinationMenu renderDestinationMenu = RenderDestinationMenu.getInstance();
+ renderDestinationMenu.setEnableMenu(false);
+ viewMenu.add(renderDestinationMenu.getMenu());
+ viewMenu.addSeparator();
+
+ allowSubsampling = new JCheckBoxMenuItem("Allow subsampling");
+ allowSubsampling.setEnabled(false);
+ viewMenu.add(allowSubsampling);
+
+ viewMenu.addSeparator();
+
+ repairAcroFormMenuItem = new JCheckBoxMenuItem("Repair AcroForm");
+ repairAcroFormMenuItem.setEnabled(false);
+ viewMenu.add(repairAcroFormMenuItem);
+
return viewMenu;
}
@@ -507,7 +596,7 @@ public JMenu getFindMenu()
}
/**
- * Returns the Edit > Find > Find menu item.
+ * Returns the Edit > Find > Find menu item.
*/
public JMenuItem getFindMenuItem()
{
@@ -515,7 +604,7 @@ public JMenuItem getFindMenuItem()
}
/**
- * Returns the Edit > Find > Find Next menu item.
+ * Returns the Edit > Find > Find Next menu item.
*/
public JMenuItem getFindNextMenuItem()
{
@@ -523,7 +612,7 @@ public JMenuItem getFindNextMenuItem()
}
/**
- * Returns the Edit > Find > Find Previous menu item.
+ * Returns the Edit > Find > Find Previous menu item.
*/
public JMenuItem getFindPreviousMenuItem()
{
@@ -563,15 +652,15 @@ private void openMenuItemActionPerformed(ActionEvent evt)
openDialog.setFilenameFilter(new FilenameFilter()
{
@Override
- public boolean accept(File file, String s)
+ public boolean accept(File dir, String name)
{
- return file.getName().toLowerCase().endsWith(".pdf");
+ return name.toLowerCase().endsWith(".pdf");
}
});
openDialog.setVisible(true);
if (openDialog.getFile() != null)
{
- readPDFFile(openDialog.getFile(), "");
+ readPDFFile(new File(openDialog.getDirectory(),openDialog.getFile()), "");
}
}
else
@@ -602,6 +691,8 @@ private void jTree1ValueChanged(TreeSelectionEvent evt)
{
Object selectedNode = path.getLastPathComponent();
+ statusBar.getStatusLabel().setText("");
+
if (isPage(selectedNode))
{
showPage(selectedNode);
@@ -635,12 +726,12 @@ && isFlagNode(selectedNode, path.getParentPath().getLastPathComponent()))
showString(selectedNode);
return;
}
- if (jSplitPane1.getRightComponent() == null
- || !jSplitPane1.getRightComponent().equals(jScrollPane2))
+ if (jSplitPane.getRightComponent() == null
+ || !jSplitPane.getRightComponent().equals(jScrollPaneRight))
{
- replaceRightComponent(jScrollPane2);
+ replaceRightComponent(jScrollPaneRight);
}
- jTextPane1.setText(convertToString(selectedNode));
+ jTextPane.setText(convertToString(selectedNode));
}
catch (Exception e)
{
@@ -711,6 +802,7 @@ private boolean isFlagNode(Object selectedNode, Object parentNode)
(COSName.F.equals(key) && isAnnot(parentNode)) ||
COSName.FF.equals(key) ||
COSName.PANOSE.equals(key) ||
+ COSName.SIG_FLAGS.equals(key) ||
(COSName.P.equals(key) && isEncrypt(parentNode));
}
return false;
@@ -721,7 +813,7 @@ private boolean isEncrypt(Object obj)
if (obj instanceof MapEntry)
{
MapEntry entry = (MapEntry) obj;
- return (COSName.ENCRYPT.equals(entry.getKey()) && entry.getValue() instanceof COSDictionary);
+ return COSName.ENCRYPT.equals(entry.getKey()) && entry.getValue() instanceof COSDictionary;
}
return false;
}
@@ -730,16 +822,14 @@ private boolean isFontDescriptor(Object obj)
{
Object underneathObject = getUnderneathObject(obj);
return underneathObject instanceof COSDictionary &&
- ((COSDictionary) underneathObject).containsKey(COSName.TYPE) &&
- ((COSDictionary) underneathObject).getCOSName(COSName.TYPE).equals(COSName.FONT_DESC);
+ COSName.FONT_DESC.equals(((COSDictionary) underneathObject).getCOSName(COSName.TYPE));
}
private boolean isAnnot(Object obj)
{
Object underneathObject = getUnderneathObject(obj);
return underneathObject instanceof COSDictionary &&
- ((COSDictionary) underneathObject).containsKey(COSName.TYPE) &&
- ((COSDictionary) underneathObject).getCOSName(COSName.TYPE).equals(COSName.ANNOT);
+ COSName.ANNOT.equals(((COSDictionary) underneathObject).getCOSName(COSName.TYPE));
}
private boolean isStream(Object selectedNode)
@@ -757,26 +847,23 @@ private boolean isFont(Object selectedNode)
selectedNode = getUnderneathObject(selectedNode);
if (selectedNode instanceof COSDictionary)
{
- COSDictionary dic = (COSDictionary)selectedNode;
- return dic.containsKey(COSName.TYPE) &&
- dic.getCOSName(COSName.TYPE).equals(COSName.FONT) &&
- !isCIDFont(dic);
+ COSDictionary dic = (COSDictionary) selectedNode;
+ return COSName.FONT.equals(dic.getCOSName(COSName.TYPE)) && !isCIDFont(dic);
}
return false;
}
private boolean isCIDFont(COSDictionary dic)
{
- return dic.containsKey(COSName.SUBTYPE) &&
- (dic.getCOSName(COSName.SUBTYPE).equals(COSName.CID_FONT_TYPE0)
- || dic.getCOSName(COSName.SUBTYPE).equals(COSName.CID_FONT_TYPE2));
+ return COSName.CID_FONT_TYPE0.equals(dic.getCOSName(COSName.SUBTYPE)) ||
+ COSName.CID_FONT_TYPE2.equals(dic.getCOSName(COSName.SUBTYPE));
}
/**
* Show a Panel describing color spaces in more detail and interactive way.
* @param csNode the special color space containing node.
*/
- private void showColorPane(Object csNode)
+ private void showColorPane(Object csNode) throws IOException
{
csNode = getUnderneathObject(csNode);
@@ -824,7 +911,7 @@ private void showPage(Object selectedNode)
COSBase typeItem = page.getItem(COSName.TYPE);
if (COSName.PAGE.equals(typeItem))
{
- PagePane pagePane = new PagePane(document, page);
+ PagePane pagePane = new PagePane(document, page, statusBar.getStatusLabel());
replaceRightComponent(new JScrollPane(pagePane.getPanel()));
}
}
@@ -836,7 +923,9 @@ private void showFlagPane(Object parentNode, Object selectedNode)
{
selectedNode = ((MapEntry)selectedNode).getKey();
selectedNode = getUnderneathObject(selectedNode);
- FlagBitsPane flagBitsPane = new FlagBitsPane((COSDictionary) parentNode, (COSName) selectedNode);
+ FlagBitsPane flagBitsPane = new FlagBitsPane(document,
+ (COSDictionary) parentNode,
+ (COSName) selectedNode);
replaceRightComponent(flagBitsPane.getPane());
}
}
@@ -865,7 +954,8 @@ else if (COSName.CONTENTS.equals(parentKey) || COSName.CHAR_PROCS.equals(parentK
isContentStream = true;
}
else if (COSName.FORM.equals(stream.getCOSName(COSName.SUBTYPE)) ||
- COSName.PATTERN.equals(stream.getCOSName(COSName.TYPE)))
+ COSName.PATTERN.equals(stream.getCOSName(COSName.TYPE)) ||
+ stream.getInt(COSName.PATTERN_TYPE) == 1)
{
if (stream.containsKey(COSName.RESOURCES))
{
@@ -875,7 +965,6 @@ else if (COSName.FORM.equals(stream.getCOSName(COSName.SUBTYPE)) ||
}
else if (COSName.THUMB.equals(key))
{
- resourcesDic = null;
isThumb = true;
}
else if (COSName.IMAGE.equals((stream).getCOSName(COSName.SUBTYPE)))
@@ -898,7 +987,7 @@ private void showFont(Object selectedNode, TreePath path)
if (pane == null)
{
// unsupported font type
- replaceRightComponent(jScrollPane2);
+ replaceRightComponent(jScrollPaneRight);
return;
}
replaceRightComponent(pane);
@@ -907,9 +996,9 @@ private void showFont(Object selectedNode, TreePath path)
// replace the right component while keeping divider position
private void replaceRightComponent(Component pane)
{
- int div = jSplitPane1.getDividerLocation();
- jSplitPane1.setRightComponent(pane);
- jSplitPane1.setDividerLocation(div);
+ int div = jSplitPane.getDividerLocation();
+ jSplitPane.setRightComponent(pane);
+ jSplitPane.setDividerLocation(div);
}
private void showString(Object selectedNode)
@@ -988,23 +1077,21 @@ else if( selectedNode instanceof COSString )
}
else if( selectedNode instanceof COSStream )
{
+ COSStream stream = (COSStream) selectedNode;
+ InputStream in = null;
try
{
- COSStream stream = (COSStream)selectedNode;
- InputStream ioStream = stream.createInputStream();
- ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int amountRead;
- while( (amountRead = ioStream.read( buffer, 0, buffer.length ) ) != -1 )
- {
- byteArray.write( buffer, 0, amountRead );
- }
- data = byteArray.toString();
+ in = stream.createInputStream();
+ data = new String(IOUtils.toByteArray(in));
}
- catch( IOException e )
+ catch (IOException e)
{
throw new RuntimeException(e);
}
+ finally
+ {
+ IOUtils.closeQuietly(in);
+ }
}
else if( selectedNode instanceof MapEntry )
{
@@ -1017,7 +1104,7 @@ else if( selectedNode instanceof ArrayEntry )
return data;
}
- private void exitMenuItemActionPerformed(ActionEvent evt)
+ private void exitMenuItemActionPerformed(ActionEvent ignored)
{
if( document != null )
{
@@ -1035,51 +1122,75 @@ private void exitMenuItemActionPerformed(ActionEvent evt)
throw new RuntimeException(e);
}
}
+ windowPrefs.setExtendedState(getExtendedState());
+ this.setExtendedState(Frame.NORMAL);
+ windowPrefs.setBounds(getBounds());
+ windowPrefs.setDividerLocation(jSplitPane.getDividerLocation());
+ performApplicationExit();
+ }
+
+ /**
+ * Exit the application after the window is closed. This is protected to let
+ * subclasses override the behavior.
+ */
+ @SuppressWarnings("WeakerAccess")
+ protected void performApplicationExit()
+ {
System.exit(0);
}
private void printMenuItemActionPerformed(ActionEvent evt)
{
- if( document != null )
+ if (document == null)
{
- try
- {
- PrinterJob job = PrinterJob.getPrinterJob();
- job.setPageable(new PDFPageable(document));
- if (job.printDialog())
- {
- job.print();
- }
- }
- catch (PrinterException e)
- {
- throw new RuntimeException(e);
- }
+ return;
}
- }
-
- /**
- * Exit the Application.
- */
- private void exitForm(WindowEvent evt)
- {
- if( document != null )
+ AccessPermission ap = document.getCurrentAccessPermission();
+ if (!ap.canPrint())
{
- try
+ JOptionPane.showMessageDialog(this, "You do not have permission to print");
+ return;
+ }
+
+ try
+ {
+ PrinterJob job = PrinterJob.getPrinterJob();
+ job.setPageable(new PDFPageable(document));
+ PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
+ PDViewerPreferences vp = document.getDocumentCatalog().getViewerPreferences();
+ if (vp != null && vp.getDuplex() != null)
{
- document.close();
- if (!currentFilePath.startsWith("http"))
+ String dp = vp.getDuplex();
+ if (PDViewerPreferences.DUPLEX.DuplexFlipLongEdge.toString().equals(dp))
{
- recentFiles.addFile(currentFilePath);
+ pras.add(Sides.TWO_SIDED_LONG_EDGE);
+ }
+ else if (PDViewerPreferences.DUPLEX.DuplexFlipShortEdge.toString().equals(dp))
+ {
+ pras.add(Sides.TWO_SIDED_SHORT_EDGE);
+ }
+ else if (PDViewerPreferences.DUPLEX.Simplex.toString().equals(dp))
+ {
+ pras.add(Sides.ONE_SIDED);
}
- recentFiles.close();
}
- catch( IOException e )
+ if (job.printDialog(pras))
{
- throw new RuntimeException(e);
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ try
+ {
+ job.print(pras);
+ }
+ finally
+ {
+ setCursor(Cursor.getDefaultCursor());
+ }
}
}
- System.exit(0);
+ catch (PrinterException e)
+ {
+ throw new RuntimeException(e);
+ }
}
/**
@@ -1091,8 +1202,11 @@ private void exitForm(WindowEvent evt)
public static void main(String[] args) throws Exception
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- System.setProperty("apple.laf.useScreenMenuBar", "true");
-
+ if (System.getProperty("apple.laf.useScreenMenuBar") == null)
+ {
+ System.setProperty("apple.laf.useScreenMenuBar", "true");
+ }
+
// handle uncaught exceptions
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
{
@@ -1102,9 +1216,10 @@ public void uncaughtException(Thread thread, Throwable throwable)
new ErrorDialog(throwable).setVisible(true);
}
});
-
+
// open file, if any
String filename = null;
+ @SuppressWarnings({"squid:S2068"})
String password = "";
boolean viewPages = true;
@@ -1129,8 +1244,25 @@ else if( args[i].equals(VIEW_STRUCTURE) )
}
}
final PDFDebugger viewer = new PDFDebugger(viewPages);
-
-
+
+ // use our custom logger
+ // this works only if there is no "LogFactory.getLog()" in this class,
+ // and if there are no methods that call logging, even invisible
+ // use reduced file from PDFBOX-3653 to see logging
+ LogDialog.init(viewer, viewer.statusBar.getLogLabel());
+ System.setProperty("org.apache.commons.logging.Log", "org.apache.pdfbox.debugger.ui.DebugLog");
+
+ // trigger premature initializations for more accurate rendering benchmarks
+ // See discussion in PDFBOX-3988
+ if (PDType1Font.COURIER.isStandard14())
+ {
+ // Yes this is always true
+ PDDeviceCMYK.INSTANCE.toRGB(new float[] { 0, 0, 0, 0} );
+ PDDeviceRGB.INSTANCE.toRGB(new float[] { 0, 0, 0 } );
+ IIORegistry.getDefaultInstance();
+ FilterFactory.INSTANCE.getFilter(COSName.FLATE_DECODE);
+ }
+
if (filename != null)
{
File file = new File(filename);
@@ -1148,7 +1280,7 @@ private void readPDFFile(String filePath, String password) throws IOException
readPDFFile(file, password);
}
- private void readPDFFile(File file, String password) throws IOException
+ private void readPDFFile(final File file, String password) throws IOException
{
if( document != null )
{
@@ -1160,7 +1292,18 @@ private void readPDFFile(File file, String password) throws IOException
}
currentFilePath = file.getPath();
recentFiles.removeFile(file.getPath());
- parseDocument( file, password );
+ LogDialog.instance().clear();
+ DocumentOpener documentOpener = new DocumentOpener(password)
+ {
+ @Override
+ PDDocument open() throws IOException
+ {
+ return PDDocument.load(file, password);
+ }
+ };
+ document = documentOpener.parse();
+ printMenuItem.setEnabled(true);
+ reopenMenuItem.setEnabled(true);
initTree();
@@ -1176,7 +1319,7 @@ private void readPDFFile(File file, String password) throws IOException
addRecentFileItems();
}
- private void readPDFurl(String urlString, String password) throws IOException
+ private void readPDFurl(final String urlString, String password) throws IOException
{
if (document != null)
{
@@ -1187,8 +1330,18 @@ private void readPDFurl(String urlString, String password) throws IOException
}
}
currentFilePath = urlString;
- URL url = new URL(urlString);
- document = PDDocument.load(url.openStream(), password);
+ LogDialog.instance().clear();
+ DocumentOpener documentOpener = new DocumentOpener(password)
+ {
+ @Override
+ PDDocument open() throws IOException
+ {
+ return PDDocument.load(new URL(urlString).openStream(), password);
+ }
+ };
+ document = documentOpener.parse();
+ printMenuItem.setEnabled(true);
+ reopenMenuItem.setEnabled(true);
initTree();
@@ -1212,6 +1365,10 @@ private void initTree()
{
File file = new File(currentFilePath);
DocumentEntry documentEntry = new DocumentEntry(document, file.getName());
+ ZoomMenu.getInstance().resetZoom();
+ RotationMenu.getInstance().setRotationSelection(RotationMenu.ROTATE_0_DEGREES);
+ ImageTypeMenu.getInstance().setImageTypeSelection(ImageTypeMenu.IMAGETYPE_RGB);
+ RenderDestinationMenu.getInstance().setRenderDestinationSelection(RenderDestinationMenu.RENDER_DESTINATION_EXPORT);
tree.setModel(new PDFTreeModel(documentEntry));
// Root/Pages/Kids/[0] is not always the first page, so use the first row instead:
tree.setSelectionPath(tree.getPathForRow(1));
@@ -1222,44 +1379,65 @@ private void initTree()
tree.setSelectionPath(treeStatus.getPathForString("Root"));
}
}
-
+
/**
- * This will parse a document.
- *
- * @param file The file addressing the document.
- *
- * @throws IOException If there is an error parsing the document.
+ * Internal class to avoid double code in password entry loop.
*/
- private void parseDocument( File file, String password )throws IOException
+ abstract static class DocumentOpener
{
- while (true)
+ String password;
+
+ DocumentOpener(String password)
{
- try
- {
- document = PDDocument.load(file, password);
- }
- catch (InvalidPasswordException ipe)
+ this.password = password;
+ }
+
+ /**
+ * Override to load the actual input type (File, URL, stream), don't call it directly!
+ *
+ * @return the PDDocument instance
+ * @throws IOException Cannot read document
+ */
+ abstract PDDocument open() throws IOException;
+
+ /**
+ * Call this!
+ *
+ * @return the PDDocument instance
+ * @throws IOException Cannot read document
+ */
+ final PDDocument parse() throws IOException
+ {
+ while (true)
{
- // https://stackoverflow.com/questions/8881213/joptionpane-to-get-password
- JPanel panel = new JPanel();
- JLabel label = new JLabel("Password:");
- JPasswordField pass = new JPasswordField(10);
- panel.add(label);
- panel.add(pass);
- String[] options = new String[] {"OK", "Cancel"};
- int option = JOptionPane.showOptionDialog(null, panel, "Enter password",
- JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE,
- null, options, "");
- if (option == 0)
+ try
{
- password = new String(pass.getPassword());
- continue;
+ return open();
+ }
+ catch (InvalidPasswordException ipe)
+ {
+ // https://stackoverflow.com/questions/8881213/joptionpane-to-get-password
+ JPanel panel = new JPanel();
+ JLabel label = new JLabel("Password:");
+ JPasswordField pass = new JPasswordField(10);
+ panel.add(label);
+ panel.add(pass);
+ String[] options = new String[]
+ {
+ "OK", "Cancel"
+ };
+ int option = JOptionPane.showOptionDialog(null, panel, "Enter password",
+ JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE,
+ null, options, "");
+ if (option == 0)
+ {
+ password = new String(pass.getPassword());
+ continue;
+ }
+ throw ipe;
}
- throw ipe;
}
- break;
- }
- printMenuItem.setEnabled(true);
+ }
}
private void addRecentFileItems()
@@ -1311,4 +1489,33 @@ private static void usage()
System.err.println(message);
System.exit(1);
}
+
+ /**
+ * Convenience method to get the page label if available.
+ *
+ * @param document
+ * @param pageIndex 0-based page number.
+ * @return a page label or null if not available.
+ */
+ public static String getPageLabel(PDDocument document, int pageIndex)
+ {
+ PDPageLabels pageLabels;
+ try
+ {
+ pageLabels = document.getDocumentCatalog().getPageLabels();
+ }
+ catch (IOException ex)
+ {
+ return ex.getMessage();
+ }
+ if (pageLabels != null)
+ {
+ String[] labels = pageLabels.getLabelsByPageIndices();
+ if (labels[pageIndex] != null)
+ {
+ return labels[pageIndex];
+ }
+ }
+ return null;
+ }
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSArrayBased.java b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSArrayBased.java
index b68d0cebe0f..b0072df2cc6 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSArrayBased.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSArrayBased.java
@@ -19,12 +19,14 @@
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
+import java.awt.color.ColorSpace;
import java.io.IOException;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased;
import org.apache.pdfbox.pdmodel.graphics.color.PDPattern;
/**
@@ -84,6 +86,44 @@ private void initUI()
colorCountLabel.setFont(new Font(Font.MONOSPACED, Font.BOLD, 20));
panel.add(colorCountLabel);
}
+
+ if (colorSpace instanceof PDICCBased)
+ {
+ PDICCBased icc = (PDICCBased) colorSpace;
+ int colorSpaceType = icc.getColorSpaceType();
+ String cs;
+ switch (colorSpaceType)
+ {
+ case ColorSpace.CS_LINEAR_RGB:
+ cs = "linear RGB";
+ break;
+ case ColorSpace.CS_CIEXYZ:
+ cs = "CIEXYZ";
+ break;
+ case ColorSpace.CS_GRAY:
+ cs = "linear gray";
+ break;
+ case ColorSpace.CS_sRGB:
+ cs = "sRGB";
+ break;
+ case ColorSpace.TYPE_RGB:
+ cs = "RGB";
+ break;
+ case ColorSpace.TYPE_GRAY:
+ cs = "gray";
+ break;
+ case ColorSpace.TYPE_CMYK:
+ cs = "CMYK";
+ break;
+ default:
+ cs = "type " + colorSpaceType;
+ break;
+ }
+ JLabel otherLabel = new JLabel("Colorspace type: " + cs);
+ otherLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ otherLabel.setFont(new Font(Font.MONOSPACED, Font.BOLD, 20));
+ panel.add(otherLabel);
+ }
}
/**
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSDeviceN.java b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSDeviceN.java
index 23f1a4e46ff..3826d1a0516 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSDeviceN.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSDeviceN.java
@@ -31,36 +31,27 @@
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceN;
-
-/**
- *@author Khyrul Bashar.
- */
-
/**
* A class that provides the necessary UI and functionalities to show the DeviceN color space.
+ *
+ * @author Khyrul Bashar.
+ *
*/
public class CSDeviceN
{
- private PDDeviceN deviceN;
+ private final PDDeviceN deviceN;
private JPanel panel;
/**
* Constructor
*
- * @param array COSArray instance that holds DeviceN color space
+ * @param array COSArray instance that holds the DeviceN color space
*/
- public CSDeviceN(COSArray array)
+ public CSDeviceN(COSArray array) throws IOException
{
- try
- {
- deviceN = new PDDeviceN(array);
- DeviceNColorant[] colorants = getColorantData();
- initUI(colorants);
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
+ deviceN = new PDDeviceN(array);
+ DeviceNColorant[] colorants = getColorantData();
+ initUI(colorants);
}
/**
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSIndexed.java b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSIndexed.java
index 32edf8b0686..9251ae0db34 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSIndexed.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSIndexed.java
@@ -33,41 +33,33 @@
import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.pdmodel.graphics.color.PDIndexed;
-/**
- * @author Khyrul Bashar.
- */
-
/**
* A class that provides the necessary UI and functionalities to show the Indexed colorspace.
+ *
+ * @author Khyrul Bashar.
*/
public class CSIndexed
{
- private PDIndexed indexed;
+ private final PDIndexed indexed;
private JPanel panel;
- private int colorCount;
+ private final int colorCount;
/**
* Constructor.
- * @param array COSArray instance for Indexed Colorspace.
+ * @param array COSArray instance for Indexed color space.
+ * @throws java.io.IOException
*/
- public CSIndexed(COSArray array)
+ public CSIndexed(COSArray array) throws IOException
{
- try
- {
- indexed = new PDIndexed(array);
- colorCount = getHival(array);
- initUI(getColorantData());
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
+ indexed = new PDIndexed(array);
+ colorCount = getHival(array) + 1;
+ initUI(getColorantData());
}
/**
* Parses the colorant data from the array and return.
*
- * @return
+ * @return the colorant data
*/
private IndexedColorant[] getColorantData()
{
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSSeparation.java b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSSeparation.java
index 7eb210a37be..5e613494a1d 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSSeparation.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/CSSeparation.java
@@ -40,12 +40,10 @@
import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation;
/**
+ * A class that provides the necessary UI and functionalities to show the Separation color space.
+ *
* @author Khyrul Bashar.
*/
-
-/**
- *A class that provides the necessary UI and functionalities to show the Separation color space.
- */
public class CSSeparation implements ChangeListener, ActionListener
{
private JSlider slider;
@@ -53,23 +51,19 @@ public class CSSeparation implements ChangeListener, ActionListener
private JLabel colorBar;
private JPanel panel;
- private PDSeparation separation;
+ private final PDSeparation separation;
private float tintValue = 1;
/**
* Constructor
- * @param array COSArray instance of the separation color space.
+ *
+ * @param array COSArray instance of the Separation color space.
+ *
+ * @throws java.io.IOException
*/
- public CSSeparation(COSArray array)
+ public CSSeparation(COSArray array) throws IOException
{
- try
- {
- separation = new PDSeparation(array);
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
+ separation = new PDSeparation(array);
initUI();
initValues();
}
@@ -77,7 +71,7 @@ public CSSeparation(COSArray array)
/**
* initialize all the UI elements and arrange them.
*/
- private void initUI()
+ private void initUI() throws IOException
{
Font boldFont = new Font(Font.MONOSPACED, Font.BOLD, 20);
@@ -90,7 +84,8 @@ private void initUI()
slider.setMajorTickSpacing(50);
slider.setPaintTicks(true);
- Dictionary labelTable = new Hashtable();
+ @SuppressWarnings({"squid:S1149"})
+ Dictionary labelTable = new Hashtable();
JLabel lightest = new JLabel("lightest");
lightest.setFont(new Font(Font.MONOSPACED, Font.BOLD, 10));
JLabel darkest = new JLabel("darkest");
@@ -204,10 +199,17 @@ public JPanel getPanel()
@Override
public void stateChanged(ChangeEvent changeEvent)
{
- int value = slider.getValue();
- tintValue = getFloatRepresentation(value);
- tintField.setText(Float.toString(tintValue));
+ int value = slider.getValue();
+ tintValue = getFloatRepresentation(value);
+ tintField.setText(Float.toString(tintValue));
+ try
+ {
updateColorBar();
+ }
+ catch (IOException ex)
+ {
+ tintField.setText(ex.getMessage());
+ }
}
/**
@@ -228,36 +230,26 @@ public void actionPerformed(ActionEvent actionEvent)
{
tintField.setText(Float.toString(tintValue));
}
+ catch (IOException ex)
+ {
+ tintField.setText(ex.getMessage());
+ }
}
- private void updateColorBar()
+ private void updateColorBar() throws IOException
{
- try
- {
- float[] rgbValues = separation.toRGB(new float[] {tintValue});
- colorBar.setBackground(new Color(rgbValues[0], rgbValues[1], rgbValues[2]));
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
+ float[] rgbValues = separation.toRGB(new float[] {tintValue});
+ colorBar.setBackground(new Color(rgbValues[0], rgbValues[1], rgbValues[2]));
}
/**
* Set a little border around colorbar. color of the border is the darkest of the colorant.
*/
- private void setColorBarBorder()
+ private void setColorBarBorder() throws IOException
{
- try
- {
- float[] rgbValues = separation.toRGB(new float[] {1});
- Color darkest= new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
- colorBar.setBorder(new BevelBorder(BevelBorder.LOWERED, darkest, darkest));
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
+ float[] rgbValues = separation.toRGB(new float[] {1});
+ Color darkest= new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
+ colorBar.setBorder(new BevelBorder(BevelBorder.LOWERED, darkest, darkest));
}
private float getFloatRepresentation(int value)
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/DeviceNTableModel.java b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/DeviceNTableModel.java
index 346ccb274bb..5fb66e188f5 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/DeviceNTableModel.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/DeviceNTableModel.java
@@ -26,6 +26,7 @@
/**
* This the table model for showing DeviceN color space which extends AbstractTableModel.
*/
+@SuppressWarnings({"serial","squid:S1948"})
public class DeviceNTableModel extends AbstractTableModel
{
private static final String[] COLUMNNAMES = new String[] { "Colorant", "Maximum", "Minimum"};
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/IndexedTableModel.java b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/IndexedTableModel.java
index fd32f42b4f1..b9a272a83a8 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/IndexedTableModel.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/colorpane/IndexedTableModel.java
@@ -26,6 +26,7 @@
/**
* This the table model for showing Indexed color space which extends AbstractTableModel.
*/
+@SuppressWarnings({"serial","squid:S1948"})
public class IndexedTableModel extends AbstractTableModel
{
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/AnnotFlag.java b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/AnnotFlag.java
index 24da4bc1a1d..f6bcb5b8ecc 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/AnnotFlag.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/AnnotFlag.java
@@ -67,7 +67,7 @@ Object[][] getFlagBits()
new Object[]{7, "ReadOnly", annotation.isReadOnly()},
new Object[]{8, "Locked", annotation.isLocked()},
new Object[]{9, "ToggleNoView", annotation.isToggleNoView()},
- new Object[]{10, "LockedContents", annotation.isLocked()}
+ new Object[]{10, "LockedContents", annotation.isLockedContents()}
};
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FieldFlag.java b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FieldFlag.java
index c835308331f..bf45d45dfff 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FieldFlag.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FieldFlag.java
@@ -53,7 +53,7 @@ else if (COSName.CH.equals(fieldType))
{
return "Choice field flag";
}
- return null;
+ return "Field flag";
}
@Override
@@ -80,7 +80,7 @@ else if (COSName.CH.equals(fieldType))
{
return getChoiceFieldFlagBits(flagValue);
}
- return null;
+ return getFieldFlagBits(flagValue);
}
private Object[][] getTextFieldFlagBits(final int flagValue)
@@ -127,6 +127,15 @@ private Object[][] getChoiceFieldFlagBits(final int flagValue)
};
}
+ private Object[][] getFieldFlagBits(final int flagValue)
+ {
+ return new Object[][]{
+ new Object[]{1, "ReadOnly", isFlagBitSet(flagValue, 1)},
+ new Object[]{2, "Required", isFlagBitSet(flagValue, 2)},
+ new Object[]{3, "NoExport", isFlagBitSet(flagValue, 3)}
+ };
+ }
+
/**
* Check the corresponding flag bit if set or not
* @param flagValue the flag integer
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPane.java
index 7b8520c4460..42279080a4a 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPane.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPane.java
@@ -20,6 +20,7 @@
import javax.swing.JPanel;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
/**
* @author Khyrul Bashar
@@ -29,14 +30,16 @@
public class FlagBitsPane
{
private FlagBitsPaneView view;
+ private final PDDocument document;
/**
* Constructor.
* @param dictionary COSDictionary instance.
* @param flagType COSName instance.
*/
- public FlagBitsPane(final COSDictionary dictionary, COSName flagType)
+ public FlagBitsPane(PDDocument document, final COSDictionary dictionary, COSName flagType)
{
+ this.document = document;
createPane(dictionary, flagType);
}
@@ -77,6 +80,12 @@ private void createPane(final COSDictionary dictionary, final COSName flagType)
view = new FlagBitsPaneView(
flag.getFlagType(), flag.getFlagValue(), flag.getFlagBits(), flag.getColumnNames());
}
+ if (COSName.SIG_FLAGS.equals(flagType))
+ {
+ flag = new SigFlag(document, dictionary);
+ view = new FlagBitsPaneView(
+ flag.getFlagType(), flag.getFlagValue(), flag.getFlagBits(), flag.getColumnNames());
+ }
}
/**
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPaneView.java b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPaneView.java
index 6d9b69c01b9..62c2523ed23 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPaneView.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/FlagBitsPaneView.java
@@ -35,11 +35,11 @@
*/
class FlagBitsPaneView
{
- final private JPanel panel;
- final private String flagHeader;
- final private String flagValue;
- final private Object[][] tableData;
- final private String[] columnNames;
+ private final JPanel panel;
+ private final String flagHeader;
+ private final String flagValue;
+ private final Object[][] tableData;
+ private final String[] columnNames;
/**
* Constructor
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/PanoseFlag.java b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/PanoseFlag.java
index ffb24e9f058..256a60da2b8 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/PanoseFlag.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/PanoseFlag.java
@@ -257,11 +257,7 @@ private String getXHeightValue(int index)
public final byte[] getPanoseBytes(COSDictionary style)
{
- if (style != null)
- {
- COSString panose = (COSString)style.getDictionaryObject(COSName.PANOSE);
- return panose.getBytes();
- }
- return null;
+ COSString panose = (COSString)style.getDictionaryObject(COSName.PANOSE);
+ return panose.getBytes();
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/SigFlag.java b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/SigFlag.java
new file mode 100644
index 00000000000..4df39c81945
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/flagbitspane/SigFlag.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.debugger.flagbitspane;
+
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+
+/**
+ * @author Tilman Hausherr
+ *
+ * A class that provides signature flag bits.
+ */
+public class SigFlag extends Flag
+{
+ private final PDDocument document;
+ private final COSDictionary acroFormDictionary;
+
+ /**
+ * Constructor
+ *
+ * @param acroFormDictionary COSDictionary instance.
+ */
+ SigFlag(PDDocument document, COSDictionary acroFormDictionary)
+ {
+ this.document = document;
+ this.acroFormDictionary = acroFormDictionary;
+ }
+
+ @Override
+ String getFlagType()
+ {
+ return "Signature flag";
+ }
+
+ @Override
+ String getFlagValue()
+ {
+ return "Flag value: " + acroFormDictionary.getInt(COSName.SIG_FLAGS);
+ }
+
+ @Override
+ Object[][] getFlagBits()
+ {
+ PDAcroForm acroForm = new PDAcroForm(document, acroFormDictionary);
+ return new Object[][]{
+ new Object[]{1, "SignaturesExist", acroForm.isSignaturesExist()},
+ new Object[]{2, "AppendOnly", acroForm.isAppendOnly()},
+ };
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingPaneController.java b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingPaneController.java
index 0891fba8f6f..0f5a8b8fba9 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingPaneController.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingPaneController.java
@@ -16,19 +16,49 @@
package org.apache.pdfbox.debugger.fontencodingpane;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
import java.io.IOException;
import javax.swing.JPanel;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDResources;
-import org.apache.pdfbox.pdmodel.font.PDCIDFontType2;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.font.PDType3Font;
-interface FontPane
+abstract class FontPane
{
- JPanel getPanel();
+ abstract JPanel getPanel();
+
+ /**
+ * Calculate vertical bounds common to all rendered glyphs.
+ *
+ * @param tableData
+ * @param glyphIndex the table index that has glyphs.
+ * @return an array with two elements: min lower bound (but max 0), and max upper bound (but min
+ * 0).
+ */
+ double[] getYBounds(Object[][] tableData, int glyphIndex)
+ {
+ double minY = 0;
+ double maxY = 0;
+ for (Object[] aTableData : tableData)
+ {
+ GeneralPath path = (GeneralPath) aTableData[glyphIndex];
+ Rectangle2D bounds2D = path.getBounds2D();
+ if (bounds2D.isEmpty())
+ {
+ continue;
+ }
+ minY = Math.min(minY, bounds2D.getMinY());
+ maxY = Math.max(maxY, bounds2D.getMaxY());
+ }
+ return new double[]{minY, maxY};
+ }
}
/**
@@ -39,6 +69,8 @@ interface FontPane
*/
public class FontEncodingPaneController
{
+ private static final Log LOG = LogFactory.getLog(FontEncodingPaneController.class);
+
private FontPane fontPane;
/**
@@ -52,19 +84,22 @@ public FontEncodingPaneController(COSName fontName, COSDictionary dictionary)
try
{
PDFont font = resources.getFont(fontName);
- if (font instanceof PDSimpleFont)
+ if (font instanceof PDType3Font)
+ {
+ fontPane = new Type3Font((PDType3Font) font, resources);
+ }
+ else if (font instanceof PDSimpleFont)
{
fontPane = new SimpleFont((PDSimpleFont) font);
}
- else if (font instanceof PDType0Font
- && ((PDType0Font) font).getDescendantFont() instanceof PDCIDFontType2)
+ else if (font instanceof PDType0Font)
{
- fontPane = new Type0Font((PDCIDFontType2) ((PDType0Font) font).getDescendantFont(), font);
+ fontPane = new Type0Font(((PDType0Font) font).getDescendantFont(), (PDType0Font) font);
}
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingView.java b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingView.java
index 48fbf6b167e..13da1fb7053 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingView.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/FontEncodingView.java
@@ -20,8 +20,16 @@
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.Map;
import javax.swing.JLabel;
@@ -30,25 +38,31 @@
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.TableCellRenderer;
+import org.apache.pdfbox.debugger.ui.HighResolutionImageIcon;
/**
* @author Khyrul Bashar
+ * @author Tilman Hausherr
* A class that creates the UI for font encoding pane.
*/
class FontEncodingView
{
private JPanel panel;
+ private static final AffineTransform DEFAULT_TRANSFORM = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform();
+
/**
* Constructor.
* @param tableData Object[][] instance as table data.
* @param headerAttributes Map instance which contains info for showing in header
* panel. Here keys will be info type.
* @param columnNames String array containing the columns name.
+ * @param yBounds min low and max high bound of all glyphs.
*/
- FontEncodingView(Object[][] tableData, Map headerAttributes, String[] columnNames)
+ FontEncodingView(Object[][] tableData, Map headerAttributes, String[] columnNames, double[] yBounds)
{
- createView(getHeaderPanel(headerAttributes), getTable(tableData, columnNames));
+ createView(getHeaderPanel(headerAttributes), getTable(tableData, columnNames, yBounds));
}
private void createView(JPanel headerPanel, JTable table)
@@ -78,11 +92,11 @@ private void createView(JPanel headerPanel, JTable table)
panel.add(scrollPane, gbc);
}
- private JTable getTable(Object[][] tableData, String[] columnNames)
+ private JTable getTable(Object[][] tableData, String[] columnNames, double[] yBounds)
{
JTable table = new JTable(tableData, columnNames);
table.setRowHeight(40);
- table.setDefaultRenderer(Object.class, new GlyphCellRenderer());
+ table.setDefaultRenderer(Object.class, new GlyphCellRenderer(yBounds));
return table;
}
@@ -95,7 +109,6 @@ private JPanel getHeaderPanel(Map attributes)
Iterator keys = attributes.keySet().iterator();
int row = 0;
while (keys.hasNext())
-
{
String key = keys.next();
JLabel encodingNameLabel = new JLabel(key + ": " + attributes.get(key));
@@ -108,7 +121,6 @@ private JPanel getHeaderPanel(Map attributes)
gbc.anchor = GridBagConstraints.LINE_START;
headerPanel.add(encodingNameLabel, gbc);
-
}
}
return headerPanel;
@@ -121,14 +133,68 @@ JPanel getPanel()
private static final class GlyphCellRenderer implements TableCellRenderer
{
+ private final double[] yBounds;
+
+ private GlyphCellRenderer(double[] yBounds)
+ {
+ this.yBounds = yBounds;
+ }
@Override
- public Component getTableCellRendererComponent(JTable jTable, Object o, boolean b, boolean b1, int i, int i1)
+ public Component getTableCellRendererComponent(JTable jTable, Object o, boolean b, boolean b1, int row, int col)
{
+ if (o instanceof GeneralPath)
+ {
+ GeneralPath path = (GeneralPath) o;
+ Rectangle2D bounds2D = path.getBounds2D();
+ if (bounds2D.isEmpty())
+ {
+ JLabel label = new JLabel(SimpleFont.NO_GLYPH, SwingConstants.CENTER);
+ label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 25));
+ label.setForeground(Color.RED);
+ return label;
+ }
+ Rectangle cellRect = jTable.getCellRect(row, col, false);
+ BufferedImage bim = renderGlyph(path, bounds2D, cellRect);
+ return new JLabel(new HighResolutionImageIcon(
+ bim,
+ (int) Math.ceil(bim.getWidth() / DEFAULT_TRANSFORM.getScaleX()),
+ (int) Math.ceil(bim.getHeight() / DEFAULT_TRANSFORM.getScaleY())),
+ SwingConstants.CENTER);
+ }
+ if (o instanceof BufferedImage)
+ {
+ Rectangle cellRect = jTable.getCellRect(row, col, false);
+ BufferedImage glyphImage = (BufferedImage) o;
+ BufferedImage cellImage = new BufferedImage(
+ (int) (cellRect.getWidth() * DEFAULT_TRANSFORM.getScaleX()),
+ (int) (cellRect.getHeight() * DEFAULT_TRANSFORM.getScaleY()),
+ BufferedImage.TYPE_INT_RGB);
+ Graphics2D g = (Graphics2D) cellImage.getGraphics();
+ g.setBackground(Color.white);
+ g.clearRect(0, 0, cellImage.getWidth(), cellImage.getHeight());
+
+ double scale = 1 / (glyphImage.getHeight() / cellRect.getHeight());
+
+ // horizontal center
+ g.translate((cellRect.getWidth() - glyphImage.getWidth() * scale) / 2 * DEFAULT_TRANSFORM.getScaleX(), 0);
+
+ // scale from the glyph to the cell
+ g.scale(scale * DEFAULT_TRANSFORM.getScaleX(), scale * DEFAULT_TRANSFORM.getScaleY());
+
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.drawImage(glyphImage, 0, 0, null);
+ g.dispose();
+ return new JLabel(new HighResolutionImageIcon(
+ cellImage,
+ (int) Math.ceil(cellImage.getWidth() / DEFAULT_TRANSFORM.getScaleX()),
+ (int) Math.ceil(cellImage.getHeight() / DEFAULT_TRANSFORM.getScaleY())));
+ }
if (o != null)
{
- JLabel label = new JLabel(o.toString());
- label.setHorizontalAlignment(SwingConstants.CENTER);
+ JLabel label = new JLabel(o.toString(), SwingConstants.CENTER);
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 25));
if (SimpleFont.NO_GLYPH.equals(o) || ".notdef".equals(o))
{
@@ -139,7 +205,39 @@ public Component getTableCellRendererComponent(JTable jTable, Object o, boolean
}
return new JLabel();
}
+
+ private BufferedImage renderGlyph(GeneralPath path, Rectangle2D bounds2D, Rectangle cellRect)
+ {
+ BufferedImage bim = new BufferedImage(
+ (int) (cellRect.getWidth() * DEFAULT_TRANSFORM.getScaleX()),
+ (int) (cellRect.getHeight() * DEFAULT_TRANSFORM.getScaleY()),
+ BufferedImage.TYPE_INT_RGB);
+ Graphics2D g = (Graphics2D) bim.getGraphics();
+ g.setBackground(Color.white);
+ g.clearRect(0, 0, bim.getWidth(), bim.getHeight());
+
+ double scale = 1 / ((yBounds[1] - yBounds[0]) / cellRect.getHeight());
+
+ // flip
+ g.scale(1, -1);
+ g.translate(0, -bim.getHeight());
+
+ // horizontal center
+ g.translate((cellRect.getWidth() - bounds2D.getWidth() * scale) / 2 * DEFAULT_TRANSFORM.getScaleX(), 0);
+
+ // scale from the glyph to the cell
+ g.scale(scale * DEFAULT_TRANSFORM.getScaleX(), scale * DEFAULT_TRANSFORM.getScaleY());
+
+ // Adjust for negative y min bound
+ g.translate(0, -yBounds[0]);
+
+ g.setColor(Color.black);
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.fill(path);
+ g.dispose();
+ return bim;
+ }
}
}
-
-
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/SimpleFont.java b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/SimpleFont.java
index 2418b9fe204..768de686ff4 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/SimpleFont.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/SimpleFont.java
@@ -16,65 +16,96 @@
package org.apache.pdfbox.debugger.fontencodingpane;
+import java.awt.geom.GeneralPath;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.JPanel;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
+import org.apache.pdfbox.pdmodel.font.PDVectorFont;
/**
* @author Khyrul Bashar
* A class that shows the glyph table along with unicode characters for SimpleFont.
*/
-class SimpleFont implements FontPane
+class SimpleFont extends FontPane
{
- public static final String NO_GLYPH = "No glyph";
- private int totalAvailableGlyph = 0;
+ private static final Log LOG = LogFactory.getLog(SimpleFont.class);
+
+ public static final String NO_GLYPH = "None";
private final FontEncodingView view;
+ private int totalAvailableGlyph = 0;
/**
* Constructor.
- * @param font PDSimpleFont instance.
+ * @param font PDSimpleFont instance, but not a type 3 font.
* @throws IOException If fails to parse unicode characters.
*/
SimpleFont(PDSimpleFont font) throws IOException
{
Object[][] tableData = getGlyphs(font);
+
+ double[] yBounds = getYBounds(tableData, 3);
Map attributes = new LinkedHashMap();
- attributes.put("Encoding", getEncodingName(font));
attributes.put("Font", font.getName());
- attributes.put("Glyph count", Integer.toString(totalAvailableGlyph));
+ attributes.put("Encoding", getEncodingName(font));
+ attributes.put("Glyphs", Integer.toString(totalAvailableGlyph));
+ attributes.put("Standard 14", Boolean.toString(font.isStandard14()));
- view = new FontEncodingView(tableData, attributes, new String[] {"Code", "Glyph Name","Unicode Character"});
+ view = new FontEncodingView(tableData, attributes,
+ new String[] {"Code", "Glyph Name", "Unicode Character", "Glyph"}, yBounds);
}
private Object[][] getGlyphs(PDSimpleFont font) throws IOException
{
- Object[][] glyphs = new Object[256][3];
+ Object[][] glyphs = new Object[256][4];
for (int index = 0; index <= 255; index++)
{
glyphs[index][0] = index;
- if (font.getEncoding().contains(index))
+ if (font.getEncoding().contains(index) || font.toUnicode(index) != null)
{
- glyphs[index][1] = font.getEncoding().getName(index);
+ String glyphName = font.getEncoding().getName(index);
+ glyphs[index][1] = glyphName;
glyphs[index][2] = font.toUnicode(index);
+ try
+ {
+ if (font instanceof PDVectorFont)
+ {
+ // using names didn't work with the file from PDFBOX-3445
+ glyphs[index][3] = ((PDVectorFont) font).getPath(index);
+ }
+ else
+ {
+ // type 1 font isn't a vector font in 2.0
+ glyphs[index][3] = font.getPath(glyphName);
+ }
+ }
+ catch (IOException ex)
+ {
+ LOG.error("Couldn't render code " + index + " ('" + glyphName + "') of font " +
+ font.getName(), ex);
+ glyphs[index][3] = new GeneralPath();
+ }
totalAvailableGlyph++;
}
else
{
glyphs[index][1] = NO_GLYPH;
glyphs[index][2] = NO_GLYPH;
+ glyphs[index][3] = font.getPath(".notdef");
}
}
return glyphs;
}
-
private String getEncodingName(PDSimpleFont font)
{
- return font.getEncoding().getClass().getSimpleName();
+ return font.getEncoding().getClass().getSimpleName() + " / " + font.getEncoding().getEncodingName();
}
@Override
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type0Font.java b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type0Font.java
index e773660855f..d532c7c750c 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type0Font.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type0Font.java
@@ -16,49 +16,99 @@
package org.apache.pdfbox.debugger.fontencodingpane;
-import java.awt.Dimension;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import javax.swing.JPanel;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.io.IOUtils;
-import org.apache.pdfbox.pdmodel.font.PDCIDFontType2;
+import org.apache.pdfbox.pdmodel.font.PDCIDFont;
import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import javax.swing.JPanel;
+import java.awt.Dimension;
+import java.awt.geom.GeneralPath;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
/**
* @author Khyrul Bashar
* A class that shows the CIDToGID table along with unicode characters for Type0Fonts when descendent
* font is of type PDCIDFontType2.
*/
-class Type0Font implements FontPane
+class Type0Font extends FontPane
{
- private FontEncodingView view;
-
+ public static final String NO_GLYPH = "No glyph";
+ private final FontEncodingView view;
+ private int totalAvailableGlyph = 0;
+
/**
* Constructor.
* @param descendantFont PDCIDFontType2 instance.
* @param parentFont PDFont instance.
* @throws IOException If fails to parse cidtogid map.
*/
- Type0Font(PDCIDFontType2 descendantFont, PDFont parentFont) throws IOException
+ Type0Font(PDCIDFont descendantFont, PDType0Font parentFont) throws IOException
{
Object[][] cidtogid = readCIDToGIDMap(descendantFont, parentFont);
if (cidtogid != null)
{
Map attributes = new LinkedHashMap();
attributes.put("Font", descendantFont.getName());
- attributes.put("CID count", Integer.toString(cidtogid.length));
+ attributes.put("CIDs", Integer.toString(cidtogid.length));
+
+ view = new FontEncodingView(cidtogid, attributes,
+ new String[]{"CID", "GID", "Unicode Character", "Glyph"}, getYBounds(cidtogid, 3));
+ }
+ else
+ {
+ Object[][] tab = readMap(descendantFont, parentFont);
+ Map attributes = new LinkedHashMap();
+ attributes.put("Font", descendantFont.getName());
+ attributes.put("CIDs", Integer.toString(tab.length));
+ attributes.put("Glyphs", Integer.toString(totalAvailableGlyph));
+ attributes.put("Standard 14", Boolean.toString(parentFont.isStandard14()));
- view = new FontEncodingView(cidtogid, attributes, new String[]{"CID", "GID", "Unicode Character"});
+ view = new FontEncodingView(tab, attributes,
+ new String[]{"Code", "CID", "GID", "Unicode Character", "Glyph"}, getYBounds(tab, 4));
+ }
+ }
+
+ private Object[][] readMap(PDCIDFont descendantFont, PDType0Font parentFont) throws IOException
+ {
+ int codes = 0;
+ for (int code = 0; code < 65535; ++code)
+ {
+ if (descendantFont.hasGlyph(code))
+ {
+ ++codes;
+ }
+ }
+ Object[][] tab = new Object[codes][5];
+ int index = 0;
+ for (int code = 0; code < 65535; ++code)
+ {
+ if (descendantFont.hasGlyph(code))
+ {
+ tab[index][0] = code;
+ tab[index][1] = descendantFont.codeToCID(code);
+ tab[index][2] = descendantFont.codeToGID(code);
+ tab[index][3] = parentFont.toUnicode(code);
+ GeneralPath path = descendantFont.getPath(code);
+ tab[index][4] = path;
+ if (!path.getBounds2D().isEmpty())
+ {
+ ++totalAvailableGlyph;
+ }
+ ++index;
+ }
}
+ return tab;
}
- private Object[][] readCIDToGIDMap(PDCIDFontType2 font, PDFont parentFont) throws IOException
+ private Object[][] readCIDToGIDMap(PDCIDFont font, PDFont parentFont) throws IOException
{
Object[][] cid2gid = null;
COSDictionary dict = font.getCOSObject();
@@ -71,7 +121,7 @@ private Object[][] readCIDToGIDMap(PDCIDFontType2 font, PDFont parentFont) throw
byte[] mapAsBytes = IOUtils.toByteArray(is);
IOUtils.closeQuietly(is);
int numberOfInts = mapAsBytes.length / 2;
- cid2gid = new Object[numberOfInts][3];
+ cid2gid = new Object[numberOfInts][4];
int offset = 0;
for (int index = 0; index < numberOfInts; index++)
{
@@ -82,14 +132,18 @@ private Object[][] readCIDToGIDMap(PDCIDFontType2 font, PDFont parentFont) throw
{
cid2gid[index][2] = parentFont.toUnicode(index);
}
+ GeneralPath path = font.getPath(index);
+ cid2gid[index][3] = path;
+ if (!path.getBounds2D().isEmpty())
+ {
+ ++totalAvailableGlyph;
+ }
offset += 2;
}
}
return cid2gid;
}
-
-
@Override
public JPanel getPanel()
{
@@ -101,4 +155,4 @@ public JPanel getPanel()
panel.setPreferredSize(new Dimension(300, 500));
return panel;
}
-}
\ No newline at end of file
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type3Font.java b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type3Font.java
new file mode 100644
index 00000000000..90e679aa200
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/fontencodingpane/Type3Font.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.debugger.fontencodingpane;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.swing.JPanel;
+
+import org.apache.fontbox.util.BoundingBox;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDType3CharProc;
+import org.apache.pdfbox.pdmodel.font.PDType3Font;
+import org.apache.pdfbox.rendering.PDFRenderer;
+import org.apache.pdfbox.util.Charsets;
+import org.apache.pdfbox.util.Matrix;
+
+/**
+ * @author Khyrul Bashar
+ * @author Tilman Hausherr
+ *
+ * A class that shows the glyph table along with unicode characters for PDType3Font.
+ */
+class Type3Font extends FontPane
+{
+ public static final String NO_GLYPH = "No glyph";
+ private final FontEncodingView view;
+ private int totalAvailableGlyph = 0;
+ private PDRectangle fontBBox;
+ private final PDResources resources;
+
+ /**
+ * Constructor.
+ * @param font PDSimpleFont instance.
+ * @throws IOException If fails to parse unicode characters.
+ */
+ Type3Font(PDType3Font font, PDResources resources) throws IOException
+ {
+ this.resources = resources;
+
+ calcBBox(font);
+
+ Object[][] tableData = getGlyphs(font);
+
+ Map attributes = new LinkedHashMap();
+ attributes.put("Font", font.getName());
+ attributes.put("Encoding", getEncodingName(font));
+ attributes.put("Glyphs", Integer.toString(totalAvailableGlyph));
+
+ view = new FontEncodingView(tableData, attributes,
+ new String[] {"Code", "Glyph Name", "Unicode Character", "Glyph"}, null);
+ }
+
+ private void calcBBox(PDType3Font font) throws IOException
+ {
+ double minX = 0;
+ double maxX = 0;
+ double minY = 0;
+ double maxY = 0;
+ for (int index = 0; index <= 255; ++index)
+ {
+ PDType3CharProc charProc = font.getCharProc(index);
+ if (charProc == null)
+ {
+ continue;
+ }
+ PDRectangle glyphBBox = charProc.getGlyphBBox();
+ if (glyphBBox == null)
+ {
+ continue;
+ }
+ minX = Math.min(minX, glyphBBox.getLowerLeftX());
+ maxX = Math.max(maxX, glyphBBox.getUpperRightX());
+ minY = Math.min(minY, glyphBBox.getLowerLeftY());
+ maxY = Math.max(maxY, glyphBBox.getUpperRightY());
+ }
+ fontBBox = new PDRectangle((float) minX, (float) minY, (float) (maxX - minX), (float) (maxY - minY));
+ if (fontBBox.getWidth() <= 0 || fontBBox.getHeight() <= 0)
+ {
+ // less reliable, but good as a fallback solution for PDF.js issue 10717
+ BoundingBox boundingBox = font.getBoundingBox();
+ fontBBox = new PDRectangle(boundingBox.getLowerLeftX(),
+ boundingBox.getLowerLeftY(),
+ boundingBox.getWidth(),
+ boundingBox.getHeight());
+ }
+ }
+
+ private Object[][] getGlyphs(PDType3Font font) throws IOException
+ {
+ boolean isEmpty = fontBBox.toGeneralPath().getBounds2D().isEmpty();
+ Object[][] glyphs = new Object[256][4];
+
+ // map needed to lessen memory footprint for files with duplicates
+ // e.g. PDF.js issue 10717
+ Map map = new HashMap();
+
+ for (int index = 0; index <= 255; index++)
+ {
+ glyphs[index][0] = index;
+ if (font.getEncoding().contains(index) || font.toUnicode(index) != null)
+ {
+ String name = font.getEncoding().getName(index);
+ glyphs[index][1] = name;
+ glyphs[index][2] = font.toUnicode(index);
+ if (isEmpty)
+ {
+ glyphs[index][3] = NO_GLYPH;
+ }
+ else if (map.containsKey(name))
+ {
+ glyphs[index][3] = map.get(name);
+ }
+ else
+ {
+ BufferedImage image = renderType3Glyph(font, index);
+ map.put(name, image);
+ glyphs[index][3] = image;
+ }
+ totalAvailableGlyph++;
+ }
+ else
+ {
+ glyphs[index][1] = NO_GLYPH;
+ glyphs[index][2] = NO_GLYPH;
+ glyphs[index][3] = NO_GLYPH;
+ }
+ }
+ return glyphs;
+ }
+
+ // Kindof an overkill to create a PDF for one glyph, but there is no better way at this time.
+ // Isn't called if no bounds are available
+ private BufferedImage renderType3Glyph(PDType3Font font, int index) throws IOException
+ {
+ PDDocument doc = new PDDocument();
+ int scale = 1;
+ if (fontBBox.getWidth() < 72 || fontBBox.getHeight() < 72)
+ {
+ // e.g. T4 font of PDFBOX-2959
+ scale = (int) (72 / Math.min(fontBBox.getWidth(), fontBBox.getHeight()));
+ }
+ PDPage page = new PDPage(new PDRectangle(fontBBox.getWidth() * scale, fontBBox.getHeight() * scale));
+ page.setResources(resources);
+
+ PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, false);
+ try
+ {
+ // any changes here must be done carefully and each file must be tested again
+ // just inverting didn't work with
+ // https://www.treasury.gov/ofac/downloads/sdnlist.pdf (has rotated matrix)
+ // also PDFBOX-4228-type3.pdf (identity matrix)
+ // Root/Pages/Kids/[0]/Resources/XObject/X1/Resources/XObject/X3/Resources/Font/F10
+ // PDFBOX-1794-vattenfall.pdf (scale 0.001)
+ float scalingFactorX = font.getFontMatrix().getScalingFactorX();
+ float scalingFactorY = font.getFontMatrix().getScalingFactorY();
+ float translateX = scalingFactorX > 0 ? -fontBBox.getLowerLeftX() : fontBBox.getUpperRightX();
+ float translateY = scalingFactorY > 0 ? -fontBBox.getLowerLeftY() : fontBBox.getUpperRightY();
+ cs.transform(Matrix.getTranslateInstance(translateX * scale, translateY * scale));
+ cs.beginText();
+ cs.setFont(font, scale / Math.min(Math.abs(scalingFactorX), Math.abs(scalingFactorY)));
+ // can't use showText() because there's no guarantee we have the unicode
+ cs.appendRawCommands(String.format("<%02X> Tj%n", index).getBytes(Charsets.ISO_8859_1));
+ cs.endText();
+ cs.close();
+ doc.addPage(page);
+ return new PDFRenderer(doc).renderImage(0);
+ }
+ finally
+ {
+ doc.close();
+ }
+ }
+
+ private String getEncodingName(PDType3Font font)
+ {
+ return font.getEncoding().getClass().getSimpleName() + " / " + font.getEncoding().getEncodingName();
+ }
+
+ @Override
+ public JPanel getPanel()
+ {
+ return view.getPanel();
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/ASCIIPane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/ASCIIPane.java
index ce95f1bc616..ced49920f2e 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/ASCIIPane.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/ASCIIPane.java
@@ -30,6 +30,7 @@
* This class shows corresponding ASCII characters for bytes. For every 16 byte there is one line.
* This paints the only visible contents at one time.
*/
+@SuppressWarnings({"serial","squid:S1948"})
class ASCIIPane extends JComponent implements HexModelChangeListener
{
private final HexModel model;
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexEditor.java b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexEditor.java
index f5fe52f6dcc..b054e32f09a 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexEditor.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexEditor.java
@@ -43,8 +43,9 @@
/**
* @author Khyrul Bashar
*
- * This class hosts all the UI components of Hex view and cordinate among them.
+ * This class hosts all the UI components of Hex view and coordinates among them.
*/
+@SuppressWarnings({"serial","squid:S1948"})
class HexEditor extends JPanel implements SelectionChangeListener
{
private final HexModel model;
@@ -135,6 +136,7 @@ private JScrollPane getScrollPane()
@Override
public void actionPerformed(ActionEvent actionEvent)
{
+ // do nothing
}
};
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexModel.java b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexModel.java
index bb770c78161..677f5e90ff4 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexModel.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexModel.java
@@ -23,7 +23,7 @@
/**
* @author Khyrul Bashar
*
- * A class that acts as a model for the hex viewer. It holds the data and provide the data as ncessary.
+ * A class that acts as a model for the hex viewer. It holds the data and provide the data as necessary.
* It'll let listen for any underlying data changes.
*/
class HexModel implements HexChangeListener
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexPane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexPane.java
index af8fd4d0625..ecdd98f79af 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexPane.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/hexviewer/HexPane.java
@@ -40,6 +40,7 @@
* HexPane shows the byte in a Grid table where every row has 16 bytes. It only draws bytes those are
* only visible at a given time.
*/
+@SuppressWarnings("squid:S1948")
class HexPane extends JPanel implements KeyListener, MouseListener, MouseMotionListener, HexModelChangeListener
{
private final HexModel model;
@@ -170,15 +171,15 @@ private void setDefault(Graphics g)
*/
private int getIndexForPoint(Point point)
{
- if (point.x <= 20 || point.x >= (16 * HexView.CHAR_WIDTH)+20 )
+ if (point.x <= 20 || point.x >= (16 * HexView.CHAR_WIDTH) + 20)
{
return -1;
}
int y = point.y;
- int lineNumber = (y+ (HexView.CHAR_HEIGHT -(y % HexView.CHAR_HEIGHT)))/ HexView.CHAR_HEIGHT;
+ int lineNumber = (y + (HexView.CHAR_HEIGHT - (y % HexView.CHAR_HEIGHT))) / HexView.CHAR_HEIGHT;
int x = point.x - 20;
- int elementNumber = (x / HexView.CHAR_WIDTH);
- return (lineNumber-1) * 16 + elementNumber;
+ int elementNumber = x / HexView.CHAR_WIDTH;
+ return (lineNumber - 1) * 16 + elementNumber;
}
/**
@@ -306,7 +307,7 @@ public void keyPressed(KeyEvent keyEvent)
@Override
public void keyReleased(KeyEvent keyEvent)
{
-
+ // do nothing
}
@Override
@@ -324,36 +325,37 @@ public void mouseClicked(MouseEvent mouseEvent)
@Override
public void mousePressed(MouseEvent mouseEvent)
{
-
+ // do nothing
}
@Override
public void mouseReleased(MouseEvent mouseEvent)
{
+ // do nothing
}
@Override
public void mouseEntered(MouseEvent mouseEvent)
{
-
+ // do nothing
}
@Override
public void mouseExited(MouseEvent mouseEvent)
{
-
+ // do nothing
}
@Override
public void mouseDragged(MouseEvent mouseEvent)
{
-
+ // do nothing
}
@Override
public void mouseMoved(MouseEvent mouseEvent)
{
-
+ // do nothing
}
private static boolean isHexChar(char c)
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java
index 40b30b9930d..650760785b7 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/pagepane/PagePane.java
@@ -18,19 +18,33 @@
import java.awt.Color;
import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Desktop;
import java.awt.Font;
+import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.swing.BoxLayout;
-import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.debugger.ui.ImageUtil;
import org.apache.pdfbox.debugger.ui.RotationMenu;
@@ -38,6 +52,25 @@
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.PDFRenderer;
+import org.apache.pdfbox.debugger.PDFDebugger;
+import org.apache.pdfbox.debugger.ui.ErrorDialog;
+import org.apache.pdfbox.debugger.ui.HighResolutionImageIcon;
+import org.apache.pdfbox.debugger.ui.ImageTypeMenu;
+import org.apache.pdfbox.debugger.ui.RenderDestinationMenu;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.fixup.AcroFormDefaultFixup;
+import org.apache.pdfbox.pdmodel.fixup.PDDocumentFixup;
+import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
+import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
+import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
/**
* Display the page number and a page rendering.
@@ -45,21 +78,122 @@
* @author Tilman Hausherr
* @author John Hewson
*/
-public class PagePane implements ActionListener, AncestorListener
+public class PagePane implements ActionListener, AncestorListener, MouseMotionListener, MouseListener
{
+ private static final Log LOG = LogFactory.getLog(PagePane.class);
private JPanel panel;
private int pageIndex = -1;
private final PDDocument document;
private JLabel label;
private ZoomMenu zoomMenu;
private RotationMenu rotationMenu;
+ private ImageTypeMenu imageTypeMenu;
+ private RenderDestinationMenu renderDestinationMenu;
+ private final JLabel statuslabel;
+ private final PDPage page;
+ private String labelText = "";
+ private String currentURI = "";
+ private final Map rectMap = new HashMap();
+ private final AffineTransform defaultTransform = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform();
- public PagePane(PDDocument document, COSDictionary page)
+ public PagePane(PDDocument document, COSDictionary pageDict, JLabel statuslabel)
{
- PDPage pdPage = new PDPage(page);
- pageIndex = document.getPages().indexOf(pdPage);
+ page = new PDPage(pageDict);
+ pageIndex = document.getPages().indexOf(page);
this.document = document;
+ this.statuslabel = statuslabel;
initUI();
+ initRectMap();
+ }
+
+ private void initRectMap()
+ {
+ try
+ {
+ collectFieldLocations();
+ collectLinkLocations();
+ }
+ catch (IOException ex)
+ {
+ LOG.error(ex.getMessage(), ex);
+ }
+ }
+
+ private void collectLinkLocations() throws IOException
+ {
+ for (PDAnnotation annotation : page.getAnnotations())
+ {
+ if (annotation instanceof PDAnnotationLink)
+ {
+ collectLinkLocation((PDAnnotationLink) annotation);
+ }
+ }
+ }
+
+ private void collectLinkLocation(PDAnnotationLink linkAnnotation) throws IOException
+ {
+ PDAction action = linkAnnotation.getAction();
+ if (action instanceof PDActionURI)
+ {
+ PDActionURI uriAction = (PDActionURI) action;
+ rectMap.put(linkAnnotation.getRectangle(), "URI: " + uriAction.getURI());
+ return;
+ }
+ PDDestination destination;
+ if (action instanceof PDActionGoTo)
+ {
+ PDActionGoTo goToAction = (PDActionGoTo) action;
+ destination = goToAction.getDestination();
+ }
+ else
+ {
+ destination = linkAnnotation.getDestination();
+ }
+ if (destination instanceof PDNamedDestination)
+ {
+ destination = document.getDocumentCatalog().
+ findNamedDestinationPage((PDNamedDestination) destination);
+ }
+ if (destination instanceof PDPageDestination)
+ {
+ PDPageDestination pageDestination = (PDPageDestination) destination;
+ int pageNum = pageDestination.retrievePageNumber();
+ if (pageNum != -1)
+ {
+ rectMap.put(linkAnnotation.getRectangle(), "Page destination: " + (pageNum + 1));
+ }
+ }
+ }
+
+ private void collectFieldLocations() throws IOException
+ {
+ // get Acroform without applying fixups to enure that we get the original content
+ boolean repairSelected = PDFDebugger.repairAcroFormMenuItem.isSelected();
+ PDDocumentFixup fixup = repairSelected ? new AcroFormDefaultFixup(document) : null;
+ PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(fixup);
+
+ if (acroForm == null)
+ {
+ return;
+ }
+ Set dictionarySet = new HashSet();
+ for (PDAnnotation annotation : page.getAnnotations())
+ {
+ dictionarySet.add(annotation.getCOSObject());
+ }
+ for (PDField field : acroForm.getFieldTree())
+ {
+ for (PDAnnotationWidget widget : field.getWidgets())
+ {
+ // check if the annotation widget is on this page
+ // (checking widget.getPage() also works, but it is sometimes null)
+ if (dictionarySet.contains(widget.getCOSObject()))
+ {
+ rectMap.put(widget.getRectangle(), "Field name: " + field.getFullyQualifiedName());
+ }
+ }
+ }
}
private void initUI()
@@ -68,7 +202,14 @@ private void initUI()
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
String pageLabelText = pageIndex < 0 ? "Page number not found" : "Page " + (pageIndex + 1);
-
+
+ // append PDF page label, if available
+ String lbl = PDFDebugger.getPageLabel(document, pageIndex);
+ if (lbl != null)
+ {
+ pageLabelText += " - " + lbl;
+ }
+
JLabel pageLabel = new JLabel(pageLabelText);
pageLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
pageLabel.setFont(new Font(Font.MONOSPACED, Font.BOLD, 30));
@@ -76,14 +217,16 @@ private void initUI()
panel.add(pageLabel);
label = new JLabel();
+ label.addMouseMotionListener(this);
+ label.addMouseListener(this);
label.setBackground(panel.getBackground());
label.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(label);
panel.addAncestorListener(this);
- // render in a background thread: rendering is read-only, so this should be ok, despite
- // the fact that PDDocument is not officially thread safe
- new RenderWorker(1, 0).execute();
+ zoomMenu = ZoomMenu.getInstance();
+ zoomMenu.changeZoomSelection(zoomMenu.getPageZoomScale());
+ startRendering();
}
/**
@@ -100,24 +243,55 @@ public Component getPanel()
public void actionPerformed(ActionEvent actionEvent)
{
String actionCommand = actionEvent.getActionCommand();
- if (ZoomMenu.isZoomMenu(actionCommand) || RotationMenu.isRotationMenu(actionCommand))
+ if (actionEvent.getSource() == PDFDebugger.repairAcroFormMenuItem)
+ {
+ boolean repairSelected = PDFDebugger.repairAcroFormMenuItem.isSelected();
+ PDDocumentFixup fixup = repairSelected ? new AcroFormDefaultFixup(document) : null;
+ document.getDocumentCatalog().getAcroForm(fixup);
+ startRendering();
+ }
+ else if (ZoomMenu.isZoomMenu(actionCommand) ||
+ RotationMenu.isRotationMenu(actionCommand) ||
+ ImageTypeMenu.isImageTypeMenu(actionCommand) ||
+ RenderDestinationMenu.isRenderDestinationMenu(actionCommand) ||
+ actionEvent.getSource() == PDFDebugger.allowSubsampling)
{
- new RenderWorker(ZoomMenu.getZoomScale(), RotationMenu.getRotationDegrees()).execute();
+ startRendering();
+ zoomMenu.setPageZoomScale(ZoomMenu.getZoomScale());
}
}
+ private void startRendering()
+ {
+ // render in a background thread: rendering is read-only, so this should be ok, despite
+ // the fact that PDDocument is not officially thread safe
+ new RenderWorker().execute();
+ zoomMenu.setPageZoomScale(ZoomMenu.getZoomScale());
+ }
+
@Override
public void ancestorAdded(AncestorEvent ancestorEvent)
{
- zoomMenu = ZoomMenu.getInstance();
zoomMenu.addMenuListeners(this);
- zoomMenu.setZoomSelection(ZoomMenu.ZOOM_100_PERCENT);
zoomMenu.setEnableMenu(true);
rotationMenu = RotationMenu.getInstance();
rotationMenu.addMenuListeners(this);
- rotationMenu.setRotationSelection(RotationMenu.ROTATE_0_DEGREES);
rotationMenu.setEnableMenu(true);
+
+ imageTypeMenu = ImageTypeMenu.getInstance();
+ imageTypeMenu.addMenuListeners(this);
+ imageTypeMenu.setEnableMenu(true);
+
+ renderDestinationMenu = RenderDestinationMenu.getInstance();
+ renderDestinationMenu.addMenuListeners(this);
+ renderDestinationMenu.setEnableMenu(true);
+
+ PDFDebugger.allowSubsampling.setEnabled(true);
+ PDFDebugger.allowSubsampling.addActionListener(this);
+
+ PDFDebugger.repairAcroFormMenuItem.setEnabled(true);
+ PDFDebugger.repairAcroFormMenuItem.addActionListener(this);
}
@Override
@@ -125,34 +299,157 @@ public void ancestorRemoved(AncestorEvent ancestorEvent)
{
zoomMenu.setEnableMenu(false);
rotationMenu.setEnableMenu(false);
+ imageTypeMenu.setEnableMenu(false);
+ renderDestinationMenu.setEnableMenu(false);
+
+ PDFDebugger.allowSubsampling.setEnabled(false);
+ PDFDebugger.allowSubsampling.removeActionListener(this);
}
@Override
public void ancestorMoved(AncestorEvent ancestorEvent)
{
+ // do nothing
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent e)
+ {
+ // do nothing
}
/**
- * Note that PDDocument is not officially thread safe, caution advised.
+ * Catch mouse event to display cursor position in PDF coordinates in the status bar.
+ *
+ * @param e mouse event with position
*/
- private final class RenderWorker extends SwingWorker
+ @Override
+ public void mouseMoved(MouseEvent e)
{
- private final float scale;
- private final int rotation;
+ PDRectangle cropBox = page.getCropBox();
+ float height = cropBox.getHeight();
+ float width = cropBox.getWidth();
+ float offsetX = cropBox.getLowerLeftX();
+ float offsetY = cropBox.getLowerLeftY();
+ float zoomScale = zoomMenu.getPageZoomScale();
+ float x = e.getX() / zoomScale * (float) defaultTransform.getScaleX();
+ float y = e.getY() / zoomScale * (float) defaultTransform.getScaleY();
+ int x1;
+ int y1;
+ switch ((RotationMenu.getRotationDegrees() + page.getRotation()) % 360)
+ {
+ case 90:
+ x1 = (int) (y + offsetX);
+ y1 = (int) (x + offsetY);
+ break;
+ case 180:
+ x1 = (int) (width - x + offsetX);
+ y1 = (int) (y - offsetY);
+ break;
+ case 270:
+ x1 = (int) (width - y + offsetX);
+ y1 = (int) (height - x + offsetY);
+ break;
+ case 0:
+ default:
+ x1 = (int) (x + offsetX);
+ y1 = (int) (height - y + offsetY);
+ break;
+ }
+ String text = "x: " + x1 + ", y: " + y1;
- private RenderWorker(float scale, int rotation)
+ // are we in a field widget or a link annotation?
+ Cursor cursor = Cursor.getDefaultCursor();
+ currentURI = "";
+ for (Map.Entry entry : rectMap.entrySet())
{
- this.scale = scale;
- this.rotation = rotation;
+ if (entry.getKey().contains(x1, y1))
+ {
+ String s = rectMap.get(entry.getKey());
+ text += ", " + s;
+ if (s.startsWith("URI: "))
+ {
+ currentURI = s.substring(5);
+ cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+ }
+ break;
+ }
}
+ panel.setCursor(cursor);
+
+ statuslabel.setText(text);
+ }
+ @Override
+ public void mouseClicked(MouseEvent e)
+ {
+ if (!currentURI.isEmpty() &&
+ Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE))
+ {
+ try
+ {
+ Desktop.getDesktop().browse(new URI(currentURI));
+ }
+ catch (URISyntaxException ex)
+ {
+ new ErrorDialog(ex).setVisible(true);
+ }
+ catch (IOException ex)
+ {
+ new ErrorDialog(ex).setVisible(true);
+ }
+ }
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ // do nothing
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e)
+ {
+ // do nothing
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e)
+ {
+ // do nothing
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e)
+ {
+ statuslabel.setText(labelText);
+ }
+
+ /**
+ * Note that PDDocument is not officially thread safe, caution advised.
+ */
+ private final class RenderWorker extends SwingWorker
+ {
@Override
protected BufferedImage doInBackground() throws IOException
{
+ // rendering can take a long time, so remember all options that are used later
+ float scale = ZoomMenu.getZoomScale();
+ int rotation = RotationMenu.getRotationDegrees();
+
label.setIcon(null);
- label.setText("Loading...");
+ labelText = "Rendering...";
+ label.setText(labelText);
+
PDFRenderer renderer = new PDFRenderer(document);
- BufferedImage bim = renderer.renderImage(pageIndex, scale);
+ renderer.setSubsamplingAllowed(PDFDebugger.allowSubsampling.isSelected());
+
+ long t0 = System.currentTimeMillis();
+ statuslabel.setText(labelText);
+ BufferedImage bim = renderer.renderImage(pageIndex, scale, ImageTypeMenu.getImageType(), RenderDestinationMenu.getRenderDestination());
+ float t = (System.currentTimeMillis() - t0) / 1000f;
+ labelText = "Rendered in " + t + " second" + (t > 1 ? "s" : "");
+ statuslabel.setText(labelText);
return ImageUtil.getRotatedImage(bim, rotation);
}
@@ -161,7 +458,16 @@ protected void done()
{
try
{
- label.setIcon(new ImageIcon(get()));
+ BufferedImage image = get();
+
+ // We cannot use "label.setIcon(new ImageIcon(get()))" here
+ // because of blurry upscaling in JDK9. Instead, the label is now created with
+ // a smaller size than the image to compensate that the
+ // image is scaled up with some screen configurations (e.g. 125% on windows).
+ // See PDFBOX-3665 for more sample code and discussion.
+ label.setSize((int) Math.ceil(image.getWidth() / defaultTransform.getScaleX()),
+ (int) Math.ceil(image.getHeight() / defaultTransform.getScaleY()));
+ label.setIcon(new HighResolutionImageIcon(image, label.getWidth(), label.getHeight()));
label.setText(null);
}
catch (InterruptedException e)
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/OperatorMarker.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/OperatorMarker.java
index 3bbf8991a36..b6546220dd3 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/OperatorMarker.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/OperatorMarker.java
@@ -24,19 +24,29 @@
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
+import org.apache.pdfbox.contentstream.operator.OperatorName;
+
/**
* @author Khyrul Bashar
*/
final class OperatorMarker
{
- public static final String BEGIN_TEXT_OBJECT = "BT";
- public static final String END_TEXT_OBJECT = "ET";
- public static final String SAVE_GRAPHICS_STATE = "q";
- public static final String RESTORE_GRAPHICS_STATE = "Q";
- public static final String CONCAT = "cm";
- public static final String INLINE_IMAGE_BEGIN = "BI";
- public static final String IMAGE_DATA = "ID";
- public static final String INLINE_IMAGE_END = "EI";
+ @Deprecated
+ public static final String BEGIN_TEXT_OBJECT = OperatorName.BEGIN_TEXT;
+ @Deprecated
+ public static final String END_TEXT_OBJECT = OperatorName.END_TEXT;
+ @Deprecated
+ public static final String SAVE_GRAPHICS_STATE = OperatorName.SAVE;
+ @Deprecated
+ public static final String RESTORE_GRAPHICS_STATE = OperatorName.RESTORE;
+ @Deprecated
+ public static final String CONCAT = OperatorName.CONCAT;
+ @Deprecated
+ public static final String INLINE_IMAGE_BEGIN = OperatorName.BEGIN_INLINE_IMAGE;
+ @Deprecated
+ public static final String IMAGE_DATA = OperatorName.BEGIN_INLINE_IMAGE_DATA;
+ @Deprecated
+ public static final String INLINE_IMAGE_END = OperatorName.END_INLINE_IMAGE;
private static final Map operatorStyleMap;
@@ -64,14 +74,14 @@ final class OperatorMarker
Map styleMap = new HashMap();
- styleMap.put(BEGIN_TEXT_OBJECT, textObjectStyle);
- styleMap.put(END_TEXT_OBJECT, textObjectStyle);
- styleMap.put(SAVE_GRAPHICS_STATE, graphicsStyle);
- styleMap.put(RESTORE_GRAPHICS_STATE, graphicsStyle);
- styleMap.put(CONCAT, concatStyle);
- styleMap.put(INLINE_IMAGE_BEGIN, inlineImage);
- styleMap.put(IMAGE_DATA, imageData);
- styleMap.put(INLINE_IMAGE_END, inlineImage);
+ styleMap.put(OperatorName.BEGIN_TEXT, textObjectStyle);
+ styleMap.put(OperatorName.END_TEXT, textObjectStyle);
+ styleMap.put(OperatorName.SAVE, graphicsStyle);
+ styleMap.put(OperatorName.RESTORE, graphicsStyle);
+ styleMap.put(OperatorName.CONCAT, concatStyle);
+ styleMap.put(OperatorName.BEGIN_INLINE_IMAGE, inlineImage);
+ styleMap.put(OperatorName.BEGIN_INLINE_IMAGE_DATA, imageData);
+ styleMap.put(OperatorName.END_INLINE_IMAGE, inlineImage);
operatorStyleMap = styleMap;
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/Stream.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/Stream.java
index c6a295a21f7..e234c53cc1f 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/Stream.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/Stream.java
@@ -23,6 +23,8 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
@@ -35,16 +37,20 @@
/**
* @author Khyrul Bashar
*
- * A class that provides the COSStream in different version and related informations.
+ * A class that provides the COSStream in various views and related information.
*/
public class Stream
{
+ private static final Log LOG = LogFactory.getLog(Stream.class);
+
public static final String UNFILTERED = "Unfiltered";
public static final String IMAGE = "Image";
- private final COSStream stream;
+ private final COSStream strm;
private final boolean isThumb;
private final boolean isImage;
+ private final boolean isXmlMetadata;
+
private final Map> filters;
/**
@@ -55,9 +61,10 @@ public class Stream
*/
Stream(COSStream cosStream, boolean isThumb)
{
- this.stream = cosStream;
+ this.strm = cosStream;
this.isThumb = isThumb;
this.isImage = isImageStream(cosStream, isThumb);
+ this.isXmlMetadata = isXmlMetadataStream(cosStream);
filters = createFilterList(cosStream);
}
@@ -71,6 +78,16 @@ public boolean isImage()
{
return isImage;
}
+
+ /**
+ * Return if this is stream is an Metadata stream.
+ *
+ * @return true if this a metadata stream and false otherwise.
+ */
+ public boolean isXmlMetadata()
+ {
+ return isXmlMetadata;
+ }
/**
* Return the available filter list. Only "Unfiltered" is returned if there is no filter and in
@@ -80,12 +97,7 @@ public boolean isImage()
*/
public List getFilterList()
{
- List list = new ArrayList();
- for (Map.Entry> entry : filters.entrySet())
- {
- list.add(entry.getKey());
- }
- return list;
+ return new ArrayList(filters.keySet());
}
/**
@@ -94,7 +106,7 @@ public List getFilterList()
private String getFilteredLabel()
{
StringBuilder sb = new StringBuilder();
- COSBase base = stream.getFilters();
+ COSBase base = strm.getFilters();
if (base instanceof COSName)
{
sb.append(((COSName) base).getName());
@@ -126,20 +138,20 @@ public InputStream getStream(String key)
{
if (UNFILTERED.equals(key))
{
- return stream.createInputStream();
+ return strm.createInputStream();
}
else if (getFilteredLabel().equals(key))
{
- return stream.createRawInputStream();
+ return strm.createRawInputStream();
}
else
{
- return new PDStream(stream).createInputStream(filters.get(key));
+ return new PDStream(strm).createInputStream(filters.get(key));
}
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
return null;
}
@@ -157,17 +169,17 @@ public BufferedImage getImage(PDResources resources)
PDImageXObject imageXObject;
if (isThumb)
{
- imageXObject = PDImageXObject.createThumbnail(stream);
+ imageXObject = PDImageXObject.createThumbnail(strm);
}
else
{
- imageXObject = new PDImageXObject(new PDStream(stream), resources);
+ imageXObject = new PDImageXObject(new PDStream(strm), resources);
}
return imageXObject.getImage();
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
return null;
}
@@ -199,12 +211,12 @@ private Map> createFilterList(COSStream stream)
private String getPartialStreamCommand(final int indexOfStopFilter)
{
- List avaiablrFilters = new PDStream(stream).getFilters();
+ List availableFilters = new PDStream(strm).getFilters();
StringBuilder nameListBuilder = new StringBuilder();
- for (int i = indexOfStopFilter; i < avaiablrFilters.size(); i++)
+ for (int i = indexOfStopFilter; i < availableFilters.size(); i++)
{
- nameListBuilder.append(avaiablrFilters.get(i).getName()).append(" & ");
+ nameListBuilder.append(availableFilters.get(i).getName()).append(" & ");
}
nameListBuilder.delete(nameListBuilder.lastIndexOf("&"), nameListBuilder.length());
@@ -213,10 +225,10 @@ private String getPartialStreamCommand(final int indexOfStopFilter)
private List getStopFilterList(final int stopFilterIndex)
{
- List avaiablrFilters = new PDStream(stream).getFilters();
+ List availableFilters = new PDStream(strm).getFilters();
final List stopFilters = new ArrayList(1);
- stopFilters.add(avaiablrFilters.get(stopFilterIndex).getName());
+ stopFilters.add(availableFilters.get(stopFilterIndex).getName());
return stopFilters;
}
@@ -229,4 +241,9 @@ private boolean isImageStream(COSDictionary dic, boolean isThumb)
}
return dic.containsKey(COSName.SUBTYPE) && dic.getCOSName(COSName.SUBTYPE).equals(COSName.IMAGE);
}
+
+ private boolean isXmlMetadataStream(COSDictionary dic)
+ {
+ return dic.containsKey(COSName.SUBTYPE) && dic.getCOSName(COSName.SUBTYPE).equals(COSName.getPDFName("XML"));
+ }
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamImageView.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamImageView.java
index 6accf5ffc56..a2d6c423a7c 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamImageView.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamImageView.java
@@ -19,13 +19,15 @@
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
+
import javax.swing.Box;
import javax.swing.BoxLayout;
-import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
@@ -34,6 +36,7 @@
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
+import org.apache.pdfbox.debugger.ui.HighResolutionImageIcon;
import org.apache.pdfbox.debugger.ui.ImageUtil;
import org.apache.pdfbox.debugger.ui.RotationMenu;
import org.apache.pdfbox.debugger.ui.ZoomMenu;
@@ -66,10 +69,13 @@ private void initUI()
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ zoomMenu = ZoomMenu.getInstance();
+ zoomMenu.changeZoomSelection(zoomMenu.getImageZoomScale());
+
label = new JLabel();
label.setBorder(new LineBorder(Color.BLACK));
label.setAlignmentX(Component.CENTER_ALIGNMENT);
- label.setIcon(new ImageIcon(image));
+ addImage(zoomImage(image, zoomMenu.getImageZoomScale(), RotationMenu.getRotationDegrees()));
panel.add(Box.createVerticalGlue());
panel.add(label);
@@ -95,7 +101,7 @@ private Image zoomImage(BufferedImage origin, float scale, int rotation)
BufferedImage rotatedImage = ImageUtil.getRotatedImage(origin, rotation);
int resizedWidth = (int) (rotatedImage.getWidth() * scale);
int resizedHeight = (int) (rotatedImage.getHeight() * scale);
- return rotatedImage.getScaledInstance(resizedWidth, resizedHeight, BufferedImage.SCALE_SMOOTH);
+ return rotatedImage.getScaledInstance(resizedWidth, resizedHeight, Image.SCALE_SMOOTH);
}
@Override
@@ -105,21 +111,25 @@ public void actionPerformed(ActionEvent actionEvent)
if (ZoomMenu.isZoomMenu(actionCommand) || RotationMenu.isRotationMenu(actionCommand))
{
addImage(zoomImage(image, ZoomMenu.getZoomScale(), RotationMenu.getRotationDegrees()));
+ zoomMenu.setImageZoomScale(ZoomMenu.getZoomScale());
}
}
private void addImage(Image img)
{
- label.setIcon(new ImageIcon(img));
+ // for JDK9; see explanation in PagePane
+ AffineTransform tx = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform();
+ label.setSize((int) Math.ceil(img.getWidth(null) / tx.getScaleX()),
+ (int) Math.ceil(img.getHeight(null) / tx.getScaleY()));
+ label.setIcon(new HighResolutionImageIcon(img, label.getWidth(), label.getHeight()));
label.revalidate();
}
@Override
public void ancestorAdded(AncestorEvent ancestorEvent)
{
- zoomMenu = ZoomMenu.getInstance();
zoomMenu.addMenuListeners(this);
- zoomMenu.setZoomSelection(ZoomMenu.ZOOM_100_PERCENT);
zoomMenu.setEnableMenu(true);
rotationMenu = RotationMenu.getInstance();
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamPane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamPane.java
index fa2c0cf1edf..22f0dd4b079 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamPane.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamPane.java
@@ -26,11 +26,12 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
+import java.util.Vector;
import java.util.concurrent.ExecutionException;
-import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JComponent;
@@ -44,13 +45,25 @@
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
+import javax.xml.XMLConstants;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.contentstream.operator.Operator;
+import org.apache.pdfbox.contentstream.operator.OperatorName;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSBoolean;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSNull;
import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.cos.COSString;
@@ -60,6 +73,8 @@
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.util.Charsets;
+import org.apache.pdfbox.util.XMLUtil;
+import org.w3c.dom.Document;
/**
* @author Khyrul Bashar
@@ -68,16 +83,28 @@
*/
public class StreamPane implements ActionListener
{
- public static final String BEGIN_TEXT_OBJECT = "BT";
- public static final String END_TEXT_OBJECT = "ET";
- public static final String SAVE_GRAPHICS_STATE = "q";
- public static final String RESTORE_GRAPHICS_STATE = "Q";
- public static final String INLINE_IMAGE_BEGIN = "BI";
- public static final String IMAGE_DATA = "ID";
- public static final String INLINE_IMAGE_END = "EI";
- public static final String BEGIN_MARKED_CONTENT1 = "BMC";
- public static final String BEGIN_MARKED_CONTENT2 = "BDC";
- public static final String END_MARKED_CONTENT = "EMC";
+ private static final Log LOG = LogFactory.getLog(StreamPane.class);
+
+ @Deprecated
+ public static final String BEGIN_TEXT_OBJECT = OperatorName.BEGIN_TEXT;
+ @Deprecated
+ public static final String END_TEXT_OBJECT = OperatorName.END_TEXT;
+ @Deprecated
+ public static final String SAVE_GRAPHICS_STATE = OperatorName.SAVE;
+ @Deprecated
+ public static final String RESTORE_GRAPHICS_STATE = OperatorName.RESTORE;
+ @Deprecated
+ public static final String INLINE_IMAGE_BEGIN = OperatorName.BEGIN_INLINE_IMAGE;
+ @Deprecated
+ public static final String IMAGE_DATA = OperatorName.BEGIN_INLINE_IMAGE_DATA;
+ @Deprecated
+ public static final String INLINE_IMAGE_END = OperatorName.END_INLINE_IMAGE;
+ @Deprecated
+ public static final String BEGIN_MARKED_CONTENT1 = OperatorName.BEGIN_MARKED_CONTENT;
+ @Deprecated
+ public static final String BEGIN_MARKED_CONTENT2 = OperatorName.BEGIN_MARKED_CONTENT_SEQ;
+ @Deprecated
+ public static final String END_MARKED_CONTENT = OperatorName.END_MARKED_CONTENT;
private static final StyleContext CONTEXT = StyleContext.getDefaultStyleContext();
private static final Style OPERATOR_STYLE = CONTEXT.addStyle("operator", null);
@@ -100,11 +127,11 @@ public class StreamPane implements ActionListener
private final JPanel panel;
private final HexView hexView;
private final JTabbedPane tabbedPane;
- private final StreamPaneView view;
+ private final StreamPaneView rawView;
+ private final StreamPaneView niceView;
private final Stream stream;
private ToolTipController tTController;
private PDResources resources;
- private final boolean isContentStream;
/**
* Constructor.
@@ -118,8 +145,6 @@ public class StreamPane implements ActionListener
public StreamPane(COSStream cosStream, boolean isContentStream, boolean isThumb,
COSDictionary resourcesDic) throws IOException
{
- this.isContentStream = isContentStream;
-
this.stream = new Stream(cosStream, isThumb);
if (resourcesDic != null)
{
@@ -131,8 +156,16 @@ public StreamPane(COSStream cosStream, boolean isContentStream, boolean isThumb,
panel.setPreferredSize(new Dimension(300, 500));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- view = new StreamPaneView();
+ rawView = new StreamPaneView();
hexView = new HexView();
+ if (isContentStream || stream.isXmlMetadata())
+ {
+ niceView = new StreamPaneView();
+ }
+ else
+ {
+ niceView = null;
+ }
if (stream.isImage())
{
@@ -146,8 +179,21 @@ public StreamPane(COSStream cosStream, boolean isContentStream, boolean isThumb,
}
tabbedPane = new JTabbedPane();
- tabbedPane.add("Text view", view.getStreamPanel());
- tabbedPane.add("Hex view", hexView.getPane());
+ if (stream.isImage())
+ {
+ tabbedPane.add("Image view", rawView.getStreamPanel());
+ }
+ else if (niceView != null)
+ {
+ tabbedPane.add("Nice view", niceView.getStreamPanel());
+ tabbedPane.add("Raw view", rawView.getStreamPanel());
+ tabbedPane.add("Hex view", hexView.getPane());
+ }
+ else
+ {
+ tabbedPane.add("Text view", rawView.getStreamPanel());
+ tabbedPane.add("Hex view", hexView.getPane());
+ }
panel.add(tabbedPane);
}
@@ -159,7 +205,7 @@ public JComponent getPanel()
private JPanel createHeaderPanel(List availableFilters, String i, ActionListener actionListener)
{
- JComboBox filters = new JComboBox(availableFilters.toArray());
+ JComboBox filters = new JComboBox(new Vector(availableFilters));
filters.setSelectedItem(i);
filters.addActionListener(actionListener);
@@ -172,7 +218,7 @@ private JPanel createHeaderPanel(List availableFilters, String i, Action
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- if (actionEvent.getActionCommand().equals("comboBoxChanged"))
+ if ("comboBoxChanged".equals(actionEvent.getActionCommand()))
{
JComboBox comboBox = (JComboBox) actionEvent.getSource();
String currentFilter = (String) comboBox.getSelectedItem();
@@ -182,18 +228,32 @@ public void actionPerformed(ActionEvent actionEvent)
if (currentFilter.equals(Stream.IMAGE))
{
requestImageShowing();
+ tabbedPane.removeAll();
+ tabbedPane.add("Image view", rawView.getStreamPanel());
return;
}
+ tabbedPane.removeAll();
+ if (Stream.UNFILTERED.equals(currentFilter) && niceView != null)
+ {
+ tabbedPane.add("Nice view", niceView.getStreamPanel());
+ tabbedPane.add("Raw view", rawView.getStreamPanel());
+ tabbedPane.add("Hex view", hexView.getPane());
+ }
+ else
+ {
+ tabbedPane.add("Text view", rawView.getStreamPanel());
+ tabbedPane.add("Hex view", hexView.getPane());
+ }
requestStreamText(currentFilter);
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
}
- private void requestImageShowing() throws IOException
+ private void requestImageShowing()
{
if (stream.isImage())
{
@@ -207,20 +267,17 @@ private void requestImageShowing() throws IOException
JOptionPane.showMessageDialog(panel, "image not available (filter missing?)");
return;
}
- view.showStreamImage(image);
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ImageIO.write(image, "jpg", baos);
- baos.flush();
- byte[] bytes = baos.toByteArray();
- baos.close();
- hexView.changeData(bytes);
+ rawView.showStreamImage(image);
}
}
private void requestStreamText(String command) throws IOException
{
- new DocumentCreator(command).execute();
+ new DocumentCreator(rawView, command, false).execute();
+ if (niceView != null)
+ {
+ new DocumentCreator(niceView, command, true).execute();
+ }
synchronized (stream)
{
InputStream is = stream.getStream(command);
@@ -238,31 +295,45 @@ private void requestStreamText(String command) throws IOException
*/
private final class DocumentCreator extends SwingWorker
{
+ private final StreamPaneView targetView;
private final String filterKey;
+ private final boolean nice;
private int indent;
private boolean needIndent;
- private DocumentCreator(String filterKey)
+ private DocumentCreator(StreamPaneView targetView, String filterKey, boolean nice)
{
+ this.targetView = targetView;
this.filterKey = filterKey;
+ this.nice = nice;
}
@Override
protected StyledDocument doInBackground()
{
+ // default encoding to use when reading text base content
+ String encoding = "ISO-8859-1";
synchronized (stream)
{
+ if (stream.isXmlMetadata())
+ {
+ encoding = "UTF-8";
+ }
InputStream inputStream = stream.getStream(filterKey);
- if (isContentStream && Stream.UNFILTERED.equals(filterKey))
+ if (nice && Stream.UNFILTERED.equals(filterKey))
{
+ if (stream.isXmlMetadata())
+ {
+ return getXMLDocument(inputStream, encoding);
+ }
StyledDocument document = getContentStreamDocument(inputStream);
if (document != null)
{
return document;
}
- return getDocument(stream.getStream(filterKey));
+ return getDocument(inputStream, encoding);
}
- return getDocument(inputStream);
+ return getDocument(inputStream, encoding);
}
}
@@ -271,58 +342,102 @@ protected void done()
{
try
{
- view.showStreamText(get(), tTController);
+ targetView.showStreamText(get(), tTController);
}
catch (InterruptedException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
catch (ExecutionException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
- private String getStringOfStream(InputStream ioStream)
+ private String getStringOfStream(InputStream in, String encoding)
{
- ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int amountRead;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
try
{
- while ((amountRead = ioStream.read(buffer, 0, buffer.length)) != -1)
- {
- byteArray.write(buffer, 0, amountRead);
- }
+ IOUtils.copy(in, baos);
+ return baos.toString(encoding);
}
catch (IOException e)
{
- e.printStackTrace();
- }
- try
- {
- return byteArray.toString("ISO-8859-1");
- }
- catch (UnsupportedEncodingException e)
- {
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
return null;
}
}
- private StyledDocument getDocument(InputStream inputStream)
+ private StyledDocument getDocument(InputStream inputStream, String encoding)
{
StyledDocument docu = new DefaultStyledDocument();
if (inputStream != null)
{
- String data = getStringOfStream(inputStream);
+ String data = getStringOfStream(inputStream, encoding);
+
+ // CR is not displayed in the raw view (see file from PDFBOX-4964),
+ // but LF is displayed, so lets first replace CR LF with LF and then
+ // replace the remaining CRs with LF
+ if (data != null)
+ {
+ data = data.replace("\r\n", "\n").replace('\r', '\n');
+ }
+
try
{
docu.insertString(0, data, null);
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
+ }
+ }
+ return docu;
+ }
+
+ private StyledDocument getXMLDocument(InputStream inputStream, String encoding)
+ {
+ StyledDocument docu = new DefaultStyledDocument();
+ if (inputStream != null)
+ {
+ try
+ {
+ Document doc = XMLUtil.parse(inputStream);
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ // XMLConstants.ACCESS_EXTERNAL_DTD in jdk 1.7
+ transformerFactory.setAttribute("http://javax.xml.XMLConstants/property/accessExternalDTD", "");
+ // XMLConstants.ACCESS_EXTERNAL_STYLESHEET in jdk 1.7
+ transformerFactory.setAttribute("http://javax.xml.XMLConstants/property/accessExternalStylesheet", "");
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "1");
+ StringWriter sw = new StringWriter();
+ StreamResult result = new StreamResult(sw);
+ DOMSource source = new DOMSource(doc);
+ transformer.transform(source, result);
+ docu.insertString(0, sw.toString(), null);
+ }
+ catch (UnsupportedEncodingException ex)
+ {
+ LOG.error(ex.getMessage(), ex);
+ }
+ catch (TransformerConfigurationException ex)
+ {
+ LOG.error(ex.getMessage(), ex);
+ }
+ catch (TransformerException ex)
+ {
+ LOG.error(ex.getMessage(), ex);
+ }
+ catch (BadLocationException ex)
+ {
+ LOG.error(ex.getMessage(), ex);
+ }
+ catch (IOException ex)
+ {
+ LOG.error(ex.getMessage(), ex);
}
}
return docu;
@@ -365,7 +480,7 @@ private void writeToken(Object obj, StyledDocument docu)
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
@@ -414,7 +529,7 @@ else if (chr == '(' || chr == ')' || chr == '\n' || chr == '\r' ||
}
else
{
- String str = "" + (char)chr;
+ String str = Character.toString((char) chr);
docu.insertString(docu.getLength(), str, STRING_STYLE);
}
}
@@ -444,6 +559,10 @@ else if (obj instanceof COSDictionary)
}
docu.insertString(docu.getLength(), ">> ", null);
}
+ else if (obj instanceof COSNull)
+ {
+ docu.insertString(docu.getLength(), "null ", null);
+ }
else
{
String str = obj.toString();
@@ -456,17 +575,17 @@ private void addOperators(Object obj, StyledDocument docu) throws BadLocationExc
{
Operator op = (Operator) obj;
- if (op.getName().equals(END_TEXT_OBJECT)
- || op.getName().equals(RESTORE_GRAPHICS_STATE)
- || op.getName().equals(END_MARKED_CONTENT))
+ if (op.getName().equals(OperatorName.END_TEXT)
+ || op.getName().equals(OperatorName.RESTORE)
+ || op.getName().equals(OperatorName.END_MARKED_CONTENT))
{
indent--;
}
writeIndent(docu);
- if (op.getName().equals(INLINE_IMAGE_BEGIN))
+ if (op.getName().equals(OperatorName.BEGIN_INLINE_IMAGE))
{
- docu.insertString(docu.getLength(), INLINE_IMAGE_BEGIN + "\n", OPERATOR_STYLE);
+ docu.insertString(docu.getLength(), OperatorName.BEGIN_INLINE_IMAGE + "\n", OPERATOR_STYLE);
COSDictionary dic = op.getImageParameters();
for (COSName key : dic.keySet())
{
@@ -476,10 +595,10 @@ private void addOperators(Object obj, StyledDocument docu) throws BadLocationExc
docu.insertString(docu.getLength(), "\n", null);
}
String imageString = new String(op.getImageData(), Charsets.ISO_8859_1);
- docu.insertString(docu.getLength(), IMAGE_DATA + "\n", INLINE_IMAGE_STYLE);
+ docu.insertString(docu.getLength(), OperatorName.BEGIN_INLINE_IMAGE_DATA + "\n", INLINE_IMAGE_STYLE);
docu.insertString(docu.getLength(), imageString, null);
docu.insertString(docu.getLength(), "\n", null);
- docu.insertString(docu.getLength(), INLINE_IMAGE_END + "\n", OPERATOR_STYLE);
+ docu.insertString(docu.getLength(), OperatorName.END_INLINE_IMAGE + "\n", OPERATOR_STYLE);
}
else
{
@@ -487,10 +606,10 @@ private void addOperators(Object obj, StyledDocument docu) throws BadLocationExc
docu.insertString(docu.getLength(), operator + "\n", OPERATOR_STYLE);
// nested opening operators
- if (op.getName().equals(BEGIN_TEXT_OBJECT) ||
- op.getName().equals(SAVE_GRAPHICS_STATE) ||
- op.getName().equals(BEGIN_MARKED_CONTENT1) ||
- op.getName().equals(BEGIN_MARKED_CONTENT2))
+ if (op.getName().equals(OperatorName.BEGIN_TEXT) ||
+ op.getName().equals(OperatorName.SAVE) ||
+ op.getName().equals(OperatorName.BEGIN_MARKED_CONTENT) ||
+ op.getName().equals(OperatorName.BEGIN_MARKED_CONTENT_SEQ))
{
indent++;
}
@@ -510,4 +629,4 @@ void writeIndent(StyledDocument docu) throws BadLocationException
}
}
}
-}
+}
\ No newline at end of file
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamTextView.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamTextView.java
index b0fbda09276..25f35256bc4 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamTextView.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/StreamTextView.java
@@ -89,6 +89,7 @@ JComponent getView()
@Override
public void mouseDragged(MouseEvent mouseEvent)
{
+ // do nothing
}
@Override
@@ -126,5 +127,6 @@ public void ancestorRemoved(AncestorEvent ancestorEvent)
@Override
public void ancestorMoved(AncestorEvent ancestorEvent)
{
+ // do nothing
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/FontToolTip.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/FontToolTip.java
index 20c41a0f0ef..30bfd09b0b3 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/FontToolTip.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/FontToolTip.java
@@ -18,16 +18,19 @@
package org.apache.pdfbox.debugger.streampane.tooltip;
import java.io.IOException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.font.PDFont;
/**
* @author Khyrul Bashar
- * A class that provieds tooltip text for font. This shows the name of the font.
+ * A class that provides tooltip text for font. This shows the name of the font.
*/
final class FontToolTip implements ToolTip
{
+ private static final Log LOG = LogFactory.getLog(FontToolTip.class);
private String markup;
/**
@@ -54,7 +57,7 @@ private void initUI(String fontReferenceName, PDResources resources)
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/KToolTip.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/KToolTip.java
index ced245938b8..1ffdab4a886 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/KToolTip.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/KToolTip.java
@@ -23,6 +23,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
/**
@@ -30,8 +32,9 @@
* A class that provide tooltip for K and k.
*/
final class KToolTip extends ColorToolTip
-
{
+ private static final Log LOG = LogFactory.getLog(KToolTip.class);
+
/**
* Constructor.
* @param rowText String instance.
@@ -53,7 +56,7 @@ private void createMarkUp(String rowText)
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
}
@@ -77,9 +80,9 @@ ICC_Profile getICCProfile() throws IOException
// Instead, the "ISO Coated v2 300% (basICColor)" is used, which
// is an open alternative to the "ISO Coated v2 300% (ECI)" profile.
- String name = "org/apache/pdfbox/resources/icc/ISOcoated_v2_300_bas.icc";
+ String name = "/org/apache/pdfbox/resources/icc/ISOcoated_v2_300_bas.icc";
- URL url = PDDeviceCMYK.class.getClassLoader().getResource(name);
+ URL url = PDDeviceCMYK.class.getResource(name);
if (url == null)
{
throw new IOException("Error loading resource: " + name);
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/SCNToolTip.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/SCNToolTip.java
index 96566444fc0..c862c319c9c 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/SCNToolTip.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/SCNToolTip.java
@@ -19,9 +19,12 @@
import java.awt.Color;
import java.io.IOException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.apache.pdfbox.pdmodel.graphics.color.PDPattern;
/**
* @author Khyrul Bashar
@@ -29,6 +32,8 @@
*/
final class SCNToolTip extends ColorToolTip
{
+ private static final Log LOG = LogFactory.getLog(SCNToolTip.class);
+
/**
* Constructor.
* @param rowText String instance.
@@ -47,7 +52,12 @@ private void createMarkUp(PDResources resources, String colorSpaceName, String r
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
+ }
+ if (colorSpace instanceof PDPattern)
+ {
+ setToolTipText("Pattern");
+ return;
}
if (colorSpace != null)
{
@@ -62,7 +72,7 @@ private void createMarkUp(PDResources resources, String colorSpaceName, String r
}
catch (IOException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/ToolTipController.java b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/ToolTipController.java
index 8ac91aecf65..18f1b9efb6f 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/ToolTipController.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/streampane/tooltip/ToolTipController.java
@@ -22,6 +22,9 @@
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.contentstream.operator.OperatorName;
import org.apache.pdfbox.pdmodel.PDResources;
interface ToolTip
@@ -31,21 +34,11 @@ interface ToolTip
/**
* @author Khyrul Bashar
- * A class that provieds the tooltip for an operator.
+ * A class that provides the tooltip for an operator.
*/
public class ToolTipController
{
- private static final String FONT_OPERATOR = "Tf";
- private static final String STROKING_COLOR = "SCN";
- private static final String STROKING_COLOR_SPACE = "CS";
- private static final String NON_STROKING_COLOR_SPACE = "cs";
- private static final String NON_STROKING_COLOR = "scn";
- private static final String RGB_STROKING_COLOR = "RG";
- private static final String RGB_NON_STROKING_COLOR = "rg";
- private static final String CMYK_STROKING_COLOR = "K";
- private static final String CMYK_NON_STROKING_COLOR = "k";
- private static final String GRAY_STROKING_COLOR = "G";
- private static final String GRAY_NON_STROKING_COLOR = "g";
+ private static final Log LOG = LogFactory.getLog(ToolTipController.class);
private final PDResources resources;
private JTextComponent textComponent;
@@ -89,40 +82,40 @@ public String getToolTip(int offset, JTextComponent textComponent)
if (word != null)
{
ToolTip toolTip;
- if (word.equals(FONT_OPERATOR))
+ if (word.equals(OperatorName.SET_FONT_AND_SIZE))
{
toolTip = new FontToolTip(resources, rowText);
return toolTip.getToolTipText();
}
- else if (word.equals(STROKING_COLOR))
+ else if (word.equals(OperatorName.STROKING_COLOR_N))
{
- String colorSpaceName = findColorSpace(offset, STROKING_COLOR_SPACE);
+ String colorSpaceName = findColorSpace(offset, OperatorName.STROKING_COLORSPACE);
if (colorSpaceName != null)
{
toolTip = new SCNToolTip(resources, colorSpaceName, rowText);
return toolTip.getToolTipText();
}
}
- else if (word.equals(NON_STROKING_COLOR))
+ else if (word.equals(OperatorName.NON_STROKING_COLOR_N))
{
- String colorSpaceName = findColorSpace(offset, NON_STROKING_COLOR_SPACE);
+ String colorSpaceName = findColorSpace(offset, OperatorName.NON_STROKING_COLORSPACE);
if (colorSpaceName != null)
{
toolTip = new SCNToolTip(resources, colorSpaceName, rowText);
return toolTip.getToolTipText();
}
}
- else if (word.equals(RGB_STROKING_COLOR) || word.equals(RGB_NON_STROKING_COLOR))
+ else if (word.equals(OperatorName.STROKING_COLOR_RGB) || word.equals(OperatorName.NON_STROKING_RGB))
{
toolTip = new RGToolTip(rowText);
return toolTip.getToolTipText();
}
- else if (word.equals(CMYK_STROKING_COLOR) || word.equals(CMYK_NON_STROKING_COLOR))
+ else if (word.equals(OperatorName.STROKING_COLOR_CMYK) || word.equals(OperatorName.NON_STROKING_CMYK))
{
toolTip = new KToolTip(rowText);
return toolTip.getToolTipText();
}
- else if (word.equals(GRAY_STROKING_COLOR) || word.equals(GRAY_NON_STROKING_COLOR))
+ else if (word.equals(OperatorName.STROKING_COLOR_GRAY) || word.equals(OperatorName.NON_STROKING_GRAY))
{
toolTip = new GToolTip(rowText);
return toolTip.getToolTipText();
@@ -152,8 +145,7 @@ private String findColorSpace(int offset, String colorSpaceType)
}
catch (BadLocationException e)
{
- e.printStackTrace();
- return null;
+ LOG.error(e, e);
}
return null;
}
@@ -174,7 +166,7 @@ private String getWord(int offset)
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e, e);
}
return null;
}
@@ -189,7 +181,7 @@ private String getRowText(int offset)
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e, e);
}
return null;
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/stringpane/StringPane.java b/debugger/src/main/java/org/apache/pdfbox/debugger/stringpane/StringPane.java
index 151e5e591e2..2a7fcd5ded6 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/stringpane/StringPane.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/stringpane/StringPane.java
@@ -62,7 +62,7 @@ private String getTextString(COSString cosString)
String text = cosString.getString();
for (char c : text.toCharArray())
{
- if (Character.isISOControl(c))
+ if (Character.isISOControl(c) && c != '\n' && c != '\r' && c != '\t')
{
text = "<" + cosString.toHexString() + ">";
break;
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/treestatus/TreeStatus.java b/debugger/src/main/java/org/apache/pdfbox/debugger/treestatus/TreeStatus.java
index 965e196e212..8c5e9d9d62a 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/treestatus/TreeStatus.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/treestatus/TreeStatus.java
@@ -173,7 +173,7 @@ private List parsePathString(String path)
* An object is searched in the tree structure using the identifiers parsed earlier step.
* @param obj
* @param searchStr
- * @return
+ * @return the Object found or null
*/
private Object searchNode(Object obj, String searchStr)
{
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DebugLog.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DebugLog.java
new file mode 100644
index 00000000000..14871e1bb9e
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DebugLog.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.debugger.ui;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Custom Log implementation which forwards to LogDialog.
+ *
+ * @author John Hewson
+ */
+public class DebugLog implements Log
+{
+ private final String name;
+
+ // hardcoded, but kept to aid with debugging custom builds
+ private static final boolean INFO = true;
+ private static final boolean TRACE = false;
+ private static final boolean DEBUG = false;
+
+ public DebugLog(String name)
+ {
+ this.name = name;
+ }
+
+ @Override
+ public void debug(Object o)
+ {
+ if (DEBUG)
+ {
+ LogDialog.instance().log(name, "debug", o, null);
+ }
+ }
+
+ @Override
+ public void debug(Object o, Throwable throwable)
+ {
+ if (DEBUG)
+ {
+ LogDialog.instance().log(name, "debug", o, throwable);
+ }
+ }
+
+ @Override
+ public void error(Object o)
+ {
+ LogDialog.instance().log(name, "error", o, null);
+ }
+
+ @Override
+ public void error(Object o, Throwable throwable)
+ {
+ LogDialog.instance().log(name, "error", o, throwable);
+ }
+
+ @Override
+ public void fatal(Object o)
+ {
+ LogDialog.instance().log(name, "fatal", o, null);
+ }
+
+ @Override
+ public void fatal(Object o, Throwable throwable)
+ {
+ LogDialog.instance().log(name, "fatal", o, throwable);
+ }
+
+ @Override
+ public void info(Object o)
+ {
+ if (INFO)
+ {
+ LogDialog.instance().log(name, "info", o, null);
+ }
+ }
+
+ @Override
+ public void info(Object o, Throwable throwable)
+ {
+ if (INFO)
+ {
+ LogDialog.instance().log(name, "info", o, throwable);
+ }
+ }
+
+ @Override
+ public boolean isDebugEnabled()
+ {
+ return DEBUG;
+ }
+
+ @Override
+ public boolean isErrorEnabled()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isFatalEnabled()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isInfoEnabled()
+ {
+ return INFO;
+ }
+
+ @Override
+ public boolean isTraceEnabled()
+ {
+ return TRACE;
+ }
+
+ @Override
+ public boolean isWarnEnabled()
+ {
+ return true;
+ }
+
+ @Override
+ public void trace(Object o)
+ {
+ if (TRACE)
+ {
+ LogDialog.instance().log(name, "trace", o, null);
+ }
+ }
+
+ @Override
+ public void trace(Object o, Throwable throwable)
+ {
+ if (TRACE)
+ {
+ LogDialog.instance().log(name, "trace", o, throwable);
+ }
+ }
+
+ @Override
+ public void warn(Object o)
+ {
+ LogDialog.instance().log(name, "warn", o, null);
+ }
+
+ @Override
+ public void warn(Object o, Throwable throwable)
+ {
+ LogDialog.instance().log(name, "warn", o, throwable);
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DocumentEntry.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DocumentEntry.java
index 78da1b6efc2..8dd08cc49eb 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DocumentEntry.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/DocumentEntry.java
@@ -19,6 +19,7 @@
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.debugger.PDFDebugger;
/**
* Represents an abstract view of a document in the tree view.
@@ -44,7 +45,8 @@ public int getPageCount()
public PageEntry getPage(int index)
{
PDPage page = doc.getPages().get(index);
- return new PageEntry(page.getCOSObject(), index + 1);
+ String pageLabel = PDFDebugger.getPageLabel(doc, index);
+ return new PageEntry(page.getCOSObject(), index + 1, pageLabel);
}
public int indexOf(PageEntry page)
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ErrorDialog.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ErrorDialog.java
index 9ba34a31d61..f19b1387328 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ErrorDialog.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ErrorDialog.java
@@ -39,6 +39,8 @@
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.WindowConstants;
/**
* A dialog to display a runtime exception stack trace.
@@ -50,7 +52,7 @@
* package.
*
*/
-@SuppressWarnings("serial")
+@SuppressWarnings({"serial","squid:MaximumInheritanceDepth"})
public class ErrorDialog extends JDialog
{
private static final List FILTERS = Arrays.asList(
@@ -110,7 +112,7 @@ public ErrorDialog(JComponent owner, Icon icon, Throwable t)
{
setIconImage(((ImageIcon) icon).getImage());
}
- setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
error = t;
message = createErrorMessage(error);
main = createContent();
@@ -148,8 +150,6 @@ static void position(Component c, Component parent)
/**
* Creates the display with the top-level exception message followed by a pane (that toggles)
* for detailed stack traces.
- *
- * @param t a non-null exception
*/
final JComponent createContent()
{
@@ -169,7 +169,7 @@ public void actionPerformed(ActionEvent e)
{
if (details == null)
{
- details = createDetailedMessage(error);
+ details = createDetailedMessage();
StringBuilder buffer = new StringBuilder();
stacktrace.setText(generateStackTrace(error, buffer).toString());
stacktrace.setCaretPosition(0);
@@ -249,14 +249,13 @@ final JComponent createErrorMessage(Throwable t)
/**
* Creates a non-editable widget to display the detailed stack trace.
*/
- JScrollPane createDetailedMessage(Throwable t)
+ JScrollPane createDetailedMessage()
{
stacktrace = new JTextPane();
stacktrace.setEditable(false);
- JScrollPane pane = new JScrollPane(stacktrace,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
- JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- return pane;
+ return new JScrollPane(stacktrace,
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
/**
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/FileOpenSaveDialog.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/FileOpenSaveDialog.java
index 5f9a49e5a9c..b5f886bae5a 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/FileOpenSaveDialog.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/FileOpenSaveDialog.java
@@ -64,6 +64,7 @@ public void approveSelection()
public FileOpenSaveDialog(Component parentUI, FileFilter fileFilter)
{
mainUI = parentUI;
+ fileChooser.resetChoosableFileFilters();
fileChooser.setFileFilter(fileFilter);
}
@@ -74,16 +75,21 @@ public FileOpenSaveDialog(Component parentUI, FileFilter fileFilter)
* @return true if the file is saved successfully or false if failed.
* @throws IOException if there is an error in creation of the file.
*/
- public boolean saveFile(byte[] bytes) throws IOException
+ public boolean saveFile(byte[] bytes, String extension) throws IOException
{
int result = fileChooser.showSaveDialog(mainUI);
if (result == JFileChooser.APPROVE_OPTION)
{
- File selectedFile = fileChooser.getSelectedFile();
+ String filename = fileChooser.getSelectedFile().getAbsolutePath();
+ if (extension != null && !filename.endsWith(extension))
+ {
+ filename += "." + extension;
+ }
+
FileOutputStream outputStream = null;
try
{
- outputStream = new FileOutputStream(selectedFile);
+ outputStream = new FileOutputStream(filename);
outputStream.write(bytes);
}
finally
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/HighResolutionImageIcon.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/HighResolutionImageIcon.java
new file mode 100644
index 00000000000..3c96d0845ae
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/HighResolutionImageIcon.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.debugger.ui;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Image;
+import javax.swing.Icon;
+
+public class HighResolutionImageIcon implements Icon
+{
+ private final Image image;
+ private final int baseWidth;
+ private final int baseHeight;
+
+ public HighResolutionImageIcon(Image image, int baseWidth, int baseHeight)
+ {
+ this.image = image;
+ this.baseWidth = baseWidth;
+ this.baseHeight = baseHeight;
+ }
+
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.drawImage(image, x, y, getIconWidth(), getIconHeight(), null);
+ }
+
+ @Override
+ public int getIconWidth()
+ {
+ return baseWidth;
+ }
+
+ @Override
+ public int getIconHeight()
+ {
+ return baseHeight;
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageTypeMenu.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageTypeMenu.java
new file mode 100644
index 00000000000..0451ef1680f
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageTypeMenu.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.debugger.ui;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JMenu;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.apache.pdfbox.rendering.ImageType;
+
+/**
+ * @author Tilman Hausherr
+ *
+ * A singleton class that provides the imagetype menu for the menubar. To act upon the menu item
+ * selection, the user of the class must add ActionListener which will check for the action command
+ * and act accordingly.
+ */
+public final class ImageTypeMenu extends MenuBase
+{
+ public static final String IMAGETYPE_RGB = "RGB";
+ public static final String IMAGETYPE_ARGB = "ARGB";
+ public static final String IMAGETYPE_GRAY = "Gray";
+ public static final String IMAGETYPE_BITONAL = "Bitonal";
+
+ private static ImageTypeMenu instance;
+ private JRadioButtonMenuItem rgbItem;
+ private JRadioButtonMenuItem argbItem;
+ private JRadioButtonMenuItem grayItem;
+ private JRadioButtonMenuItem bitonalItem;
+
+ /**
+ * Constructor.
+ */
+ private ImageTypeMenu()
+ {
+ setMenu(createMenu());
+ }
+
+ /**
+ * Provides the ImageTypeMenu instance.
+ * @return ImageTypeMenu instance.
+ */
+ public static ImageTypeMenu getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new ImageTypeMenu();
+ }
+ return instance;
+ }
+
+ /**
+ * Set the image type selection.
+ * @param selection String instance.
+ */
+ public void setImageTypeSelection(String selection)
+ {
+ if (IMAGETYPE_RGB.equals(selection))
+ {
+ rgbItem.setSelected(true);
+ }
+ else if (IMAGETYPE_ARGB.equals(selection))
+ {
+ argbItem.setSelected(true);
+ }
+ else if (IMAGETYPE_GRAY.equals(selection))
+ {
+ grayItem.setSelected(true);
+ }
+ else if (IMAGETYPE_BITONAL.equals(selection))
+ {
+ bitonalItem.setSelected(true);
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static boolean isImageTypeMenu(String actionCommand)
+ {
+ return IMAGETYPE_RGB.equals(actionCommand) || IMAGETYPE_ARGB.equals(actionCommand) ||
+ IMAGETYPE_GRAY.equals(actionCommand) || IMAGETYPE_BITONAL.equals(actionCommand);
+ }
+
+ public static ImageType getImageType()
+ {
+ if (instance.argbItem.isSelected())
+ {
+ return ImageType.ARGB;
+ }
+ if (instance.grayItem.isSelected())
+ {
+ return ImageType.GRAY;
+ }
+ if (instance.bitonalItem.isSelected())
+ {
+ return ImageType.BINARY;
+ }
+ return ImageType.RGB;
+ }
+
+ public static ImageType getImageType(String actionCommand)
+ {
+ if (IMAGETYPE_RGB.equals(actionCommand))
+ {
+ return ImageType.RGB;
+ }
+ else if (IMAGETYPE_ARGB.equals(actionCommand))
+ {
+ return ImageType.ARGB;
+ }
+ else if (IMAGETYPE_GRAY.equals(actionCommand))
+ {
+ return ImageType.GRAY;
+ }
+ else if (IMAGETYPE_BITONAL.equals(actionCommand))
+ {
+ return ImageType.BINARY;
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private JMenu createMenu()
+ {
+ JMenu menu = new JMenu();
+ menu.setText("Image type");
+
+ rgbItem = new JRadioButtonMenuItem();
+ argbItem = new JRadioButtonMenuItem();
+ grayItem = new JRadioButtonMenuItem();
+ bitonalItem = new JRadioButtonMenuItem();
+ rgbItem.setSelected(true);
+
+ ButtonGroup bg = new ButtonGroup();
+ bg.add(rgbItem);
+ bg.add(argbItem);
+ bg.add(grayItem);
+ bg.add(bitonalItem);
+
+ rgbItem.setText(IMAGETYPE_RGB);
+ argbItem.setText(IMAGETYPE_ARGB);
+ grayItem.setText(IMAGETYPE_GRAY);
+ bitonalItem.setText(IMAGETYPE_BITONAL);
+
+ menu.add(rgbItem);
+ menu.add(argbItem);
+ menu.add(grayItem);
+ menu.add(bitonalItem);
+
+ return menu;
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageUtil.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageUtil.java
index 1c0312b08b9..51dcf4bf93e 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageUtil.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ImageUtil.java
@@ -36,30 +36,34 @@ private ImageUtil()
* @param image The image to rotate.
* @param rotation The rotation in degrees.
* @return The rotated image.
+ * @throws IllegalArgumentException if the angle isn't a multiple of 90°.
*/
public static BufferedImage getRotatedImage(BufferedImage image, int rotation)
{
int width = image.getWidth();
int height = image.getHeight();
- double x = 0, y = 0;
+ int x = 0;
+ int y = 0;
BufferedImage rotatedImage;
- switch (rotation % 360)
+ switch ((rotation + 360) % 360)
{
+ case 0:
+ return image;
case 90:
x = height;
- rotatedImage = new BufferedImage(height, width, BufferedImage.TYPE_INT_RGB);
+ rotatedImage = new BufferedImage(height, width, image.getType());
break;
case 270:
y = width;
- rotatedImage = new BufferedImage(height, width, BufferedImage.TYPE_INT_RGB);
+ rotatedImage = new BufferedImage(height, width, image.getType());
break;
case 180:
x = width;
y = height;
- rotatedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ rotatedImage = new BufferedImage(width, height, image.getType());
break;
default:
- return image;
+ throw new IllegalArgumentException("Only multiple of 90° are supported");
}
Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
g.translate(x, y);
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/LogDialog.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/LogDialog.java
new file mode 100644
index 00000000000..74f9610b41b
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/LogDialog.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.debugger.ui;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Frame;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+
+/**
+ * Custom log dialog.
+ *
+ * @author John Hewson
+ */
+@SuppressWarnings({"serial","squid:MaximumInheritanceDepth"})
+public class LogDialog extends JDialog
+{
+ private static LogDialog instance;
+ private final JLabel logLabel;
+ private final JTextPane textPane;
+ private final JScrollPane scrollPane;
+ private int fatalCount = 0;
+ private int errorCount = 0;
+ private int warnCount = 0;
+ private int otherCount = 0;
+ private int exceptionCount = 0;
+
+ private LogDialog(Frame owner, JLabel logLabel)
+ {
+ super(owner);
+ this.logLabel = logLabel;
+
+ textPane = new JTextPane();
+ scrollPane = new JScrollPane(textPane);
+ getContentPane().add(scrollPane);
+
+ this.pack();
+ }
+
+ public static void init(Frame owner, JLabel logLabel)
+ {
+ instance = new LogDialog(owner, logLabel);
+ }
+
+ public static LogDialog instance()
+ {
+ return instance;
+ }
+
+ public void log(String name, String level, Object o, Throwable throwable)
+ {
+ StyledDocument doc = textPane.getStyledDocument();
+
+ String levelText;
+ SimpleAttributeSet levelStyle = new SimpleAttributeSet();
+ if ("fatal".equals(level))
+ {
+ levelText = "Fatal";
+ StyleConstants.setForeground(levelStyle, Color.WHITE);
+ StyleConstants.setBackground(levelStyle, Color.BLACK);
+ fatalCount++;
+ }
+ else if ("error".equals(level))
+ {
+ levelText = "Error";
+ StyleConstants.setForeground(levelStyle, new Color(0xFF291F));
+ StyleConstants.setBackground(levelStyle, new Color(0xFFF0F0));
+ errorCount++;
+ }
+ else if ("warn".equals(level))
+ {
+ levelText = "Warning";
+ StyleConstants.setForeground(levelStyle, new Color(0x614201));
+ StyleConstants.setBackground(levelStyle, new Color(0xFFFCE5));
+ warnCount++;
+ }
+ else if ("info".equals(level))
+ {
+ levelText = "Info";
+ StyleConstants.setForeground(levelStyle, new Color(0x203261));
+ StyleConstants.setBackground(levelStyle, new Color(0xE2E8FF));
+ otherCount++;
+ }
+ else if ("debug".equals(level))
+ {
+ levelText = "Debug";
+ StyleConstants.setForeground(levelStyle, new Color(0x32612E));
+ StyleConstants.setBackground(levelStyle, new Color(0xF4FFEC));
+ otherCount++;
+ }
+ else if ("trace".equals(level))
+ {
+ levelText = "Trace";
+ StyleConstants.setForeground(levelStyle, new Color(0x64438D));
+ StyleConstants.setBackground(levelStyle, new Color(0xFEF3FF));
+ otherCount++;
+ }
+ else
+ {
+ throw new Error(level);
+ }
+
+ SimpleAttributeSet nameStyle = new SimpleAttributeSet();
+ StyleConstants.setForeground(nameStyle, new Color(0x6A6A6A));
+
+ String shortName = name.substring(name.lastIndexOf('.') + 1);
+ String message = o == null ? "(null)" : o.toString();
+
+ if (throwable != null)
+ {
+ StringWriter sw = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(sw));
+ message += "\n " + sw.toString();
+ exceptionCount++;
+ }
+
+ try
+ {
+ doc.insertString(doc.getLength(), " " + levelText + " ", levelStyle);
+ doc.insertString(doc.getLength(), " [" + shortName + "]", nameStyle);
+ doc.insertString(doc.getLength(), " " + message + "\n", null);
+ }
+ catch (BadLocationException e)
+ {
+ throw new Error(e);
+ }
+ textPane.setCaretPosition(doc.getLength());
+
+ // update status bar with new counts
+ updateStatusBar();
+ }
+
+ private void updateStatusBar()
+ {
+ List infos = new ArrayList();
+
+ if (exceptionCount > 0)
+ {
+ infos.add(exceptionCount + " exception" + (errorCount > 1 ? "s" : ""));
+ }
+
+ if (fatalCount > 0)
+ {
+ infos.add(errorCount + " error" + (errorCount > 1 ? "s" : ""));
+ }
+
+ if (errorCount > 0)
+ {
+ infos.add(errorCount + " error" + (errorCount > 1 ? "s" : ""));
+ }
+
+ if (warnCount > 0)
+ {
+ infos.add(warnCount + " warning" + (warnCount > 1 ? "s" : ""));
+ }
+
+ if (otherCount > 0)
+ {
+ infos.add(otherCount + " message" + (otherCount > 1 ? "s" : ""));
+ }
+
+ String info = "";
+ for (String str : infos)
+ {
+ if (info.length() > 0)
+ {
+ info += ", ";
+ }
+ info += str;
+ }
+
+ logLabel.setText(info);
+ }
+
+ public void clear()
+ {
+ fatalCount = 0;
+ errorCount = 0;
+ warnCount = 0;
+ otherCount = 0;
+ exceptionCount = 0;
+ textPane.setText("");
+ logLabel.setText("");
+ }
+
+ // these two just to avoid the "overridable method call in constructor" warning
+
+ @Override
+ public final Container getContentPane()
+ {
+ return super.getContentPane();
+ }
+
+ @Override
+ public final void pack()
+ {
+ super.pack();
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/MenuBase.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/MenuBase.java
index fe3fff7de11..7667628b2c0 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/MenuBase.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/MenuBase.java
@@ -57,7 +57,7 @@ public void setEnableMenu(boolean isEnable)
}
/**
- * Add the ActionListener for the menuitems.
+ * Add the ActionListener for the menu items.
*
* @param listener ActionListener.
*/
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/OSXAdapter.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/OSXAdapter.java
index 8736fb9f92c..b2e4c3d22a7 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/OSXAdapter.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/OSXAdapter.java
@@ -18,7 +18,7 @@
/*
* This file includes code under the following terms:
- *
+ *
* Version: 2.0
*
* Disclaimer: IMPORTANT: This Apple software is supplied to you by
@@ -64,10 +64,14 @@
package org.apache.pdfbox.debugger.ui;
+import java.awt.Desktop;
+import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.util.StringTokenizer;
+import java.util.List;
/**
* Hooks existing preferences/about/quit functionality from an
@@ -87,10 +91,63 @@ public class OSXAdapter implements InvocationHandler
protected String proxySignature;
static Object macOSXApplication;
+
+ private static boolean isMinJdk9()
+ {
+ // strategy from lucene-solr/lucene/core/src/java/org/apache/lucene/util/Constants.java
+ String version = System.getProperty("java.specification.version");
+ final StringTokenizer st = new StringTokenizer(version, ".");
+ try
+ {
+ int major = Integer.parseInt(st.nextToken());
+ int minor = 0;
+ if (st.hasMoreTokens())
+ {
+ minor = Integer.parseInt(st.nextToken());
+ }
+ return major > 1 || (major == 1 && minor >= 9);
+ }
+ catch (NumberFormatException nfe)
+ {
+ // maybe some new numbering scheme in the 22nd century
+ return true;
+ }
+ }
// Pass this method an Object and Method equipped to perform application shutdown logic
// The method passed should return a boolean stating whether or not the quit should occur
- public static void setQuitHandler(Object target, Method quitHandler) {
+ public static void setQuitHandler(final Object target, final Method quitHandler)
+ {
+ if (isMinJdk9())
+ {
+ try
+ {
+ Desktop desktopObject = Desktop.getDesktop();
+ Class> filesHandlerClass = Class.forName("java.awt.desktop.QuitHandler");
+ final Method setQuitHandlerMethod = desktopObject.getClass().getMethod("setQuitHandler", filesHandlerClass);
+ Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(),
+ new Class[] { filesHandlerClass }, new InvocationHandler()
+ {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ if ("handleQuitRequestWith".equals(method.getName()))
+ {
+ // We just call our own quit handler
+ quitHandler.invoke(target);
+ }
+ return null;
+ }
+ });
+ setQuitHandlerMethod.invoke(desktopObject, osxAdapterProxy);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ return;
+ }
setHandler(new OSXAdapter("handleQuit", target, quitHandler));
}
@@ -104,8 +161,8 @@ public static void setAboutHandler(Object target, Method aboutHandler) {
// If we're setting a handler, enable the About menu item by calling
// com.apple.eawt.Application reflectively
try {
- Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class });
- enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) });
+ Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", boolean.class);
+ enableAboutMethod.invoke(macOSXApplication, Boolean.valueOf(enableAboutMenu));
} catch (Exception ex) {
System.err.println("OSXAdapter could not access the About Menu");
throw new RuntimeException(ex);
@@ -122,8 +179,8 @@ public static void setPreferencesHandler(Object target, Method prefsHandler) {
// If we're setting a handler, enable the Preferences menu item by calling
// com.apple.eawt.Application reflectively
try {
- Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class });
- enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) });
+ Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", boolean.class);
+ enablePrefsMethod.invoke(macOSXApplication, enablePrefsMenu);
} catch (Exception ex) {
System.err.println("OSXAdapter could not access the About Menu");
throw new RuntimeException(ex);
@@ -133,16 +190,65 @@ public static void setPreferencesHandler(Object target, Method prefsHandler) {
// Pass this method an Object and a Method equipped to handle document events from the Finder
// Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the
// application bundle's Info.plist
- public static void setFileHandler(Object target, Method fileHandler) {
- setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) {
+ public static void setFileHandler(Object target, Method fileHandler)
+ {
+ if (isMinJdk9())
+ {
+ try
+ {
+ Desktop desktopObject = Desktop.getDesktop();
+ Class> filesHandlerClass = Class.forName("java.awt.desktop.OpenFilesHandler");
+ Method setOpenFileHandlerMethod = desktopObject.getClass().getMethod("setOpenFileHandler", filesHandlerClass);
+ Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(),
+ new Class[]
+ {
+ filesHandlerClass
+ }, new OSXAdapter("openFiles", target, fileHandler)
+ {
+ // Override OSXAdapter.callTarget to send information on the
+ // file to be opened
+ @Override
+ public boolean callTarget(Object openFilesEvent)
+ {
+ if (openFilesEvent != null)
+ {
+ try
+ {
+ Method getFilesMethod = openFilesEvent.getClass().getDeclaredMethod("getFiles",
+ (Class[]) null);
+ @SuppressWarnings("unchecked")
+ List files = (List) getFilesMethod.invoke(openFilesEvent,
+ (Object[]) null);
+ this.targetMethod.invoke(this.targetObject, files.get(0).getAbsolutePath());
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+ return true;
+ }
+ });
+ setOpenFileHandlerMethod.invoke(desktopObject, osxAdapterProxy);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ return;
+ }
+ /* JDK <= 1.8, using Apple classes */
+ setHandler(new OSXAdapter("handleOpenFile", target, fileHandler)
+ {
// Override OSXAdapter.callTarget to send information on the
// file to be opened
+ @Override
public boolean callTarget(Object appleEvent) {
if (appleEvent != null) {
try {
Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null);
String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null);
- this.targetMethod.invoke(this.targetObject, new Object[] { filename });
+ this.targetMethod.invoke(this.targetObject, filename);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@@ -157,13 +263,13 @@ public static void setHandler(OSXAdapter adapter) {
try {
Class> applicationClass = Class.forName("com.apple.eawt.Application");
if (macOSXApplication == null) {
- macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null);
+ macOSXApplication = applicationClass.getDeclaredConstructor((Class[])null).newInstance((Object[])null);
}
Class> applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
- Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass });
+ Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", applicationListenerClass);
// Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener
Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[]{applicationListenerClass}, adapter);
- addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy });
+ addListenerMethod.invoke(macOSXApplication, osxAdapterProxy);
} catch (ClassNotFoundException cnfe) {
System.err.println("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")");
} catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods
@@ -188,7 +294,7 @@ public boolean callTarget(Object appleEvent) throws InvocationTargetException, I
if (result == null) {
return true;
}
- return Boolean.valueOf(result.toString()).booleanValue();
+ return Boolean.valueOf(result.toString());
}
// InvocationHandler implementation
@@ -213,9 +319,9 @@ protected boolean isCorrectMethod(Method method, Object[] args) {
protected void setApplicationEventHandled(Object event, boolean handled) {
if (event != null) {
try {
- Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class });
+ Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", boolean.class);
// If the target method returns a boolean, use that as a hint
- setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) });
+ setHandledMethod.invoke(event, Boolean.valueOf(handled));
} catch (Exception ex) {
System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event);
throw new RuntimeException(ex);
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeCellRenderer.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeCellRenderer.java
index 3dd0ff29312..6ecad49196c 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeCellRenderer.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeCellRenderer.java
@@ -102,7 +102,7 @@ private Object toTreeObject(Object nodeValue)
else
{
ArrayEntry entry = (ArrayEntry) nodeValue;
- key = "" + entry.getIndex();
+ key = Integer.toString(entry.getIndex());
object = toTreeObject(entry.getValue());
value = entry.getValue();
item = entry.getItem();
@@ -125,15 +125,15 @@ private Object toTreeObject(Object nodeValue)
}
else if (nodeValue instanceof COSBoolean)
{
- result = "" + ((COSBoolean) nodeValue).getValue();
+ result = Boolean.toString(((COSBoolean) nodeValue).getValue());
}
else if (nodeValue instanceof COSFloat)
{
- result = "" + ((COSFloat) nodeValue).floatValue();
+ result = Float.toString(((COSFloat) nodeValue).floatValue());
}
else if (nodeValue instanceof COSInteger)
{
- result = "" + ((COSInteger) nodeValue).intValue();
+ result = Integer.toString(((COSInteger) nodeValue).intValue());
}
else if (nodeValue instanceof COSString)
{
@@ -188,16 +188,36 @@ private String toTreePostfix(Object nodeValue)
StringBuilder sb = new StringBuilder();
COSDictionary dict = (COSDictionary)nodeValue;
+
+ if (COSName.ANNOT.equals(dict.getCOSName(COSName.TYPE))
+ && COSName.WIDGET.equals(dict.getCOSName(COSName.SUBTYPE)) ||
+ dict.containsKey(COSName.T) && dict.containsKey(COSName.KIDS))
+ {
+ String name = dict.getString(COSName.T);
+ if (name != null)
+ {
+ sb.append(" Name: ");
+ sb.append(name);
+ sb.append(' ');
+ }
+ }
+
if (dict.containsKey(COSName.TYPE))
{
COSName type = dict.getCOSName(COSName.TYPE);
- sb.append(" /T:").append(type.getName());
+ if (type != null)
+ {
+ sb.append(" /T:").append(type.getName());
+ }
}
-
+
if (dict.containsKey(COSName.SUBTYPE))
{
COSName subtype = dict.getCOSName(COSName.SUBTYPE);
- sb.append(" /S:").append(subtype.getName());
+ if (subtype != null)
+ {
+ sb.append(" /S:").append(subtype.getName());
+ }
}
return sb.toString();
}
@@ -315,7 +335,7 @@ else if (nodeValue instanceof PageEntry)
/**
* An ImageIcon which allows other ImageIcon overlays.
*/
- private class OverlayIcon extends ImageIcon
+ private static class OverlayIcon extends ImageIcon
{
private final ImageIcon base;
private final List overlays;
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeModel.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeModel.java
index 7a577373ae8..2230ed0b393 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeModel.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PDFTreeModel.java
@@ -92,7 +92,7 @@ public void addTreeModelListener(TreeModelListener l)
* Returns the child of parent at index index in the parent's child
* array. parent must be a node previously obtained from this data source. This
* should not return null if index is a valid index for
- * parent (that is index >= 0 &&
+ * parent (that is index >= 0 &&
* index < getChildCount(parent)).
*
* @param parent a node in the tree, obtained from this data source
@@ -306,7 +306,7 @@ public Object getRoot()
@Override
public boolean isLeaf(Object node)
{
- boolean isLeaf = !(node instanceof COSDictionary ||
+ return !(node instanceof COSDictionary ||
node instanceof COSArray ||
node instanceof COSDocument ||
node instanceof DocumentEntry ||
@@ -314,7 +314,6 @@ public boolean isLeaf(Object node)
node instanceof COSObject ||
(node instanceof MapEntry && !isLeaf(((MapEntry)node).getValue()) ) ||
(node instanceof ArrayEntry && !isLeaf(((ArrayEntry)node).getValue()) ));
- return isLeaf;
}
/** Removes a listener previously added with
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PageEntry.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PageEntry.java
index 8c7ef56c27c..2011be41b3e 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PageEntry.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/PageEntry.java
@@ -18,6 +18,7 @@
package org.apache.pdfbox.debugger.ui;
import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
@@ -30,11 +31,13 @@ public class PageEntry
{
private final COSDictionary dict;
private final int pageNum;
-
- public PageEntry(COSDictionary page, int pageNum)
+ private final String pageLabel;
+
+ public PageEntry(COSDictionary page, int pageNum, String pageLabel)
{
dict = page;
this.pageNum = pageNum;
+ this.pageLabel = pageLabel;
}
public COSDictionary getDict()
@@ -50,7 +53,7 @@ public int getPageNum()
@Override
public String toString()
{
- return "Page: " + pageNum;
+ return "Page: " + pageNum + (pageLabel == null ? "" : " - " + pageLabel);
}
public String getPath()
@@ -61,8 +64,18 @@ public String getPath()
COSDictionary node = dict;
while (node.containsKey(COSName.PARENT))
{
- COSDictionary parent = (COSDictionary)node.getDictionaryObject(COSName.PARENT);
- COSArray kids = (COSArray)parent.getDictionaryObject(COSName.KIDS);
+ COSBase base = node.getDictionaryObject(COSName.PARENT);
+ if (!(base instanceof COSDictionary))
+ {
+ return "";
+ }
+ COSDictionary parent = (COSDictionary) base;
+ base = parent.getDictionaryObject(COSName.KIDS);
+ if (!(base instanceof COSArray))
+ {
+ return "";
+ }
+ COSArray kids = (COSArray) base;
int idx = kids.indexOfObject(node);
sb.append("/Kids/[").append(idx).append("]");
node = parent;
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ReaderBottomPanel.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ReaderBottomPanel.java
index d5f6f774d67..80f13ac6b46 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ReaderBottomPanel.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ReaderBottomPanel.java
@@ -16,12 +16,16 @@
*/
package org.apache.pdfbox.debugger.ui;
+import java.awt.BorderLayout;
+import java.awt.Cursor;
import java.awt.Dimension;
-
+import java.awt.Window;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import javax.swing.JLabel;
import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
-import javax.swing.JLabel;
-import java.awt.FlowLayout;
/**
* A panel to display at the bottom of the window for status and other stuff.
*
@@ -29,31 +33,47 @@
*/
public class ReaderBottomPanel extends JPanel
{
-
private JLabel statusLabel = null;
-
- /**
- * This is the default constructor.
- */
+ private JLabel logLabel = null;
+
public ReaderBottomPanel()
{
- FlowLayout flowLayout = new FlowLayout();
- this.setLayout(flowLayout);
- this.setComponentOrientation(java.awt.ComponentOrientation.LEFT_TO_RIGHT);
- this.setPreferredSize(new Dimension(1000, 20));
- flowLayout.setAlignment(FlowLayout.LEFT);
+ BorderLayout layout = new BorderLayout();
+ this.setLayout(layout);
+
statusLabel = new JLabel();
statusLabel.setText("Ready");
- this.add(statusLabel, null);
- }
+ this.add(statusLabel, BorderLayout.WEST);
- /**
- * Return the status label.
- *
- * @return JLabel The status label.
- */
+ logLabel = new JLabel();
+ logLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ logLabel.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mouseClicked(MouseEvent e)
+ {
+ Window viewer = LogDialog.instance().getOwner();
+
+ // show the log window
+ LogDialog.instance().setSize(800, 400);
+ LogDialog.instance().setVisible(true);
+ LogDialog.instance().setLocation(viewer.getLocationOnScreen().x + viewer.getWidth() / 2,
+ viewer.getLocationOnScreen().y + viewer.getHeight() / 2);
+ }
+ });
+ this.add(logLabel, BorderLayout.EAST);
+
+ this.setBorder(new EmptyBorder(0, 5, 0, 5));
+ this.setPreferredSize(new Dimension(1000, 24));
+ }
+
public JLabel getStatusLabel()
{
return statusLabel;
}
+
+ public JLabel getLogLabel()
+ {
+ return logLabel;
+ }
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RecentFiles.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RecentFiles.java
index 563f7bb431c..69822afa895 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RecentFiles.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RecentFiles.java
@@ -160,7 +160,7 @@ private String[] breakString(String fullPath)
private void writeHistoryToPref(Queue filePaths)
{
- if (filePaths.size() == 0)
+ if (filePaths.isEmpty())
{
return;
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RenderDestinationMenu.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RenderDestinationMenu.java
new file mode 100644
index 00000000000..48e0c585e7e
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/RenderDestinationMenu.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.debugger.ui;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JMenu;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.apache.pdfbox.rendering.RenderDestination;
+
+/**
+ * @author Tilman Hausherr
+ *
+ * A singleton class that provides the RenderDestination menu for the menubar. To act upon the menu
+ * item selection, the user of the class must add ActionListener which will check for the action
+ * command and act accordingly.
+ */
+public final class RenderDestinationMenu extends MenuBase
+{
+ public static final String RENDER_DESTINATION_EXPORT = "Export";
+ public static final String RENDER_DESTINATION_PRINT = "Print";
+ public static final String RENDER_DESTINATION_VIEW = "View";
+
+ private static RenderDestinationMenu instance;
+ private JRadioButtonMenuItem exportItem;
+ private JRadioButtonMenuItem printItem;
+ private JRadioButtonMenuItem viewItem;
+
+ /**
+ * Constructor.
+ */
+ private RenderDestinationMenu()
+ {
+ setMenu(createMenu());
+ }
+
+ /**
+ * Provides the RenderDestination instance.
+ * @return RenderDestination instance.
+ */
+ public static RenderDestinationMenu getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new RenderDestinationMenu();
+ }
+ return instance;
+ }
+
+ /**
+ * Set the render destination selection.
+ * @param selection String instance.
+ */
+ public void setRenderDestinationSelection(String selection)
+ {
+ if (RENDER_DESTINATION_EXPORT.equals(selection))
+ {
+ exportItem.setSelected(true);
+ }
+ else if (RENDER_DESTINATION_PRINT.equals(selection))
+ {
+ printItem.setSelected(true);
+ }
+ else if (RENDER_DESTINATION_VIEW.equals(selection))
+ {
+ viewItem.setSelected(true);
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static boolean isRenderDestinationMenu(String actionCommand)
+ {
+ return RENDER_DESTINATION_EXPORT.equals(actionCommand) || RENDER_DESTINATION_PRINT.equals(actionCommand) ||
+ RENDER_DESTINATION_VIEW.equals(actionCommand);
+ }
+
+ public static RenderDestination getRenderDestination()
+ {
+ if (instance.printItem.isSelected())
+ {
+ return RenderDestination.PRINT;
+ }
+ if (instance.viewItem.isSelected())
+ {
+ return RenderDestination.VIEW;
+ }
+ return RenderDestination.EXPORT;
+ }
+
+ public static RenderDestination getRenderDestination(String actionCommand)
+ {
+ if (RENDER_DESTINATION_EXPORT.equals(actionCommand))
+ {
+ return RenderDestination.EXPORT;
+ }
+ else if (RENDER_DESTINATION_PRINT.equals(actionCommand))
+ {
+ return RenderDestination.PRINT;
+ }
+ else if (RENDER_DESTINATION_VIEW.equals(actionCommand))
+ {
+ return RenderDestination.VIEW;
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private JMenu createMenu()
+ {
+ JMenu menu = new JMenu();
+ menu.setText("Render destination");
+
+ exportItem = new JRadioButtonMenuItem();
+ printItem = new JRadioButtonMenuItem();
+ viewItem = new JRadioButtonMenuItem();
+ exportItem.setSelected(true);
+
+ ButtonGroup bg = new ButtonGroup();
+ bg.add(exportItem);
+ bg.add(printItem);
+ bg.add(viewItem);
+
+ exportItem.setText(RENDER_DESTINATION_EXPORT);
+ printItem.setText(RENDER_DESTINATION_PRINT);
+ viewItem.setText(RENDER_DESTINATION_VIEW);
+
+ menu.add(exportItem);
+ menu.add(printItem);
+ menu.add(viewItem);
+
+ return menu;
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
index 14982202df4..d0bf4e56941 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/Tree.java
@@ -18,6 +18,7 @@
package org.apache.pdfbox.debugger.ui;
import java.awt.Component;
+import java.awt.Desktop;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
@@ -25,6 +26,8 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -32,6 +35,8 @@
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.TreePath;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
@@ -46,10 +51,10 @@
*
* A customized tree for PDFDebugger.
*/
+@SuppressWarnings({"serial","squid:S1948"})
public class Tree extends JTree
{
private final JPopupMenu treePopupMenu;
- private final Component parent;
private final Object rootNode;
/**
@@ -60,8 +65,7 @@ public Tree(Component parentComponent)
{
treePopupMenu = new JPopupMenu();
setComponentPopupMenu(treePopupMenu);
- parent = parentComponent;
- rootNode = this.getModel().getRoot();
+ rootNode = getModel().getRoot();
}
@Override
@@ -70,12 +74,13 @@ public Point getPopupLocation(MouseEvent event)
if (event != null)
{
TreePath path = getClosestPathForLocation(event.getX(), event.getY());
- setSelectionPath(path);
- treePopupMenu.removeAll();
- for (JMenuItem menuItem : getPopupMenuItems(path))
+ if (path == null)
{
- treePopupMenu.add(menuItem);
+ return null;
}
+ setSelectionPath(path);
+ treePopupMenu.removeAll();
+ addPopupMenuItems(path);
return event.getPoint();
}
return null;
@@ -86,12 +91,11 @@ public Point getPopupLocation(MouseEvent event)
* @param nodePath is instance of TreePath of the specified Node.
* @return the JMenuItem list for the node
*/
- private List getPopupMenuItems(TreePath nodePath)
+ private void addPopupMenuItems(TreePath nodePath)
{
Object obj = nodePath.getLastPathComponent();
- List menuItems = new ArrayList();
- menuItems.add(getTreePathMenuItem(nodePath));
+ treePopupMenu.add(getTreePathMenuItem(nodePath));
if (obj instanceof MapEntry)
{
@@ -102,24 +106,34 @@ else if (obj instanceof ArrayEntry)
obj = ((ArrayEntry) obj).getValue();
}
- if (obj instanceof COSStream)
+ if (!(obj instanceof COSStream))
{
- COSStream stream = (COSStream) obj;
- menuItems.add(getUnFilteredStreamSaveMenu(stream));
- if (stream.getFilters() != null)
+ return;
+ }
+
+ treePopupMenu.addSeparator();
+
+ COSStream stream = (COSStream) obj;
+ treePopupMenu.add(getStreamSaveMenu(stream, nodePath));
+
+ if (stream.getFilters() != null)
+ {
+ if (stream.getFilters() instanceof COSArray && ((COSArray) stream.getFilters()).size() >= 2)
{
- if (stream.getFilters() instanceof COSArray && ((COSArray) stream.getFilters()).size() >= 2)
+ for (JMenuItem menuItem : getPartiallyDecodedStreamSaveMenu(stream))
{
- for (JMenuItem menuItem : getPartiallyFilteredStreamSaveMenu(stream))
- {
- menuItems.add(menuItem);
- }
+ treePopupMenu.add(menuItem);
}
- menuItems.add(getFilteredStreamSaveMenu(stream));
}
+ treePopupMenu.add(getRawStreamSaveMenu(stream));
+ }
+
+ JMenuItem open = getFileOpenMenu(stream, nodePath);
+ if (open != null)
+ {
+ treePopupMenu.addSeparator();
+ treePopupMenu.add(open);
}
-
- return menuItems;
}
/**
@@ -143,13 +157,13 @@ public void actionPerformed(ActionEvent actionEvent)
}
/**
- * Produce JMenuItem that saves filtered stream
+ * Produce JMenuItem that saves the raw stream
* @param cosStream stream to save
- * @return JMenuItem for saving filtered stream
+ * @return JMenuItem for saving the raw stream
*/
- private JMenuItem getFilteredStreamSaveMenu(final COSStream cosStream)
+ private JMenuItem getRawStreamSaveMenu(final COSStream cosStream)
{
- JMenuItem saveMenuItem = new JMenuItem("Save Filtered Stream (" + getFilters(cosStream) + ")...");
+ JMenuItem saveMenuItem = new JMenuItem("Save Raw Stream (" + getFilters(cosStream) + ") As...");
saveMenuItem.addActionListener(new ActionListener()
{
@Override
@@ -158,7 +172,7 @@ public void actionPerformed(ActionEvent actionEvent)
try
{
byte[] bytes = IOUtils.toByteArray(cosStream.createRawInputStream());
- saveStream(bytes);
+ saveStream(bytes, null, null);
}
catch (IOException e)
{
@@ -176,36 +190,75 @@ private String getFilters(COSStream cosStream)
{
StringBuilder sb = new StringBuilder();
COSBase filters = cosStream.getFilters();
- if (filters != null)
+ if (filters instanceof COSName)
{
- if (filters instanceof COSName)
- {
- sb.append(((COSName) filters).getName());
- }
- else if (filters instanceof COSArray)
+ sb.append(((COSName) filters).getName());
+ }
+ else if (filters instanceof COSArray)
+ {
+ COSArray filterArray = (COSArray) filters;
+ for (int i = 0; i < filterArray.size(); i++)
{
- COSArray filterArray = (COSArray) filters;
- for (int i = 0; i < filterArray.size(); i++)
+ if (i > 0)
{
- if (i > 0)
- {
- sb.append(", ");
- }
- sb.append(((COSName) filterArray.get(i)).getName());
+ sb.append(", ");
}
+ sb.append(((COSName) filterArray.get(i)).getName());
}
}
return sb.toString();
}
/**
- * Produce JMenuItem that saves unfiltered stream
+ * Produce JMenuItem that saves the stream
* @param cosStream stream to save
- * @return JMenuItem for saving unfiltered stream
+ * @return JMenuItem for saving stream
*/
- private JMenuItem getUnFilteredStreamSaveMenu(final COSStream cosStream)
+ private JMenuItem getStreamSaveMenu(final COSStream cosStream, final TreePath nodePath)
{
- JMenuItem saveMenuItem = new JMenuItem("Save Unfiltered Stream...");
+ // set file extension based on stream type
+ final String extension = getFileExtensionForStream(cosStream, nodePath);
+ final FileFilter fileFilter;
+
+ if (extension != null)
+ {
+ if (extension.equals("pdb"))
+ {
+ fileFilter = new FileNameExtensionFilter("Type 1 Font (*.pfb)", "pfb");
+ }
+ else if (extension.equals("ttf"))
+ {
+ fileFilter = new FileNameExtensionFilter("TrueType Font (*.ttf)", "ttf");
+ }
+ else if (extension.equals("cff"))
+ {
+ fileFilter = new FileNameExtensionFilter("Compact Font Format (*.cff)", "cff");
+ }
+ else if (extension.equals("otf"))
+ {
+ fileFilter = new FileNameExtensionFilter("OpenType Font (*.otf)", "otf");
+ }
+ else
+ {
+ fileFilter = null;
+ }
+ }
+ else
+ {
+ fileFilter = null;
+ }
+
+ String format;
+ if (extension != null)
+ {
+ format = " " + extension.toUpperCase();
+ }
+ else
+ {
+ format = "";
+ }
+
+ JMenuItem saveMenuItem = new JMenuItem("Save Stream As" + format + "...");
saveMenuItem.addActionListener(new ActionListener()
{
@Override
@@ -214,7 +267,7 @@ public void actionPerformed(ActionEvent actionEvent)
try
{
byte[] bytes = IOUtils.toByteArray(cosStream.createInputStream());
- saveStream(bytes);
+ saveStream(bytes, fileFilter, extension);
}
catch (IOException e)
{
@@ -226,11 +279,88 @@ public void actionPerformed(ActionEvent actionEvent)
}
/**
- * produce possible partially filtered stream saving menu items
+ * Returns the recommended file extension for the given cos stream.
+ */
+ private String getFileExtensionForStream(final COSStream cosStream, final TreePath nodePath)
+ {
+ String name = nodePath.getLastPathComponent().toString();
+ if (name.equals("FontFile"))
+ {
+ return "pfb";
+ }
+ else if (name.equals("FontFile2"))
+ {
+ return "ttf";
+ }
+ else if (name.equals("FontFile3"))
+ {
+ if (cosStream.getCOSName(COSName.SUBTYPE) == COSName.OPEN_TYPE)
+ {
+ return "otf";
+ }
+ else
+ {
+ return "cff";
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Produce JMenuItem that opens the stream with the system's default app.
+ */
+ private JMenuItem getFileOpenMenu(final COSStream cosStream, final TreePath nodePath)
+ {
+ // if we know the file type, create a system open menu
+ final String extension = getFileExtensionForStream(cosStream, nodePath);
+ if (extension == null)
+ {
+ return null;
+ }
+
+ JMenuItem openMenuItem = new JMenuItem("Open with Default Application");
+ openMenuItem.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent)
+ {
+ try
+ {
+ byte[] bytes = IOUtils.toByteArray(cosStream.createInputStream());
+ File temp = File.createTempFile("pdfbox", "." + extension);
+ temp.deleteOnExit();
+
+ FileOutputStream outputStream = null;
+ try
+ {
+ outputStream = new FileOutputStream(temp);
+ outputStream.write(bytes);
+
+ Desktop.getDesktop().open(temp);
+ }
+ finally
+ {
+ if (outputStream != null)
+ {
+ outputStream.close();
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ });
+ return openMenuItem;
+ }
+
+ /**
+ * produce possible partially decoded stream saving menu items
* @param cosStream stream to save
- * @return JMenuItems for saving partially filtered streams
+ * @return JMenuItems for saving partially decoded streams
*/
- private List getPartiallyFilteredStreamSaveMenu(final COSStream cosStream)
+ private List getPartiallyDecodedStreamSaveMenu(final COSStream cosStream)
{
List menuItems = new ArrayList();
PDStream stream = new PDStream(cosStream);
@@ -267,7 +397,7 @@ public void actionPerformed(ActionEvent actionEvent)
try
{
InputStream data = stream.createInputStream(stopFilters);
- saveStream(IOUtils.toByteArray(data));
+ saveStream(IOUtils.toByteArray(data), null, null);
}
catch (IOException e)
{
@@ -281,11 +411,12 @@ public void actionPerformed(ActionEvent actionEvent)
/**
* Save the stream.
* @param bytes byte array of the stream.
+ * @param filter an optional FileFilter
* @throws IOException if there is an error in creation of the file.
*/
- private void saveStream(byte[] bytes) throws IOException
+ private void saveStream(byte[] bytes, FileFilter filter, String extension) throws IOException
{
- FileOpenSaveDialog saveDialog = new FileOpenSaveDialog(parent, null);
- saveDialog.saveFile(bytes);
+ FileOpenSaveDialog saveDialog = new FileOpenSaveDialog(getParent(), filter);
+ saveDialog.saveFile(bytes, extension);
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/WindowPrefs.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/WindowPrefs.java
new file mode 100644
index 00000000000..42dac27915f
--- /dev/null
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/WindowPrefs.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.debugger.ui;
+
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.util.prefs.Preferences;
+
+/**
+ * A class to save windows position and size in preference using java Preference API.
+ *
+ * @author Tilman Hausherr
+ */
+public class WindowPrefs
+{
+ private static final String KEY = "window_prefs_";
+ private final Preferences pref;
+
+ public WindowPrefs(Class className)
+ {
+ this.pref = Preferences.userNodeForPackage(className);
+ }
+
+ public void setBounds(Rectangle rect)
+ {
+ Preferences node = pref.node(KEY);
+ node.putInt("X", rect.x);
+ node.putInt("Y", rect.y);
+ node.putInt("W", rect.width);
+ node.putInt("H", rect.height);
+ }
+
+ public Rectangle getBounds()
+ {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Preferences node = pref.node(KEY);
+ int x = node.getInt("X", screenSize.width / 4);
+ int y = node.getInt("Y", screenSize.height / 4);
+ int w = node.getInt("W", screenSize.width / 2);
+ int h = node.getInt("H", screenSize.height / 2);
+ return new Rectangle(x, y, w, h);
+ }
+
+ public void setDividerLocation(int divider)
+ {
+ Preferences node = pref.node(KEY);
+ node.putInt("DIV", divider);
+ }
+
+ public int getDividerLocation()
+ {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Preferences node = pref.node(KEY);
+ return node.getInt("DIV", screenSize.width / 8);
+ }
+
+ public void setExtendedState(int extendedState)
+ {
+ Preferences node = pref.node(KEY);
+ node.putInt("EXTSTATE", extendedState);
+ }
+
+ public int getExtendedState()
+ {
+ Preferences node = pref.node(KEY);
+ return node.getInt("EXTSTATE", Frame.NORMAL);
+ }
+}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ZoomMenu.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ZoomMenu.java
index f46de343c3e..a13679320c1 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ZoomMenu.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/ZoomMenu.java
@@ -31,6 +31,7 @@
*/
public final class ZoomMenu extends MenuBase
{
+ @SuppressWarnings("squid:MaximumInheritanceDepth")
private static class ZoomMenuItem extends JRadioButtonMenuItem
{
private final int zoom;
@@ -42,8 +43,9 @@ private static class ZoomMenuItem extends JRadioButtonMenuItem
}
}
- public static final String ZOOM_100_PERCENT = "100%";
- private static final int[] ZOOMS = new int[] { 25, 50, 100, 200, 400 };
+ private float pageZoomScale = 1;
+ private float imageZoomScale = 1;
+ private static final int[] ZOOMS = new int[] { 25, 50, 100, 150, 200, 400, 1000, 2000 };
private static ZoomMenu instance;
private final JMenu menu;
@@ -81,21 +83,22 @@ public static ZoomMenu getInstance()
/**
* Set the zoom selection.
*
- * @param selection zoom menu string, e.g. "100%".
+ * @param zoomValue e.g. 1, 0.25, 4.
* @throws IllegalArgumentException if the parameter doesn't belong to a zoom menu item.
*/
- public void setZoomSelection(String selection)
+ public void changeZoomSelection(float zoomValue)
{
+ int selection = (int) (zoomValue * 100);
for (Component comp : menu.getMenuComponents())
{
- JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) comp;
- if (menuItem.getText().equals(selection))
+ ZoomMenuItem menuItem = (ZoomMenuItem) comp;
+ if (menuItem.zoom == selection)
{
menuItem.setSelected(true);
return;
}
}
- throw new IllegalArgumentException("no zoom menu item found for: " + selection);
+ throw new IllegalArgumentException("no zoom menu item found for: " + selection + "%");
}
/**
@@ -132,4 +135,35 @@ public static float getZoomScale()
}
throw new IllegalStateException("no zoom menu item is selected");
}
+
+ public float getPageZoomScale()
+ {
+ return pageZoomScale;
+ }
+
+ public void setPageZoomScale(float pageZoomValue)
+ {
+ pageZoomScale = pageZoomValue;
+ }
+
+ public float getImageZoomScale()
+ {
+ return imageZoomScale;
+ }
+
+ public void setImageZoomScale(float imageZoomValue)
+ {
+ imageZoomScale = imageZoomValue;
+ }
+
+ /**
+ * When a new file is loaded zoom values should be reset.
+ *
+ */
+ public void resetZoom()
+ {
+ setPageZoomScale(1);
+ setImageZoomScale(1);
+ changeZoomSelection(1);
+ }
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/SearchEngine.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/SearchEngine.java
index 93d44ab36aa..5b038ab4f63 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/SearchEngine.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/SearchEngine.java
@@ -23,6 +23,8 @@
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
/**
* @author Khyrul Bashar
@@ -30,6 +32,8 @@
*/
class SearchEngine
{
+ private static final Log LOG = LogFactory.getLog(SearchEngine.class);
+
private final Document document;
private final Highlighter highlighter;
private final Highlighter.HighlightPainter painter;
@@ -73,7 +77,7 @@ public List search(String searchKey, boolean isCaseSensit
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
return highlights;
}
if (!isCaseSensitive)
@@ -97,7 +101,7 @@ public List search(String searchKey, boolean isCaseSensit
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
}
diff --git a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/Searcher.java b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/Searcher.java
index 5fe983b7c0c..718deea879c 100644
--- a/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/Searcher.java
+++ b/debugger/src/main/java/org/apache/pdfbox/debugger/ui/textsearcher/Searcher.java
@@ -34,6 +34,8 @@
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.debugger.PDFDebugger;
@@ -42,6 +44,8 @@
*/
public class Searcher implements DocumentListener, ChangeListener, ComponentListener
{
+ private static final Log LOG = LogFactory.getLog(Searcher.class);
+
private static final Highlighter.HighlightPainter PAINTER =
new DefaultHighlighter.DefaultHighlightPainter(Color.yellow);
private static final Highlighter.HighlightPainter SELECTION_PAINTER =
@@ -139,7 +143,7 @@ private void search(DocumentEvent documentEvent)
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
@@ -192,7 +196,7 @@ private void scrollToWord(int offset)
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
@@ -218,7 +222,7 @@ private void changeHighlighter(int index, Highlighter.HighlightPainter newPainte
}
catch (BadLocationException e)
{
- e.printStackTrace();
+ LOG.error(e.getMessage(), e);
}
}
@@ -234,13 +238,13 @@ public void stateChanged(ChangeEvent changeEvent)
@Override
public void componentResized(ComponentEvent componentEvent)
{
-
+ // do nothing
}
@Override
public void componentMoved(ComponentEvent componentEvent)
{
-
+ // do nothing
}
@Override
diff --git a/examples/pom.xml b/examples/pom.xml
index 189b33e754d..02878e6fa48 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -23,7 +23,7 @@
org.apache.pdfboxpdfbox-parent
- 2.0.0-SNAPSHOT
+ 2.0.25-SNAPSHOT../parent/pom.xml
@@ -42,6 +42,26 @@
+
+
+
+ [11,)
+
+
+
+ javax.xml.bind
+ jaxb-api
+ provided
+
+
+ javax.activation
+ activation
+ provided
+
+
+
+
+
org.bouncycastle
@@ -77,7 +97,8 @@
org.apache.antant
- 1.9.6
+
+ 1.9.15junit
@@ -85,9 +106,15 @@
test
- org.apache.wink
- wink-component-test-support
- 1.4
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ test
+
+
+ org.apache.geronimo.specs
+ geronimo-jaxrs_1.1_spec
+ 1.0test
@@ -106,9 +133,49 @@
src/test/resources/org/apache/pdfbox/examples/signature/*
+ src/main/resources/org/apache/pdfbox/resources/pdfa/sRGB.*
+
+ com.googlecode.maven-download-plugin
+ download-maven-plugin
+
+
+ testAddValidationInformation
+ generate-test-resources
+
+ wget
+
+
+ https://issues.apache.org/jira/secure/attachment/13014110/notCertified_368835_Sig_en_201026090509.pdf
+ ${project.build.directory}/pdfs
+ notCertified_368835_Sig_en_201026090509.pdf
+ eec730efc741d52ec2de5a26bb74e34fda3d01f8f547a92c4730478d4e35cd383c9c9f3c397e388ee0882166a18c1396d9576c6f9ab0a70c9551fb49c0ee3e6f
+
+
+
+ testDoubleVisibleSignatureOnEncryptedFile
+ generate-test-resources
+
+ wget
+
+
+ https://issues.apache.org/jira/secure/attachment/12682897/FormI-9-English.pdf
+ ${project.build.directory}/pdfs
+ PDFBOX-2469-1-AcroForm-AES128.pdf
+ a5067d67da88dcb3f2b6e63c6387d2fc7170db104d67e81de59d12e9e6b1ad473c0325411fc1cc235e12fbc56a37a67181f85b4e49cb208fbea0c0a01ebe6dd2
+
+
+
+
+
+
+ maven-surefire-plugin
+
+ ${addmod}
+
+
diff --git a/examples/src/main/appended-resources/META-INF/LICENSE b/examples/src/main/appended-resources/META-INF/LICENSE
index 839853550ce..77e285f61f5 100644
--- a/examples/src/main/appended-resources/META-INF/LICENSE
+++ b/examples/src/main/appended-resources/META-INF/LICENSE
@@ -169,14 +169,111 @@ The International Components for Unicode library (http://site.icu-project.org/)
copyright holder.
The file "sRGB Color Space Profile.icm" is:
-Copyright (c) 1998 Hewlett-Packard Company
-
-To anyone who acknowledges that the file "sRGB Color Space Profile.icm"
-is provided "AS IS" WITH NO EXPRESS OR IMPLIED WARRANTY:
-permission to use, copy and distribute this file for any purpose is hereby
-granted without fee, provided that the file is not changed including the HP
-copyright notice tag, and that the name of Hewlett-Packard Company not be
-used in advertising or publicity pertaining to distribution of the software
-without specific, written prior permission. Hewlett-Packard Company makes
-no representations about the suitability of this software for any purpose.
+ Copyright (c) 1998 Hewlett-Packard Company
+
+ To anyone who acknowledges that the file "sRGB Color Space Profile.icm"
+ is provided "AS IS" WITH NO EXPRESS OR IMPLIED WARRANTY:
+ permission to use, copy and distribute this file for any purpose is hereby
+ granted without fee, provided that the file is not changed including the HP
+ copyright notice tag, and that the name of Hewlett-Packard Company not be
+ used in advertising or publicity pertaining to distribution of the software
+ without specific, written prior permission. Hewlett-Packard Company makes
+ no representations about the suitability of this software for any purpose.
+
+Lohit-Bengali font (https://pagure.io/lohit):
+
+ Copyright 2011-13 Lohit Fonts Project contributors
+
+
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
+ This license is copied below, and is also available with a FAQ at:
+ http://scripts.sil.org/OFL
+
+
+ -----------------------------------------------------------
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+ -----------------------------------------------------------
+
+ PREAMBLE
+ The goals of the Open Font License (OFL) are to stimulate worldwide
+ development of collaborative font projects, to support the font creation
+ efforts of academic and linguistic communities, and to provide a free and
+ open framework in which fonts may be shared and improved in partnership
+ with others.
+
+ The OFL allows the licensed fonts to be used, studied, modified and
+ redistributed freely as long as they are not sold by themselves. The
+ fonts, including any derivative works, can be bundled, embedded,
+ redistributed and/or sold with any software provided that any reserved
+ names are not used by derivative works. The fonts and derivatives,
+ however, cannot be released under any other type of license. The
+ requirement for fonts to remain under this license does not apply
+ to any document created using the fonts or their derivatives.
+
+ DEFINITIONS
+ "Font Software" refers to the set of files released by the Copyright
+ Holder(s) under this license and clearly marked as such. This may
+ include source files, build scripts and documentation.
+
+ "Reserved Font Name" refers to any names specified as such after the
+ copyright statement(s).
+
+ "Original Version" refers to the collection of Font Software components as
+ distributed by the Copyright Holder(s).
+
+ "Modified Version" refers to any derivative made by adding to, deleting,
+ or substituting -- in part or in whole -- any of the components of the
+ Original Version, by changing formats or by porting the Font Software to a
+ new environment.
+
+ "Author" refers to any designer, engineer, programmer, technical
+ writer or other person who contributed to the Font Software.
+
+ PERMISSION & CONDITIONS
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
+ redistribute, and sell modified and unmodified copies of the Font
+ Software, subject to the following conditions:
+
+ 1) Neither the Font Software nor any of its individual components,
+ in Original or Modified Versions, may be sold by itself.
+
+ 2) Original or Modified Versions of the Font Software may be bundled,
+ redistributed and/or sold with any software, provided that each copy
+ contains the above copyright notice and this license. These can be
+ included either as stand-alone text files, human-readable headers or
+ in the appropriate machine-readable metadata fields within text or
+ binary files as long as those fields can be easily viewed by the user.
+
+ 3) No Modified Version of the Font Software may use the Reserved Font
+ Name(s) unless explicit written permission is granted by the corresponding
+ Copyright Holder. This restriction only applies to the primary font name as
+ presented to the users.
+
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ Software shall not be used to promote, endorse or advertise any
+ Modified Version, except to acknowledge the contribution(s) of the
+ Copyright Holder(s) and the Author(s) or with their explicit written
+ permission.
+
+ 5) The Font Software, modified or unmodified, in part or in whole,
+ must be distributed entirely under this license, and must not be
+ distributed under any other license. The requirement for fonts to
+ remain under this license does not apply to any document created
+ using the Font Software.
+
+ TERMINATION
+ This license becomes null and void if any of the above conditions are
+ not met.
+
+ DISCLAIMER
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+ OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/ant/PDFToTextTask.java b/examples/src/main/java/org/apache/pdfbox/examples/ant/PDFToTextTask.java
index cbcd875a892..5d508af544a 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/ant/PDFToTextTask.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/ant/PDFToTextTask.java
@@ -19,7 +19,6 @@
import java.io.File;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import org.apache.pdfbox.tools.ExtractText;
@@ -36,7 +35,7 @@
*/
public class PDFToTextTask extends Task
{
- private final List fileSets = new ArrayList();
+ private final List fileSets = new ArrayList();
/**
* Adds a set of files (nested fileset attribute).
@@ -55,11 +54,10 @@ public void addFileset( FileSet set )
public void execute()
{
log( "PDFToTextTask executing" );
- Iterator fileSetIter = fileSets.iterator();
- while( fileSetIter.hasNext() )
+
+ for (FileSet fileSet : fileSets)
{
- FileSet next = (FileSet)fileSetIter.next();
- DirectoryScanner dirScanner = next.getDirectoryScanner( getProject() );
+ DirectoryScanner dirScanner = fileSet.getDirectoryScanner(getProject());
dirScanner.scan();
String[] files = dirScanner.getIncludedFiles();
for (String file : files)
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/ant/package.html b/examples/src/main/java/org/apache/pdfbox/examples/ant/package.html
index 1d70ed480a7..6153e670443 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/ant/package.html
+++ b/examples/src/main/java/org/apache/pdfbox/examples/ant/package.html
@@ -15,20 +15,20 @@
! limitations under the License.
!-->
-
-
+
+ANT tasks that utilize PDFBox features can be found in this package.
-This is an example of using the PDF2Text task:
+This is an example of using the PDF2Text task:
-<taskdef name="pdf2text" classname="org.apache.pdfbox.ant.PDFToTextTask" classpathref="build.classpath" />
+<taskdef name="pdf2text" classname="org.apache.pdfbox.ant.PDFToTextTask" classpathref="build.classpath" />
-<pdf2text>
- <fileset dir="test">
- <include name="**/*.pdf" />
- </fileset>
-</pdf2text>
+<pdf2text>
+ <fileset dir="test">
+ <include name="**/*.pdf" />
+ </fileset>
+</pdf2text>
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/AddBorderToField.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/AddBorderToField.java
index 79acaf22272..fd6d8d11231 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/AddBorderToField.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/AddBorderToField.java
@@ -54,12 +54,12 @@ public static void main(String[] args) throws IOException
PDField field = acroForm.getField("SampleField");
PDAnnotationWidget widget = field.getWidgets().get(0);
- // Create the definition for a green border
- PDAppearanceCharacteristicsDictionary fieldAppearance =
+ // Create the definition for a red border
+ PDAppearanceCharacteristicsDictionary fieldAppearance =
new PDAppearanceCharacteristicsDictionary(new COSDictionary());
- PDColor green = new PDColor(new float[] { 0, 1, 0 }, PDDeviceRGB.INSTANCE);
- fieldAppearance.setBorderColour(green);
-
+ PDColor red = new PDColor(new float[] { 1, 0, 0 }, PDDeviceRGB.INSTANCE);
+ fieldAppearance.setBorderColour(red);
+
// Set the information to be used by the widget which is responsible
// for the visual style of the form field.
widget.setAppearanceCharacteristics(fieldAppearance);
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateCheckBox.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateCheckBox.java
new file mode 100644
index 00000000000..3cee71aba68
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateCheckBox.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.interactive.form;
+
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import org.apache.fontbox.afm.AFMParser;
+import org.apache.fontbox.afm.CharMetric;
+import org.apache.fontbox.afm.FontMetrics;
+import org.apache.fontbox.util.BoundingBox;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceEntry;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDCheckBox;
+
+/**
+ * Example to create a checkbox.
+ *
+ * @author Tilman Hausherr
+ */
+public class CreateCheckBox
+{
+ private CreateCheckBox()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument document = new PDDocument();
+ PDPage page = new PDPage();
+ document.addPage(page);
+
+ PDAcroForm acroForm = new PDAcroForm(document);
+ document.getDocumentCatalog().setAcroForm(acroForm);
+
+ // if you want to see what Adobe does, activate this, open with Adobe
+ // save the file, and then open it with PDFDebugger
+ //acroForm.setNeedAppearances(true)
+
+ float x = 50;
+ float y = page.getMediaBox().getHeight() - 50;
+
+ PDRectangle rect = new PDRectangle(x, y, 20, 20);
+
+ PDCheckBox checkbox = new PDCheckBox(acroForm);
+ checkbox.setPartialName("MyCheckBox");
+ PDAnnotationWidget widget = checkbox.getWidgets().get(0);
+ widget.setPage(page);
+ widget.setRectangle(rect);
+ widget.setPrinted(true);
+
+ PDAppearanceCharacteristicsDictionary appearanceCharacteristics = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
+ appearanceCharacteristics.setBorderColour(new PDColor(new float[]{1, 0, 0}, PDDeviceRGB.INSTANCE));
+ appearanceCharacteristics.setBackground(new PDColor(new float[]{1, 1, 0}, PDDeviceRGB.INSTANCE));
+ // 8 = cross; 4 = checkmark; H = star; u = diamond; n = square, l = dot
+ appearanceCharacteristics.setNormalCaption("4");
+ widget.setAppearanceCharacteristics(appearanceCharacteristics);
+
+ PDBorderStyleDictionary borderStyleDictionary = new PDBorderStyleDictionary();
+ borderStyleDictionary.setWidth(1);
+ borderStyleDictionary.setStyle(PDBorderStyleDictionary.STYLE_SOLID);
+ widget.setBorderStyle(borderStyleDictionary);
+
+ PDAppearanceDictionary ap = new PDAppearanceDictionary();
+ widget.setAppearance(ap);
+ PDAppearanceEntry normalAppearance = ap.getNormalAppearance();
+
+ COSDictionary normalAppearanceDict = (COSDictionary) normalAppearance.getCOSObject();
+ normalAppearanceDict.setItem(COSName.Off, createAppearanceStream(document, widget, false));
+ normalAppearanceDict.setItem(COSName.YES, createAppearanceStream(document, widget, true));
+
+ // If we ever decide to implement a /D (down) appearance, just
+ // replace the background colors c with c * 0.75
+ page.getAnnotations().add(checkbox.getWidgets().get(0));
+ acroForm.getFields().add(checkbox);
+
+ // always call check() or unCheck(), or the box will remain invisible.
+ checkbox.unCheck();
+
+ document.save("target/CheckBoxSample.pdf");
+ document.close();
+ }
+
+ private static PDAppearanceStream createAppearanceStream(
+ final PDDocument document, PDAnnotationWidget widget, boolean on) throws IOException
+ {
+ PDRectangle rect = widget.getRectangle();
+ PDAppearanceCharacteristicsDictionary appearanceCharacteristics;
+ PDAppearanceStream yesAP = new PDAppearanceStream(document);
+ yesAP.setBBox(new PDRectangle(rect.getWidth(), rect.getHeight()));
+ yesAP.setResources(new PDResources());
+ PDPageContentStream yesAPCS = new PDPageContentStream(document, yesAP);
+ appearanceCharacteristics = widget.getAppearanceCharacteristics();
+ PDColor backgroundColor = appearanceCharacteristics.getBackground();
+ PDColor borderColor = appearanceCharacteristics.getBorderColour();
+ float lineWidth = getLineWidth(widget);
+ yesAPCS.setLineWidth(lineWidth); // border style (dash) ignored
+ yesAPCS.setNonStrokingColor(backgroundColor);
+ yesAPCS.addRect(0, 0, rect.getWidth(), rect.getHeight());
+ yesAPCS.fill();
+ yesAPCS.setStrokingColor(borderColor);
+ yesAPCS.addRect(lineWidth / 2, lineWidth / 2, rect.getWidth() - lineWidth, rect.getHeight() - lineWidth);
+ yesAPCS.stroke();
+ if (!on)
+ {
+ yesAPCS.close();
+ return yesAP;
+ }
+
+ yesAPCS.addRect(lineWidth, lineWidth, rect.getWidth() - lineWidth * 2, rect.getHeight() - lineWidth * 2);
+ yesAPCS.clip();
+
+ String normalCaption = appearanceCharacteristics.getNormalCaption();
+ if (normalCaption == null)
+ {
+ normalCaption = "4"; // Adobe behaviour
+ }
+ if ("8".equals(normalCaption))
+ {
+ // Adobe paints a cross instead of using the Zapf Dingbats cross symbol
+ yesAPCS.setStrokingColor(0f);
+ yesAPCS.moveTo(lineWidth * 2, rect.getHeight() - lineWidth * 2);
+ yesAPCS.lineTo(rect.getWidth() - lineWidth * 2, lineWidth * 2);
+ yesAPCS.moveTo(rect.getWidth() - lineWidth * 2, rect.getHeight() - lineWidth * 2);
+ yesAPCS.lineTo(lineWidth * 2, lineWidth * 2);
+ yesAPCS.stroke();
+ }
+ else
+ {
+ Rectangle2D bounds = new Rectangle2D.Float();
+ String unicode = null;
+
+ // ZapfDingbats font may be missing or substituted, let's use AFM resources instead.
+ AFMParser parser = new AFMParser(PDType1Font.class.getResourceAsStream(
+ "/org/apache/pdfbox/resources/afm/ZapfDingbats.afm"));
+ FontMetrics metric = parser.parse();
+ for (CharMetric cm : metric.getCharMetrics())
+ {
+ // The caption is not unicode, but the Zapf Dingbats code in the PDF.
+ // Assume that only the first character is used.
+ if (normalCaption.codePointAt(0) == cm.getCharacterCode())
+ {
+ BoundingBox bb = cm.getBoundingBox();
+ bounds = new Rectangle2D.Float(bb.getLowerLeftX(), bb.getLowerLeftY(),
+ bb.getWidth(), bb.getHeight());
+ unicode = PDType1Font.ZAPF_DINGBATS.getGlyphList().toUnicode(cm.getName());
+ break;
+ }
+ }
+ if (bounds.isEmpty())
+ {
+ throw new IOException("Bounds rectangle for chosen glyph is empty");
+ }
+ float size = (float) Math.min(bounds.getWidth(), bounds.getHeight()) / 1000;
+ // assume that checkmark has square size
+ // the calculations approximate what Adobe is doing, i.e. put the glyph in the middle
+ float fontSize = (rect.getWidth() - lineWidth * 2) / size * 0.6666f;
+ float xOffset = (float) (rect.getWidth() - (bounds.getWidth()) / 1000 * fontSize) / 2;
+ xOffset -= bounds.getX() / 1000 * fontSize;
+ float yOffset = (float) (rect.getHeight() - (bounds.getHeight()) / 1000 * fontSize) / 2;
+ yOffset -= bounds.getY() / 1000 * fontSize;
+ yesAPCS.setNonStrokingColor(0f);
+ yesAPCS.beginText();
+ yesAPCS.setFont(PDType1Font.ZAPF_DINGBATS, fontSize);
+ yesAPCS.newLineAtOffset(xOffset, yOffset);
+ yesAPCS.showText(unicode);
+ yesAPCS.endText();
+ }
+ yesAPCS.close();
+ return yesAP;
+ }
+
+ static float getLineWidth(PDAnnotationWidget widget)
+ {
+ PDBorderStyleDictionary bs = widget.getBorderStyle();
+ if (bs != null)
+ {
+ return bs.getWidth();
+ }
+ return 1;
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateMultiWidgetsForm.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateMultiWidgetsForm.java
new file mode 100644
index 00000000000..250b6c75437
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateMultiWidgetsForm.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.interactive.form;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
+
+/**
+ * An example of creating an AcroForm and a form field from scratch, with two widgets for one field:
+ * This means that the same field is visible on two separate pages, but can be on different
+ * positions and different size and colors. Changing the value on one page will also change it on
+ * the other page.
+ *
+ * The form field is created with properties similar to creating a form with default settings in
+ * Adobe Acrobat.
+ *
+ */
+public final class CreateMultiWidgetsForm
+{
+ private CreateMultiWidgetsForm()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ // Create a new document with 2 empty pages.
+ PDDocument document = new PDDocument();
+ PDPage page1 = new PDPage(PDRectangle.A4);
+ document.addPage(page1);
+ PDPage page2 = new PDPage(PDRectangle.A4);
+ document.addPage(page2);
+
+ // Adobe Acrobat uses Helvetica as a default font and
+ // stores that under the name '/Helv' in the resources dictionary
+ PDFont font = PDType1Font.HELVETICA;
+ PDResources resources = new PDResources();
+ resources.put(COSName.getPDFName("Helv"), font);
+
+ // Add a new AcroForm and add that to the document
+ PDAcroForm acroForm = new PDAcroForm(document);
+ document.getDocumentCatalog().setAcroForm(acroForm);
+
+ // Add and set the resources and default appearance at the form level
+ acroForm.setDefaultResources(resources);
+
+ // Acrobat sets the font size on the form level to be
+ // auto sized as default. This is done by setting the font size to '0'
+ String defaultAppearanceString = "/Helv 0 Tf 0 g";
+ acroForm.setDefaultAppearance(defaultAppearanceString);
+
+ // Add a form field to the form.
+ PDTextField textBox = new PDTextField(acroForm);
+ textBox.setPartialName("SampleField");
+ // Acrobat sets the font size to 12 as default
+ // This is done by setting the font size to '12' on the
+ // field level.
+ // The text color is set to blue in this example.
+ // To use black, replace "0 0 1 rg" with "0 0 0 rg" or "0 g".
+ defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
+ textBox.setDefaultAppearance(defaultAppearanceString);
+
+ // add the field to the AcroForm
+ acroForm.getFields().add(textBox);
+
+ // Specify 1st annotation associated with the field
+ PDAnnotationWidget widget1 = new PDAnnotationWidget();
+ PDRectangle rect = new PDRectangle(50, 750, 250, 50);
+ widget1.setRectangle(rect);
+ widget1.setPage(page1);
+ widget1.setParent(textBox);
+
+ // Specify 2nd annotation associated with the field
+ PDAnnotationWidget widget2 = new PDAnnotationWidget();
+ PDRectangle rect2 = new PDRectangle(200, 650, 100, 50);
+ widget2.setRectangle(rect2);
+ widget2.setPage(page2);
+ widget2.setParent(textBox);
+
+ // set green border and yellow background for 1st widget
+ // if you prefer defaults, delete this code block
+ PDAppearanceCharacteristicsDictionary fieldAppearance1
+ = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
+ fieldAppearance1.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
+ fieldAppearance1.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
+ widget1.setAppearanceCharacteristics(fieldAppearance1);
+
+ // set red border and green background for 2nd widget
+ // if you prefer defaults, delete this code block
+ PDAppearanceCharacteristicsDictionary fieldAppearance2
+ = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
+ fieldAppearance2.setBorderColour(new PDColor(new float[]{1,0,0}, PDDeviceRGB.INSTANCE));
+ fieldAppearance2.setBackground(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
+ widget2.setAppearanceCharacteristics(fieldAppearance2);
+
+ List widgets = new ArrayList();
+ widgets.add(widget1);
+ widgets.add(widget2);
+ textBox.setWidgets(widgets);
+
+ // make sure the annotations are visible on screen and paper
+ widget1.setPrinted(true);
+ widget2.setPrinted(true);
+
+ // Add the annotations to the pages
+ page1.getAnnotations().add(widget1);
+ page2.getAnnotations().add(widget2);
+
+ // set the field value
+ textBox.setValue("Sample field");
+
+ document.save("target/MultiWidgetsForm.pdf");
+ document.close();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateRadioButtons.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateRadioButtons.java
new file mode 100644
index 00000000000..cdf18ab7fd0
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateRadioButtons.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.interactive.form;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceEntry;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDRadioButton;
+
+/**
+ * Example to create radio buttons.
+ *
+ * @author Tilman Hausherr
+ */
+public class CreateRadioButtons
+{
+ private CreateRadioButtons()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument document = new PDDocument();
+ PDPage page = new PDPage(PDRectangle.A4);
+ document.addPage(page);
+ PDAcroForm acroForm = new PDAcroForm(document);
+
+ // if you want to see what Adobe does, activate this, open with Adobe
+ // save the file, and then open it with PDFDebugger
+
+ //acroForm.setNeedAppearances(true)
+
+ document.getDocumentCatalog().setAcroForm(acroForm);
+ List options = Arrays.asList("a", "b", "c");
+ PDRadioButton radioButton = new PDRadioButton(acroForm);
+ radioButton.setPartialName("MyRadioButton");
+ radioButton.setExportValues(options);
+
+ PDAppearanceCharacteristicsDictionary appearanceCharacteristics = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
+ appearanceCharacteristics.setBorderColour(new PDColor(new float[] { 1, 0, 0 }, PDDeviceRGB.INSTANCE));
+ appearanceCharacteristics.setBackground(new PDColor(new float[]{0, 1, 0.3f}, PDDeviceRGB.INSTANCE));
+ // no caption => round
+ // with caption => see checkbox example
+
+ List widgets = new ArrayList();
+ for (int i = 0; i < options.size(); i++)
+ {
+ PDAnnotationWidget widget = new PDAnnotationWidget();
+ widget.setRectangle(new PDRectangle(30, PDRectangle.A4.getHeight() - 40 - i * 35, 30, 30));
+ widget.setAppearanceCharacteristics(appearanceCharacteristics);
+ PDBorderStyleDictionary borderStyleDictionary = new PDBorderStyleDictionary();
+ borderStyleDictionary.setWidth(2);
+ borderStyleDictionary.setStyle(PDBorderStyleDictionary.STYLE_SOLID);
+ widget.setBorderStyle(borderStyleDictionary);
+ widget.setPage(page);
+
+ COSDictionary apNDict = new COSDictionary();
+ apNDict.setItem(COSName.Off, createAppearanceStream(document, widget, false));
+ apNDict.setItem(options.get(i), createAppearanceStream(document, widget, true));
+
+ PDAppearanceDictionary appearance = new PDAppearanceDictionary();
+ PDAppearanceEntry appearanceNEntry = new PDAppearanceEntry(apNDict);
+ appearance.setNormalAppearance(appearanceNEntry);
+ widget.setAppearance(appearance);
+ widget.setAppearanceState("Off"); // don't forget this, or button will be invisible
+ widgets.add(widget);
+ page.getAnnotations().add(widget);
+ }
+ radioButton.setWidgets(widgets);
+
+ acroForm.getFields().add(radioButton);
+
+ // Set the texts
+ PDPageContentStream contents = new PDPageContentStream(document, page);
+ for (int i = 0; i < options.size(); i++)
+ {
+ contents.beginText();
+ contents.setFont(PDType1Font.HELVETICA, 15);
+ contents.newLineAtOffset(70, PDRectangle.A4.getHeight() - 30 - i * 35);
+ contents.showText(options.get(i));
+ contents.endText();
+ }
+ contents.close();
+
+ radioButton.setValue("c");
+
+ document.save("target/RadioButtonsSample.pdf");
+ document.close();
+ }
+
+ private static PDAppearanceStream createAppearanceStream(
+ final PDDocument document, PDAnnotationWidget widget, boolean on) throws IOException
+ {
+ PDRectangle rect = widget.getRectangle();
+ PDAppearanceStream onAP = new PDAppearanceStream(document);
+ onAP.setBBox(new PDRectangle(rect.getWidth(), rect.getHeight()));
+ PDPageContentStream onAPCS = new PDPageContentStream(document, onAP);
+
+ PDAppearanceCharacteristicsDictionary appearanceCharacteristics = widget.getAppearanceCharacteristics();
+ PDColor backgroundColor = appearanceCharacteristics.getBackground();
+ PDColor borderColor = appearanceCharacteristics.getBorderColour();
+ float lineWidth = getLineWidth(widget);
+ onAPCS.setLineWidth(lineWidth); // border style (dash) ignored
+ onAPCS.setNonStrokingColor(backgroundColor);
+ float radius = Math.min(rect.getWidth() / 2, rect.getHeight() / 2);
+ drawCircle(onAPCS, rect.getWidth() / 2, rect.getHeight() / 2, radius);
+ onAPCS.fill();
+ onAPCS.setStrokingColor(borderColor);
+ drawCircle(onAPCS, rect.getWidth() / 2, rect.getHeight() / 2, radius - lineWidth / 2);
+ onAPCS.stroke();
+ if (on)
+ {
+ onAPCS.setNonStrokingColor(0f);
+ drawCircle(onAPCS, rect.getWidth() / 2, rect.getHeight() / 2, (radius - lineWidth) / 2);
+ onAPCS.fill();
+ }
+
+ onAPCS.close();
+ return onAP;
+ }
+
+ static float getLineWidth(PDAnnotationWidget widget)
+ {
+ PDBorderStyleDictionary bs = widget.getBorderStyle();
+ if (bs != null)
+ {
+ return bs.getWidth();
+ }
+ return 1;
+ }
+
+ static void drawCircle(PDPageContentStream cs, float x, float y, float r) throws IOException
+ {
+ // http://stackoverflow.com/a/2007782/535646
+ float magic = r * 0.551784f;
+ cs.moveTo(x, y + r);
+ cs.curveTo(x + magic, y + r, x + r, y + magic, x + r, y);
+ cs.curveTo(x + r, y - magic, x + magic, y - r, x, y - r);
+ cs.curveTo(x - magic, y - r, x - r, y - magic, x - r, y);
+ cs.curveTo(x - r, y + magic, x - magic, y + r, x, y + r);
+ cs.closePath();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleForm.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleForm.java
index 0cca6556c59..5e60f66cac6 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleForm.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleForm.java
@@ -18,16 +18,22 @@
package org.apache.pdfbox.examples.interactive.form;
import java.io.IOException;
+import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
+import org.apache.pdfbox.pdmodel.interactive.form.PDVariableText;
/**
* An example of creating an AcroForm and a form field from scratch.
@@ -72,24 +78,49 @@ public static void main(String[] args) throws IOException
textBox.setPartialName("SampleField");
// Acrobat sets the font size to 12 as default
// This is done by setting the font size to '12' on the
- // field level.
- defaultAppearanceString = "/Helv 12 Tf 0 g";
+ // field level.
+ // The text color is set to blue in this example.
+ // To use black, replace "0 0 1 rg" with "0 0 0 rg" or "0 g".
+ defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
textBox.setDefaultAppearance(defaultAppearanceString);
-
+
// add the field to the acroform
acroForm.getFields().add(textBox);
-
- // Specify the annotation associated with the field
+
+ // Specify the widget annotation associated with the field
PDAnnotationWidget widget = textBox.getWidgets().get(0);
PDRectangle rect = new PDRectangle(50, 750, 200, 50);
widget.setRectangle(rect);
widget.setPage(page);
+
+ // set green border and yellow background
+ // if you prefer defaults, delete this code block
+ PDAppearanceCharacteristicsDictionary fieldAppearance
+ = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
+ fieldAppearance.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
+ fieldAppearance.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
+ widget.setAppearanceCharacteristics(fieldAppearance);
+
+ // make sure the widget annotation is visible on screen and paper
+ widget.setPrinted(true);
- // Add the annotation to the page
+ // Add the widget annotation to the page
page.getAnnotations().add(widget);
-
+
+ // set the alignment ("quadding")
+ textBox.setQ(PDVariableText.QUADDING_CENTERED);
+
// set the field value
- textBox.setValue("Sample field");
+ textBox.setValue("Sample field content");
+
+ // put some text near the field
+ PDPageContentStream cs = new PDPageContentStream(document, page);
+ cs.beginText();
+ cs.setFont(PDType1Font.HELVETICA, 15);
+ cs.newLineAtOffset(50, 810);
+ cs.showText("Field:");
+ cs.endText();
+ cs.close();
document.save("target/SimpleForm.pdf");
document.close();
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleFormWithEmbeddedFont.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleFormWithEmbeddedFont.java
new file mode 100644
index 00000000000..d232ed392d1
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/CreateSimpleFormWithEmbeddedFont.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.interactive.form;
+
+import java.io.IOException;
+import org.apache.pdfbox.cos.COSDictionary;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
+
+/**
+ * An example of creating an AcroForm and a form field from scratch with a font fully embedded to
+ * allow non-WinAnsiEncoding input.
+ *
+ * The form field is created with properties similar to creating a form with default settings in
+ * Adobe Acrobat.
+ *
+ */
+public class CreateSimpleFormWithEmbeddedFont
+{
+ private CreateSimpleFormWithEmbeddedFont()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ // Create a new document with an empty page.
+ PDDocument doc = new PDDocument();
+ PDPage page = new PDPage(PDRectangle.A4);
+ doc.addPage(page);
+ PDAcroForm acroForm = new PDAcroForm(doc);
+ doc.getDocumentCatalog().setAcroForm(acroForm);
+
+ // Note that the font is fully embedded. If you use a different font, make sure that
+ // its license allows full embedding.
+ PDFont formFont = PDType0Font.load(doc, CreateSimpleFormWithEmbeddedFont.class.getResourceAsStream(
+ "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf"), false);
+
+ // Add and set the resources and default appearance at the form level
+ final PDResources resources = new PDResources();
+ acroForm.setDefaultResources(resources);
+ final String fontName = resources.add(formFont).getName();
+
+ // Acrobat sets the font size on the form level to be
+ // auto sized as default. This is done by setting the font size to '0'
+ acroForm.setDefaultResources(resources);
+ String defaultAppearanceString = "/" + fontName + " 0 Tf 0 g";
+
+ PDTextField textBox = new PDTextField(acroForm);
+ textBox.setPartialName("SampleField");
+ textBox.setDefaultAppearance(defaultAppearanceString);
+ acroForm.getFields().add(textBox);
+
+ // Specify the widget annotation associated with the field
+ PDAnnotationWidget widget = textBox.getWidgets().get(0);
+ PDRectangle rect = new PDRectangle(50, 700, 200, 50);
+ widget.setRectangle(rect);
+ widget.setPage(page);
+ page.getAnnotations().add(widget);
+
+ // set green border and yellow background
+ // if you prefer defaults, delete this code block
+ PDAppearanceCharacteristicsDictionary fieldAppearance
+ = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
+ fieldAppearance.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
+ fieldAppearance.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
+ widget.setAppearanceCharacteristics(fieldAppearance);
+
+ // set the field value. Note that the last character is a turkish capital I with a dot,
+ // which is not part of WinAnsiEncoding
+ textBox.setValue("Sample field İ");
+
+ // put some text near the field
+ PDPageContentStream cs = new PDPageContentStream(doc, page);
+ cs.beginText();
+ cs.setFont(PDType1Font.HELVETICA, 15);
+ cs.newLineAtOffset(50, 760);
+ cs.showText("Field:");
+ cs.endText();
+ cs.close();
+
+ doc.save("target/SimpleFormWithEmbeddedFont.pdf");
+ doc.close();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/FillFormField.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/FillFormField.java
index 045867b4499..c391ce8ef66 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/FillFormField.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/FillFormField.java
@@ -46,7 +46,7 @@ public static void main(String[] args) throws IOException
// as there might not be an AcroForm entry a null check is necessary
if (acroForm != null)
{
- // Retrieve an individual field and set it's value.
+ // Retrieve an individual field and set its value.
PDTextField field = (PDTextField) acroForm.getField( "sampleField" );
field.setValue("Text Entry");
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/PrintFields.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/PrintFields.java
index 65e151bd4ec..319122c1f34 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/PrintFields.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/PrintFields.java
@@ -61,12 +61,9 @@ private void processField(PDField field, String sLevel, String sParent) throws I
if (field instanceof PDNonTerminalField)
{
- if (!sParent.equals(field.getPartialName()))
+ if (!sParent.equals(field.getPartialName()) && partialName != null)
{
- if (partialName != null)
- {
- sParent = sParent + "." + partialName;
- }
+ sParent = sParent + "." + partialName;
}
System.out.println(sLevel + sParent);
@@ -91,7 +88,7 @@ private void processField(PDField field, String sLevel, String sParent) throws I
}
/**
- * This will read a PDF file and print out the form elements.
+ * This will read a PDF file and print out the form elements.
* see usage() for commandline
*
* @param args command line arguments
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/SetField.java b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/SetField.java
index 08a2e809b5b..7cc1928f897 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/SetField.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/interactive/form/SetField.java
@@ -54,7 +54,15 @@ public void setField(PDDocument pdfDocument, String name, String value) throws I
{
if (field instanceof PDCheckBox)
{
- field.setValue("Yes");
+ PDCheckBox checkbox = (PDCheckBox) field;
+ if (value.isEmpty())
+ {
+ checkbox.unCheck();
+ }
+ else
+ {
+ checkbox.check();
+ }
}
else if (field instanceof PDComboBox)
{
@@ -81,7 +89,7 @@ else if (field instanceof PDTextField)
/**
* This will read a PDF file and set a field and then write it the pdf out
- * again.
+ * again.
* see usage() for commandline
*
* @param args command line arguments
@@ -108,7 +116,7 @@ private void setField(String[] args) throws IOException
SetField example = new SetField();
pdf = PDDocument.load(new File(args[0]));
example.setField(pdf, args[1], args[2]);
- pdf.save(args[0]);
+ pdf.save(calculateOutputFilename(args[0]));
}
}
finally
@@ -120,6 +128,21 @@ private void setField(String[] args) throws IOException
}
}
+ private static String calculateOutputFilename(String filename)
+ {
+ String outputFilename;
+ if (filename.toLowerCase().endsWith(".pdf"))
+ {
+ outputFilename = filename.substring(0, filename.length() - 4);
+ }
+ else
+ {
+ outputFilename = filename;
+ }
+ outputFilename += "_filled.pdf";
+ return outputFilename;
+ }
+
/**
* This will print out a message telling how to use this example.
*/
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/lucene/LucenePDFDocument.java b/examples/src/main/java/org/apache/pdfbox/examples/lucene/LucenePDFDocument.java
index 55b4db254ba..058a0d64fcb 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/lucene/LucenePDFDocument.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/lucene/LucenePDFDocument.java
@@ -43,6 +43,7 @@
* This class is used to create a document for the lucene search engine. This should easily plug into the IndexPDFFiles
* that comes with the lucene project. This class will populate the following fields.
*
+ *
*
*
Lucene Field Name
*
Description
@@ -414,7 +415,7 @@ private void addContent(Document document, InputStream is, String documentLocati
}
catch (InvalidPasswordException e)
{
- // they didn't suppply a password and the default of "" was wrong.
+ // they didn't supply a password and the default of "" was wrong.
throw new IOException("Error: The document(" + documentLocation + ") is encrypted and will not be indexed.", e);
}
finally
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/lucene/package.html b/examples/src/main/java/org/apache/pdfbox/examples/lucene/package.html
index 8607cfa536d..fd30787858c 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/lucene/package.html
+++ b/examples/src/main/java/org/apache/pdfbox/examples/lucene/package.html
@@ -15,8 +15,8 @@
! limitations under the License.
!-->
-
-
+
+
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddAnnotations.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddAnnotations.java
index e7360fc5737..bb2de06155a 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddAnnotations.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddAnnotations.java
@@ -18,6 +18,9 @@
import java.io.IOException;
import java.util.List;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSFloat;
+import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
@@ -26,13 +29,17 @@
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLine;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationSquareCircle;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
/**
* Add annotations to pages of a PDF document.
@@ -56,13 +63,18 @@ public static void main(String[] args) throws IOException
PDDocument document = new PDDocument();
try
{
- PDPage page = new PDPage();
- document.addPage(page);
- List annotations = page.getAnnotations();
+ PDPage page1 = new PDPage();
+ PDPage page2 = new PDPage();
+ PDPage page3 = new PDPage();
+ document.addPage(page1);
+ document.addPage(page2);
+ document.addPage(page3);
+ List annotations = page1.getAnnotations();
// Some basic reusable objects/constants
// Annotations themselves can only be used once!
PDColor red = new PDColor(new float[] { 1, 0, 0 }, PDDeviceRGB.INSTANCE);
+ PDColor green = new PDColor(new float[] { 0, 1, 0 }, PDDeviceRGB.INSTANCE);
PDColor blue = new PDColor(new float[] { 0, 0, 1 }, PDDeviceRGB.INSTANCE);
PDColor black = new PDColor(new float[] { 0, 0, 0 }, PDDeviceRGB.INSTANCE);
@@ -76,18 +88,20 @@ public static void main(String[] args) throws IOException
borderULine.setStyle(PDBorderStyleDictionary.STYLE_UNDERLINE);
borderULine.setWidth(INCH / 72); // 1 point
- float pw = page.getMediaBox().getUpperRightX();
- float ph = page.getMediaBox().getUpperRightY();
+ float pw = page1.getMediaBox().getUpperRightX();
+ float ph = page1.getMediaBox().getUpperRightY();
// First add some text, two lines we'll add some annotations to this later
PDFont font = PDType1Font.HELVETICA_BOLD;
- PDPageContentStream contents = new PDPageContentStream(document, page);
+ PDPageContentStream contents = new PDPageContentStream(document, page1);
contents.beginText();
contents.setFont(font, 18);
contents.newLineAtOffset(INCH, ph - INCH - 18);
contents.showText("PDFBox");
contents.newLineAtOffset(0, -(INCH / 2));
- contents.showText("Click Here");
+ contents.showText("External URL");
+ contents.newLineAtOffset(0, -(INCH / 2));
+ contents.showText("Jump to page three");
contents.endText();
contents.close();
@@ -95,14 +109,17 @@ public static void main(String[] args) throws IOException
PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(
PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT);
txtMark.setColor(blue);
- txtMark.setConstantOpacity((float)0.2); // 20% transparent
+
+ // remove line below if PDF/A-2b (and possibly other PDF-A flavours)
+ // also add txtMark.setPrinted(true)
+ txtMark.setConstantOpacity((float) 0.2); // 20% transparent
// Set the rectangle containing the markup
float textWidth = font.getStringWidth("PDFBox") / 1000 * 18;
PDRectangle position = new PDRectangle();
position.setLowerLeftX(INCH);
position.setLowerLeftY(ph - INCH - 18);
- position.setUpperRightX(72 + textWidth);
+ position.setUpperRightX(INCH + textWidth);
position.setUpperRightY(ph - INCH);
txtMark.setRectangle(position);
@@ -124,16 +141,16 @@ public static void main(String[] args) throws IOException
txtMark.setContents("Highlighted since it's important");
annotations.add(txtMark);
- // Now add the link annotation, so the clickme works
+ // Now add the link annotation, so the click on "External URL" works
PDAnnotationLink txtLink = new PDAnnotationLink();
txtLink.setBorderStyle(borderULine);
// Set the rectangle containing the link
- textWidth = font.getStringWidth("Click Here") / 1000 * 18;
+ textWidth = font.getStringWidth("External URL") / 1000 * 18;
position = new PDRectangle();
position.setLowerLeftX(INCH);
position.setLowerLeftY(ph - 1.5f * INCH -20); // down a couple of points
- position.setUpperRightX(72 + textWidth);
+ position.setUpperRightX(INCH + textWidth);
position.setUpperRightY(ph - 1.5f * INCH);
txtLink.setRectangle(position);
@@ -168,7 +185,7 @@ public static void main(String[] args) throws IOException
aSquare.setColor(red); // Outline in red, not setting a fill
aSquare.setBorderStyle(borderThick);
- // Place the annotation on the page, we'll make this 1" (72points) square
+ // Place the annotation on the page, we'll make this 1" (72 points) square
// 3.5" down, 1" in from the right on the page
position = new PDRectangle(); // Reuse the variable, but note it's a new object!
position.setLowerLeftX(pw - 2 * INCH); // 1" in from right, 1" wide
@@ -203,7 +220,103 @@ public static void main(String[] args) throws IOException
aLine.setBorderStyle(borderThick);
aLine.setColor(black);
annotations.add(aLine);
+
+
+ // Now add the link annotation, so the click on "Jump to page three" works
+ PDAnnotationLink pageLink = new PDAnnotationLink();
+ pageLink.setBorderStyle(borderULine);
+
+ // Set the rectangle containing the link
+ textWidth = font.getStringWidth("Jump to page three") / 1000 * 18;
+ position = new PDRectangle();
+ position.setLowerLeftX(INCH);
+ position.setLowerLeftY(ph - 2 * INCH - 20); // down a couple of points
+ position.setUpperRightX(INCH + textWidth);
+ position.setUpperRightY(ph - 2 * INCH);
+ pageLink.setRectangle(position);
+ // add the GoTo action
+ PDActionGoTo actionGoto = new PDActionGoTo();
+ // see javadoc for other types of PDPageDestination
+ PDPageDestination dest = new PDPageFitWidthDestination();
+ // do not use setPageNumber(), this is for external destinations only
+ dest.setPage(page3);
+ actionGoto.setDestination(dest);
+ pageLink.setAction(actionGoto);
+ annotations.add(pageLink);
+
+ PDAnnotationMarkup freeTextAnnotation = new PDAnnotationMarkup();
+ freeTextAnnotation.getCOSObject().setName(COSName.SUBTYPE, PDAnnotationMarkup.SUB_TYPE_FREETEXT);
+ PDColor yellow = new PDColor(new float[] { 1, 1, 0 }, PDDeviceRGB.INSTANCE);
+ // this sets background only (contradicts PDF specification)
+ freeTextAnnotation.setColor(yellow);
+ position = new PDRectangle();
+ position.setLowerLeftX(1 * INCH);
+ position.setLowerLeftY(ph - 5f * INCH - 3 * INCH);
+ position.setUpperRightX(pw - INCH);
+ position.setUpperRightY(ph - 5f * INCH);
+ freeTextAnnotation.setRectangle(position);
+ freeTextAnnotation.setTitlePopup("Sophia Lorem");
+ freeTextAnnotation.setSubject("Lorem ipsum");
+ freeTextAnnotation.setContents("Lorem ipsum dolor sit amet, consetetur sadipscing elitr,"
+ + " sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam "
+ + "erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea "
+ + "rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum "
+ + "dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, "
+ + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam "
+ + "erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea "
+ + "rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum "
+ + "dolor sit amet.");
+ // Text and border in blue RGB color, "Helv" font, 20 point
+ // setDefaultAppearance() missing in 2.0
+ freeTextAnnotation.getCOSObject().setString(COSName.DA, "0 0 1 rg /Helv 20 Tf");
+ freeTextAnnotation.setIntent("FreeTextCallout");
+ COSArray newCallout = new COSArray();
+ newCallout.setFloatArray(new float[]{0, ph - 9 * INCH, 3 * INCH, ph - 9 * INCH, 4 * INCH, ph - 8 * INCH});
+ // setCallout() missing in 2.0
+ freeTextAnnotation.getCOSObject().setItem(COSName.CL, newCallout);
+ freeTextAnnotation.getCOSObject();
+ // setLineEndingStyle() missing in 2.0
+ freeTextAnnotation.getCOSObject().setName(COSName.LE, PDAnnotationLine.LE_OPEN_ARROW);
+ annotations.add(freeTextAnnotation);
+
+ // create a polygon annotation. Yes this is clunky, it will be easier in 3.0
+ PDAnnotationMarkup polygon = new PDAnnotationMarkup();
+ polygon.getCOSObject().setName(COSName.SUBTYPE, PDAnnotationMarkup.SUB_TYPE_POLYGON);
+ position = new PDRectangle();
+ position.setLowerLeftX(pw - INCH);
+ position.setLowerLeftY(ph - INCH);
+ position.setUpperRightX(pw - 2 * INCH);
+ position.setUpperRightY(ph - 2 * INCH);
+ polygon.setRectangle(position);
+ polygon.setColor(blue); // border color
+ polygon.getCOSObject().setItem(COSName.IC, green.toCOSArray()); // interior color
+ COSArray verticesArray = new COSArray();
+ verticesArray.add(new COSFloat(pw - INCH));
+ verticesArray.add(new COSFloat(ph - 2 * INCH));
+ verticesArray.add(new COSFloat(pw - INCH * 1.5f));
+ verticesArray.add(new COSFloat(ph - INCH));
+ verticesArray.add(new COSFloat(pw - 2 * INCH));
+ verticesArray.add(new COSFloat(ph - 2 * INCH));
+ polygon.getCOSObject().setItem(COSName.VERTICES, verticesArray);
+ polygon.setBorderStyle(borderThick);
+ polygon.setContents("Polygon annotation");
+ annotations.add(polygon);
+
+ // Create the appearance streams.
+ // Adobe Reader will always display annotations without appearance streams nicely,
+ // but other applications may not.
+ // Pass the PDDocument so that the appearance handler can look into the default resources
+ // for non-standard fonts.
+ for (PDAnnotation ann : annotations)
+ {
+ ann.constructAppearances(document);
+ }
+
+ showPageNo(document, page1, "Page 1");
+ showPageNo(document, page2, "Page 2");
+ showPageNo(document, page3, "Page 3");
+
// save the PDF
document.save(args[0]);
}
@@ -212,4 +325,23 @@ public static void main(String[] args) throws IOException
document.close();
}
}
+
+ private static void showPageNo(PDDocument document, PDPage page, String pageText)
+ throws IOException
+ {
+ int fontSize = 10;
+
+ PDPageContentStream contents =
+ new PDPageContentStream(document, page, PDPageContentStream.AppendMode.PREPEND, true);
+ float pageWidth = page.getMediaBox().getWidth();
+ float pageHeight = page.getMediaBox().getHeight();
+ PDFont font = PDType1Font.HELVETICA;
+ contents.setFont(font, fontSize);
+ float textWidth = font.getStringWidth(pageText) / 1000 * fontSize;
+ contents.beginText();
+ contents.newLineAtOffset(pageWidth / 2 - textWidth / 2, pageHeight - INCH / 2);
+ contents.showText(pageText);
+ contents.endText();
+ contents.close();
+ }
}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddImageToPDF.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddImageToPDF.java
index e6ca5b1f637..9b4446af26e 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddImageToPDF.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddImageToPDF.java
@@ -58,7 +58,7 @@ public void createPDFFromImage( String inputFile, String imagePath, String outpu
// if you already have the image in a BufferedImage,
// call LosslessFactory.createFromImage() instead
PDImageXObject pdImage = PDImageXObject.createFromFile(imagePath, doc);
- PDPageContentStream contentStream = new PDPageContentStream(doc, page, AppendMode.APPEND, true);
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page, AppendMode.APPEND, true, true);
// contentStream.drawImage(ximage, 20, 20 );
// better method inspired by http://stackoverflow.com/a/22318681/535646
@@ -80,7 +80,7 @@ public void createPDFFromImage( String inputFile, String imagePath, String outpu
/**
* This will load a PDF document and add a single image on it.
- *
+ *
* see usage() for commandline
*
* @param args Command line arguments.
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java
index 56d3fc79583..434d70be8ee 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java
@@ -16,6 +16,7 @@
*/
package org.apache.pdfbox.examples.pdmodel;
+import java.awt.Color;
import java.io.File;
import java.io.IOException;
@@ -81,7 +82,7 @@ public void doIt( String file, String message, String outfile ) throws IOExcept
// set font and font size
contentStream.setFont( font, fontSize );
// set text color to red
- contentStream.setNonStrokingColor(255, 0, 0);
+ contentStream.setNonStrokingColor(Color.red);
if (rotate)
{
// rotate the text according to the page rotation
@@ -109,7 +110,7 @@ public void doIt( String file, String message, String outfile ) throws IOExcept
/**
* This will create a hello world PDF document.
- *
+ *
* see usage() for commandline
*
* @param args Command line arguments.
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java
index b214f28eb9d..74610501e99 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java
@@ -21,6 +21,8 @@
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PageMode;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
@@ -71,7 +73,10 @@ public static void main( String[] args ) throws IOException
for( PDPage page : document.getPages() )
{
pageNum++;
- PDPageFitWidthDestination dest = new PDPageFitWidthDestination();
+ PDPageDestination dest = new PDPageFitWidthDestination();
+ // If you want to have several bookmarks pointing to different areas
+ // on the same page, have a look at the other classes derived from PDPageDestination.
+
dest.setPage( page );
PDOutlineItem bookmark = new PDOutlineItem();
bookmark.setDestination( dest );
@@ -80,6 +85,9 @@ public static void main( String[] args ) throws IOException
}
pagesOutline.openNode();
outline.openNode();
+
+ // optional: show the outlines when opening the file
+ document.getDocumentCatalog().setPageMode(PageMode.USE_OUTLINES);
document.save( args[1] );
}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateGradientShadingPDF.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateGradientShadingPDF.java
index 0bef63de143..6d3e0d9332b 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateGradientShadingPDF.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateGradientShadingPDF.java
@@ -15,15 +15,17 @@
*/
package org.apache.pdfbox.examples.pdmodel;
-import java.awt.image.BufferedImage;
-import java.io.File;
import java.io.IOException;
-import javax.imageio.ImageIO;
+import java.io.OutputStream;
+
+import javax.imageio.stream.MemoryCacheImageOutputStream;
+
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
-import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSNumber;
+import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.function.PDFunctionType2;
@@ -33,11 +35,11 @@
import org.apache.pdfbox.pdmodel.graphics.shading.PDShading;
import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType2;
import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType3;
-import org.apache.pdfbox.rendering.PDFRenderer;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType4;
/**
- * This example creates a PDF with type 2 (axial) and 3 (radial) shadings with a
- * type 2 (exponential) function.
+ * This example creates a PDF with type 2 (axial) and type 3 (radial) shadings with a type 2
+ * (exponential) function, and a type 4 (gouraud triangle shading) without function.
*
* @author Tilman Hausherr
*/
@@ -65,16 +67,16 @@ public void create(String file) throws IOException
COSDictionary fdict = new COSDictionary();
fdict.setInt(COSName.FUNCTION_TYPE, 2);
COSArray domain = new COSArray();
- domain.add(COSInteger.get(0));
- domain.add(COSInteger.get(1));
+ domain.add(COSInteger.ZERO);
+ domain.add(COSInteger.ONE);
COSArray c0 = new COSArray();
- c0.add(COSFloat.get("1"));
- c0.add(COSFloat.get("0"));
- c0.add(COSFloat.get("0"));
+ c0.add(COSInteger.ONE);
+ c0.add(COSInteger.ZERO);
+ c0.add(COSInteger.ZERO);
COSArray c1 = new COSArray();
- c1.add(COSFloat.get("0.5"));
- c1.add(COSFloat.get("1"));
- c1.add(COSFloat.get("0.5"));
+ c1.add(COSNumber.get("0.5"));
+ c1.add(COSInteger.ONE);
+ c1.add(COSNumber.get("0.5"));
fdict.setItem(COSName.DOMAIN, domain);
fdict.setItem(COSName.C0, c0);
fdict.setItem(COSName.C1, c1);
@@ -107,21 +109,89 @@ public void create(String file) throws IOException
radialShading.setCoords(coords2);
radialShading.setFunction(func);
+ // Gouraud shading
+ // See PDF 32000 specification,
+ // 8.7.4.5.5 Type 4 Shadings (Free-Form Gouraud-Shaded Triangle Meshes)
+ PDShadingType4 gouraudShading = new PDShadingType4(document.getDocument().createCOSStream());
+ gouraudShading.setShadingType(PDShading.SHADING_TYPE4);
+ // we use multiple of 8, so that no padding is needed
+ gouraudShading.setBitsPerFlag(8);
+ gouraudShading.setBitsPerCoordinate(16);
+ gouraudShading.setBitsPerComponent(8);
+ COSArray decodeArray = new COSArray();
+ // coordinates x y map 16 bits 0..FFFF to 0..FFFF to make your life easy
+ // so no calculation is needed, but you can only use integer coordinates
+ // for real numbers, you'll need smaller bounds, e.g. 0xFFFF / 0xA = 0x1999
+ // would allow 1 point decimal result coordinate.
+ // See in PDF specification: 8.9.5.2 Decode Arrays
+ decodeArray.add(COSInteger.ZERO);
+ decodeArray.add(COSInteger.get(0xFFFF));
+ decodeArray.add(COSInteger.ZERO);
+ decodeArray.add(COSInteger.get(0xFFFF));
+ // colors r g b map 8 bits from 0..FF to 0..1
+ decodeArray.add(COSInteger.ZERO);
+ decodeArray.add(COSInteger.ONE);
+ decodeArray.add(COSInteger.ZERO);
+ decodeArray.add(COSInteger.ONE);
+ decodeArray.add(COSInteger.ZERO);
+ decodeArray.add(COSInteger.ONE);
+ gouraudShading.setDecodeValues(decodeArray);
+ gouraudShading.setColorSpace(PDDeviceRGB.INSTANCE);
+
+ // Function is not required for type 4 shadings and not really useful,
+ // because if a function would be used, each corner "color" of a triangle would be one value,
+ // which would then transformed into n color components by the function so it is
+ // difficult to get 3 "extremes".
+
+ // fill the vertex stream
+ OutputStream os = ((COSStream) gouraudShading.getCOSObject()).createOutputStream();
+ MemoryCacheImageOutputStream mcos = new MemoryCacheImageOutputStream(os);
+
+ // Vertex 1, starts with flag1
+ // (flags always 0 for vertices of start triangle)
+ mcos.writeByte(0);
+ // x1 y1 (left corner)
+ mcos.writeShort(0);
+ mcos.writeShort(0);
+ // r1 g1 b1 (red)
+ mcos.writeByte(0xFF);
+ mcos.writeByte(0);
+ mcos.writeByte(0);
+
+ // Vertex 2, starts with flag2
+ mcos.writeByte(0);
+ // x2 y2 (top corner)
+ mcos.writeShort(100);
+ mcos.writeShort(100);
+ // r2 g2 b2 (green)
+ mcos.writeByte(0);
+ mcos.writeByte(0xFF);
+ mcos.writeByte(0);
+
+ // Vertex 3, starts with flag3
+ mcos.writeByte(0);
+ // x3 y3 (right corner)
+ mcos.writeShort(200);
+ mcos.writeShort(0);
+ // r3 g3 b3 (blue)
+ mcos.writeByte(0);
+ mcos.writeByte(0);
+ mcos.writeByte(0xFF);
+
+ mcos.close();
+ // outside stream MUST be closed as well, see javadoc of MemoryCacheImageOutputStream
+ os.close();
+
// invoke shading from content stream
// compress parameter is set to false so that you can see the stream in a text editor
PDPageContentStream contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, false);
contentStream.shadingFill(axialShading);
contentStream.shadingFill(radialShading);
+ contentStream.shadingFill(gouraudShading);
contentStream.close();
document.save(file);
document.close();
-
- // render the PDF and save it into a PNG file
- document = PDDocument.load(new File(file));
- BufferedImage bim = new PDFRenderer(document).renderImageWithDPI(0, 300);
- ImageIO.write(bim, "png", new File(file + ".png"));
- document.close();
}
finally
{
@@ -157,6 +227,6 @@ public static void main(String[] args) throws IOException
*/
private static void usage()
{
- System.err.println("usage: java o" + CreateGradientShadingPDF.class.getName() + " ");
+ System.err.println("usage: java " + CreateGradientShadingPDF.class.getName() + " ");
}
}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePDFA.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePDFA.java
index 9a7e3db1e47..ee85bc7d6f1 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePDFA.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePDFA.java
@@ -64,6 +64,21 @@ public static void main(String[] args) throws IOException, TransformerException
// load the font as this needs to be embedded
PDFont font = PDType0Font.load(doc, new File(fontfile));
+
+ // A PDF/A file needs to have the font embedded if the font is used for text rendering
+ // in rendering modes other than text rendering mode 3.
+ //
+ // This requirement includes the PDF standard fonts, so don't use their static PDFType1Font classes such as
+ // PDFType1Font.HELVETICA.
+ //
+ // As there are many different font licenses it is up to the developer to check if the license terms for the
+ // font loaded allows embedding in the PDF.
+ //
+ if (!font.isEmbedded())
+ {
+ throw new IllegalStateException("PDF/A compliance requires that all fonts used for"
+ + " text rendering in rendering modes other than rendering mode 3 are embedded.");
+ }
// create a page with the message
PDPageContentStream contents = new PDPageContentStream(doc, page);
@@ -72,7 +87,6 @@ public static void main(String[] args) throws IOException, TransformerException
contents.newLineAtOffset(100, 700);
contents.showText(message);
contents.endText();
- contents.saveGraphicsState();
contents.close();
// add XMP metadata
@@ -103,7 +117,7 @@ public static void main(String[] args) throws IOException, TransformerException
// sRGB output intent
InputStream colorProfile = CreatePDFA.class.getResourceAsStream(
- "/org/apache/pdfbox/resources/pdfa/sRGB Color Space Profile.icm");
+ "/org/apache/pdfbox/resources/pdfa/sRGB.icc");
PDOutputIntent intent = new PDOutputIntent(doc, colorProfile);
intent.setInfo("sRGB IEC61966-2.1");
intent.setOutputCondition("sRGB IEC61966-2.1");
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePageLabels.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePageLabels.java
new file mode 100644
index 00000000000..d1b3c456724
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePageLabels.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.common.PDPageLabelRange;
+import org.apache.pdfbox.pdmodel.common.PDPageLabels;
+
+/**
+ * Create a 3-page PDF with the page labels "RO III", "RO IV", "1".
+ *
+ * @author Tilman Hausherr
+ */
+public class CreatePageLabels
+{
+ /**
+ * Constructor.
+ */
+ private CreatePageLabels()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument doc = new PDDocument();
+ doc.addPage(new PDPage());
+ doc.addPage(new PDPage());
+ doc.addPage(new PDPage());
+ PDPageLabels pageLabels = new PDPageLabels(doc);
+ PDPageLabelRange pageLabelRange1 = new PDPageLabelRange();
+ pageLabelRange1.setPrefix("RO ");
+ pageLabelRange1.setStart(3);
+ pageLabelRange1.setStyle(PDPageLabelRange.STYLE_ROMAN_UPPER);
+ pageLabels.setLabelItem(0, pageLabelRange1);
+ PDPageLabelRange pageLabelRange2 = new PDPageLabelRange();
+ pageLabelRange2.setStart(1);
+ pageLabelRange2.setStyle(PDPageLabelRange.STYLE_DECIMAL);
+ pageLabels.setLabelItem(2, pageLabelRange2);
+ doc.getDocumentCatalog().setPageLabels(pageLabels);
+ doc.save("labels.pdf");
+ doc.close();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePatternsPDF.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePatternsPDF.java
new file mode 100644
index 00000000000..88f7beee176
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePatternsPDF.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.awt.Color;
+import java.io.IOException;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.graphics.color.PDPattern;
+import org.apache.pdfbox.pdmodel.graphics.pattern.PDTilingPattern;
+
+/**
+ * This is an example of how to create a page that uses patterns to paint areas.
+ *
+ * @author Tilman Hausherr
+ */
+public final class CreatePatternsPDF
+{
+ private CreatePatternsPDF()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument doc = new PDDocument();
+ PDPage page = new PDPage();
+ doc.addPage(page);
+ page.setResources(new PDResources());
+
+ PDPageContentStream pcs = new PDPageContentStream(doc, page);
+
+ // Colored pattern, i.e. the pattern content stream will set its own color(s)
+ PDColorSpace patternCS1 = new PDPattern(null, PDDeviceRGB.INSTANCE);
+
+ // Table 75 spec
+ PDTilingPattern tilingPattern1 = new PDTilingPattern();
+ tilingPattern1.setBBox(new PDRectangle(0, 0, 10, 10));
+ tilingPattern1.setPaintType(PDTilingPattern.PAINT_COLORED);
+ tilingPattern1.setTilingType(PDTilingPattern.TILING_CONSTANT_SPACING);
+ tilingPattern1.setXStep(10);
+ tilingPattern1.setYStep(10);
+
+ COSName patternName1 = page.getResources().add(tilingPattern1);
+
+ PDPageContentStream cs1 = new PDPageContentStream(doc,
+ tilingPattern1,
+ tilingPattern1.getContentStream().createOutputStream());
+ // Set color, draw diagonal line + 2 more diagonals so that corners look good
+ cs1.setStrokingColor(Color.red);
+ cs1.moveTo(0, 0);
+ cs1.lineTo(10, 10);
+ cs1.moveTo(-1, 9);
+ cs1.lineTo(1, 11);
+ cs1.moveTo(9, -1);
+ cs1.lineTo(11, 1);
+ cs1.stroke();
+ cs1.close();
+
+ PDColor patternColor1 = new PDColor(patternName1, patternCS1);
+
+ pcs.addRect(50, 500, 200, 200);
+ pcs.setNonStrokingColor(patternColor1);
+ pcs.fill();
+
+ // Uncolored pattern - the color is passed later
+ PDTilingPattern tilingPattern2 = new PDTilingPattern();
+ tilingPattern2.setBBox(new PDRectangle(0, 0, 10, 10));
+ tilingPattern2.setPaintType(PDTilingPattern.PAINT_UNCOLORED);
+ tilingPattern2.setTilingType(PDTilingPattern.TILING_NO_DISTORTION);
+ tilingPattern2.setXStep(10);
+ tilingPattern2.setYStep(10);
+
+ COSName patternName2 = page.getResources().add(tilingPattern2);
+
+ PDPageContentStream cs2 = new PDPageContentStream(doc,
+ tilingPattern2,
+ tilingPattern2.getContentStream().createOutputStream());
+ // draw a cross
+ cs2.moveTo(0, 5);
+ cs2.lineTo(10, 5);
+ cs2.moveTo(5, 0);
+ cs2.lineTo(5, 10);
+ cs2.stroke();
+ cs2.close();
+
+ // Uncolored pattern colorspace needs to know the colorspace
+ // for the color values that will be passed when painting the fill
+ PDColorSpace patternCS2 = new PDPattern(null, PDDeviceRGB.INSTANCE);
+ PDColor patternColor2green = new PDColor(
+ new float[]{0,1,0},
+ patternName2,
+ patternCS2);
+
+ pcs.addRect(300, 500, 100, 100);
+ pcs.setNonStrokingColor(patternColor2green);
+ pcs.fill();
+
+ // same pattern again but with different color + different pattern start position
+ PDColor patternColor2blue = new PDColor(
+ new float[]{0,0,1},
+ patternName2,
+ patternCS2);
+ pcs.addRect(455, 505, 100, 100);
+ pcs.setNonStrokingColor(patternColor2blue);
+ pcs.fill();
+
+ pcs.close();
+ doc.save("patterns.pdf");
+ doc.close();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePortableCollection.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePortableCollection.java
new file mode 100644
index 00000000000..d25dbb422be
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePortableCollection.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
+import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PageMode;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.util.Charsets;
+
+/**
+ * This is an example on how to create a portable collection PDF, as described in the PDF 1.7
+ * specification in chapter 12.3.5. It uses the COS methods because there are not any PD classes
+ * yet. If you want to help, we'd need PDCollection, PDCollectionField, PDCollectionSort and
+ * PDCollectionItem.
+ *
+ * @author Tilman Hausherr
+ */
+public class CreatePortableCollection
+{
+
+ /**
+ * Constructor.
+ */
+ private CreatePortableCollection()
+ {
+ }
+
+ /**
+ * Create a portable collection PDF with two files.
+ *
+ * @param file The file to write the PDF to.
+ *
+ * @throws IOException If there is an error writing the data.
+ */
+ public void doIt(String file) throws IOException
+ {
+ PDDocument doc = new PDDocument();
+ PDPage page = new PDPage();
+ doc.addPage(page);
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page);
+ contentStream.beginText();
+ contentStream.setFont(PDType1Font.HELVETICA, 12);
+ contentStream.newLineAtOffset(100, 700);
+ contentStream.showText("Example of a portable collection");
+ contentStream.endText();
+ contentStream.close();
+
+ //embedded files are stored in a named tree
+ PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode();
+
+ //first create the file specification, which holds the embedded file
+ PDComplexFileSpecification fs1 = new PDComplexFileSpecification();
+
+ // use both methods for backwards, cross-platform and cross-language compatibility.
+ fs1.setFile("Test1.txt");
+ fs1.setFileUnicode("Test1.txt");
+
+ //create a dummy file stream, this would probably normally be a FileInputStream
+ byte[] data1 = "This is the contents of the first embedded file".getBytes(Charsets.ISO_8859_1);
+ PDEmbeddedFile ef1 = new PDEmbeddedFile(doc, new ByteArrayInputStream(data1), COSName.FLATE_DECODE);
+ //now lets some of the optional parameters
+ ef1.setSubtype("text/plain");
+ ef1.setSize(data1.length);
+ ef1.setCreationDate(new GregorianCalendar());
+
+ // use both methods for backwards, cross-platform and cross-language compatibility.
+ fs1.setEmbeddedFile(ef1);
+ fs1.setEmbeddedFileUnicode(ef1);
+ fs1.setFileDescription("The first file");
+
+ //first create the file specification, which holds the embedded file
+ PDComplexFileSpecification fs2 = new PDComplexFileSpecification();
+
+ // use both methods for backwards, cross-platform and cross-language compatibility.
+ fs2.setFile("Test2.txt");
+ fs2.setFileUnicode("Test2.txt");
+
+ //create a dummy file stream, this would probably normally be a FileInputStream
+ byte[] data2 = "This is the contents of the second embedded file".getBytes(Charsets.ISO_8859_1);
+ PDEmbeddedFile ef2 = new PDEmbeddedFile(doc, new ByteArrayInputStream(data2), COSName.FLATE_DECODE);
+ //now lets some of the optional parameters
+ ef2.setSubtype("text/plain");
+ ef2.setSize(data2.length);
+ ef2.setCreationDate(new GregorianCalendar());
+
+ // use both methods for backwards, cross-platform and cross-language compatibility.
+ fs2.setEmbeddedFile(ef2);
+ fs2.setEmbeddedFileUnicode(ef2);
+ fs2.setFileDescription("The second file");
+
+ Map map = new HashMap();
+ map.put("Attachment 1", fs1);
+ map.put("Attachment 2", fs2);
+
+ // create a new tree node and add the embedded file
+ PDEmbeddedFilesNameTreeNode treeNode = new PDEmbeddedFilesNameTreeNode();
+ treeNode.setNames(map);
+ // add the new node as kid to the root node
+ List kids = new ArrayList();
+ kids.add(treeNode);
+ efTree.setKids(kids);
+
+ // add the tree to the document catalog
+ PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
+ names.setEmbeddedFiles(efTree);
+ doc.getDocumentCatalog().setNames(names);
+
+ // show attachments panel in some viewers
+ doc.getDocumentCatalog().setPageMode(PageMode.USE_ATTACHMENTS);
+
+ // create collection directory
+ COSDictionary collectionDic = new COSDictionary();
+ COSDictionary schemaDict = new COSDictionary();
+ schemaDict.setItem(COSName.TYPE, COSName.COLLECTION_SCHEMA);
+ COSDictionary sortDic = new COSDictionary();
+ sortDic.setItem(COSName.TYPE, COSName.COLLECTION_SORT);
+ sortDic.setString(COSName.A, "true"); // sort ascending
+ // "it identifies a field described in the parent collection dictionary"
+ // sort by field 2
+ sortDic.setItem(COSName.S, COSName.getPDFName("fieldtwo"));
+ collectionDic.setItem(COSName.TYPE, COSName.COLLECTION);
+ collectionDic.setItem(COSName.SCHEMA, schemaDict);
+ collectionDic.setItem(COSName.SORT, sortDic);
+ collectionDic.setItem(COSName.VIEW, COSName.D); // Details mode
+ COSDictionary fieldDict1 = new COSDictionary();
+ fieldDict1.setItem(COSName.TYPE, COSName.COLLECTION_FIELD);
+ fieldDict1.setItem(COSName.SUBTYPE, COSName.S); // type: text field
+ fieldDict1.setString(COSName.N, "field header one (description)"); // header text
+ fieldDict1.setInt(COSName.O, 1); // order on the screen
+ COSDictionary fieldDict2 = new COSDictionary();
+ fieldDict2.setItem(COSName.TYPE, COSName.COLLECTION_FIELD);
+ fieldDict2.setItem(COSName.SUBTYPE, COSName.S); // type: text field
+ fieldDict2.setString(COSName.N, "field header two (name)");
+ fieldDict2.setInt(COSName.O, 2);
+ COSDictionary fieldDict3 = new COSDictionary();
+ fieldDict3.setItem(COSName.TYPE, COSName.COLLECTION_FIELD);
+ fieldDict3.setItem(COSName.SUBTYPE, COSName.N); // type: number field
+ fieldDict3.setString(COSName.N, "field header three (size)");
+ fieldDict3.setInt(COSName.O, 3);
+ schemaDict.setItem("fieldone", fieldDict1); // field name (this is a key)
+ schemaDict.setItem("fieldtwo", fieldDict2);
+ schemaDict.setItem("fieldthree", fieldDict3);
+ doc.getDocumentCatalog().getCOSObject().setItem(COSName.COLLECTION, collectionDic);
+ doc.getDocumentCatalog().setVersion("1.7");
+
+ // collection item dictionary with fields for 1st file
+ COSDictionary ciDict1 = new COSDictionary();
+ ciDict1.setItem(COSName.TYPE, COSName.COLLECTION_ITEM);
+ // use the field names from earlier
+ ciDict1.setString("fieldone", fs1.getFileDescription());
+ ciDict1.setString("fieldtwo", fs1.getFile());
+ ciDict1.setInt("fieldthree", fs1.getEmbeddedFile().getSize());
+ fs1.getCOSObject().setItem(COSName.CI, ciDict1);
+
+ // collection item dictionary with fields for 2nd file
+ COSDictionary ciDict2 = new COSDictionary();
+ ciDict2.setItem(COSName.TYPE, COSName.COLLECTION_ITEM);
+ // use the field names from earlier
+ ciDict2.setString("fieldone", fs2.getFileDescription());
+ ciDict2.setString("fieldtwo", fs2.getFile());
+ ciDict2.setInt("fieldthree", fs2.getEmbeddedFile().getSize());
+ fs2.getCOSObject().setItem(COSName.CI, ciDict2);
+
+ doc.save(file);
+ doc.close();
+ }
+
+ /**
+ * This will create a portable collection PDF.
+ *
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args) throws IOException
+ {
+ CreatePortableCollection app = new CreatePortableCollection();
+ if (args.length != 1)
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt(args[0]);
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println("usage: " + this.getClass().getName() + " ");
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateSeparationColorBox.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateSeparationColorBox.java
new file mode 100644
index 00000000000..9bcca404dec
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateSeparationColorBox.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSNumber;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.function.PDFunctionType2;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation;
+
+/**
+ * This example shows how to use a separation color / spot color. Here it is a placeholder for gold,
+ * and it is displayed as yellow. You can see the colorspace in PDFDebugger by going to
+ * "Root/Pages/Kids/[0]/Resources/ColorSpace/cs1".
+ *
+ * @author Tilman Hausherr
+ */
+public class CreateSeparationColorBox
+{
+ private CreateSeparationColorBox()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument doc = new PDDocument();
+ PDPage page = new PDPage();
+ doc.addPage(page);
+
+ COSArray separationArray = new COSArray();
+ separationArray.add(COSName.SEPARATION); // type
+ separationArray.add(COSName.getPDFName("Gold")); // the name, e.g. metallic, fluorescent, glitter
+ separationArray.add(COSName.DEVICERGB); // alternate colorspace
+
+ // tint transform function, results between C0=white (1 1 1) and C1=yellow (1 1 0)
+ COSDictionary fdict = new COSDictionary();
+ fdict.setInt(COSName.FUNCTION_TYPE, 2);
+ COSArray range = new COSArray();
+ range.add(COSInteger.ZERO);
+ range.add(COSInteger.ONE);
+ range.add(COSInteger.ZERO);
+ range.add(COSInteger.ONE);
+ range.add(COSInteger.ZERO);
+ range.add(COSInteger.ONE);
+ fdict.setItem(COSName.RANGE, range);
+ COSArray domain = new COSArray();
+ domain.add(COSInteger.ZERO);
+ domain.add(COSInteger.ONE);
+ fdict.setItem(COSName.DOMAIN, domain);
+ COSArray c0 = new COSArray();
+ c0.add(COSInteger.ONE);
+ c0.add(COSInteger.ONE);
+ c0.add(COSInteger.ONE);
+ fdict.setItem(COSName.C0, c0);
+ COSArray c1 = new COSArray();
+ c1.add(COSInteger.ONE);
+ c1.add(COSInteger.ONE);
+ c1.add(COSInteger.ZERO);
+ fdict.setItem(COSName.C1, c1);
+ fdict.setInt(COSName.N, 1);
+ PDFunctionType2 func = new PDFunctionType2(fdict);
+ separationArray.add(func);
+
+ PDColorSpace spotColorSpace = new PDSeparation(separationArray);
+
+ PDPageContentStream cs = new PDPageContentStream(doc, page);
+ PDColor color = new PDColor(new float[]{0.5f}, spotColorSpace);
+ cs.setStrokingColor(color);
+ cs.setLineWidth(10);
+ cs.addRect(50, 50, 500, 700);
+ cs.stroke();
+ cs.close();
+ doc.save("gold.pdf");
+ doc.close();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java
index b8d5162857c..a5bb418add3 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java
@@ -27,11 +27,10 @@
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import org.apache.pdfbox.pdmodel.PDPage;
-
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
-
+import org.apache.pdfbox.pdmodel.PageMode;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
@@ -45,9 +44,8 @@ public class EmbeddedFiles
/**
* Constructor.
*/
- public EmbeddedFiles()
+ private EmbeddedFiles()
{
- super();
}
/**
@@ -59,7 +57,6 @@ public EmbeddedFiles()
*/
public void doIt( String file) throws IOException
{
- // the document
PDDocument doc = null;
try
{
@@ -82,19 +79,26 @@ public void doIt( String file) throws IOException
//first create the file specification, which holds the embedded file
PDComplexFileSpecification fs = new PDComplexFileSpecification();
+
+ // use both methods for backwards, cross-platform and cross-language compatibility.
fs.setFile( "Test.txt" );
+ fs.setFileUnicode("Test.txt");
+
//create a dummy file stream, this would probably normally be a FileInputStream
byte[] data = "This is the contents of the embedded file".getBytes("ISO-8859-1");
- ByteArrayInputStream fakeFile =
- new ByteArrayInputStream( data );
+ ByteArrayInputStream fakeFile = new ByteArrayInputStream(data);
PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile );
//now lets some of the optional parameters
- ef.setSubtype( "test/plain" );
+ ef.setSubtype( "text/plain" );
ef.setSize( data.length );
ef.setCreationDate( new GregorianCalendar() );
+
+ // use both methods for backwards, cross-platform and cross-language compatibility.
fs.setEmbeddedFile( ef );
+ fs.setEmbeddedFileUnicode(ef);
+ fs.setFileDescription("Very interesting file");
- // create a new tree node and add the embedded file
+ // create a new tree node and add the embedded file
PDEmbeddedFilesNameTreeNode treeNode = new PDEmbeddedFilesNameTreeNode();
treeNode.setNames( Collections.singletonMap( "My first attachment", fs ) );
// add the new node as kid to the root node
@@ -106,7 +110,8 @@ public void doIt( String file) throws IOException
names.setEmbeddedFiles( efTree );
doc.getDocumentCatalog().setNames( names );
-
+ // show attachments panel in some viewers
+ doc.getDocumentCatalog().setPageMode(PageMode.USE_ATTACHMENTS);
doc.save( file );
}
finally
@@ -120,7 +125,7 @@ public void doIt( String file) throws IOException
/**
* This will create a hello world PDF document with an embedded file.
- *
+ *
* see usage() for commandline
*
* @param args Command line arguments.
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFonts.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFonts.java
index b1e9c62ccbd..aa6220bf084 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFonts.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedFonts.java
@@ -51,7 +51,7 @@ public static void main(String[] args) throws IOException
stream.beginText();
stream.setFont(font, 12);
- stream.setLeading(12 * 1.2);
+ stream.setLeading(12 * 1.2f);
stream.newLineAtOffset(50, 600);
stream.showText("PDFBox's Unicode with Embedded TrueType Font");
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedMultipleFonts.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedMultipleFonts.java
new file mode 100644
index 00000000000..6ba7c08209b
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedMultipleFonts.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fontbox.ttf.TrueTypeCollection;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.font.encoding.GlyphList;
+import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding;
+
+/**
+ * Output a text without knowing which font is the right one. One use case is a worldwide address
+ * list. Only LTR languages are supported, RTL (e.g. Hebrew, Arabic) are not supported so they would
+ * appear in the wrong direction. Complex scripts (Thai, Arabic, some Indian languages) are also not
+ * supported, any output will look weird. There is an (unfinished) effort here:
+ * https://issues.apache.org/jira/browse/PDFBOX-4189
+ *
+ * @author Tilman Hausherr
+ */
+public class EmbeddedMultipleFonts
+{
+ private EmbeddedMultipleFonts()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument document = new PDDocument();
+
+ PDPage page = new PDPage(PDRectangle.A4);
+ document.addPage(page);
+
+ PDFont font1 = PDType1Font.HELVETICA; // always have a simple font as first one
+ TrueTypeCollection ttc2 = new TrueTypeCollection(new File("c:/windows/fonts/batang.ttc"));
+ PDType0Font font2 = PDType0Font.load(document, ttc2.getFontByName("Batang"), true); // Korean
+ TrueTypeCollection ttc3 = new TrueTypeCollection(new File("c:/windows/fonts/mingliu.ttc"));
+ PDType0Font font3 = PDType0Font.load(document, ttc3.getFontByName("MingLiU"), true); // Chinese
+ PDType0Font font4 = PDType0Font.load(document, new File("c:/windows/fonts/mangal.ttf")); // Indian
+ PDType0Font font5 = PDType0Font.load(document, new File("c:/windows/fonts/ArialUni.ttf")); // Fallback
+
+ PDPageContentStream cs = new PDPageContentStream(document, page);
+
+ cs.beginText();
+ List fonts = new ArrayList();
+ fonts.add(font1);
+ fonts.add(font2);
+ fonts.add(font3);
+ fonts.add(font4);
+ fonts.add(font5);
+ cs.newLineAtOffset(20, 700);
+ showTextMultiple(cs, "abc 한국 中国 भारत 日本 abc", fonts, 20);
+ cs.endText();
+ cs.close();
+
+ document.save("example.pdf");
+ document.close();
+
+ ttc2.close();
+ ttc3.close();
+ }
+
+ static void showTextMultiple(PDPageContentStream cs, String text, List fonts, float size)
+ throws IOException
+ {
+ try
+ {
+ // first try all at once
+ fonts.get(0).encode(text);
+ cs.setFont(fonts.get(0), size);
+ cs.showText(text);
+ return;
+ }
+ catch (IllegalArgumentException ex)
+ {
+ // do nothing
+ }
+ // now try separately
+ int i = 0;
+ while (i < text.length())
+ {
+ boolean found = false;
+ for (PDFont font : fonts)
+ {
+ try
+ {
+ String s = text.substring(i, i + 1);
+ font.encode(s);
+ // it works! Try more with this font
+ int j = i + 1;
+ for (; j < text.length(); ++j)
+ {
+ String s2 = text.substring(j, j + 1);
+
+ if (isWinAnsiEncoding(s2.codePointAt(0)) && font != fonts.get(0))
+ {
+ // Without this segment, the example would have a flaw:
+ // This code tries to keep the current font, so
+ // the second "abc" would appear in a different font
+ // than the first one, which would be weird.
+ // This segment assumes that the first font has WinAnsiEncoding.
+ // (all static PDType1Font Times / Helvetica / Courier fonts)
+ break;
+ }
+ try
+ {
+ font.encode(s2);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ // it's over
+ break;
+ }
+ }
+ s = text.substring(i, j);
+ cs.setFont(font, size);
+ cs.showText(s);
+ i = j;
+ found = true;
+ break;
+ }
+ catch (IllegalArgumentException ex)
+ {
+ // didn't work, will try next font
+ }
+ }
+ if (!found)
+ {
+ throw new IllegalArgumentException("Could not show '" + text.substring(i, i + 1)
+ + "' with the fonts provided");
+ }
+ }
+ }
+
+ static boolean isWinAnsiEncoding(int unicode)
+ {
+ String name = GlyphList.getAdobeGlyphList().codePointToName(unicode);
+ if (".notdef".equals(name))
+ {
+ return false;
+ }
+ return WinAnsiEncoding.INSTANCE.contains(name);
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedVerticalFonts.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedVerticalFonts.java
new file mode 100644
index 00000000000..a0b35a68c0f
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/EmbeddedVerticalFonts.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2018 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+/**
+ *
+ * @author Aaron Madlon-Kay
+ */
+public class EmbeddedVerticalFonts
+{
+ private EmbeddedVerticalFonts()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ PDDocument document = new PDDocument();
+ PDPage page = new PDPage();
+ document.addPage(page);
+
+ // The actual font file
+ // Download: https://ipafont.ipa.go.jp/ipafont/ipag00303.zip
+ // (free license: https://www.gnu.org/licenses/license-list.html#IPAFONT)
+ File ipafont = new File("ipag.ttf");
+
+ // You can also use a Windows 7 TrueType font collection, e.g. MingLiU:
+ // TrueTypeFont ttf = new TrueTypeCollection(new File("C:/windows/fonts/mingliu.ttc")).getFontByName("MingLiU")
+ // PDType0Font.loadVertical(document, ttf, true)
+
+ // Load as horizontal
+ PDType0Font hfont = PDType0Font.load(document, ipafont);
+
+ // Load as vertical
+ PDType0Font vfont = PDType0Font.loadVertical(document, ipafont);
+
+ // Load as vertical, but disable vertical glyph substitution
+ // (You will usually not want this because it doesn't look good!)
+ TrueTypeFont ttf = new TTFParser().parse(ipafont);
+ PDType0Font vfont2 = PDType0Font.loadVertical(document, ttf, true);
+ ttf.disableGsubFeature("vrt2");
+ ttf.disableGsubFeature("vert");
+
+ PDPageContentStream contentStream = new PDPageContentStream(document, page);
+ contentStream.beginText();
+ contentStream.setFont(hfont, 20);
+ contentStream.setLeading(25);
+ contentStream.newLineAtOffset(20, 300);
+ contentStream.showText("Key:");
+ contentStream.newLine();
+ contentStream.showText("① Horizontal");
+ contentStream.newLine();
+ contentStream.showText("② Vertical with substitution");
+ contentStream.newLine();
+ contentStream.showText("③ Vertical without substitution");
+ contentStream.endText();
+
+ contentStream.beginText();
+ contentStream.setFont(hfont, 20);
+ contentStream.newLineAtOffset(20, 650);
+ contentStream.showText("①「あーだこーだ」");
+ contentStream.endText();
+
+ contentStream.beginText();
+ contentStream.setFont(vfont, 20);
+ contentStream.newLineAtOffset(50, 600);
+ contentStream.showText("②「あーだこーだ」");
+ contentStream.endText();
+
+ contentStream.beginText();
+ contentStream.setFont(vfont2, 20);
+ contentStream.newLineAtOffset(100, 600);
+ contentStream.showText("③「あーだこーだ」");
+ contentStream.endText();
+ contentStream.close();
+ // result file should look like the one attached to JIRA issue PDFBOX-4106
+ document.save("vertical.pdf");
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java
index bf24ee508ed..e3db313f8a5 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractEmbeddedFiles.java
@@ -57,70 +57,86 @@ public static void main( String[] args ) throws IOException
usage();
System.exit(1);
}
- else
+
+ PDDocument document = null;
+ try
{
- PDDocument document = null;
- try
+ File pdfFile = new File(args[0]);
+ String filePath = pdfFile.getParent() + System.getProperty("file.separator");
+ document = PDDocument.load(pdfFile );
+ PDDocumentNameDictionary namesDictionary =
+ new PDDocumentNameDictionary( document.getDocumentCatalog() );
+ PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();
+ if (efTree != null)
{
- File pdfFile = new File(args[0]);
- String filePath = pdfFile.getParent() + System.getProperty("file.separator");
- document = PDDocument.load(pdfFile );
- PDDocumentNameDictionary namesDictionary =
- new PDDocumentNameDictionary( document.getDocumentCatalog() );
- PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();
- if (efTree != null)
- {
- Map names = efTree.getNames();
- if (names != null)
- {
- extractFiles(names, filePath);
- }
- else
- {
- List> kids = efTree.getKids();
- for (PDNameTreeNode node : kids)
- {
- names = node.getNames();
- extractFiles(names, filePath);
- }
- }
- }
-
- // extract files from annotations
- for (PDPage page : document.getPages())
- {
- for (PDAnnotation annotation : page.getAnnotations())
- {
- if (annotation instanceof PDAnnotationFileAttachment)
- {
- PDAnnotationFileAttachment annotationFileAttachment = (PDAnnotationFileAttachment) annotation;
- PDComplexFileSpecification fileSpec = (PDComplexFileSpecification) annotationFileAttachment.getFile();
- PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec);
- extractFile(filePath, fileSpec.getFilename(), embeddedFile);
- }
- }
- }
-
+ extractFilesFromEFTree(efTree, filePath);
}
- finally
+
+ // extract files from page annotations
+ for (PDPage page : document.getPages())
{
- if( document != null )
+ extractFilesFromPage(page, filePath);
+ }
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+
+ }
+
+ private static void extractFilesFromPage(PDPage page, String filePath) throws IOException
+ {
+ for (PDAnnotation annotation : page.getAnnotations())
+ {
+ if (annotation instanceof PDAnnotationFileAttachment)
+ {
+ PDAnnotationFileAttachment annotationFileAttachment = (PDAnnotationFileAttachment) annotation;
+ PDComplexFileSpecification fileSpec = (PDComplexFileSpecification) annotationFileAttachment.getFile();
+ PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec);
+ if (embeddedFile != null)
{
- document.close();
+ extractFile(filePath, fileSpec.getFilename(), embeddedFile);
}
}
}
}
+ private static void extractFilesFromEFTree(PDNameTreeNode efTree, String filePath) throws IOException
+ {
+ Map names = efTree.getNames();
+ if (names != null)
+ {
+ extractFiles(names, filePath);
+ }
+ else
+ {
+ List> kids = efTree.getKids();
+ if (kids == null)
+ {
+ return;
+ }
+ for (PDNameTreeNode node : kids)
+ {
+ extractFilesFromEFTree(node, filePath);
+ }
+ }
+ }
+
private static void extractFiles(Map names, String filePath)
throws IOException
{
for (Entry entry : names.entrySet())
{
- String filename = entry.getKey();
PDComplexFileSpecification fileSpec = entry.getValue();
PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec);
- extractFile(filePath, filename, embeddedFile);
+ if (embeddedFile != null)
+ {
+ extractFile(filePath, fileSpec.getFilename(), embeddedFile);
+ }
}
}
@@ -128,7 +144,14 @@ private static void extractFile(String filePath, String filename, PDEmbeddedFile
throws IOException
{
String embeddedFilename = filePath + filename;
- File file = new File(filePath + filename);
+ File file = new File(embeddedFilename);
+ File parentDir = file.getParentFile();
+ if (!parentDir.exists())
+ {
+ // sometimes paths contain a directory
+ System.out.println("Creating " + parentDir);
+ parentDir.mkdirs();
+ }
System.out.println("Writing " + embeddedFilename);
FileOutputStream fos = null;
try
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractMetadata.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractMetadata.java
index 4fb104cb0a4..20cf8fadbe5 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractMetadata.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractMetadata.java
@@ -25,7 +25,6 @@
import java.io.IOException;
import java.text.DateFormat;
import java.util.Calendar;
-import java.util.Iterator;
import java.util.List;
import org.apache.xmpbox.XMPMetadata;
@@ -74,37 +73,15 @@ public static void main(String[] args) throws IOException, XmpParsingException
DomXmpParser xmpParser = new DomXmpParser();
try
{
- XMPMetadata metadata = xmpParser.parse(meta.createInputStream());
-
- DublinCoreSchema dc = metadata.getDublinCoreSchema();
- if (dc != null)
- {
- display("Title:", dc.getTitle());
- display("Description:", dc.getDescription());
- listString("Creators: ", dc.getCreators());
- listCalendar("Dates:", dc.getDates());
- listString("Subjects:", dc.getSubjects());
- }
-
- AdobePDFSchema pdf = metadata.getAdobePDFSchema();
- if (pdf != null)
- {
- display("Keywords:", pdf.getKeywords());
- display("PDF Version:", pdf.getPDFVersion());
- display("PDF Producer:", pdf.getProducer());
- }
-
- XMPBasicSchema basic = metadata.getXMPBasicSchema();
- if (basic != null)
- {
- display("Create Date:", basic.getCreateDate());
- display("Modify Date:", basic.getModifyDate());
- display("Creator Tool:", basic.getCreatorTool());
- }
+ XMPMetadata metadata = xmpParser.parse(meta.toByteArray());
+
+ showDublinCoreSchema(metadata);
+ showAdobePDFSchema(metadata);
+ showXMPBasicSchema(metadata);
}
catch (XmpParsingException e)
{
- System.err.println("An error ouccred when parsing the meta data: "
+ System.err.println("An error occurred when parsing the metadata: "
+ e.getMessage());
}
}
@@ -130,6 +107,41 @@ public static void main(String[] args) throws IOException, XmpParsingException
}
}
+ private static void showXMPBasicSchema(XMPMetadata metadata)
+ {
+ XMPBasicSchema basic = metadata.getXMPBasicSchema();
+ if (basic != null)
+ {
+ display("Create Date:", basic.getCreateDate());
+ display("Modify Date:", basic.getModifyDate());
+ display("Creator Tool:", basic.getCreatorTool());
+ }
+ }
+
+ private static void showAdobePDFSchema(XMPMetadata metadata)
+ {
+ AdobePDFSchema pdf = metadata.getAdobePDFSchema();
+ if (pdf != null)
+ {
+ display("Keywords:", pdf.getKeywords());
+ display("PDF Version:", pdf.getPDFVersion());
+ display("PDF Producer:", pdf.getProducer());
+ }
+ }
+
+ private static void showDublinCoreSchema(XMPMetadata metadata)
+ {
+ DublinCoreSchema dc = metadata.getDublinCoreSchema();
+ if (dc != null)
+ {
+ display("Title:", dc.getTitle());
+ display("Description:", dc.getDescription());
+ listString("Creators: ", dc.getCreators());
+ listCalendar("Dates:", dc.getDates());
+ listString("Subjects:", dc.getSubjects());
+ }
+ }
+
private static void showDocumentInformation(PDDocumentInformation information)
{
display("Title:", information.getTitle());
@@ -146,10 +158,8 @@ private static void listString(String title, List list)
return;
}
System.out.println(title);
- Iterator iter = list.iterator();
- while (iter.hasNext())
+ for (String string : list)
{
- String string = iter.next();
System.out.println(" " + string);
}
}
@@ -161,10 +171,8 @@ private static void listCalendar(String title, List list)
return;
}
System.out.println(title);
- Iterator iter = list.iterator();
- while (iter.hasNext())
+ for (Calendar calendar : list)
{
- Calendar calendar = iter.next();
System.out.println(" " + format(calendar));
}
}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractTTFFonts.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractTTFFonts.java
index 27ba9bbfeb4..2c92cd940ff 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractTTFFonts.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ExtractTTFFonts.java
@@ -43,6 +43,7 @@ public final class ExtractTTFFonts
{
private int fontCounter = 1;
+ @SuppressWarnings({"squid:S2068"})
private static final String PASSWORD = "-password";
private static final String PREFIX = "-prefix";
private static final String ADDKEY = "-addkey";
@@ -73,6 +74,7 @@ private void extractFonts(String[] args) throws IOException
else
{
String pdfFile = null;
+ @SuppressWarnings({"squid:S2068"})
String password = "";
String prefix = null;
boolean addKey = false;
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/HelloWorldType1.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/HelloWorldType1.java
index c2ba427ba73..bf9837968b8 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/HelloWorldType1.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/HelloWorldType1.java
@@ -19,6 +19,7 @@
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
@@ -54,7 +55,9 @@ public static void main(String[] args) throws IOException
PDPage page = new PDPage();
doc.addPage(page);
- PDFont font = new PDType1Font(doc, new FileInputStream(pfbPath));
+ InputStream is = new FileInputStream(pfbPath);
+ PDFont font = new PDType1Font(doc, is);
+ is.close();
PDPageContentStream contents = new PDPageContentStream(doc, page);
contents.beginText();
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/LightlyPdfToImage.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/LightlyPdfToImage.java
new file mode 100644
index 00000000000..45e198c9fe8
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/LightlyPdfToImage.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import org.apache.pdfbox.io.MemoryUsageSetting;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.PDFRenderer;
+import org.apache.pdfbox.tools.imageio.ImageIOUtil;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Convert PDF documents to images.
+ *
+ *
+ * The example is very important to prevent OOM when parsing complex PDF files.
+ *
+ *
+ * @author lanshiqin
+ */
+public final class LightlyPdfToImage {
+
+ private static final int DPI = 100;
+ private static final String FILE_SUFFIX = ".pdf";
+ private static final int ARGS_LENGTH = 2;
+
+ private LightlyPdfToImage() {
+ }
+
+ public static void main(String[] args) throws IOException {
+ if (args.length != ARGS_LENGTH) {
+ System.err.println("usage: " + LightlyPdfToImage.class.getName() + " ");
+ System.exit(1);
+ }
+
+ String pdfPath = args[0];
+ String outputPath = args[1];
+
+ if (!pdfPath.endsWith(FILE_SUFFIX)) {
+ System.err.println("Last argument must be the destination .pdf file");
+ System.exit(1);
+ }
+
+ InputStream in = new URL("file:///" + pdfPath).openStream();
+ // Load document with temp file only
+ // This is very important to prevent OOM when parsing complex PDF files
+ PDDocument document = PDDocument.load(in, MemoryUsageSetting.setupTempFileOnly());
+ try {
+ // no use resource cache, Preventing large objects
+ document.setResourceCache(null);
+ PDFRenderer renderer = new PDFRenderer(document);
+ // Indicates that the renderer is allowed to sub sample the image before drawing.
+ // This is very important to prevent OOM when parsing complex PDF files
+ renderer.setSubsamplingAllowed(true);
+ for (int i = 0; i < document.getNumberOfPages(); i++) {
+ BufferedImage bufferedImage = renderer.renderImageWithDPI(i, DPI);
+ try {
+ ImageIOUtil.writeImage(bufferedImage, outputPath + i + ".png", DPI, -1);
+ } finally {
+ bufferedImage.getGraphics().dispose();
+ }
+ }
+ } finally {
+ document.close();
+ in.close();
+ }
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintBookmarks.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintBookmarks.java
index e7c6509449b..e57ff14fd06 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintBookmarks.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintBookmarks.java
@@ -16,14 +16,17 @@
*/
package org.apache.pdfbox.examples.pdmodel;
+import java.io.File;
+import java.io.IOException;
+
import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
+import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;
-import java.io.File;
-import java.io.IOException;
-
/**
* This is an example on how to access the bookmarks that are part of a pdf document.
*
@@ -55,7 +58,7 @@ public static void main( String[] args ) throws IOException
PDDocumentOutline outline = document.getDocumentCatalog().getDocumentOutline();
if( outline != null )
{
- meta.printBookmark( outline, "" );
+ meta.printBookmark(document, outline, "");
}
else
{
@@ -83,20 +86,68 @@ private static void usage()
/**
* This will print the documents bookmarks to System.out.
*
+ * @param document The document.
* @param bookmark The bookmark to print out.
* @param indentation A pretty printing parameter
*
* @throws IOException If there is an error getting the page count.
*/
- public void printBookmark( PDOutlineNode bookmark, String indentation ) throws IOException
+ public void printBookmark(PDDocument document, PDOutlineNode bookmark, String indentation) throws IOException
{
PDOutlineItem current = bookmark.getFirstChild();
while( current != null )
{
+ // one could also use current.findDestinationPage(document) to get the page number,
+ // but this example does it the hard way to explain the different types
+ // Note that bookmarks can also do completely different things, e.g. link to a website,
+ // or to an external file. This example focuses on internal pages.
+
+ if (current.getDestination() instanceof PDPageDestination)
+ {
+ PDPageDestination pd = (PDPageDestination) current.getDestination();
+ System.out.println(indentation + "Destination page: " + (pd.retrievePageNumber() + 1));
+ }
+ else if (current.getDestination() instanceof PDNamedDestination)
+ {
+ PDPageDestination pd = document.getDocumentCatalog().findNamedDestinationPage((PDNamedDestination) current.getDestination());
+ if (pd != null)
+ {
+ System.out.println(indentation + "Destination page: " + (pd.retrievePageNumber() + 1));
+ }
+ }
+ else if (current.getDestination() != null)
+ {
+ System.out.println(indentation + "Destination class: " + current.getDestination().getClass().getSimpleName());
+ }
+
+ if (current.getAction() instanceof PDActionGoTo)
+ {
+ PDActionGoTo gta = (PDActionGoTo) current.getAction();
+ if (gta.getDestination() instanceof PDPageDestination)
+ {
+ PDPageDestination pd = (PDPageDestination) gta.getDestination();
+ System.out.println(indentation + "Destination page: " + (pd.retrievePageNumber() + 1));
+ }
+ else if (gta.getDestination() instanceof PDNamedDestination)
+ {
+ PDPageDestination pd = document.getDocumentCatalog().findNamedDestinationPage((PDNamedDestination) gta.getDestination());
+ if (pd != null)
+ {
+ System.out.println(indentation + "Destination page: " + (pd.retrievePageNumber() + 1));
+ }
+ }
+ else
+ {
+ System.out.println(indentation + "Destination class: " + gta.getDestination().getClass().getSimpleName());
+ }
+ }
+ else if (current.getAction() != null)
+ {
+ System.out.println(indentation + "Action class: " + current.getAction().getClass().getSimpleName());
+ }
System.out.println( indentation + current.getTitle() );
- printBookmark( current, indentation + " " );
+ printBookmark( document, current, indentation + " " );
current = current.getNextSibling();
}
-
}
}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintURLs.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintURLs.java
index a245cc8fc40..73aa6222122 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintURLs.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintURLs.java
@@ -19,6 +19,8 @@
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -27,7 +29,6 @@
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
-import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.text.PDFTextStripperByArea;
@@ -47,8 +48,8 @@ private PrintURLs()
}
/**
- * This will create a hello world PDF document.
- *
+ * This will output all URLs and the texts in the annotation rectangle of a document.
+ *
* see usage() for commandline
*
* @param args Command line arguments.
@@ -77,10 +78,10 @@ public static void main(String[] args) throws IOException
for( int j=0; j
+ *
* see usage() for commandline
*
* @param args Command line arguments.
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java
index 75a6159ca21..dc26c7c91f4 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java
@@ -37,7 +37,8 @@
/**
- * This is an example on how to add a rubber stamp with an image to pages of a PDF document.
+ * This is an example on how to add a rubber stamp annotation with an image to pages of a PDF
+ * document. To add a normal image, use the AddImageToPDF.java example.
*/
public class RubberStampWithImage
{
@@ -47,7 +48,7 @@ public class RubberStampWithImage
private static final String XOBJECT_DO = "Do\n";
private static final String SPACE = " ";
- private static final NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US );
+ private static final NumberFormat FORMATDECIMAL = NumberFormat.getNumberInstance( Locale.US );
/**
* Add a rubber stamp with an jpg image to every page of the given document.
@@ -86,12 +87,12 @@ public void doIt( String[] args ) throws IOException
PDImageXObject ximage = PDImageXObject.createFromFile(args[2], document);
// define and set the target rectangle
- int lowerLeftX = 250;
- int lowerLeftY = 550;
- int formWidth = 150;
- int formHeight = 25;
- int imgWidth = 50;
- int imgHeight = 25;
+ float lowerLeftX = 250;
+ float lowerLeftY = 550;
+ float formWidth = 150;
+ float formHeight = 25;
+ float imgWidth = 50;
+ float imgHeight = 25;
PDRectangle rect = new PDRectangle();
rect.setLowerLeftX(lowerLeftX);
@@ -139,17 +140,17 @@ private void drawXObject( PDImageXObject xobject, PDResources resources, OutputS
COSName xObjectId = resources.add(xobject);
appendRawCommands( os, SAVE_GRAPHICS_STATE );
- appendRawCommands( os, formatDecimal.format( width ) );
+ appendRawCommands( os, FORMATDECIMAL.format( width ) );
appendRawCommands( os, SPACE );
- appendRawCommands( os, formatDecimal.format( 0 ) );
+ appendRawCommands( os, FORMATDECIMAL.format( 0 ) );
appendRawCommands( os, SPACE );
- appendRawCommands( os, formatDecimal.format( 0 ) );
+ appendRawCommands( os, FORMATDECIMAL.format( 0 ) );
appendRawCommands( os, SPACE );
- appendRawCommands( os, formatDecimal.format( height ) );
+ appendRawCommands( os, FORMATDECIMAL.format( height ) );
appendRawCommands( os, SPACE );
- appendRawCommands( os, formatDecimal.format( x ) );
+ appendRawCommands( os, FORMATDECIMAL.format( x ) );
appendRawCommands( os, SPACE );
- appendRawCommands( os, formatDecimal.format( y ) );
+ appendRawCommands( os, FORMATDECIMAL.format( y ) );
appendRawCommands( os, SPACE );
appendRawCommands( os, CONCATENATE_MATRIX );
appendRawCommands( os, SPACE );
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java
index edd91e8bf1f..c6e0866ec4d 100644
--- a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java
@@ -21,6 +21,7 @@
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.util.Matrix;
/**
* Creates a simple document. The example is taken from the pdf file format specification.
@@ -60,6 +61,15 @@ public static void main(String[] args) throws IOException
contents.addRect(10, 10, 100, 100);
contents.fill();
+ // draw a blue box with rect x=200, y=500, w=200, h=100
+ // 105° rotation is around the bottom left corner
+ contents.saveGraphicsState();
+ contents.setNonStrokingColor(Color.BLUE);
+ contents.transform(Matrix.getRotateInstance(Math.toRadians(105), 200, 500));
+ contents.addRect(0, 0, 200, 100);
+ contents.fill();
+ contents.restoreGraphicsState();
+
contents.close();
doc.save(filename);
}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowTextWithPositioning.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowTextWithPositioning.java
new file mode 100644
index 00000000000..810ec5a477d
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ShowTextWithPositioning.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2017 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding;
+import org.apache.pdfbox.util.Matrix;
+
+/**
+ * This example shows how to justify a string using the showTextWithPositioning method. First only
+ * spaces are adjusted, and then every letter.
+ *
+ * @author Dan Fickling
+ */
+public class ShowTextWithPositioning
+{
+ private static final float FONT_SIZE = 20.0f;
+
+ private ShowTextWithPositioning()
+ {
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ doIt("Hello World, this is a test!", "justify-example.pdf");
+ }
+
+ public static void doIt(String message, String outfile) throws IOException
+ {
+ // the document
+ PDDocument doc = new PDDocument();
+ InputStream is = PDDocument.class.getResourceAsStream("/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf");
+
+ // Page 1
+ PDFont font = PDType0Font.load(doc, is, true);
+ PDPage page = new PDPage(PDRectangle.A4);
+ doc.addPage(page);
+
+ // Get the non-justified string width in text space units.
+ float stringWidth = font.getStringWidth(message) * FONT_SIZE;
+
+ // Get the string height in text space units.
+ float stringHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() * FONT_SIZE;
+
+ // Get the width we have to justify in.
+ PDRectangle pageSize = page.getMediaBox();
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc,
+ page, AppendMode.OVERWRITE, false);
+
+ contentStream.beginText();
+ contentStream.setFont(font, FONT_SIZE);
+
+ // Start at top of page.
+ contentStream.setTextMatrix(
+ Matrix.getTranslateInstance(0, pageSize.getHeight() - stringHeight / 1000f));
+
+ // First show non-justified.
+ contentStream.showText(message);
+
+ // Move to next line.
+ contentStream.setTextMatrix(
+ Matrix.getTranslateInstance(0, pageSize.getHeight() - stringHeight / 1000f * 2));
+
+ // Now show word justified.
+ // The space we have to make up, in text space units.
+ float justifyWidth = pageSize.getWidth() * 1000f - stringWidth;
+
+ List