aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/pdfbox
diff options
context:
space:
mode:
authortknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
committertknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
commit6025b6016517c6d898d8957d1d7e03ba71431912 (patch)
treeb15bd6fa5ffe9588a9bca3f2b8a7e358f83b6eba /src/main/java/org/pdfbox
parentd2c77e820ab4aba8235d71275755021347b3ad10 (diff)
downloadpdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.gz
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.bz2
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.zip
Initial import of release 2.2.REL-2.2@923
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c
Diffstat (limited to 'src/main/java/org/pdfbox')
-rw-r--r--src/main/java/org/pdfbox/Decrypt.java122
-rw-r--r--src/main/java/org/pdfbox/Encrypt.java234
-rw-r--r--src/main/java/org/pdfbox/ExportFDF.java155
-rw-r--r--src/main/java/org/pdfbox/ExportXFDF.java155
-rw-r--r--src/main/java/org/pdfbox/ExtractImages.java210
-rw-r--r--src/main/java/org/pdfbox/ExtractText.java270
-rw-r--r--src/main/java/org/pdfbox/ImportFDF.java156
-rw-r--r--src/main/java/org/pdfbox/ImportXFDF.java157
-rw-r--r--src/main/java/org/pdfbox/Overlay.java530
-rw-r--r--src/main/java/org/pdfbox/PDFDebugger.java420
-rw-r--r--src/main/java/org/pdfbox/PDFReader.java308
-rw-r--r--src/main/java/org/pdfbox/PDFSplit.java227
-rw-r--r--src/main/java/org/pdfbox/PDFToImage.java270
-rw-r--r--src/main/java/org/pdfbox/PrintPDF.java118
-rw-r--r--src/main/java/org/pdfbox/TextToPDF.java259
-rw-r--r--src/main/java/org/pdfbox/Version.java100
-rw-r--r--src/main/java/org/pdfbox/afmparser/AFMParser.java1084
-rw-r--r--src/main/java/org/pdfbox/afmparser/package.html12
-rw-r--r--src/main/java/org/pdfbox/afmtypes/CharMetric.java299
-rw-r--r--src/main/java/org/pdfbox/afmtypes/Composite.java89
-rw-r--r--src/main/java/org/pdfbox/afmtypes/CompositePart.java93
-rw-r--r--src/main/java/org/pdfbox/afmtypes/FontMetric.java891
-rw-r--r--src/main/java/org/pdfbox/afmtypes/KernPair.java110
-rw-r--r--src/main/java/org/pdfbox/afmtypes/Ligature.java76
-rw-r--r--src/main/java/org/pdfbox/afmtypes/TrackKern.java127
-rw-r--r--src/main/java/org/pdfbox/afmtypes/package.html9
-rw-r--r--src/main/java/org/pdfbox/ant/PDFToTextTask.java100
-rw-r--r--src/main/java/org/pdfbox/ant/package.html18
-rw-r--r--src/main/java/org/pdfbox/cmapparser/CMapParser.java285
-rw-r--r--src/main/java/org/pdfbox/cmapparser/package.html9
-rw-r--r--src/main/java/org/pdfbox/cmaptypes/CMap.java161
-rw-r--r--src/main/java/org/pdfbox/cmaptypes/CodespaceRange.java88
-rw-r--r--src/main/java/org/pdfbox/cmaptypes/package.html9
-rw-r--r--src/main/java/org/pdfbox/cos/COSArray.java492
-rw-r--r--src/main/java/org/pdfbox/cos/COSBase.java86
-rw-r--r--src/main/java/org/pdfbox/cos/COSBoolean.java161
-rw-r--r--src/main/java/org/pdfbox/cos/COSDictionary.java1167
-rw-r--r--src/main/java/org/pdfbox/cos/COSDocument.java518
-rw-r--r--src/main/java/org/pdfbox/cos/COSFloat.java173
-rw-r--r--src/main/java/org/pdfbox/cos/COSInteger.java190
-rw-r--r--src/main/java/org/pdfbox/cos/COSName.java572
-rw-r--r--src/main/java/org/pdfbox/cos/COSNull.java88
-rw-r--r--src/main/java/org/pdfbox/cos/COSNumber.java115
-rw-r--r--src/main/java/org/pdfbox/cos/COSObject.java226
-rw-r--r--src/main/java/org/pdfbox/cos/COSStream.java495
-rw-r--r--src/main/java/org/pdfbox/cos/COSString.java403
-rw-r--r--src/main/java/org/pdfbox/cos/ICOSVisitor.java132
-rw-r--r--src/main/java/org/pdfbox/cos/package.html12
-rw-r--r--src/main/java/org/pdfbox/encoding/AFMEncoding.java76
-rw-r--r--src/main/java/org/pdfbox/encoding/DictionaryEncoding.java112
-rw-r--r--src/main/java/org/pdfbox/encoding/Encoding.java268
-rw-r--r--src/main/java/org/pdfbox/encoding/EncodingManager.java87
-rw-r--r--src/main/java/org/pdfbox/encoding/MacRomanEncoding.java267
-rw-r--r--src/main/java/org/pdfbox/encoding/PdfDocEncoding.java289
-rw-r--r--src/main/java/org/pdfbox/encoding/StandardEncoding.java209
-rw-r--r--src/main/java/org/pdfbox/encoding/WinAnsiEncoding.java281
-rw-r--r--src/main/java/org/pdfbox/encoding/package.html9
-rw-r--r--src/main/java/org/pdfbox/encryption/ARCFour.java182
-rw-r--r--src/main/java/org/pdfbox/encryption/DocumentEncryption.java427
-rw-r--r--src/main/java/org/pdfbox/encryption/PDFEncryption.java599
-rw-r--r--src/main/java/org/pdfbox/encryption/package.html9
-rw-r--r--src/main/java/org/pdfbox/examples/AbstractExample.java125
-rw-r--r--src/main/java/org/pdfbox/examples/fdf/PrintFields.java163
-rw-r--r--src/main/java/org/pdfbox/examples/fdf/SetField.java128
-rw-r--r--src/main/java/org/pdfbox/examples/fdf/package.html9
-rw-r--r--src/main/java/org/pdfbox/examples/package.html9
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/AddJavascript.java96
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/AddMessageToEachPage.java148
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java180
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/CreateBlankPDF.java123
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/CreateBookmarks.java119
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/EmbeddedFiles.java170
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/HelloWorld.java137
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/HelloWorldTTF.java134
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java134
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/ImageToPDF.java146
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/PrintBookmarks.java142
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/PrintDocumentMetaData.java165
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/RemoveFirstPage.java98
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/ReplaceString.java186
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/RubberStamp.java113
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/ShowColorBoxes.java135
-rw-r--r--src/main/java/org/pdfbox/examples/pdmodel/package.html9
-rw-r--r--src/main/java/org/pdfbox/examples/persistence/AppendAndFillDoc.java282
-rw-r--r--src/main/java/org/pdfbox/examples/persistence/AppendDoc.java357
-rw-r--r--src/main/java/org/pdfbox/examples/persistence/CopyDoc.java140
-rw-r--r--src/main/java/org/pdfbox/examples/persistence/FieldsDoc.java177
-rw-r--r--src/main/java/org/pdfbox/examples/persistence/WriteDecodedDoc.java151
-rw-r--r--src/main/java/org/pdfbox/examples/persistence/package.html9
-rw-r--r--src/main/java/org/pdfbox/examples/signature/ShowSignature.java168
-rw-r--r--src/main/java/org/pdfbox/examples/signature/package.html9
-rw-r--r--src/main/java/org/pdfbox/examples/util/ExtractTextByArea.java119
-rw-r--r--src/main/java/org/pdfbox/examples/util/PrintTextLocations.java144
-rw-r--r--src/main/java/org/pdfbox/examples/util/package.html9
-rw-r--r--src/main/java/org/pdfbox/exceptions/COSVisitorException.java53
-rw-r--r--src/main/java/org/pdfbox/exceptions/CryptographyException.java82
-rw-r--r--src/main/java/org/pdfbox/exceptions/InvalidPasswordException.java51
-rw-r--r--src/main/java/org/pdfbox/exceptions/OutlineNotLocalException.java55
-rw-r--r--src/main/java/org/pdfbox/exceptions/WrappedException.java75
-rw-r--r--src/main/java/org/pdfbox/exceptions/WrappedIOException.java76
-rw-r--r--src/main/java/org/pdfbox/exceptions/package.html9
-rw-r--r--src/main/java/org/pdfbox/filter/ASCII85Filter.java103
-rw-r--r--src/main/java/org/pdfbox/filter/ASCIIHexFilter.java205
-rw-r--r--src/main/java/org/pdfbox/filter/CCITTFaxDecodeFilter.java735
-rw-r--r--src/main/java/org/pdfbox/filter/DCTFilter.java77
-rw-r--r--src/main/java/org/pdfbox/filter/Filter.java68
-rw-r--r--src/main/java/org/pdfbox/filter/FilterManager.java135
-rw-r--r--src/main/java/org/pdfbox/filter/FlateFilter.java303
-rw-r--r--src/main/java/org/pdfbox/filter/LZWDictionary.java215
-rw-r--r--src/main/java/org/pdfbox/filter/LZWFilter.java235
-rw-r--r--src/main/java/org/pdfbox/filter/LZWNode.java115
-rw-r--r--src/main/java/org/pdfbox/filter/RunLengthDecodeFilter.java126
-rw-r--r--src/main/java/org/pdfbox/filter/package.html9
-rw-r--r--src/main/java/org/pdfbox/io/ASCII85InputStream.java269
-rw-r--r--src/main/java/org/pdfbox/io/ASCII85OutputStream.java304
-rw-r--r--src/main/java/org/pdfbox/io/ByteArrayPushBackInputStream.java404
-rw-r--r--src/main/java/org/pdfbox/io/FastByteArrayOutputStream.java62
-rw-r--r--src/main/java/org/pdfbox/io/NBitInputStream.java124
-rw-r--r--src/main/java/org/pdfbox/io/NBitOutputStream.java116
-rw-r--r--src/main/java/org/pdfbox/io/PushBackInputStream.java92
-rw-r--r--src/main/java/org/pdfbox/io/RandomAccessFileInputStream.java132
-rw-r--r--src/main/java/org/pdfbox/io/RandomAccessFileOutputStream.java145
-rw-r--r--src/main/java/org/pdfbox/io/package.html9
-rw-r--r--src/main/java/org/pdfbox/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdfparser/BaseParser.java1369
-rw-r--r--src/main/java/org/pdfbox/pdfparser/PDFObjectStreamParser.java137
-rw-r--r--src/main/java/org/pdfbox/pdfparser/PDFParser.java557
-rw-r--r--src/main/java/org/pdfbox/pdfparser/PDFStreamParser.java403
-rw-r--r--src/main/java/org/pdfbox/pdfparser/PDFXref.java96
-rw-r--r--src/main/java/org/pdfbox/pdfparser/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java83
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/MapEntry.java105
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java102
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java145
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java332
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PageDrawer.java268
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PageWrapper.java118
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java86
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdfwriter/COSStandardOutputStream.java180
-rw-r--r--src/main/java/org/pdfbox/pdfwriter/COSWriter.java1091
-rw-r--r--src/main/java/org/pdfbox/pdfwriter/COSWriterXRefEntry.java165
-rw-r--r--src/main/java/org/pdfbox/pdfwriter/ContentStreamWriter.java200
-rw-r--r--src/main/java/org/pdfbox/pdfwriter/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDDestinationNameTreeNode.java90
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDDocument.java725
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDDocumentCatalog.java378
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDDocumentInformation.java297
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDDocumentNameDictionary.java164
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java89
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDPage.java776
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDPageNode.java459
-rw-r--r--src/main/java/org/pdfbox/pdmodel/PDResources.java313
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/COSArrayList.java643
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/COSDictionaryMap.java278
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/COSObjectable.java49
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/COSStreamArray.java304
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/DualCOSObjectable.java56
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDMatrix.java120
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDMemoryStream.java284
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDMetadata.java87
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDNameTreeNode.java337
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDNamedTextStream.java137
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDObjectStream.java151
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDRange.java146
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDRectangle.java295
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDStream.java538
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/PDTextStream.java180
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java326
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java298
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java83
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java95
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/filespecification/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/common/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java149
-rw-r--r--src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java76
-rw-r--r--src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html10
-rw-r--r--src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java222
-rw-r--r--src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java648
-rw-r--r--src/main/java/org/pdfbox/pdmodel/edit/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java193
-rw-r--r--src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionManager.java131
-rw-r--r--src/main/java/org/pdfbox/pdmodel/encryption/PDStandardEncryption.java416
-rw-r--r--src/main/java/org/pdfbox/pdmodel/encryption/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFAnnotation.java114
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFCatalog.java195
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFDictionary.java465
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFDocument.java377
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFField.java763
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFIconFit.java227
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFJavaScript.java172
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFNamedPageReference.java128
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFOptionElement.java129
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFPage.java148
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFPageInfo.java85
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/FDFTemplate.java167
-rw-r--r--src/main/java/org/pdfbox/pdmodel/fdf/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDCIDFont.java248
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType0Font.java66
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType2Font.java66
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDFont.java863
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptor.java530
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorAFM.java446
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java580
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDFontFactory.java108
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDMMType1Font.java62
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDSimpleFont.java239
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDTrueTypeFont.java437
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDType0Font.java129
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDType1AfmPfbFont.java206
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDType1Font.java267
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/PDType3Font.java152
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/Type3StreamParser.java607
-rw-r--r--src/main/java/org/pdfbox/pdmodel/font/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java724
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/PDFontSetting.java133
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/PDGraphicsState.java438
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/PDLineDashPattern.java135
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalGray.java240
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalRGB.java289
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpace.java97
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java218
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceInstance.java130
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java137
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceGray.java112
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceN.java244
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceNAttributes.java126
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java130
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDGamma.java151
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDICCBased.java343
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDIndexed.java271
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDLab.java300
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDPattern.java122
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDSeparation.java198
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/PDTristimulus.java155
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/color/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/Average.java81
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/None.java104
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/Paeth.java121
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java336
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/Sub.java86
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/Up.java100
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/Uptimum.java153
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/predictor/package.html10
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDCcitt.java598
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java201
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDJpeg.java156
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java236
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObject.java207
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java120
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java244
-rw-r--r--src/main/java/org/pdfbox/pdmodel/graphics/xobject/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/PDActionFactory.java96
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java106
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java380
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java238
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java216
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java150
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDAction.java187
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionGoTo.java92
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java102
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java244
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java187
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionURI.java183
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java180
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/action/type/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java503
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java153
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java54
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java65
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java245
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java146
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/annotation/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java85
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java125
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java142
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java155
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java102
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java132
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java188
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java134
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java159
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java62
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java425
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java320
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java328
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java645
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java187
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java95
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java127
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java610
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java218
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java84
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java170
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java90
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java64
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java72
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java324
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java152
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java234
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java365
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/package.html9
-rw-r--r--src/main/java/org/pdfbox/pdmodel/text/PDTextState.java286
-rw-r--r--src/main/java/org/pdfbox/pdmodel/text/package.html9
-rw-r--r--src/main/java/org/pdfbox/persistence/util/COSHEXTable.java566
-rw-r--r--src/main/java/org/pdfbox/persistence/util/COSObjectKey.java131
-rw-r--r--src/main/java/org/pdfbox/persistence/util/package.html9
-rw-r--r--src/main/java/org/pdfbox/pfb/PfbParser.java211
-rw-r--r--src/main/java/org/pdfbox/pfb/package.html9
-rw-r--r--src/main/java/org/pdfbox/searchengine/lucene/IndexFiles.java308
-rw-r--r--src/main/java/org/pdfbox/searchengine/lucene/LucenePDFDocument.java387
-rw-r--r--src/main/java/org/pdfbox/searchengine/lucene/package.html9
-rw-r--r--src/main/java/org/pdfbox/searchengine/package.html9
-rw-r--r--src/main/java/org/pdfbox/ttf/CMAPEncodingEntry.java219
-rw-r--r--src/main/java/org/pdfbox/ttf/CMAPTable.java122
-rw-r--r--src/main/java/org/pdfbox/ttf/DigitalSignatureTable.java45
-rw-r--r--src/main/java/org/pdfbox/ttf/GlyphData.java125
-rw-r--r--src/main/java/org/pdfbox/ttf/GlyphTable.java88
-rw-r--r--src/main/java/org/pdfbox/ttf/HeaderTable.java332
-rw-r--r--src/main/java/org/pdfbox/ttf/HorizontalHeaderTable.java332
-rw-r--r--src/main/java/org/pdfbox/ttf/HorizontalMetricsTable.java95
-rw-r--r--src/main/java/org/pdfbox/ttf/IndexToLocationTable.java96
-rw-r--r--src/main/java/org/pdfbox/ttf/MaximumProfileTable.java300
-rw-r--r--src/main/java/org/pdfbox/ttf/MemoryTTFDataStream.java231
-rw-r--r--src/main/java/org/pdfbox/ttf/NameRecord.java242
-rw-r--r--src/main/java/org/pdfbox/ttf/NamingTable.java112
-rw-r--r--src/main/java/org/pdfbox/ttf/OS2WindowsMetricsTable.java694
-rw-r--r--src/main/java/org/pdfbox/ttf/PostScriptTable.java304
-rw-r--r--src/main/java/org/pdfbox/ttf/RAFDataStream.java193
-rw-r--r--src/main/java/org/pdfbox/ttf/TTFDataStream.java253
-rw-r--r--src/main/java/org/pdfbox/ttf/TTFParser.java219
-rw-r--r--src/main/java/org/pdfbox/ttf/TTFTable.java115
-rw-r--r--src/main/java/org/pdfbox/ttf/TrueTypeFont.java221
-rw-r--r--src/main/java/org/pdfbox/ttf/package.html9
-rw-r--r--src/main/java/org/pdfbox/util/BitFlagHelper.java85
-rw-r--r--src/main/java/org/pdfbox/util/BoundingBox.java188
-rw-r--r--src/main/java/org/pdfbox/util/DateConverter.java281
-rw-r--r--src/main/java/org/pdfbox/util/DefaultFileFilter.java285
-rw-r--r--src/main/java/org/pdfbox/util/ErrorLogger.java72
-rw-r--r--src/main/java/org/pdfbox/util/ImageParameters.java234
-rw-r--r--src/main/java/org/pdfbox/util/Matrix.java350
-rw-r--r--src/main/java/org/pdfbox/util/PDFHighlighter.java213
-rw-r--r--src/main/java/org/pdfbox/util/PDFOperator.java153
-rw-r--r--src/main/java/org/pdfbox/util/PDFStreamEngine.java622
-rw-r--r--src/main/java/org/pdfbox/util/PDFText2HTML.java271
-rw-r--r--src/main/java/org/pdfbox/util/PDFTextStripper.java1033
-rw-r--r--src/main/java/org/pdfbox/util/PDFTextStripperByArea.java165
-rw-r--r--src/main/java/org/pdfbox/util/ResourceLoader.java169
-rw-r--r--src/main/java/org/pdfbox/util/SimpleConfigurator.java68
-rw-r--r--src/main/java/org/pdfbox/util/Splitter.java201
-rw-r--r--src/main/java/org/pdfbox/util/TextPosition.java203
-rw-r--r--src/main/java/org/pdfbox/util/TextPositionComparator.java126
-rw-r--r--src/main/java/org/pdfbox/util/XMLUtil.java103
-rw-r--r--src/main/java/org/pdfbox/util/operator/BeginText.java68
-rw-r--r--src/main/java/org/pdfbox/util/operator/Concatenate.java106
-rw-r--r--src/main/java/org/pdfbox/util/operator/EndText.java67
-rw-r--r--src/main/java/org/pdfbox/util/operator/GRestore.java67
-rw-r--r--src/main/java/org/pdfbox/util/operator/GSave.java66
-rw-r--r--src/main/java/org/pdfbox/util/operator/Invoke.java113
-rw-r--r--src/main/java/org/pdfbox/util/operator/MoveAndShow.java75
-rw-r--r--src/main/java/org/pdfbox/util/operator/MoveText.java76
-rw-r--r--src/main/java/org/pdfbox/util/operator/MoveTextSetLeading.java80
-rw-r--r--src/main/java/org/pdfbox/util/operator/NextLine.java82
-rw-r--r--src/main/java/org/pdfbox/util/operator/OperatorProcessor.java93
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetCharSpacing.java79
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetGraphicsStateParameters.java72
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetHorizontalTextScaling.java64
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetLineWidth.java64
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetMatrix.java90
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetMoveAndShow.java80
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetNonStrokingCMYKColor.java69
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetNonStrokingColorSpace.java86
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetNonStrokingRGBColor.java69
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetStrokingCMYKColor.java71
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetStrokingColorSpace.java89
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetStrokingRGBColor.java71
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetTextFont.java96
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetTextLeading.java69
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetTextRenderingMode.java64
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetTextRise.java64
-rw-r--r--src/main/java/org/pdfbox/util/operator/SetWordSpacing.java68
-rw-r--r--src/main/java/org/pdfbox/util/operator/ShowText.java73
-rw-r--r--src/main/java/org/pdfbox/util/operator/ShowTextGlyph.java99
-rw-r--r--src/main/java/org/pdfbox/util/operator/package.html9
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java77
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/BeginInlineImage.java114
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/ClosePath.java59
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/CurveTo.java73
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java69
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java76
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java71
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java71
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/Invoke.java180
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/LineTo.java65
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/MoveTo.java68
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetLineWidth.java65
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingCMYKColor.java64
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingColorSpace.java71
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingRGBColor.java65
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingCMYKColor.java64
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingColorSpace.java70
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingRGBColor.java65
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/StrokePath.java73
-rw-r--r--src/main/java/org/pdfbox/util/operator/pagedrawer/package.html9
-rw-r--r--src/main/java/org/pdfbox/util/package.html9
415 files changed, 78282 insertions, 0 deletions
diff --git a/src/main/java/org/pdfbox/Decrypt.java b/src/main/java/org/pdfbox/Decrypt.java
new file mode 100644
index 0000000..b338552
--- /dev/null
+++ b/src/main/java/org/pdfbox/Decrypt.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+/**
+ * This will read a document from the filesystem, decrypt it and and then write
+ * the results to the filesystem. <br/><br/>
+ *
+ * usage: java org.pdfbox.Decrypt &lt;password&gt; &lt;inputfile&gt; &lt;outputfile&gt;
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class Decrypt
+{
+
+ /**
+ * This is the entry point for the application.
+ *
+ * @param args The command-line arguments.
+ *
+ * @throws Exception If there is an error decrypting the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ Decrypt decrypt = new Decrypt();
+ decrypt.decrypt( args );
+ }
+
+ private void decrypt( String[] args ) throws Exception
+ {
+ if( args.length < 2 || args.length > 3 )
+ {
+ usage();
+ }
+ else
+ {
+ String password = args[0];
+ String infile = args[1];
+ String outfile = args[1];
+ if( args.length == 3 )
+ {
+ outfile = args[2];
+ }
+
+
+ PDDocument document = null;
+
+ try
+ {
+ document = PDDocument.load( infile );
+
+ if( document.isEncrypted() )
+ {
+ if( document.isOwnerPassword( password ) )
+ {
+ document.decrypt( password );
+ document.save( outfile );
+ }
+ else
+ {
+ throw new IOException(
+ "Error: You are only allowed to decrypt a document with the owner password." );
+ }
+ }
+ else
+ {
+ System.err.println( "Error: Document is not encrypted." );
+ }
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print a usage message.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: java org.pdfbox.Decrypt " +
+ "<password> <inputfile> [outputfile]" );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/Encrypt.java b/src/main/java/org/pdfbox/Encrypt.java
new file mode 100644
index 0000000..9038e61
--- /dev/null
+++ b/src/main/java/org/pdfbox/Encrypt.java
@@ -0,0 +1,234 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.encryption.PDStandardEncryption;
+import org.pdfbox.pdmodel.encryption.PDEncryptionDictionary;
+
+/**
+ * This will read a document from the filesystem, encrypt it and and then write
+ * the results to the filesystem. <br/><br/>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class Encrypt
+{
+
+ /**
+ * This is the entry point for the application.
+ *
+ * @param args The command-line arguments.
+ *
+ * @throws Exception If there is an error decrypting the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ Encrypt encrypt = new Encrypt();
+ encrypt.encrypt( args );
+ }
+
+ private void encrypt( String[] args ) throws Exception
+ {
+ if( args.length < 1 )
+ {
+ usage();
+ }
+ else
+ {
+ String infile = null;
+ String outfile = null;
+ String userPassword = "";
+ String ownerPassword = "";
+
+ PDDocument document = null;
+
+ try
+ {
+
+ PDStandardEncryption enc = new PDStandardEncryption();
+ enc.setVersion( PDEncryptionDictionary.VERSION1_40_BIT_ALGORITHM );
+ enc.setRevision( PDStandardEncryption.REVISION2 );
+ for( int i=0; i<args.length; i++ )
+ {
+ String key = args[i];
+ if( key.equals( "-O" ) )
+ {
+ ownerPassword = args[++i];
+ }
+ else if( key.equals( "-U" ) )
+ {
+ userPassword = args[++i];
+ }
+ else if( key.equals( "-canAssemble" ) )
+ {
+ enc.setCanAssembleDocument( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canExtractContent" ) )
+ {
+ enc.setCanExtractContent( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canExtractForAccessibility" ) )
+ {
+ enc.setCanExtractForAccessibility( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canFillInForm" ) )
+ {
+ enc.setCanFillInForm( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canModify" ) )
+ {
+ enc.setCanModify( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canModifyAnnotations" ) )
+ {
+ enc.setCanModifyAnnotations( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canPrint" ) )
+ {
+ enc.setCanPrint( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-canPrintDegraded" ) )
+ {
+ enc.setCanPrintDegraded( args[++i].equalsIgnoreCase( "true" ) );
+ }
+ else if( key.equals( "-keyLength" ) )
+ {
+ try
+ {
+ enc.setLength( Integer.parseInt( args[++i] ) );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new NumberFormatException(
+ "Error: -keyLength is not an integer '" + args[i] + "'" );
+ }
+ }
+ else if( key.equals( "-version" ) )
+ {
+ try
+ {
+ enc.setVersion( Integer.parseInt( args[++i] ) );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new NumberFormatException( "Error: -version is not an integer '" + args[i] + "'" );
+ }
+ }
+ else if( key.equals( "-revision" ) )
+ {
+ try
+ {
+ enc.setRevision( Integer.parseInt( args[++i] ) );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new NumberFormatException( "Error: -version is not an integer '" + args[i] + "'" );
+ }
+ }
+ else if( infile == null )
+ {
+ infile = key;
+ }
+ else if( outfile == null )
+ {
+ outfile = key;
+ }
+ else
+ {
+ usage();
+ }
+ }
+ if( infile == null )
+ {
+ usage();
+ }
+ if( outfile == null )
+ {
+ outfile = infile;
+ }
+ document = PDDocument.load( infile );
+
+ if( !document.isEncrypted() )
+ {
+ document.setEncryptionDictionary( enc );
+ document.encrypt( ownerPassword, userPassword );
+ document.save( outfile );
+ }
+ else
+ {
+ System.err.println( "Error: Document is already encrypted." );
+ }
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print a usage message.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: java org.pdfbox.Encrypt [options] <inputfile> [outputfile]" );
+ System.err.println( " -O <password> Set the owner password" );
+ System.err.println( " -U <password> Set the user password" );
+ System.err.println( " -canAssemble <true|false> Set the assemble permission" );
+ System.err.println( " -canExtractContent <true|false> Set the extraction permission" );
+ System.err.println( " -canExtractForAccessibility <true|false> Set the extraction permission" );
+ System.err.println( " -canFillInForm <true|false> Set the fill in form permission" );
+ System.err.println( " -canModify <true|false> Set the modify permission" );
+ System.err.println( " -canModifyAnnotations <true|false> Set the modify annots permission" );
+ System.err.println( " -canPrint <true|false> Set the print permission" );
+ System.err.println( " -canPrintDegraded <true|false> Set the print degraded permission" );
+ System.err.println( " -keyLength <length> The length of the key in bits(40)" );
+ System.err.println( " -version <0|1|2|3|4> The encryption version(1)" );
+ System.err.println( " 0 - Undocumented/Unpublished" );
+ System.err.println( " 1 - Allows for 40 bit encryption" );
+ System.err.println( " 2 - Allows for variable length encryption" );
+ System.err.println( " 3 - Unpublished but allows for key lengths from 40 to 128" );
+ System.err.println( " 4 - Security handler defines encryption" );
+ System.err.println( " -revision <2|3|4> The encryption revision(2)" );
+ System.err.println( " 2 - version less than 2" );
+ System.err.println( " 3 - version 2 or 3 or uses a revision 3 permission" );
+ System.err.println( " 4 - version of 4" );
+ System.err.println( "\nNote: By default all permissions are set to true!" );
+ System.exit( 1 );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ExportFDF.java b/src/main/java/org/pdfbox/ExportFDF.java
new file mode 100644
index 0000000..d8f6246
--- /dev/null
+++ b/src/main/java/org/pdfbox/ExportFDF.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+
+/**
+ * This example will take a PDF document and fill the fields with data from the
+ * FDF fields.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class ExportFDF
+{
+ /**
+ * Creates a new instance of ImportFDF.
+ */
+ public ExportFDF()
+ {
+ }
+
+ /**
+ * This will import an fdf document and write out another pdf.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ *
+ * @throws Exception If there is an error importing the FDF document.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ ExportFDF exporter = new ExportFDF();
+ exporter.exportFDF( args );
+ }
+
+ private void exportFDF( String[] args ) throws Exception
+ {
+ PDDocument pdf = null;
+ FDFDocument fdf = null;
+
+ try
+ {
+ if( args.length != 1 && args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ pdf = PDDocument.load( args[0] );
+ PDAcroForm form = pdf.getDocumentCatalog().getAcroForm();
+ if( form == null )
+ {
+ System.err.println( "Error: This PDF does not contain a form." );
+ }
+ else
+ {
+ String fdfName = null;
+ if( args.length == 2 )
+ {
+ fdfName = args[1];
+ }
+ else
+ {
+ if( args[0].length() > 4 )
+ {
+ fdfName = args[0].substring( 0, args[0].length() -4 ) + ".fdf";
+ }
+ }
+ fdf = form.exportFDF();
+ fdf.save( fdfName );
+ }
+ }
+ }
+ finally
+ {
+ close( fdf );
+ close( pdf );
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: org.pdfbox.ExortFDF <pdf-file> [output-fdf-file]" );
+ System.err.println( " [output-fdf-file] - Default is pdf name, test.pdf->test.fdf" );
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( FDFDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( PDDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ExportXFDF.java b/src/main/java/org/pdfbox/ExportXFDF.java
new file mode 100644
index 0000000..a1cfcf2
--- /dev/null
+++ b/src/main/java/org/pdfbox/ExportXFDF.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+
+/**
+ * This example will take a PDF document and fill the fields with data from the
+ * FDF fields.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class ExportXFDF
+{
+ /**
+ * Creates a new instance of ImportFDF.
+ */
+ public ExportXFDF()
+ {
+ }
+
+ /**
+ * This will import an fdf document and write out another pdf.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ *
+ * @throws Exception If there is an error importing the FDF document.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ ExportXFDF exporter = new ExportXFDF();
+ exporter.exportXFDF( args );
+ }
+
+ private void exportXFDF( String[] args ) throws Exception
+ {
+ PDDocument pdf = null;
+ FDFDocument fdf = null;
+
+ try
+ {
+ if( args.length != 1 && args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ pdf = PDDocument.load( args[0] );
+ PDAcroForm form = pdf.getDocumentCatalog().getAcroForm();
+ if( form == null )
+ {
+ System.err.println( "Error: This PDF does not contain a form." );
+ }
+ else
+ {
+ String fdfName = null;
+ if( args.length == 2 )
+ {
+ fdfName = args[1];
+ }
+ else
+ {
+ if( args[0].length() > 4 )
+ {
+ fdfName = args[0].substring( 0, args[0].length() -4 ) + ".xfdf";
+ }
+ }
+ fdf = form.exportFDF();
+ fdf.saveXFDF( fdfName );
+ }
+ }
+ }
+ finally
+ {
+ close( fdf );
+ close( pdf );
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: org.pdfbox.ExortXFDF <pdf-file> [output-xfdf-file]" );
+ System.err.println( " [output-xfdf-file] - Default is pdf name, test.pdf->test.xfdf" );
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( FDFDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( PDDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ExtractImages.java b/src/main/java/org/pdfbox/ExtractImages.java
new file mode 100644
index 0000000..a8e46ee
--- /dev/null
+++ b/src/main/java/org/pdfbox/ExtractImages.java
@@ -0,0 +1,210 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.encryption.PDEncryptionDictionary;
+import org.pdfbox.pdmodel.encryption.PDStandardEncryption;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+
+/**
+ * This will read a read pdf and extract images. <br/><br/>
+ *
+ * usage: java org.pdfbox.ExtractImages &lt;pdffile&gt; &lt;password&gt; [imageprefix]
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class ExtractImages
+{
+ private int imageCounter = 1;
+
+ private static final String PASSWORD = "-password";
+ private static final String PREFIX = "-prefix";
+
+ /**
+ * This is the entry point for the application.
+ *
+ * @param args The command-line arguments.
+ *
+ * @throws Exception If there is an error decrypting the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ ExtractImages extractor = new ExtractImages();
+ extractor.extractImages( args );
+ }
+
+ private void extractImages( String[] args ) throws Exception
+ {
+ if( args.length < 1 || args.length > 3 )
+ {
+ usage();
+ }
+ else
+ {
+ String pdfFile = null;
+ String password = "";
+ String prefix = null;
+ for( int i=0; i<args.length; i++ )
+ {
+ if( args[i].equals( PASSWORD ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ password = args[i];
+ }
+ else if( args[i].equals( PREFIX ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ prefix = args[i];
+ }
+ else
+ {
+ if( pdfFile == null )
+ {
+ pdfFile = args[i];
+ }
+ }
+ }
+ if( prefix == null && pdfFile.length() >4 )
+ {
+ prefix = pdfFile.substring( 0, pdfFile.length() -4 );
+ }
+
+ PDDocument document = null;
+
+ try
+ {
+ document = PDDocument.load( pdfFile );
+
+ if( document.isEncrypted() )
+ {
+ if( document.isOwnerPassword( password ) )
+ {
+ document.decrypt( password );
+ }
+ else
+ {
+ throw new IOException(
+ "Error: You are only allowed to extract images with the owner password." );
+ }
+ }
+
+ PDEncryptionDictionary encDictionary = document.getEncryptionDictionary();
+
+ //only care about standard encryption and if it was decrypted with the
+ //user password
+ if( encDictionary instanceof PDStandardEncryption &&
+ !document.wasDecryptedWithOwnerPassword() )
+ {
+ PDStandardEncryption stdEncryption = (PDStandardEncryption)encDictionary;
+ if( !stdEncryption.canExtractContent() )
+ {
+ throw new IOException( "You do not have permission to extract images." );
+ }
+ }
+
+ List pages = document.getDocumentCatalog().getAllPages();
+ Iterator iter = pages.iterator();
+ while( iter.hasNext() )
+ {
+ PDPage page = (PDPage)iter.next();
+ PDResources resources = page.getResources();
+ Map images = resources.getImages();
+ if( images != null )
+ {
+ Iterator imageIter = images.keySet().iterator();
+ while( imageIter.hasNext() )
+ {
+ String key = (String)imageIter.next();
+ PDXObjectImage image = (PDXObjectImage)images.get( key );
+ String name = getUniqueFileName( key, image.getSuffix() );
+ System.out.println( "Writing image:" + name );
+ image.write2file( name );
+ }
+ }
+ }
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ private String getUniqueFileName( String prefix, String suffix )
+ {
+ String uniqueName = null;
+ File f = null;
+ while( f == null || f.exists() )
+ {
+ uniqueName = prefix + "-" + imageCounter;
+ f = new File( uniqueName + "." + suffix );
+ imageCounter++;
+ }
+ return uniqueName;
+ }
+
+ /**
+ * This will print the usage requirements and exit.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.ExtractImages [OPTIONS] <PDF file>\n" +
+ " -password <password> Password to decrypt document\n" +
+ " -suffix <password> Image suffix(default to pdf name)\n" +
+ " <PDF file> The PDF document to use\n"
+ );
+ System.exit( 1 );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ExtractText.java b/src/main/java/org/pdfbox/ExtractText.java
new file mode 100644
index 0000000..5f5a328
--- /dev/null
+++ b/src/main/java/org/pdfbox/ExtractText.java
@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.util.PDFText2HTML;
+import org.pdfbox.util.PDFTextStripper;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This is the main program that simply parses the pdf document and transforms it
+ * into text.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.9 $
+ */
+public class ExtractText
+{
+ private static final Logger LOG = Logger.getLogger( ExtractText.class );
+
+ /**
+ * This is the default encoding of the text to be output.
+ */
+ public static final String DEFAULT_ENCODING =
+ null;
+ //"ISO-8859-1";
+ //"ISO-8859-6"; //arabic
+ //"US-ASCII";
+ //"UTF-8";
+ //"UTF-16";
+ //"UTF-16BE";
+ //"UTF-16LE";
+
+
+ private static final String PASSWORD = "-password";
+ private static final String ENCODING = "-encoding";
+ private static final String CONSOLE = "-console";
+ private static final String START_PAGE = "-startPage";
+ private static final String END_PAGE = "-endPage";
+ private static final String SORT = "-sort";
+ private static final String HTML = "-html"; // jjb - added simple HTML output
+
+ /**
+ * private constructor.
+ */
+ private ExtractText()
+ {
+ //static class
+ }
+
+ /**
+ * Infamous main method.
+ *
+ * @param args Command line arguments, should be one and a reference to a file.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ boolean toConsole = false;
+ boolean toHTML = false;
+ boolean sort = false;
+ String password = "";
+ String encoding = DEFAULT_ENCODING;
+ String pdfFile = null;
+ String textFile = null;
+ int startPage = 1;
+ int endPage = Integer.MAX_VALUE;
+ for( int i=0; i<args.length; i++ )
+ {
+ if( args[i].equals( PASSWORD ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ password = args[i];
+ }
+ else if( args[i].equals( ENCODING ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ encoding = args[i];
+ }
+ else if( args[i].equals( START_PAGE ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ startPage = Integer.parseInt( args[i] );
+ }
+ else if( args[i].equals( HTML ) )
+ {
+ toHTML = true;
+ }
+ else if( args[i].equals( SORT ) )
+ {
+ sort = true;
+ }
+ else if( args[i].equals( END_PAGE ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ endPage = Integer.parseInt( args[i] );
+ }
+ else if( args[i].equals( CONSOLE ) )
+ {
+ toConsole = true;
+ }
+ else
+ {
+ if( pdfFile == null )
+ {
+ pdfFile = args[i];
+ }
+ else
+ {
+ textFile = args[i];
+ }
+ }
+ }
+
+ if( pdfFile == null )
+ {
+ usage();
+ }
+
+ if( textFile == null && pdfFile.length() >4 )
+ {
+ textFile = pdfFile.substring( 0, pdfFile.length() -4 ) + ".txt";
+ }
+
+ Writer output = null;
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( pdfFile );
+
+ //document.print();
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( password );
+ }
+ catch( InvalidPasswordException e )
+ {
+ if( args.length == 4 )//they supplied the wrong password
+ {
+ System.err.println( "Error: The supplied password is incorrect." );
+ System.exit( 2 );
+ }
+ else
+ {
+ //they didn't suppply a password and the default of "" was wrong.
+ System.err.println( "Error: The document is encrypted." );
+ usage();
+ }
+ }
+ }
+ if( toConsole )
+ {
+ output = new OutputStreamWriter( System.out );
+ }
+ else
+ {
+ if( encoding != null )
+ {
+ output = new OutputStreamWriter(
+ new FileOutputStream( textFile ), encoding );
+ }
+ else
+ {
+ //use default encoding
+ output = new OutputStreamWriter(
+ new FileOutputStream( textFile ) );
+ }
+ }
+
+ PDFTextStripper stripper = null;
+ if(toHTML)
+ {
+ stripper = new PDFText2HTML();
+ }
+ else
+ {
+ stripper = new PDFTextStripper();
+ }
+ stripper.setSortByPosition( sort );
+ stripper.setStartPage( startPage );
+ stripper.setEndPage( endPage );
+ stripper.writeText( document, output );
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.close();
+ }
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+
+ /**
+ * This will print the usage requirements and exit.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.ExtractText [OPTIONS] <PDF file> [Text File]\n" +
+ " -password <password> Password to decrypt document\n" +
+ " -encoding <output encoding> (ISO-8859-1,UTF-16BE,UTF-16LE,...)\n" +
+ " -console Send text to console instead of file\n" +
+ " -html Output in HTML format instead of raw text\n" +
+ " -sort Sort the text before writing\n" +
+ " -startPage <number> The first page to start extraction(1 based)\n" +
+ " -endPage <number> The last page to extract(inclusive)\n" +
+ " <PDF file> The PDF document to use\n" +
+ " [Text File] The file to write the text to\n"
+ );
+ System.exit( 1 );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ImportFDF.java b/src/main/java/org/pdfbox/ImportFDF.java
new file mode 100644
index 0000000..5a0cf27
--- /dev/null
+++ b/src/main/java/org/pdfbox/ImportFDF.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+
+/**
+ * This example will take a PDF document and fill the fields with data from the
+ * FDF fields.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class ImportFDF
+{
+ /**
+ * Creates a new instance of ImportFDF.
+ */
+ public ImportFDF()
+ {
+ }
+
+ /**
+ * This will takes the values from the fdf document and import them into the
+ * PDF document.
+ *
+ * @param pdfDocument The document to put the fdf data into.
+ * @param fdfDocument The FDF document to get the data from.
+ *
+ * @throws IOException If there is an error setting the data in the field.
+ */
+ public void importFDF( PDDocument pdfDocument, FDFDocument fdfDocument ) throws IOException
+ {
+ PDDocumentCatalog docCatalog = pdfDocument.getDocumentCatalog();
+ PDAcroForm acroForm = docCatalog.getAcroForm();
+ acroForm.setCacheFields( true );
+ acroForm.importFDF( fdfDocument );
+ }
+
+ /**
+ * This will import an fdf document and write out another pdf.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ *
+ * @throws Exception If there is an error importing the FDF document.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ ImportFDF importer = new ImportFDF();
+ importer.importFDF( args );
+ }
+
+ private void importFDF( String[] args ) throws Exception
+ {
+ PDDocument pdf = null;
+ FDFDocument fdf = null;
+
+ try
+ {
+ if( args.length != 3 )
+ {
+ usage();
+ }
+ else
+ {
+ ImportFDF importer = new ImportFDF();
+
+ pdf = PDDocument.load( args[0] );
+ fdf = FDFDocument.load( args[1] );
+ importer.importFDF( pdf, fdf );
+
+ pdf.save( args[2] );
+ }
+ }
+ finally
+ {
+ close( fdf );
+ close( pdf );
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: org.pdfbox.ImportFDF <pdf-file> <fdf-file> <output-file>" );
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( FDFDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( PDDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ImportXFDF.java b/src/main/java/org/pdfbox/ImportXFDF.java
new file mode 100644
index 0000000..24987a4
--- /dev/null
+++ b/src/main/java/org/pdfbox/ImportXFDF.java
@@ -0,0 +1,157 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+
+import java.io.IOException;
+
+
+/**
+ * This example will take a PDF document and fill the fields with data from the
+ * XFDF fields.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class ImportXFDF
+{
+ /**
+ * Creates a new instance of ImportFDF.
+ */
+ public ImportXFDF()
+ {
+ }
+
+ /**
+ * This will takes the values from the fdf document and import them into the
+ * PDF document.
+ *
+ * @param pdfDocument The document to put the fdf data into.
+ * @param fdfDocument The FDF document to get the data from.
+ *
+ * @throws IOException If there is an error setting the data in the field.
+ */
+ public void importFDF( PDDocument pdfDocument, FDFDocument fdfDocument ) throws IOException
+ {
+ PDDocumentCatalog docCatalog = pdfDocument.getDocumentCatalog();
+ PDAcroForm acroForm = docCatalog.getAcroForm();
+ acroForm.setCacheFields( true );
+ acroForm.importFDF( fdfDocument );
+ }
+
+ /**
+ * This will import an fdf document and write out another pdf.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ *
+ * @throws Exception If there is an error importing the FDF document.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ ImportXFDF importer = new ImportXFDF();
+ importer.importXFDF( args );
+ }
+
+ private void importXFDF( String[] args ) throws Exception
+ {
+ PDDocument pdf = null;
+ FDFDocument fdf = null;
+
+ try
+ {
+ if( args.length != 3 )
+ {
+ usage();
+ }
+ else
+ {
+ ImportFDF importer = new ImportFDF();
+ pdf = PDDocument.load( args[0] );
+ fdf = FDFDocument.loadXFDF( args[1] );
+
+ importer.importFDF( pdf, fdf );
+ pdf.save( args[2] );
+ fdf.save( "tmp/outputXFDFtoPDF.fdf");
+ }
+ }
+ finally
+ {
+ close( fdf );
+ close( pdf );
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: org.pdfbox.ImportXFDF <pdf-file> <fdf-file> <output-file>" );
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( FDFDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( PDDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/Overlay.java b/src/main/java/org/pdfbox/Overlay.java
new file mode 100644
index 0000000..7850d89
--- /dev/null
+++ b/src/main/java/org/pdfbox/Overlay.java
@@ -0,0 +1,530 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.pdfparser.PDFParser;
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.PDResources;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+import java.util.Map;
+
+/**
+ * Overlay on document with another one.<br>
+ * e.g. Overlay an invoice with your company layout<br>
+ * <br>
+ * How it (should) work:<br>
+ * If the document has 10 pages, and the layout 2 the following is the result:<br>
+ * <pre>
+ * Document: 1234567890
+ * Layout : 1212121212
+ * </pre>
+ * <br>
+ *
+ * @author Mario Ivankovits (mario@ops.co.at)
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ *
+ * @version $Revision: 1.4 $
+ */
+public class Overlay
+{
+ /**
+ * COSName constant.
+ */
+ public static final COSName XOBJECT = COSName.getPDFName("XObject");
+ /**
+ * COSName constant.
+ */
+ public static final COSName PROC_SET = COSName.getPDFName("ProcSet");
+ /**
+ * COSName constant.
+ */
+ public static final COSName EXT_G_STATE = COSName.getPDFName("ExtGState" );
+
+ private static Logger log = Logger.getLogger(Overlay.class);
+
+ private List layoutPages = new ArrayList(10);
+
+ private PDDocument pdfOverlay;
+ private PDDocument pdfDocument;
+ private int pageCount = 0;
+ private COSStream saveGraphicsStateStream;
+ private COSStream restoreGraphicsStateStream;
+
+ /**
+ * This will overlay a document and write out the results.<br/><br/>
+ *
+ * usage: java org.pdfbox.Overlay &lt;overlay.pdf&gt; &lt;document.pdf&gt; &lt;result.pdf&gt;
+ *
+ * @param args The command line arguments.
+ *
+ * @throws IOException If there is an error reading/writing the document.
+ * @throws COSVisitorException If there is an error writing the document.
+ */
+ public static void main( String[] args ) throws IOException, COSVisitorException
+ {
+ if( args.length != 3 )
+ {
+ usage();
+ System.exit(1);
+ }
+ else
+ {
+ PDDocument overlay = null;
+ PDDocument pdf = null;
+
+ try
+ {
+ overlay = getDocument( args[0] );
+ pdf = getDocument( args[1] );
+ Overlay overlayer = new Overlay();
+ overlayer.overlay( overlay, pdf );
+ writeDocument( pdf, args[2] );
+ }
+ finally
+ {
+ if( overlay != null )
+ {
+ overlay.close();
+ }
+ if( pdf != null )
+ {
+ pdf.close();
+ }
+ }
+ }
+ }
+
+ private static void writeDocument( PDDocument pdf, String filename ) throws IOException, COSVisitorException
+ {
+ FileOutputStream output = null;
+ COSWriter writer = null;
+ try
+ {
+ output = new FileOutputStream( filename );
+ writer = new COSWriter( output );
+ writer.write( pdf );
+ }
+ finally
+ {
+ if( writer != null )
+ {
+ writer.close();
+ }
+ if( output != null )
+ {
+ output.close();
+ }
+ }
+ }
+
+ private static PDDocument getDocument( String filename ) throws IOException
+ {
+ FileInputStream input = null;
+ PDFParser parser = null;
+ PDDocument result = null;
+ try
+ {
+ input = new FileInputStream( filename );
+ parser = new PDFParser( input );
+ parser.parse();
+ result = parser.getPDDocument();
+ }
+ finally
+ {
+ if( input != null )
+ {
+ input.close();
+ }
+ }
+ return result;
+ }
+
+ private static void usage()
+ {
+ System.err.println( "usage: java " + Overlay.class.getName() + "<overlay.pdf> <document.pdf> <result.pdf>" );
+ }
+
+ /**
+ * Private class.
+ */
+ private static class LayoutPage
+ {
+ private final COSBase contents;
+ private final COSDictionary res;
+ private final Map objectNameMap;
+
+ /**
+ * Constructor.
+ *
+ * @param contentsValue The contents.
+ * @param resValue The resource dictionary
+ * @param objectNameMapValue The map
+ */
+ public LayoutPage(COSBase contentsValue, COSDictionary resValue, Map objectNameMapValue)
+ {
+ contents = contentsValue;
+ res = resValue;
+ objectNameMap = objectNameMapValue;
+ }
+ }
+
+ /**
+ * This will overlay two documents onto each other. The overlay document is
+ * repeatedly overlayed onto the destination document for every page in the
+ * destination.
+ *
+ * @param overlay The document to copy onto the destination
+ * @param destination The file that the overlay should be placed on.
+ *
+ * @return The destination pdf, same as argument passed in.
+ *
+ * @throws IOException If there is an error accessing data.
+ */
+ public PDDocument overlay( PDDocument overlay, PDDocument destination ) throws IOException
+ {
+ pdfOverlay = overlay;
+ pdfDocument = destination;
+
+ PDDocumentCatalog overlayCatalog = pdfOverlay.getDocumentCatalog();
+ collectLayoutPages( overlayCatalog.getAllPages() );
+
+ COSDictionary saveGraphicsStateDic = new COSDictionary();
+ saveGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() );
+ OutputStream saveStream = saveGraphicsStateStream.createUnfilteredStream();
+ saveStream.write( " q\n".getBytes() );
+ saveStream.flush();
+
+ restoreGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() );
+ OutputStream restoreStream = restoreGraphicsStateStream.createUnfilteredStream();
+ restoreStream.write( " Q\n".getBytes() );
+ restoreStream.flush();
+
+
+ PDDocumentCatalog pdfCatalog = pdfDocument.getDocumentCatalog();
+ processPages( pdfCatalog.getAllPages() );
+
+ return pdfDocument;
+ }
+
+ private void collectLayoutPages( List pages) throws IOException
+ {
+ Iterator pagesIter = pages.iterator();
+ while( pagesIter.hasNext() )
+ {
+ PDPage page = (PDPage)pagesIter.next();
+ COSBase contents = page.getCOSDictionary().getDictionaryObject( COSName.CONTENTS );
+ PDResources resources = page.findResources();
+ if( resources == null )
+ {
+ resources = new PDResources();
+ page.setResources( resources );
+ }
+ COSDictionary res = resources.getCOSDictionary();
+
+ if( contents instanceof COSStream )
+ {
+ COSStream stream = (COSStream) contents;
+ Map objectNameMap = new TreeMap();
+ stream = makeUniqObjectNames(objectNameMap, stream);
+
+ layoutPages.add(new LayoutPage(stream, res, objectNameMap));
+ }
+ else if( contents instanceof COSArray )
+ {
+ throw new UnsupportedOperationException("Layout pages with COSArray currently not supported.");
+ // layoutPages.add(new LayoutPage(contents, res));
+ }
+ else
+ {
+ throw new IOException( "Contents are unknown type:" + contents.getClass().getName() );
+ }
+ }
+ }
+
+ private COSStream makeUniqObjectNames(Map objectNameMap, COSStream stream) throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(10240);
+
+ byte[] buf = new byte[10240];
+ int read;
+ InputStream is = stream.getUnfilteredStream();
+ while ((read = is.read(buf)) > -1)
+ {
+ baos.write(buf, 0, read);
+ }
+
+ buf = baos.toByteArray();
+ baos = new ByteArrayOutputStream(buf.length + 100);
+ StringBuffer sbObjectName = new StringBuffer(10);
+ boolean bInObjectIdent = false;
+ boolean bInText = false;
+ boolean bInEscape = false;
+ for (int i = 0; i<buf.length; i++)
+ {
+ byte b = buf[i];
+
+ if (!bInEscape)
+ {
+ if (!bInText && b == '(')
+ {
+ bInText = true;
+ }
+ if (bInText && b == ')')
+ {
+ bInText = false;
+ }
+ if (b == '\\')
+ {
+ bInEscape = true;
+ }
+
+ if (!bInText && !bInEscape)
+ {
+ if (b == '/')
+ {
+ bInObjectIdent = true;
+ }
+ else if (bInObjectIdent && Character.isWhitespace((char) b))
+ {
+ bInObjectIdent = false;
+
+ // System.err.println(sbObjectName);
+ // String object = sbObjectName.toString();
+
+ String objectName = sbObjectName.toString().substring(1);
+ String newObjectName = objectName + "overlay";
+ baos.write('/');
+ baos.write(newObjectName.getBytes());
+
+ objectNameMap.put(objectName, COSName.getPDFName(newObjectName));
+
+ sbObjectName.delete(0, sbObjectName.length());
+ }
+ }
+
+ if (bInObjectIdent)
+ {
+ sbObjectName.append((char) b);
+ continue;
+ }
+ }
+ else
+ {
+ bInEscape = false;
+ }
+
+ baos.write(b);
+ }
+
+ COSDictionary streamDict = new COSDictionary();
+ streamDict.setItem(COSName.LENGTH, new COSInteger(baos.size()));
+ COSStream output = new COSStream(streamDict, pdfDocument.getDocument().getScratchFile());
+ output.setFilters(stream.getFilters());
+ OutputStream os = output.createUnfilteredStream();
+ baos.writeTo(os);
+ os.close();
+
+ return output;
+ }
+
+ private void processPages( List pages ) throws IOException
+ {
+ Iterator pageIter = pages.iterator();
+ while( pageIter.hasNext() )
+ {
+ PDPage page = (PDPage)pageIter.next();
+ COSDictionary pageDictionary = page.getCOSDictionary();
+ COSBase contents = pageDictionary.getDictionaryObject( COSName.CONTENTS );
+ if( contents instanceof COSStream )
+ {
+ COSStream contentsStream = (COSStream)contents;
+ // System.err.println("stream");
+ pageCount++;
+
+ COSArray array = new COSArray();
+
+ array.add(contentsStream);
+
+ mergePage( array, page );
+
+ pageDictionary.setItem(COSName.CONTENTS, array);
+ }
+ else if( contents instanceof COSArray )
+ {
+ COSArray contentsArray = (COSArray)contents;
+
+ mergePage( contentsArray, page );
+ }
+ else
+ {
+ throw new IOException( "Contents are unknown type:" + contents.getClass().getName() );
+ }
+ }
+ }
+
+ private void mergePage(COSArray array, PDPage page )
+ {
+ int layoutPageNum = pageCount % layoutPages.size();
+ LayoutPage layoutPage = (LayoutPage) layoutPages.get(layoutPageNum);
+ PDResources resources = page.findResources();
+ if( resources == null )
+ {
+ resources = new PDResources();
+ page.setResources( resources );
+ }
+ COSDictionary docResDict = resources.getCOSDictionary();
+ COSDictionary layoutResDict = layoutPage.res;
+ mergeArray(PROC_SET, docResDict, layoutResDict);
+ mergeDictionary(COSName.FONT, docResDict, layoutResDict, layoutPage.objectNameMap);
+ mergeDictionary(XOBJECT, docResDict, layoutResDict, layoutPage.objectNameMap);
+ mergeDictionary(EXT_G_STATE, docResDict, layoutResDict, layoutPage.objectNameMap);
+
+ //we are going to wrap the existing content around some save/restore
+ //graphics state, so the result is
+ //
+ //<save graphics state>
+ //<all existing content streams>
+ //<restore graphics state>
+ //<overlay content>
+ array.add(0, saveGraphicsStateStream );
+ array.add( restoreGraphicsStateStream );
+ array.add(layoutPage.contents);
+ }
+
+ /**
+ * merges two dictionaries.
+ *
+ * @param dest
+ * @param source
+ */
+ private void mergeDictionary(COSName name, COSDictionary dest, COSDictionary source, Map objectNameMap)
+ {
+ COSDictionary destDict = (COSDictionary) dest.getDictionaryObject(name);
+ COSDictionary sourceDict = (COSDictionary) source.getDictionaryObject(name);
+
+ if (destDict == null)
+ {
+ destDict = new COSDictionary();
+ dest.setItem(name, destDict);
+ }
+ if( sourceDict != null )
+ {
+
+ Iterator iterKeys = sourceDict.keyList().iterator();
+ while (iterKeys.hasNext())
+ {
+ COSName key = (COSName) iterKeys.next();
+ COSName mappedKey = (COSName) objectNameMap.get(key.getName());
+ if (mappedKey == null)
+ {
+ // object not needet
+ if (log.isDebugEnabled())
+ {
+ log.debug("MergeDict " + name.getName() +
+ ": Object " + key.getName() + " not used by content. Discarded.");
+ }
+ continue;
+ }
+
+ destDict.setItem(mappedKey, sourceDict.getItem(key));
+ }
+ }
+ }
+
+ /**
+ * merges two arrays.
+ *
+ * @param dest
+ * @param source
+ */
+ private void mergeArray(COSName name, COSDictionary dest, COSDictionary source)
+ {
+ COSArray destDict = (COSArray) dest.getDictionaryObject(name);
+ COSArray sourceDict = (COSArray) source.getDictionaryObject(name);
+
+ if (destDict == null)
+ {
+ destDict = new COSArray();
+ dest.setItem(name, destDict);
+ }
+
+ for (int sourceDictIdx = 0; sourceDict != null && sourceDictIdx<sourceDict.size(); sourceDictIdx++)
+ {
+ COSBase key = sourceDict.get(sourceDictIdx);
+ if (key instanceof COSName)
+ {
+ COSName keyname = (COSName) key;
+
+ boolean bFound = false;
+ for (int destDictIdx = 0; destDictIdx<destDict.size(); destDictIdx++)
+ {
+ COSBase destkey = destDict.get(destDictIdx);
+ if (destkey instanceof COSName)
+ {
+ COSName destkeyname = (COSName) destkey;
+ if (destkeyname.equals(keyname))
+ {
+ bFound = true;
+ break;
+ }
+ }
+ }
+ if (!bFound)
+ {
+ destDict.add(keyname);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/PDFDebugger.java b/src/main/java/org/pdfbox/PDFDebugger.java
new file mode 100644
index 0000000..0ccf2fe
--- /dev/null
+++ b/src/main/java/org/pdfbox/PDFDebugger.java
@@ -0,0 +1,420 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdfviewer.PDFTreeModel;
+import org.pdfbox.pdfviewer.PDFTreeCellRenderer;
+import org.pdfbox.pdfviewer.ArrayEntry;
+import org.pdfbox.pdfviewer.MapEntry;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.util.DefaultFileFilter;
+
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+//import javax.swing.tree.*;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import javax.swing.JFileChooser;
+import javax.swing.JScrollPane;
+import javax.swing.JPanel;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ *
+ * @author wurtz
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDFDebugger extends javax.swing.JFrame
+{
+ private File currentDir=new File(".");
+
+ /**
+ * Constructor.
+ */
+ public PDFDebugger()
+ {
+ initComponents();
+ }
+
+ /**
+ * This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ private void initComponents()
+ {
+ jSplitPane1 = new javax.swing.JSplitPane();
+ jScrollPane1 = new javax.swing.JScrollPane();
+ jTree1 = new javax.swing.JTree();
+ jScrollPane2 = new javax.swing.JScrollPane();
+ jTextPane1 = new javax.swing.JTextPane();
+ menuBar = new javax.swing.JMenuBar();
+ fileMenu = new javax.swing.JMenu();
+ openMenuItem = new javax.swing.JMenuItem();
+ saveMenuItem = new javax.swing.JMenuItem();
+ saveAsMenuItem = new javax.swing.JMenuItem();
+ exitMenuItem = new javax.swing.JMenuItem();
+ editMenu = new javax.swing.JMenu();
+ cutMenuItem = new javax.swing.JMenuItem();
+ copyMenuItem = new javax.swing.JMenuItem();
+ pasteMenuItem = new javax.swing.JMenuItem();
+ deleteMenuItem = new javax.swing.JMenuItem();
+ helpMenu = new javax.swing.JMenu();
+ contentsMenuItem = new javax.swing.JMenuItem();
+ aboutMenuItem = new javax.swing.JMenuItem();
+
+ jTree1.setCellRenderer( new PDFTreeCellRenderer() );
+ jTree1.setModel( null );
+
+ setTitle("PDFBox - PDF Viewer");
+ addWindowListener(new java.awt.event.WindowAdapter()
+ {
+ public void windowClosing(java.awt.event.WindowEvent evt)
+ {
+ exitForm(evt);
+ }
+ });
+
+
+ jScrollPane1.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED));
+ jScrollPane1.setPreferredSize(new java.awt.Dimension(300, 500));
+ jTree1.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener()
+ {
+ public void valueChanged(javax.swing.event.TreeSelectionEvent evt)
+ {
+ jTree1ValueChanged(evt);
+ }
+ });
+
+ jScrollPane1.setViewportView(jTree1);
+
+ jSplitPane1.setRightComponent(jScrollPane2);
+
+ jScrollPane2.setPreferredSize(new java.awt.Dimension(300, 500));
+ jScrollPane2.setViewportView(jTextPane1);
+
+ jSplitPane1.setLeftComponent(jScrollPane1);
+
+ JScrollPane documentScroller = new JScrollPane();
+ //documentScroller.setPreferredSize( new Dimension( 300, 500 ) );
+ documentScroller.setViewportView( documentPanel );
+
+ getContentPane().add( jSplitPane1, java.awt.BorderLayout.CENTER );
+
+ fileMenu.setText("File");
+ openMenuItem.setText("Open");
+ openMenuItem.setToolTipText("Open PDF file");
+ openMenuItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(java.awt.event.ActionEvent evt)
+ {
+ openMenuItemActionPerformed(evt);
+ }
+ });
+
+ fileMenu.add(openMenuItem);
+
+ saveMenuItem.setText("Save");
+ //fileMenu.add(saveMenuItem);
+
+ saveAsMenuItem.setText("Save As ...");
+ //fileMenu.add(saveAsMenuItem);
+
+ exitMenuItem.setText("Exit");
+ exitMenuItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(java.awt.event.ActionEvent evt)
+ {
+ exitMenuItemActionPerformed(evt);
+ }
+ });
+
+ fileMenu.add(exitMenuItem);
+
+ menuBar.add(fileMenu);
+
+ editMenu.setText("Edit");
+ cutMenuItem.setText("Cut");
+ editMenu.add(cutMenuItem);
+
+ copyMenuItem.setText("Copy");
+ editMenu.add(copyMenuItem);
+
+ pasteMenuItem.setText("Paste");
+ editMenu.add(pasteMenuItem);
+
+ deleteMenuItem.setText("Delete");
+ editMenu.add(deleteMenuItem);
+
+ //menuBar.add(editMenu);
+
+ helpMenu.setText("Help");
+ contentsMenuItem.setText("Contents");
+ helpMenu.add(contentsMenuItem);
+
+ aboutMenuItem.setText("About");
+ helpMenu.add(aboutMenuItem);
+
+ //menuBar.add(helpMenu);
+
+ setJMenuBar(menuBar);
+
+
+ java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
+ setBounds((screenSize.width-700)/2, (screenSize.height-600)/2, 700, 600);
+ }//GEN-END:initComponents
+
+ private void openMenuItemActionPerformed(java.awt.event.ActionEvent evt)
+ {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setCurrentDirectory(currentDir);
+
+ DefaultFileFilter pdfFilter = new DefaultFileFilter(new String[] {"pdf", "PDF"}, "PDF Files");
+ chooser.setFileFilter(pdfFilter);
+ int result = chooser.showOpenDialog(PDFDebugger.this);
+ if (result == JFileChooser.APPROVE_OPTION)
+ {
+ String name = chooser.getSelectedFile().getPath();
+ currentDir = new File(name).getParentFile();
+ try
+ {
+ readPDFFile(name);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }//GEN-LAST:event_openMenuItemActionPerformed
+
+ private void jTree1ValueChanged(javax.swing.event.TreeSelectionEvent evt)
+ {
+ TreePath path = jTree1.getSelectionPath();
+ if (path != null)
+ {
+ try
+ {
+ Object selectedNode = path.getLastPathComponent();
+ String data=convertToString(selectedNode);
+
+
+
+ if (data != null)
+ {
+ jTextPane1.setText(data);
+ }
+ else
+ {
+ jTextPane1.setText( "" );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }//GEN-LAST:event_jTree1ValueChanged
+
+ private String convertToString( Object selectedNode )
+ {
+ String data = null;
+ if(selectedNode instanceof COSBoolean)
+ {
+ data = "" + ((COSBoolean)selectedNode).getValue();
+ }
+ else if( selectedNode instanceof COSFloat )
+ {
+ data = "" + ((COSFloat)selectedNode).floatValue();
+ }
+ else if( selectedNode instanceof COSNull )
+ {
+ data = "null";
+ }
+ else if( selectedNode instanceof COSInteger )
+ {
+ data = "" + ((COSInteger)selectedNode).intValue();
+ }
+ else if( selectedNode instanceof COSName )
+ {
+ data = "" + ((COSName)selectedNode).getName();
+ }
+ else if( selectedNode instanceof COSString )
+ {
+ data = "" + ((COSString)selectedNode).getString();
+ }
+ else if( selectedNode instanceof COSStream )
+ {
+ try
+ {
+ COSStream stream = (COSStream)selectedNode;
+ InputStream ioStream = stream.getUnfilteredStream();
+ ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ while( (amountRead = ioStream.read( buffer, 0, buffer.length ) ) != -1 )
+ {
+ byteArray.write( buffer, 0, amountRead );
+ }
+ data = byteArray.toString();
+ }
+ catch( IOException e )
+ {
+ e.printStackTrace();
+ }
+ }
+ else if( selectedNode instanceof MapEntry )
+ {
+ data = convertToString( ((MapEntry)selectedNode).getValue() );
+ }
+ else if( selectedNode instanceof ArrayEntry )
+ {
+ data = convertToString( ((ArrayEntry)selectedNode).getValue() );
+ }
+ return data;
+ }
+
+ private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt)
+ {
+ System.exit(0);
+ }
+
+ /**
+ * Exit the Application.
+ */
+ private void exitForm(java.awt.event.WindowEvent evt)
+ {
+ System.exit(0);
+ }
+
+ /**
+ * @param args the command line arguments
+ *
+ * @throws Exception If anything goes wrong.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ PDFDebugger viewer = new PDFDebugger();
+ if( args.length >0 )
+ {
+ viewer.readPDFFile( args[0] );
+ }
+ viewer.show();
+ }
+
+ private void readPDFFile(String file) throws Exception
+ {
+ InputStream input = null;
+ File f = new File( file );
+ input = new FileInputStream(f);
+ PDDocument document = parseDocument( input );
+ TreeModel model=new PDFTreeModel(document);
+ jTree1.setModel(model);
+ setTitle( "PDFBox - " + f.getAbsolutePath() );
+ /*
+ List pages = document.getDocumentCatalog().getAllPages();
+ for( int i=0; i<pages.size(); i++ )
+ {
+ PageWrapper wrapper = new PageWrapper();
+ wrapper.displayPage( (PDPage)pages.get(i) );
+ documentPanel.add( wrapper.getPanel() );
+ }*/
+ }
+ /**
+ * This will parse a document.
+ *
+ * @param input The input stream for the document.
+ *
+ * @return The document.
+ *
+ * @throws IOException If there is an error parsing the document.
+ */
+ private static PDDocument parseDocument( InputStream input )throws IOException
+ {
+ PDDocument document = PDDocument.load( input );
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: The document is encrypted." );
+ }
+ catch( org.pdfbox.exceptions.CryptographyException e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ return document;
+ }
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JMenuItem aboutMenuItem;
+ private javax.swing.JMenuItem contentsMenuItem;
+ private javax.swing.JMenuItem copyMenuItem;
+ private javax.swing.JMenuItem cutMenuItem;
+ private javax.swing.JMenuItem deleteMenuItem;
+ private javax.swing.JMenu editMenu;
+ private javax.swing.JMenuItem exitMenuItem;
+ private javax.swing.JMenu fileMenu;
+ private javax.swing.JMenu helpMenu;
+ private javax.swing.JScrollPane jScrollPane1;
+ private javax.swing.JScrollPane jScrollPane2;
+ private javax.swing.JSplitPane jSplitPane1;
+ private javax.swing.JTextPane jTextPane1;
+ private javax.swing.JTree jTree1;
+ private javax.swing.JMenuBar menuBar;
+ private javax.swing.JMenuItem openMenuItem;
+ private javax.swing.JMenuItem pasteMenuItem;
+ private javax.swing.JMenuItem saveAsMenuItem;
+ private javax.swing.JMenuItem saveMenuItem;
+ private JPanel documentPanel = new JPanel();
+ // End of variables declaration//GEN-END:variables
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/PDFReader.java b/src/main/java/org/pdfbox/PDFReader.java
new file mode 100644
index 0000000..cf9f548
--- /dev/null
+++ b/src/main/java/org/pdfbox/PDFReader.java
@@ -0,0 +1,308 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdfviewer.PageWrapper;
+import org.pdfbox.pdfviewer.ReaderBottomPanel;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.util.DefaultFileFilter;
+
+import javax.swing.JFileChooser;
+import javax.swing.JScrollPane;
+import javax.swing.JPanel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * An application to read PDF documents. This will provide Acrobat Reader like
+ * funtionality.
+ *
+ * @author Ben Litchfield (ben@pdfbox.org)
+ * @version $Revision: 1.2 $
+ */
+public class PDFReader extends javax.swing.JFrame
+{
+ private File currentDir=new File(".");
+
+ /**
+ * Constructor.
+ */
+ public PDFReader()
+ {
+ initComponents();
+ }
+
+ /**
+ * This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ private void initComponents()
+ {
+ menuBar = new javax.swing.JMenuBar();
+ fileMenu = new javax.swing.JMenu();
+ openMenuItem = new javax.swing.JMenuItem();
+ saveMenuItem = new javax.swing.JMenuItem();
+ saveAsMenuItem = new javax.swing.JMenuItem();
+ exitMenuItem = new javax.swing.JMenuItem();
+ editMenu = new javax.swing.JMenu();
+ cutMenuItem = new javax.swing.JMenuItem();
+ copyMenuItem = new javax.swing.JMenuItem();
+ pasteMenuItem = new javax.swing.JMenuItem();
+ deleteMenuItem = new javax.swing.JMenuItem();
+ helpMenu = new javax.swing.JMenu();
+ contentsMenuItem = new javax.swing.JMenuItem();
+ aboutMenuItem = new javax.swing.JMenuItem();
+
+
+ setTitle("PDFBox - PDF Reader");
+ addWindowListener(new java.awt.event.WindowAdapter()
+ {
+ public void windowClosing(java.awt.event.WindowEvent evt)
+ {
+ exitForm(evt);
+ }
+ });
+
+
+ JScrollPane documentScroller = new JScrollPane();
+ documentScroller.setViewportView( documentPanel );
+
+
+ getContentPane().add( documentScroller, java.awt.BorderLayout.CENTER );
+ getContentPane().add( bottomStatusPanel, java.awt.BorderLayout.SOUTH );
+
+ fileMenu.setText("File");
+ openMenuItem.setText("Open");
+ openMenuItem.setToolTipText("Open PDF file");
+ openMenuItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(java.awt.event.ActionEvent evt)
+ {
+ openMenuItemActionPerformed(evt);
+ }
+ });
+
+ fileMenu.add(openMenuItem);
+
+ saveMenuItem.setText("Save");
+ //fileMenu.add(saveMenuItem);
+
+ saveAsMenuItem.setText("Save As ...");
+ //fileMenu.add(saveAsMenuItem);
+
+ exitMenuItem.setText("Exit");
+ exitMenuItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(java.awt.event.ActionEvent evt)
+ {
+ exitMenuItemActionPerformed(evt);
+ }
+ });
+
+ fileMenu.add(exitMenuItem);
+
+ menuBar.add(fileMenu);
+
+ editMenu.setText("Edit");
+ cutMenuItem.setText("Cut");
+ editMenu.add(cutMenuItem);
+
+ copyMenuItem.setText("Copy");
+ editMenu.add(copyMenuItem);
+
+ pasteMenuItem.setText("Paste");
+ editMenu.add(pasteMenuItem);
+
+ deleteMenuItem.setText("Delete");
+ editMenu.add(deleteMenuItem);
+
+ //menuBar.add(editMenu);
+
+ helpMenu.setText("Help");
+ contentsMenuItem.setText("Contents");
+ helpMenu.add(contentsMenuItem);
+
+ aboutMenuItem.setText("About");
+ helpMenu.add(aboutMenuItem);
+
+ //menuBar.add(helpMenu);
+
+ setJMenuBar(menuBar);
+
+
+ java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
+ setBounds((screenSize.width-700)/2, (screenSize.height-600)/2, 700, 600);
+ }//GEN-END:initComponents
+
+ private void openMenuItemActionPerformed(java.awt.event.ActionEvent evt)
+ {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setCurrentDirectory(currentDir);
+
+ DefaultFileFilter pdfFilter = new DefaultFileFilter(new String[] {"pdf", "PDF"}, "PDF Files");
+ chooser.setFileFilter(pdfFilter);
+ int result = chooser.showOpenDialog(PDFReader.this);
+ if (result == JFileChooser.APPROVE_OPTION)
+ {
+ String name = chooser.getSelectedFile().getPath();
+ currentDir = new File(name).getParentFile();
+ try
+ {
+ openPDFFile(name);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }//GEN-LAST:event_openMenuItemActionPerformed
+
+ private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt)
+ {
+ System.exit(0);
+ }
+
+ /**
+ * Exit the Application.
+ */
+ private void exitForm(java.awt.event.WindowEvent evt)
+ {
+ System.exit(0);
+ }
+
+ /**
+ * @param args the command line arguments
+ *
+ * @throws Exception If anything goes wrong.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ PDFReader viewer = new PDFReader();
+ if( args.length >0 )
+ {
+ viewer.openPDFFile( args[0] );
+ }
+ viewer.show();
+ }
+
+ private void openPDFFile(String file) throws Exception
+ {
+ if( document != null )
+ {
+ document.close();
+ documentPanel.removeAll();
+ }
+ InputStream input = null;
+ File f = new File( file );
+ input = new FileInputStream(f);
+ document = parseDocument( input );
+ setTitle( "PDFBox - " + f.getAbsolutePath() );
+
+ List pages = document.getDocumentCatalog().getAllPages();
+ for( int i=0; i<pages.size(); i++ )
+ {
+ PageWrapper wrapper = new PageWrapper( this );
+ wrapper.displayPage( (PDPage)pages.get(i) );
+ documentPanel.add( wrapper.getPanel() );
+ }
+ }
+ /**
+ * This will parse a document.
+ *
+ * @param input The input stream for the document.
+ *
+ * @return The document.
+ *
+ * @throws IOException If there is an error parsing the document.
+ */
+ private static PDDocument parseDocument( InputStream input )throws IOException
+ {
+ PDDocument document = PDDocument.load( input );
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: The document is encrypted." );
+ }
+ catch( org.pdfbox.exceptions.CryptographyException e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ return document;
+ }
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JMenuItem aboutMenuItem;
+ private javax.swing.JMenuItem contentsMenuItem;
+ private javax.swing.JMenuItem copyMenuItem;
+ private javax.swing.JMenuItem cutMenuItem;
+ private javax.swing.JMenuItem deleteMenuItem;
+ private javax.swing.JMenu editMenu;
+ private javax.swing.JMenuItem exitMenuItem;
+ private javax.swing.JMenu fileMenu;
+ private javax.swing.JMenu helpMenu;
+ private javax.swing.JMenuBar menuBar;
+ private javax.swing.JMenuItem openMenuItem;
+ private javax.swing.JMenuItem pasteMenuItem;
+ private javax.swing.JMenuItem saveAsMenuItem;
+ private javax.swing.JMenuItem saveMenuItem;
+ private JPanel documentPanel = new JPanel();
+ private ReaderBottomPanel bottomStatusPanel = new ReaderBottomPanel();
+ // End of variables declaration//GEN-END:variables
+
+ private PDDocument document = null;
+
+ /**
+ * Get the bottom status panel.
+ *
+ * @return The bottom status panel.
+ */
+ public ReaderBottomPanel getBottomStatusPanel()
+ {
+ return bottomStatusPanel;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/PDFSplit.java b/src/main/java/org/pdfbox/PDFSplit.java
new file mode 100644
index 0000000..2a5a3c9
--- /dev/null
+++ b/src/main/java/org/pdfbox/PDFSplit.java
@@ -0,0 +1,227 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import java.util.List;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.util.Splitter;
+
+/**
+ * This is the main program that will take a pdf document and split it into
+ * a number of other documents.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDFSplit
+{
+ private static final String PASSWORD = "-password";
+ private static final String SPLIT = "-split";
+
+ /**
+ * Infamous main method.
+ *
+ * @param args Command line arguments, should be one and a reference to a file.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ PDFSplit split = new PDFSplit();
+ split.split( args );
+ }
+
+ private void split( String[] args ) throws Exception
+ {
+ String password = "";
+ String split = "1";
+
+ Splitter splitter = new Splitter();
+ String pdfFile = null;
+ for( int i=0; i<args.length; i++ )
+ {
+ if( args[i].equals( PASSWORD ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ password = args[i];
+ }
+ else if( args[i].equals( SPLIT ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ split = args[i];
+ }
+ else
+ {
+ if( pdfFile == null )
+ {
+ pdfFile = args[i];
+ }
+ }
+ }
+
+ if( pdfFile == null )
+ {
+ usage();
+ }
+
+ InputStream input = null;
+ PDDocument document = null;
+ List documents = null;
+ try
+ {
+ input = new FileInputStream( pdfFile );
+ document = parseDocument( input );
+
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( password );
+ }
+ catch( InvalidPasswordException e )
+ {
+ if( args.length == 4 )//they supplied the wrong password
+ {
+ System.err.println( "Error: The supplied password is incorrect." );
+ System.exit( 2 );
+ }
+ else
+ {
+ //they didn't suppply a password and the default of "" was wrong.
+ System.err.println( "Error: The document is encrypted." );
+ usage();
+ }
+ }
+ }
+
+ splitter.setSplitAtPage( Integer.parseInt( split ) );
+ documents = splitter.split( document );
+ for( int i=0; i<documents.size(); i++ )
+ {
+ PDDocument doc = (PDDocument)documents.get( i );
+ String fileName = pdfFile.substring(0, pdfFile.length()-4 ) + "-" + i + ".pdf";
+ writeDocument( doc, fileName );
+ doc.close();
+ }
+
+ }
+ finally
+ {
+ if( input != null )
+ {
+ input.close();
+ }
+ if( document != null )
+ {
+ document.close();
+ }
+ for( int i=0; documents != null && i<documents.size(); i++ )
+ {
+ PDDocument doc = (PDDocument)documents.get( i );
+ doc.close();
+ }
+ }
+ }
+
+ private static final void writeDocument( PDDocument doc, String fileName ) throws IOException, COSVisitorException
+ {
+ FileOutputStream output = null;
+ COSWriter writer = null;
+ try
+ {
+ output = new FileOutputStream( fileName );
+ writer = new COSWriter( output );
+ writer.write( doc );
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.close();
+ }
+ if( writer != null )
+ {
+ writer.close();
+ }
+ }
+ }
+
+ /**
+ * This will parse a document.
+ *
+ * @param input The input stream for the document.
+ *
+ * @return The document.
+ *
+ * @throws IOException If there is an error parsing the document.
+ */
+ private static PDDocument parseDocument( InputStream input )throws IOException
+ {
+ PDFParser parser = new PDFParser( input );
+ parser.parse();
+ return parser.getPDDocument();
+ }
+
+ /**
+ * This will print the usage requirements and exit.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.PDFSplit [OPTIONS] <PDF file>\n" +
+ " -password <password> Password to decrypt document\n" +
+ " -split <integer> split after this many pages\n" +
+ " <PDF file> The PDF document to use\n"
+ );
+ System.exit( 1 );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/PDFToImage.java b/src/main/java/org/pdfbox/PDFToImage.java
new file mode 100644
index 0000000..7a6df6b
--- /dev/null
+++ b/src/main/java/org/pdfbox/PDFToImage.java
@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.imageio.IIOException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.ImageOutputStream;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+/**
+ * Convert a PDF document to an image.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDFToImage
+{
+
+
+
+ private static final String PASSWORD = "-password";
+ private static final String START_PAGE = "-startPage";
+ private static final String END_PAGE = "-endPage";
+ private static final String IMAGE_TYPE = "-imageType";
+ private static final String OUTPUT_PREFIX = "-outputPrefix";
+
+ /**
+ * private constructor.
+ */
+ private PDFToImage()
+ {
+ //static class
+ }
+
+ /**
+ * Infamous main method.
+ *
+ * @param args Command line arguments, should be one and a reference to a file.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ String password = "";
+ String pdfFile = null;
+ String outputPrefix = null;
+ String imageType = "jpg";
+ int startPage = 1;
+ int endPage = Integer.MAX_VALUE;
+ for( int i=0; i<args.length; i++ )
+ {
+ if( args[i].equals( PASSWORD ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ password = args[i];
+ }
+ else if( args[i].equals( START_PAGE ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ startPage = Integer.parseInt( args[i] );
+ }
+ else if( args[i].equals( END_PAGE ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ endPage = Integer.parseInt( args[i] );
+ }
+ else if( args[i].equals( IMAGE_TYPE ) )
+ {
+ i++;
+ imageType = args[i];
+ }
+ else if( args[i].equals( OUTPUT_PREFIX ) )
+ {
+ i++;
+ outputPrefix = args[i];
+ }
+ else
+ {
+ if( pdfFile == null )
+ {
+ pdfFile = args[i];
+ }
+ }
+ }
+
+ if( pdfFile == null )
+ {
+ usage();
+ }
+ if(outputPrefix == null)
+ {
+ outputPrefix = pdfFile.substring( 0, pdfFile.lastIndexOf( '.' ));
+ }
+
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( pdfFile );
+
+
+ //document.print();
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( password );
+ }
+ catch( InvalidPasswordException e )
+ {
+ if( args.length == 4 )//they supplied the wrong password
+ {
+ System.err.println( "Error: The supplied password is incorrect." );
+ System.exit( 2 );
+ }
+ else
+ {
+ //they didn't suppply a password and the default of "" was wrong.
+ System.err.println( "Error: The document is encrypted." );
+ usage();
+ }
+ }
+ }
+ List pages = document.getDocumentCatalog().getAllPages();
+ for( int i=startPage-1; i<endPage && i<pages.size(); i++ )
+ {
+ ImageOutputStream output = null;
+ ImageWriter imageWriter = null;
+ try
+ {
+ PDPage page = (PDPage)pages.get( i );
+ BufferedImage image = page.convertToImage();
+ String fileName = outputPrefix + (i+1) + "." + imageType;
+ System.out.println( "Writing:" + fileName );
+ output = ImageIO.createImageOutputStream( new File( fileName ) );
+
+ boolean foundWriter = false;
+ Iterator writerIter = ImageIO.getImageWritersByFormatName( imageType );
+ while( writerIter.hasNext() && !foundWriter )
+ {
+ try
+ {
+ imageWriter = (ImageWriter)writerIter.next();
+ ImageWriteParam writerParams = imageWriter.getDefaultWriteParam();
+ if(writerParams.canWriteCompressed() )
+ {
+ writerParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+ writerParams.setCompressionQuality(1.0f);
+ }
+
+
+ imageWriter.setOutput( output );
+ imageWriter.write( null, new IIOImage( image, null, null), writerParams );
+ foundWriter = true;
+ }
+ catch( IIOException io )
+ {
+ //ignore exception
+ }
+ finally
+ {
+ imageWriter.dispose();
+ }
+ }
+ if( !foundWriter )
+ {
+ throw new RuntimeException( "Error: no writer found for image type '" + imageType + "'" );
+ }
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.flush();
+ output.close();
+ }
+ }
+ }
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+
+ /**
+ * This will print the usage requirements and exit.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.ExtractText [OPTIONS] <PDF file> [Text File]\n" +
+ " -password <password> Password to decrypt document\n" +
+ " -imageType <image type> (" + getImageFormats() + ")\n" +
+ " -outputPrefix <output prefix> Filename prefix for image files\n" +
+ " -startPage <number> The first page to start extraction(1 based)\n" +
+ " -endPage <number> The last page to extract(inclusive)\n" +
+ " <PDF file> The PDF document to use\n"
+ );
+ System.exit( 1 );
+ }
+
+ private static String getImageFormats()
+ {
+ StringBuffer retval = new StringBuffer();
+ String[] formats = ImageIO.getReaderFormatNames();
+ for( int i=0; i<formats.length; i++ )
+ {
+ retval.append( formats[i] );
+ if( i+1<formats.length )
+ {
+ retval.append( "," );
+ }
+ }
+ return retval.toString();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/PrintPDF.java b/src/main/java/org/pdfbox/PrintPDF.java
new file mode 100644
index 0000000..8db25d7
--- /dev/null
+++ b/src/main/java/org/pdfbox/PrintPDF.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+/**
+ * This is a command line program that will print a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class PrintPDF
+{
+
+ private static final String PASSWORD = "-password";
+
+ /**
+ * private constructor.
+ */
+ private PrintPDF()
+ {
+ //static class
+ }
+
+ /**
+ * Infamous main method.
+ *
+ * @param args Command line arguments, should be one and a reference to a file.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ String password = "";
+ String pdfFile = null;
+ for( int i=0; i<args.length; i++ )
+ {
+ if( args[i].equals( PASSWORD ) )
+ {
+ i++;
+ if( i >= args.length )
+ {
+ usage();
+ }
+ password = args[i];
+ }
+ else
+ {
+ pdfFile = args[i];
+ }
+ }
+
+ if( pdfFile == null )
+ {
+ usage();
+ }
+
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( pdfFile );
+
+ if( document.isEncrypted() )
+ {
+ document.decrypt( password );
+ }
+
+ document.print();
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+
+ /**
+ * This will print the usage requirements and exit.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.PrintPDF [OPTIONS] <PDF file>\n" +
+ " -password <password> Password to decrypt document\n"
+ );
+ System.exit( 1 );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/TextToPDF.java b/src/main/java/org/pdfbox/TextToPDF.java
new file mode 100644
index 0000000..413d981
--- /dev/null
+++ b/src/main/java/org/pdfbox/TextToPDF.java
@@ -0,0 +1,259 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.pdfbox.pdmodel.font.PDSimpleFont;
+import org.pdfbox.pdmodel.font.PDTrueTypeFont;
+import org.pdfbox.pdmodel.font.PDType1Font;
+
+
+/**
+ * This will take a text file and ouput a pdf with that text.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class TextToPDF
+{
+ private int fontSize = 10;
+ private PDSimpleFont font = PDType1Font.HELVETICA;
+
+ /**
+ * Create a PDF document with some text.
+ *
+ * @param text The stream of text data.
+ *
+ * @return The document with the text in it.
+ *
+ * @throws IOException If there is an error writing the data.
+ */
+ public PDDocument createPDFFromText( Reader text ) throws IOException
+ {
+ PDDocument doc = null;
+ try
+ {
+
+ int margin = 40;
+ float height = font.getFontDescriptor().getFontBoundingBox().getHeight()/1000;
+
+ //calculate font height and increase by 5 percent.
+ height = height*fontSize*1.05f;
+ doc = new PDDocument();
+ BufferedReader data = new BufferedReader( text );
+ String nextLine = null;
+ PDPage page = new PDPage();
+ PDPageContentStream contentStream = null;
+ float y = -1;
+ float maxStringLength = page.getMediaBox().getWidth() - 2*margin;
+ while( (nextLine = data.readLine()) != null )
+ {
+
+ String[] lineWords = nextLine.trim().split( " " );
+ int lineIndex = 0;
+ while( lineIndex < lineWords.length )
+ {
+ StringBuffer nextLineToDraw = new StringBuffer();
+ float lengthIfUsingNextWord = 0;
+ do
+ {
+ nextLineToDraw.append( lineWords[lineIndex] );
+ nextLineToDraw.append( " " );
+ lineIndex++;
+ if( lineIndex < lineWords.length )
+ {
+ String lineWithNextWord = nextLineToDraw.toString() + lineWords[lineIndex];
+ lengthIfUsingNextWord =
+ (font.getStringWidth( lineWithNextWord )/1000) * fontSize;
+ }
+ }
+ while( lineIndex < lineWords.length &&
+ lengthIfUsingNextWord < maxStringLength );
+ if( y < margin )
+ {
+ page = new PDPage();
+ doc.addPage( page );
+ if( contentStream != null )
+ {
+ contentStream.endText();
+ contentStream.close();
+ }
+ contentStream = new PDPageContentStream(doc, page);
+ contentStream.setFont( font, fontSize );
+ contentStream.beginText();
+ y = page.getMediaBox().getHeight() - margin + height;
+ contentStream.moveTextPositionByAmount(
+ margin, y );
+
+ }
+ //System.out.println( "Drawing string at " + x + "," + y );
+
+
+ contentStream.moveTextPositionByAmount( 0, -height);
+ y -= height;
+ contentStream.drawString( nextLineToDraw.toString() );
+ }
+
+
+ }
+ if( contentStream != null )
+ {
+ contentStream.endText();
+ contentStream.close();
+ }
+ }
+ catch( IOException io )
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ throw io;
+ }
+ return doc;
+ }
+
+ /**
+ * This will create a PDF document with a single image on it.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ *
+ * @throws IOException If there is an error with the PDF.
+ */
+ public static void main(String[] args) throws IOException
+ {
+ TextToPDF app = new TextToPDF();
+ PDDocument doc = null;
+ try
+ {
+ if( args.length < 2 )
+ {
+ app.usage();
+ }
+ else
+ {
+ for( int i=0; i<args.length-2; i++ )
+ {
+ if( args[i].equals( "-standardFont" ))
+ {
+ i++;
+ app.setFont( PDType1Font.getStandardFont( args[i] ));
+ }
+ else if( args[i].equals( "-ttf" ))
+ {
+ i++;
+ PDTrueTypeFont font = PDTrueTypeFont.loadTTF( doc, new File( args[i]));
+ app.setFont( font );
+ }
+ else if( args[i].equals( "-fontSize" ))
+ {
+ i++;
+ app.setFontSize( Integer.parseInt( args[i] ) );
+ }
+ else
+ {
+ throw new IOException( "Unknown argument:" + args[i] );
+ }
+ }
+ doc = app.createPDFFromText( new FileReader( args[args.length-1] ) );
+ doc.save( args[args.length-2] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ String[] std14 = PDType1Font.getStandard14Names();
+ System.err.println( "usage: " + this.getClass().getName() + " [options] <output-file> <text-file>" );
+ System.err.println( " -standardFont <name> default:" + PDType1Font.HELVETICA.getBaseFont() );
+ for( int i=0; i<std14.length; i++ )
+ {
+ System.err.println( " " + std14[i] );
+ }
+ System.err.println( " -ttf <ttf file> The TTF font to use.");
+ System.err.println( " -fontSize <fontSize> default:10" );
+
+
+ }
+ /**
+ * @return Returns the font.
+ */
+ public PDSimpleFont getFont()
+ {
+ return font;
+ }
+ /**
+ * @param aFont The font to set.
+ */
+ public void setFont(PDSimpleFont aFont)
+ {
+ this.font = aFont;
+ }
+ /**
+ * @return Returns the fontSize.
+ */
+ public int getFontSize()
+ {
+ return fontSize;
+ }
+ /**
+ * @param aFontSize The fontSize to set.
+ */
+ public void setFontSize(int aFontSize)
+ {
+ this.fontSize = aFontSize;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/Version.java b/src/main/java/org/pdfbox/Version.java
new file mode 100644
index 0000000..b2e2f34
--- /dev/null
+++ b/src/main/java/org/pdfbox/Version.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox;
+
+import java.io.IOException;
+
+import java.util.Properties;
+
+import org.pdfbox.util.ResourceLoader;
+
+
+/**
+ * A simple command line utility to get the version of PDFBox.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class Version
+{
+ private static final String PDFBOX_VERSION_PROPERTIES = "Resources/pdfbox.version";
+
+ private Version()
+ {
+ //should not be constructed.
+ }
+
+ /**
+ * Get the version of PDFBox or unknown if it is not known.
+ *
+ * @return The version of pdfbox that is being used.
+ */
+ public static String getVersion()
+ {
+ String version = "unknown";
+ try
+ {
+ Properties props = ResourceLoader.loadProperties( PDFBOX_VERSION_PROPERTIES );
+ version = props.getProperty( "pdfbox.version", version );
+ }
+ catch( IOException io )
+ {
+ //if there is a problem loading the properties then don't throw an
+ //exception, 'unknown' will be returned instead.
+ io.printStackTrace();
+ }
+ return version;
+ }
+
+ /**
+ * This will print out the version of PDF to System.out.
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ if( args.length != 0 )
+ {
+ usage();
+ return;
+ }
+ System.out.println( "Version:" + getVersion() );
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: " + Version.class.getName() );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmparser/AFMParser.java b/src/main/java/org/pdfbox/afmparser/AFMParser.java
new file mode 100644
index 0000000..44c594d
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmparser/AFMParser.java
@@ -0,0 +1,1084 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmparser;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.util.StringTokenizer;
+
+import org.pdfbox.afmtypes.Composite;
+import org.pdfbox.afmtypes.CompositePart;
+import org.pdfbox.afmtypes.CharMetric;
+import org.pdfbox.afmtypes.FontMetric;
+import org.pdfbox.afmtypes.KernPair;
+import org.pdfbox.afmtypes.Ligature;
+import org.pdfbox.afmtypes.TrackKern;
+
+import org.pdfbox.util.BoundingBox;
+
+/**
+ * This class is used to parse AFM(Adobe Font Metrics) documents.
+ *
+ * @see <A href="http://partners.adobe.com/asn/developer/type/">AFM Documentation</A>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.9 $
+ */
+public class AFMParser
+{
+ /**
+ * This is a comment in a AFM file.
+ */
+ public static final String COMMENT = "Comment";
+ /**
+ * This is the constant used in the AFM file to start a font metrics item.
+ */
+ public static final String START_FONT_METRICS = "StartFontMetrics";
+ /**
+ * This is the constant used in the AFM file to end a font metrics item.
+ */
+ public static final String END_FONT_METRICS = "EndFontMetrics";
+ /**
+ * This is the font name.
+ */
+ public static final String FONT_NAME = "FontName";
+ /**
+ * This is the full name.
+ */
+ public static final String FULL_NAME = "FullName";
+ /**
+ * This is the Family name.
+ */
+ public static final String FAMILY_NAME = "FamilyName";
+ /**
+ * This is the weight.
+ */
+ public static final String WEIGHT = "Weight";
+ /**
+ * This is the font bounding box.
+ */
+ public static final String FONT_BBOX = "FontBBox";
+ /**
+ * This is the version of the font.
+ */
+ public static final String VERSION = "Version";
+ /**
+ * This is the notice.
+ */
+ public static final String NOTICE = "Notice";
+ /**
+ * This is the encoding scheme.
+ */
+ public static final String ENCODING_SCHEME = "EncodingScheme";
+ /**
+ * This is the mapping scheme.
+ */
+ public static final String MAPPING_SCHEME = "MappingScheme";
+ /**
+ * This is the escape character.
+ */
+ public static final String ESC_CHAR = "EscChar";
+ /**
+ * This is the character set.
+ */
+ public static final String CHARACTER_SET = "CharacterSet";
+ /**
+ * This is the characters attribute.
+ */
+ public static final String CHARACTERS = "Characters";
+ /**
+ * This will determine if this is a base font.
+ */
+ public static final String IS_BASE_FONT = "IsBaseFont";
+ /**
+ * This is the V Vector attribute.
+ */
+ public static final String V_VECTOR = "VVector";
+ /**
+ * This will tell if the V is fixed.
+ */
+ public static final String IS_FIXED_V = "IsFixedV";
+ /**
+ * This is the cap height attribute.
+ */
+ public static final String CAP_HEIGHT = "CapHeight";
+ /**
+ * This is the X height.
+ */
+ public static final String X_HEIGHT = "XHeight";
+ /**
+ * This is ascender attribute.
+ */
+ public static final String ASCENDER = "Ascender";
+ /**
+ * This is the descender attribute.
+ */
+ public static final String DESCENDER = "Descender";
+
+ /**
+ * The underline position.
+ */
+ public static final String UNDERLINE_POSITION = "UnderlinePosition";
+ /**
+ * This is the Underline thickness.
+ */
+ public static final String UNDERLINE_THICKNESS = "UnderlineThickness";
+ /**
+ * This is the italic angle.
+ */
+ public static final String ITALIC_ANGLE = "ItalicAngle";
+ /**
+ * This is the char width.
+ */
+ public static final String CHAR_WIDTH = "CharWidth";
+ /**
+ * This will determine if this is fixed pitch.
+ */
+ public static final String IS_FIXED_PITCH = "IsFixedPitch";
+ /**
+ * This is the start of character metrics.
+ */
+ public static final String START_CHAR_METRICS = "StartCharMetrics";
+ /**
+ * This is the end of character metrics.
+ */
+ public static final String END_CHAR_METRICS = "EndCharMetrics";
+ /**
+ * The character metrics c value.
+ */
+ public static final String CHARMETRICS_C = "C";
+ /**
+ * The character metrics c value.
+ */
+ public static final String CHARMETRICS_CH = "CH";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_WX = "WX";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W0X = "W0X";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W1X = "W1X";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_WY = "WY";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W0Y = "W0Y";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W1Y = "W1Y";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W = "W";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W0 = "W0";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_W1 = "W1";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_VV = "VV";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_N = "N";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_B = "B";
+ /**
+ * The character metrics value.
+ */
+ public static final String CHARMETRICS_L = "L";
+ /**
+ * The character metrics value.
+ */
+ public static final String STD_HW = "StdHW";
+ /**
+ * The character metrics value.
+ */
+ public static final String STD_VW = "StdVW";
+ /**
+ * This is the start of track kern data.
+ */
+ public static final String START_TRACK_KERN = "StartTrackKern";
+ /**
+ * This is the end of track kern data.
+ */
+ public static final String END_TRACK_KERN = "EndTrackKern";
+ /**
+ * This is the start of kern data.
+ */
+ public static final String START_KERN_DATA = "StartKernData";
+ /**
+ * This is the end of kern data.
+ */
+ public static final String END_KERN_DATA = "EndKernData";
+ /**
+ * This is the start of kern pairs data.
+ */
+ public static final String START_KERN_PAIRS = "StartKernPairs";
+ /**
+ * This is the end of kern pairs data.
+ */
+ public static final String END_KERN_PAIRS = "EndKernPairs";
+ /**
+ * This is the start of kern pairs data.
+ */
+ public static final String START_KERN_PAIRS0 = "StartKernPairs0";
+ /**
+ * This is the start of kern pairs data.
+ */
+ public static final String START_KERN_PAIRS1 = "StartKernPairs1";
+ /**
+ * This is the start compisites data section.
+ */
+ public static final String START_COMPOSITES = "StartComposites";
+ /**
+ * This is the end compisites data section.
+ */
+ public static final String END_COMPOSITES = "EndComposites";
+ /**
+ * This is a composite character.
+ */
+ public static final String CC = "CC";
+ /**
+ * This is a composite charater part.
+ */
+ public static final String PCC = "PCC";
+ /**
+ * This is a kern pair.
+ */
+ public static final String KERN_PAIR_KP = "KP";
+ /**
+ * This is a kern pair.
+ */
+ public static final String KERN_PAIR_KPH = "KPH";
+ /**
+ * This is a kern pair.
+ */
+ public static final String KERN_PAIR_KPX = "KPX";
+ /**
+ * This is a kern pair.
+ */
+ public static final String KERN_PAIR_KPY = "KPY";
+
+ private static final int BITS_IN_HEX = 16;
+
+
+ private InputStream input;
+ private FontMetric result;
+
+ /**
+ * A method to test parsing of all AFM documents in the resources
+ * directory.
+ *
+ * @param args Ignored.
+ *
+ * @throws IOException If there is an error parsing one of the documents.
+ */
+ public static void main( String[] args ) throws IOException
+ {
+ java.io.File afmDir = new java.io.File( "Resources/afm" );
+ java.io.File[] files = afmDir.listFiles();
+ for( int i=0; i< files.length; i++ )
+ {
+ if( files[i].getPath().toUpperCase().endsWith( ".AFM" ) )
+ {
+ long start = System.currentTimeMillis();
+ java.io.FileInputStream input = new java.io.FileInputStream( files[i] );
+ AFMParser parser = new AFMParser( input );
+ parser.parse();
+ long stop = System.currentTimeMillis();
+ System.out.println( "Parsing:" + files[i].getPath() + " " + (stop-start) );
+ }
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param in The input stream to read the AFM document from.
+ */
+ public AFMParser( InputStream in )
+ {
+ input = in;
+ }
+
+ /**
+ * This will parse the AFM document. This will close the Input stream
+ * when the parsing is finished.
+ *
+ * @throws IOException If there is an IO error reading the document.
+ */
+ public void parse() throws IOException
+ {
+ result = parseFontMetric();
+ }
+
+ /**
+ * This will get the result of the parsing.
+ *
+ * @return The parsed java object.
+ */
+ public FontMetric getResult()
+ {
+ return result;
+ }
+
+ /**
+ * This will parse a font metrics item.
+ *
+ * @return The parse font metrics item.
+ *
+ * @throws IOException If there is an error reading the AFM file.
+ */
+ private FontMetric parseFontMetric() throws IOException
+ {
+ FontMetric fontMetrics = new FontMetric();
+ String startFontMetrics = readString();
+ if( !START_FONT_METRICS.equals( startFontMetrics ) )
+ {
+ throw new IOException( "Error: The AFM file should start with " + START_FONT_METRICS +
+ " and not '" + startFontMetrics + "'" );
+ }
+ fontMetrics.setAFMVersion( readFloat() );
+ String nextCommand = null;
+ while( !END_FONT_METRICS.equals( (nextCommand = readString() ) ) )
+ {
+ if( FONT_NAME.equals( nextCommand ) )
+ {
+ fontMetrics.setFontName( readLine() );
+ }
+ else if( FULL_NAME.equals( nextCommand ) )
+ {
+ fontMetrics.setFullName( readLine() );
+ }
+ else if( FAMILY_NAME.equals( nextCommand ) )
+ {
+ fontMetrics.setFamilyName( readLine() );
+ }
+ else if( WEIGHT.equals( nextCommand ) )
+ {
+ fontMetrics.setWeight( readLine() );
+ }
+ else if( FONT_BBOX.equals( nextCommand ) )
+ {
+ BoundingBox bBox = new BoundingBox();
+ bBox.setLowerLeftX( readFloat() );
+ bBox.setLowerLeftY( readFloat() );
+ bBox.setUpperRightX( readFloat() );
+ bBox.setUpperRightY( readFloat() );
+ fontMetrics.setFontBBox( bBox );
+ }
+ else if( VERSION.equals( nextCommand ) )
+ {
+ fontMetrics.setFontVersion( readLine() );
+ }
+ else if( NOTICE.equals( nextCommand ) )
+ {
+ fontMetrics.setNotice( readLine() );
+ }
+ else if( ENCODING_SCHEME.equals( nextCommand ) )
+ {
+ fontMetrics.setEncodingScheme( readLine() );
+ }
+ else if( MAPPING_SCHEME.equals( nextCommand ) )
+ {
+ fontMetrics.setMappingScheme( readInt() );
+ }
+ else if( ESC_CHAR.equals( nextCommand ) )
+ {
+ fontMetrics.setEscChar( readInt() );
+ }
+ else if( CHARACTER_SET.equals( nextCommand ) )
+ {
+ fontMetrics.setCharacterSet( readLine() );
+ }
+ else if( CHARACTERS.equals( nextCommand ) )
+ {
+ fontMetrics.setCharacters( readInt() );
+ }
+ else if( IS_BASE_FONT.equals( nextCommand ) )
+ {
+ fontMetrics.setIsBaseFont( readBoolean() );
+ }
+ else if( V_VECTOR.equals( nextCommand ) )
+ {
+ float[] vector = new float[2];
+ vector[0] = readFloat();
+ vector[1] = readFloat();
+ fontMetrics.setVVector( vector );
+ }
+ else if( IS_FIXED_V.equals( nextCommand ) )
+ {
+ fontMetrics.setIsFixedV( readBoolean() );
+ }
+ else if( CAP_HEIGHT.equals( nextCommand ) )
+ {
+ fontMetrics.setCapHeight( readFloat() );
+ }
+ else if( X_HEIGHT.equals( nextCommand ) )
+ {
+ fontMetrics.setXHeight( readFloat() );
+ }
+ else if( ASCENDER.equals( nextCommand ) )
+ {
+ fontMetrics.setAscender( readFloat() );
+ }
+ else if( DESCENDER.equals( nextCommand ) )
+ {
+ fontMetrics.setDescender( readFloat() );
+ }
+ else if( STD_HW.equals( nextCommand ) )
+ {
+ fontMetrics.setStandardHorizontalWidth( readFloat() );
+ }
+ else if( STD_VW.equals( nextCommand ) )
+ {
+ fontMetrics.setStandardVerticalWidth( readFloat() );
+ }
+ else if( COMMENT.equals( nextCommand ) )
+ {
+ fontMetrics.addComment( readLine() );
+ }
+ else if( UNDERLINE_POSITION.equals( nextCommand ) )
+ {
+ fontMetrics.setUnderlinePosition( readFloat() );
+ }
+ else if( UNDERLINE_THICKNESS.equals( nextCommand ) )
+ {
+ fontMetrics.setUnderlineThickness( readFloat() );
+ }
+ else if( ITALIC_ANGLE.equals( nextCommand ) )
+ {
+ fontMetrics.setItalicAngle( readFloat() );
+ }
+ else if( CHAR_WIDTH.equals( nextCommand ) )
+ {
+ float[] widths = new float[2];
+ widths[0] = readFloat();
+ widths[1] = readFloat();
+ fontMetrics.setCharWidth( widths );
+ }
+ else if( IS_FIXED_PITCH.equals( nextCommand ) )
+ {
+ fontMetrics.setFixedPitch( readBoolean() );
+ }
+ else if( START_CHAR_METRICS.equals( nextCommand ) )
+ {
+ int count = readInt();
+ for( int i=0; i<count; i++ )
+ {
+ CharMetric charMetric = parseCharMetric();
+ fontMetrics.addCharMetric( charMetric );
+ }
+ String end = readString();
+ if( !end.equals( END_CHAR_METRICS ) )
+ {
+ throw new IOException( "Error: Expected '" + END_CHAR_METRICS + "' actual '" +
+ end + "'" );
+ }
+ }
+ else if( START_COMPOSITES.equals( nextCommand ) )
+ {
+ int count = readInt();
+ for( int i=0; i<count; i++ )
+ {
+ Composite part = parseComposite();
+ fontMetrics.addComposite( part );
+ }
+ String end = readString();
+ if( !end.equals( END_COMPOSITES ) )
+ {
+ throw new IOException( "Error: Expected '" + END_COMPOSITES + "' actual '" +
+ end + "'" );
+ }
+ }
+ else if( START_KERN_DATA.equals( nextCommand ) )
+ {
+ parseKernData( fontMetrics );
+ }
+ else
+ {
+ throw new IOException( "Unknown AFM key '" + nextCommand + "'" );
+ }
+ }
+ return fontMetrics;
+ }
+
+ /**
+ * This will parse the kern data.
+ *
+ * @param fontMetrics The metrics class to put the parsed data into.
+ *
+ * @throws IOException If there is an error parsing the data.
+ */
+ private void parseKernData( FontMetric fontMetrics ) throws IOException
+ {
+ String nextCommand = null;
+ while( !(nextCommand = readString()).equals( END_KERN_DATA ) )
+ {
+ if( START_TRACK_KERN.equals( nextCommand ) )
+ {
+ int count = readInt();
+ for( int i=0; i<count; i++ )
+ {
+ TrackKern kern = new TrackKern();
+ kern.setDegree( readInt() );
+ kern.setMinPointSize( readFloat() );
+ kern.setMinKern( readFloat() );
+ kern.setMaxPointSize( readFloat() );
+ kern.setMaxKern( readFloat() );
+ fontMetrics.addTrackKern( kern );
+ }
+ String end = readString();
+ if( !end.equals( END_TRACK_KERN ) )
+ {
+ throw new IOException( "Error: Expected '" + END_TRACK_KERN + "' actual '" +
+ end + "'" );
+ }
+ }
+ else if( START_KERN_PAIRS.equals( nextCommand ) )
+ {
+ int count = readInt();
+ for( int i=0; i<count; i++ )
+ {
+ KernPair pair = parseKernPair();
+ fontMetrics.addKernPair( pair );
+ }
+ String end = readString();
+ if( !end.equals( END_KERN_PAIRS ) )
+ {
+ throw new IOException( "Error: Expected '" + END_KERN_PAIRS + "' actual '" +
+ end + "'" );
+ }
+ }
+ else if( START_KERN_PAIRS0.equals( nextCommand ) )
+ {
+ int count = readInt();
+ for( int i=0; i<count; i++ )
+ {
+ KernPair pair = parseKernPair();
+ fontMetrics.addKernPair0( pair );
+ }
+ String end = readString();
+ if( !end.equals( END_KERN_PAIRS ) )
+ {
+ throw new IOException( "Error: Expected '" + END_KERN_PAIRS + "' actual '" +
+ end + "'" );
+ }
+ }
+ else if( START_KERN_PAIRS1.equals( nextCommand ) )
+ {
+ int count = readInt();
+ for( int i=0; i<count; i++ )
+ {
+ KernPair pair = parseKernPair();
+ fontMetrics.addKernPair1( pair );
+ }
+ String end = readString();
+ if( !end.equals( END_KERN_PAIRS ) )
+ {
+ throw new IOException( "Error: Expected '" + END_KERN_PAIRS + "' actual '" +
+ end + "'" );
+ }
+ }
+ else
+ {
+ throw new IOException( "Unknown kerning data type '" + nextCommand + "'" );
+ }
+ }
+ }
+
+ /**
+ * This will parse a kern pair from the data stream.
+ *
+ * @return The kern pair that was parsed from the stream.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ private KernPair parseKernPair() throws IOException
+ {
+ KernPair kernPair = new KernPair();
+ String cmd = readString();
+ if( KERN_PAIR_KP.equals( cmd ) )
+ {
+ String first = readString();
+ String second = readString();
+ float x = readFloat();
+ float y = readFloat();
+ kernPair.setFirstKernCharacter( first );
+ kernPair.setSecondKernCharacter( second );
+ kernPair.setX( x );
+ kernPair.setY( y );
+ }
+ else if( KERN_PAIR_KPH.equals( cmd ) )
+ {
+ String first = hexToString( readString() );
+ String second = hexToString( readString() );
+ float x = readFloat();
+ float y = readFloat();
+ kernPair.setFirstKernCharacter( first );
+ kernPair.setSecondKernCharacter( second );
+ kernPair.setX( x );
+ kernPair.setY( y );
+ }
+ else if( KERN_PAIR_KPX.equals( cmd ) )
+ {
+ String first = readString();
+ String second = readString();
+ float x = readFloat();
+ kernPair.setFirstKernCharacter( first );
+ kernPair.setSecondKernCharacter( second );
+ kernPair.setX( x );
+ kernPair.setY( 0 );
+ }
+ else if( KERN_PAIR_KPY.equals( cmd ) )
+ {
+ String first = readString();
+ String second = readString();
+ float y = readFloat();
+ kernPair.setFirstKernCharacter( first );
+ kernPair.setSecondKernCharacter( second );
+ kernPair.setX( 0 );
+ kernPair.setY( y );
+ }
+ else
+ {
+ throw new IOException( "Error expected kern pair command actual='" + cmd + "'" );
+ }
+ return kernPair;
+ }
+
+ /**
+ * This will convert and angle bracket hex string to a string.
+ *
+ * @param hexString An angle bracket string.
+ *
+ * @return The bytes of the hex string.
+ *
+ * @throws IOException If the string is in an invalid format.
+ */
+ private String hexToString( String hexString ) throws IOException
+ {
+ if( hexString.length() < 2 )
+ {
+ throw new IOException( "Error: Expected hex string of length >= 2 not='" + hexString );
+ }
+ if( hexString.charAt( 0 ) != '<' ||
+ hexString.charAt( hexString.length() -1 ) != '>' )
+ {
+ throw new IOException( "String should be enclosed by angle brackets '" + hexString+ "'" );
+ }
+ hexString = hexString.substring( 1, hexString.length() -1 );
+ byte[] data = new byte[ (hexString.length() / 2) ];
+ for( int i=0; i<hexString.length(); i+=2 )
+ {
+ String hex = "" + hexString.charAt( i ) + hexString.charAt( i+1 );
+ try
+ {
+ data[ i / 2 ] = (byte)Integer.parseInt( hex, BITS_IN_HEX );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error parsing AFM file:" + e );
+ }
+ }
+ return new String( data );
+ }
+
+ /**
+ * This will parse a composite part from the stream.
+ *
+ * @return The composite.
+ *
+ * @throws IOException If there is an error parsing the composite.
+ */
+ private Composite parseComposite() throws IOException
+ {
+ Composite composite = new Composite();
+ String partData = readLine();
+ StringTokenizer tokenizer = new StringTokenizer( partData, " ;" );
+
+
+ String cc = tokenizer.nextToken();
+ if( !cc.equals( CC ) )
+ {
+ throw new IOException( "Expected '" + CC + "' actual='" + cc + "'" );
+ }
+ String name = tokenizer.nextToken();
+ composite.setName( name );
+
+ int partCount;
+ try
+ {
+ partCount = Integer.parseInt( tokenizer.nextToken() );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error parsing AFM document:" + e );
+ }
+ for( int i=0; i<partCount; i++ )
+ {
+ CompositePart part = new CompositePart();
+ String pcc = tokenizer.nextToken();
+ if( !pcc.equals( PCC ) )
+ {
+ throw new IOException( "Expected '" + PCC + "' actual='" + pcc + "'" );
+ }
+ String partName = tokenizer.nextToken();
+ try
+ {
+ int x = Integer.parseInt( tokenizer.nextToken() );
+ int y = Integer.parseInt( tokenizer.nextToken() );
+
+ part.setName( partName );
+ part.setXDisplacement( x );
+ part.setYDisplacement( y );
+ composite.addPart( part );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error parsing AFM document:" + e );
+ }
+ }
+ return composite;
+ }
+
+ /**
+ * This will parse a single CharMetric object from the stream.
+ *
+ * @return The next char metric in the stream.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ private CharMetric parseCharMetric() throws IOException
+ {
+ CharMetric charMetric = new CharMetric();
+ String metrics = readLine();
+ StringTokenizer metricsTokenizer = new StringTokenizer( metrics );
+ try
+ {
+ while( metricsTokenizer.hasMoreTokens() )
+ {
+ String nextCommand = metricsTokenizer.nextToken();
+ if( nextCommand.equals( CHARMETRICS_C ) )
+ {
+ String charCode = metricsTokenizer.nextToken();
+ charMetric.setCharacterCode( Integer.parseInt( charCode ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_CH ) )
+ {
+ //Is the hex string <FF> or FF, the spec is a little
+ //unclear, wait and see if it breaks anything.
+ String charCode = metricsTokenizer.nextToken();
+ charMetric.setCharacterCode( Integer.parseInt( charCode, BITS_IN_HEX ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_WX ) )
+ {
+ String wx = metricsTokenizer.nextToken();
+ charMetric.setWx( Float.parseFloat( wx ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W0X ) )
+ {
+ String w0x = metricsTokenizer.nextToken();
+ charMetric.setW0x( Float.parseFloat( w0x ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W1X ) )
+ {
+ String w1x = metricsTokenizer.nextToken();
+ charMetric.setW0x( Float.parseFloat( w1x ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_WY ) )
+ {
+ String wy = metricsTokenizer.nextToken();
+ charMetric.setWy( Float.parseFloat( wy ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W0Y ) )
+ {
+ String w0y = metricsTokenizer.nextToken();
+ charMetric.setW0y( Float.parseFloat( w0y ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W1Y ) )
+ {
+ String w1y = metricsTokenizer.nextToken();
+ charMetric.setW0y( Float.parseFloat( w1y ) );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W ) )
+ {
+ String w0 = metricsTokenizer.nextToken();
+ String w1 = metricsTokenizer.nextToken();
+ float[] w = new float[2];
+ w[0] = Float.parseFloat( w0 );
+ w[1] = Float.parseFloat( w1 );
+ charMetric.setW( w );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W0 ) )
+ {
+ String w00 = metricsTokenizer.nextToken();
+ String w01 = metricsTokenizer.nextToken();
+ float[] w0 = new float[2];
+ w0[0] = Float.parseFloat( w00 );
+ w0[1] = Float.parseFloat( w01 );
+ charMetric.setW0( w0 );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_W1 ) )
+ {
+ String w10 = metricsTokenizer.nextToken();
+ String w11 = metricsTokenizer.nextToken();
+ float[] w1 = new float[2];
+ w1[0] = Float.parseFloat( w10 );
+ w1[1] = Float.parseFloat( w11 );
+ charMetric.setW1( w1 );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_VV ) )
+ {
+ String vv0 = metricsTokenizer.nextToken();
+ String vv1 = metricsTokenizer.nextToken();
+ float[] vv = new float[2];
+ vv[0] = Float.parseFloat( vv0 );
+ vv[1] = Float.parseFloat( vv1 );
+ charMetric.setVv( vv );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_N ) )
+ {
+ String name = metricsTokenizer.nextToken();
+ charMetric.setName( name );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_B ) )
+ {
+ String llx = metricsTokenizer.nextToken();
+ String lly = metricsTokenizer.nextToken();
+ String urx = metricsTokenizer.nextToken();
+ String ury = metricsTokenizer.nextToken();
+ BoundingBox box = new BoundingBox();
+ box.setLowerLeftX( Float.parseFloat( llx ) );
+ box.setLowerLeftY( Float.parseFloat( lly ) );
+ box.setUpperRightX( Float.parseFloat( urx ) );
+ box.setUpperRightY( Float.parseFloat( ury ) );
+ charMetric.setBoundingBox( box );
+ verifySemicolon( metricsTokenizer );
+ }
+ else if( nextCommand.equals( CHARMETRICS_L ) )
+ {
+ String successor = metricsTokenizer.nextToken();
+ String ligature = metricsTokenizer.nextToken();
+ Ligature lig = new Ligature();
+ lig.setSuccessor( successor );
+ lig.setLigature( ligature );
+ charMetric.addLigature( lig );
+ verifySemicolon( metricsTokenizer );
+ }
+ else
+ {
+ throw new IOException( "Unknown CharMetrics command '" + nextCommand + "'" );
+ }
+ }
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error: Corrupt AFM document:" + e );
+ }
+ return charMetric;
+ }
+
+ /**
+ * This is used to verify that a semicolon is the next token in the stream.
+ *
+ * @param tokenizer The tokenizer to read from.
+ *
+ * @throws IOException If the semicolon is missing.
+ */
+ private void verifySemicolon( StringTokenizer tokenizer ) throws IOException
+ {
+ if( tokenizer.hasMoreTokens() )
+ {
+ String semicolon = tokenizer.nextToken();
+ if( !semicolon.equals( ";" ) )
+ {
+ throw new IOException( "Error: Expected semicolon in stream actual='" +
+ semicolon + "'" );
+ }
+ }
+ else
+ {
+ throw new IOException( "CharMetrics is missing a semicolon after a command" );
+ }
+ }
+
+ /**
+ * This will read a boolean from the stream.
+ *
+ * @return The boolean in the stream.
+ */
+ private boolean readBoolean() throws IOException
+ {
+ String theBoolean = readString();
+ return Boolean.valueOf( theBoolean ).booleanValue();
+ }
+
+ /**
+ * This will read an integer from the stream.
+ *
+ * @return The integer in the stream.
+ */
+ private int readInt() throws IOException
+ {
+ String theInt = readString();
+ try
+ {
+ return Integer.parseInt( theInt );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error parsing AFM document:" + e );
+ }
+ }
+
+ /**
+ * This will read a float from the stream.
+ *
+ * @return The float in the stream.
+ */
+ private float readFloat() throws IOException
+ {
+ String theFloat = readString();
+ return Float.parseFloat( theFloat );
+ }
+
+ /**
+ * This will read until the end of a line.
+ *
+ * @return The string that is read.
+ */
+ private String readLine() throws IOException
+ {
+ //First skip the whitespace
+ StringBuffer buf = new StringBuffer();
+ int nextByte = input.read();
+ while( isWhitespace( nextByte ) )
+ {
+ nextByte = input.read();
+ //do nothing just skip the whitespace.
+ }
+ buf.append( (char)nextByte );
+
+ //now read the data
+ while( !isEOL(nextByte = input.read()) )
+ {
+ buf.append( (char)nextByte );
+ }
+ return buf.toString();
+ }
+
+ /**
+ * This will read a string from the input stream and stop at any whitespace.
+ *
+ * @return The string read from the stream.
+ *
+ * @throws IOException If an IO error occurs when reading from the stream.
+ */
+ private String readString() throws IOException
+ {
+ //First skip the whitespace
+ StringBuffer buf = new StringBuffer();
+ int nextByte = input.read();
+ while( isWhitespace( nextByte ) )
+ {
+ nextByte = input.read();
+ //do nothing just skip the whitespace.
+ }
+ buf.append( (char)nextByte );
+
+ //now read the data
+ while( !isWhitespace(nextByte = input.read()) )
+ {
+ buf.append( (char)nextByte );
+ }
+ return buf.toString();
+ }
+
+ /**
+ * This will determine if the byte is a whitespace character or not.
+ *
+ * @param character The character to test for whitespace.
+ *
+ * @return true If the character is whitespace as defined by the AFM spec.
+ */
+ private boolean isEOL( int character )
+ {
+ return character == 0x0D ||
+ character == 0x0A;
+ }
+
+ /**
+ * This will determine if the byte is a whitespace character or not.
+ *
+ * @param character The character to test for whitespace.
+ *
+ * @return true If the character is whitespace as defined by the AFM spec.
+ */
+ private boolean isWhitespace( int character )
+ {
+ return character == ' ' ||
+ character == '\t' ||
+ character == 0x0D ||
+ character == 0x0A;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmparser/package.html b/src/main/java/org/pdfbox/afmparser/package.html
new file mode 100644
index 0000000..0785815
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmparser/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package holds classes used to parse AFM(Adobe Font Metrics) files.
+<br/>
+More information about AFM files can be found at
+<a href="http://partners.adobe.com/asn/developer/type/">http://partners.adobe.com/asn/developer/type/</a>
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/afmtypes/CharMetric.java b/src/main/java/org/pdfbox/afmtypes/CharMetric.java
new file mode 100644
index 0000000..7615845
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/CharMetric.java
@@ -0,0 +1,299 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.util.BoundingBox;
+
+/**
+ * This class represents a single character metric.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class CharMetric
+{
+ private int characterCode;
+
+ private float wx;
+ private float w0x;
+ private float w1x;
+
+ private float wy;
+ private float w0y;
+ private float w1y;
+
+ private float[] w;
+ private float[] w0;
+ private float[] w1;
+ private float[] vv;
+
+ private String name;
+ private BoundingBox boundingBox;
+ private List ligatures = new ArrayList();
+
+ /** Getter for property boundingBox.
+ * @return Value of property boundingBox.
+ */
+ public BoundingBox getBoundingBox()
+ {
+ return boundingBox;
+ }
+
+ /** Setter for property boundingBox.
+ * @param bBox New value of property boundingBox.
+ */
+ public void setBoundingBox(BoundingBox bBox)
+ {
+ boundingBox = bBox;
+ }
+
+ /** Getter for property characterCode.
+ * @return Value of property characterCode.
+ */
+ public int getCharacterCode()
+ {
+ return characterCode;
+ }
+
+ /** Setter for property characterCode.
+ * @param cCode New value of property characterCode.
+ */
+ public void setCharacterCode(int cCode)
+ {
+ characterCode = cCode;
+ }
+
+ /**
+ * This will add an entry to the list of ligatures.
+ *
+ * @param ligature The ligature to add.
+ */
+ public void addLigature( Ligature ligature )
+ {
+ ligatures.add( ligature );
+ }
+
+ /** Getter for property ligatures.
+ * @return Value of property ligatures.
+ */
+ public List getLigatures()
+ {
+ return ligatures;
+ }
+
+ /** Setter for property ligatures.
+ * @param lig New value of property ligatures.
+ */
+ public void setLigatures(List lig)
+ {
+ this.ligatures = lig;
+ }
+
+ /** Getter for property name.
+ * @return Value of property name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /** Setter for property name.
+ * @param n New value of property name.
+ */
+ public void setName(String n)
+ {
+ this.name = n;
+ }
+
+ /** Getter for property vv.
+ * @return Value of property vv.
+ */
+ public float[] getVv()
+ {
+ return this.vv;
+ }
+
+ /** Setter for property vv.
+ * @param vvValue New value of property vv.
+ */
+ public void setVv(float[] vvValue)
+ {
+ this.vv = vvValue;
+ }
+
+ /** Getter for property w.
+ * @return Value of property w.
+ */
+ public float[] getW()
+ {
+ return this.w;
+ }
+
+ /** Setter for property w.
+ * @param wValue New value of property w.
+ */
+ public void setW(float[] wValue)
+ {
+ this.w = wValue;
+ }
+
+ /** Getter for property w0.
+ * @return Value of property w0.
+ */
+ public float[] getW0()
+ {
+ return this.w0;
+ }
+
+ /** Setter for property w0.
+ * @param w0Value New value of property w0.
+ */
+ public void setW0(float[] w0Value)
+ {
+ w0 = w0Value;
+ }
+
+ /** Getter for property w0x.
+ * @return Value of property w0x.
+ */
+ public float getW0x()
+ {
+ return w0x;
+ }
+
+ /** Setter for property w0x.
+ * @param w0xValue New value of property w0x.
+ */
+ public void setW0x(float w0xValue)
+ {
+ w0x = w0xValue;
+ }
+
+ /** Getter for property w0y.
+ * @return Value of property w0y.
+ */
+ public float getW0y()
+ {
+ return w0y;
+ }
+
+ /** Setter for property w0y.
+ * @param w0yValue New value of property w0y.
+ */
+ public void setW0y(float w0yValue)
+ {
+ w0y = w0yValue;
+ }
+
+ /** Getter for property w1.
+ * @return Value of property w1.
+ */
+ public float[] getW1()
+ {
+ return this.w1;
+ }
+
+ /** Setter for property w1.
+ * @param w1Value New value of property w1.
+ */
+ public void setW1(float[] w1Value)
+ {
+ w1 = w1Value;
+ }
+
+ /** Getter for property w1x.
+ * @return Value of property w1x.
+ */
+ public float getW1x()
+ {
+ return w1x;
+ }
+
+ /** Setter for property w1x.
+ * @param w1xValue New value of property w1x.
+ */
+ public void setW1x(float w1xValue)
+ {
+ w1x = w1xValue;
+ }
+
+ /** Getter for property w1y.
+ * @return Value of property w1y.
+ */
+ public float getW1y()
+ {
+ return w1y;
+ }
+
+ /** Setter for property w1y.
+ * @param w1yValue New value of property w1y.
+ */
+ public void setW1y(float w1yValue)
+ {
+ w1y = w1yValue;
+ }
+
+ /** Getter for property wx.
+ * @return Value of property wx.
+ */
+ public float getWx()
+ {
+ return wx;
+ }
+
+ /** Setter for property wx.
+ * @param wxValue New value of property wx.
+ */
+ public void setWx(float wxValue)
+ {
+ wx = wxValue;
+ }
+
+ /** Getter for property wy.
+ * @return Value of property wy.
+ */
+ public float getWy()
+ {
+ return wy;
+ }
+
+ /** Setter for property wy.
+ * @param wyValue New value of property wy.
+ */
+ public void setWy(float wyValue)
+ {
+ this.wy = wyValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/Composite.java b/src/main/java/org/pdfbox/afmtypes/Composite.java
new file mode 100644
index 0000000..874e373
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/Composite.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents composite character data.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class Composite
+{
+ private String name;
+ private List parts = new ArrayList();
+
+ /** Getter for property name.
+ * @return Value of property name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /** Setter for property name.
+ * @param nameValue New value of property name.
+ */
+ public void setName(String nameValue)
+ {
+ this.name = nameValue;
+ }
+
+ /**
+ * This will add a composite part.
+ *
+ * @param part The composite part to add.
+ */
+ public void addPart( CompositePart part )
+ {
+ parts.add( part );
+ }
+
+ /** Getter for property parts.
+ * @return Value of property parts.
+ */
+ public List getParts()
+ {
+ return parts;
+ }
+
+ /** Setter for property parts.
+ * @param partsList New value of property parts.
+ */
+ public void setParts(List partsList)
+ {
+ this.parts = partsList;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/CompositePart.java b/src/main/java/org/pdfbox/afmtypes/CompositePart.java
new file mode 100644
index 0000000..b88ac9e
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/CompositePart.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+/**
+ * This class represents a part of composite character data.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class CompositePart
+{
+ private String name;
+ private int xDisplacement;
+ private int yDisplacement;
+
+ /** Getter for property name.
+ * @return Value of property name.
+ */
+ public java.lang.String getName()
+ {
+ return name;
+ }
+
+ /** Setter for property name.
+ * @param nameValue New value of property name.
+ */
+ public void setName(String nameValue)
+ {
+ name = nameValue;
+ }
+
+ /** Getter for property xDisplacement.
+ * @return Value of property xDisplacement.
+ */
+ public int getXDisplacement()
+ {
+ return xDisplacement;
+ }
+
+ /** Setter for property xDisplacement.
+ * @param xDisp New value of property xDisplacement.
+ */
+ public void setXDisplacement(int xDisp)
+ {
+ xDisplacement = xDisp;
+ }
+
+ /** Getter for property yDisplacement.
+ * @return Value of property yDisplacement.
+ */
+ public int getYDisplacement()
+ {
+ return yDisplacement;
+ }
+
+ /** Setter for property yDisplacement.
+ * @param yDisp New value of property yDisplacement.
+ */
+ public void setYDisplacement(int yDisp)
+ {
+ yDisplacement = yDisp;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/FontMetric.java b/src/main/java/org/pdfbox/afmtypes/FontMetric.java
new file mode 100644
index 0000000..b9fbeb8
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/FontMetric.java
@@ -0,0 +1,891 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.util.BoundingBox;
+
+/**
+ * This is the outermost AFM type. This can be created by the afmparser with a valid
+ * AFM document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class FontMetric
+{
+ /**
+ * This is the version of the FontMetrics.
+ */
+ private float afmVersion;
+ private int metricSets = 0;
+ private String fontName;
+ private String fullName;
+ private String familyName;
+ private String weight;
+ private BoundingBox fontBBox;
+ private String fontVersion;
+ private String notice;
+ private String encodingScheme;
+ private int mappingScheme;
+ private int escChar;
+ private String characterSet;
+ private int characters;
+ private boolean isBaseFont;
+ private float[] vVector;
+ private boolean isFixedV;
+ private float capHeight;
+ private float xHeight;
+ private float ascender;
+ private float descender;
+ private List comments = new ArrayList();
+
+ private float underlinePosition;
+ private float underlineThickness;
+ private float italicAngle;
+ private float[] charWidth;
+ private boolean isFixedPitch;
+ private float standardHorizontalWidth;
+ private float standardVerticalWidth;
+
+ private List charMetrics = new ArrayList();
+ private Map charMetricsMap = new HashMap();
+ private List trackKern = new ArrayList();
+ private List composites = new ArrayList();
+ private List kernPairs = new ArrayList();
+ private List kernPairs0 = new ArrayList();
+ private List kernPairs1 = new ArrayList();
+
+ /**
+ * Constructor.
+ */
+ public FontMetric()
+ {
+ }
+
+ /**
+ * This will get the width of a character.
+ *
+ * @param name The character to get the width for.
+ *
+ * @return The width of the character.
+ *
+ * @throws IOException If this AFM file does not handle the character.
+ */
+ public float getCharacterWidth( String name ) throws IOException
+ {
+ float result = 0;
+ CharMetric metric = (CharMetric)charMetricsMap.get( name );
+ if( metric == null )
+ {
+ result=0;
+ //don't throw an exception right away.
+ //throw new IOException( "Unknown AFM(" + getFullName() + ") characer '" + name + "'" );
+ }
+ else
+ {
+ result = metric.getWx();
+ }
+ return result;
+ }
+
+ /**
+ * This will get the average width of a character.
+ *
+ * @return The width of the character.
+ *
+ * @throws IOException If this AFM file does not handle the character.
+ */
+ public float getAverageCharacterWidth() throws IOException
+ {
+ float average = 0;
+ float totalWidths = 0;
+ float characterCount = 0;
+ Iterator iter = charMetricsMap.values().iterator();
+ while( iter.hasNext() )
+ {
+ CharMetric metric = (CharMetric)iter.next();
+ if( metric.getWx() > 0 )
+ {
+ totalWidths += metric.getWx();
+ characterCount += 1;
+ }
+ }
+ if( totalWidths > 0 )
+ {
+ average = totalWidths / characterCount;
+ }
+
+ return average;
+ }
+
+ /**
+ * This will add a new comment.
+ *
+ * @param comment The comment to add to this metric.
+ */
+ public void addComment( String comment )
+ {
+ comments.add( comment );
+ }
+
+ /**
+ * This will get all comments.
+ *
+ * @return The list of all comments.
+ */
+ public List getComments()
+ {
+ return comments;
+ }
+
+ /**
+ * This will get the version of the AFM document.
+ *
+ * @return The version of the document.
+ */
+ public float getAFMVersion()
+ {
+ return afmVersion;
+ }
+
+ /**
+ * This will get the metricSets attribute.
+ *
+ * @return The value of the metric sets.
+ */
+ public int getMetricSets()
+ {
+ return metricSets;
+ }
+
+ /**
+ * This will set the version of the AFM document.
+ *
+ * @param afmVersionValue The version of the document.
+ */
+ public void setAFMVersion( float afmVersionValue )
+ {
+ afmVersion = afmVersionValue;
+ }
+
+ /**
+ * This will set the metricSets attribute. This value must be 0,1, or 2.
+ *
+ * @param metricSetsValue The new metric sets attribute.
+ */
+ public void setMetricSets( int metricSetsValue )
+ {
+ if( metricSetsValue < 0 || metricSetsValue > 2 )
+ {
+ throw new RuntimeException( "The metricSets attribute must be in the " +
+ "set {0,1,2} and not '" + metricSetsValue + "'" );
+ }
+ metricSets = metricSetsValue;
+ }
+
+ /**
+ * Getter for property fontName.
+ *
+ * @return Value of property fontName.
+ */
+ public String getFontName()
+ {
+ return fontName;
+ }
+
+ /**
+ * Setter for property fontName.
+ *
+ * @param name New value of property fontName.
+ */
+ public void setFontName(String name)
+ {
+ fontName = name;
+ }
+
+ /**
+ * Getter for property fullName.
+ *
+ * @return Value of property fullName.
+ */
+ public String getFullName()
+ {
+ return fullName;
+ }
+
+ /**
+ * Setter for property fullName.
+ *
+ * @param fullNameValue New value of property fullName.
+ */
+ public void setFullName(String fullNameValue)
+ {
+ fullName = fullNameValue;
+ }
+
+ /**
+ * Getter for property familyName.
+ *
+ * @return Value of property familyName.
+ */
+ public String getFamilyName()
+ {
+ return familyName;
+ }
+
+ /**
+ * Setter for property familyName.
+ *
+ * @param familyNameValue New value of property familyName.
+ */
+ public void setFamilyName(String familyNameValue)
+ {
+ familyName = familyNameValue;
+ }
+
+ /**
+ * Getter for property weight.
+ *
+ * @return Value of property weight.
+ */
+ public String getWeight()
+ {
+ return weight;
+ }
+
+ /**
+ * Setter for property weight.
+ *
+ * @param weightValue New value of property weight.
+ */
+ public void setWeight(String weightValue)
+ {
+ weight = weightValue;
+ }
+
+ /**
+ * Getter for property fontBBox.
+ *
+ * @return Value of property fontBBox.
+ */
+ public BoundingBox getFontBBox()
+ {
+ return fontBBox;
+ }
+
+ /**
+ * Setter for property fontBBox.
+ *
+ * @param bBox New value of property fontBBox.
+ */
+ public void setFontBBox(BoundingBox bBox)
+ {
+ this.fontBBox = bBox;
+ }
+
+ /**
+ * Getter for property notice.
+ *
+ * @return Value of property notice.
+ */
+ public String getNotice()
+ {
+ return notice;
+ }
+
+ /**
+ * Setter for property notice.
+ *
+ * @param noticeValue New value of property notice.
+ */
+ public void setNotice(String noticeValue)
+ {
+ notice = noticeValue;
+ }
+
+ /**
+ * Getter for property encodingScheme.
+ *
+ * @return Value of property encodingScheme.
+ */
+ public String getEncodingScheme()
+ {
+ return encodingScheme;
+ }
+
+ /**
+ * Setter for property encodingScheme.
+ *
+ * @param encodingSchemeValue New value of property encodingScheme.
+ */
+ public void setEncodingScheme(String encodingSchemeValue)
+ {
+ encodingScheme = encodingSchemeValue;
+ }
+
+ /**
+ * Getter for property mappingScheme.
+ *
+ * @return Value of property mappingScheme.
+ */
+ public int getMappingScheme()
+ {
+ return mappingScheme;
+ }
+
+ /**
+ * Setter for property mappingScheme.
+ *
+ * @param mappingSchemeValue New value of property mappingScheme.
+ */
+ public void setMappingScheme(int mappingSchemeValue)
+ {
+ mappingScheme = mappingSchemeValue;
+ }
+
+ /**
+ * Getter for property escChar.
+ *
+ * @return Value of property escChar.
+ */
+ public int getEscChar()
+ {
+ return escChar;
+ }
+
+ /**
+ * Setter for property escChar.
+ *
+ * @param escCharValue New value of property escChar.
+ */
+ public void setEscChar(int escCharValue)
+ {
+ escChar = escCharValue;
+ }
+
+ /**
+ * Getter for property characterSet.
+ *
+ * @return Value of property characterSet.
+ */
+ public String getCharacterSet()
+ {
+ return characterSet;
+ }
+
+ /**
+ * Setter for property characterSet.
+ *
+ * @param characterSetValue New value of property characterSet.
+ */
+ public void setCharacterSet(String characterSetValue)
+ {
+ characterSet = characterSetValue;
+ }
+
+ /**
+ * Getter for property characters.
+ *
+ * @return Value of property characters.
+ */
+ public int getCharacters()
+ {
+ return characters;
+ }
+
+ /**
+ * Setter for property characters.
+ *
+ * @param charactersValue New value of property characters.
+ */
+ public void setCharacters(int charactersValue)
+ {
+ characters = charactersValue;
+ }
+
+ /**
+ * Getter for property isBaseFont.
+ *
+ * @return Value of property isBaseFont.
+ */
+ public boolean isBaseFont()
+ {
+ return isBaseFont;
+ }
+
+ /**
+ * Setter for property isBaseFont.
+ *
+ * @param isBaseFontValue New value of property isBaseFont.
+ */
+ public void setIsBaseFont(boolean isBaseFontValue)
+ {
+ isBaseFont = isBaseFontValue;
+ }
+
+ /**
+ * Getter for property vVector.
+ *
+ * @return Value of property vVector.
+ */
+ public float[] getVVector()
+ {
+ return this.vVector;
+ }
+
+ /**
+ * Setter for property vVector.
+ *
+ * @param vVectorValue New value of property vVector.
+ */
+ public void setVVector(float[] vVectorValue)
+ {
+ vVector = vVectorValue;
+ }
+
+ /**
+ * Getter for property isFixedV.
+ *
+ * @return Value of property isFixedV.
+ */
+ public boolean isFixedV()
+ {
+ return isFixedV;
+ }
+
+ /**
+ * Setter for property isFixedV.
+ *
+ * @param isFixedVValue New value of property isFixedV.
+ */
+ public void setIsFixedV(boolean isFixedVValue)
+ {
+ isFixedV = isFixedVValue;
+ }
+
+ /**
+ * Getter for property capHeight.
+ *
+ * @return Value of property capHeight.
+ */
+ public float getCapHeight()
+ {
+ return capHeight;
+ }
+
+ /**
+ * Setter for property capHeight.
+ *
+ * @param capHeightValue New value of property capHeight.
+ */
+ public void setCapHeight(float capHeightValue)
+ {
+ capHeight = capHeightValue;
+ }
+
+ /**
+ * Getter for property xHeight.
+ *
+ * @return Value of property xHeight.
+ */
+ public float getXHeight()
+ {
+ return xHeight;
+ }
+
+ /**
+ * Setter for property xHeight.
+ *
+ * @param xHeightValue New value of property xHeight.
+ */
+ public void setXHeight( float xHeightValue )
+ {
+ xHeight = xHeightValue;
+ }
+
+ /**
+ * Getter for property ascender.
+ *
+ * @return Value of property ascender.
+ */
+ public float getAscender()
+ {
+ return ascender;
+ }
+
+ /**
+ * Setter for property ascender.
+ *
+ * @param ascenderValue New value of property ascender.
+ */
+ public void setAscender( float ascenderValue )
+ {
+ ascender = ascenderValue;
+ }
+
+ /**
+ * Getter for property descender.
+ *
+ * @return Value of property descender.
+ */
+ public float getDescender()
+ {
+ return descender;
+ }
+
+ /**
+ * Setter for property descender.
+ *
+ * @param descenderValue New value of property descender.
+ */
+ public void setDescender( float descenderValue )
+ {
+ descender = descenderValue;
+ }
+
+ /**
+ * Getter for property fontVersion.
+ *
+ * @return Value of property fontVersion.
+ */
+ public String getFontVersion()
+ {
+ return fontVersion;
+ }
+
+ /**
+ * Setter for property fontVersion.
+ *
+ * @param fontVersionValue New value of property fontVersion.
+ */
+ public void setFontVersion(String fontVersionValue)
+ {
+ fontVersion = fontVersionValue;
+ }
+
+ /**
+ * Getter for property underlinePosition.
+ *
+ * @return Value of property underlinePosition.
+ */
+ public float getUnderlinePosition()
+ {
+ return underlinePosition;
+ }
+
+ /**
+ * Setter for property underlinePosition.
+ *
+ * @param underlinePositionValue New value of property underlinePosition.
+ */
+ public void setUnderlinePosition(float underlinePositionValue)
+ {
+ underlinePosition = underlinePositionValue;
+ }
+
+ /**
+ * Getter for property underlineThickness.
+ *
+ * @return Value of property underlineThickness.
+ */
+ public float getUnderlineThickness()
+ {
+ return underlineThickness;
+ }
+
+ /**
+ * Setter for property underlineThickness.
+ *
+ * @param underlineThicknessValue New value of property underlineThickness.
+ */
+ public void setUnderlineThickness(float underlineThicknessValue)
+ {
+ underlineThickness = underlineThicknessValue;
+ }
+
+ /**
+ * Getter for property italicAngle.
+ *
+ * @return Value of property italicAngle.
+ */
+ public float getItalicAngle()
+ {
+ return italicAngle;
+ }
+
+ /**
+ * Setter for property italicAngle.
+ *
+ * @param italicAngleValue New value of property italicAngle.
+ */
+ public void setItalicAngle(float italicAngleValue)
+ {
+ italicAngle = italicAngleValue;
+ }
+
+ /**
+ * Getter for property charWidth.
+ *
+ * @return Value of property charWidth.
+ */
+ public float[] getCharWidth()
+ {
+ return this.charWidth;
+ }
+
+ /**
+ * Setter for property charWidth.
+ *
+ * @param charWidthValue New value of property charWidth.
+ */
+ public void setCharWidth(float[] charWidthValue)
+ {
+ charWidth = charWidthValue;
+ }
+
+ /**
+ * Getter for property isFixedPitch.
+ *
+ * @return Value of property isFixedPitch.
+ */
+ public boolean isFixedPitch()
+ {
+ return isFixedPitch;
+ }
+
+ /**
+ * Setter for property isFixedPitch.
+ *
+ * @param isFixedPitchValue New value of property isFixedPitch.
+ */
+ public void setFixedPitch(boolean isFixedPitchValue)
+ {
+ isFixedPitch = isFixedPitchValue;
+ }
+
+ /** Getter for property charMetrics.
+ * @return Value of property charMetrics.
+ */
+ public List getCharMetrics()
+ {
+ return charMetrics;
+ }
+
+ /** Setter for property charMetrics.
+ * @param charMetricsValue New value of property charMetrics.
+ */
+ public void setCharMetrics(List charMetricsValue)
+ {
+ charMetrics = charMetricsValue;
+ }
+
+ /**
+ * This will add another character metric.
+ *
+ * @param metric The character metric to add.
+ */
+ public void addCharMetric( CharMetric metric )
+ {
+ charMetrics.add( metric );
+ charMetricsMap.put( metric.getName(), metric );
+ }
+
+ /** Getter for property trackKern.
+ * @return Value of property trackKern.
+ */
+ public List getTrackKern()
+ {
+ return trackKern;
+ }
+
+ /** Setter for property trackKern.
+ * @param trackKernValue New value of property trackKern.
+ */
+ public void setTrackKern(List trackKernValue)
+ {
+ trackKern = trackKernValue;
+ }
+
+ /**
+ * This will add another track kern.
+ *
+ * @param kern The track kerning data.
+ */
+ public void addTrackKern( TrackKern kern )
+ {
+ trackKern.add( kern );
+ }
+
+ /** Getter for property composites.
+ * @return Value of property composites.
+ */
+ public List getComposites()
+ {
+ return composites;
+ }
+
+ /** Setter for property composites.
+ * @param compositesList New value of property composites.
+ */
+ public void setComposites(List compositesList)
+ {
+ composites = compositesList;
+ }
+
+ /**
+ * This will add a single composite part to the picture.
+ *
+ * @param composite The composite info to add.
+ */
+ public void addComposite( Composite composite )
+ {
+ composites.add( composite );
+ }
+
+ /** Getter for property kernPairs.
+ * @return Value of property kernPairs.
+ */
+ public java.util.List getKernPairs()
+ {
+ return kernPairs;
+ }
+
+ /**
+ * This will add a kern pair.
+ *
+ * @param kernPair The kern pair to add.
+ */
+ public void addKernPair( KernPair kernPair )
+ {
+ kernPairs.add( kernPair );
+ }
+
+ /** Setter for property kernPairs.
+ * @param kernPairsList New value of property kernPairs.
+ */
+ public void setKernPairs(java.util.List kernPairsList)
+ {
+ kernPairs = kernPairsList;
+ }
+
+ /** Getter for property kernPairs0.
+ * @return Value of property kernPairs0.
+ */
+ public java.util.List getKernPairs0()
+ {
+ return kernPairs0;
+ }
+
+ /**
+ * This will add a kern pair.
+ *
+ * @param kernPair The kern pair to add.
+ */
+ public void addKernPair0( KernPair kernPair )
+ {
+ kernPairs0.add( kernPair );
+ }
+
+ /** Setter for property kernPairs0.
+ * @param kernPairs0List New value of property kernPairs0.
+ */
+ public void setKernPairs0(java.util.List kernPairs0List)
+ {
+ kernPairs0 = kernPairs0List;
+ }
+
+ /** Getter for property kernPairs1.
+ * @return Value of property kernPairs1.
+ */
+ public java.util.List getKernPairs1()
+ {
+ return kernPairs1;
+ }
+
+ /**
+ * This will add a kern pair.
+ *
+ * @param kernPair The kern pair to add.
+ */
+ public void addKernPair1( KernPair kernPair )
+ {
+ kernPairs1.add( kernPair );
+ }
+
+ /** Setter for property kernPairs1.
+ * @param kernPairs1List New value of property kernPairs1.
+ */
+ public void setKernPairs1(java.util.List kernPairs1List)
+ {
+ kernPairs1 = kernPairs1List;
+ }
+
+ /** Getter for property standardHorizontalWidth.
+ * @return Value of property standardHorizontalWidth.
+ */
+ public float getStandardHorizontalWidth()
+ {
+ return standardHorizontalWidth;
+ }
+
+ /** Setter for property standardHorizontalWidth.
+ * @param standardHorizontalWidthValue New value of property standardHorizontalWidth.
+ */
+ public void setStandardHorizontalWidth(float standardHorizontalWidthValue)
+ {
+ standardHorizontalWidth = standardHorizontalWidthValue;
+ }
+
+ /** Getter for property standardVerticalWidth.
+ * @return Value of property standardVerticalWidth.
+ */
+ public float getStandardVerticalWidth()
+ {
+ return standardVerticalWidth;
+ }
+
+ /** Setter for property standardVerticalWidth.
+ * @param standardVerticalWidthValue New value of property standardVerticalWidth.
+ */
+ public void setStandardVerticalWidth(float standardVerticalWidthValue)
+ {
+ standardVerticalWidth = standardVerticalWidthValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/KernPair.java b/src/main/java/org/pdfbox/afmtypes/KernPair.java
new file mode 100644
index 0000000..a822d22
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/KernPair.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+/**
+ * This represents some kern pair data.
+ *
+ * @author Ben Litchfied (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class KernPair
+{
+ private String firstKernCharacter;
+ private String secondKernCharacter;
+ private float x;
+ private float y;
+
+ /** Getter for property firstKernCharacter.
+ * @return Value of property firstKernCharacter.
+ */
+ public java.lang.String getFirstKernCharacter()
+ {
+ return firstKernCharacter;
+ }
+
+ /** Setter for property firstKernCharacter.
+ * @param firstKernCharacterValue New value of property firstKernCharacter.
+ */
+ public void setFirstKernCharacter(String firstKernCharacterValue)
+ {
+ firstKernCharacter = firstKernCharacterValue;
+ }
+
+ /** Getter for property secondKernCharacter.
+ * @return Value of property secondKernCharacter.
+ */
+ public java.lang.String getSecondKernCharacter()
+ {
+ return secondKernCharacter;
+ }
+
+ /** Setter for property secondKernCharacter.
+ * @param secondKernCharacterValue New value of property secondKernCharacter.
+ */
+ public void setSecondKernCharacter(String secondKernCharacterValue)
+ {
+ secondKernCharacter = secondKernCharacterValue;
+ }
+
+ /** Getter for property x.
+ * @return Value of property x.
+ */
+ public float getX()
+ {
+ return x;
+ }
+
+ /** Setter for property x.
+ * @param xValue New value of property x.
+ */
+ public void setX(float xValue)
+ {
+ x = xValue;
+ }
+
+ /** Getter for property y.
+ * @return Value of property y.
+ */
+ public float getY()
+ {
+ return y;
+ }
+
+ /** Setter for property y.
+ * @param yValue New value of property y.
+ */
+ public void setY(float yValue)
+ {
+ y = yValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/Ligature.java b/src/main/java/org/pdfbox/afmtypes/Ligature.java
new file mode 100644
index 0000000..99e6ae9
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/Ligature.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+/**
+ * This class represents a ligature, which is an entry of the CharMetrics.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class Ligature
+{
+ private String successor;
+ private String ligature;
+
+ /** Getter for property ligature.
+ * @return Value of property ligature.
+ */
+ public String getLigature()
+ {
+ return ligature;
+ }
+
+ /** Setter for property ligature.
+ * @param lig New value of property ligature.
+ */
+ public void setLigature(String lig)
+ {
+ ligature = lig;
+ }
+
+ /** Getter for property successor.
+ * @return Value of property successor.
+ */
+ public String getSuccessor()
+ {
+ return successor;
+ }
+
+ /** Setter for property successor.
+ * @param successorValue New value of property successor.
+ */
+ public void setSuccessor(String successorValue)
+ {
+ successor = successorValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/TrackKern.java b/src/main/java/org/pdfbox/afmtypes/TrackKern.java
new file mode 100644
index 0000000..a806850
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/TrackKern.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.afmtypes;
+
+/**
+ * This class represents a piece of track kerning data.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class TrackKern
+{
+ private int degree;
+ private float minPointSize;
+ private float minKern;
+ private float maxPointSize;
+ private float maxKern;
+
+ /** Getter for property degree.
+ * @return Value of property degree.
+ */
+ public int getDegree()
+ {
+ return degree;
+ }
+
+ /** Setter for property degree.
+ * @param degreeValue New value of property degree.
+ */
+ public void setDegree(int degreeValue)
+ {
+ degree = degreeValue;
+ }
+
+ /** Getter for property maxKern.
+ * @return Value of property maxKern.
+ */
+ public float getMaxKern()
+ {
+ return maxKern;
+ }
+
+ /** Setter for property maxKern.
+ * @param maxKernValue New value of property maxKern.
+ */
+ public void setMaxKern(float maxKernValue)
+ {
+ maxKern = maxKernValue;
+ }
+
+ /** Getter for property maxPointSize.
+ * @return Value of property maxPointSize.
+ */
+ public float getMaxPointSize()
+ {
+ return maxPointSize;
+ }
+
+ /** Setter for property maxPointSize.
+ * @param maxPointSizeValue New value of property maxPointSize.
+ */
+ public void setMaxPointSize(float maxPointSizeValue)
+ {
+ maxPointSize = maxPointSizeValue;
+ }
+
+ /** Getter for property minKern.
+ * @return Value of property minKern.
+ */
+ public float getMinKern()
+ {
+ return minKern;
+ }
+
+ /** Setter for property minKern.
+ * @param minKernValue New value of property minKern.
+ */
+ public void setMinKern(float minKernValue)
+ {
+ minKern = minKernValue;
+ }
+
+ /** Getter for property minPointSize.
+ * @return Value of property minPointSize.
+ */
+ public float getMinPointSize()
+ {
+ return minPointSize;
+ }
+
+ /** Setter for property minPointSize.
+ * @param minPointSizeValue New value of property minPointSize.
+ */
+ public void setMinPointSize(float minPointSizeValue)
+ {
+ minPointSize = minPointSizeValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/afmtypes/package.html b/src/main/java/org/pdfbox/afmtypes/package.html
new file mode 100644
index 0000000..e565720
--- /dev/null
+++ b/src/main/java/org/pdfbox/afmtypes/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains all the base types that can be found in an AFM file.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/ant/PDFToTextTask.java b/src/main/java/org/pdfbox/ant/PDFToTextTask.java
new file mode 100644
index 0000000..56ef42b
--- /dev/null
+++ b/src/main/java/org/pdfbox/ant/PDFToTextTask.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ant;
+
+import java.io.File;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * This is an ant task that will allow pdf documents to be converted using an
+ * and task.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class PDFToTextTask extends Task
+{
+ private List fileSets = new ArrayList();
+
+ /**
+ * Adds a set of files (nested fileset attribute).
+ *
+ * @param set Another fileset to add.
+ */
+ public void addFileset( FileSet set )
+ {
+ fileSets.add( set );
+ }
+
+ /**
+ * This will perform the execution.
+ */
+ public void execute()
+ {
+ log( "PDFToTextTask executing" );
+ Iterator fileSetIter = fileSets.iterator();
+ while( fileSetIter.hasNext() )
+ {
+ FileSet next = (FileSet)fileSetIter.next();
+ DirectoryScanner dirScanner = next.getDirectoryScanner( getProject() );
+ dirScanner.scan();
+ String[] files = dirScanner.getIncludedFiles();
+ for( int i=0; i<files.length; i++ )
+ {
+ File f = new File( dirScanner.getBasedir(), files[i] );
+ log( "processing: " + f.getAbsolutePath() );
+ String pdfFile = f.getAbsolutePath();
+ if( pdfFile.toUpperCase().endsWith( ".PDF" ) )
+ {
+ String textFile = pdfFile.substring( 0, pdfFile.length() -3 );
+ textFile = textFile + "txt";
+ try
+ {
+ org.pdfbox.ExtractText.main( new String[] { pdfFile, textFile } );
+ }
+ catch( Exception e )
+ {
+ log( "Error processing " + pdfFile + e.getMessage() );
+ }
+ }
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ant/package.html b/src/main/java/org/pdfbox/ant/package.html
new file mode 100644
index 0000000..675014e
--- /dev/null
+++ b/src/main/java/org/pdfbox/ant/package.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+<A href="http://jakarta.apache.org/ant/index.html">ANT</a> tasks that utilize PDFBox features can be found in this package.
+This is an example of using the PDF2Text task:<br/> <br/>
+
+&lt;taskdef name="pdf2text" classname="org.pdfbox.ant.PDFToTextTask" classpathref="build.classpath" /&gt;<br/>
+
+&lt;pdf2text&gt;<br/>
+ &nbsp;&nbsp;&lt;fileset dir="test"&gt;<br/>
+ &nbsp;&nbsp;&nbsp;&nbsp;&lt;include name="**/*.pdf" /&gt;<br/>
+ &nbsp;&nbsp;&lt;/fileset&gt;<br/>
+&lt;/pdf2text&gt;<br/>
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/cmapparser/CMapParser.java b/src/main/java/org/pdfbox/cmapparser/CMapParser.java
new file mode 100644
index 0000000..5434bb7
--- /dev/null
+++ b/src/main/java/org/pdfbox/cmapparser/CMapParser.java
@@ -0,0 +1,285 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cmapparser;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import java.util.List;
+
+import org.pdfbox.cmaptypes.CMap;
+import org.pdfbox.cmaptypes.CodespaceRange;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdfparser.PDFStreamParser;
+
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * This will parser a CMap stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.10 $
+ */
+public class CMapParser
+{
+ private static final String BEGIN_CODESPACE_RANGE = "begincodespacerange";
+ private static final String BEGIN_BASE_FONT_CHAR = "beginbfchar";
+ private static final String BEGIN_BASE_FONT_RANGE = "beginbfrange";
+
+ private InputStream input;
+ private CMap result;
+ private RandomAccessFile file;
+
+ /**
+ * Creates a new instance of CMapParser.
+ *
+ * @param in The input stream to read data from.
+ * @param raf The random access file from the document
+ */
+ public CMapParser( InputStream in, RandomAccessFile raf )
+ {
+ input = in;
+ file = raf;
+ }
+
+ /**
+ * This will get the results of the parsing. parse() must be called first.
+ *
+ * @return The parsed CMap file.
+ */
+ public CMap getResult()
+ {
+ return result;
+ }
+
+ /**
+ * This will parse the stream and create a cmap object.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ public void parse() throws IOException
+ {
+ result = new CMap();
+ PDFStreamParser parser = new PDFStreamParser( input, file );
+ parser.parse();
+ List tokens = parser.getTokens();
+ for( int i=0; i<tokens.size(); i++ )
+ {
+ Object token = tokens.get( i );
+ if( token instanceof PDFOperator )
+ {
+ PDFOperator op = (PDFOperator)token;
+ if( op.getOperation().equals( BEGIN_CODESPACE_RANGE ) )
+ {
+ COSNumber cosCount = (COSNumber)tokens.get( i-1 );
+ for( int j=0; j<cosCount.intValue(); j++ )
+ {
+ i++;
+ COSString startRange = (COSString)tokens.get( i );
+ i++;
+ COSString endRange = (COSString)tokens.get( i );
+ CodespaceRange range = new CodespaceRange();
+ range.setStart( startRange.getBytes() );
+ range.setEnd( endRange.getBytes() );
+ result.addCodespaceRange( range );
+ }
+ }
+ else if( op.getOperation().equals( BEGIN_BASE_FONT_CHAR ) )
+ {
+ COSNumber cosCount = (COSNumber)tokens.get( i-1 );
+ for( int j=0; j<cosCount.intValue(); j++ )
+ {
+ i++;
+ COSString inputCode = (COSString)tokens.get( i );
+ i++;
+ Object nextToken = tokens.get( i );
+ if( nextToken instanceof COSString )
+ {
+ byte[] bytes = ((COSString)nextToken).getBytes();
+ String value = createStringFromBytes( bytes );
+ result.addMapping( inputCode.getBytes(), value );
+ }
+ else if( nextToken instanceof COSName )
+ {
+ result.addMapping( inputCode.getBytes(), ((COSName)nextToken).getName() );
+ }
+ else
+ {
+ throw new IOException( "Error parsing CMap beginbfchar, expected{COSString " +
+ "or COSName} and not " + nextToken );
+ }
+ }
+ }
+ else if( op.getOperation().equals( BEGIN_BASE_FONT_RANGE ) )
+ {
+ COSNumber cosCount = (COSNumber)tokens.get( i-1 );
+
+ for( int j=0; j<cosCount.intValue(); j++ )
+ {
+ i++;
+ COSString startCode = (COSString)tokens.get( i );
+ i++;
+ COSString endCode = (COSString)tokens.get( i );
+ i++;
+ Object nextToken = tokens.get( i );
+ COSArray array = null;
+ if( nextToken instanceof COSArray )
+ {
+ array = (COSArray)nextToken;
+ }
+
+ byte[] startBytes = startCode.getBytes();
+ byte[] endBytes = endCode.getBytes();
+ byte[] tokenBytes = null;
+ if( array == null )
+ {
+ tokenBytes = ((COSString)nextToken).getBytes();
+ }
+ else
+ {
+ tokenBytes = ((COSString)array.getObject( 0 )).getBytes();
+ }
+
+ String value = null;
+
+ int arrayIndex = 0;
+ boolean done = false;
+ while( !done )
+ {
+ if( compare( startBytes, endBytes ) >= 0 )
+ {
+ done = true;
+ }
+ value = createStringFromBytes( tokenBytes );
+ result.addMapping( startBytes, value );
+ increment( startBytes );
+
+ if( array == null )
+ {
+ increment( tokenBytes );
+ }
+ else
+ {
+ if( arrayIndex < array.size() )
+ {
+ tokenBytes = ((COSString)array.getObject( arrayIndex++ )).getBytes();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void increment( byte[] data )
+ {
+ increment( data, data.length-1 );
+ }
+
+ private void increment( byte[] data, int position )
+ {
+ if( position > 0 && (data[position]+256)%256 == 255 )
+ {
+ data[position]=0;
+ increment( data, position-1);
+ }
+ else
+ {
+ data[position] = (byte)(data[position]+1);
+ }
+ }
+
+ private String createStringFromBytes( byte[] bytes ) throws IOException
+ {
+ String retval = null;
+ if( bytes.length == 1 )
+ {
+ retval = new String( bytes );
+ }
+ else
+ {
+ retval = new String( bytes, "UTF-16BE" );
+ }
+ return retval;
+ }
+
+ private int compare( byte[] first, byte[] second )
+ {
+ int retval = 1;
+ boolean done = false;
+ for( int i=0; i<first.length && !done; i++ )
+ {
+ if( first[i] == second[i] )
+ {
+ //move to next position
+ }
+ else if( ((first[i]+256)%256) < ((second[i]+256)%256) )
+ {
+ done = true;
+ retval = -1;
+ }
+ else
+ {
+ done = true;
+ retval = 1;
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * A simple class to test parsing of cmap files.
+ *
+ * @param args Some command line arguments.
+ *
+ * @throws Exception If there is an error parsing the file.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 1 )
+ {
+ System.err.println( "usage: java org.pdfbox.cmapparser.CMapParser <CMAP File>" );
+ System.exit( -1 );
+ }
+ CMapParser parser = new CMapParser( new FileInputStream( args[0] ), null );
+ parser.parse();
+ CMap result = parser.getResult();
+ System.out.println( "Result:" + result );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cmapparser/package.html b/src/main/java/org/pdfbox/cmapparser/package.html
new file mode 100644
index 0000000..929bcb2
--- /dev/null
+++ b/src/main/java/org/pdfbox/cmapparser/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package holds classes that are necessary to parse cmap files.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/cmaptypes/CMap.java b/src/main/java/org/pdfbox/cmaptypes/CMap.java
new file mode 100644
index 0000000..a8f7fec
--- /dev/null
+++ b/src/main/java/org/pdfbox/cmaptypes/CMap.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cmaptypes;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class represents a CMap file.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class CMap
+{
+ private List codeSpaceRanges = new ArrayList();
+ private Map singleByteMappings = new HashMap();
+ private Map doubleByteMappings = new HashMap();
+
+ /**
+ * Creates a new instance of CMap.
+ */
+ public CMap()
+ {
+ //default constructor
+ }
+
+ /**
+ * This will tell if this cmap has any one byte mappings.
+ *
+ * @return true If there are any one byte mappings, false otherwise.
+ */
+ public boolean hasOneByteMappings()
+ {
+ return singleByteMappings.size() > 0;
+ }
+
+ /**
+ * This will tell if this cmap has any two byte mappings.
+ *
+ * @return true If there are any two byte mappings, false otherwise.
+ */
+ public boolean hasTwoByteMappings()
+ {
+ return doubleByteMappings.size() > 0;
+ }
+
+ /**
+ * This will perform a lookup into the map.
+ *
+ * @param code The code used to lookup.
+ * @param offset The offset into the byte array.
+ * @param length The length of the data we are getting.
+ *
+ * @return The string that matches the lookup.
+ */
+ public String lookup( byte[] code, int offset, int length )
+ {
+
+ String result = null;
+ Integer key = null;
+ if( length == 1 )
+ {
+
+ key = new Integer( (code[offset]+256)%256 );
+ result = (String)singleByteMappings.get( key );
+ }
+ else if( length == 2 )
+ {
+ int intKey = (code[offset]+256)%256;
+ intKey <<= 8;
+ intKey += (code[offset+1]+256)%256;
+ key = new Integer( intKey );
+
+ result = (String)doubleByteMappings.get( key );
+ }
+
+ return result;
+ }
+
+ /**
+ * This will add a mapping.
+ *
+ * @param src The src to the mapping.
+ * @param dest The dest to the mapping.
+ *
+ * @throws IOException if the src is invalid.
+ */
+ public void addMapping( byte[] src, String dest ) throws IOException
+ {
+ if( src.length == 1 )
+ {
+ singleByteMappings.put( new Integer( src[0] ), dest );
+ }
+ else if( src.length == 2 )
+ {
+ int intSrc = src[0];
+ intSrc <<= 8;
+ intSrc |= (src[1]&0xFF);
+ doubleByteMappings.put( new Integer( intSrc ), dest );
+ }
+ else
+ {
+ throw new IOException( "Mapping code should be 1 or two bytes and not " + src.length );
+ }
+ }
+
+
+ /**
+ * This will add a codespace range.
+ *
+ * @param range A single codespace range.
+ */
+ public void addCodespaceRange( CodespaceRange range )
+ {
+ codeSpaceRanges.add( range );
+ }
+
+ /**
+ * Getter for property codeSpaceRanges.
+ *
+ * @return Value of property codeSpaceRanges.
+ */
+ public List getCodeSpaceRanges()
+ {
+ return codeSpaceRanges;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cmaptypes/CodespaceRange.java b/src/main/java/org/pdfbox/cmaptypes/CodespaceRange.java
new file mode 100644
index 0000000..0f1ab2e
--- /dev/null
+++ b/src/main/java/org/pdfbox/cmaptypes/CodespaceRange.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cmaptypes;
+
+/**
+ * This represents a single entry in the codespace range.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class CodespaceRange
+{
+
+ private byte[] start;
+ private byte[] end;
+
+ /**
+ * Creates a new instance of CodespaceRange.
+ */
+ public CodespaceRange()
+ {
+ }
+
+ /** Getter for property end.
+ * @return Value of property end.
+ *
+ */
+ public byte[] getEnd()
+ {
+ return this.end;
+ }
+
+ /** Setter for property end.
+ * @param endBytes New value of property end.
+ *
+ */
+ public void setEnd(byte[] endBytes)
+ {
+ end = endBytes;
+ }
+
+ /** Getter for property start.
+ * @return Value of property start.
+ *
+ */
+ public byte[] getStart()
+ {
+ return this.start;
+ }
+
+ /** Setter for property start.
+ * @param startBytes New value of property start.
+ *
+ */
+ public void setStart(byte[] startBytes)
+ {
+ start = startBytes;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cmaptypes/package.html b/src/main/java/org/pdfbox/cmaptypes/package.html
new file mode 100644
index 0000000..f1bfcac
--- /dev/null
+++ b/src/main/java/org/pdfbox/cmaptypes/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package holds classes that are used to represent cmap files as java objects.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/cos/COSArray.java b/src/main/java/org/pdfbox/cos/COSArray.java
new file mode 100644
index 0000000..cb7d278
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSArray.java
@@ -0,0 +1,492 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * An array of PDFBase objects as part of the PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.22 $
+ */
+public class COSArray extends COSBase
+{
+ private List objects = new ArrayList();
+
+ /**
+ * Constructor.
+ */
+ public COSArray()
+ {
+ //default constructor
+ }
+
+ /**
+ * This will add an object to the array.
+ *
+ * @param object The object to add to the array.
+ */
+ public void add( COSBase object )
+ {
+ objects.add( object );
+ }
+
+ /**
+ * This will add an object to the array.
+ *
+ * @param object The object to add to the array.
+ */
+ public void add( COSObjectable object )
+ {
+ objects.add( object.getCOSObject() );
+ }
+
+ /**
+ * Add the specified object at the ith location and push the rest to the
+ * right.
+ *
+ * @param i The index to add at.
+ * @param object The object to add at that index.
+ */
+ public void add( int i, COSBase object)
+ {
+ objects.add( i, object );
+ }
+
+ /**
+ * This will remove all of the objects in the collection.
+ */
+ public void clear()
+ {
+ objects.clear();
+ }
+
+ /**
+ * This will remove all of the objects in the collection.
+ *
+ * @param objectsList The list of objects to remove from the collection.
+ */
+ public void removeAll( Collection objectsList )
+ {
+ objects.removeAll( objectsList );
+ }
+
+ /**
+ * This will retain all of the objects in the collection.
+ *
+ * @param objectsList The list of objects to retain from the collection.
+ */
+ public void retainAll( Collection objectsList )
+ {
+ objects.retainAll( objectsList );
+ }
+
+ /**
+ * This will add an object to the array.
+ *
+ * @param objectsList The object to add to the array.
+ */
+ public void addAll( Collection objectsList )
+ {
+ objects.addAll( objectsList );
+ }
+
+ /**
+ * This will add all objects to this array.
+ *
+ * @param objectList The objects to add.
+ */
+ public void addAll( COSArray objectList )
+ {
+ objects.addAll( objectList.objects );
+ }
+
+ /**
+ * Add the specified object at the ith location and push the rest to the
+ * right.
+ *
+ * @param i The index to add at.
+ * @param objectList The object to add at that index.
+ */
+ public void addAll( int i, Collection objectList )
+ {
+ objects.addAll( i, objectList );
+ }
+
+ /**
+ * This will set an object at a specific index.
+ *
+ * @param index zero based index into array.
+ * @param object The object to set.
+ */
+ public void set( int index, COSBase object )
+ {
+ objects.set( index, object );
+ }
+
+ /**
+ * This will set an object at a specific index.
+ *
+ * @param index zero based index into array.
+ * @param intVal The object to set.
+ */
+ public void set( int index, int intVal )
+ {
+ objects.set( index, new COSInteger( intVal ) );
+ }
+
+ /**
+ * This will set an object at a specific index.
+ *
+ * @param index zero based index into array.
+ * @param object The object to set.
+ */
+ public void set( int index, COSObjectable object )
+ {
+ COSBase base = null;
+ if( object != null )
+ {
+ base = object.getCOSObject();
+ }
+ objects.set( index, base );
+ }
+
+ /**
+ * This will get an object from the array. This will dereference the object.
+ * If the object is COSNull then null will be returned.
+ *
+ * @param index The index into the array to get the object.
+ *
+ * @return The object at the requested index.
+ */
+ public COSBase getObject( int index )
+ {
+ Object obj = objects.get( index );
+ if( obj instanceof COSObject )
+ {
+ obj = ((COSObject)obj).getObject();
+ }
+ if( obj instanceof COSNull )
+ {
+ obj = null;
+ }
+ return (COSBase)obj;
+ }
+
+ /**
+ * This will get an object from the array. This will NOT derefernce
+ * the COS object.
+ *
+ * @param index The index into the array to get the object.
+ *
+ * @return The object at the requested index.
+ */
+ public COSBase get( int index )
+ {
+ return (COSBase)objects.get( index );
+ }
+
+ /**
+ * Get the value of the array as an integer.
+ *
+ * @param index The index into the list.
+ *
+ * @return The value at that index or -1 if it is null.
+ */
+ public int getInt( int index )
+ {
+ return getInt( index, -1 );
+ }
+
+ /**
+ * Get the value of the array as an integer, return the default if it does
+ * not exist.
+ *
+ * @param index The value of the array.
+ * @param defaultValue The value to return if the value is null.
+ * @return The value at the index or the defaultValue.
+ */
+ public int getInt( int index, int defaultValue )
+ {
+ int retval = defaultValue;
+ if( defaultValue < size() )
+ {
+ COSNumber number = (COSNumber)get( index );
+ if( number != null )
+ {
+ retval = number.intValue();
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Set the value in the array as an integer.
+ *
+ * @param index The index into the array.
+ * @param value The value to set.
+ */
+ public void setInt( int index, int value )
+ {
+ set( index, new COSInteger( value ) );
+ }
+
+ /**
+ * Set the value in the array as a name.
+ * @param index The index into the array.
+ * @param name The name to set in the array.
+ */
+ public void setName( int index, String name )
+ {
+ set( index, COSName.getPDFName( name ) );
+ }
+
+ /**
+ * Get the value of the array as a string.
+ *
+ * @param index The index into the array.
+ * @return The name converted to a string or null if it does not exist.
+ */
+ public String getName( int index )
+ {
+ return getName( index, null );
+ }
+
+ /**
+ * Get an entry in the array that is expected to be a COSName.
+ * @param index The index into the array.
+ * @param defaultValue The value to return if it is null.
+ * @return The value at the index or defaultValue if none is found.
+ */
+ public String getName( int index, String defaultValue )
+ {
+ String retval = defaultValue;
+ if( index < size() )
+ {
+ COSName name = (COSName)get( index );
+ if( name != null )
+ {
+ retval = name.getName();
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Set the value in the array as a string.
+ * @param index The index into the array.
+ * @param string The string to set in the array.
+ */
+ public void setString( int index, String string )
+ {
+ set( index, new COSString( string ) );
+ }
+
+ /**
+ * Get the value of the array as a string.
+ *
+ * @param index The index into the array.
+ * @return The string or null if it does not exist.
+ */
+ public String getString( int index )
+ {
+ return getString( index, null );
+ }
+
+ /**
+ * Get an entry in the array that is expected to be a COSName.
+ * @param index The index into the array.
+ * @param defaultValue The value to return if it is null.
+ * @return The value at the index or defaultValue if none is found.
+ */
+ public String getString( int index, String defaultValue )
+ {
+ String retval = defaultValue;
+ if( index < size() )
+ {
+ COSString string = (COSString)get( index );
+ if( string != null )
+ {
+ retval = string.getString();
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the size of this array.
+ *
+ * @return The number of elements in the array.
+ */
+ public int size()
+ {
+ return objects.size();
+ }
+
+ /**
+ * This will remove an element from the array.
+ *
+ * @param i The index of the object to remove.
+ *
+ * @return The object that was removed.
+ */
+ public COSBase remove( int i )
+ {
+ return (COSBase)objects.remove( i );
+ }
+
+ /**
+ * This will remove an element from the array.
+ *
+ * @param o The object to remove.
+ *
+ * @return The object that was removed.
+ */
+ public boolean remove( COSBase o )
+ {
+ return objects.remove( o );
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "COSArray{" + objects + "}";
+ }
+
+ /**
+ * Get access to the list.
+ *
+ * @return an iterator over the array elements
+ */
+ public Iterator iterator()
+ {
+ return objects.iterator();
+ }
+
+ /**
+ * This will return the index of the entry or -1 if it is not found.
+ *
+ * @param object The object to search for.
+ * @return The index of the object or -1.
+ */
+ public int indexOf( COSBase object )
+ {
+ int retval = -1;
+ for( int i=0; retval < 0 && i<size(); i++ )
+ {
+ if( get( i ).equals( object ) )
+ {
+ retval = i;
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will add null values until the size of the array is at least
+ * as large as the parameter. If the array is already larger than the
+ * parameter then nothing is done.
+ *
+ * @param size The desired size of the array.
+ */
+ public void growToSize( int size )
+ {
+ growToSize( size, null );
+ }
+
+ /**
+ * This will add the object until the size of the array is at least
+ * as large as the parameter. If the array is already larger than the
+ * parameter then nothing is done.
+ *
+ * @param size The desired size of the array.
+ * @param object The object to fill the array with.
+ */
+ public void growToSize( int size, COSBase object )
+ {
+ while( size() < size )
+ {
+ add( object );
+ }
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromArray(this);
+ }
+
+ /**
+ * This will take an COSArray of numbers and convert it to a float[].
+ *
+ * @return This COSArray as an array of float numbers.
+ */
+ public float[] toFloatArray()
+ {
+ float[] retval = new float[size()];
+ for( int i=0; i<size(); i++ )
+ {
+ retval[i] = ((COSNumber)getObject( i )).floatValue();
+ }
+ return retval;
+ }
+
+ /**
+ * Clear the current contents of the COSArray and set it with the float[].
+ *
+ * @param value The new value of the float array.
+ */
+ public void setFloatArray( float[] value )
+ {
+ this.clear();
+ for( int i=0; i<value.length; i++ )
+ {
+ add( new COSFloat( value[i] ) );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSBase.java b/src/main/java/org/pdfbox/cos/COSBase.java
new file mode 100644
index 0000000..85e0c45
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSBase.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import org.pdfbox.filter.FilterManager;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * The base object that all objects in the PDF document will extend.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.13 $
+ */
+public abstract class COSBase implements COSObjectable
+{
+ /**
+ * Constructor.
+ */
+ public COSBase()
+ {
+ }
+
+ /**
+ * This will get the filter manager to use to filter streams.
+ *
+ * @return The filter manager.
+ */
+ public FilterManager getFilterManager()
+ {
+ /**
+ * @todo move this to PDFdocument or something better
+ */
+ return new FilterManager();
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return this;
+ }
+
+
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public abstract Object accept(ICOSVisitor visitor) throws COSVisitorException;
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSBoolean.java b/src/main/java/org/pdfbox/cos/COSBoolean.java
new file mode 100644
index 0000000..0fe2d6f
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSBoolean.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This class represents a boolean value in the PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.13 $
+ */
+public class COSBoolean extends COSBase
+{
+ /**
+ * The true boolean token.
+ */
+ public static final byte[] TRUE_BYTES = new byte[]{ 116, 114, 117, 101 }; //"true".getBytes( "ISO-8859-1" );
+ /**
+ * The false boolean token.
+ */
+ public static final byte[] FALSE_BYTES = new byte[]{ 102, 97, 108, 115, 101 }; //"false".getBytes( "ISO-8859-1" );
+
+ /**
+ * The PDF true value.
+ */
+ public static final COSBoolean TRUE = new COSBoolean( true );
+
+ /**
+ * The PDF false value.
+ */
+ public static final COSBoolean FALSE = new COSBoolean( false );
+
+ private boolean value;
+
+ /**
+ * Constructor.
+ *
+ * @param aValue The boolean value.
+ */
+ private COSBoolean(boolean aValue )
+ {
+ value = aValue;
+ }
+
+ /**
+ * This will get the value that this object wraps.
+ *
+ * @return The boolean value of this object.
+ */
+ public boolean getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This will get the value that this object wraps.
+ *
+ * @return The boolean value of this object.
+ */
+ public Boolean getValueAsObject()
+ {
+ return (value?Boolean.TRUE:Boolean.FALSE);
+ }
+
+ /**
+ * This will get the boolean value.
+ *
+ * @param value Parameter telling which boolean value to get.
+ *
+ * @return The single boolean instance that matches the parameter.
+ */
+ public static COSBoolean getBoolean( boolean value )
+ {
+ return (value?TRUE:FALSE);
+ }
+
+ /**
+ * This will get the boolean value.
+ *
+ * @param value Parameter telling which boolean value to get.
+ *
+ * @return The single boolean instance that matches the parameter.
+ */
+ public static COSBoolean getBoolean( Boolean value )
+ {
+ return getBoolean( value.booleanValue() );
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromBoolean(this);
+ }
+
+ /**
+ * Return a string representation of this object.
+ *
+ * @return The string value of this object.
+ */
+ public String toString()
+ {
+ return String.valueOf( value );
+ }
+
+ /**
+ * This will write this object out to a PDF stream.
+ *
+ * @param output The stream to write this object out to.
+ *
+ * @throws IOException If an error occurs while writing out this object.
+ */
+ public void writePDF( OutputStream output ) throws IOException
+ {
+ if( value )
+ {
+ output.write( TRUE_BYTES );
+ }
+ else
+ {
+ output.write( FALSE_BYTES );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSDictionary.java b/src/main/java/org/pdfbox/cos/COSDictionary.java
new file mode 100644
index 0000000..eb92433
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSDictionary.java
@@ -0,0 +1,1167 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.Iterator;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.util.DateConverter;
+
+/**
+ * This class represents a dictionary where name/value pairs reside.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.29 $
+ */
+public class COSDictionary extends COSBase
+{
+ private static final String PATH_SEPARATOR = "/";
+
+ /**
+ * These are all of the items in the dictionary.
+ */
+ private Map items = new HashMap();
+
+ /**
+ * Used to store original sequence of keys, for testing.
+ */
+ private List keys = new ArrayList();
+
+ /**
+ * Constructor.
+ */
+ public COSDictionary()
+ {
+ //default constructor
+ }
+
+ /**
+ * Copy Constructor. This will make a shallow copy of this dictionary.
+ *
+ * @param dict The dictionary to copy.
+ */
+ public COSDictionary( COSDictionary dict )
+ {
+ items = new HashMap( dict.items );
+ keys = new ArrayList( dict.keys );
+ }
+
+ /**
+ * This will return the number of elements in this dictionary.
+ *
+ * @return The number of elements in the dictionary.
+ */
+ public int size()
+ {
+ return keys.size();
+ }
+
+ /**
+ * This will clear all items in the map.
+ */
+ public void clear()
+ {
+ items.clear();
+ keys.clear();
+ }
+
+ /**
+ * This will get an object from this dictionary. If the object is a reference then it will
+ * dereference it and get it from the document. If the object is COSNull then
+ * null will be returned.
+ *
+ * @param key The key to the object that we are getting.
+ *
+ * @return The object that matches the key.
+ */
+ public COSBase getDictionaryObject( String key )
+ {
+ return getDictionaryObject( COSName.getPDFName( key ) );
+ }
+
+ /**
+ * This is a special case of getDictionaryObject that takes multiple keys, it will handle
+ * the situation where multiple keys could get the same value, ie if either CS or ColorSpace
+ * is used to get the colorspace.
+ * This will get an object from this dictionary. If the object is a reference then it will
+ * dereference it and get it from the document. If the object is COSNull then
+ * null will be returned.
+ *
+ * @param keyList The list of keys to find a value.
+ *
+ * @return The object that matches the key.
+ */
+ public COSBase getDictionaryObject( String[] keyList )
+ {
+ COSBase retval = null;
+ for( int i=0; i<keyList.length && retval == null; i++ )
+ {
+ retval = getDictionaryObject( COSName.getPDFName( keyList[i] ) );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get an object from this dictionary. If the object is a reference then it will
+ * dereference it and get it from the document. If the object is COSNull then
+ * null will be returned.
+ *
+ * @param key The key to the object that we are getting.
+ *
+ * @return The object that matches the key.
+ */
+ public COSBase getDictionaryObject( COSName key )
+ {
+ COSBase retval = (COSBase)items.get( key );
+ if( retval instanceof COSObject )
+ {
+ retval = ((COSObject)retval).getObject();
+ }
+ if( retval instanceof COSNull )
+ {
+ retval = null;
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an item in the dictionary. If value is null then the result
+ * will be the same as removeItem( key ).
+ *
+ * @param key The key to the dictionary object.
+ * @param value The value to the dictionary object.
+ */
+ public void setItem( COSName key, COSBase value )
+ {
+ if( value == null )
+ {
+ removeItem( key );
+ }
+ else
+ {
+ if (!items.containsKey(key))
+ {
+ // insert only if not already there
+ keys.add(key);
+ }
+ items.put( key, value );
+ }
+ }
+
+ /**
+ * This will set an item in the dictionary. If value is null then the result
+ * will be the same as removeItem( key ).
+ *
+ * @param key The key to the dictionary object.
+ * @param value The value to the dictionary object.
+ */
+ public void setItem( COSName key, COSObjectable value )
+ {
+ COSBase base = null;
+ if( value != null )
+ {
+ base = value.getCOSObject();
+ }
+ setItem( key, base );
+ }
+
+ /**
+ * This will set an item in the dictionary. If value is null then the result
+ * will be the same as removeItem( key ).
+ *
+ * @param key The key to the dictionary object.
+ * @param value The value to the dictionary object.
+ */
+ public void setItem( String key, COSObjectable value )
+ {
+ setItem( COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This will set an item in the dictionary.
+ *
+ * @param key The key to the dictionary object.
+ * @param value The value to the dictionary object.
+ */
+ public void setBoolean( String key, boolean value )
+ {
+ setItem( COSName.getPDFName( key ), COSBoolean.getBoolean( value ) );
+ }
+
+ /**
+ * This will set an item in the dictionary.
+ *
+ * @param key The key to the dictionary object.
+ * @param value The value to the dictionary object.
+ */
+ public void setBoolean( COSName key, boolean value )
+ {
+ setItem( key , COSBoolean.getBoolean( value ) );
+ }
+
+ /**
+ * This will set an item in the dictionary. If value is null then the result
+ * will be the same as removeItem( key ).
+ *
+ * @param key The key to the dictionary object.
+ * @param value The value to the dictionary object.
+ */
+ public void setItem( String key, COSBase value )
+ {
+ setItem( COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSName
+ * object. If it is null then the object will be removed.
+ *
+ * @param key The key to the object,
+ * @param value The string value for the name.
+ */
+ public void setName( String key, String value )
+ {
+ setName( COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSName
+ * object. If it is null then the object will be removed.
+ *
+ * @param key The key to the object,
+ * @param value The string value for the name.
+ */
+ public void setName( COSName key, String value )
+ {
+ COSName name = null;
+ if( value != null )
+ {
+ name = COSName.getPDFName( value );
+ }
+ setItem( key, name );
+ }
+
+ /**
+ * Set the value of a date entry in the dictionary.
+ *
+ * @param key The key to the date value.
+ * @param date The date value.
+ */
+ public void setDate( String key, Calendar date )
+ {
+ setDate( COSName.getPDFName( key ), date );
+ }
+
+ /**
+ * Set the date object.
+ *
+ * @param key The key to the date.
+ * @param date The date to set.
+ */
+ public void setDate( COSName key, Calendar date )
+ {
+ setString( key, DateConverter.toString( date ) );
+ }
+
+ /**
+ * Set the value of a date entry in the dictionary.
+ *
+ * @param embedded The embedded dictionary.
+ * @param key The key to the date value.
+ * @param date The date value.
+ */
+ public void setEmbeddedDate( String embedded, String key, Calendar date )
+ {
+ setEmbeddedDate( embedded, COSName.getPDFName( key ), date );
+ }
+
+ /**
+ * Set the date object.
+ *
+ * @param embedded The embedded dictionary.
+ * @param key The key to the date.
+ * @param date The date to set.
+ */
+ public void setEmbeddedDate( String embedded, COSName key, Calendar date )
+ {
+ COSDictionary dic = (COSDictionary)getDictionaryObject( embedded );
+ if( dic == null && date != null )
+ {
+ dic = new COSDictionary();
+ setItem( embedded, dic );
+ }
+ if( dic != null )
+ {
+ dic.setDate( key, date );
+ }
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSString
+ * object. If it is null then the object will be removed.
+ *
+ * @param key The key to the object,
+ * @param value The string value for the name.
+ */
+ public void setString( String key, String value )
+ {
+ setString( COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSString
+ * object. If it is null then the object will be removed.
+ *
+ * @param key The key to the object,
+ * @param value The string value for the name.
+ */
+ public void setString( COSName key, String value )
+ {
+ COSString name = null;
+ if( value != null )
+ {
+ name = new COSString( value );
+ }
+ setItem( key, name );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSString
+ * object. If it is null then the object will be removed.
+ *
+ * @param embedded The embedded dictionary to set the item in.
+ * @param key The key to the object,
+ * @param value The string value for the name.
+ */
+ public void setEmbeddedString( String embedded, String key, String value )
+ {
+ setEmbeddedString( embedded, COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSString
+ * object. If it is null then the object will be removed.
+ *
+ * @param embedded The embedded dictionary to set the item in.
+ * @param key The key to the object,
+ * @param value The string value for the name.
+ */
+ public void setEmbeddedString( String embedded, COSName key, String value )
+ {
+ COSDictionary dic = (COSDictionary)getDictionaryObject( embedded );
+ if( dic == null && value != null )
+ {
+ dic = new COSDictionary();
+ setItem( embedded, dic );
+ }
+ if( dic != null )
+ {
+ dic.setString( key, value );
+ }
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSInteger
+ * object.
+ *
+ * @param key The key to the object,
+ * @param value The int value for the name.
+ */
+ public void setInt( String key, int value )
+ {
+ setInt( COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSInteger
+ * object.
+ *
+ * @param key The key to the object,
+ * @param value The int value for the name.
+ */
+ public void setInt( COSName key, int value )
+ {
+ COSInteger intVal = null;
+ intVal = new COSInteger(value);
+ setItem( key, intVal );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSInteger
+ * object.
+ *
+ * @param embeddedDictionary The embedded dictionary.
+ * @param key The key to the object,
+ * @param value The int value for the name.
+ */
+ public void setEmbeddedInt( String embeddedDictionary, String key, int value )
+ {
+ setEmbeddedInt( embeddedDictionary, COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSInteger
+ * object.
+ *
+ * @param embeddedDictionary The embedded dictionary.
+ * @param key The key to the object,
+ * @param value The int value for the name.
+ */
+ public void setEmbeddedInt( String embeddedDictionary, COSName key, int value )
+ {
+ COSDictionary embedded = (COSDictionary)getDictionaryObject( embeddedDictionary );
+ if( embedded == null )
+ {
+ embedded = new COSDictionary();
+ setItem( embeddedDictionary, embedded );
+ }
+ embedded.setInt( key, value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSFloat
+ * object.
+ *
+ * @param key The key to the object,
+ * @param value The int value for the name.
+ */
+ public void setFloat( String key, float value )
+ {
+ setFloat( COSName.getPDFName( key ), value );
+ }
+
+ /**
+ * This is a convenience method that will convert the value to a COSFloat
+ * object.
+ *
+ * @param key The key to the object,
+ * @param value The int value for the name.
+ */
+ public void setFloat( COSName key, float value )
+ {
+ COSFloat fltVal = new COSFloat( value );
+ setItem( key, fltVal );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ */
+ public String getNameAsString( String key )
+ {
+ return getNameAsString( COSName.getPDFName( key ) );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ */
+ public String getNameAsString( COSName key )
+ {
+ String retval = null;
+ COSName name = (COSName)getDictionaryObject( key );
+ if( name != null )
+ {
+ retval = name.getName();
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The name converted to a string.
+ */
+ public String getNameAsString( String key, String defaultValue )
+ {
+ return getNameAsString( COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The name converted to a string.
+ */
+ public String getNameAsString( COSName key, String defaultValue )
+ {
+ String retval = getNameAsString( key );
+ if( retval == null )
+ {
+ retval = defaultValue;
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ */
+ public String getString( String key )
+ {
+ return getString( COSName.getPDFName( key ) );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ */
+ public String getString( COSName key )
+ {
+ String retval = null;
+ COSString name = (COSString)getDictionaryObject( key );
+ if( name != null )
+ {
+ retval = name.getString();
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ */
+ public String getString( String key, String defaultValue )
+ {
+ return getString( COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ */
+ public String getString( COSName key, String defaultValue )
+ {
+ String retval = getString( key );
+ if( retval == null )
+ {
+ retval = defaultValue;
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary.
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ */
+ public String getEmbeddedString( String embedded, String key )
+ {
+ return getEmbeddedString( embedded, COSName.getPDFName( key ), null );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary.
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ */
+ public String getEmbeddedString( String embedded, COSName key )
+ {
+ return getEmbeddedString( embedded, key, null );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary.
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ */
+ public String getEmbeddedString( String embedded, String key, String defaultValue )
+ {
+ return getEmbeddedString( embedded, COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary.
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ */
+ public String getEmbeddedString( String embedded, COSName key, String defaultValue )
+ {
+ String retval = defaultValue;
+ COSDictionary dic = (COSDictionary)getDictionaryObject( embedded );
+ if( dic != null )
+ {
+ retval = dic.getString( key, defaultValue );
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getDate( String key ) throws IOException
+ {
+ return getDate( COSName.getPDFName( key ) );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ *
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getDate( COSName key ) throws IOException
+ {
+ COSString date = (COSString)getDictionaryObject( key );
+ return DateConverter.toCalendar( date );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a date. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getDate( String key, Calendar defaultValue ) throws IOException
+ {
+ return getDate( COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a date. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getDate( COSName key, Calendar defaultValue ) throws IOException
+ {
+ Calendar retval = getDate( key );
+ if( retval == null )
+ {
+ retval = defaultValue;
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary to get.
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getEmbeddedDate( String embedded, String key ) throws IOException
+ {
+ return getEmbeddedDate( embedded, COSName.getPDFName( key ), null );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a name and convert it to a string. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary to get.
+ * @param key The key to the item in the dictionary.
+ * @return The name converted to a string.
+ *
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getEmbeddedDate( String embedded, COSName key ) throws IOException
+ {
+ return getEmbeddedDate( embedded, key, null );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a date. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary to get.
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getEmbeddedDate( String embedded, String key, Calendar defaultValue ) throws IOException
+ {
+ return getEmbeddedDate( embedded, COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a date. Null is returned
+ * if the entry does not exist in the dictionary.
+ *
+ * @param embedded The embedded dictionary to get.
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The default value to return.
+ * @return The name converted to a string.
+ * @throws IOException If there is an error converting to a date.
+ */
+ public Calendar getEmbeddedDate( String embedded, COSName key, Calendar defaultValue ) throws IOException
+ {
+ Calendar retval = defaultValue;
+ COSDictionary eDic = (COSDictionary)getDictionaryObject( embedded );
+ if( eDic != null )
+ {
+ retval = eDic.getDate( key, defaultValue );
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a cos boolean and convert it to a primitive boolean.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value returned if the entry is null.
+ *
+ * @return The value converted to a boolean.
+ */
+ public boolean getBoolean( String key, boolean defaultValue )
+ {
+ return getBoolean( COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a COSBoolean and convert it to a primitive boolean.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value returned if the entry is null.
+ *
+ * @return The entry converted to a boolean.
+ */
+ public boolean getBoolean( COSName key, boolean defaultValue )
+ {
+ boolean retval = defaultValue;
+ COSBoolean bool = (COSBoolean)getDictionaryObject( key );
+ if( bool != null )
+ {
+ retval = bool.getValue();
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an int. -1 is returned if there is no value.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The integer value.
+ */
+ public int getInt( String key )
+ {
+ return getInt( COSName.getPDFName( key ) );
+ }
+
+ /**
+ * Get an integer from an embedded dictionary. Useful for 1-1 mappings. default:-1
+ *
+ * @param embeddedDictionary The name of the embedded dictionary.
+ * @param key The key in the embedded dictionary.
+ *
+ * @return The value of the embedded integer.
+ */
+ public int getEmbeddedInt( String embeddedDictionary, String key )
+ {
+ return getEmbeddedInt( embeddedDictionary, COSName.getPDFName( key ) );
+ }
+
+ /**
+ * Get an integer from an embedded dictionary. Useful for 1-1 mappings. default:-1
+ *
+ * @param embeddedDictionary The name of the embedded dictionary.
+ * @param key The key in the embedded dictionary.
+ *
+ * @return The value of the embedded integer.
+ */
+ public int getEmbeddedInt( String embeddedDictionary, COSName key )
+ {
+ return getEmbeddedInt( embeddedDictionary, key, -1 );
+ }
+
+ /**
+ * Get an integer from an embedded dictionary. Useful for 1-1 mappings.
+ *
+ * @param embeddedDictionary The name of the embedded dictionary.
+ * @param key The key in the embedded dictionary.
+ * @param defaultValue The value if there is no embedded dictionary or it does not contain the key.
+ *
+ * @return The value of the embedded integer.
+ */
+ public int getEmbeddedInt( String embeddedDictionary, String key, int defaultValue )
+ {
+ return getEmbeddedInt( embeddedDictionary, COSName.getPDFName( key ), defaultValue );
+ }
+
+
+ /**
+ * Get an integer from an embedded dictionary. Useful for 1-1 mappings.
+ *
+ * @param embeddedDictionary The name of the embedded dictionary.
+ * @param key The key in the embedded dictionary.
+ * @param defaultValue The value if there is no embedded dictionary or it does not contain the key.
+ *
+ * @return The value of the embedded integer.
+ */
+ public int getEmbeddedInt( String embeddedDictionary, COSName key, int defaultValue )
+ {
+ int retval = defaultValue;
+ COSDictionary embedded = (COSDictionary)getDictionaryObject( embeddedDictionary );
+ if( embedded != null )
+ {
+ retval = embedded.getInt( key, defaultValue );
+ }
+ return retval;
+ }
+
+
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an int. -1 is returned if there is no value.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The integer value..
+ */
+ public int getInt( COSName key )
+ {
+ return getInt( key, -1 );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an integer. If the dictionary value is null then the
+ * default Value will be returned.
+ *
+ * @param keyList The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The integer value.
+ */
+ public int getInt( String[] keyList, int defaultValue )
+ {
+ int retval = defaultValue;
+ COSNumber obj = (COSNumber)getDictionaryObject( keyList );
+ if( obj != null )
+ {
+ retval = obj.intValue();
+ }
+ return retval;
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an integer. If the dictionary value is null then the
+ * default Value will be returned.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The integer value.
+ */
+ public int getInt( String key, int defaultValue )
+ {
+ return getInt( new String []{ key }, defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an integer. If the dictionary value is null then the
+ * default Value will be returned.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The integer value.
+ */
+ public int getInt( COSName key, int defaultValue )
+ {
+ return getInt(key.getName(), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an int. -1 is returned if there is no value.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The float value.
+ */
+ public float getFloat( String key )
+ {
+ return getFloat( COSName.getPDFName( key ) );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an float. -1 is returned if there is no value.
+ *
+ * @param key The key to the item in the dictionary.
+ * @return The float value.
+ */
+ public float getFloat( COSName key )
+ {
+ return getFloat( key, -1 );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be a float. If the dictionary value is null then the
+ * default Value will be returned.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The float value.
+ */
+ public float getFloat( String key, float defaultValue )
+ {
+ return getFloat( COSName.getPDFName( key ), defaultValue );
+ }
+
+ /**
+ * This is a convenience method that will get the dictionary object that
+ * is expected to be an float. If the dictionary value is null then the
+ * default Value will be returned.
+ *
+ * @param key The key to the item in the dictionary.
+ * @param defaultValue The value to return if the dictionary item is null.
+ * @return The float value.
+ */
+ public float getFloat( COSName key, float defaultValue )
+ {
+ float retval = defaultValue;
+ COSNumber obj = (COSNumber)getDictionaryObject( key );
+ if( obj != null )
+ {
+ retval = obj.floatValue();
+ }
+ return retval;
+ }
+
+ /**
+ * This will remove an item for the dictionary. This
+ * will do nothing of the object does not exist.
+ *
+ * @param key The key to the item to remove from the dictionary.
+ */
+ public void removeItem( COSName key )
+ {
+ keys.remove( key );
+ items.remove( key );
+ }
+
+ /**
+ * This will do a lookup into the dictionary.
+ *
+ * @param key The key to the object.
+ *
+ * @return The item that matches the key.
+ */
+ public COSBase getItem( COSName key )
+ {
+ return (COSBase)items.get( key );
+ }
+
+
+
+
+
+ /**
+ * This will get the keys for all objects in the dictionary in the sequence that
+ * they were added.
+ *
+ * @return a list of the keys in the sequence of insertion
+ *
+ */
+ public List keyList()
+ {
+ return keys;
+ }
+
+ /**
+ * This will get all of the values for the dictionary.
+ *
+ * @return All the values for the dictionary.
+ */
+ public Collection getValues()
+ {
+ return items.values();
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return The object that the visitor returns.
+ *
+ * @throws COSVisitorException If there is an error visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromDictionary(this);
+ }
+
+ /**
+ * This will add all of the dictionarys keys/values to this dictionary.
+ *
+ * @param dic The dic to get the keys from.
+ */
+ public void addAll( COSDictionary dic )
+ {
+ Iterator dicKeys = dic.keyList().iterator();
+ while( dicKeys.hasNext() )
+ {
+ COSName key = (COSName)dicKeys.next();
+ COSBase value = dic.getItem( key );
+ setItem( key, value );
+ }
+ }
+
+ /**
+ * This will add all of the dictionarys keys/values to this dictionary, but only
+ * if they don't already exist. If a key already exists in this dictionary then
+ * nothing is changed.
+ *
+ * @param dic The dic to get the keys from.
+ */
+ public void mergeInto( COSDictionary dic )
+ {
+ Iterator dicKeys = dic.keyList().iterator();
+ while( dicKeys.hasNext() )
+ {
+ COSName key = (COSName)dicKeys.next();
+ COSBase value = dic.getItem( key );
+ if( getItem( key ) == null )
+ {
+ setItem( key, value );
+ }
+ }
+ }
+
+ /**
+ * Nice method, gives you every object you want
+ * Arrays works properly too. Try "P/Annots/[k]/Rect"
+ * where k means the index of the Annotsarray.
+ *
+ * @param objPath the relative path to the object.
+ * @return the object
+ */
+ public COSBase getObjectFromPath(String objPath)
+ {
+ COSBase retval = null;
+ String[] path = objPath.split(PATH_SEPARATOR);
+ retval = this;
+
+ for (int i = 0; i < path.length; i++)
+ {
+ if(retval instanceof COSArray)
+ {
+ int idx = new Integer(path[i].replaceAll("\\[","").replaceAll("\\]","")).intValue();
+ retval = ((COSArray)retval).getObject(idx);
+ }
+ else if (retval instanceof COSDictionary)
+ {
+ retval = ((COSDictionary)retval).getDictionaryObject( path[i] );
+ }
+ }
+ return retval;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSDocument.java b/src/main/java/org/pdfbox/cos/COSDocument.java
new file mode 100644
index 0000000..51ae684
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSDocument.java
@@ -0,0 +1,518 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdfparser.PDFObjectStreamParser;
+import org.pdfbox.persistence.util.COSObjectKey;
+
+/**
+ * This is the in-memory representation of the PDF document. You need to call
+ * close() on this object when you are done using it!!
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.26 $
+ */
+public class COSDocument extends COSBase
+{
+ private float version;
+
+ /**
+ * added objects (actually preserving original sequence).
+ */
+ private List objects = new ArrayList();
+
+ /**
+ * a pool of objects read/referenced so far
+ * used to resolve indirect object references.
+ */
+ private Map objectPool = new HashMap();
+
+ /**
+ * Document trailer dictionary.
+ */
+ private COSDictionary trailer;
+
+ /**
+ * This file will store the streams in order to conserve memory.
+ */
+ private RandomAccessFile scratchFile = null;
+ private File tmpFile = null;
+
+ private String headerString = "%PDF-1.4";
+
+ /**
+ * Constructor. Uses the java.io.tmpdir value to create a file
+ * to store the streams.
+ *
+ * @throws IOException If there is an error creating the tmp file.
+ */
+ public COSDocument() throws IOException
+ {
+ this( new File( System.getProperty( "java.io.tmpdir" ) ) );
+ }
+
+ /**
+ * Constructor that will create a create a scratch file in the
+ * following directory.
+ *
+ * @param scratchDir The directory to store a scratch file.
+ *
+ * @throws IOException If there is an error creating the tmp file.
+ */
+ public COSDocument( File scratchDir ) throws IOException
+ {
+ tmpFile = File.createTempFile( "pdfbox", "tmp", scratchDir );
+ scratchFile = new RandomAccessFile( tmpFile, "rw" );
+ }
+
+ /**
+ * Constructor that will use the following random access file for storage
+ * of the PDF streams. The client of this method is responsible for deleting
+ * the storage if necessary that this file will write to. The close method
+ * will close the file though.
+ *
+ * @param file The random access file to use for storage.
+ */
+ public COSDocument( RandomAccessFile file )
+ {
+ scratchFile = file;
+ }
+
+ /**
+ * This will get the scratch file for this document.
+ *
+ * @return The scratch file.
+ */
+ public RandomAccessFile getScratchFile()
+ {
+ return scratchFile;
+ }
+
+ /**
+ * This will get the first dictionary object by type.
+ *
+ * @param type The type of the object.
+ *
+ * @return This will return an object with the specified type.
+ */
+ public COSObject getObjectByType( String type )
+ {
+ return getObjectByType( COSName.getPDFName( type ) );
+ }
+
+ /**
+ * This will get the first dictionary object by type.
+ *
+ * @param type The type of the object.
+ *
+ * @return This will return an object with the specified type.
+ */
+ public COSObject getObjectByType( COSName type )
+ {
+ COSObject retval = null;
+ Iterator iter = objects.iterator();
+ while( iter.hasNext() && retval == null)
+ {
+ COSObject object = (COSObject)iter.next();
+
+ COSBase realObject = object.getObject();
+ if( realObject instanceof COSDictionary )
+ {
+ COSDictionary dic = (COSDictionary)realObject;
+ COSName objectType = (COSName)dic.getItem( COSName.TYPE );
+ if( objectType != null && objectType.equals( type ) )
+ {
+ retval = object;
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will get all dictionary objects by type.
+ *
+ * @param type The type of the object.
+ *
+ * @return This will return an object with the specified type.
+ */
+ public List getObjectsByType( String type )
+ {
+ return getObjectsByType( COSName.getPDFName( type ) );
+ }
+
+ /**
+ * This will get a dictionary object by type.
+ *
+ * @param type The type of the object.
+ *
+ * @return This will return an object with the specified type.
+ */
+ public List getObjectsByType( COSName type )
+ {
+ List retval = new ArrayList();
+ Iterator iter = objects.iterator();
+ while( iter.hasNext() )
+ {
+ COSObject object = (COSObject)iter.next();
+
+ COSBase realObject = object.getObject();
+ if( realObject instanceof COSStream )
+ {
+ realObject = (COSStream)realObject;
+ }
+ if( realObject instanceof COSDictionary )
+ {
+ COSDictionary dic = (COSDictionary)realObject;
+ COSName objectType = (COSName)dic.getItem( COSName.TYPE );
+ if( objectType != null && objectType.equals( type ) )
+ {
+ retval.add( object );
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will print contents to stdout.
+ */
+ public void print()
+ {
+ Iterator iter = objects.iterator();
+ while( iter.hasNext() )
+ {
+ COSObject object = (COSObject)iter.next();
+ System.out.println( object);
+ }
+ }
+
+ /**
+ * This will set the version of this PDF document.
+ *
+ * @param versionValue The version of the PDF document.
+ */
+ public void setVersion( float versionValue )
+ {
+ version = versionValue;
+ }
+
+ /**
+ * This will get the version of this PDF document.
+ *
+ * @return This documents version.
+ */
+ public float getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * This will tell if this is an encrypted document.
+ *
+ * @return true If this document is encrypted.
+ */
+ public boolean isEncrypted()
+ {
+ boolean encrypted = false;
+ if( trailer != null )
+ {
+ encrypted = trailer.getDictionaryObject( "Encrypt" ) != null;
+ }
+ return encrypted;
+ }
+
+ /**
+ * This will get the encryption dictionary if the document is encrypted or null
+ * if the document is not encrypted.
+ *
+ * @return The encryption dictionary.
+ */
+ public COSDictionary getEncryptionDictionary()
+ {
+ return (COSDictionary)trailer.getDictionaryObject( COSName.getPDFName( "Encrypt" ) );
+ }
+
+ /**
+ * This will set the encryption dictionary, this should only be called when
+ * encypting the document.
+ *
+ * @param encDictionary The encryption dictionary.
+ */
+ public void setEncryptionDictionary( COSDictionary encDictionary )
+ {
+ trailer.setItem( COSName.getPDFName( "Encrypt" ), encDictionary );
+ }
+
+ /**
+ * This will get the document ID.
+ *
+ * @return The document id.
+ */
+ public COSArray getDocumentID()
+ {
+ return (COSArray) getTrailer().getItem(COSName.getPDFName("ID"));
+ }
+
+ /**
+ * This will set the document ID.
+ *
+ * @param id The document id.
+ */
+ public void setDocumentID( COSArray id )
+ {
+ getTrailer().setItem(COSName.getPDFName("ID"), id);
+ }
+
+ /**
+ * This will create an object for this document.
+ *
+ * Create an indirect object out of the direct type and include in the document
+ * for later lookup via document a map from direct object to indirect object
+ * is maintained. this provides better support for manual PDF construction.
+ *
+ * @param base the base object to wrap in an indirect object.
+ *
+ * @return The pdf object that wraps the base, or creates a new one.
+ */
+ /**
+ public COSObject createObject( COSBase base )
+ {
+ COSObject obj = (COSObject)objectMap.get(base);
+ if (obj == null)
+ {
+ obj = new COSObject( base );
+ obj.addTo(this);
+ }
+ return obj;
+ }**/
+
+ /**
+ * This will get the document catalog.
+ *
+ * Maybe this should move to an object at PDFEdit level
+ *
+ * @return catalog is the root of all document activities
+ *
+ * @throws IOException If no catalog can be found.
+ */
+ public COSObject getCatalog() throws IOException
+ {
+ COSObject catalog = (COSObject)getObjectByType( COSName.CATALOG );
+ if( catalog == null )
+ {
+ throw new IOException( "Catalog cannot be found" );
+ }
+ return catalog;
+ }
+
+ /**
+ * This will get a list of all available objects.
+ *
+ * @return A list of all objects.
+ */
+ public List getObjects()
+ {
+ return new ArrayList(objects);
+ }
+
+ /**
+ * This will get the document trailer.
+ *
+ * @return the document trailer dict
+ */
+ public COSDictionary getTrailer()
+ {
+ return trailer;
+ }
+
+ /**
+ * // MIT added, maybe this should not be supported as trailer is a persistence construct.
+ * This will set the document trailer.
+ *
+ * @param newTrailer the document trailer dictionary
+ */
+ public void setTrailer(COSDictionary newTrailer)
+ {
+ trailer = newTrailer;
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromDocument( this );
+ }
+
+ /**
+ * This will close all storage and delete the tmp files.
+ *
+ * @throws IOException If there is an error close resources.
+ */
+ public void close() throws IOException
+ {
+ if( scratchFile != null )
+ {
+ scratchFile.close();
+ scratchFile = null;
+ }
+ if( tmpFile != null )
+ {
+ tmpFile.delete();
+ tmpFile = null;
+ }
+ }
+
+ /**
+ * The sole purpose of this is to inform a client of PDFBox that they
+ * did not close the document.
+ */
+ protected void finalize()
+ {
+ if( tmpFile != null || scratchFile != null )
+ {
+ Throwable t = new Throwable( "Warning: You did not close the PDF Document" );
+ t.printStackTrace();
+ }
+ }
+ /**
+ * @return Returns the headerString.
+ */
+ public String getHeaderString()
+ {
+ return headerString;
+ }
+ /**
+ * @param header The headerString to set.
+ */
+ public void setHeaderString(String header)
+ {
+ headerString = header;
+ }
+
+ /**
+ * This method will search the list of objects for types of ObjStm. If it finds
+ * them then it will parse out all of the objects from the stream that is contains.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ public void dereferenceObjectStreams() throws IOException
+ {
+ Iterator objStm = getObjectsByType( "ObjStm" ).iterator();
+ while( objStm.hasNext() )
+ {
+ COSObject objStream = (COSObject)objStm.next();
+ COSStream stream = (COSStream)objStream.getObject();
+ PDFObjectStreamParser parser = new PDFObjectStreamParser( stream, this );
+ parser.parse();
+ Iterator compressedObjects = parser.getObjects().iterator();
+ while( compressedObjects.hasNext() )
+ {
+ COSObject next = (COSObject)compressedObjects.next();
+ COSObjectKey key = new COSObjectKey( next );
+ COSObject obj = getObjectFromPool( key );
+ obj.setObject( next.getObject() );
+ }
+ }
+ }
+
+ /**
+ * This will add an object to this document.
+ * the method checks if obj is already present as there may be cyclic dependencies
+ *
+ * @param obj The object to add to the document.
+ * @return The object that was actually added to this document, if an object reference already
+ * existed then that will be returned.
+ *
+ * @throws IOException If there is an error adding the object.
+ */
+ public COSObject addObject(COSObject obj) throws IOException
+ {
+ COSObjectKey key = null;
+ if( obj.getObjectNumber() != null )
+ {
+ key = new COSObjectKey( obj );
+ }
+ COSObject fromPool = getObjectFromPool( key );
+ fromPool.setObject( obj.getObject() );
+ return fromPool;
+ }
+
+ /**
+ * This will get an object from the pool.
+ *
+ * @param key The object key.
+ *
+ * @return The object in the pool or a new one if it has not been parsed yet.
+ *
+ * @throws IOException If there is an error getting the proxy object.
+ */
+ public COSObject getObjectFromPool(COSObjectKey key) throws IOException
+ {
+ COSObject obj = null;
+ if( key != null )
+ {
+ obj = (COSObject) objectPool.get(key);
+ }
+ if (obj == null)
+ {
+ // this was a forward reference, make "proxy" object
+ obj = new COSObject(null);
+ if( key != null )
+ {
+ obj.setObjectNumber( new COSInteger( key.getNumber() ) );
+ obj.setGenerationNumber( new COSInteger( key.getGeneration() ) );
+ objectPool.put(key, obj);
+ }
+ objects.add( obj );
+ }
+
+ return obj;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSFloat.java b/src/main/java/org/pdfbox/cos/COSFloat.java
new file mode 100644
index 0000000..eab0180
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSFloat.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This class represents a floating point number in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.15 $
+ */
+public class COSFloat extends COSNumber
+{
+ private float value;
+
+ /**
+ * Constructor.
+ *
+ * @param aFloat The primitive float object that this object wraps.
+ */
+ public COSFloat( float aFloat )
+ {
+ value = aFloat;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param aFloat The primitive float object that this object wraps.
+ *
+ * @throws IOException If aFloat is not a float.
+ */
+ public COSFloat( String aFloat ) throws IOException
+ {
+ try
+ {
+ value = Float.parseFloat( aFloat );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error expected floating point number actual='" +aFloat + "'" );
+ }
+ }
+
+ /**
+ * The value of the float object that this one wraps.
+ *
+ * @return The value of this object.
+ */
+ public float floatValue()
+ {
+ return value;
+ }
+
+ /**
+ * The value of the double object that this one wraps.
+ *
+ * @return The double of this object.
+ */
+ public double doubleValue()
+ {
+ return value;
+ }
+
+ /**
+ * This will get the integer value of this object.
+ *
+ * @return The int value of this object,
+ */
+ public long longValue()
+ {
+ return (long)value;
+ }
+
+ /**
+ * This will get the integer value of this object.
+ *
+ * @return The int value of this object,
+ */
+ public int intValue()
+ {
+ return (int)value;
+ }
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals( Object o )
+ {
+ return o instanceof COSFloat && Float.floatToIntBits(((COSFloat)o).value) == Float.floatToIntBits(value);
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return Float.floatToIntBits(value);
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "COSFloat{" + value + "}";
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromFloat(this);
+ }
+
+ /**
+ * This will output this string as a PDF object.
+ *
+ * @param output The stream to write to.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writePDF( OutputStream output ) throws IOException
+ {
+ DecimalFormat formatDecimal = (DecimalFormat)NumberFormat.getNumberInstance();
+ formatDecimal.setMaximumFractionDigits( 10 );
+ formatDecimal.setGroupingUsed( false );
+ DecimalFormatSymbols symbols = formatDecimal.getDecimalFormatSymbols();
+ symbols.setDecimalSeparator( '.' );
+ formatDecimal.setDecimalFormatSymbols( symbols );
+ output.write(formatDecimal.format( value ).getBytes());
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSInteger.java b/src/main/java/org/pdfbox/cos/COSInteger.java
new file mode 100644
index 0000000..0a523bd
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSInteger.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ *
+ * This class represents an integer number in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.11 $
+ */
+public class COSInteger extends COSNumber
+{
+
+ private long value;
+
+ /**
+ * constructor.
+ *
+ * @param val The integer value of this object.
+ */
+ public COSInteger( long val )
+ {
+ value = val;
+ }
+
+ /**
+ * constructor.
+ *
+ * @param val The integer value of this object.
+ */
+ public COSInteger( int val )
+ {
+ this( (long)val );
+ }
+
+ /**
+ * This will create a new PDF Int object using a string.
+ *
+ * @param val The string value of the integer.
+ *
+ * @throws IOException If the val is not an integer type.
+ */
+ public COSInteger( String val ) throws IOException
+ {
+ try
+ {
+ value = Long.parseLong( val );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error: value is not an integer type actual='" + val + "'" );
+ }
+ }
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals(Object o)
+ {
+ return o instanceof COSInteger && ((COSInteger)o).intValue() == intValue();
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ //taken from java.lang.Long
+ return (int)(value ^ (value >> 32));
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "COSInt{" + value + "}";
+ }
+
+ /**
+ * Change the value of this reference.
+ *
+ * @param newValue The new value.
+ */
+ public void setValue( long newValue )
+ {
+ value = newValue;
+ }
+
+
+
+ /**
+ * polymorphic access to value as float.
+ *
+ * @return The float value of this object.
+ */
+ public float floatValue()
+ {
+ return (float)value;
+ }
+
+ /**
+ * polymorphic access to value as float.
+ *
+ * @return The double value of this object.
+ */
+ public double doubleValue()
+ {
+ return (double)value;
+ }
+
+ /**
+ * Polymorphic access to value as int
+ * This will get the integer value of this object.
+ *
+ * @return The int value of this object,
+ */
+ public int intValue()
+ {
+ return (int)value;
+ }
+
+ /**
+ * Polymorphic access to value as int
+ * This will get the integer value of this object.
+ *
+ * @return The int value of this object,
+ */
+ public long longValue()
+ {
+ return value;
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromInt(this);
+ }
+
+ /**
+ * This will output this string as a PDF object.
+ *
+ * @param output The stream to write to.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writePDF( OutputStream output ) throws IOException
+ {
+ output.write(String.valueOf(value).getBytes());
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSName.java b/src/main/java/org/pdfbox/cos/COSName.java
new file mode 100644
index 0000000..8532cc4
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSName.java
@@ -0,0 +1,572 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.persistence.util.COSHEXTable;
+
+
+/**
+ * This class represents a PDF named object.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.38 $
+ */
+public final class COSName extends COSBase implements Comparable
+{
+ /**
+ * Note: This is synchronized because a HashMap must be synchronized if accessed by
+ * multiple threads.
+ */
+ private static Map nameMap = Collections.synchronizedMap( new HashMap(8192) );
+
+
+ /**
+ * A common COSName value.
+ */
+ public static final COSName AA = new COSName( "AA" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ACRO_FORM = new COSName( "AcroForm" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ANNOTS = new COSName( "Annots" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ART_BOX = new COSName("ArtBox" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ASCII85_DECODE = new COSName( "ASCII85Decode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ASCII85_DECODE_ABBREVIATION = new COSName( "A85" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ASCII_HEX_DECODE = new COSName( "ASCIIHexDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ASCII_HEX_DECODE_ABBREVIATION = new COSName( "AHx" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName AP = new COSName( "AP" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName B = new COSName( "B" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName BASE_ENCODING = new COSName( "BaseEncoding" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName BASE_FONT = new COSName( "BaseFont" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName BBOX = new COSName( "BBox" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName BLEED_BOX = new COSName("BleedBox" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CATALOG = new COSName( "Catalog" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CALGRAY = new COSName( "CalGray" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CALRGB = new COSName( "CalRGB" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CCITTFAX_DECODE = new COSName( "CCITTFaxDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CCITTFAX_DECODE_ABBREVIATION = new COSName( "CCF" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName COLORSPACE = new COSName( "ColorSpace" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CONTENTS = new COSName( "Contents" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName COUNT = new COSName( "Count" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName CROP_BOX = new COSName( "CropBox" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DESCENDANT_FONTS = new COSName( "DescendantFonts" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DIFFERENCES = new COSName( "Differences" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DCT_DECODE = new COSName( "DCTDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DCT_DECODE_ABBREVIATION = new COSName( "DCT" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DEVICECMYK = new COSName( "DeviceCMYK" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DEVICEGRAY = new COSName( "DeviceGray" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DEVICEN = new COSName( "DeviceN" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DEVICERGB = new COSName( "DeviceRGB" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName DV = new COSName( "DV" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ENCODING = new COSName( "Encoding" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FIELDS = new COSName( "Fields" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FILTER = new COSName( "Filter" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FIRST_CHAR = new COSName( "FirstChar" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FLATE_DECODE = new COSName( "FlateDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FLATE_DECODE_ABBREVIATION = new COSName( "Fl" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FONT = new COSName( "Font" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FONT_FILE = new COSName("FontFile");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FONT_FILE2 = new COSName("FontFile2");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FONT_FILE3 = new COSName("FontFile3");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FONT_DESC = new COSName("FontDescriptor");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FONT_MATRIX = new COSName("FontMatrix" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FORMTYPE = new COSName( "FormType" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName FRM = new COSName( "FRM" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName HEIGHT = new COSName( "Height" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ICCBASED = new COSName( "ICCBased" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName IDENTITY_H = new COSName( "Identity-H" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName IMAGE = new COSName( "Image" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName INDEXED = new COSName( "Indexed" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName INFO = new COSName( "Info" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName JPX_DECODE = new COSName( "JPXDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName KIDS = new COSName( "Kids" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LAB = new COSName( "Lab" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LAST_CHAR = new COSName( "LastChar" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LENGTH = new COSName( "Length" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LENGTH1 = new COSName( "Length1" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LZW_DECODE = new COSName( "LZWDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LZW_DECODE_ABBREVIATION = new COSName( "LZW" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName MAC_ROMAN_ENCODING = new COSName( "MacRomanEncoding" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName MATRIX = new COSName( "Matrix" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName MEDIA_BOX = new COSName( "MediaBox" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName METADATA = new COSName( "Metadata" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName N = new COSName( "N" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName NAME = new COSName( "Name" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName P = new COSName( "P" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName PAGE = new COSName( "Page" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName PAGES = new COSName( "Pages" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName PARENT = new COSName( "Parent" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName PATTERN = new COSName( "Pattern" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName PDF_DOC_ENCODING = new COSName( "PDFDocEncoding" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName PREV = new COSName( "Prev" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName R = new COSName( "R" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName RESOURCES = new COSName( "Resources" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ROOT = new COSName( "Root" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName ROTATE = new COSName( "Rotate" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName RUN_LENGTH_DECODE = new COSName( "RunLengthDecode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName RUN_LENGTH_DECODE_ABBREVIATION = new COSName( "RL" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName SEPARATION = new COSName( "Separation" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName STANDARD_ENCODING = new COSName( "StandardEncoding" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName SUBTYPE = new COSName( "Subtype" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName TRIM_BOX = new COSName("TrimBox" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName TRUE_TYPE = new COSName("TrueType" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName TO_UNICODE = new COSName( "ToUnicode" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName TYPE = new COSName( "Type" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName TYPE0 = new COSName( "Type0" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName V = new COSName( "V" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName VERSION = new COSName( "Version" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName WIDTHS = new COSName( "Widths" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName WIN_ANSI_ENCODING = new COSName( "WinAnsiEncoding" );
+ /**
+ * A common COSName value.
+ */
+ public static final COSName XOBJECT = new COSName( "XObject" );
+
+ /**
+ * The prefix to a PDF name.
+ */
+ public static final byte[] NAME_PREFIX = new byte[] { 47 }; // The / character
+ /**
+ * The escape character for a name.
+ */
+ public static final byte[] NAME_ESCAPE = new byte[] { 35 }; //The # character
+
+ private String name;
+ private int hashCode;
+
+ /**
+ * This will get a COSName object with that name.
+ *
+ * @param aName The name of the object.
+ *
+ * @return A COSName with the specified name.
+ */
+ public static final COSName getPDFName( String aName )
+ {
+ COSName name = null;
+ if( aName != null )
+ {
+ name = (COSName)nameMap.get( aName );
+ if( name == null )
+ {
+ //name is added to map in the constructor
+ name = new COSName( aName );
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Private constructor. This will limit the number of COSName objects.
+ * that are created.
+ *
+ * @param aName The name of the COSName object.
+ */
+ private COSName( String aName )
+ {
+ name = aName;
+ nameMap.put( aName, this );
+ hashCode = name.hashCode();
+ }
+
+ /**
+ * This will get the name of this COSName object.
+ *
+ * @return The name of the object.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "COSName{" + name + "}";
+ }
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals( Object o )
+ {
+ boolean retval = this == o;
+ if( !retval && o instanceof COSName )
+ {
+ COSName other = (COSName)o;
+ retval = name == other.name || name.equals( other.name );
+ }
+ return retval;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ /**
+ * @see Comparable#compareTo( Object )
+ */
+ public int compareTo(Object o)
+ {
+ COSName other = (COSName)o;
+ return this.name.compareTo( other.name );
+ }
+
+
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromName(this);
+ }
+
+ /**
+ * This will output this string as a PDF object.
+ *
+ * @param output The stream to write to.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writePDF( OutputStream output ) throws IOException
+ {
+ output.write(NAME_PREFIX);
+ byte[] bytes = getName().getBytes();
+ for (int i = 0; i < bytes.length;i++)
+ {
+ int current = ((bytes[i]+256)%256);
+
+ if(current <= 32 || current >= 127 ||
+ current == '(' ||
+ current == ')' ||
+ current == '[' ||
+ current == ']' ||
+ current == '/' ||
+ current == '%' ||
+ current == '<' ||
+ current == '>' ||
+ current == NAME_ESCAPE[0] )
+ {
+ output.write(NAME_ESCAPE);
+ output.write(COSHEXTable.TABLE[current]);
+ }
+ else
+ {
+ output.write(current);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSNull.java b/src/main/java/org/pdfbox/cos/COSNull.java
new file mode 100644
index 0000000..15356b5
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSNull.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This class represents a null PDF object.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.12 $
+ */
+public class COSNull extends COSBase
+{
+ /**
+ * The null token.
+ */
+ public static final byte[] NULL_BYTES = new byte[] {110, 117, 108, 108}; //"null".getBytes( "ISO-8859-1" );
+
+ /**
+ * The one null object in the system.
+ */
+ public static final COSNull NULL = new COSNull();
+
+ /**
+ * Constructor.
+ */
+ private COSNull()
+ {
+ //limit creation to one instance.
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept( ICOSVisitor visitor ) throws COSVisitorException
+ {
+ return visitor.visitFromNull( this );
+ }
+
+ /**
+ * This will output this string as a PDF object.
+ *
+ * @param output The stream to write to.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writePDF( OutputStream output ) throws IOException
+ {
+ output.write(NULL_BYTES);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSNumber.java b/src/main/java/org/pdfbox/cos/COSNumber.java
new file mode 100644
index 0000000..c629f66
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSNumber.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class represents an abstract number in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.9 $
+ */
+public abstract class COSNumber extends COSBase
+{
+ /**
+ * ZERO.
+ */
+ public static final COSInteger ZERO = new COSInteger( 0 );
+ /**
+ * ONE.
+ */
+ public static final COSInteger ONE = new COSInteger( 1 );
+ private static final Map COMMON_NUMBERS = new HashMap();
+
+ static
+ {
+ COMMON_NUMBERS.put( "0", ZERO );
+ COMMON_NUMBERS.put( "1", ONE );
+ }
+
+ /**
+ * This will get the float value of this number.
+ *
+ * @return The float value of this object.
+ */
+ public abstract float floatValue();
+
+ /**
+ * This will get the double value of this number.
+ *
+ * @return The double value of this number.
+ */
+ public abstract double doubleValue();
+
+ /**
+ * This will get the integer value of this number.
+ *
+ * @return The integer value of this number.
+ */
+ public abstract int intValue();
+
+ /**
+ * This will get the long value of this number.
+ *
+ * @return The long value of this number.
+ */
+ public abstract long longValue();
+
+ /**
+ * This factory method will get the appropriate number object.
+ *
+ * @param number The string representation of the number.
+ *
+ * @return A number object, either float or int.
+ *
+ * @throws IOException If the string is not a number.
+ */
+ public static COSNumber get( String number ) throws IOException
+ {
+ COSNumber result = (COSNumber)COMMON_NUMBERS.get( number );
+ if( result == null )
+ {
+ if (number.indexOf('.') >= 0)
+ {
+ result = new COSFloat( number );
+ }
+ else
+ {
+ result = new COSInteger( number );
+ }
+ }
+ return result;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSObject.java b/src/main/java/org/pdfbox/cos/COSObject.java
new file mode 100644
index 0000000..28f2316
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSObject.java
@@ -0,0 +1,226 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class represents a PDF object.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.35 $
+ */
+public class COSObject extends COSBase
+{
+ private static Logger log = Logger.getLogger(COSObject.class);
+
+ private COSBase baseObject;
+ private COSInteger objectNumber;
+ private COSInteger generationNumber;
+
+ /**
+ * Constructor.
+ *
+ * @param object The object that this encapsulates.
+ *
+ * @throws IOException If there is an error with the object passed in.
+ */
+ public COSObject( COSBase object ) throws IOException
+ {
+ setObject( object );
+ }
+
+ /**
+ * This will get the dictionary object in this object that has the name key and
+ * if it is a pdfobjref then it will dereference that and return it.
+ *
+ * @param key The key to the value that we are searching for.
+ *
+ * @return The pdf object that matches the key.
+ */
+ public COSBase getDictionaryObject( COSName key )
+ {
+ COSBase retval =null;
+ if( baseObject instanceof COSDictionary )
+ {
+ retval = ((COSDictionary)baseObject).getDictionaryObject( key );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the dictionary object in this object that has the name key.
+ *
+ * @param key The key to the value that we are searching for.
+ *
+ * @return The pdf object that matches the key.
+ */
+ public COSBase getItem( COSName key )
+ {
+ COSBase retval =null;
+ if( baseObject instanceof COSDictionary )
+ {
+ retval = ((COSDictionary)baseObject).getItem( key );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the object that this object encapsulates.
+ *
+ * @return The encapsulated object.
+ */
+ public COSBase getObject()
+ {
+ return baseObject;
+ }
+
+ /**
+ * This will set the object that this object encapsulates.
+ *
+ * @param object The new object to encapsulate.
+ *
+ * @throws IOException If there is an error setting the updated object.
+ */
+ public void setObject( COSBase object ) throws IOException
+ {
+ baseObject = object;
+ /*if( baseObject == null )
+ {
+ baseObject = object;
+ }
+ else
+ {
+ //This is for when an object appears twice in the
+ //pdf file we really want to replace it such that
+ //object references still work correctly.
+ //see owcp-as-received.pdf for an example
+ if( baseObject instanceof COSDictionary )
+ {
+ COSDictionary dic = (COSDictionary)baseObject;
+ COSDictionary dicObject = (COSDictionary)object;
+ dic.clear();
+ dic.addAll( dicObject );
+ }
+ else if( baseObject instanceof COSArray )
+ {
+ COSArray array = (COSArray)baseObject;
+ COSArray arrObject = (COSArray)object;
+ array.clear();
+ for( int i=0; i<arrObject.size(); i++ )
+ {
+ array.add( arrObject.get( i ) );
+ }
+ }
+ else if( baseObject instanceof COSStream )
+ {
+ COSStream oldStream = (COSStream)baseObject;
+ System.out.println( "object:" + object.getClass().getName() );
+ COSStream newStream = (COSStream)object;
+ oldStream.replaceWithStream( newStream );
+ }
+ else if( baseObject instanceof COSInteger )
+ {
+ COSInteger oldInt = (COSInteger)baseObject;
+ COSInteger newInt = (COSInteger)object;
+ oldInt.setValue( newInt.longValue() );
+ }
+ else if( baseObject == null )
+ {
+ baseObject = object;
+ }
+ else
+ {
+ throw new IOException( "Unknown object substitution type:" + baseObject );
+ }
+ }*/
+
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "COSObject{" +
+ (objectNumber == null ? "unknown" : "" + objectNumber.intValue() ) + ", " +
+ (generationNumber == null ? "unknown" : "" + generationNumber.intValue() ) +
+ "}";
+ }
+
+ /** Getter for property objectNumber.
+ * @return Value of property objectNumber.
+ */
+ public COSInteger getObjectNumber()
+ {
+ return objectNumber;
+ }
+
+ /** Setter for property objectNumber.
+ * @param objectNum New value of property objectNumber.
+ */
+ public void setObjectNumber(COSInteger objectNum)
+ {
+ objectNumber = objectNum;
+ }
+
+ /** Getter for property generationNumber.
+ * @return Value of property generationNumber.
+ */
+ public COSInteger getGenerationNumber()
+ {
+ return generationNumber;
+ }
+
+ /** Setter for property generationNumber.
+ * @param generationNumberValue New value of property generationNumber.
+ */
+ public void setGenerationNumber(COSInteger generationNumberValue)
+ {
+ generationNumber = generationNumberValue;
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept( ICOSVisitor visitor ) throws COSVisitorException
+ {
+ return getObject() != null ? getObject().accept( visitor ) : COSNull.NULL.accept( visitor );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSStream.java b/src/main/java/org/pdfbox/cos/COSStream.java
new file mode 100644
index 0000000..206a854
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSStream.java
@@ -0,0 +1,495 @@
+/**
+ * Copyright (c) 200-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.pdfbox.filter.Filter;
+import org.pdfbox.filter.FilterManager;
+
+import org.pdfbox.pdfparser.PDFStreamParser;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.io.RandomAccessFileInputStream;
+import org.pdfbox.io.RandomAccessFileOutputStream;
+
+/**
+ * This class represents a stream object in a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.36 $
+ */
+public class COSStream extends COSDictionary
+{
+ private static final int BUFFER_SIZE=16384;
+ private static Logger log = Logger.getLogger(COSStream.class);
+
+ private RandomAccessFile file;
+ /**
+ * The stream with all of the filters applied.
+ */
+ private RandomAccessFileOutputStream filteredStream;
+
+ /**
+ * The stream with no filters, this contains the useful data.
+ */
+ private RandomAccessFileOutputStream unFilteredStream;
+
+ /**
+ * Constructor. Creates a new stream with an empty dictionary.
+ *
+ * @param storage The intermediate storage for the stream.
+ */
+ public COSStream( RandomAccessFile storage )
+ {
+ super();
+ file = storage;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dictionary The dictionary that is associated with this stream.
+ * @param storage The intermediate storage for the stream.
+ */
+ public COSStream( COSDictionary dictionary, RandomAccessFile storage )
+ {
+ super( dictionary );
+ file = storage;
+ }
+
+ /**
+ * This will replace this object with the data from the new object. This
+ * is used to easily maintain referential integrity when changing references
+ * to new objects.
+ *
+ * @param stream The stream that have the new values in it.
+ */
+ public void replaceWithStream( COSStream stream )
+ {
+ this.clear();
+ this.addAll( stream );
+ file = stream.file;
+ filteredStream = stream.filteredStream;
+ unFilteredStream = stream.unFilteredStream;
+ }
+
+ /**
+ * This will get the scratch file associated with this stream.
+ *
+ * @return The scratch file where this stream is being stored.
+ */
+ public RandomAccessFile getScratchFile()
+ {
+ return file;
+ }
+
+ /**
+ * This will get all the tokens in the stream.
+ *
+ * @return All of the tokens in the stream.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ public List getStreamTokens() throws IOException
+ {
+ PDFStreamParser parser = new PDFStreamParser( this );
+ parser.parse();
+ return parser.getTokens();
+ }
+
+ /**
+ * This will get the stream with all of the filters applied.
+ *
+ * @return the bytes of the physical (endoced) stream
+ *
+ * @throws IOException when encoding/decoding causes an exception
+ */
+ public InputStream getFilteredStream() throws IOException
+ {
+ if( filteredStream == null )
+ {
+ doEncode();
+ }
+ long position = filteredStream.getPosition();
+ long length = filteredStream.getLength();
+
+ RandomAccessFileInputStream input =
+ new RandomAccessFileInputStream( file, position, length );
+ return new BufferedInputStream( input, BUFFER_SIZE );
+ }
+
+ /**
+ * This will get the logical content stream with none of the filters.
+ *
+ * @return the bytes of the logical (decoded) stream
+ *
+ * @throws IOException when encoding/decoding causes an exception
+ */
+ public InputStream getUnfilteredStream() throws IOException
+ {
+ InputStream retval = null;
+ if( unFilteredStream == null )
+ {
+ doDecode();
+ }
+
+ //if unFilteredStream is still null then this stream has not been
+ //created yet, so we should return null.
+ if( unFilteredStream != null )
+ {
+ long position = unFilteredStream.getPosition();
+ long length = unFilteredStream.getLength();
+ RandomAccessFileInputStream input =
+ new RandomAccessFileInputStream( file, position, length );
+ retval = new BufferedInputStream( input, BUFFER_SIZE );
+ }
+ else
+ {
+ // We should check if the COSStream contains data, maybe it
+ // has been created with a RandomAccessFile - which is not
+ // necessary empty.
+ // In this case, the creation was been done as an input, this should
+ // be the unfiltered file, since no filter has been applied yet.
+// if ( (file != null) &&
+// (file.length() > 0) )
+// {
+// retval = new RandomAccessFileInputStream( file,
+// 0,
+// file.length() );
+// }
+// else
+// {
+ //if there is no stream data then simply return an empty stream.
+ retval = new ByteArrayInputStream( new byte[0] );
+// }
+ }
+ return retval;
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromStream(this);
+ }
+
+ /**
+ * This will decode the physical byte stream applying all of the filters to the stream.
+ *
+ * @throws IOException If there is an error applying a filter to the stream.
+ */
+ private void doDecode() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode() start");
+ }
+// FIXME: We shouldn't keep the same reference?
+ unFilteredStream = filteredStream;
+
+ COSBase filters = getFilters();
+ if( filters == null )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode() - No filter to apply");
+ }
+ }
+ else if( filters instanceof COSName )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode( COSName )");
+ }
+ doDecode( (COSName)filters );
+ }
+ else if( filters instanceof COSArray )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode( COSArray )");
+ }
+ COSArray filterArray = (COSArray)filters;
+ for( int i=0; i<filterArray.size(); i++ )
+ {
+ COSName filterName = (COSName)filterArray.get( i );
+ doDecode( filterName );
+ }
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown filter type:" + filters );
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode() end");
+ }
+ }
+
+ /**
+ * This will decode applying a single filter on the stream.
+ *
+ * @param filterName The name of the filter.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ private void doDecode( COSName filterName ) throws IOException
+ {
+ long start = System.currentTimeMillis();
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode( " + filterName.getName() + " ) dic=" + this +
+ " read.length="+unFilteredStream.getLength() );
+ }
+
+ FilterManager manager = getFilterManager();
+ Filter filter = manager.getFilter( filterName );
+ InputStream input;
+
+ boolean done = false;
+ IOException exception = null;
+ long position = unFilteredStream.getPosition();
+ long length = unFilteredStream.getLength();
+
+ if( length == 0 )
+ {
+ //if the length is zero then don't bother trying to decode
+ //some filters don't work when attempting to decode
+ //with a zero length stream. See zlib_error_01.pdf
+ unFilteredStream = new RandomAccessFileOutputStream( file );
+ done = true;
+ }
+ else
+ {
+ //ok this is a simple hack, sometimes we read a couple extra
+ //bytes that shouldn't be there, so we encounter an error we will just
+ //try again with one less byte.
+ for( int tryCount=0; !done && tryCount<5; tryCount++ )
+ {
+ try
+ {
+ input = new BufferedInputStream(
+ new RandomAccessFileInputStream( file, position, length ), BUFFER_SIZE );
+ unFilteredStream = new RandomAccessFileOutputStream( file );
+ filter.decode( input, unFilteredStream, this );
+ done = true;
+ }
+ catch( IOException io )
+ {
+ length--;
+ exception = io;
+ }
+ }
+ }
+ if( !done )
+ {
+ throw exception;
+ }
+ long stop = System.currentTimeMillis();
+ if( log.isDebugEnabled() )
+ {
+ log.debug("doDecode( " + filterName.getName() + " ) done time=" + (stop-start) );
+ }
+ }
+
+ /**
+ * This will encode the logical byte stream applying all of the filters to the stream.
+ *
+ * @throws IOException If there is an error applying a filter to the stream.
+ */
+ private void doEncode() throws IOException
+ {
+ filteredStream = unFilteredStream;
+
+ COSBase filters = getFilters();
+ if( filters == null )
+ {
+ log.debug( "No filters for stream" );
+ //there is no filter to apply
+ }
+ else if( filters instanceof COSName )
+ {
+ doEncode( (COSName)filters );
+ }
+ else if( filters instanceof COSArray )
+ {
+ // apply filters in reverse order
+ COSArray filterArray = (COSArray)filters;
+ for( int i=filterArray.size()-1; i>=0; i-- )
+ {
+ COSName filterName = (COSName)filterArray.get( i );
+ doEncode( filterName );
+ }
+ }
+ }
+
+ /**
+ * This will encode applying a single filter on the stream.
+ *
+ * @param filterName The name of the filter.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ private void doEncode( COSName filterName ) throws IOException
+ {
+ FilterManager manager = getFilterManager();
+ Filter filter = manager.getFilter( filterName );
+ InputStream input;
+
+ input = new BufferedInputStream(
+ new RandomAccessFileInputStream( file, filteredStream.getPosition(),
+ filteredStream.getLength() ), BUFFER_SIZE );
+ filteredStream = new RandomAccessFileOutputStream( file );
+ filter.encode( input, filteredStream, this );
+ }
+
+ /**
+ * This will return the filters to apply to the byte stream.
+ * The method will return
+ * - null if no filters are to be applied
+ * - a COSName if one filter is to be applied
+ * - a COSArray containing COSNames if multiple filters are to be applied
+ *
+ * @return the COSBase object representing the filters
+ */
+ public COSBase getFilters()
+ {
+ return getDictionaryObject(COSName.FILTER);
+ }
+
+ /**
+ * This will create a new stream for which filtered byte should be
+ * written to. You probably don't want this but want to use the
+ * createUnfilteredStream, which is used to write raw bytes to.
+ *
+ * @return A stream that can be written to.
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public OutputStream createFilteredStream() throws IOException
+ {
+ filteredStream = new RandomAccessFileOutputStream( file );
+ unFilteredStream = null;
+ return new BufferedOutputStream( filteredStream, BUFFER_SIZE );
+ }
+
+ /**
+ * This will create a new stream for which filtered byte should be
+ * written to. You probably don't want this but want to use the
+ * createUnfilteredStream, which is used to write raw bytes to.
+ *
+ * @param expectedLength An entry where a length is expected.
+ *
+ * @return A stream that can be written to.
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public OutputStream createFilteredStream( COSBase expectedLength ) throws IOException
+ {
+ filteredStream = new RandomAccessFileOutputStream( file );
+ filteredStream.setExpectedLength( expectedLength );
+ unFilteredStream = null;
+ return new BufferedOutputStream( filteredStream, BUFFER_SIZE );
+ }
+
+ /**
+ * set the filters to be applied to the stream.
+ *
+ * @param filters The filters to set on this stream.
+ *
+ * @throws IOException If there is an error clearing the old filters.
+ */
+ public void setFilters(COSBase filters) throws IOException
+ {
+ setItem(COSName.FILTER, filters);
+ // kill cached filtered streams
+ filteredStream = null;
+ }
+
+ /**
+ * This will create an output stream that can be written to.
+ *
+ * @return An output stream which raw data bytes should be written to.
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public OutputStream createUnfilteredStream() throws IOException
+ {
+ unFilteredStream = new RandomAccessFileOutputStream( file );
+ filteredStream = null;
+ return new BufferedOutputStream( unFilteredStream, BUFFER_SIZE );
+ }
+
+ /**
+ * This will print a byte array as a hex string to standard output.
+ *
+ * @param data The array to print.
+ */
+ private static void printHexString( byte[] data )
+ {
+ for( int i=0; i<data.length; i++ )
+ {
+ int nextByte = (data[i] + 256)%256;
+ String hexString = Integer.toHexString( nextByte );
+ if( hexString.length() < 2 )
+ {
+ hexString = "0" + hexString;
+ }
+ System.out.print( hexString );
+ if( i != 0 && (i+1) % 2 == 0 )
+ {
+ System.out.print( " " );
+ }
+ if( i!= 0 && i % 20 == 0 )
+ {
+ System.out.println();
+ }
+ }
+ System.out.println();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/COSString.java b/src/main/java/org/pdfbox/cos/COSString.java
new file mode 100644
index 0000000..2b882f9
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/COSString.java
@@ -0,0 +1,403 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.pdfbox.persistence.util.COSHEXTable;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This represents a string object in a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.26 $
+ */
+public class COSString extends COSBase
+{
+ /**
+ * One of the open string tokens.
+ */
+ public static final byte[] STRING_OPEN = new byte[]{ 40 }; //"(".getBytes();
+ /**
+ * One of the close string tokens.
+ */
+ public static final byte[] STRING_CLOSE = new byte[]{ 41 }; //")".getBytes( "ISO-8859-1" );
+ /**
+ * One of the open string tokens.
+ */
+ public static final byte[] HEX_STRING_OPEN = new byte[]{ 60 }; //"<".getBytes( "ISO-8859-1" );
+ /**
+ * One of the close string tokens.
+ */
+ public static final byte[] HEX_STRING_CLOSE = new byte[]{ 62 }; //">".getBytes( "ISO-8859-1" );
+ /**
+ * the escape character in strings.
+ */
+ public static final byte[] ESCAPE = new byte[]{ 92 }; //"\\".getBytes( "ISO-8859-1" );
+
+ /**
+ * CR escape characters.
+ */
+ public static final byte[] CR_ESCAPE = new byte[]{ 92, 114 }; //"\\r".getBytes( "ISO-8859-1" );
+ /**
+ * LF escape characters.
+ */
+ public static final byte[] LF_ESCAPE = new byte[]{ 92, 110 }; //"\\n".getBytes( "ISO-8859-1" );
+ /**
+ * HT escape characters.
+ */
+ public static final byte[] HT_ESCAPE = new byte[]{ 92, 116 }; //"\\t".getBytes( "ISO-8859-1" );
+ /**
+ * BS escape characters.
+ */
+ public static final byte[] BS_ESCAPE = new byte[]{ 92, 98 }; //"\\b".getBytes( "ISO-8859-1" );
+ /**
+ * FF escape characters.
+ */
+ public static final byte[] FF_ESCAPE = new byte[]{ 92, 102 }; //"\\f".getBytes( "ISO-8859-1" );
+
+ private ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ /**
+ * Constructor.
+ */
+ public COSString()
+ {
+ }
+
+ /**
+ * Explicit constructor for ease of manual PDF construction.
+ *
+ * @param value The string value of the object.
+ */
+ public COSString( String value )
+ {
+ try
+ {
+ boolean unicode16 = false;
+ char[] chars = value.toCharArray();
+ for( int i=0; i<chars.length; i++ )
+ {
+ if( chars[i] > 255 )
+ {
+ unicode16 = true;
+ }
+ }
+ if( unicode16 )
+ {
+ out.write( 0xFE );
+ out.write( 0xFF );
+ out.write( value.getBytes( "UTF-16BE" ) );
+ }
+ else
+ {
+ out.write(value.getBytes());
+ }
+ }
+ catch (IOException ignore)
+ {
+ ignore.printStackTrace();
+ //should never happen
+ }
+ }
+
+ /**
+ * Explicit constructor for ease of manual PDF construction.
+ *
+ * @param value The string value of the object.
+ */
+ public COSString( byte[] value )
+ {
+ try
+ {
+ out.write( value );
+ }
+ catch (IOException ignore)
+ {
+ ignore.printStackTrace();
+ //should never happen
+ }
+ }
+
+ /**
+ * This will create a COS string from a string of hex characters.
+ *
+ * @param hex A hex string.
+ * @return A cos string with the hex characters converted to their actual bytes.
+ * @throws IOException If there is an error with the hex string.
+ */
+ public static COSString createFromHexString( String hex ) throws IOException
+ {
+ COSString retval = new COSString();
+ StringBuffer hexBuffer = new StringBuffer( hex.trim() );
+ //if odd number then the last hex digit is assumed to be 0
+ if( hexBuffer.length() % 2 == 1 )
+ {
+ hexBuffer.append( "0" );
+ }
+ for( int i=0; i<hexBuffer.length();)
+ {
+ String hexChars = "" + hexBuffer.charAt( i++ ) + hexBuffer.charAt( i++ );
+ try
+ {
+ retval.append( Integer.parseInt( hexChars, 16 ) );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error: Expected hex number, actual='" + hexChars + "'" );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will take this string and create a hex representation of the bytes that make the string.
+ *
+ * @return A hex string representing the bytes in this string.
+ */
+ public String getHexString()
+ {
+ StringBuffer retval = new StringBuffer( out.size() * 2 );
+ byte[] data = getBytes();
+ for( int i=0; i<data.length; i++ )
+ {
+ retval.append( COSHEXTable.HEX_TABLE[ (data[i]+256)%256 ] );
+ }
+
+ return retval.toString();
+ }
+
+ /**
+ * This will get the string that this object wraps.
+ *
+ * @return The wrapped string.
+ */
+ public String getString()
+ {
+ String retval;
+ String encoding = "ISO-8859-1";
+ byte[] data = getBytes();
+ int start = 0;
+ if( data.length > 2 )
+ {
+ if( data[0] == (byte)0xFF && data[1] == (byte)0xFE )
+ {
+ encoding = "UTF-16LE";
+ start=2;
+ }
+ else if( data[0] == (byte)0xFE && data[1] == (byte)0xFF )
+ {
+ encoding = "UTF-16BE";
+ start=2;
+ }
+ }
+ try
+ {
+ if( encoding != null )
+ {
+ retval = new String( getBytes(), start, data.length-start, encoding );
+ }
+ else
+ {
+ retval = new String( getBytes() );
+ }
+ }
+ catch( UnsupportedEncodingException e )
+ {
+ //should never happen
+ e.printStackTrace();
+ retval = new String( getBytes() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will append a byte[] to the string.
+ *
+ * @param data The byte[] to add to this string.
+ *
+ * @throws IOException If an IO error occurs while writing the byte.
+ */
+ public void append( byte[] data ) throws IOException
+ {
+ out.write( data );
+ }
+
+ /**
+ * This will append a byte to the string.
+ *
+ * @param in The byte to add to this string.
+ *
+ * @throws IOException If an IO error occurs while writing the byte.
+ */
+ public void append( int in ) throws IOException
+ {
+ out.write( in );
+ }
+
+ /**
+ * This will reset the internal buffer.
+ */
+ public void reset()
+ {
+ out.reset();
+ }
+
+ /**
+ * This will get the bytes of the string.
+ *
+ * @return A byte array that represents the string.
+ */
+ public byte[] getBytes()
+ {
+ return out.toByteArray();
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "COSString{" + new String( getBytes() ) + "}";
+ }
+
+ /**
+ * This will output this string as a PDF object.
+ *
+ * @param output The stream to write to.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writePDF( OutputStream output ) throws IOException
+ {
+ boolean outsideASCII = false;
+ //Lets first check if we need to escape this string.
+ byte[] bytes = getBytes();
+ for( int i=0; i<bytes.length && !outsideASCII; i++ )
+ {
+ //if the byte is negative then it is an eight bit byte and is
+ //outside the ASCII range.
+ outsideASCII = bytes[i] <0;
+ }
+ if( !outsideASCII )
+ {
+ output.write(STRING_OPEN);
+ for( int i=0; i<bytes.length; i++ )
+ {
+ int b = (bytes[i]+256)%256;
+ switch( b )
+ {
+ case '(':
+ case ')':
+ case '\\':
+ {
+ output.write(ESCAPE);
+ output.write(b);
+ break;
+ }
+ case 10: //LF
+ {
+ output.write( LF_ESCAPE );
+ break;
+ }
+ case 13: // CR
+ {
+ output.write( CR_ESCAPE );
+ break;
+ }
+ case '\t':
+ {
+ output.write( HT_ESCAPE );
+ break;
+ }
+ case '\b':
+ {
+ output.write( BS_ESCAPE );
+ break;
+ }
+ case '\f':
+ {
+ output.write( FF_ESCAPE );
+ break;
+ }
+ default:
+ {
+ output.write( b );
+ }
+ }
+ }
+ output.write(STRING_CLOSE);
+ }
+ else
+ {
+ output.write(HEX_STRING_OPEN);
+ for(int i=0; i<bytes.length; i++ )
+ {
+ output.write( COSHEXTable.TABLE[ (bytes[i]+256)%256 ] );
+ }
+ output.write(HEX_STRING_CLOSE);
+ }
+ }
+
+
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return visitor.visitFromString( this );
+ }
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals(Object obj)
+ {
+ return (obj instanceof COSString) && java.util.Arrays.equals(((COSString) obj).getBytes(), getBytes());
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return getBytes().hashCode();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/ICOSVisitor.java b/src/main/java/org/pdfbox/cos/ICOSVisitor.java
new file mode 100644
index 0000000..04b7542
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/ICOSVisitor.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.cos;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * An interface for visiting a PDF document at the type (COS) level.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.6 $
+ */
+public interface ICOSVisitor
+{
+ /**
+ * Notification of visit to Array object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromArray( COSArray obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to boolean object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromBoolean( COSBoolean obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to dictionary object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromDictionary( COSDictionary obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to document object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromDocument( COSDocument obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to float object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromFloat( COSFloat obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to integer object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromInt( COSInteger obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to name object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromName( COSName obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to null object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromNull( COSNull obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to stream object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromStream( COSStream obj ) throws COSVisitorException;
+
+ /**
+ * Notification of visit to string object.
+ *
+ * @param obj The Object that is being visited.
+ * @return any Object depending on the visitor implementation, or null
+ * @throws COSVisitorException If there is an error while visiting this object.
+ */
+ public Object visitFromString( COSString obj ) throws COSVisitorException;
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/cos/package.html b/src/main/java/org/pdfbox/cos/package.html
new file mode 100644
index 0000000..4db3df3
--- /dev/null
+++ b/src/main/java/org/pdfbox/cos/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These are the low level objects that make up a PDF document.
+<br/><br/>
+
+See the <A href="http://partners.adobe.com/asn/developer/acrosdk/docs/filefmtspecs/PDFReference.pdf">PDF Reference 1.4</A>.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/encoding/AFMEncoding.java b/src/main/java/org/pdfbox/encoding/AFMEncoding.java
new file mode 100644
index 0000000..badb7bf
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/AFMEncoding.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import java.util.Iterator;
+
+import org.pdfbox.afmtypes.CharMetric;
+import org.pdfbox.afmtypes.FontMetric;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This will handle the encoding from an AFM font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class AFMEncoding extends Encoding
+{
+ private FontMetric metric = null;
+
+ /**
+ * Constructor.
+ *
+ * @param fontInfo The font metric information.
+ */
+ public AFMEncoding( FontMetric fontInfo )
+ {
+ metric = fontInfo;
+ Iterator characters = metric.getCharMetrics().iterator();
+ while( characters.hasNext() )
+ {
+ CharMetric nextMetric = (CharMetric)characters.next();
+ addCharacterEncoding( nextMetric.getCharacterCode(), COSName.getPDFName( nextMetric.getName() ) );
+ }
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/DictionaryEncoding.java b/src/main/java/org/pdfbox/encoding/DictionaryEncoding.java
new file mode 100644
index 0000000..4378898
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/DictionaryEncoding.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+
+/**
+ * This will perform the encoding from a dictionary.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.12 $
+ */
+public class DictionaryEncoding extends Encoding
+{
+ private COSDictionary encoding = null;
+
+ /**
+ * Constructor.
+ *
+ * @param fontEncoding The encoding dictionary.
+ *
+ * @throws IOException If there is a problem getting the base font.
+ */
+ public DictionaryEncoding( COSDictionary fontEncoding ) throws IOException
+ {
+ encoding = fontEncoding;
+
+ //first set up the base encoding
+ //The previious value WinAnsiEncoding() has been changed to StandardEnding
+ //see p 389 of the PDF 1.5 reférence table 5.11 entries in a dictionary encoding
+ //"If this entry is absent, the Differences entry describes differences from an implicit
+ //base encoding. For a font program that is embedded in the PDF file, the
+ //implicit base encoding is the font program’s built-in encoding, as described
+ //above and further elaborated in the sections on specific font types below. Otherwise,
+ //for a nonsymbolic font, it is StandardEncoding, and for a symbolic font, it
+ //is the font’s built-in encoding."
+
+ //so the default base encoding is standardEncoding
+ Encoding baseEncoding = new StandardEncoding();
+ COSName baseEncodingName = (COSName)encoding.getDictionaryObject( COSName.BASE_ENCODING );
+
+ if( baseEncodingName != null )
+ {
+ EncodingManager manager = new EncodingManager();
+ baseEncoding = manager.getEncoding( baseEncodingName );
+ }
+ nameToCode.putAll( baseEncoding.nameToCode );
+ codeToName.putAll( baseEncoding.codeToName );
+
+
+ //now replace with the differences.
+ COSArray differences = (COSArray)encoding.getDictionaryObject( COSName.DIFFERENCES );
+ int currentIndex = -1;
+ for( int i=0; differences != null && i<differences.size(); i++ )
+ {
+ COSBase next = differences.getObject( i );
+ if( next instanceof COSNumber )
+ {
+ currentIndex = (int)((COSNumber)next).intValue();
+ }
+ else if( next instanceof COSName )
+ {
+ COSName name = (COSName)next;
+ addCharacterEncoding( currentIndex++, name );
+ }
+ }
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return encoding;
+ }
+}
diff --git a/src/main/java/org/pdfbox/encoding/Encoding.java b/src/main/java/org/pdfbox/encoding/Encoding.java
new file mode 100644
index 0000000..dc6387d
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/Encoding.java
@@ -0,0 +1,268 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.util.ResourceLoader;
+
+import org.apache.log4j.Logger;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This is an interface to a text encoder.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.13 $
+ */
+public abstract class Encoding implements COSObjectable
+{
+
+
+ /**
+ * This is a mapping from a character code to a character name.
+ */
+ protected Map codeToName = new HashMap();
+ /**
+ * This is a mapping from a character name to a character code.
+ */
+ protected Map nameToCode = new HashMap();
+
+ private static final Map NAME_TO_CHARACTER = new HashMap();
+ private static final Map CHARACTER_TO_NAME = new HashMap();
+
+ private static Logger log = Logger.getLogger( Encoding.class );
+
+ static
+ {
+ BufferedReader glyphStream = null;
+ try
+ {
+ InputStream resource = ResourceLoader.loadResource( "Resources/glyphlist.txt" );
+ glyphStream = new BufferedReader( new InputStreamReader( resource ) );
+ String line = null;
+ while( (line = glyphStream.readLine()) != null )
+ {
+ line = line.trim();
+ //lines starting with # are comments which we can ignore.
+ if( !line.startsWith("#" ) )
+ {
+ int semicolonIndex = line.indexOf( ';' );
+ if( semicolonIndex >= 0 )
+ {
+ try
+ {
+ String characterName = line.substring( 0, semicolonIndex );
+ String unicodeValue = line.substring( semicolonIndex+1, line.length() );
+ StringTokenizer tokenizer = new StringTokenizer( unicodeValue, " ", false );
+ String value = "";
+ while(tokenizer.hasMoreTokens())
+ {
+ int characterCode = Integer.parseInt( tokenizer.nextToken(), 16 );
+ value += (char)characterCode;
+ }
+
+ NAME_TO_CHARACTER.put( COSName.getPDFName( characterName ), value );
+ }
+ catch( NumberFormatException nfe )
+ {
+ log.error( "Error parsing line '" + line + "' ", nfe );
+ }
+ }
+ }
+ }
+ }
+ catch( IOException io )
+ {
+ log.error( "Error reading Resources/glyphlist.txt", io );
+ }
+ finally
+ {
+ if( glyphStream != null )
+ {
+ try
+ {
+ glyphStream.close();
+ }
+ catch( IOException e )
+ {
+ log.warn( "Error closing stream", e );
+ }
+
+ }
+ }
+
+
+ NAME_TO_CHARACTER.put( COSName.getPDFName( ".notdef" ), "" );
+ NAME_TO_CHARACTER.put( COSName.getPDFName( "fi" ), "fi" );
+ NAME_TO_CHARACTER.put( COSName.getPDFName( "fl" ), "fl" );
+ NAME_TO_CHARACTER.put( COSName.getPDFName( "ffi" ), "ffi" );
+ NAME_TO_CHARACTER.put( COSName.getPDFName( "ff" ), "ff" );
+ NAME_TO_CHARACTER.put( COSName.getPDFName( "pi" ), "pi" );
+
+ Iterator keys = NAME_TO_CHARACTER.keySet().iterator();
+ while( keys.hasNext() )
+ {
+ Object key = keys.next();
+ Object value = NAME_TO_CHARACTER.get( key );
+ CHARACTER_TO_NAME.put( value, key );
+ }
+ }
+
+
+ /**
+ * This will add a character encoding.
+ *
+ * @param code The character code that matches the character.
+ * @param name The name of the character.
+ */
+ protected void addCharacterEncoding( int code, COSName name )
+ {
+ Integer intCode = new Integer( code );
+ codeToName.put( intCode, name );
+ nameToCode.put( name, intCode );
+ }
+
+ /**
+ * This will get the character code for the name.
+ *
+ * @param name The name of the character.
+ *
+ * @return The code for the character.
+ *
+ * @throws IOException If there is no character code for the name.
+ */
+ public int getCode( COSName name ) throws IOException
+ {
+ Integer code = (Integer)nameToCode.get( name );
+ if( code == null )
+ {
+ throw new IOException( "No character code for character name '" + name.getName() + "'" );
+ }
+ return code.intValue();
+ }
+
+ /**
+ * This will take a character code and get the name from the code.
+ *
+ * @param code The character code.
+ *
+ * @return The name of the character.
+ *
+ * @throws IOException If there is no name for the code.
+ */
+ public COSName getName( int code ) throws IOException
+ {
+ COSName name = (COSName)codeToName.get( new Integer( code ) );
+ if( name == null )
+ {
+ //lets be forgiving for now
+ name = COSName.getPDFName( "space" );
+ //throw new IOException( getClass().getName() +
+ // ": No name for character code '" + code + "'" );
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Encoding.getName( " + code + " )=" + name );
+ }
+ return name;
+ }
+
+ /**
+ * This will take a character code and get the name from the code.
+ *
+ * @param c The character.
+ *
+ * @return The name of the character.
+ *
+ * @throws IOException If there is no name for the character.
+ */
+ public COSName getNameFromCharacter( char c ) throws IOException
+ {
+ COSName name = (COSName)CHARACTER_TO_NAME.get( "" + c );
+ if( name == null )
+ {
+ throw new IOException( "No name for character '" + c + "'" );
+ }
+ return name;
+ }
+
+ /**
+ * This will get the character from the code.
+ *
+ * @param code The character code.
+ *
+ * @return The printable character for the code.
+ *
+ * @throws IOException If there is not name for the character.
+ */
+ public String getCharacter( int code ) throws IOException
+ {
+ String character = getCharacter( getName( code ) );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Encoding.getCharacter( " + code + " )=" + character );
+ }
+ return character;
+ }
+
+ /**
+ * This will get the character from the name.
+ *
+ * @param name The name of the character.
+ *
+ * @return The printable character for the code.
+ */
+ public static String getCharacter( COSName name )
+ {
+ String character = (String)NAME_TO_CHARACTER.get( name );
+ if( character == null )
+ {
+ character = name.getName();
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Encoding.getCharacter(" + name + ")=" + character );
+ }
+ return character;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/EncodingManager.java b/src/main/java/org/pdfbox/encoding/EncodingManager.java
new file mode 100644
index 0000000..9e8e4b3
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/EncodingManager.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pdfbox.cos.COSName;
+
+/**
+ * This class will handle getting the appropriate encodings.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class EncodingManager
+{
+ private static final Map ENCODINGS = new HashMap();
+
+ static
+ {
+ ENCODINGS.put( COSName.MAC_ROMAN_ENCODING, new MacRomanEncoding() );
+ ENCODINGS.put( COSName.PDF_DOC_ENCODING, new PdfDocEncoding() );
+ ENCODINGS.put( COSName.STANDARD_ENCODING, new StandardEncoding() );
+ ENCODINGS.put( COSName.WIN_ANSI_ENCODING, new WinAnsiEncoding() );
+
+ }
+
+ /**
+ * This will get the standard encoding.
+ *
+ * @return The standard encoding.
+ */
+ public Encoding getStandardEncoding()
+ {
+ return (Encoding)ENCODINGS.get( COSName.STANDARD_ENCODING );
+ }
+
+ /**
+ * This will get an encoding by name.
+ *
+ * @param name The name of the encoding to get.
+ *
+ * @return The encoding that matches the name.
+ *
+ * @throws IOException If there is not encoding with that name.
+ */
+ public Encoding getEncoding( COSName name ) throws IOException
+ {
+ Encoding encoding = (Encoding)ENCODINGS.get( name );
+ if( encoding == null )
+ {
+ throw new IOException( "Unknown encoding for '" + name.getName() + "'" );
+ }
+ return encoding;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/MacRomanEncoding.java b/src/main/java/org/pdfbox/encoding/MacRomanEncoding.java
new file mode 100644
index 0000000..6a560a2
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/MacRomanEncoding.java
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is an interface to a text encoder.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.9 $
+ */
+public class MacRomanEncoding extends Encoding
+{
+ /**
+ * Constructor.
+ */
+ public MacRomanEncoding()
+ {
+ addCharacterEncoding( 0101, COSName.getPDFName( "A" ) );
+ addCharacterEncoding( 0256, COSName.getPDFName( "AE" ) );
+ addCharacterEncoding( 0347, COSName.getPDFName( "Aacute" ) );
+ addCharacterEncoding( 0345, COSName.getPDFName( "Acircumflex" ) );
+ addCharacterEncoding( 0200, COSName.getPDFName( "Adieresis" ) );
+ addCharacterEncoding( 0313, COSName.getPDFName( "Agrave" ) );
+ addCharacterEncoding( 0201, COSName.getPDFName( "Aring" ) );
+ addCharacterEncoding( 0314, COSName.getPDFName( "Atilde" ) );
+ addCharacterEncoding( 0102, COSName.getPDFName( "B" ) );
+ addCharacterEncoding( 0103, COSName.getPDFName( "C" ) );
+ addCharacterEncoding( 0202, COSName.getPDFName( "Ccedilla" ) );
+ addCharacterEncoding( 0104, COSName.getPDFName( "D" ) );
+ addCharacterEncoding( 0105, COSName.getPDFName( "E" ) );
+ addCharacterEncoding( 0203, COSName.getPDFName( "Eacute" ) );
+ addCharacterEncoding( 0346, COSName.getPDFName( "Ecircumflex" ) );
+ addCharacterEncoding( 0350, COSName.getPDFName( "Edieresis" ) );
+ addCharacterEncoding( 0351, COSName.getPDFName( "Egrave" ) );
+ addCharacterEncoding( 0106, COSName.getPDFName( "F" ) );
+ addCharacterEncoding( 0107, COSName.getPDFName( "G" ) );
+ addCharacterEncoding( 0110, COSName.getPDFName( "H" ) );
+ addCharacterEncoding( 0111, COSName.getPDFName( "I" ) );
+ addCharacterEncoding( 0352, COSName.getPDFName( "Iacute" ) );
+ addCharacterEncoding( 0353, COSName.getPDFName( "Icircumflex" ) );
+ addCharacterEncoding( 0354, COSName.getPDFName( "Idieresis" ) );
+ addCharacterEncoding( 0355, COSName.getPDFName( "Igrave" ) );
+ addCharacterEncoding( 0112, COSName.getPDFName( "J" ) );
+ addCharacterEncoding( 0113, COSName.getPDFName( "K" ) );
+ addCharacterEncoding( 0114, COSName.getPDFName( "L" ) );
+ addCharacterEncoding( 0115, COSName.getPDFName( "M" ) );
+ addCharacterEncoding( 0116, COSName.getPDFName( "N" ) );
+ addCharacterEncoding( 0204, COSName.getPDFName( "Ntilde" ) );
+ addCharacterEncoding( 0117, COSName.getPDFName( "O" ) );
+ addCharacterEncoding( 0316, COSName.getPDFName( "OE" ) );
+ addCharacterEncoding( 0356, COSName.getPDFName( "Oacute" ) );
+ addCharacterEncoding( 0357, COSName.getPDFName( "Ocircumflex" ) );
+ addCharacterEncoding( 0205, COSName.getPDFName( "Odieresis" ) );
+ addCharacterEncoding( 0361, COSName.getPDFName( "Ograve" ) );
+ addCharacterEncoding( 0257, COSName.getPDFName( "Oslash" ) );
+ addCharacterEncoding( 0315, COSName.getPDFName( "Otilde" ) );
+ addCharacterEncoding( 0120, COSName.getPDFName( "P" ) );
+ addCharacterEncoding( 0121, COSName.getPDFName( "Q" ) );
+ addCharacterEncoding( 0122, COSName.getPDFName( "R" ) );
+ addCharacterEncoding( 0123, COSName.getPDFName( "S" ) );
+ addCharacterEncoding( 0124, COSName.getPDFName( "T" ) );
+ addCharacterEncoding( 0125, COSName.getPDFName( "U" ) );
+ addCharacterEncoding( 0362, COSName.getPDFName( "Uacute" ) );
+ addCharacterEncoding( 0363, COSName.getPDFName( "Ucircumflex" ) );
+ addCharacterEncoding( 0206, COSName.getPDFName( "Udieresis" ) );
+ addCharacterEncoding( 0364, COSName.getPDFName( "Ugrave" ) );
+ addCharacterEncoding( 0126, COSName.getPDFName( "V" ) );
+ addCharacterEncoding( 0127, COSName.getPDFName( "W" ) );
+ addCharacterEncoding( 0130, COSName.getPDFName( "X" ) );
+ addCharacterEncoding( 0131, COSName.getPDFName( "Y" ) );
+ addCharacterEncoding( 0331, COSName.getPDFName( "Ydieresis" ) );
+ addCharacterEncoding( 0132, COSName.getPDFName( "Z" ) );
+ addCharacterEncoding( 0141, COSName.getPDFName( "a" ) );
+ addCharacterEncoding( 0207, COSName.getPDFName( "aacute" ) );
+ addCharacterEncoding( 0211, COSName.getPDFName( "acircumflex" ) );
+ addCharacterEncoding( 0253, COSName.getPDFName( "acute" ) );
+ addCharacterEncoding( 0212, COSName.getPDFName( "adieresis" ) );
+ addCharacterEncoding( 0276, COSName.getPDFName( "ae" ) );
+ addCharacterEncoding( 0210, COSName.getPDFName( "agrave" ) );
+ addCharacterEncoding( 046, COSName.getPDFName( "ampersand" ) );
+ addCharacterEncoding( 0214, COSName.getPDFName( "aring" ) );
+ addCharacterEncoding( 0136, COSName.getPDFName( "asciicircum" ) );
+ addCharacterEncoding( 0176, COSName.getPDFName( "asciitilde" ) );
+ addCharacterEncoding( 052, COSName.getPDFName( "asterisk" ) );
+ addCharacterEncoding( 0100, COSName.getPDFName( "at" ) );
+ addCharacterEncoding( 0213, COSName.getPDFName( "atilde" ) );
+ addCharacterEncoding( 0142, COSName.getPDFName( "b" ) );
+ addCharacterEncoding( 0134, COSName.getPDFName( "backslash" ) );
+ addCharacterEncoding( 0174, COSName.getPDFName( "bar" ) );
+ addCharacterEncoding( 0173, COSName.getPDFName( "braceleft" ) );
+ addCharacterEncoding( 0175, COSName.getPDFName( "braceright" ) );
+ addCharacterEncoding( 0133, COSName.getPDFName( "bracketleft" ) );
+ addCharacterEncoding( 0135, COSName.getPDFName( "bracketright" ) );
+ addCharacterEncoding( 0371, COSName.getPDFName( "breve" ) );
+ addCharacterEncoding( 0245, COSName.getPDFName( "bullet" ) );
+ addCharacterEncoding( 0143, COSName.getPDFName( "c" ) );
+ addCharacterEncoding( 0377, COSName.getPDFName( "caron" ) );
+ addCharacterEncoding( 0215, COSName.getPDFName( "ccedilla" ) );
+ addCharacterEncoding( 0374, COSName.getPDFName( "cedilla" ) );
+ addCharacterEncoding( 0242, COSName.getPDFName( "cent" ) );
+ addCharacterEncoding( 0366, COSName.getPDFName( "circumflex" ) );
+ addCharacterEncoding( 072, COSName.getPDFName( "colon" ) );
+ addCharacterEncoding( 054, COSName.getPDFName( "comma" ) );
+ addCharacterEncoding( 0251, COSName.getPDFName( "copyright" ) );
+ addCharacterEncoding( 0333, COSName.getPDFName( "currency1" ) );
+ addCharacterEncoding( 0144, COSName.getPDFName( "d" ) );
+ addCharacterEncoding( 0240, COSName.getPDFName( "dagger" ) );
+ addCharacterEncoding( 0340, COSName.getPDFName( "daggerdbl" ) );
+ addCharacterEncoding( 0241, COSName.getPDFName( "degree" ) );
+ addCharacterEncoding( 0254, COSName.getPDFName( "dieresis" ) );
+ addCharacterEncoding( 0326, COSName.getPDFName( "divide" ) );
+ addCharacterEncoding( 044, COSName.getPDFName( "dollar" ) );
+ addCharacterEncoding( 0372, COSName.getPDFName( "dotaccent" ) );
+ addCharacterEncoding( 0365, COSName.getPDFName( "dotlessi" ) );
+ addCharacterEncoding( 0145, COSName.getPDFName( "e" ) );
+ addCharacterEncoding( 0216, COSName.getPDFName( "eacute" ) );
+ addCharacterEncoding( 0220, COSName.getPDFName( "ecircumflex" ) );
+ addCharacterEncoding( 0221, COSName.getPDFName( "edieresis" ) );
+ addCharacterEncoding( 0217, COSName.getPDFName( "egrave" ) );
+ addCharacterEncoding( 070, COSName.getPDFName( "eight" ) );
+ addCharacterEncoding( 0311, COSName.getPDFName( "ellipsis" ) );
+ addCharacterEncoding( 0321, COSName.getPDFName( "emdash" ) );
+ addCharacterEncoding( 0320, COSName.getPDFName( "endash" ) );
+ addCharacterEncoding( 075, COSName.getPDFName( "equal" ) );
+ addCharacterEncoding( 041, COSName.getPDFName( "exclam" ) );
+ addCharacterEncoding( 0301, COSName.getPDFName( "exclamdown" ) );
+ addCharacterEncoding( 0146, COSName.getPDFName( "f" ) );
+ addCharacterEncoding( 0336, COSName.getPDFName( "fi" ) );
+ addCharacterEncoding( 065, COSName.getPDFName( "five" ) );
+ addCharacterEncoding( 0337, COSName.getPDFName( "fl" ) );
+ addCharacterEncoding( 0304, COSName.getPDFName( "florin" ) );
+ addCharacterEncoding( 064, COSName.getPDFName( "four" ) );
+ addCharacterEncoding( 0332, COSName.getPDFName( "fraction" ) );
+ addCharacterEncoding( 0147, COSName.getPDFName( "g" ) );
+ addCharacterEncoding( 0247, COSName.getPDFName( "germandbls" ) );
+ addCharacterEncoding( 0140, COSName.getPDFName( "grave" ) );
+ addCharacterEncoding( 076, COSName.getPDFName( "greater" ) );
+ addCharacterEncoding( 0307, COSName.getPDFName( "guillemotleft" ) );
+ addCharacterEncoding( 0310, COSName.getPDFName( "guillemotright" ) );
+ addCharacterEncoding( 0334, COSName.getPDFName( "guilsinglleft" ) );
+ addCharacterEncoding( 0335, COSName.getPDFName( "guilsinglright" ) );
+ addCharacterEncoding( 0150, COSName.getPDFName( "h" ) );
+ addCharacterEncoding( 0375, COSName.getPDFName( "hungarumlaut" ) );
+ addCharacterEncoding( 055, COSName.getPDFName( "hyphen" ) );
+ addCharacterEncoding( 0151, COSName.getPDFName( "i" ) );
+ addCharacterEncoding( 0222, COSName.getPDFName( "iacute" ) );
+ addCharacterEncoding( 0224, COSName.getPDFName( "icircumflex" ) );
+ addCharacterEncoding( 0225, COSName.getPDFName( "idieresis" ) );
+ addCharacterEncoding( 0223, COSName.getPDFName( "igrave" ) );
+ addCharacterEncoding( 0152, COSName.getPDFName( "j" ) );
+ addCharacterEncoding( 0153, COSName.getPDFName( "k" ) );
+ addCharacterEncoding( 0154, COSName.getPDFName( "l" ) );
+ addCharacterEncoding( 074, COSName.getPDFName( "less" ) );
+ addCharacterEncoding( 0302, COSName.getPDFName( "logicalnot" ) );
+ addCharacterEncoding( 0155, COSName.getPDFName( "m" ) );
+ addCharacterEncoding( 0370, COSName.getPDFName( "macron" ) );
+ addCharacterEncoding( 0265, COSName.getPDFName( "mu" ) );
+ addCharacterEncoding( 0156, COSName.getPDFName( "n" ) );
+ addCharacterEncoding( 071, COSName.getPDFName( "nine" ) );
+ addCharacterEncoding( 0226, COSName.getPDFName( "ntilde" ) );
+ addCharacterEncoding( 043, COSName.getPDFName( "numbersign" ) );
+ addCharacterEncoding( 0157, COSName.getPDFName( "o" ) );
+ addCharacterEncoding( 0227, COSName.getPDFName( "oacute" ) );
+ addCharacterEncoding( 0231, COSName.getPDFName( "ocircumflex" ) );
+ addCharacterEncoding( 0232, COSName.getPDFName( "odieresis" ) );
+ addCharacterEncoding( 0317, COSName.getPDFName( "oe" ) );
+ addCharacterEncoding( 0376, COSName.getPDFName( "ogonek" ) );
+ addCharacterEncoding( 0230, COSName.getPDFName( "ograve" ) );
+ addCharacterEncoding( 061, COSName.getPDFName( "one" ) );
+ addCharacterEncoding( 0273, COSName.getPDFName( "ordfeminine" ) );
+ addCharacterEncoding( 0274, COSName.getPDFName( "ordmasculine" ) );
+ addCharacterEncoding( 0277, COSName.getPDFName( "oslash" ) );
+ addCharacterEncoding( 0233, COSName.getPDFName( "otilde" ) );
+ addCharacterEncoding( 0160, COSName.getPDFName( "p" ) );
+ addCharacterEncoding( 0246, COSName.getPDFName( "paragraph" ) );
+ addCharacterEncoding( 050, COSName.getPDFName( "parenleft" ) );
+ addCharacterEncoding( 051, COSName.getPDFName( "parenright" ) );
+ addCharacterEncoding( 045, COSName.getPDFName( "percent" ) );
+ addCharacterEncoding( 056, COSName.getPDFName( "period" ) );
+ addCharacterEncoding( 0341, COSName.getPDFName( "periodcentered" ) );
+ addCharacterEncoding( 0344, COSName.getPDFName( "perthousand" ) );
+ addCharacterEncoding( 053, COSName.getPDFName( "plus" ) );
+ addCharacterEncoding( 0261, COSName.getPDFName( "plusminus" ) );
+ addCharacterEncoding( 0161, COSName.getPDFName( "q" ) );
+ addCharacterEncoding( 077, COSName.getPDFName( "question" ) );
+ addCharacterEncoding( 0300, COSName.getPDFName( "questiondown" ) );
+ addCharacterEncoding( 042, COSName.getPDFName( "quotedbl" ) );
+ addCharacterEncoding( 0343, COSName.getPDFName( "quotedblbase" ) );
+ addCharacterEncoding( 0322, COSName.getPDFName( "quotedblleft" ) );
+ addCharacterEncoding( 0323, COSName.getPDFName( "quotedblright" ) );
+ addCharacterEncoding( 0324, COSName.getPDFName( "quoteleft" ) );
+ addCharacterEncoding( 0325, COSName.getPDFName( "quoteright" ) );
+ addCharacterEncoding( 0342, COSName.getPDFName( "quotesinglbase" ) );
+ addCharacterEncoding( 047, COSName.getPDFName( "quotesingle" ) );
+ addCharacterEncoding( 0162, COSName.getPDFName( "r" ) );
+ addCharacterEncoding( 0250, COSName.getPDFName( "registered" ) );
+ addCharacterEncoding( 0373, COSName.getPDFName( "ring" ) );
+ addCharacterEncoding( 0163, COSName.getPDFName( "s" ) );
+ addCharacterEncoding( 0244, COSName.getPDFName( "section" ) );
+ addCharacterEncoding( 073, COSName.getPDFName( "semicolon" ) );
+ addCharacterEncoding( 067, COSName.getPDFName( "seven" ) );
+ addCharacterEncoding( 066, COSName.getPDFName( "six" ) );
+ addCharacterEncoding( 057, COSName.getPDFName( "slash" ) );
+ addCharacterEncoding( 040, COSName.getPDFName( "space" ) );
+ addCharacterEncoding( 0243, COSName.getPDFName( "sterling" ) );
+ addCharacterEncoding( 0164, COSName.getPDFName( "t" ) );
+ addCharacterEncoding( 063, COSName.getPDFName( "three" ) );
+ addCharacterEncoding( 0367, COSName.getPDFName( "tilde" ) );
+ addCharacterEncoding( 0252, COSName.getPDFName( "trademark" ) );
+ addCharacterEncoding( 062, COSName.getPDFName( "two" ) );
+ addCharacterEncoding( 0165, COSName.getPDFName( "u" ) );
+ addCharacterEncoding( 0234, COSName.getPDFName( "uacute" ) );
+ addCharacterEncoding( 0236, COSName.getPDFName( "ucircumflex" ) );
+ addCharacterEncoding( 0237, COSName.getPDFName( "udieresis" ) );
+ addCharacterEncoding( 0235, COSName.getPDFName( "ugrave" ) );
+ addCharacterEncoding( 0137, COSName.getPDFName( "underscore" ) );
+ addCharacterEncoding( 0166, COSName.getPDFName( "v" ) );
+ addCharacterEncoding( 0167, COSName.getPDFName( "w" ) );
+ addCharacterEncoding( 0170, COSName.getPDFName( "x" ) );
+ addCharacterEncoding( 0171, COSName.getPDFName( "y" ) );
+ addCharacterEncoding( 0330, COSName.getPDFName( "ydieresis" ) );
+ addCharacterEncoding( 0264, COSName.getPDFName( "yen" ) );
+ addCharacterEncoding( 0172, COSName.getPDFName( "z" ) );
+ addCharacterEncoding( 060, COSName.getPDFName( "zero" ) );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return COSName.MAC_ROMAN_ENCODING;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/PdfDocEncoding.java b/src/main/java/org/pdfbox/encoding/PdfDocEncoding.java
new file mode 100644
index 0000000..1153002
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/PdfDocEncoding.java
@@ -0,0 +1,289 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is an interface to a text encoder.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.9 $
+ */
+public class PdfDocEncoding extends Encoding
+{
+ /**
+ * Constructor.
+ */
+ public PdfDocEncoding()
+ {
+ addCharacterEncoding( 0101, COSName.getPDFName( "A" ) );
+ addCharacterEncoding( 0306, COSName.getPDFName( "AE" ) );
+ addCharacterEncoding( 0301, COSName.getPDFName( "Aacute" ) );
+ addCharacterEncoding( 0302, COSName.getPDFName( "Acircumflex" ) );
+ addCharacterEncoding( 0304, COSName.getPDFName( "Adieresis" ) );
+ addCharacterEncoding( 0300, COSName.getPDFName( "Agrave" ) );
+ addCharacterEncoding( 0305, COSName.getPDFName( "Aring" ) );
+ addCharacterEncoding( 0303, COSName.getPDFName( "Atilde" ) );
+ addCharacterEncoding( 0102, COSName.getPDFName( "B" ) );
+ addCharacterEncoding( 0103, COSName.getPDFName( "C" ) );
+ addCharacterEncoding( 0307, COSName.getPDFName( "Ccedilla" ) );
+ addCharacterEncoding( 0104, COSName.getPDFName( "D" ) );
+ addCharacterEncoding( 0105, COSName.getPDFName( "E" ) );
+ addCharacterEncoding( 0311, COSName.getPDFName( "Eacute" ) );
+ addCharacterEncoding( 0312, COSName.getPDFName( "Ecircumflex" ) );
+ addCharacterEncoding( 0313, COSName.getPDFName( "Edieresis" ) );
+ addCharacterEncoding( 0310, COSName.getPDFName( "Egrave" ) );
+ addCharacterEncoding( 0320, COSName.getPDFName( "Eth" ) );
+ addCharacterEncoding( 0240, COSName.getPDFName( "Euro" ) );
+ addCharacterEncoding( 0106, COSName.getPDFName( "F" ) );
+ addCharacterEncoding( 0107, COSName.getPDFName( "G" ) );
+ addCharacterEncoding( 0110, COSName.getPDFName( "H" ) );
+ addCharacterEncoding( 0111, COSName.getPDFName( "I" ) );
+ addCharacterEncoding( 0315, COSName.getPDFName( "Iacute" ) );
+ addCharacterEncoding( 0316, COSName.getPDFName( "Icircumflex" ) );
+ addCharacterEncoding( 0317, COSName.getPDFName( "Idieresis" ) );
+ addCharacterEncoding( 0314, COSName.getPDFName( "Igrave" ) );
+ addCharacterEncoding( 0112, COSName.getPDFName( "J" ) );
+ addCharacterEncoding( 0113, COSName.getPDFName( "K" ) );
+ addCharacterEncoding( 0114, COSName.getPDFName( "L" ) );
+ addCharacterEncoding( 0225, COSName.getPDFName( "Lslash" ) );
+ addCharacterEncoding( 0115, COSName.getPDFName( "M" ) );
+ addCharacterEncoding( 0116, COSName.getPDFName( "N" ) );
+ addCharacterEncoding( 0321, COSName.getPDFName( "Ntilde" ) );
+ addCharacterEncoding( 0117, COSName.getPDFName( "O" ) );
+ addCharacterEncoding( 0226, COSName.getPDFName( "OE" ) );
+ addCharacterEncoding( 0323, COSName.getPDFName( "Oacute" ) );
+ addCharacterEncoding( 0324, COSName.getPDFName( "Ocircumflex" ) );
+ addCharacterEncoding( 0326, COSName.getPDFName( "Odieresis" ) );
+ addCharacterEncoding( 0322, COSName.getPDFName( "Ograve" ) );
+ addCharacterEncoding( 0330, COSName.getPDFName( "Oslash" ) );
+ addCharacterEncoding( 0325, COSName.getPDFName( "Otilde" ) );
+ addCharacterEncoding( 0120, COSName.getPDFName( "P" ) );
+ addCharacterEncoding( 0121, COSName.getPDFName( "Q" ) );
+ addCharacterEncoding( 0122, COSName.getPDFName( "R" ) );
+ addCharacterEncoding( 0123, COSName.getPDFName( "S" ) );
+ addCharacterEncoding( 0227, COSName.getPDFName( "Scaron" ) );
+ addCharacterEncoding( 0124, COSName.getPDFName( "T" ) );
+ addCharacterEncoding( 0336, COSName.getPDFName( "Thorn" ) );
+ addCharacterEncoding( 0125, COSName.getPDFName( "U" ) );
+ addCharacterEncoding( 0332, COSName.getPDFName( "Uacute" ) );
+ addCharacterEncoding( 0333, COSName.getPDFName( "Ucircumflex" ) );
+ addCharacterEncoding( 0334, COSName.getPDFName( "Udieresis" ) );
+ addCharacterEncoding( 0331, COSName.getPDFName( "Ugrave" ) );
+ addCharacterEncoding( 0126, COSName.getPDFName( "V" ) );
+ addCharacterEncoding( 0127, COSName.getPDFName( "W" ) );
+ addCharacterEncoding( 0130, COSName.getPDFName( "X" ) );
+ addCharacterEncoding( 0131, COSName.getPDFName( "Y" ) );
+ addCharacterEncoding( 0335, COSName.getPDFName( "Yacute" ) );
+ addCharacterEncoding( 0230, COSName.getPDFName( "Ydieresis" ) );
+ addCharacterEncoding( 0132, COSName.getPDFName( "Z" ) );
+ addCharacterEncoding( 0231, COSName.getPDFName( "Zcaron" ) );
+ addCharacterEncoding( 0141, COSName.getPDFName( "a" ) );
+ addCharacterEncoding( 0341, COSName.getPDFName( "aacute" ) );
+ addCharacterEncoding( 0342, COSName.getPDFName( "acircumflex" ) );
+ addCharacterEncoding( 0264, COSName.getPDFName( "acute" ) );
+ addCharacterEncoding( 0344, COSName.getPDFName( "adieresis" ) );
+ addCharacterEncoding( 0346, COSName.getPDFName( "ae" ) );
+ addCharacterEncoding( 0340, COSName.getPDFName( "agrave" ) );
+ addCharacterEncoding( 046, COSName.getPDFName( "ampersand" ) );
+ addCharacterEncoding( 0345, COSName.getPDFName( "aring" ) );
+ addCharacterEncoding( 0136, COSName.getPDFName( "asciicircum" ) );
+ addCharacterEncoding( 0176, COSName.getPDFName( "asciitilde" ) );
+ addCharacterEncoding( 052, COSName.getPDFName( "asterisk" ) );
+ addCharacterEncoding( 0100, COSName.getPDFName( "at" ) );
+ addCharacterEncoding( 0343, COSName.getPDFName( "atilde" ) );
+ addCharacterEncoding( 0142, COSName.getPDFName( "b" ) );
+ addCharacterEncoding( 0134, COSName.getPDFName( "backslash" ) );
+ addCharacterEncoding( 0174, COSName.getPDFName( "bar" ) );
+ addCharacterEncoding( 0173, COSName.getPDFName( "braceleft" ) );
+ addCharacterEncoding( 0175, COSName.getPDFName( "braceright" ) );
+ addCharacterEncoding( 0133, COSName.getPDFName( "bracketleft" ) );
+ addCharacterEncoding( 0135, COSName.getPDFName( "bracketright" ) );
+ addCharacterEncoding( 030, COSName.getPDFName( "breve" ) );
+ addCharacterEncoding( 0246, COSName.getPDFName( "brokenbar" ) );
+ addCharacterEncoding( 0200, COSName.getPDFName( "bullet" ) );
+ addCharacterEncoding( 0143, COSName.getPDFName( "c" ) );
+ addCharacterEncoding( 031, COSName.getPDFName( "caron" ) );
+ addCharacterEncoding( 0347, COSName.getPDFName( "ccedilla" ) );
+ addCharacterEncoding( 0270, COSName.getPDFName( "cedilla" ) );
+ addCharacterEncoding( 0242, COSName.getPDFName( "cent" ) );
+ addCharacterEncoding( 032, COSName.getPDFName( "circumflex" ) );
+ addCharacterEncoding( 072, COSName.getPDFName( "colon" ) );
+ addCharacterEncoding( 054, COSName.getPDFName( "comma" ) );
+ addCharacterEncoding( 0251, COSName.getPDFName( "copyright" ) );
+ addCharacterEncoding( 0244, COSName.getPDFName( "currency1" ) );
+ addCharacterEncoding( 0144, COSName.getPDFName( "d" ) );
+ addCharacterEncoding( 0201, COSName.getPDFName( "dagger" ) );
+ addCharacterEncoding( 0202, COSName.getPDFName( "daggerdbl" ) );
+ addCharacterEncoding( 0260, COSName.getPDFName( "degree" ) );
+ addCharacterEncoding( 0250, COSName.getPDFName( "dieresis" ) );
+ addCharacterEncoding( 0367, COSName.getPDFName( "divide" ) );
+ addCharacterEncoding( 044, COSName.getPDFName( "dollar" ) );
+ addCharacterEncoding( 033, COSName.getPDFName( "dotaccent" ) );
+ addCharacterEncoding( 0232, COSName.getPDFName( "dotlessi" ) );
+ addCharacterEncoding( 0145, COSName.getPDFName( "e" ) );
+ addCharacterEncoding( 0351, COSName.getPDFName( "eacute" ) );
+ addCharacterEncoding( 0352, COSName.getPDFName( "ecircumflex" ) );
+ addCharacterEncoding( 0353, COSName.getPDFName( "edieresis" ) );
+ addCharacterEncoding( 0350, COSName.getPDFName( "egrave" ) );
+ addCharacterEncoding( 070, COSName.getPDFName( "eight" ) );
+ addCharacterEncoding( 0203, COSName.getPDFName( "ellipsis" ) );
+ addCharacterEncoding( 0204, COSName.getPDFName( "emdash" ) );
+ addCharacterEncoding( 0205, COSName.getPDFName( "endash" ) );
+ addCharacterEncoding( 075, COSName.getPDFName( "equal" ) );
+ addCharacterEncoding( 0360, COSName.getPDFName( "eth" ) );
+ addCharacterEncoding( 041, COSName.getPDFName( "exclam" ) );
+ addCharacterEncoding( 0241, COSName.getPDFName( "exclamdown" ) );
+ addCharacterEncoding( 0146, COSName.getPDFName( "f" ) );
+ addCharacterEncoding( 0223, COSName.getPDFName( "fi" ) );
+ addCharacterEncoding( 065, COSName.getPDFName( "five" ) );
+ addCharacterEncoding( 0224, COSName.getPDFName( "fl" ) );
+ addCharacterEncoding( 0206, COSName.getPDFName( "florin" ) );
+ addCharacterEncoding( 064, COSName.getPDFName( "four" ) );
+ addCharacterEncoding( 0207, COSName.getPDFName( "fraction" ) );
+ addCharacterEncoding( 0147, COSName.getPDFName( "g" ) );
+ addCharacterEncoding( 0337, COSName.getPDFName( "germandbls" ) );
+ addCharacterEncoding( 0140, COSName.getPDFName( "grave" ) );
+ addCharacterEncoding( 076, COSName.getPDFName( "greater" ) );
+ addCharacterEncoding( 0253, COSName.getPDFName( "guillemotleft" ) );
+ addCharacterEncoding( 0273, COSName.getPDFName( "guillemotright" ) );
+ addCharacterEncoding( 0210, COSName.getPDFName( "guilsinglleft" ) );
+ addCharacterEncoding( 0211, COSName.getPDFName( "guilsinglright" ) );
+ addCharacterEncoding( 0150, COSName.getPDFName( "h" ) );
+ addCharacterEncoding( 034, COSName.getPDFName( "hungarumlaut" ) );
+ addCharacterEncoding( 055, COSName.getPDFName( "hyphen" ) );
+ addCharacterEncoding( 0151, COSName.getPDFName( "i" ) );
+ addCharacterEncoding( 0355, COSName.getPDFName( "iacute" ) );
+ addCharacterEncoding( 0356, COSName.getPDFName( "icircumflex" ) );
+ addCharacterEncoding( 0357, COSName.getPDFName( "idieresis" ) );
+ addCharacterEncoding( 0354, COSName.getPDFName( "igrave" ) );
+ addCharacterEncoding( 0152, COSName.getPDFName( "j" ) );
+ addCharacterEncoding( 0153, COSName.getPDFName( "k" ) );
+ addCharacterEncoding( 0154, COSName.getPDFName( "l" ) );
+ addCharacterEncoding( 074, COSName.getPDFName( "less" ) );
+ addCharacterEncoding( 0254, COSName.getPDFName( "logicalnot" ) );
+ addCharacterEncoding( 0233, COSName.getPDFName( "lslash" ) );
+ addCharacterEncoding( 0155, COSName.getPDFName( "m" ) );
+ addCharacterEncoding( 0257, COSName.getPDFName( "macron" ) );
+ addCharacterEncoding( 0212, COSName.getPDFName( "minus" ) );
+ addCharacterEncoding( 0265, COSName.getPDFName( "mu" ) );
+ addCharacterEncoding( 0327, COSName.getPDFName( "multiply" ) );
+ addCharacterEncoding( 0156, COSName.getPDFName( "n" ) );
+ addCharacterEncoding( 071, COSName.getPDFName( "nine" ) );
+ addCharacterEncoding( 0361, COSName.getPDFName( "ntilde" ) );
+ addCharacterEncoding( 043, COSName.getPDFName( "numbersign" ) );
+ addCharacterEncoding( 0157, COSName.getPDFName( "o" ) );
+ addCharacterEncoding( 0363, COSName.getPDFName( "oacute" ) );
+ addCharacterEncoding( 0364, COSName.getPDFName( "ocircumflex" ) );
+ addCharacterEncoding( 0366, COSName.getPDFName( "odieresis" ) );
+ addCharacterEncoding( 0234, COSName.getPDFName( "oe" ) );
+ addCharacterEncoding( 035, COSName.getPDFName( "ogonek" ) );
+ addCharacterEncoding( 0362, COSName.getPDFName( "ograve" ) );
+ addCharacterEncoding( 061, COSName.getPDFName( "one" ) );
+ addCharacterEncoding( 0275, COSName.getPDFName( "onehalf" ) );
+ addCharacterEncoding( 0274, COSName.getPDFName( "onequarter" ) );
+ addCharacterEncoding( 0271, COSName.getPDFName( "onesuperior" ) );
+ addCharacterEncoding( 0252, COSName.getPDFName( "ordfeminine" ) );
+ addCharacterEncoding( 0272, COSName.getPDFName( "ordmasculine" ) );
+ addCharacterEncoding( 0370, COSName.getPDFName( "oslash" ) );
+ addCharacterEncoding( 0365, COSName.getPDFName( "otilde" ) );
+ addCharacterEncoding( 0160, COSName.getPDFName( "p" ) );
+ addCharacterEncoding( 0266, COSName.getPDFName( "paragraph" ) );
+ addCharacterEncoding( 050, COSName.getPDFName( "parenleft" ) );
+ addCharacterEncoding( 051, COSName.getPDFName( "parenright" ) );
+ addCharacterEncoding( 045, COSName.getPDFName( "percent" ) );
+ addCharacterEncoding( 056, COSName.getPDFName( "period" ) );
+ addCharacterEncoding( 0267, COSName.getPDFName( "periodcentered" ) );
+ addCharacterEncoding( 0213, COSName.getPDFName( "perthousand" ) );
+ addCharacterEncoding( 053, COSName.getPDFName( "plus" ) );
+ addCharacterEncoding( 0261, COSName.getPDFName( "plusminus" ) );
+ addCharacterEncoding( 0161, COSName.getPDFName( "q" ) );
+ addCharacterEncoding( 077, COSName.getPDFName( "question" ) );
+ addCharacterEncoding( 0277, COSName.getPDFName( "questiondown" ) );
+ addCharacterEncoding( 042, COSName.getPDFName( "quotedbl" ) );
+ addCharacterEncoding( 0214, COSName.getPDFName( "quotedblbase" ) );
+ addCharacterEncoding( 0215, COSName.getPDFName( "quotedblleft" ) );
+ addCharacterEncoding( 0216, COSName.getPDFName( "quotedblright" ) );
+ addCharacterEncoding( 0217, COSName.getPDFName( "quoteleft" ) );
+ addCharacterEncoding( 0220, COSName.getPDFName( "quoteright" ) );
+ addCharacterEncoding( 0221, COSName.getPDFName( "quotesinglbase" ) );
+ addCharacterEncoding( 047, COSName.getPDFName( "quotesingle" ) );
+ addCharacterEncoding( 0162, COSName.getPDFName( "r" ) );
+ addCharacterEncoding( 0256, COSName.getPDFName( "registered" ) );
+ addCharacterEncoding( 036, COSName.getPDFName( "ring" ) );
+ addCharacterEncoding( 0163, COSName.getPDFName( "s" ) );
+ addCharacterEncoding( 0235, COSName.getPDFName( "scaron" ) );
+ addCharacterEncoding( 0247, COSName.getPDFName( "section" ) );
+ addCharacterEncoding( 073, COSName.getPDFName( "semicolon" ) );
+ addCharacterEncoding( 067, COSName.getPDFName( "seven" ) );
+ addCharacterEncoding( 066, COSName.getPDFName( "six" ) );
+ addCharacterEncoding( 057, COSName.getPDFName( "slash" ) );
+ addCharacterEncoding( 040, COSName.getPDFName( "space" ) );
+ addCharacterEncoding( 0243, COSName.getPDFName( "sterling" ) );
+ addCharacterEncoding( 0164, COSName.getPDFName( "t" ) );
+ addCharacterEncoding( 0376, COSName.getPDFName( "thorn" ) );
+ addCharacterEncoding( 063, COSName.getPDFName( "three" ) );
+ addCharacterEncoding( 0276, COSName.getPDFName( "threequarters" ) );
+ addCharacterEncoding( 0263, COSName.getPDFName( "threesuperior" ) );
+ addCharacterEncoding( 037, COSName.getPDFName( "tilde" ) );
+ addCharacterEncoding( 0222, COSName.getPDFName( "trademark" ) );
+ addCharacterEncoding( 062, COSName.getPDFName( "two" ) );
+ addCharacterEncoding( 0262, COSName.getPDFName( "twosuperior" ) );
+ addCharacterEncoding( 0165, COSName.getPDFName( "u" ) );
+ addCharacterEncoding( 0372, COSName.getPDFName( "uacute" ) );
+ addCharacterEncoding( 0373, COSName.getPDFName( "ucircumflex" ) );
+ addCharacterEncoding( 0374, COSName.getPDFName( "udieresis" ) );
+ addCharacterEncoding( 0371, COSName.getPDFName( "ugrave" ) );
+ addCharacterEncoding( 0137, COSName.getPDFName( "underscore" ) );
+ addCharacterEncoding( 0166, COSName.getPDFName( "v" ) );
+ addCharacterEncoding( 0167, COSName.getPDFName( "w" ) );
+ addCharacterEncoding( 0170, COSName.getPDFName( "x" ) );
+ addCharacterEncoding( 0171, COSName.getPDFName( "y" ) );
+ addCharacterEncoding( 0375, COSName.getPDFName( "yacute" ) );
+ addCharacterEncoding( 0377, COSName.getPDFName( "ydieresis" ) );
+ addCharacterEncoding( 0245, COSName.getPDFName( "yen" ) );
+ addCharacterEncoding( 0172, COSName.getPDFName( "z" ) );
+ addCharacterEncoding( 0236, COSName.getPDFName( "zcaron" ) );
+ addCharacterEncoding( 060, COSName.getPDFName( "zero" ) );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return COSName.PDF_DOC_ENCODING;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/StandardEncoding.java b/src/main/java/org/pdfbox/encoding/StandardEncoding.java
new file mode 100644
index 0000000..229adb4
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/StandardEncoding.java
@@ -0,0 +1,209 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is an interface to a text encoder.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.9 $
+ */
+public class StandardEncoding extends Encoding
+{
+ /**
+ * Constructor.
+ */
+ public StandardEncoding()
+ {
+ addCharacterEncoding( 0101, COSName.getPDFName( "A" ) );
+ addCharacterEncoding( 0341, COSName.getPDFName( "AE" ) );
+ addCharacterEncoding( 0102, COSName.getPDFName( "B" ) );
+ addCharacterEncoding( 0103, COSName.getPDFName( "C" ) );
+ addCharacterEncoding( 0104, COSName.getPDFName( "D" ) );
+ addCharacterEncoding( 0105, COSName.getPDFName( "E" ) );
+ addCharacterEncoding( 0106, COSName.getPDFName( "F" ) );
+ addCharacterEncoding( 0107, COSName.getPDFName( "G" ) );
+ addCharacterEncoding( 0110, COSName.getPDFName( "H" ) );
+ addCharacterEncoding( 0111, COSName.getPDFName( "I" ) );
+ addCharacterEncoding( 0112, COSName.getPDFName( "J" ) );
+ addCharacterEncoding( 0113, COSName.getPDFName( "K" ) );
+ addCharacterEncoding( 0114, COSName.getPDFName( "L" ) );
+ addCharacterEncoding( 0350, COSName.getPDFName( "Lslash" ) );
+ addCharacterEncoding( 0115, COSName.getPDFName( "M" ) );
+ addCharacterEncoding( 0116, COSName.getPDFName( "N" ) );
+ addCharacterEncoding( 0117, COSName.getPDFName( "O" ) );
+ addCharacterEncoding( 0352, COSName.getPDFName( "OE" ) );
+ addCharacterEncoding( 0351, COSName.getPDFName( "Oslash" ) );
+ addCharacterEncoding( 0120, COSName.getPDFName( "P" ) );
+ addCharacterEncoding( 0121, COSName.getPDFName( "Q" ) );
+ addCharacterEncoding( 0122, COSName.getPDFName( "R" ) );
+ addCharacterEncoding( 0123, COSName.getPDFName( "S" ) );
+ addCharacterEncoding( 0124, COSName.getPDFName( "T" ) );
+ addCharacterEncoding( 0125, COSName.getPDFName( "U" ) );
+ addCharacterEncoding( 0126, COSName.getPDFName( "V" ) );
+ addCharacterEncoding( 0127, COSName.getPDFName( "W" ) );
+ addCharacterEncoding( 0130, COSName.getPDFName( "X" ) );
+ addCharacterEncoding( 0131, COSName.getPDFName( "Y" ) );
+ addCharacterEncoding( 0132, COSName.getPDFName( "Z" ) );
+ addCharacterEncoding( 0141, COSName.getPDFName( "a" ) );
+ addCharacterEncoding( 0302, COSName.getPDFName( "acute" ) );
+ addCharacterEncoding( 0361, COSName.getPDFName( "ae" ) );
+ addCharacterEncoding( 0046, COSName.getPDFName( "ampersand" ) );
+ addCharacterEncoding( 0136, COSName.getPDFName( "asciicircum" ) );
+ addCharacterEncoding( 0176, COSName.getPDFName( "asciitilde" ) );
+ addCharacterEncoding( 0052, COSName.getPDFName( "asterisk" ) );
+ addCharacterEncoding( 0100, COSName.getPDFName( "at" ) );
+ addCharacterEncoding( 0142, COSName.getPDFName( "b" ) );
+ addCharacterEncoding( 0134, COSName.getPDFName( "backslash" ) );
+ addCharacterEncoding( 0174, COSName.getPDFName( "bar" ) );
+ addCharacterEncoding( 0173, COSName.getPDFName( "braceleft" ) );
+ addCharacterEncoding( 0175, COSName.getPDFName( "braceright" ) );
+ addCharacterEncoding( 0133, COSName.getPDFName( "bracketleft" ) );
+ addCharacterEncoding( 0135, COSName.getPDFName( "bracketright" ) );
+ addCharacterEncoding( 0306, COSName.getPDFName( "breve" ) );
+ addCharacterEncoding( 0267, COSName.getPDFName( "bullet" ) );
+ addCharacterEncoding( 0143, COSName.getPDFName( "c" ) );
+ addCharacterEncoding( 0317, COSName.getPDFName( "caron" ) );
+ addCharacterEncoding( 0313, COSName.getPDFName( "cedilla" ) );
+ addCharacterEncoding( 0242, COSName.getPDFName( "cent" ) );
+ addCharacterEncoding( 0303, COSName.getPDFName( "circumflex" ) );
+ addCharacterEncoding( 0072, COSName.getPDFName( "colon" ) );
+ addCharacterEncoding( 0054, COSName.getPDFName( "comma" ) );
+ addCharacterEncoding( 0250, COSName.getPDFName( "currency1" ) );
+ addCharacterEncoding( 0144, COSName.getPDFName( "d" ) );
+ addCharacterEncoding( 0262, COSName.getPDFName( "dagger" ) );
+ addCharacterEncoding( 0263, COSName.getPDFName( "daggerdbl" ) );
+ addCharacterEncoding( 0310, COSName.getPDFName( "dieresis" ) );
+ addCharacterEncoding( 0044, COSName.getPDFName( "dollar" ) );
+ addCharacterEncoding( 0307, COSName.getPDFName( "dotaccent" ) );
+ addCharacterEncoding( 0365, COSName.getPDFName( "dotlessi" ) );
+ addCharacterEncoding( 0145, COSName.getPDFName( "e" ) );
+ addCharacterEncoding( 0070, COSName.getPDFName( "eight" ) );
+ addCharacterEncoding( 0274, COSName.getPDFName( "ellipsis" ) );
+ addCharacterEncoding( 0320, COSName.getPDFName( "emdash" ) );
+ addCharacterEncoding( 0261, COSName.getPDFName( "endash" ) );
+ addCharacterEncoding( 0075, COSName.getPDFName( "equal" ) );
+ addCharacterEncoding( 0041, COSName.getPDFName( "exclam" ) );
+ addCharacterEncoding( 0241, COSName.getPDFName( "exclamdown" ) );
+ addCharacterEncoding( 0146, COSName.getPDFName( "f" ) );
+ addCharacterEncoding( 0256, COSName.getPDFName( "fi" ) );
+ addCharacterEncoding( 0065, COSName.getPDFName( "five" ) );
+ addCharacterEncoding( 0257, COSName.getPDFName( "fl" ) );
+ addCharacterEncoding( 0246, COSName.getPDFName( "florin" ) );
+ addCharacterEncoding( 0064, COSName.getPDFName( "four" ) );
+ addCharacterEncoding( 0244, COSName.getPDFName( "fraction" ) );
+ addCharacterEncoding( 0147, COSName.getPDFName( "g" ) );
+ addCharacterEncoding( 0373, COSName.getPDFName( "germandbls" ) );
+ addCharacterEncoding( 0301, COSName.getPDFName( "grave" ) );
+ addCharacterEncoding( 0076, COSName.getPDFName( "greater" ) );
+ addCharacterEncoding( 0253, COSName.getPDFName( "guillemotleft" ) );
+ addCharacterEncoding( 0273, COSName.getPDFName( "guillemotright" ) );
+ addCharacterEncoding( 0254, COSName.getPDFName( "guilsinglleft" ) );
+ addCharacterEncoding( 0255, COSName.getPDFName( "guilsinglright" ) );
+ addCharacterEncoding( 0150, COSName.getPDFName( "h" ) );
+ addCharacterEncoding( 0315, COSName.getPDFName( "hungarumlaut" ) );
+ addCharacterEncoding( 0055, COSName.getPDFName( "hyphen" ) );
+ addCharacterEncoding( 0151, COSName.getPDFName( "i" ) );
+ addCharacterEncoding( 0152, COSName.getPDFName( "j" ) );
+ addCharacterEncoding( 0153, COSName.getPDFName( "k" ) );
+ addCharacterEncoding( 0154, COSName.getPDFName( "l" ) );
+ addCharacterEncoding( 0074, COSName.getPDFName( "less" ) );
+ addCharacterEncoding( 0370, COSName.getPDFName( "lslash" ) );
+ addCharacterEncoding( 0155, COSName.getPDFName( "m" ) );
+ addCharacterEncoding( 0305, COSName.getPDFName( "macron" ) );
+ addCharacterEncoding( 0156, COSName.getPDFName( "n" ) );
+ addCharacterEncoding( 0071, COSName.getPDFName( "nine" ) );
+ addCharacterEncoding( 0043, COSName.getPDFName( "numbersign" ) );
+ addCharacterEncoding( 0157, COSName.getPDFName( "o" ) );
+ addCharacterEncoding( 0372, COSName.getPDFName( "oe" ) );
+ addCharacterEncoding( 0316, COSName.getPDFName( "ogonek" ) );
+ addCharacterEncoding( 0061, COSName.getPDFName( "one" ) );
+ addCharacterEncoding( 0343, COSName.getPDFName( "ordfeminine" ) );
+ addCharacterEncoding( 0353, COSName.getPDFName( "ordmasculine" ) );
+ addCharacterEncoding( 0371, COSName.getPDFName( "oslash" ) );
+ addCharacterEncoding( 0160, COSName.getPDFName( "p" ) );
+ addCharacterEncoding( 0266, COSName.getPDFName( "paragraph" ) );
+ addCharacterEncoding( 0050, COSName.getPDFName( "parenleft" ) );
+ addCharacterEncoding( 0051, COSName.getPDFName( "parenright" ) );
+ addCharacterEncoding( 0045, COSName.getPDFName( "percent" ) );
+ addCharacterEncoding( 0056, COSName.getPDFName( "period" ) );
+ addCharacterEncoding( 0264, COSName.getPDFName( "periodcentered" ) );
+ addCharacterEncoding( 0275, COSName.getPDFName( "perthousand" ) );
+ addCharacterEncoding( 0053, COSName.getPDFName( "plus" ) );
+ addCharacterEncoding( 0161, COSName.getPDFName( "q" ) );
+ addCharacterEncoding( 0077, COSName.getPDFName( "question" ) );
+ addCharacterEncoding( 0277, COSName.getPDFName( "questiondown" ) );
+ addCharacterEncoding( 0042, COSName.getPDFName( "quotedbl" ) );
+ addCharacterEncoding( 0271, COSName.getPDFName( "quotedblbase" ) );
+ addCharacterEncoding( 0252, COSName.getPDFName( "quotedblleft" ) );
+ addCharacterEncoding( 0272, COSName.getPDFName( "quotedblright" ) );
+ addCharacterEncoding( 0140, COSName.getPDFName( "quoteleft" ) );
+ addCharacterEncoding( 0047, COSName.getPDFName( "quoteright" ) );
+ addCharacterEncoding( 0270, COSName.getPDFName( "quotesinglbase" ) );
+ addCharacterEncoding( 0251, COSName.getPDFName( "quotesingle" ) );
+ addCharacterEncoding( 0162, COSName.getPDFName( "r" ) );
+ addCharacterEncoding( 0312, COSName.getPDFName( "ring" ) );
+ addCharacterEncoding( 0163, COSName.getPDFName( "s" ) );
+ addCharacterEncoding( 0247, COSName.getPDFName( "section" ) );
+ addCharacterEncoding( 0073, COSName.getPDFName( "semicolon" ) );
+ addCharacterEncoding( 0067, COSName.getPDFName( "seven" ) );
+ addCharacterEncoding( 0066, COSName.getPDFName( "six" ) );
+ addCharacterEncoding( 0057, COSName.getPDFName( "slash" ) );
+ addCharacterEncoding( 0040, COSName.getPDFName( "space" ) );
+ addCharacterEncoding( 0243, COSName.getPDFName( "sterling" ) );
+ addCharacterEncoding( 0164, COSName.getPDFName( "t" ) );
+ addCharacterEncoding( 0063, COSName.getPDFName( "three" ) );
+ addCharacterEncoding( 0304, COSName.getPDFName( "tilde" ) );
+ addCharacterEncoding( 0062, COSName.getPDFName( "two" ) );
+ addCharacterEncoding( 0165, COSName.getPDFName( "u" ) );
+ addCharacterEncoding( 0137, COSName.getPDFName( "underscore" ) );
+ addCharacterEncoding( 0166, COSName.getPDFName( "v" ) );
+ addCharacterEncoding( 0167, COSName.getPDFName( "w" ) );
+ addCharacterEncoding( 0170, COSName.getPDFName( "x" ) );
+ addCharacterEncoding( 0171, COSName.getPDFName( "y" ) );
+ addCharacterEncoding( 0245, COSName.getPDFName( "yen" ) );
+ addCharacterEncoding( 0172, COSName.getPDFName( "z" ) );
+ addCharacterEncoding( 0060, COSName.getPDFName( "zero" ) );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return COSName.STANDARD_ENCODING;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/WinAnsiEncoding.java b/src/main/java/org/pdfbox/encoding/WinAnsiEncoding.java
new file mode 100644
index 0000000..dcbe872
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/WinAnsiEncoding.java
@@ -0,0 +1,281 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encoding;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This the win ansi encoding.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.9 $
+ */
+public class WinAnsiEncoding extends Encoding
+{
+ /**
+ * Constructor.
+ */
+ public WinAnsiEncoding()
+ {
+ addCharacterEncoding( 0101, COSName.getPDFName( "A" ) );
+ addCharacterEncoding( 0306, COSName.getPDFName( "AE" ) );
+ addCharacterEncoding( 0301, COSName.getPDFName( "Aacute" ) );
+ addCharacterEncoding( 0302, COSName.getPDFName( "Acircumflex" ) );
+ addCharacterEncoding( 0304, COSName.getPDFName( "Adieresis" ) );
+ addCharacterEncoding( 0300, COSName.getPDFName( "Agrave" ) );
+ addCharacterEncoding( 0305, COSName.getPDFName( "Aring" ) );
+ addCharacterEncoding( 0303, COSName.getPDFName( "Atilde" ) );
+ addCharacterEncoding( 0102, COSName.getPDFName( "B" ) );
+ addCharacterEncoding( 0103, COSName.getPDFName( "C" ) );
+ addCharacterEncoding( 0307, COSName.getPDFName( "Ccedilla" ) );
+ addCharacterEncoding( 0104, COSName.getPDFName( "D" ) );
+ addCharacterEncoding( 0105, COSName.getPDFName( "E" ) );
+ addCharacterEncoding( 0311, COSName.getPDFName( "Eacute" ) );
+ addCharacterEncoding( 0312, COSName.getPDFName( "Ecircumflex" ) );
+ addCharacterEncoding( 0313, COSName.getPDFName( "Edieresis" ) );
+ addCharacterEncoding( 0310, COSName.getPDFName( "Egrave" ) );
+ addCharacterEncoding( 0320, COSName.getPDFName( "Eth" ) );
+ addCharacterEncoding( 0200, COSName.getPDFName( "Euro" ) );
+ addCharacterEncoding( 0106, COSName.getPDFName( "F" ) );
+ addCharacterEncoding( 0107, COSName.getPDFName( "G" ) );
+ addCharacterEncoding( 0110, COSName.getPDFName( "H" ) );
+ addCharacterEncoding( 0111, COSName.getPDFName( "I" ) );
+ addCharacterEncoding( 0315, COSName.getPDFName( "Iacute" ) );
+ addCharacterEncoding( 0316, COSName.getPDFName( "Icircumflex" ) );
+ addCharacterEncoding( 0317, COSName.getPDFName( "Idieresis" ) );
+ addCharacterEncoding( 0314, COSName.getPDFName( "Igrave" ) );
+ addCharacterEncoding( 0112, COSName.getPDFName( "J" ) );
+ addCharacterEncoding( 0113, COSName.getPDFName( "K" ) );
+ addCharacterEncoding( 0114, COSName.getPDFName( "L" ) );
+ addCharacterEncoding( 0115, COSName.getPDFName( "M" ) );
+ addCharacterEncoding( 0116, COSName.getPDFName( "N" ) );
+ addCharacterEncoding( 0321, COSName.getPDFName( "Ntilde" ) );
+ addCharacterEncoding( 0117, COSName.getPDFName( "O" ) );
+ addCharacterEncoding( 0214, COSName.getPDFName( "OE" ) );
+ addCharacterEncoding( 0323, COSName.getPDFName( "Oacute" ) );
+ addCharacterEncoding( 0324, COSName.getPDFName( "Ocircumflex" ) );
+ addCharacterEncoding( 0326, COSName.getPDFName( "Odieresis" ) );
+ addCharacterEncoding( 0322, COSName.getPDFName( "Ograve" ) );
+ addCharacterEncoding( 0330, COSName.getPDFName( "Oslash" ) );
+ addCharacterEncoding( 0325, COSName.getPDFName( "Otilde" ) );
+ addCharacterEncoding( 0120, COSName.getPDFName( "P" ) );
+ addCharacterEncoding( 0121, COSName.getPDFName( "Q" ) );
+ addCharacterEncoding( 0122, COSName.getPDFName( "R" ) );
+ addCharacterEncoding( 0123, COSName.getPDFName( "S" ) );
+ addCharacterEncoding( 0212, COSName.getPDFName( "Scaron" ) );
+ addCharacterEncoding( 0124, COSName.getPDFName( "T" ) );
+ addCharacterEncoding( 0336, COSName.getPDFName( "Thorn" ) );
+ addCharacterEncoding( 0125, COSName.getPDFName( "U" ) );
+ addCharacterEncoding( 0332, COSName.getPDFName( "Uacute" ) );
+ addCharacterEncoding( 0333, COSName.getPDFName( "Ucircumflex" ) );
+ addCharacterEncoding( 0334, COSName.getPDFName( "Udieresis" ) );
+ addCharacterEncoding( 0331, COSName.getPDFName( "Ugrave" ) );
+ addCharacterEncoding( 0126, COSName.getPDFName( "V" ) );
+ addCharacterEncoding( 0127, COSName.getPDFName( "W" ) );
+ addCharacterEncoding( 0130, COSName.getPDFName( "X" ) );
+ addCharacterEncoding( 0131, COSName.getPDFName( "Y" ) );
+ addCharacterEncoding( 0335, COSName.getPDFName( "Yacute" ) );
+ addCharacterEncoding( 0237, COSName.getPDFName( "Ydieresis" ) );
+ addCharacterEncoding( 0132, COSName.getPDFName( "Z" ) );
+ addCharacterEncoding( 0216, COSName.getPDFName( "Zcaron" ) );
+ addCharacterEncoding( 0141, COSName.getPDFName( "a" ) );
+ addCharacterEncoding( 0341, COSName.getPDFName( "aacute" ) );
+ addCharacterEncoding( 0342, COSName.getPDFName( "acircumflex" ) );
+ addCharacterEncoding( 0264, COSName.getPDFName( "acute" ) );
+ addCharacterEncoding( 0344, COSName.getPDFName( "adieresis" ) );
+ addCharacterEncoding( 0346, COSName.getPDFName( "ae" ) );
+ addCharacterEncoding( 0340, COSName.getPDFName( "agrave" ) );
+ addCharacterEncoding( 046, COSName.getPDFName( "ampersand" ) );
+ addCharacterEncoding( 0345, COSName.getPDFName( "aring" ) );
+ addCharacterEncoding( 0136, COSName.getPDFName( "asciicircum" ) );
+ addCharacterEncoding( 0176, COSName.getPDFName( "asciitilde" ) );
+ addCharacterEncoding( 052, COSName.getPDFName( "asterisk" ) );
+ addCharacterEncoding( 0100, COSName.getPDFName( "at" ) );
+ addCharacterEncoding( 0343, COSName.getPDFName( "atilde" ) );
+ addCharacterEncoding( 0142, COSName.getPDFName( "b" ) );
+ addCharacterEncoding( 0134, COSName.getPDFName( "backslash" ) );
+ addCharacterEncoding( 0174, COSName.getPDFName( "bar" ) );
+ addCharacterEncoding( 0173, COSName.getPDFName( "braceleft" ) );
+ addCharacterEncoding( 0175, COSName.getPDFName( "braceright" ) );
+ addCharacterEncoding( 0133, COSName.getPDFName( "bracketleft" ) );
+ addCharacterEncoding( 0135, COSName.getPDFName( "bracketright" ) );
+ addCharacterEncoding( 0246, COSName.getPDFName( "brokenbar" ) );
+ addCharacterEncoding( 0225, COSName.getPDFName( "bullet" ) );
+ addCharacterEncoding( 0143, COSName.getPDFName( "c" ) );
+ addCharacterEncoding( 0347, COSName.getPDFName( "ccedilla" ) );
+ addCharacterEncoding( 0270, COSName.getPDFName( "cedilla" ) );
+ addCharacterEncoding( 0242, COSName.getPDFName( "cent" ) );
+ addCharacterEncoding( 0210, COSName.getPDFName( "circumflex" ) );
+ addCharacterEncoding( 072, COSName.getPDFName( "colon" ) );
+ addCharacterEncoding( 054, COSName.getPDFName( "comma" ) );
+ addCharacterEncoding( 0251, COSName.getPDFName( "copyright" ) );
+
+ /**
+ * Added because cweb.pdf uses circlecopyrt
+ */
+ addCharacterEncoding( 0251, COSName.getPDFName( "circlecopyrt" ) );
+ addCharacterEncoding( 0244, COSName.getPDFName( "currency" ) );
+ addCharacterEncoding( 0144, COSName.getPDFName( "d" ) );
+ addCharacterEncoding( 0206, COSName.getPDFName( "dagger" ) );
+ addCharacterEncoding( 0207, COSName.getPDFName( "daggerdbl" ) );
+ addCharacterEncoding( 0260, COSName.getPDFName( "degree" ) );
+ addCharacterEncoding( 0250, COSName.getPDFName( "dieresis" ) );
+ addCharacterEncoding( 0367, COSName.getPDFName( "divide" ) );
+ addCharacterEncoding( 044, COSName.getPDFName( "dollar" ) );
+ addCharacterEncoding( 0145, COSName.getPDFName( "e" ) );
+ addCharacterEncoding( 0351, COSName.getPDFName( "eacute" ) );
+ addCharacterEncoding( 0352, COSName.getPDFName( "ecircumflex" ) );
+ addCharacterEncoding( 0353, COSName.getPDFName( "edieresis" ) );
+ addCharacterEncoding( 0350, COSName.getPDFName( "egrave" ) );
+ addCharacterEncoding( 070, COSName.getPDFName( "eight" ) );
+ addCharacterEncoding( 0205, COSName.getPDFName( "ellipsis" ) );
+ addCharacterEncoding( 0227, COSName.getPDFName( "emdash" ) );
+ addCharacterEncoding( 0226, COSName.getPDFName( "endash" ) );
+ addCharacterEncoding( 075, COSName.getPDFName( "equal" ) );
+ addCharacterEncoding( 0360, COSName.getPDFName( "eth" ) );
+ addCharacterEncoding( 041, COSName.getPDFName( "exclam" ) );
+ addCharacterEncoding( 0241, COSName.getPDFName( "exclamdown" ) );
+ addCharacterEncoding( 0146, COSName.getPDFName( "f" ) );
+ addCharacterEncoding( 065, COSName.getPDFName( "five" ) );
+ addCharacterEncoding( 0203, COSName.getPDFName( "florin" ) );
+ addCharacterEncoding( 064, COSName.getPDFName( "four" ) );
+ addCharacterEncoding( 0147, COSName.getPDFName( "g" ) );
+ addCharacterEncoding( 0337, COSName.getPDFName( "germandbls" ) );
+ addCharacterEncoding( 0140, COSName.getPDFName( "grave" ) );
+ addCharacterEncoding( 076, COSName.getPDFName( "greater" ) );
+ addCharacterEncoding( 0253, COSName.getPDFName( "guillemotleft" ) );
+ addCharacterEncoding( 0273, COSName.getPDFName( "guillemotright" ) );
+ addCharacterEncoding( 0213, COSName.getPDFName( "guilsinglleft" ) );
+ addCharacterEncoding( 0233, COSName.getPDFName( "guilsinglright" ) );
+ addCharacterEncoding( 0150, COSName.getPDFName( "h" ) );
+ addCharacterEncoding( 055, COSName.getPDFName( "hyphen" ) );
+ addCharacterEncoding( 0151, COSName.getPDFName( "i" ) );
+ addCharacterEncoding( 0355, COSName.getPDFName( "iacute" ) );
+ addCharacterEncoding( 0356, COSName.getPDFName( "icircumflex" ) );
+ addCharacterEncoding( 0357, COSName.getPDFName( "idieresis" ) );
+ addCharacterEncoding( 0354, COSName.getPDFName( "igrave" ) );
+ addCharacterEncoding( 0152, COSName.getPDFName( "j" ) );
+ addCharacterEncoding( 0153, COSName.getPDFName( "k" ) );
+ addCharacterEncoding( 0154, COSName.getPDFName( "l" ) );
+ addCharacterEncoding( 074, COSName.getPDFName( "less" ) );
+ addCharacterEncoding( 0254, COSName.getPDFName( "logicalnot" ) );
+ addCharacterEncoding( 0155, COSName.getPDFName( "m" ) );
+ addCharacterEncoding( 0257, COSName.getPDFName( "macron" ) );
+ addCharacterEncoding( 0265, COSName.getPDFName( "mu" ) );
+ addCharacterEncoding( 0327, COSName.getPDFName( "multiply" ) );
+ addCharacterEncoding( 0156, COSName.getPDFName( "n" ) );
+ addCharacterEncoding( 071, COSName.getPDFName( "nine" ) );
+ addCharacterEncoding( 0361, COSName.getPDFName( "ntilde" ) );
+ addCharacterEncoding( 043, COSName.getPDFName( "numbersign" ) );
+ addCharacterEncoding( 0157, COSName.getPDFName( "o" ) );
+ addCharacterEncoding( 0363, COSName.getPDFName( "oacute" ) );
+ addCharacterEncoding( 0364, COSName.getPDFName( "ocircumflex" ) );
+ addCharacterEncoding( 0366, COSName.getPDFName( "odieresis" ) );
+ addCharacterEncoding( 0234, COSName.getPDFName( "oe" ) );
+ addCharacterEncoding( 0362, COSName.getPDFName( "ograve" ) );
+ addCharacterEncoding( 061, COSName.getPDFName( "one" ) );
+ addCharacterEncoding( 0275, COSName.getPDFName( "onehalf" ) );
+ addCharacterEncoding( 0274, COSName.getPDFName( "onequarter" ) );
+ addCharacterEncoding( 0271, COSName.getPDFName( "onesuperior" ) );
+ addCharacterEncoding( 0252, COSName.getPDFName( "ordfeminine" ) );
+ addCharacterEncoding( 0272, COSName.getPDFName( "ordmasculine" ) );
+ addCharacterEncoding( 0370, COSName.getPDFName( "oslash" ) );
+ addCharacterEncoding( 0365, COSName.getPDFName( "otilde" ) );
+ addCharacterEncoding( 0160, COSName.getPDFName( "p" ) );
+ addCharacterEncoding( 0266, COSName.getPDFName( "paragraph" ) );
+ addCharacterEncoding( 050, COSName.getPDFName( "parenleft" ) );
+ addCharacterEncoding( 051, COSName.getPDFName( "parenright" ) );
+ addCharacterEncoding( 045, COSName.getPDFName( "percent" ) );
+ addCharacterEncoding( 056, COSName.getPDFName( "period" ) );
+ addCharacterEncoding( 0267, COSName.getPDFName( "periodcentered" ) );
+ addCharacterEncoding( 0211, COSName.getPDFName( "perthousand" ) );
+ addCharacterEncoding( 053, COSName.getPDFName( "plus" ) );
+ addCharacterEncoding( 0261, COSName.getPDFName( "plusminus" ) );
+ addCharacterEncoding( 0161, COSName.getPDFName( "q" ) );
+ addCharacterEncoding( 077, COSName.getPDFName( "question" ) );
+ addCharacterEncoding( 0277, COSName.getPDFName( "questiondown" ) );
+ addCharacterEncoding( 042, COSName.getPDFName( "quotedbl" ) );
+ addCharacterEncoding( 0204, COSName.getPDFName( "quotedblbase" ) );
+ addCharacterEncoding( 0223, COSName.getPDFName( "quotedblleft" ) );
+ addCharacterEncoding( 0224, COSName.getPDFName( "quotedblright" ) );
+ addCharacterEncoding( 0221, COSName.getPDFName( "quoteleft" ) );
+ addCharacterEncoding( 0222, COSName.getPDFName( "quoteright" ) );
+ addCharacterEncoding( 0202, COSName.getPDFName( "quotesinglbase" ) );
+ addCharacterEncoding( 047, COSName.getPDFName( "quotesingle" ) );
+ addCharacterEncoding( 0162, COSName.getPDFName( "r" ) );
+ addCharacterEncoding( 0256, COSName.getPDFName( "registered" ) );
+ addCharacterEncoding( 0163, COSName.getPDFName( "s" ) );
+ addCharacterEncoding( 0232, COSName.getPDFName( "scaron" ) );
+ addCharacterEncoding( 0247, COSName.getPDFName( "section" ) );
+ addCharacterEncoding( 073, COSName.getPDFName( "semicolon" ) );
+ addCharacterEncoding( 067, COSName.getPDFName( "seven" ) );
+ addCharacterEncoding( 066, COSName.getPDFName( "six" ) );
+ addCharacterEncoding( 057, COSName.getPDFName( "slash" ) );
+ addCharacterEncoding( 040, COSName.getPDFName( "space" ) );
+ addCharacterEncoding( 0243, COSName.getPDFName( "sterling" ) );
+ addCharacterEncoding( 0164, COSName.getPDFName( "t" ) );
+ addCharacterEncoding( 0376, COSName.getPDFName( "thorn" ) );
+ addCharacterEncoding( 063, COSName.getPDFName( "three" ) );
+ addCharacterEncoding( 0276, COSName.getPDFName( "threequarters" ) );
+ addCharacterEncoding( 0263, COSName.getPDFName( "threesuperior" ) );
+ addCharacterEncoding( 0230, COSName.getPDFName( "tilde" ) );
+ addCharacterEncoding( 0231, COSName.getPDFName( "trademark" ) );
+ addCharacterEncoding( 062, COSName.getPDFName( "two" ) );
+ addCharacterEncoding( 0262, COSName.getPDFName( "twosuperior" ) );
+ addCharacterEncoding( 0165, COSName.getPDFName( "u" ) );
+ addCharacterEncoding( 0372, COSName.getPDFName( "uacute" ) );
+ addCharacterEncoding( 0373, COSName.getPDFName( "ucircumflex" ) );
+ addCharacterEncoding( 0374, COSName.getPDFName( "udieresis" ) );
+ addCharacterEncoding( 0371, COSName.getPDFName( "ugrave" ) );
+ addCharacterEncoding( 0137, COSName.getPDFName( "underscore" ) );
+ addCharacterEncoding( 0166, COSName.getPDFName( "v" ) );
+ addCharacterEncoding( 0167, COSName.getPDFName( "w" ) );
+ addCharacterEncoding( 0170, COSName.getPDFName( "x" ) );
+ addCharacterEncoding( 0171, COSName.getPDFName( "y" ) );
+ addCharacterEncoding( 0375, COSName.getPDFName( "yacute" ) );
+ addCharacterEncoding( 0377, COSName.getPDFName( "ydieresis" ) );
+ addCharacterEncoding( 0245, COSName.getPDFName( "yen" ) );
+ addCharacterEncoding( 0172, COSName.getPDFName( "z" ) );
+ addCharacterEncoding( 0236, COSName.getPDFName( "zcaron" ) );
+ addCharacterEncoding( 060, COSName.getPDFName( "zero" ) );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return COSName.WIN_ANSI_ENCODING;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encoding/package.html b/src/main/java/org/pdfbox/encoding/package.html
new file mode 100644
index 0000000..2fcc5c7
--- /dev/null
+++ b/src/main/java/org/pdfbox/encoding/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains the implementations for all of the encodings that are used in PDF documents.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/encryption/ARCFour.java b/src/main/java/org/pdfbox/encryption/ARCFour.java
new file mode 100644
index 0000000..6eeeb95
--- /dev/null
+++ b/src/main/java/org/pdfbox/encryption/ARCFour.java
@@ -0,0 +1,182 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encryption;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class is an implementation of the alleged RC4 algorithm.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.7 $
+ */
+public class ARCFour
+{
+ private int[] salt;
+ private int b;
+ private int c;
+
+ /**
+ * Constructor.
+ *
+ */
+ public ARCFour()
+ {
+ salt = new int[256];
+ }
+
+ /**
+ * This will reset the key to be used.
+ *
+ * @param key The RC4 key used during encryption.
+ */
+ public void setKey( byte[] key )
+ {
+ b = 0;
+ c = 0;
+
+ if(key.length < 1 || key.length > 32)
+ {
+ throw new IllegalArgumentException("number of bytes must be between 1 and 32");
+ }
+ for(int i = 0; i < salt.length; i++)
+ {
+ salt[i] = i;
+ }
+
+ int keyIndex = 0;
+ int saltIndex = 0;
+ for( int i = 0; i < salt.length; i++)
+ {
+ saltIndex = (fixByte(key[keyIndex]) + salt[i] + saltIndex) % 256;
+ swap( salt, i, saltIndex );
+ keyIndex = (keyIndex + 1) % key.length;
+ }
+
+ }
+
+ /**
+ * Thie will ensure that the value for a byte >=0.
+ *
+ * @param aByte The byte to test against.
+ *
+ * @return A value >=0 and < 256
+ */
+ private static final int fixByte( byte aByte )
+ {
+ return aByte < 0 ? 256 + aByte : aByte;
+ }
+
+ /**
+ * This will swap two values in an array.
+ *
+ * @param data The array to swap from.
+ * @param firstIndex The index of the first element to swap.
+ * @param secondIndex The index of the second element to swap.
+ */
+ private static final void swap( int[] data, int firstIndex, int secondIndex )
+ {
+ int tmp = data[ firstIndex ];
+ data[ firstIndex ] = data[ secondIndex ];
+ data[ secondIndex ] = tmp;
+ }
+
+ /**
+ * This will encrypt and write the next byte.
+ *
+ * @param aByte The byte to encrypt.
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing to the output stream.
+ */
+ public void write( byte aByte, OutputStream output ) throws IOException
+ {
+ b = (b + 1) % 256;
+ c = (salt[b] + c) % 256;
+ swap( salt, b, c );
+ int saltIndex = (salt[b] + salt[c]) % 256;
+ output.write(aByte ^ (byte)salt[saltIndex]);
+ }
+
+ /**
+ * This will encrypt and write the data.
+ *
+ * @param data The data to encrypt.
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing to the output stream.
+ */
+ public void write( byte[] data, OutputStream output ) throws IOException
+ {
+ for( int i = 0; i < data.length; i++ )
+ {
+ write( data[i], output );
+ }
+ }
+
+ /**
+ * This will encrypt and write the data.
+ *
+ * @param data The data to encrypt.
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing to the output stream.
+ */
+ public void write( InputStream data, OutputStream output ) throws IOException
+ {
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ while( (amountRead = data.read( buffer )) != -1 )
+ {
+ write( buffer, 0, amountRead, output );
+ }
+ }
+
+ /**
+ * This will encrypt and write the data.
+ *
+ * @param data The data to encrypt.
+ * @param offset The offset into the array to start reading data from.
+ * @param len The number of bytes to attempt to read.
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing to the output stream.
+ */
+ public void write( byte[] data, int offset, int len, OutputStream output) throws IOException
+ {
+ for( int i = offset; i < offset + len; i++ )
+ {
+ write( data[i], output );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encryption/DocumentEncryption.java b/src/main/java/org/pdfbox/encryption/DocumentEncryption.java
new file mode 100644
index 0000000..9ae6936
--- /dev/null
+++ b/src/main/java/org/pdfbox/encryption/DocumentEncryption.java
@@ -0,0 +1,427 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encryption;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.pdfbox.exceptions.CryptographyException;
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.encryption.PDStandardEncryption;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * This class will deal with encrypting/decrypting a document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.10 $
+ */
+public class DocumentEncryption
+{
+ private PDDocument pdDocument = null;
+ private COSDocument document = null;
+
+ private byte[] encryptionKey = null;
+ private PDFEncryption encryption = new PDFEncryption();
+
+ private Set objects = new HashSet();
+
+ /**
+ * A set that contains potential signature dictionaries. This is used
+ * because the Contents entry of the signature is not encrypted.
+ */
+ private Set potentialSignatures = new HashSet();
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document to decrypt.
+ */
+ public DocumentEncryption( PDDocument doc )
+ {
+ pdDocument = doc;
+ document = doc.getDocument();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document to decrypt.
+ */
+ public DocumentEncryption( COSDocument doc )
+ {
+ pdDocument = new PDDocument( doc );
+ document = doc;
+ }
+
+ /**
+ * This will encrypt the given document, given the owner password and user password.
+ * The encryption method used is the standard filter.
+ *
+ * @throws CryptographyException If an error occurs during encryption.
+ * @throws IOException If there is an error accessing the data.
+ */
+ public void initForEncryption()
+ throws CryptographyException, IOException
+ {
+ String ownerPassword = pdDocument.getOwnerPasswordForEncryption();
+ String userPassword = pdDocument.getUserPasswordForEncryption();
+ if( ownerPassword == null )
+ {
+ ownerPassword = "";
+ }
+ if( userPassword == null )
+ {
+ userPassword = "";
+ }
+ PDStandardEncryption encParameters = (PDStandardEncryption)pdDocument.getEncryptionDictionary();
+ int permissionInt = encParameters.getPermissions();
+ int revision = encParameters.getRevision();
+ int length = encParameters.getLength()/8;
+ COSArray idArray = document.getDocumentID();
+
+ //check if the document has an id yet. If it does not then
+ //generate one
+ if( idArray == null || idArray.size() < 2 )
+ {
+ idArray = new COSArray();
+ try
+ {
+ MessageDigest md = MessageDigest.getInstance( "MD5" );
+ BigInteger time = BigInteger.valueOf( System.currentTimeMillis() );
+ md.update( time.toByteArray() );
+ md.update( ownerPassword.getBytes() );
+ md.update( userPassword.getBytes() );
+ md.update( document.toString().getBytes() );
+ byte[] id = md.digest( this.toString().getBytes() );
+ COSString idString = new COSString();
+ idString.append( id );
+ idArray.add( idString );
+ idArray.add( idString );
+ document.setDocumentID( idArray );
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new CryptographyException( e );
+ }
+
+ }
+ COSString id = (COSString)idArray.getObject( 0 );
+ encryption = new PDFEncryption();
+
+ byte[] o = encryption.computeOwnerPassword(
+ ownerPassword.getBytes("ISO-8859-1"),
+ userPassword.getBytes("ISO-8859-1"), revision, length);
+
+ byte[] u = encryption.computeUserPassword(
+ userPassword.getBytes("ISO-8859-1"),
+ o, permissionInt, id.getBytes(), revision, length);
+
+ encryptionKey = encryption.computeEncryptedKey(
+ userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length);
+
+ encParameters.setOwnerKey( o );
+ encParameters.setUserKey( u );
+
+ document.setEncryptionDictionary( encParameters.getCOSDictionary() );
+ }
+
+
+
+ /**
+ * This will decrypt the document.
+ *
+ * @param password The password for the document.
+ *
+ * @throws CryptographyException If there is an error decrypting the document.
+ * @throws IOException If there is an error getting the stream data.
+ * @throws InvalidPasswordException If the password is not a user or owner password.
+ */
+ public void decryptDocument( String password )
+ throws CryptographyException, IOException, InvalidPasswordException
+ {
+ if( password == null )
+ {
+ password = "";
+ }
+
+ PDStandardEncryption encParameters = (PDStandardEncryption)pdDocument.getEncryptionDictionary();
+
+
+ int permissions = encParameters.getPermissions();
+ int revision = encParameters.getRevision();
+ int length = encParameters.getLength()/8;
+
+ COSString id = (COSString)document.getDocumentID().getObject( 0 );
+ byte[] u = encParameters.getUserKey();
+ byte[] o = encParameters.getOwnerKey();
+
+ boolean isUserPassword =
+ encryption.isUserPassword( password.getBytes(), u,
+ o, permissions, id.getBytes(), revision, length );
+ boolean isOwnerPassword =
+ encryption.isOwnerPassword( password.getBytes(), u,
+ o, permissions, id.getBytes(), revision, length );
+
+ if( isUserPassword )
+ {
+ encryptionKey =
+ encryption.computeEncryptedKey(
+ password.getBytes(), o,
+ permissions, id.getBytes(), revision, length );
+ }
+ else if( isOwnerPassword )
+ {
+ byte[] computedUserPassword =
+ encryption.getUserPassword(
+ password.getBytes(),
+ o,
+ revision,
+ length );
+ encryptionKey =
+ encryption.computeEncryptedKey(
+ computedUserPassword, o,
+ permissions, id.getBytes(), revision, length );
+ }
+ else
+ {
+ throw new InvalidPasswordException( "Error: The supplied password does not match " +
+ "either the owner or user password in the document." );
+ }
+
+ COSDictionary trailer = document.getTrailer();
+ COSArray fields = (COSArray)trailer.getObjectFromPath( "Root/AcroForm/Fields" );
+
+ //We need to collect all the signature dictionaries, for some
+ //reason the 'Contents' entry of signatures is not really encrypted
+ if( fields != null )
+ {
+ for( int i=0; i<fields.size(); i++ )
+ {
+ COSDictionary field = (COSDictionary)fields.getObject( i );
+ addDictionaryAndSubDictionary( potentialSignatures, field );
+ }
+ }
+
+ List allObjects = document.getObjects();
+ Iterator objectIter = allObjects.iterator();
+ while( objectIter.hasNext() )
+ {
+ decryptObject( (COSObject)objectIter.next() );
+ }
+ document.setEncryptionDictionary( null );
+ }
+
+ private void addDictionaryAndSubDictionary( Set set, COSDictionary dic )
+ {
+ set.add( dic );
+ COSArray kids = (COSArray)dic.getDictionaryObject( "Kids" );
+ for( int i=0; kids != null && i<kids.size(); i++ )
+ {
+ addDictionaryAndSubDictionary( set, (COSDictionary)kids.getObject( i ) );
+ }
+ COSBase value = dic.getDictionaryObject( "V" );
+ if( value instanceof COSDictionary )
+ {
+ addDictionaryAndSubDictionary( set, (COSDictionary)value );
+ }
+ }
+
+ /**
+ * This will decrypt an object in the document.
+ *
+ * @param object The object to decrypt.
+ *
+ * @throws CryptographyException If there is an error decrypting the stream.
+ * @throws IOException If there is an error getting the stream data.
+ */
+ private void decryptObject( COSObject object )
+ throws CryptographyException, IOException
+ {
+ long objNum = object.getObjectNumber().intValue();
+ long genNum = object.getGenerationNumber().intValue();
+ COSBase base = object.getObject();
+ decrypt( base, objNum, genNum );
+ }
+
+ /**
+ * This will dispatch to the correct method.
+ *
+ * @param obj The object to decrypt.
+ * @param objNum The object number.
+ * @param genNum The object generation Number.
+ *
+ * @throws CryptographyException If there is an error decrypting the stream.
+ * @throws IOException If there is an error getting the stream data.
+ */
+ private void decrypt( Object obj, long objNum, long genNum )
+ throws CryptographyException, IOException
+ {
+ if( !objects.contains( obj ) )
+ {
+ objects.add( obj );
+
+ if( obj instanceof COSString )
+ {
+ decryptString( (COSString)obj, objNum, genNum );
+ }
+ else if( obj instanceof COSStream )
+ {
+ decryptStream( (COSStream)obj, objNum, genNum );
+ }
+ else if( obj instanceof COSDictionary )
+ {
+ decryptDictionary( (COSDictionary)obj, objNum, genNum );
+ }
+ else if( obj instanceof COSArray )
+ {
+ decryptArray( (COSArray)obj, objNum, genNum );
+ }
+ }
+ }
+
+ /**
+ * This will decrypt a stream.
+ *
+ * @param stream The stream to decrypt.
+ * @param objNum The object number.
+ * @param genNum The object generation number.
+ *
+ * @throws CryptographyException If there is an error getting the stream.
+ * @throws IOException If there is an error getting the stream data.
+ */
+ public void decryptStream( COSStream stream, long objNum, long genNum )
+ throws CryptographyException, IOException
+ {
+ decryptDictionary( stream, objNum, genNum );
+ InputStream encryptedStream = stream.getFilteredStream();
+ encryption.encryptData( objNum,
+ genNum,
+ encryptionKey,
+ encryptedStream,
+ stream.createFilteredStream() );
+ }
+
+ /**
+ * This will decrypt a dictionary.
+ *
+ * @param dictionary The dictionary to decrypt.
+ * @param objNum The object number.
+ * @param genNum The object generation number.
+ *
+ * @throws CryptographyException If there is an error decrypting the document.
+ * @throws IOException If there is an error creating a new string.
+ */
+ private void decryptDictionary( COSDictionary dictionary, long objNum, long genNum )
+ throws CryptographyException, IOException
+ {
+ Iterator keys = dictionary.keyList().iterator();
+ while( keys.hasNext() )
+ {
+ COSName key = (COSName)keys.next();
+ Object value = dictionary.getItem( key );
+ //if we are a signature dictionary and contain a Contents entry then
+ //we don't decrypt it.
+ if( !(key.getName().equals( "Contents" ) &&
+ value instanceof COSString &&
+ potentialSignatures.contains( dictionary )))
+ {
+ decrypt( value, objNum, genNum );
+ }
+ }
+ }
+
+ /**
+ * This will decrypt a string.
+ *
+ * @param string the string to decrypt.
+ * @param objNum The object number.
+ * @param genNum The object generation number.
+ *
+ * @throws CryptographyException If an error occurs during decryption.
+ * @throws IOException If an error occurs writing the new string.
+ */
+ public void decryptString( COSString string, long objNum, long genNum )
+ throws CryptographyException, IOException
+ {
+ ByteArrayInputStream data = new ByteArrayInputStream( string.getBytes() );
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ encryption.encryptData( objNum,
+ genNum,
+ encryptionKey,
+ data,
+ buffer );
+ string.reset();
+ string.append( buffer.toByteArray() );
+ }
+
+ /**
+ * This will decrypt an array.
+ *
+ * @param array The array to decrypt.
+ * @param objNum The object number.
+ * @param genNum The object generation number.
+ *
+ * @throws CryptographyException If an error occurs during decryption.
+ * @throws IOException If there is an error accessing the data.
+ */
+ private void decryptArray( COSArray array, long objNum, long genNum )
+ throws CryptographyException, IOException
+ {
+ for( int i=0; i<array.size(); i++ )
+ {
+ decrypt( array.get( i ), objNum, genNum );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encryption/PDFEncryption.java b/src/main/java/org/pdfbox/encryption/PDFEncryption.java
new file mode 100644
index 0000000..5bd3d64
--- /dev/null
+++ b/src/main/java/org/pdfbox/encryption/PDFEncryption.java
@@ -0,0 +1,599 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.encryption;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.pdfbox.exceptions.CryptographyException;
+
+/**
+ * This class will deal with PDF encryption algorithms.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.13 $
+ */
+public final class PDFEncryption
+{
+ private ARCFour rc4 = new ARCFour();
+ /**
+ * The encryption padding defined in the PDF 1.4 Spec algorithm 3.2.
+ */
+ public static final byte[] ENCRYPT_PADDING =
+ {
+ (byte)0x28, (byte)0xBF, (byte)0x4E, (byte)0x5E, (byte)0x4E,
+ (byte)0x75, (byte)0x8A, (byte)0x41, (byte)0x64, (byte)0x00,
+ (byte)0x4E, (byte)0x56, (byte)0xFF, (byte)0xFA, (byte)0x01,
+ (byte)0x08, (byte)0x2E, (byte)0x2E, (byte)0x00, (byte)0xB6,
+ (byte)0xD0, (byte)0x68, (byte)0x3E, (byte)0x80, (byte)0x2F,
+ (byte)0x0C, (byte)0xA9, (byte)0xFE, (byte)0x64, (byte)0x53,
+ (byte)0x69, (byte)0x7A
+ };
+
+ /**
+ * This will encrypt a piece of data.
+ *
+ * @param objectNumber The id for the object.
+ * @param genNumber The generation id for the object.
+ * @param key The key used to encrypt the data.
+ * @param data The data to encrypt/decrypt.
+ * @param output The stream to write to.
+ *
+ * @throws CryptographyException If there is an error encrypting the data.
+ * @throws IOException If there is an io error.
+ */
+ public final void encryptData(
+ long objectNumber,
+ long genNumber,
+ byte[] key,
+ InputStream data,
+ OutputStream output )
+ throws CryptographyException, IOException
+ {
+ byte[] newKey = new byte[ key.length + 5 ];
+ System.arraycopy( key, 0, newKey, 0, key.length );
+ //PDF 1.4 reference pg 73
+ //step 1
+ //we have the reference
+
+ //step 2
+ newKey[newKey.length -5] = (byte)(objectNumber & 0xff);
+ newKey[newKey.length -4] = (byte)((objectNumber >> 8) & 0xff);
+ newKey[newKey.length -3] = (byte)((objectNumber >> 16) & 0xff);
+ newKey[newKey.length -2] = (byte)(genNumber & 0xff);
+ newKey[newKey.length -1] = (byte)((genNumber >> 8) & 0xff);
+
+
+ //step 3
+ byte[] digestedKey = null;
+ try
+ {
+ MessageDigest md = MessageDigest.getInstance( "MD5" );
+ digestedKey = md.digest( newKey );
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new CryptographyException( e );
+ }
+
+ //step 4
+ int length = Math.min( newKey.length, 16 );
+ byte[] finalKey = new byte[ length ];
+ System.arraycopy( digestedKey, 0, finalKey, 0, length );
+
+ rc4.setKey( finalKey );
+ rc4.write( data, output );
+ output.flush();
+ }
+
+ /**
+ * This will get the user password from the owner password and the documents o value.
+ *
+ * @param ownerPassword The plaintext owner password.
+ * @param o The document's o entry.
+ * @param revision The document revision number.
+ * @param length The length of the encryption.
+ *
+ * @return The plaintext padded user password.
+ *
+ * @throws CryptographyException If there is an error getting the user password.
+ * @throws IOException If there is an error reading data.
+ */
+ public final byte[] getUserPassword(
+ byte[] ownerPassword,
+ byte[] o,
+ int revision,
+ long length )
+ throws CryptographyException, IOException
+ {
+ try
+ {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ //3.3 STEP 1
+ byte[] ownerPadded = truncateOrPad( ownerPassword );
+
+ //3.3 STEP 2
+ MessageDigest md = MessageDigest.getInstance( "MD5" );
+ md.update( ownerPadded );
+ byte[] digest = md.digest();
+
+ //3.3 STEP 3
+ if( revision == 3 || revision == 4 )
+ {
+ for( int i=0; i<50; i++ )
+ {
+ md.reset();
+ md.update( digest );
+ digest = md.digest();
+ }
+ }
+ if( revision == 2 && length != 5 )
+ {
+ throw new CryptographyException(
+ "Error: Expected length=5 actual=" + length );
+ }
+
+ //3.3 STEP 4
+ byte[] rc4Key = new byte[ (int)length ];
+ System.arraycopy( digest, 0, rc4Key, 0, (int)length );
+
+ //3.7 step 2
+ if( revision == 2 )
+ {
+ rc4.setKey( rc4Key );
+ rc4.write( o, result );
+ }
+ else if( revision == 3 || revision == 4)
+ {
+ /**
+ byte[] iterationKey = new byte[ rc4Key.length ];
+ byte[] dataToEncrypt = o;
+ for( int i=19; i>=0; i-- )
+ {
+ System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length );
+ for( int j=0; j< iterationKey.length; j++ )
+ {
+ iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i);
+ }
+ rc4.setKey( iterationKey );
+ rc4.write( dataToEncrypt, result );
+ dataToEncrypt = result.toByteArray();
+ result.reset();
+ }
+ result.write( dataToEncrypt, 0, dataToEncrypt.length );
+ */
+ byte[] iterationKey = new byte[ rc4Key.length ];
+
+
+ byte[] otemp = new byte[ o.length ]; //sm
+ System.arraycopy( o, 0, otemp, 0, o.length ); //sm
+ rc4.write( o, result);//sm
+
+ for( int i=19; i>=0; i-- )
+ {
+ System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length );
+ for( int j=0; j< iterationKey.length; j++ )
+ {
+ iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i);
+ }
+ rc4.setKey( iterationKey );
+ result.reset(); //sm
+ rc4.write( otemp, result ); //sm
+ otemp = result.toByteArray(); //sm
+ }
+ }
+
+
+ return result.toByteArray();
+
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new CryptographyException( e );
+ }
+ }
+
+ /**
+ * This will tell if this is the owner password or not.
+ *
+ * @param ownerPassword The plaintext owner password.
+ * @param u The U value from the PDF Document.
+ * @param o The owner password hash.
+ * @param permissions The document permissions.
+ * @param id The document id.
+ * @param revision The revision of the encryption.
+ * @param length The length of the encryption key.
+ *
+ * @return true if the owner password matches the one from the document.
+ *
+ * @throws CryptographyException If there is an error while executing crypt functions.
+ * @throws IOException If there is an error while checking owner password.
+ */
+ public final boolean isOwnerPassword(
+ byte[] ownerPassword,
+ byte[] u,
+ byte[] o,
+ int permissions,
+ byte[] id,
+ int revision,
+ int length)
+ throws CryptographyException, IOException
+ {
+ byte[] userPassword = getUserPassword( ownerPassword, o, revision, length );
+ return isUserPassword( userPassword, u, o, permissions, id, revision, length );
+ }
+
+ /**
+ * This will tell if this is a valid user password.
+ *
+ * Algorithm 3.6 pg 80
+ *
+ * @param password The password to test.
+ * @param u The U value from the PDF Document.
+ * @param o The owner password hash.
+ * @param permissions The document permissions.
+ * @param id The document id.
+ * @param revision The revision of the encryption.
+ * @param length The length of the encryption key.
+ *
+ * @return true If this is the correct user password.
+ *
+ * @throws CryptographyException If there is an error computing the value.
+ * @throws IOException If there is an IO error while computing the owners password.
+ */
+ public final boolean isUserPassword(
+ byte[] password,
+ byte[] u,
+ byte[] o,
+ int permissions,
+ byte[] id,
+ int revision,
+ int length)
+ throws CryptographyException, IOException
+ {
+ boolean matches = false;
+ //STEP 1
+ byte[] computedValue = computeUserPassword( password, o, permissions, id, revision, length );
+ if( revision == 2 )
+ {
+ //STEP 2
+ matches = arraysEqual( u, computedValue );
+ }
+ else if( revision == 3 || revision == 4 )
+ {
+ //STEP 2
+ matches = arraysEqual( u, computedValue, 16 );
+ }
+ return matches;
+ }
+
+ /**
+ * This will compare two byte[] for equality for count number of bytes.
+ *
+ * @param first The first byte array.
+ * @param second The second byte array.
+ * @param count The number of bytes to compare.
+ *
+ * @return true If the arrays contain the exact same data.
+ */
+ private final boolean arraysEqual( byte[] first, byte[] second, int count )
+ {
+ boolean equal = first.length >= count && second.length >= count;
+ for( int i=0; i<count && equal; i++ )
+ {
+ equal = first[i] == second[i];
+ }
+ return equal;
+ }
+
+ /**
+ * This will compare two byte[] for equality.
+ *
+ * @param first The first byte array.
+ * @param second The second byte array.
+ *
+ * @return true If the arrays contain the exact same data.
+ */
+ private final boolean arraysEqual( byte[] first, byte[] second )
+ {
+ boolean equal = first.length == second.length;
+ for( int i=0; i<first.length && equal; i++ )
+ {
+ equal = first[i] == second[i];
+ }
+ return equal;
+ }
+
+ /**
+ * This will compute the user password hash.
+ *
+ * @param password The plain text password.
+ * @param o The owner password hash.
+ * @param permissions The document permissions.
+ * @param id The document id.
+ * @param revision The revision of the encryption.
+ * @param length The length of the encryption key.
+ *
+ * @return The user password.
+ *
+ * @throws CryptographyException If there is an error computing the user password.
+ * @throws IOException If there is an IO error.
+ */
+ public final byte[] computeUserPassword(
+ byte[] password,
+ byte[] o,
+ int permissions,
+ byte[] id,
+ int revision,
+ int length )
+ throws CryptographyException, IOException
+ {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ //STEP 1
+ byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, revision, length );
+
+ if( revision == 2 )
+ {
+ //STEP 2
+ rc4.setKey( encryptionKey );
+ rc4.write( ENCRYPT_PADDING, result );
+ }
+ else if( revision == 3 || revision == 4 )
+ {
+ try
+ {
+ //STEP 2
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ //md.update( truncateOrPad( password ) );
+ md.update( ENCRYPT_PADDING );
+
+ //STEP 3
+ md.update( id );
+ result.write( md.digest() );
+
+ //STEP 4 and 5
+ byte[] iterationKey = new byte[ encryptionKey.length ];
+ for( int i=0; i<20; i++ )
+ {
+ System.arraycopy( encryptionKey, 0, iterationKey, 0, iterationKey.length );
+ for( int j=0; j< iterationKey.length; j++ )
+ {
+ iterationKey[j] = (byte)(iterationKey[j] ^ i);
+ }
+ rc4.setKey( iterationKey );
+ ByteArrayInputStream input = new ByteArrayInputStream( result.toByteArray() );
+ result.reset();
+ rc4.write( input, result );
+ }
+
+ //step 6
+ byte[] finalResult = new byte[32];
+ System.arraycopy( result.toByteArray(), 0, finalResult, 0, 16 );
+ System.arraycopy( ENCRYPT_PADDING, 0, finalResult, 16, 16 );
+ result.reset();
+ result.write( finalResult );
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new CryptographyException( e );
+ }
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * This will compute the encrypted key.
+ *
+ * @param password The password used to compute the encrypted key.
+ * @param o The owner password hash.
+ * @param permissions The permissions for the document.
+ * @param id The document id.
+ * @param revision The security revision.
+ * @param length The length of the encryption key.
+ *
+ * @return The encryption key.
+ *
+ * @throws CryptographyException If there is an error computing the key.
+ */
+ public final byte[] computeEncryptedKey(
+ byte[] password,
+ byte[] o,
+ int permissions,
+ byte[] id,
+ int revision,
+ int length )
+ throws CryptographyException
+ {
+ byte[] result = new byte[ length ];
+ try
+ {
+ //PDFReference 1.4 pg 78
+ //step1
+ byte[] padded = truncateOrPad( password );
+
+ //step 2
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update( padded );
+
+ //step 3
+ md.update( o );
+
+ //step 4
+ byte zero = (byte)(permissions >>> 0);
+ byte one = (byte)(permissions >>> 8);
+ byte two = (byte)(permissions >>> 16);
+ byte three = (byte)(permissions >>> 24);
+
+ md.update( zero );
+ md.update( one );
+ md.update( two );
+ md.update( three );
+
+ //step 5
+ md.update( id );
+ byte[] digest = md.digest();
+
+ //step 6
+ if( revision == 3 || revision == 4)
+ {
+ for( int i=0; i<50; i++ )
+ {
+ md.reset();
+ md.update( digest, 0, length );
+ digest = md.digest();
+ }
+ }
+
+ //step 7
+ if( revision == 2 && length != 5 )
+ {
+ throw new CryptographyException(
+ "Error: length should be 5 when revision is two actual=" + length );
+ }
+ System.arraycopy( digest, 0, result, 0, length );
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new CryptographyException( e );
+ }
+ return result;
+ }
+
+ /**
+ * This algorithm is taked from PDF Reference 1.4 Algorithm 3.3 Page 79.
+ *
+ * @param ownerPassword The plain owner password.
+ * @param userPassword The plain user password.
+ * @param revision The version of the security.
+ * @param length The length of the document.
+ *
+ * @return The computed owner password.
+ *
+ * @throws CryptographyException If there is an error computing O.
+ * @throws IOException If there is an error computing O.
+ */
+ public final byte[] computeOwnerPassword(
+ byte[] ownerPassword,
+ byte[] userPassword,
+ int revision,
+ int length )
+ throws CryptographyException, IOException
+ {
+ try
+ {
+ //STEP 1
+ byte[] ownerPadded = truncateOrPad( ownerPassword );
+
+ //STEP 2
+ MessageDigest md = MessageDigest.getInstance( "MD5" );
+ md.update( ownerPadded );
+ byte[] digest = md.digest();
+
+ //STEP 3
+ if( revision == 3 || revision == 4)
+ {
+ for( int i=0; i<50; i++ )
+ {
+ md.reset();
+ md.update( digest, 0, length );
+ digest = md.digest();
+ }
+ }
+ if( revision == 2 && length != 5 )
+ {
+ throw new CryptographyException(
+ "Error: Expected length=5 actual=" + length );
+ }
+
+ //STEP 4
+ byte[] rc4Key = new byte[ length ];
+ System.arraycopy( digest, 0, rc4Key, 0, length );
+
+ //STEP 5
+ byte[] paddedUser = truncateOrPad( userPassword );
+
+
+ //STEP 6
+ rc4.setKey( rc4Key );
+ ByteArrayOutputStream crypted = new ByteArrayOutputStream();
+ rc4.write( new ByteArrayInputStream( paddedUser ), crypted );
+
+
+ //STEP 7
+ if( revision == 3 || revision == 4 )
+ {
+ byte[] iterationKey = new byte[ rc4Key.length ];
+ for( int i=1; i<20; i++ )
+ {
+ System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length );
+ for( int j=0; j< iterationKey.length; j++ )
+ {
+ iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i);
+ }
+ rc4.setKey( iterationKey );
+ ByteArrayInputStream input = new ByteArrayInputStream( crypted.toByteArray() );
+ crypted.reset();
+ rc4.write( input, crypted );
+ }
+ }
+
+ //STEP 8
+ return crypted.toByteArray();
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new CryptographyException( e.getMessage() );
+ }
+ }
+
+ /**
+ * This will take the password and truncate or pad it as necessary.
+ *
+ * @param password The password to pad or truncate.
+ *
+ * @return The padded or truncated password.
+ */
+ private final byte[] truncateOrPad( byte[] password )
+ {
+ byte[] padded = new byte[ ENCRYPT_PADDING.length ];
+ int bytesBeforePad = Math.min( password.length, padded.length );
+ System.arraycopy( password, 0, padded, 0, bytesBeforePad );
+ System.arraycopy( ENCRYPT_PADDING, 0, padded, bytesBeforePad, ENCRYPT_PADDING.length-bytesBeforePad );
+ return padded;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/encryption/package.html b/src/main/java/org/pdfbox/encryption/package.html
new file mode 100644
index 0000000..62bc982
--- /dev/null
+++ b/src/main/java/org/pdfbox/encryption/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These classes deal with encryption algorithms that are used in the PDF Document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/examples/AbstractExample.java b/src/main/java/org/pdfbox/examples/AbstractExample.java
new file mode 100644
index 0000000..1350b73
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/AbstractExample.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.pdfbox.cos.COSDocument;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+/**
+ * A simple class which has some methods used by all examples.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public abstract class AbstractExample
+{
+ /**
+ * Close the stream.
+ *
+ * @param stream The stream to close.
+ *
+ * @throws IOException If there is an error closing the stream.
+ */
+ public void close( InputStream stream ) throws IOException
+ {
+ if( stream != null )
+ {
+ stream.close();
+ }
+ }
+
+ /**
+ * Close the stream.
+ *
+ * @param stream The stream to close.
+ *
+ * @throws IOException If there is an error closing the stream.
+ */
+ public void close( OutputStream stream ) throws IOException
+ {
+ if( stream != null )
+ {
+ stream.close();
+ }
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( COSDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * Close the document.
+ *
+ * @param doc The doc to close.
+ *
+ * @throws IOException If there is an error closing the document.
+ */
+ public void close( PDDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * Close the writer.
+ *
+ * @param writer The writer to close.
+ *
+ * @throws IOException If there is an error closing the writer.
+ */
+ public static void close( COSWriter writer ) throws IOException
+ {
+ if( writer != null )
+ {
+ writer.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/fdf/PrintFields.java b/src/main/java/org/pdfbox/examples/fdf/PrintFields.java
new file mode 100644
index 0000000..189ba72
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/fdf/PrintFields.java
@@ -0,0 +1,163 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.fdf;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.pdfbox.pdmodel.interactive.form.PDField;
+
+import org.pdfbox.exceptions.CryptographyException;
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+
+/**
+ * This example will take a PDF document and print all the fields from the file.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.15 $
+ */
+public class PrintFields
+{
+
+ /**
+ * This will print all the fields from the document.
+ *
+ * @param pdfDocument The PDF to get the fields from.
+ *
+ * @throws IOException If there is an error getting the fields.
+ */
+ public void printFields( PDDocument pdfDocument ) throws IOException
+ {
+ PDDocumentCatalog docCatalog = pdfDocument.getDocumentCatalog();
+ PDAcroForm acroForm = docCatalog.getAcroForm();
+ List fields = acroForm.getFields();
+ Iterator fieldsIter = fields.iterator();
+
+ System.out.println(new Integer(fields.size()).toString() + " top-level fields were found on the form");
+
+ while( fieldsIter.hasNext())
+ {
+ PDField field = (PDField)fieldsIter.next();
+ processField(field, "|--", field.getPartialName());
+ }
+ }
+
+ private void processField(PDField field, String sLevel, String sParent) throws IOException
+ {
+ List kids = field.getKids();
+ if(kids != null)
+ {
+ Iterator kidsIter = kids.iterator();
+ if(!sParent.equals(field.getPartialName()))
+ {
+ sParent = sParent + "." + field.getPartialName();
+ }
+ System.out.println(sLevel + sParent);
+ //System.out.println(sParent + " is of type " + field.getClass().getName());
+ while(kidsIter.hasNext())
+ {
+ Object pdfObj = kidsIter.next();
+ if(pdfObj instanceof PDField)
+ {
+ PDField kid = (PDField)pdfObj;
+ processField(kid, "| " + sLevel, sParent);
+ }
+ }
+ }
+ else
+ {
+ String outputString = sLevel + sParent + "." + field.getPartialName() + " = " + field.getValue() +
+ ", type=" + field.getClass().getName();
+
+ System.out.println(outputString);
+ }
+ }
+
+ /**
+ * This will read a PDF file and print out the form elements.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ *
+ * @throws IOException If there is an error importing the FDF document.
+ * @throws CryptographyException If there is an error decrypting the document.
+ */
+ public static void main(String[] args) throws IOException, CryptographyException
+ {
+ PDDocument pdf = null;
+ try
+ {
+ if( args.length != 1 )
+ {
+ usage();
+ }
+ else
+ {
+ pdf = PDDocument.load( args[0] );
+ PrintFields exporter = new PrintFields();
+ if( pdf.isEncrypted() )
+ {
+ try
+ {
+ pdf.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: The document is encrypted." );
+ usage();
+ }
+ }
+ exporter.printFields( pdf );
+ }
+ }
+ finally
+ {
+ if( pdf != null )
+ {
+ pdf.close();
+ }
+ }
+ }
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: org.pdfbox.examples.fdf.PrintFields <pdf-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/fdf/SetField.java b/src/main/java/org/pdfbox/examples/fdf/SetField.java
new file mode 100644
index 0000000..904feeb
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/fdf/SetField.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.fdf;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.pdfbox.pdmodel.interactive.form.PDField;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.examples.AbstractExample;
+
+/**
+ * This example will take a PDF document and set a FDF field in it.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class SetField extends AbstractExample
+{
+
+ /**
+ * This will set a single field in the document.
+ *
+ * @param pdfDocument The PDF to set the field in.
+ * @param name The name of the field to set.
+ * @param value The new value of the field.
+ *
+ * @throws IOException If there is an error setting the field.
+ */
+ public void setField( PDDocument pdfDocument, String name, String value ) throws IOException
+ {
+ PDDocumentCatalog docCatalog = pdfDocument.getDocumentCatalog();
+ PDAcroForm acroForm = docCatalog.getAcroForm();
+ PDField field = acroForm.getField( name );
+ if( field != null )
+ {
+ field.setValue( value );
+ }
+ else
+ {
+ System.err.println( "No field found with name:" + name );
+ }
+
+ }
+
+ /**
+ * This will read a PDF file and set a field and then write it the pdf out again.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ *
+ * @throws IOException If there is an error importing the FDF document.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public static void main(String[] args) throws IOException, COSVisitorException
+ {
+ SetField setter = new SetField();
+ setter.setField( args );
+ }
+
+ private void setField( String[] args ) throws IOException, COSVisitorException
+ {
+ PDDocument pdf = null;
+ try
+ {
+ if( args.length != 3 )
+ {
+ usage();
+ }
+ else
+ {
+ SetField example = new SetField();
+
+ pdf = PDDocument.load( args[0] );
+ example.setField( pdf, args[1], args[2] );
+ pdf.save( args[0] );
+ }
+ }
+ finally
+ {
+ if( pdf != null )
+ {
+ pdf.close();
+ }
+ }
+ }
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: org.pdfbox.examples.fdf.SetField <pdf-file> <field-name> <field-value>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/fdf/package.html b/src/main/java/org/pdfbox/examples/fdf/package.html
new file mode 100644
index 0000000..5c80aca
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/fdf/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These are examples that use the FDF features of a PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/examples/package.html b/src/main/java/org/pdfbox/examples/package.html
new file mode 100644
index 0000000..fe059eb
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The packages in this package will show how to use the PDFBox API.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/AddJavascript.java b/src/main/java/org/pdfbox/examples/pdmodel/AddJavascript.java
new file mode 100644
index 0000000..d04700e
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/AddJavascript.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionJavaScript;
+
+import java.io.IOException;
+
+/**
+ * This is an example of how to set some javascript in the document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class AddJavascript
+{
+ private AddJavascript()
+ {
+ //static class, should not be instantiated.
+ }
+
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( args[0] );
+ PDActionJavaScript javascript = new PDActionJavaScript(
+ "app.alert( {cMsg: 'PDFBox rocks!', nIcon: 3, nType: 0, cTitle: 'PDFBox Javascript example' } );");
+ document.getDocumentCatalog().setOpenAction( javascript );
+ if( document.isEncrypted() )
+ {
+ throw new IOException( "Encrypted documents are not supported for this example" );
+ }
+ document.save( args[1] );
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.AddJavascript <input-pdf> <output-pdf>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/AddMessageToEachPage.java b/src/main/java/org/pdfbox/examples/pdmodel/AddMessageToEachPage.java
new file mode 100644
index 0000000..efb6ec1
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/AddMessageToEachPage.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDType1Font;
+
+
+/**
+ * This is an example of how to add a message to every page
+ * in a pdf document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class AddMessageToEachPage
+{
+ /**
+ * Constructor.
+ */
+ public AddMessageToEachPage()
+ {
+ super();
+ }
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ * @param message The message to write in the file.
+ * @param outfile The resulting PDF.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt( String file, String message, String outfile ) throws IOException, COSVisitorException
+ {
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = PDDocument.load( file );
+
+ List allPages = doc.getDocumentCatalog().getAllPages();
+ PDFont font = PDType1Font.HELVETICA_BOLD;
+ float fontSize = 12.0f;
+
+ for( int i=0; i<allPages.size(); i++ )
+ {
+ PDPage page = (PDPage)allPages.get( i );
+ PDRectangle pageSize = page.findMediaBox();
+ float stringWidth = font.getStringWidth( message );
+ float centeredPosition = (pageSize.getWidth() - (stringWidth*fontSize)/1000f)/2f;
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);
+ contentStream.beginText();
+ contentStream.setFont( font, fontSize );
+ contentStream.moveTextPositionByAmount( centeredPosition, 30 );
+ contentStream.drawString( message );
+ contentStream.endText();
+ contentStream.close();
+ }
+
+
+ doc.save( outfile );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a hello world PDF document.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ AddMessageToEachPage app = new AddMessageToEachPage();
+ try
+ {
+ if( args.length != 3 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1], args[2] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <input-file> <Message> <output-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java b/src/main/java/org/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java
new file mode 100644
index 0000000..35080cd
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+import org.pdfbox.pdmodel.PDDocumentInformation;
+import org.pdfbox.pdmodel.common.PDMetadata;
+import org.pdfbox.util.DateConverter;
+
+import java.io.ByteArrayInputStream;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/**
+ * This is an example on how to add metadata to a document.
+ *
+ * Usage: java org.pdfbox.examples.pdmodel.AddMetadataToDocument &lt;input-pdf&gt; &lt;output-pdf&gt;
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class AddMetadataFromDocInfo
+{
+ private static final String PADDING =
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " ";
+
+
+
+ private AddMetadataFromDocInfo()
+ {
+ //utility class
+ }
+
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+
+ try
+ {
+ document = PDDocument.load( args[0] );
+ if( document.isEncrypted() )
+ {
+ System.err.println( "Error: Cannot add metadata to encrypted document." );
+ System.exit( 1 );
+ }
+ PDDocumentCatalog catalog = document.getDocumentCatalog();
+ PDDocumentInformation info = document.getDocumentInformation();
+
+ //Right now, PDFBox does not have any XMP library, so we will
+ //just consruct the XML by hand.
+ StringBuffer xmp= new StringBuffer();
+ xmp.append(
+ "<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>\n" +
+ "<?adobe-xap-filters esc=\"CRLF\"?>\n" +
+ "<x:xmpmeta>\n" +
+ " <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n" +
+ " <rdf:Description rdf:about='' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' " +
+ "pdf:Keywords='" + fixNull( info.getKeywords() ) + "' " +
+ "pdf:Producer='" + fixNull( info.getProducer() ) + "'></rdf:Description>\n" +
+ " <rdf:Description rdf:about='' xmlns:xap='http://ns.adobe.com/xap/1.0/' " +
+ "xap:ModifyDate='" + fixNull( info.getModificationDate() ) + "' " +
+ "xap:CreateDate='" + fixNull( info.getCreationDate() ) + "' " +
+ "xap:CreatorTool='" + fixNull( info.getCreator() ) + "' " +
+ "xap:MetadataDate='" + fixNull( new GregorianCalendar() )+ "'>\n" +
+ " </rdf:Description>\n" +
+ " <rdf:Description rdf:about='' xmlns:dc='http://purl.org/dc/elements/1.1/' " +
+ "dc:format='application/pdf'>\n" +
+ " <dc:title>\n" +
+ " <rdf:Alt>\n" +
+ " <rdf:li xml:lang='x-default'>" + fixNull( info.getTitle() ) +"</rdf:li>\n" +
+ " </rdf:Alt>\n" +
+ " </dc:title>\n" +
+ " <dc:creator>\n" +
+ " <rdf:Seq>\n" +
+ " <rdf:li>PDFBox.org</rdf:li>\n" +
+ " </rdf:Seq>\n" +
+ " </dc:creator>\n" +
+ " <dc:description>\n" +
+ " <rdf:Alt>\n" +
+ " <rdf:li xml:lang='x-default'>" + fixNull( info.getSubject() ) +"</rdf:li>\n" +
+ " </rdf:Alt>\n" +
+ " </dc:description>\n" +
+ " </rdf:Description>\n" +
+ " </rdf:RDF>\n" +
+ "</x:xmpmeta>\n" );
+
+ //xmp spec says we should put padding, so that the metadata can be appended to
+ //in place
+ xmp.append( PADDING );
+ xmp.append( PADDING );
+ xmp.append( PADDING );
+ xmp.append( "\n<?xpacket end='w'?>" );
+ ByteArrayInputStream mdInput = new ByteArrayInputStream( xmp.toString().getBytes() );
+ PDMetadata metadataStream = new PDMetadata(document, mdInput, false );
+ catalog.setMetadata( metadataStream );
+
+
+ document.save( args[1] );
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ private static String fixNull( String string )
+ {
+ return string == null ? "" : string;
+ }
+
+ private static String fixNull( Calendar cal )
+ {
+ return cal == null ? "" : DateConverter.toISO8601( cal );
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.AddMetadata <input-pdf> <output-pdf>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/CreateBlankPDF.java b/src/main/java/org/pdfbox/examples/pdmodel/CreateBlankPDF.java
new file mode 100644
index 0000000..6d942ac
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/CreateBlankPDF.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+/**
+ * This will create a blank PDF and write the contents to a file.
+ *
+ * usage: java org.pdfbox.examples.pdmodel.CreateBlankPDF &lt;outputfile.pdf&gt;
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class CreateBlankPDF
+{
+
+ /**
+ * This will create a blank PDF and write the contents to a file.
+ *
+ * @param file The name of the file to write to.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error while generating the document.
+ */
+ public void create( String file ) throws IOException, COSVisitorException
+ {
+ PDDocument document = null;
+ FileOutputStream output = null;
+ COSWriter writer = null;
+ try
+ {
+ document = new PDDocument();
+ //Every document requires at least one page, so we will add one
+ //blank page.
+ PDPage blankPage = new PDPage();
+ document.addPage( blankPage );
+ output = new FileOutputStream( file );
+ writer = new COSWriter( output );
+ writer.write( document.getDocument() );
+ }
+ finally
+ {
+ if( writer != null )
+ {
+ writer.close();
+ }
+ if( output != null )
+ {
+ output.close();
+ }
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a blank document.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws IOException If there is an error writing the document data.
+ * @throws COSVisitorException If there is an error generating the data.
+ */
+ public static void main( String[] args ) throws IOException, COSVisitorException
+ {
+ if( args.length != 1 )
+ {
+ usage();
+ }
+ else
+ {
+ CreateBlankPDF creator = new CreateBlankPDF();
+ creator.create( args[0] );
+ }
+ }
+
+ /**
+ * This will print the usage of this class.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: java org.pdfbox.examples.pdmodel.CreateBlankPDF <outputfile.pdf>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/CreateBookmarks.java b/src/main/java/org/pdfbox/examples/pdmodel/CreateBookmarks.java
new file mode 100644
index 0000000..88c09fb
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/CreateBookmarks.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
+
+import java.util.List;
+
+/**
+ * This is an example on how to add bookmarks to a PDF document. It simply
+ * adds 1 bookmark for every page.
+ *
+ * Usage: java org.pdfbox.examples.pdmodel.CreateBookmarks &lt;input-pdf&gt; &lt;output-pdf&gt;
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class CreateBookmarks
+{
+ private CreateBookmarks()
+ {
+ //utility class
+ }
+
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( args[0] );
+ if( document.isEncrypted() )
+ {
+ System.err.println( "Error: Cannot add bookmarks to encrypted document." );
+ System.exit( 1 );
+ }
+ PDDocumentOutline outline = new PDDocumentOutline();
+ document.getDocumentCatalog().setDocumentOutline( outline );
+ PDOutlineItem pagesOutline = new PDOutlineItem();
+ pagesOutline.setTitle( "All Pages" );
+ outline.appendChild( pagesOutline );
+ List pages = document.getDocumentCatalog().getAllPages();
+ for( int i=0; i<pages.size(); i++ )
+ {
+ PDPage page = (PDPage)pages.get( i );
+ PDPageFitWidthDestination dest = new PDPageFitWidthDestination();
+ dest.setPage( page );
+ PDOutlineItem bookmark = new PDOutlineItem();
+ bookmark.setDestination( dest );
+ bookmark.setTitle( "Page " + (i+1) );
+ pagesOutline.appendChild( bookmark );
+ }
+ pagesOutline.openNode();
+ outline.openNode();
+
+ document.save( args[1] );
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.CreateBookmarks <input-pdf> <output-pdf>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/EmbeddedFiles.java b/src/main/java/org/pdfbox/examples/pdmodel/EmbeddedFiles.java
new file mode 100644
index 0000000..86bc447
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/EmbeddedFiles.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentNameDictionary;
+import org.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
+import org.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDType1Font;
+
+
+/**
+ * This is an example that creates a simple document and embeds a file into it..
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class EmbeddedFiles
+{
+ /**
+ * Constructor.
+ */
+ public EmbeddedFiles()
+ {
+ super();
+ }
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt( String file) throws IOException, COSVisitorException
+ {
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = new PDDocument();
+
+ PDPage page = new PDPage();
+ doc.addPage( page );
+ PDFont font = PDType1Font.HELVETICA_BOLD;
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page);
+ contentStream.beginText();
+ contentStream.setFont( font, 12 );
+ contentStream.moveTextPositionByAmount( 100, 700 );
+ contentStream.drawString( "Go to Document->File Attachments to View Embedded Files" );
+ 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 fs = new PDComplexFileSpecification();
+ fs.setFile( "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();
+ ByteArrayInputStream fakeFile =
+ new ByteArrayInputStream( data );
+ PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile );
+ //now lets some of the optional parameters
+ ef.setSubtype( "test/plain" );
+ ef.setSize( data.length );
+ ef.setCreationDate( new GregorianCalendar() );
+ fs.setEmbeddedFile( ef );
+
+ //now add the entry to the embedded file tree and set in the document.
+ Map efMap = new HashMap();
+ efMap.put( "My first attachment", fs );
+ efTree.setNames( efMap );
+ PDDocumentNameDictionary names = new PDDocumentNameDictionary( doc.getDocumentCatalog() );
+ names.setEmbeddedFiles( efTree );
+ doc.getDocumentCatalog().setNames( names );
+
+
+ doc.save( file );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a hello world PDF document with an embedded file.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ EmbeddedFiles app = new EmbeddedFiles();
+ try
+ {
+ if( args.length != 1 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <output-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/HelloWorld.java b/src/main/java/org/pdfbox/examples/pdmodel/HelloWorld.java
new file mode 100644
index 0000000..9bceca7
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/HelloWorld.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDType1Font;
+
+
+/**
+ * This is an example that creates a simple document.
+ *
+ * The example is taken from the pdf file format specification.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class HelloWorld
+{
+ /**
+ * Constructor.
+ */
+ public HelloWorld()
+ {
+ super();
+ }
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ * @param message The message to write in the file.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt( String file, String message) throws IOException, COSVisitorException
+ {
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = new PDDocument();
+
+ PDPage page = new PDPage();
+ doc.addPage( page );
+ PDFont font = PDType1Font.HELVETICA_BOLD;
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page);
+ contentStream.beginText();
+ contentStream.setFont( font, 12 );
+ contentStream.moveTextPositionByAmount( 100, 700 );
+ contentStream.drawString( message );
+ contentStream.endText();
+ contentStream.close();
+ doc.save( file );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a hello world PDF document.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ HelloWorld app = new HelloWorld();
+ try
+ {
+ if( args.length != 2 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <output-file> <Message>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/HelloWorldTTF.java b/src/main/java/org/pdfbox/examples/pdmodel/HelloWorldTTF.java
new file mode 100644
index 0000000..16b1423
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/HelloWorldTTF.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+
+package org.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDTrueTypeFont;
+
+/**
+ * This is an example that creates a simple document
+ * with a ttf-font.
+ *
+ * @author <a href="mailto:m.g.n@gmx.de">Michael Niedermair</a>
+ * @version $Revision: 1.2 $
+ */
+public class HelloWorldTTF
+{
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ * @param message The message to write in the file.
+ * @param fontfile The ttf-font file.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt(final String file, final String message,
+ final String fontfile) throws IOException, COSVisitorException
+ {
+
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = new PDDocument();
+
+ PDPage page = new PDPage();
+ doc.addPage(page);
+ PDFont font = PDTrueTypeFont.loadTTF(doc, fontfile);
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc,
+ page);
+ contentStream.beginText();
+ contentStream.setFont(font, 12);
+ contentStream.moveTextPositionByAmount(100, 700);
+ contentStream.drawString(message);
+ contentStream.endText();
+ contentStream.close();
+ doc.save(file);
+ System.out.println(file + " created!");
+ }
+ finally
+ {
+ if (doc != null)
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a hello world PDF document
+ * with a ttf-font.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+
+ HelloWorldTTF app = new HelloWorldTTF();
+ try
+ {
+ if (args.length != 3)
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt(args[0], args[1], args[2]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println("usage: " + this.getClass().getName()
+ + " <output-file> <Message> <ttf-file>");
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java b/src/main/java/org/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java
new file mode 100644
index 0000000..2457a2e
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+
+package org.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDType1AfmPfbFont;
+
+/**
+ * This is an example that creates a simple document
+ * with a Type 1 font (afm + pfb).
+ *
+ * @author <a href="mailto:m.g.n@gmx.de">Michael Niedermair</a>
+ * @version $Revision: 1.2 $
+ */
+public class HelloWorldType1AfmPfb
+{
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ * @param message The message to write in the file.
+ * @param fontfile The ttf-font file.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt(final String file, final String message,
+ final String fontfile) throws IOException, COSVisitorException
+ {
+
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = new PDDocument();
+
+ PDPage page = new PDPage();
+ doc.addPage(page);
+ PDFont font = new PDType1AfmPfbFont(doc,fontfile);
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc,
+ page);
+ contentStream.beginText();
+ contentStream.setFont(font, 12);
+ contentStream.moveTextPositionByAmount(100, 700);
+ contentStream.drawString(message);
+ contentStream.endText();
+ contentStream.close();
+ doc.save(file);
+ System.out.println(file + " created!");
+ }
+ finally
+ {
+ if (doc != null)
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a hello world PDF document
+ * with a ttf-font.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+
+ HelloWorldType1AfmPfb app = new HelloWorldType1AfmPfb();
+ try
+ {
+ if (args.length != 3)
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt(args[0], args[1], args[2]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println("usage: " + this.getClass().getName()
+ + " <output-file> <Message> <afm-file>");
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/ImageToPDF.java b/src/main/java/org/pdfbox/examples/pdmodel/ImageToPDF.java
new file mode 100644
index 0000000..9faf79d
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/ImageToPDF.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+
+import org.pdfbox.pdmodel.graphics.xobject.PDCcitt;
+import org.pdfbox.pdmodel.graphics.xobject.PDJpeg;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+
+
+/**
+ * This is an example that creates a simple document.
+ *
+ * The example is taken from the pdf file format specification.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class ImageToPDF
+{
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ * @param image The filename of the image to put in the PDF.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void createPDFFromImage( String file, String image) throws IOException, COSVisitorException
+ {
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = new PDDocument();
+
+ PDPage page = new PDPage();
+ doc.addPage( page );
+
+ PDXObjectImage ximage = null;
+ if( image.toLowerCase().endsWith( ".jpg" ) )
+ {
+ ximage = new PDJpeg(doc, new FileInputStream( image ) );
+ }
+ else if (image.toLowerCase().endsWith(".tif") || image.toLowerCase().endsWith(".tiff"))
+ {
+ ximage = new PDCcitt(doc, new RandomAccessFile(new File(image),"r"));
+ }
+ else
+ {
+ //BufferedImage awtImage = ImageIO.read( new File( image ) );
+ //ximage = new PDPixelMap(doc, awtImage);
+ throw new IOException( "Image type not supported:" + image );
+ }
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page);
+
+ contentStream.drawImage( ximage, 20, 20 );
+
+ contentStream.close();
+ doc.save( file );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a PDF document with a single image on it.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ ImageToPDF app = new ImageToPDF();
+ try
+ {
+ if( args.length != 2 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.createPDFFromImage( args[0], args[1] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <output-file> <image>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/PrintBookmarks.java b/src/main/java/org/pdfbox/examples/pdmodel/PrintBookmarks.java
new file mode 100644
index 0000000..0a1a647
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/PrintBookmarks.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * This is an example on how to access the bookmarks that are part of a pdf document.
+ *
+ * Usage: java org.pdfbox.examples.pdmodel.PrintBookmarks &lt;input-pdf&gt;
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PrintBookmarks
+{
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 1 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ FileInputStream file = null;
+ try
+ {
+ file = new FileInputStream( args[0] );
+ PDFParser parser = new PDFParser( file );
+ parser.parse();
+ document = parser.getPDDocument();
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: Document is encrypted with a password." );
+ System.exit( 1 );
+ }
+ }
+ PrintBookmarks meta = new PrintBookmarks();
+ PDDocumentOutline outline = document.getDocumentCatalog().getDocumentOutline();
+ if( outline != null )
+ {
+ meta.printBookmark( outline, "" );
+ }
+ else
+ {
+ System.out.println( "This document does not contain any bookmarks" );
+ }
+ }
+ finally
+ {
+ if( file != null )
+ {
+ file.close();
+ }
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.PrintBookmarks <input-pdf>" );
+ }
+
+ /**
+ * This will print the documents bookmarks to System.out.
+ *
+ * @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
+ {
+ PDOutlineItem current = bookmark.getFirstChild();
+ while( current != null )
+ {
+ System.out.println( indentation + current.getTitle() );
+ printBookmark( current, indentation + " " );
+ current = current.getNextSibling();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/PrintDocumentMetaData.java b/src/main/java/org/pdfbox/examples/pdmodel/PrintDocumentMetaData.java
new file mode 100644
index 0000000..c096113
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/PrintDocumentMetaData.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentCatalog;
+import org.pdfbox.pdmodel.PDDocumentInformation;
+import org.pdfbox.pdmodel.common.PDMetadata;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import java.text.SimpleDateFormat;
+
+import java.util.Calendar;
+
+/**
+ * This is an example on how to get a documents metadata information.
+ *
+ * Usage: java org.pdfbox.examples.pdmodel.PrintDocumentMetaData &lt;input-pdf&gt;
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.10 $
+ */
+public class PrintDocumentMetaData
+{
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 1 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ FileInputStream file = null;
+ try
+ {
+ file = new FileInputStream( args[0] );
+ PDFParser parser = new PDFParser( file );
+ parser.parse();
+ document = parser.getPDDocument();
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: Document is encrypted with a password." );
+ System.exit( 1 );
+ }
+ }
+ PrintDocumentMetaData meta = new PrintDocumentMetaData();
+ meta.printMetadata( document );
+ }
+ finally
+ {
+ if( file != null )
+ {
+ file.close();
+ }
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.PrintDocumentMetaData <input-pdf>" );
+ }
+
+ /**
+ * This will print the documents data to System.out.
+ *
+ * @param document The document to get the metadata from.
+ *
+ * @throws IOException If there is an error getting the page count.
+ */
+ public void printMetadata( PDDocument document ) throws IOException
+ {
+ PDDocumentInformation info = document.getDocumentInformation();
+ PDDocumentCatalog cat = document.getDocumentCatalog();
+ PDMetadata metadata = cat.getMetadata();
+ System.out.println( "Page Count=" + document.getNumberOfPages() );
+ System.out.println( "Title=" + info.getTitle() );
+ System.out.println( "Author=" + info.getAuthor() );
+ System.out.println( "Subject=" + info.getSubject() );
+ System.out.println( "Keywords=" + info.getKeywords() );
+ System.out.println( "Creator=" + info.getCreator() );
+ System.out.println( "Producer=" + info.getProducer() );
+ System.out.println( "Creation Date=" + formatDate( info.getCreationDate() ) );
+ System.out.println( "Modification Date=" + formatDate( info.getModificationDate() ) );
+ System.out.println( "Trapped=" + info.getTrapped() );
+ if( metadata != null )
+ {
+ System.out.println( "Metadata=" + metadata.getInputStreamAsString() );
+ }
+ }
+
+ /**
+ * This will format a date object.
+ *
+ * @param date The date to format.
+ *
+ * @return A string representation of the date.
+ */
+ private String formatDate( Calendar date )
+ {
+ String retval = null;
+ if( date != null )
+ {
+ SimpleDateFormat formatter = new SimpleDateFormat();
+ retval = formatter.format( date.getTime() );
+ }
+
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/RemoveFirstPage.java b/src/main/java/org/pdfbox/examples/pdmodel/RemoveFirstPage.java
new file mode 100644
index 0000000..2b7242b
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/RemoveFirstPage.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import java.io.IOException;
+
+/**
+ * This is an example on how to remove pages from a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.4 $
+ */
+public class RemoveFirstPage
+{
+ private RemoveFirstPage()
+ {
+ //utility class, should not be instantiated.
+ }
+
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( args[0] );
+ if( document.isEncrypted() )
+ {
+ throw new IOException( "Encrypted documents are not supported for this example" );
+ }
+ if( document.getNumberOfPages() <= 1 )
+ {
+ throw new IOException( "Error: A PDF document must have at least one page, " +
+ "cannot remove the last page!");
+ }
+ document.removePage( 0 );
+ document.save( args[1] );
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.RemoveFirstPage <input-pdf> <output-pdf>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/ReplaceString.java b/src/main/java/org/pdfbox/examples/pdmodel/ReplaceString.java
new file mode 100644
index 0000000..bbf8688
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/ReplaceString.java
@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdfparser.PDFStreamParser;
+import org.pdfbox.pdfwriter.ContentStreamWriter;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.util.PDFOperator;
+
+
+/**
+ * This is an example that will replace a string in a PDF with a new one.
+ *
+ * The example is taken from the pdf file format specification.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class ReplaceString
+{
+ /**
+ * Constructor.
+ */
+ public ReplaceString()
+ {
+ super();
+ }
+
+ /**
+ * Locate a string in a PDF and replace it with a new string.
+ *
+ * @param inputFile The PDF to open.
+ * @param outputFile The PDF to write to.
+ * @param strToFind The string to find in the PDF document.
+ * @param message The message to write in the file.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt( String inputFile, String outputFile, String strToFind, String message)
+ throws IOException, COSVisitorException
+ {
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = PDDocument.load( inputFile );
+ List pages = doc.getDocumentCatalog().getAllPages();
+ for( int i=0; i<pages.size(); i++ )
+ {
+ PDPage page = (PDPage)pages.get( i );
+ PDStream contents = page.getContents();
+ PDFStreamParser parser = new PDFStreamParser(contents.getStream() );
+ parser.parse();
+ List tokens = parser.getTokens();
+ for( int j=0; j<tokens.size(); j++ )
+ {
+ Object next = tokens.get( j );
+ if( next instanceof PDFOperator )
+ {
+ PDFOperator op = (PDFOperator)next;
+ //Tj and TJ are the two operators that display
+ //strings in a PDF
+ if( op.getOperation().equals( "Tj" ) )
+ {
+ //Tj takes one operator and that is the string
+ //to display so lets update that operator
+ COSString previous = (COSString)tokens.get( j-1 );
+ String string = previous.getString();
+ string = string.replaceFirst( strToFind, message );
+ previous.reset();
+ previous.append( string.getBytes() );
+ }
+ else if( op.getOperation().equals( "TJ" ) )
+ {
+ COSArray previous = (COSArray)tokens.get( j-1 );
+ for( int k=0; k<previous.size(); k++ )
+ {
+ Object arrElement = previous.getObject( k );
+ if( arrElement instanceof COSString )
+ {
+ COSString cosString = (COSString)arrElement;
+ String string = cosString.getString();
+ string = string.replaceFirst( strToFind, message );
+ cosString.reset();
+ cosString.append( string.getBytes() );
+ }
+ }
+ }
+ }
+ }
+ //now that the tokens are updated we will replace the
+ //page content stream.
+ PDStream updatedStream = new PDStream(doc);
+ OutputStream out = updatedStream.createOutputStream();
+ ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
+ tokenWriter.writeTokens( tokens );
+ page.setContents( updatedStream );
+ }
+ doc.save( outputFile );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will open a PDF and replace a string if it finds it.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ ReplaceString app = new ReplaceString();
+ try
+ {
+ if( args.length != 4 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1], args[2], args[3] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() +
+ " <input-file> <output-file> <search-string> <Message>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/RubberStamp.java b/src/main/java/org/pdfbox/examples/pdmodel/RubberStamp.java
new file mode 100644
index 0000000..158e0f8
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/RubberStamp.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationRubberStamp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is an example on how to add annotations to pages of a PDF document.
+ *
+ * @author Paul King
+ * @version $Revision: 1.1 $
+ */
+public class RubberStamp
+{
+ private RubberStamp()
+ {
+ //utility class, should not be instantiated.
+ }
+
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( args[0] );
+ if( document.isEncrypted() )
+ {
+ throw new IOException( "Encrypted documents are not supported for this example" );
+ }
+ List allpages = new ArrayList();
+ document.getDocumentCatalog().getPages().getAllKids(allpages);
+
+ for (int i=0; i < allpages.size(); i++)
+ {
+ PDPage apage = (PDPage) allpages.get(i);
+ List annotations = apage.getAnnotations();
+
+ PDAnnotationRubberStamp rs = new PDAnnotationRubberStamp();
+ rs.setName(PDAnnotationRubberStamp.NAME_TOP_SECRET);
+ rs.setRectangle(new PDRectangle(100,100));
+ rs.setContents("A top secret note");
+
+ annotations.add(rs);
+ }
+
+ document.save( args[1] );
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.RubberStamp <input-pdf> <output-pdf>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/ShowColorBoxes.java b/src/main/java/org/pdfbox/examples/pdmodel/ShowColorBoxes.java
new file mode 100644
index 0000000..f936642
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/ShowColorBoxes.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.pdmodel;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.edit.PDPageContentStream;
+
+/**
+ * This is an example that creates a simple document.
+ *
+ * The example is taken from the pdf file format specification.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class ShowColorBoxes
+{
+ /**
+ * Constructor.
+ */
+ public ShowColorBoxes()
+ {
+ super();
+ }
+
+ /**
+ * create the second sample document from the PDF file format specification.
+ *
+ * @param file The file to write the PDF to.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error writing the PDF.
+ */
+ public void doIt( String file) throws IOException, COSVisitorException
+ {
+ // the document
+ PDDocument doc = null;
+ try
+ {
+ doc = new PDDocument();
+
+ PDPage page = new PDPage();
+ doc.addPage( page );
+
+ PDPageContentStream contentStream = new PDPageContentStream(doc, page);
+ //first fill the entire background with cyan
+ contentStream.setNonStrokingColor( Color.CYAN );
+ contentStream.fillRect( 0,0, page.findMediaBox().getWidth(), page.findMediaBox().getHeight() );
+
+ //then draw a red box in the lower left hand corner
+ contentStream.setNonStrokingColor( Color.RED );
+ contentStream.fillRect( 10, 10, 100, 100 );
+
+ contentStream.close();
+ doc.save( file );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will create a hello world PDF document.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ ShowColorBoxes app = new ShowColorBoxes();
+ try
+ {
+ if( args.length != 1 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <output-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/pdmodel/package.html b/src/main/java/org/pdfbox/examples/pdmodel/package.html
new file mode 100644
index 0000000..b9beefa
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/pdmodel/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These examples show how to use the classes in the PDModel package.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/examples/persistence/AppendAndFillDoc.java b/src/main/java/org/pdfbox/examples/persistence/AppendAndFillDoc.java
new file mode 100644
index 0000000..01b8cf6
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/persistence/AppendAndFillDoc.java
@@ -0,0 +1,282 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.persistence;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.FileInputStream;
+
+import java.util.Iterator;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSInteger;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This concatenates two documents with fields and fills the fields in the two templates using different
+ * values.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.6 $
+ */
+public class AppendAndFillDoc
+{
+ /**
+ * Constructor.
+ */
+ public AppendAndFillDoc()
+ {
+ super();
+ }
+
+ /**
+ * Append all pages from source to destination.
+ *
+ * todo: this method will go to the pdfmodel package one day
+ *
+ * @param destination the document to receive the pages
+ * @param source the document originating the new pages
+ *
+ */
+ public void appendDocument(COSDocument destination, COSDocument source)
+ {
+ COSDictionary pages2 = getPages(source);
+ COSArray kids = (COSArray)pages2.getItem(COSName.getPDFName("Kids"));
+ for (Iterator i = kids.iterator(); i.hasNext();)
+ {
+ COSDictionary page = (COSDictionary)((COSObject)i.next()).getObject();
+ appendPage(destination, page);
+ }
+ }
+ /**
+ * append a page dict to destination.
+ *
+ * todo: this method will go to the pdfmodel package one day
+ *
+ * @param destination the document to receive the page
+ * @param page the page to append to the document
+ *
+ */
+ public void appendPage(COSDocument destination, COSDictionary page)
+ {
+ // get the parent object (pages dictionary)
+ COSDictionary pages = getPages(destination);
+ // and set in the page object
+ page.setItem(COSName.PARENT, pages);
+ // add new page to kids entry in the pages dictionary
+ COSArray kids = (COSArray) pages.getItem(COSName.getPDFName("Kids"));
+ kids.add(page);
+ // and increase count
+ COSNumber count = (COSNumber) pages.getItem(COSName.COUNT);
+ pages.setItem(COSName.COUNT, new COSInteger(count.intValue() + 1));
+ }
+ /**
+ * concat two pdf documents and fill fields in both templates
+ * this is a bit tricky as one has to rename the fields if we use the same template two times.
+ * here we don't user a clever algorithm to create dynamic fieldnames - this is left to the user..
+ *
+ * @param in1 The first template file
+ * @param in2 The second template file
+ * @param out The created fiel with all pages from document one and document two
+ * @param name1 The name of the PDF field (FDF field) in the first template
+ * @param value1 The value to be used for the field in the first template
+ * @param name2 The name of the PDF field (FDF field) in the second template
+ * @param value2 The value to be used for the field in the second template
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error generating the PDF document.
+ */
+ public void doIt( String in1,
+ String in2,
+ String out,
+ String name1,
+ String value1,
+ String name2,
+ String value2) throws IOException, COSVisitorException
+ {
+ COSDocument doc1 = null;
+ COSDocument doc2 = null;
+ InputStream is1 = null;
+ InputStream is2 = null;
+ PDFParser parser1 = null;
+ PDFParser parser2 = null;
+
+ OutputStream os = null;
+ COSWriter writer = null;
+ try
+ {
+ is1 = new FileInputStream(in1);
+ parser1 = new PDFParser(is1);
+ parser1.parse();
+ doc1 = parser1.getDocument();
+
+ is2 = new FileInputStream(in2);
+ parser2 = new PDFParser(is2);
+ parser2.parse();
+ doc2 = parser2.getDocument();
+
+ setField(doc1, "doc1", new COSString(name1), new COSString(value1));
+ setField(doc2, "doc2", new COSString(name2), new COSString(value2));
+
+ appendDocument(doc1, doc2);
+
+ os = new FileOutputStream(out);
+ writer = new COSWriter(os);
+ writer.write(doc1);
+ }
+ finally
+ {
+ is1.close();
+ doc1.close();
+
+ is2.close();
+ doc2.close();
+
+ os.close();
+ writer.close();
+ }
+ }
+
+ /**
+ * Lookup the pages dictionary in a document.
+ *
+ * todo: this method will go to the pdfmodel package one day
+ *
+ * @param doc the document where the pages dict is searched
+ *
+ * @return The Pages dictionary.
+ */
+ public COSDictionary getPages(COSDocument doc)
+ {
+ // todo should access via catalog instead!
+ for (Iterator i = doc.getObjects().iterator(); i.hasNext();)
+ {
+ COSObject obj = (COSObject) i.next();
+ COSBase base = obj.getObject();
+ if (base instanceof COSDictionary)
+ {
+ COSDictionary dict = (COSDictionary) base;
+ COSBase type = dict.getItem(COSName.TYPE);
+ if (type != null && type.equals(COSName.getPDFName("Pages")))
+ {
+ return dict;
+ }
+ }
+ }
+ return null;
+ }
+ /**
+ * This will concat two pdf documents and fill fields in both.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ */
+ public static void main(String[] args)
+ {
+ AppendAndFillDoc app = new AppendAndFillDoc();
+ try
+ {
+ if( args.length != 7 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ /**
+ * lookup and fill the field.
+ *
+ * todo: this method will go to the pdfmodel package one day
+ *
+ * @param doc the document where the field resides
+ * @param prefix a prefix to use to make the field name unique in the new document
+ * @param name the name of the PDF Annotation field
+ * @param value The desired value to be used for the field
+ *
+ */
+ public void setField(COSDocument doc, String prefix, COSString name, COSString value)
+ {
+ for (Iterator i = doc.getObjects().iterator(); i.hasNext();)
+ {
+ COSObject obj = (COSObject) i.next();
+ COSBase base = obj.getObject();
+ if (base instanceof COSDictionary)
+ {
+ COSDictionary dict = (COSDictionary) base;
+ COSBase type = dict.getItem(COSName.TYPE);
+ if (type != null && type.equals(COSName.getPDFName("Annot")))
+ {
+ COSBase subtype = dict.getItem(COSName.getPDFName("Subtype"));
+ if (subtype != null && subtype.equals(COSName.getPDFName("Widget")))
+ {
+ // we found the field
+ COSBase fname = dict.getItem(COSName.getPDFName("T"));
+ if (fname != null && fname.equals(name))
+ {
+ dict.setItem(COSName.getPDFName("V"), value);
+ dict.setItem(COSName.getPDFName("T"), new COSString(prefix + name.getString()));
+ }
+ }
+ }
+ }
+ }
+ }
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() +
+ " <input-file1> <input-file2> <output-file> <name1> <value1> <name2> <value2>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/persistence/AppendDoc.java b/src/main/java/org/pdfbox/examples/persistence/AppendDoc.java
new file mode 100644
index 0000000..68e254c
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/persistence/AppendDoc.java
@@ -0,0 +1,357 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.persistence;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentInformation;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This example concatenates two documents and writes the result.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.17 $
+ */
+public class AppendDoc
+{
+ /**
+ * Constructor.
+ */
+ public AppendDoc()
+ {
+ super();
+ }
+ /**
+ * append all pages from source to destination.
+ *
+ * todo: this method will go to the pdfmodel package one day
+ *
+ * @param destination the document to receive the pages
+ * @param source the document originating the new pages
+ *
+ * @throws IOException If there is an error accessing data from either document.
+ */
+ public void appendDocument(PDDocument destination, PDDocument source) throws IOException
+ {
+ if( destination.isEncrypted() )
+ {
+ throw new IOException( "Error: destination PDF is encrypted, can't append encrypted PDF documents." );
+ }
+ if( source.isEncrypted() )
+ {
+ throw new IOException( "Error: destination PDF is encrypted, can't append encrypted PDF documents." );
+ }
+ PDDocumentInformation destInfo = destination.getDocumentInformation();
+ PDDocumentInformation srcInfo = source.getDocumentInformation();
+ destInfo.getDictionary().mergeInto( srcInfo.getDictionary() );
+
+ COSDictionary destTrailer = destination.getDocument().getTrailer();
+ COSDictionary destRoot = (COSDictionary)destTrailer.getDictionaryObject( COSName.ROOT );
+
+ COSDictionary srcTrailer = source.getDocument().getTrailer();
+ COSDictionary srcRoot = (COSDictionary)srcTrailer.getDictionaryObject( COSName.ROOT );
+
+ COSName openAction = COSName.getPDFName( "OpenAction" );
+ if( destRoot.getDictionaryObject( openAction ) == null )
+ {
+ COSBase open = srcRoot.getDictionaryObject( openAction );
+ if( open != null )
+ {
+ destRoot.setItem( openAction, copyStreamsIntoDocument( destination, open ) );
+ }
+ }
+
+ COSName acroForm = COSName.getPDFName( "AcroForm" );
+ COSDictionary destAcroForm = (COSDictionary)destRoot.getDictionaryObject( acroForm );
+ COSDictionary srcAcroForm = (COSDictionary)srcRoot.getDictionaryObject( acroForm );
+ if( srcAcroForm != null )
+ {
+ if( destAcroForm == null )
+ {
+ destRoot.setItem( acroForm, copyStreamsIntoDocument( destination, srcAcroForm ) );
+ }
+ else
+ {
+ //****************need to do proper merge***************
+ //destAcroForm.addAll( (COSArray)copyStreamsIntoDocument( destination, srcAcroForm ) );
+ }
+ }
+
+ COSName threads = COSName.getPDFName( "Threads" );
+ COSArray destThreads = (COSArray)destRoot.getDictionaryObject( threads );
+ COSArray srcThreads = (COSArray)srcRoot.getDictionaryObject( threads );
+ if( srcThreads != null )
+ {
+ if( destThreads == null )
+ {
+ destRoot.setItem( threads, copyStreamsIntoDocument( destination, srcThreads ) );
+ }
+ else
+ {
+ destThreads.addAll( (COSArray)copyStreamsIntoDocument( destination, srcThreads ) );
+ }
+ }
+
+ COSName names = COSName.getPDFName( "Names" );
+ COSDictionary destNames = (COSDictionary)destRoot.getDictionaryObject( names );
+ COSDictionary srcNames = (COSDictionary)srcRoot.getDictionaryObject( names );
+ if( srcNames != null )
+ {
+ if( destNames == null )
+ {
+ destRoot.setItem( names, copyStreamsIntoDocument( destination, srcNames ) );
+ }
+ else
+ {
+ //warning, potential for collision here!!
+ destNames.mergeInto( (COSDictionary)copyStreamsIntoDocument( destination, srcNames ) );
+ }
+ }
+
+ COSName outlines = COSName.getPDFName( "Outlines" );
+ COSDictionary destOutlines = (COSDictionary)destRoot.getDictionaryObject( outlines );
+ COSDictionary srcOutlines = (COSDictionary)srcRoot.getDictionaryObject( outlines );
+ if( srcOutlines != null && destOutlines == null )
+ {
+ destRoot.setItem( outlines, copyStreamsIntoDocument( destination, srcOutlines ) );
+ }
+
+ COSName pagemode = COSName.getPDFName( "PageMode" );
+ COSBase srcPageMode = srcRoot.getDictionaryObject( pagemode );
+ if( srcOutlines != null && destOutlines == null )
+ {
+ destRoot.setItem( pagemode, copyStreamsIntoDocument( destination, srcPageMode ) );
+ }
+
+ COSName pageLabels = COSName.getPDFName( "PageLabels" );
+ COSDictionary destLabels = (COSDictionary)destRoot.getDictionaryObject( pageLabels );
+ COSDictionary srcLabels = (COSDictionary)srcRoot.getDictionaryObject( pageLabels );
+ if( srcLabels != null )
+ {
+ int destPageCount = destination.getNumberOfPages();
+ COSArray destNums = null;
+ if( destLabels == null )
+ {
+ destLabels = new COSDictionary();
+ destNums = new COSArray();
+ destLabels.setItem( COSName.getPDFName( "Nums" ), destNums );
+ destRoot.setItem( pageLabels, destLabels );
+ }
+ else
+ {
+ destNums = (COSArray)destLabels.getDictionaryObject( COSName.getPDFName( "Nums" ) );
+ }
+ COSArray srcNums = (COSArray)srcLabels.getDictionaryObject( COSName.getPDFName( "Nums" ) );
+ for( int i=0; i<srcNums.size(); i+=2 )
+ {
+ COSNumber labelIndex = (COSNumber)srcNums.getObject( i );
+ long labelIndexValue = labelIndex.intValue();
+ destNums.add( new COSInteger( labelIndexValue + destPageCount ) );
+ destNums.add( copyStreamsIntoDocument( destination, srcNums.getObject( i+1 ) ) );
+ }
+ }
+
+ COSName metadata = COSName.getPDFName( "Metadata" );
+ COSStream destMetadata = (COSStream)destRoot.getDictionaryObject( metadata );
+ COSStream srcMetadata = (COSStream)srcRoot.getDictionaryObject( metadata );
+ if( destMetadata == null && srcMetadata != null )
+ {
+ PDStream newStream = new PDStream( destination, srcMetadata.getUnfilteredStream(), false );
+ newStream.getStream().mergeInto( srcMetadata );
+ newStream.addCompression();
+ destRoot.setItem( metadata, newStream );
+ }
+
+ //finally append the pages
+ List pages = source.getDocumentCatalog().getAllPages();
+ Iterator pageIter = pages.iterator();
+ while( pageIter.hasNext() )
+ {
+ PDPage page = (PDPage)pageIter.next();
+ PDPage newPage =
+ new PDPage( (COSDictionary)copyStreamsIntoDocument( destination, page.getCOSDictionary() ) );
+ destination.addPage( newPage );
+ }
+ }
+ Map clonedVersion = new HashMap();
+
+ private COSBase copyStreamsIntoDocument( PDDocument destination, COSBase base ) throws IOException
+ {
+
+ COSBase retval = (COSBase)clonedVersion.get( base );
+ if( retval != null )
+ {
+ return retval;
+ }
+ if( base instanceof COSObject )
+ {
+ COSObject object = (COSObject)base;
+ retval = new COSObject(copyStreamsIntoDocument( destination, object.getObject() ) );
+ }
+ else if( base instanceof COSArray )
+ {
+ retval = new COSArray();
+ COSArray array = (COSArray)base;
+ for( int i=0; i<array.size(); i++ )
+ {
+ ((COSArray)retval).add( copyStreamsIntoDocument( destination, array.get( i ) ) );
+ }
+ }
+ else if( base instanceof COSDictionary )
+ {
+ COSDictionary dic = (COSDictionary)base;
+ List keys = dic.keyList();
+ if( base instanceof COSStream )
+ {
+ COSStream originalStream = (COSStream)base;
+ PDStream stream = new PDStream( destination, originalStream.getFilteredStream(), true );
+ clonedVersion.put( base, stream.getStream() );
+ for( int i=0; i<keys.size(); i++ )
+ {
+ COSName key = (COSName)keys.get( i );
+ stream.getStream().setItem( key, copyStreamsIntoDocument(destination,dic.getItem(key)));
+ }
+ retval = stream.getStream();
+ }
+ else
+ {
+ retval = new COSDictionary();
+ clonedVersion.put( base, retval );
+ for( int i=0; i<keys.size(); i++ )
+ {
+ COSName key = (COSName)keys.get( i );
+ ((COSDictionary)retval).setItem( key, copyStreamsIntoDocument(destination,dic.getItem(key)));
+ }
+ }
+ }
+ else
+ {
+ retval = base;
+ }
+ clonedVersion.put( base, retval );
+ return retval;
+ }
+
+ /**
+ * concat two pdf documents.
+ *
+ * @param in1 The first template file
+ * @param in2 The second template file
+ * @param out The created fiel with all pages from document one and document two
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error generating the PDF document.
+ */
+ public void doIt(String in1, String in2, String out) throws IOException, COSVisitorException
+ {
+ PDDocument doc1 = null;
+ PDDocument doc2 = null;
+ try
+ {
+ doc1 = PDDocument.load( in1 );
+ doc2 = PDDocument.load( in2 );
+
+ appendDocument(doc1, doc2);
+ doc1.save( out );
+ }
+ catch( Exception e )
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ close( doc1 );
+ close( doc2 );
+ }
+ }
+
+ private void close( PDDocument doc ) throws IOException
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+
+ /**
+ * This will concat two pdf documents.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ */
+ public static void main(String[] args)
+ {
+ AppendDoc app = new AppendDoc();
+ try
+ {
+ if( args.length != 3 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1], args[2]);
+ }
+ }
+ catch( Exception e )
+ {
+ e.printStackTrace();
+ }
+ }
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <input-file1> <input-file2> <output-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/persistence/CopyDoc.java b/src/main/java/org/pdfbox/examples/persistence/CopyDoc.java
new file mode 100644
index 0000000..a3b7e3f
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/persistence/CopyDoc.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.persistence;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSDocument;
+
+
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdfwriter.COSWriter;
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This is an example used to copy a documents contents from a source doc to destination doc
+ * via an in-memory document representation.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.7 $
+ */
+public class CopyDoc
+{
+ /**
+ * Constructor.
+ */
+ public CopyDoc()
+ {
+ super();
+ }
+
+ /**
+ * This will perform the document copy.
+ *
+ * @param in The filename used for input.
+ * @param out The filename used for output.
+ *
+ * @throws IOException If there is an error parsing the document.
+ * @throws COSVisitorException If there is an error while copying the document.
+ */
+ public void doIt(String in, String out) throws IOException, COSVisitorException
+ {
+ java.io.InputStream is = null;
+ java.io.OutputStream os = null;
+ COSWriter writer = null;
+ try
+ {
+ is = new java.io.FileInputStream(in);
+ PDFParser parser = new PDFParser(is);
+ parser.parse();
+
+ COSDocument doc = parser.getDocument();
+
+ os = new java.io.FileOutputStream(out);
+ writer = new COSWriter(os);
+
+ writer.write(doc);
+
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ if( os != null )
+ {
+ os.close();
+ }
+ if( writer != null )
+ {
+ writer.close();
+ }
+ }
+ }
+
+ /**
+ * This will copy a PDF document.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ */
+ public static void main(String[] args)
+ {
+ CopyDoc app = new CopyDoc();
+ try
+ {
+ if( args.length != 2 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <input-file> <output-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/persistence/FieldsDoc.java b/src/main/java/org/pdfbox/examples/persistence/FieldsDoc.java
new file mode 100644
index 0000000..e506fe1
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/persistence/FieldsDoc.java
@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.persistence;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+
+import java.util.Iterator;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+/**
+ * This example fills a field in a pdf document and writes it to new destination.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.5 $
+ */
+public class FieldsDoc
+{
+ /**
+ * Constructor.
+ */
+ public FieldsDoc()
+ {
+ super();
+ }
+ /**
+ * fill a field in the pdf.
+ *
+ * @param in The template file
+ * @param out The file to write the PDF to.
+ * @param name The name of the PDF field (FDF field)
+ * @param value The value to be used for the field
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error generating the PDF document.
+ */
+ public void doIt(String in, String out, String name, String value) throws IOException, COSVisitorException
+ {
+ java.io.InputStream is = null;
+ COSDocument doc = null;
+ OutputStream os = null;
+ COSWriter writer = null;
+ try
+ {
+ is = new java.io.FileInputStream(in);
+ PDFParser parser = new PDFParser(is);
+ parser.parse();
+
+ doc = parser.getDocument();
+
+ setField(doc, new COSString(name), new COSString(value));
+
+ os = new FileOutputStream(out);
+ writer = new COSWriter(os);
+
+ writer.write(doc);
+
+ }
+ finally
+ {
+ is.close();
+ doc.close();
+ os.close();
+ writer.close();
+ }
+ }
+ /**
+ * This will fill a field in a PDF document.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ */
+ public static void main(String[] args)
+ {
+ FieldsDoc app = new FieldsDoc();
+ try
+ {
+ if( args.length != 4 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1], args[2], args[3]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ /**
+ * lookup and fill the field.
+ *
+ * todo: this method will go to the pdfmodel package one day
+ *
+ * @param doc the document where the field resides
+ * @param name the name of the PDF Annotation field
+ * @param value The desired value to be used for the field
+ *
+ */
+ public void setField(COSDocument doc, COSString name, COSString value)
+ {
+ for (Iterator i = doc.getObjects().iterator(); i.hasNext();)
+ {
+ COSObject obj = (COSObject) i.next();
+ COSBase base = obj.getObject();
+ if (base instanceof COSDictionary)
+ {
+ COSDictionary dict = (COSDictionary) base;
+ COSBase type = dict.getItem(COSName.TYPE);
+ if (type != null && type.equals(COSName.getPDFName("Annot")))
+ {
+ COSBase subtype = dict.getItem(COSName.getPDFName("Subtype"));
+ if (subtype != null && subtype.equals(COSName.getPDFName("Widget")))
+ {
+ // we found the field
+ COSBase fname = dict.getItem(COSName.getPDFName("T"));
+ if (fname != null && fname.equals(name))
+ {
+ dict.setItem(COSName.getPDFName("V"), value);
+ }
+ }
+ }
+ }
+ }
+ }
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <input-file> <output-file> <name> <value>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/persistence/WriteDecodedDoc.java b/src/main/java/org/pdfbox/examples/persistence/WriteDecodedDoc.java
new file mode 100644
index 0000000..8fcab7f
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/persistence/WriteDecodedDoc.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.persistence;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+/**
+ * load document and write with all streams decoded.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.8 $
+ */
+public class WriteDecodedDoc
+{
+
+ /**
+ * Constructor.
+ */
+ public WriteDecodedDoc()
+ {
+ super();
+ }
+
+ /**
+ * This will perform the document reading, decoding and writing.
+ *
+ * @param in The filename used for input.
+ * @param out The filename used for output.
+ *
+ * @throws IOException If there is an error parsing the document.
+ * @throws COSVisitorException If there is an error while copying the document.
+ */
+ public void doIt(String in, String out) throws IOException, COSVisitorException
+ {
+ PDDocument doc = null;
+ try
+ {
+ doc = PDDocument.load( in );
+ if( doc.isEncrypted() )
+ {
+ try
+ {
+ doc.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: The document is encrypted." );
+ }
+ catch( org.pdfbox.exceptions.CryptographyException e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ for (Iterator i = doc.getDocument().getObjects().iterator(); i.hasNext();)
+ {
+ COSBase base = ((COSObject) i.next()).getObject();
+ if (base instanceof COSStream)
+ {
+ // just kill the filters
+ COSStream cosStream = (COSStream)base;
+ cosStream.getUnfilteredStream();
+ cosStream.setFilters(null);
+ }
+ }
+ doc.save( out );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ /**
+ * This will write a PDF document with completely decoded streams.
+ * <br />
+ * see usage() for commandline
+ *
+ * @param args command line arguments
+ */
+ public static void main(String[] args)
+ {
+ WriteDecodedDoc app = new WriteDecodedDoc();
+ try
+ {
+ if( args.length != 2 )
+ {
+ app.usage();
+ }
+ else
+ {
+ app.doIt( args[0], args[1]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This will print out a message telling how to use this example.
+ */
+ private void usage()
+ {
+ System.err.println( "usage: " + this.getClass().getName() + " <input-file> <output-file>" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/persistence/package.html b/src/main/java/org/pdfbox/examples/persistence/package.html
new file mode 100644
index 0000000..9c0d57f
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/persistence/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These examples will show how to use the persistence features of the PDFBox.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/examples/signature/ShowSignature.java b/src/main/java/org/pdfbox/examples/signature/ShowSignature.java
new file mode 100644
index 0000000..7398534
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/signature/ShowSignature.java
@@ -0,0 +1,168 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.signature;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import java.security.cert.CertificateFactory;
+
+import java.util.Collection;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+/**
+ * This will read a document from the filesystem, decrypt it and do something with the signature.
+ *
+ * usage: java org.pdfbox.examples.signature.ShowSignature &lt;password&gt; &lt;inputfile&gt;
+ *
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+public class ShowSignature
+{
+
+ /**
+ * This is the entry point for the application.
+ *
+ * @param args The command-line arguments.
+ *
+ * @throws Exception If there is an error reading the file.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ ShowSignature show = new ShowSignature();
+ show.showSignature( args );
+ }
+
+ private void showSignature( String[] args ) throws Exception
+ {
+ if( args.length != 2 )
+ {
+ usage();
+ }
+ else
+ {
+ String password = args[0];
+ String infile = args[1];
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( infile );
+
+ if( document.isEncrypted() )
+ {
+ document.decrypt( password );
+ }
+ else
+ {
+ System.err.println( "Warning: Document is not encrypted." );
+ }
+
+ COSDictionary trailer = document.getDocument().getTrailer();
+ COSDictionary root = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
+ COSDictionary acroForm = (COSDictionary)root.getDictionaryObject( COSName.getPDFName( "AcroForm" ) );
+ COSArray fields = (COSArray)acroForm.getDictionaryObject( COSName.getPDFName( "Fields" ) );
+ for( int i=0; i<fields.size(); i++ )
+ {
+ COSDictionary field = (COSDictionary)fields.getObject( i );
+ String type = field.getNameAsString( "FT" );
+ if( "Sig".equals( type ) )
+ {
+ COSDictionary cert = (COSDictionary)field.getDictionaryObject( COSName.getPDFName( "V" ) );
+ if( cert != null )
+ {
+ System.out.println( "Certificate found" );
+ System.out.println( "Name=" + cert.getDictionaryObject( COSName.getPDFName( "Name" ) ) );
+ System.out.println( "Modified=" + cert.getDictionaryObject( COSName.getPDFName( "M" ) ) );
+ COSName subFilter = (COSName)cert.getDictionaryObject( COSName.getPDFName( "SubFilter" ) );
+ if( subFilter != null )
+ {
+ if( subFilter.getName().equals( "adbe.x509.rsa_sha1" ) )
+ {
+ COSString certString = (COSString)cert.getDictionaryObject(
+ COSName.getPDFName( "Cert" ) );
+ byte[] certData = certString.getBytes();
+ CertificateFactory factory = CertificateFactory.getInstance( "X.509" );
+ ByteArrayInputStream certStream = new ByteArrayInputStream( certData );
+ Collection certs = factory.generateCertificates( certStream );
+ System.out.println( "certs=" + certs );
+ }
+ else if( subFilter.getName().equals( "adbe.pkcs7.sha1" ) )
+ {
+ COSString certString = (COSString)cert.getDictionaryObject(
+ COSName.getPDFName( "Contents" ) );
+ byte[] certData = certString.getBytes();
+ CertificateFactory factory = CertificateFactory.getInstance( "X.509" );
+ ByteArrayInputStream certStream = new ByteArrayInputStream( certData );
+ Collection certs = factory.generateCertificates( certStream );
+ System.out.println( "certs=" + certs );
+ }
+ else
+ {
+ System.err.println( "Unknown certificate type:" + subFilter );
+ }
+ }
+ else
+ {
+ throw new IOException( "Missing subfilter for cert dictionary" );
+ }
+ }
+ else
+ {
+ System.out.println( "Signature found, but no certificate" );
+ }
+ }
+ }
+ }
+ finally
+ {
+ document.close();
+ }
+ }
+ }
+
+ /**
+ * This will print a usage message.
+ */
+ private static void usage()
+ {
+ System.err.println( "usage: java org.pdfbox.examples.signature.ShowSignature " +
+ "<password> <inputfile>" );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/signature/package.html b/src/main/java/org/pdfbox/examples/signature/package.html
new file mode 100644
index 0000000..731c0ff
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/signature/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These examples will show how to gain access to the PDF signature.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/examples/util/ExtractTextByArea.java b/src/main/java/org/pdfbox/examples/util/ExtractTextByArea.java
new file mode 100644
index 0000000..042e3e6
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/util/ExtractTextByArea.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.util;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.util.PDFTextStripperByArea;
+
+import java.awt.Rectangle;
+
+import java.util.List;
+
+/**
+ * This is an example on how to extract text from a specific area on the PDF document.
+ *
+ * Usage: java org.pdfbox.examples.util.ExtractTextByArea &lt;input-pdf&gt;
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class ExtractTextByArea
+{
+ private ExtractTextByArea()
+ {
+ //utility class and should not be constructed.
+ }
+
+
+ /**
+ * This will print the documents text in a certain area.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 1 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ try
+ {
+ document = PDDocument.load( args[0] );
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: Document is encrypted with a password." );
+ System.exit( 1 );
+ }
+ }
+ PDFTextStripperByArea stripper = new PDFTextStripperByArea();
+ stripper.setSortByPosition( true );
+ Rectangle rect = new Rectangle( 10, 280, 275, 60 );
+ stripper.addRegion( "class1", rect );
+ List allPages = document.getDocumentCatalog().getAllPages();
+ PDPage firstPage = (PDPage)allPages.get( 0 );
+ stripper.extractRegions( firstPage );
+ System.out.println( "Text in the area:" + rect );
+ System.out.println( stripper.getTextForRegion( "class1" ) );
+
+ }
+ finally
+ {
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.util.ExtractTextByArea <input-pdf>" );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/util/PrintTextLocations.java b/src/main/java/org/pdfbox/examples/util/PrintTextLocations.java
new file mode 100644
index 0000000..6c83b57
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/util/PrintTextLocations.java
@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.examples.util;
+
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.util.PDFTextStripper;
+import org.pdfbox.util.TextPosition;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import java.util.List;
+
+/**
+ * This is an example on how to get some x/y coordinates of text.
+ *
+ * Usage: java org.pdfbox.examples.util.PrintTextLocations &lt;input-pdf&gt;
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class PrintTextLocations extends PDFTextStripper
+{
+ /**
+ * Default constructor.
+ *
+ * @throws IOException If there is an error loading text stripper properties.
+ */
+ public PrintTextLocations() throws IOException
+ {
+ //default constructor.
+ }
+
+ /**
+ * This will print the documents data.
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception If there is an error parsing the document.
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ if( args.length != 1 )
+ {
+ usage();
+ }
+ else
+ {
+ PDDocument document = null;
+ FileInputStream file = null;
+ try
+ {
+ file = new FileInputStream( args[0] );
+ PDFParser parser = new PDFParser( file );
+ parser.parse();
+ document = parser.getPDDocument();
+ if( document.isEncrypted() )
+ {
+ try
+ {
+ document.decrypt( "" );
+ }
+ catch( InvalidPasswordException e )
+ {
+ System.err.println( "Error: Document is encrypted with a password." );
+ System.exit( 1 );
+ }
+ }
+ PrintTextLocations printer = new PrintTextLocations();
+ List allPages = document.getDocumentCatalog().getAllPages();
+ for( int i=0; i<allPages.size(); i++ )
+ {
+ PDPage page = (PDPage)allPages.get( i );
+ System.out.println( "Processing page: " + i );
+ printer.processStream( page, page.findResources(), page.getContents().getStream() );
+ }
+ }
+ finally
+ {
+ if( file != null )
+ {
+ file.close();
+ }
+ if( document != null )
+ {
+ document.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * A method provided as an event interface to allow a subclass to perform
+ * some specific functionality when a character needs to be displayed.
+ *
+ * @param text The character to be displayed.
+ */
+ protected void showCharacter( TextPosition text )
+ {
+ System.out.println( "String[" + text.getX() + "," + text.getY() + "]" + text.getCharacter() );
+ }
+
+ /**
+ * This will print the usage for this document.
+ */
+ private static void usage()
+ {
+ System.err.println( "Usage: java org.pdfbox.examples.pdmodel.PrintTextLocations <input-pdf>" );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/examples/util/package.html b/src/main/java/org/pdfbox/examples/util/package.html
new file mode 100644
index 0000000..bc50f59
--- /dev/null
+++ b/src/main/java/org/pdfbox/examples/util/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The packages in this package will show how to use the PDFBox util API.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/exceptions/COSVisitorException.java b/src/main/java/org/pdfbox/exceptions/COSVisitorException.java
new file mode 100644
index 0000000..8c5cee1
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/COSVisitorException.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.exceptions;
+
+/**
+ * An exception that represents something gone wrong when visiting a PDF object.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.6 $
+ */
+public class COSVisitorException extends WrappedException
+{
+
+ /**
+ * COSVisitorException constructor comment.
+ *
+ * @param e The root exception that caused this exception.
+ */
+ public COSVisitorException( Exception e )
+ {
+ super( e );
+ }
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/exceptions/CryptographyException.java b/src/main/java/org/pdfbox/exceptions/CryptographyException.java
new file mode 100644
index 0000000..c54fb10
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/CryptographyException.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.exceptions;
+
+/**
+ * An exception that indicates that something has gone wrong during a
+ * cryptography operation.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class CryptographyException extends Exception
+{
+ private Exception embedded;
+
+ /**
+ * Constructor.
+ *
+ * @param msg A msg to go with this exception.
+ */
+ public CryptographyException( String msg )
+ {
+ super( msg );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param e The root exception that caused this exception.
+ */
+ public CryptographyException( Exception e )
+ {
+ super( e.getMessage() );
+ setEmbedded( e );
+ }
+ /**
+ * This will get the exception that caused this exception.
+ *
+ * @return The embedded exception if one exists.
+ */
+ public Exception getEmbedded()
+ {
+ return embedded;
+ }
+ /**
+ * This will set the exception that caused this exception.
+ *
+ * @param e The sub exception.
+ */
+ private void setEmbedded( Exception e )
+ {
+ embedded = e;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/exceptions/InvalidPasswordException.java b/src/main/java/org/pdfbox/exceptions/InvalidPasswordException.java
new file mode 100644
index 0000000..22d92ec
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/InvalidPasswordException.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.exceptions;
+
+/**
+ * An exception that indicates an invalid password was supplied.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class InvalidPasswordException extends Exception
+{
+
+ /**
+ * Constructor.
+ *
+ * @param msg A msg to go with this exception.
+ */
+ public InvalidPasswordException( String msg )
+ {
+ super( msg );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/exceptions/OutlineNotLocalException.java b/src/main/java/org/pdfbox/exceptions/OutlineNotLocalException.java
new file mode 100644
index 0000000..db7bf76
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/OutlineNotLocalException.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.exceptions;
+
+import java.io.IOException;
+
+/**
+ * This exception will be thrown when a local destination(page within the same PDF) is required
+ * but the bookmark(PDOutlineItem) refers to an external destination or an action that does not
+ * point to a page.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class OutlineNotLocalException extends IOException
+{
+
+ /**
+ * Constructor.
+ *
+ * @param msg An error message.
+ */
+ public OutlineNotLocalException( String msg )
+ {
+ super( msg );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/exceptions/WrappedException.java b/src/main/java/org/pdfbox/exceptions/WrappedException.java
new file mode 100644
index 0000000..558221d
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/WrappedException.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.exceptions;
+
+import java.io.PrintStream;
+
+/**
+ * An exception that that holds a sub exception.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class WrappedException extends Exception
+{
+ private Exception wrapped = null;
+
+ /**
+ * constructor comment.
+ *
+ * @param e The root exception that caused this exception.
+ */
+ public WrappedException( Exception e )
+ {
+ wrapped = e;
+ }
+
+ /**
+ * Gets the wrapped exception message.
+ *
+ * @return A message indicating the exception.
+ */
+ public String getMessage()
+ {
+ return wrapped.getMessage();
+ }
+
+ /**
+ * Prints this throwable and its backtrace to the specified print stream.
+ *
+ * @param s <code>PrintStream</code> to use for output
+ */
+ public void printStackTrace(PrintStream s)
+ {
+ super.printStackTrace( s );
+ wrapped.printStackTrace( s );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/exceptions/WrappedIOException.java b/src/main/java/org/pdfbox/exceptions/WrappedIOException.java
new file mode 100644
index 0000000..38a004d
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/WrappedIOException.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.exceptions;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+/**
+ * An exception that that holds a sub exception.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class WrappedIOException extends IOException
+{
+ private Throwable wrapped = null;
+
+ /**
+ * constructor comment.
+ *
+ * @param e The root exception that caused this exception.
+ */
+ public WrappedIOException( Throwable e )
+ {
+ wrapped = e;
+ }
+
+ /**
+ * Gets the wrapped exception message.
+ *
+ * @return A message indicating the exception.
+ */
+ public String getMessage()
+ {
+ return wrapped.getMessage();
+ }
+
+ /**
+ * Prints this throwable and its backtrace to the specified print stream.
+ *
+ * @param s <code>PrintStream</code> to use for output
+ */
+ public void printStackTrace(PrintStream s)
+ {
+ super.printStackTrace( s );
+ wrapped.printStackTrace( s );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/exceptions/package.html b/src/main/java/org/pdfbox/exceptions/package.html
new file mode 100644
index 0000000..d45d03a
--- /dev/null
+++ b/src/main/java/org/pdfbox/exceptions/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package is a place holder for exceptions that are used in the PDFBox project.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/filter/ASCII85Filter.java b/src/main/java/org/pdfbox/filter/ASCII85Filter.java
new file mode 100644
index 0000000..335208c
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/ASCII85Filter.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.pdfbox.io.ASCII85InputStream;
+import org.pdfbox.io.ASCII85OutputStream;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is the used for the ASCIIHexDecode filter.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class ASCII85Filter implements Filter
+{
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData The compressed byte stream.
+ * @param result The place to write the uncompressed byte stream.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error decompressing the stream.
+ */
+ public void decode( InputStream compressedData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ ASCII85InputStream is = null;
+ try
+ {
+ is = new ASCII85InputStream(compressedData);
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ while( (amountRead = is.read( buffer, 0, 1024) ) != -1 )
+ {
+ result.write(buffer, 0, amountRead);
+ }
+ result.flush();
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ }
+ }
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData The raw data to encode.
+ * @param result The place to write to encoded results to.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error compressing the stream.
+ */
+ public void encode( InputStream rawData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ ASCII85OutputStream os = new ASCII85OutputStream(result);
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ while( (amountRead = rawData.read( buffer, 0, 1024 )) != -1 )
+ {
+ os.write( buffer, 0, amountRead );
+ }
+ os.close();
+ result.flush();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/ASCIIHexFilter.java b/src/main/java/org/pdfbox/filter/ASCIIHexFilter.java
new file mode 100644
index 0000000..b3cf506
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/ASCIIHexFilter.java
@@ -0,0 +1,205 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.persistence.util.COSHEXTable;
+
+/**
+ * This is the used for the ASCIIHexDecode filter.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class ASCIIHexFilter implements Filter
+{
+ private static final int ASCII_ZERO = (int)'0';
+
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData The compressed byte stream.
+ * @param result The place to write the uncompressed byte stream.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error decompressing the stream.
+ */
+ public void decode( InputStream compressedData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ int value =0;
+ int firstByte = 0;
+ int secondByte = 0;
+ while( (firstByte = compressedData.read()) != -1 )
+ {
+ value = REVERSE_HEX[firstByte] * 16;
+ secondByte = compressedData.read();
+ if( secondByte >= 0 )
+ {
+ value += REVERSE_HEX[ secondByte ];
+ }
+ result.write( value );
+ }
+ result.flush();
+ }
+
+ private static final int[] REVERSE_HEX =
+ {
+ -1, //0
+ -1, //1
+ -1, //2
+ -1, //3
+ -1, //4
+ -1, //5
+ -1, //6
+ -1, //7
+ -1, //8
+ -1, //9
+ -1, //10
+ -1, //11
+ -1, //12
+ -1, //13
+ -1, //14
+ -1, //15
+ -1, //16
+ -1, //17
+ -1, //18
+ -1, //19
+ -1, //20
+ -1, //21
+ -1, //22
+ -1, //23
+ -1, //24
+ -1, //25
+ -1, //26
+ -1, //27
+ -1, //28
+ -1, //29
+ -1, //30
+ -1, //31
+ -1, //32
+ -1, //33
+ -1, //34
+ -1, //35
+ -1, //36
+ -1, //37
+ -1, //38
+ -1, //39
+ -1, //40
+ -1, //41
+ -1, //42
+ -1, //43
+ -1, //44
+ -1, //45
+ -1, //46
+ -1, //47
+ 0, //48
+ 1, //49
+ 2, //50
+ 3, //51
+ 4, //52
+ 5, //53
+ 6, //54
+ 7, //55
+ 8, //56
+ 9, //57
+ -1, //58
+ -1, //59
+ -1, //60
+ -1, //61
+ -1, //62
+ -1, //63
+ -1, //64
+ 10, //65
+ 11, //66
+ 12, //67
+ 13, //68
+ 14, //69
+ 15, //70
+ -1, //71
+ -1, //72
+ -1, //73
+ -1, //74
+ -1, //75
+ -1, //76
+ -1, //77
+ -1, //78
+ -1, //79
+ -1, //80
+ -1, //81
+ -1, //82
+ -1, //83
+ -1, //84
+ -1, //85
+ -1, //86
+ -1, //87
+ -1, //88
+ -1, //89
+ -1, //90
+ -1, //91
+ -1, //92
+ -1, //93
+ -1, //94
+ -1, //95
+ -1, //96
+ 10, //97
+ 11, //98
+ 12, //99
+ 13, //100
+ 14, //101
+ 15, //102
+ };
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData The raw data to encode.
+ * @param result The place to write to encoded results to.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error compressing the stream.
+ */
+ public void encode( InputStream rawData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ int byteRead = 0;
+ while( (byteRead = rawData.read()) != -1 )
+ {
+ int value = (byteRead+256)%256;
+ result.write( COSHEXTable.TABLE[value] );
+ }
+ result.flush();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/CCITTFaxDecodeFilter.java b/src/main/java/org/pdfbox/filter/CCITTFaxDecodeFilter.java
new file mode 100644
index 0000000..15f4c58
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/CCITTFaxDecodeFilter.java
@@ -0,0 +1,735 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is a filter for the CCITTFax Decoder.
+ *
+ * @author Ben Litchfield, Marcel Kammer, Paul King
+ * @version $Revision: 1.10 $
+ */
+public class CCITTFaxDecodeFilter implements Filter
+{
+ private static Logger log = Logger.getLogger(CCITTFaxDecodeFilter.class);
+
+ // Filter will write 15 TAG's
+ // If you add or remove TAG's you will have to modify this value
+ private static final int TAG_COUNT = 15;
+
+ // HEADERLENGTH(fix 8 Bytes) plus ImageLength(variable)
+ private int offset = 8;
+
+ // Bytecounter for Bytes that will be written after the TAG-DICTIONARY
+ private int tailingBytesCount = 0;
+
+ // Bytes to write after TAG-DICTIONARY
+ private final ByteArrayOutputStream tailer = new ByteArrayOutputStream();
+
+ /**
+ * Constructor.
+ */
+ public CCITTFaxDecodeFilter()
+ {
+ }
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData
+ * The compressed byte stream.
+ * @param result
+ * The place to write the uncompressed byte stream.
+ * @param options
+ * The options to use to encode the data.
+ *
+ * @throws IOException
+ * If there is an error decompressing the stream.
+ */
+ public void decode(InputStream compressedData, OutputStream result, COSDictionary options) throws IOException
+ {
+ // log.warn( "Warning: CCITTFaxDecode.decode is not implemented yet,
+ // skipping this stream." );
+
+
+ // Get ImageParams from PDF
+ COSDictionary dict = (COSDictionary) options.getDictionaryObject("DecodeParms");
+ int width = options.getInt("Width");
+ int height = options.getInt("Height");
+ int length = options.getInt(COSName.LENGTH);
+ int compressionType = dict.getInt("K");
+ boolean blackIs1 = dict.getBoolean("BlackIs1", false);
+
+
+ // HEADER-INFO and starting point of TAG-DICTIONARY
+ writeTagHeader(result, length);
+
+ // IMAGE-DATA
+ int i = 0;
+ //int sum = 0;
+ byte[] buffer = new byte[32768];
+ int lentoread = length;
+
+ while ((lentoread > 0) && ((i = compressedData.read(buffer, 0, Math.min(lentoread, 32768))) != -1))
+ {
+ //sum += i;
+ result.write(buffer, 0, i);
+ lentoread = lentoread - i;
+ }
+
+ // If lentoread is > 0 then we need to write out some padding to equal the header
+ // We'll use what we have in the buffer it's just padding after all
+ while (lentoread > 0)
+ {
+ result.write(buffer, 0, Math.min(lentoread, 32768));
+ lentoread = lentoread - Math.min(lentoread, 32738);
+ }
+ //System.out.println("Gelesen: " + sum);
+
+ // TAG-COUNT
+ writeTagCount(result);
+
+ // WIDTH 0x0100
+ writeTagWidth(result, width);
+
+ // HEIGHT 0x0101
+ writeTagHeight(result, height);
+
+ // BITSPERSAMPLE 0x0102
+ // Always 1 for CCITTFax
+ writeTagBitsPerSample(result, 1);
+
+ // COMPRESSION 0x0103
+ writeTagCompression(result, compressionType);
+
+ // PHOTOMETRIC 0x0106
+ writeTagPhotometric(result, blackIs1);
+
+ // STRIPOFFSET 0x0111
+ // HERE ALWAYS 8, because ImageData comes before TAG-DICTIONARY
+ writeTagStripOffset(result, 8);
+
+ // ORIENTATION 0x0112
+ writeTagOrientation(result, 1);
+
+ // SamplesPerPixel 0x0115
+ writeTagSamplesPerPixel(result, 1);
+
+ // RowsPerStrip 0x0116
+ writeTagRowsPerStrip(result, height);
+
+ // Stripcount 0x0117
+ writeTagStripByteCount(result, length);
+
+ // XRESOLUTION 0x011A
+ // HERE: 200 DPI
+ writeTagXRes(result, 200, 1);
+
+ // YRESOLITION 0x011B
+ // HERE: 200 DPI
+ writeTagYRes(result, 200, 1);
+
+ // ResolutionUnit 0x0128
+ // HERE: DPI
+ writeTagResolutionUnit(result, 2);
+
+ // SOFTWARE 0x0131
+ // minimum 4 chars
+ writeTagSoftware(result, "pdfbox".getBytes());
+
+ // DATE AND TIME 0x0132
+ writeTagDateTime(result, new Date());
+
+ // END OF TAG-DICT
+ writeTagTailer(result);
+ }
+
+ private void writeTagHeader(OutputStream result, int length) throws IOException
+ {
+ byte[] header = { 'M', 'M', 0, '*' };// Big-endian
+ result.write(header);
+
+
+ // Add imagelength to offset
+ offset += length;
+
+ // OFFSET TAG-DICTIONARY
+ int i1 = offset/16777216;//=value/(256*256*256)
+ int i2 = (offset-i1*16777216)/65536;
+ int i3 = (offset-i1*16777216-i2*65536)/256;
+ int i4 = offset % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+ }
+
+ private void writeTagCount(OutputStream result) throws IOException
+ {
+ result.write(TAG_COUNT / 256);
+ result.write(TAG_COUNT % 256);// tagCount
+ }
+
+ private void writeTagWidth(OutputStream result, int width) throws IOException
+ {
+ // @todo width berechnen
+
+ // TAG-ID 100
+ result.write(1);
+ result.write(0);
+
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LENGTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE = width
+ result.write(width/256);
+ result.write(width%256);
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagHeight(OutputStream result, int height) throws IOException
+ {
+ //@todo height berechnen
+ // TAG-ID 101
+ result.write(1);
+ result.write(1);
+
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LENGTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE
+ result.write(height/256);
+ result.write(height%256);
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagBitsPerSample(OutputStream result, int value) throws IOException
+ {
+ // TAG-ID 102
+ result.write(1);
+ result.write(2);
+
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+ // TAG-LENGTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE
+ result.write(value/256);
+ result.write(value%256);
+ result.write(0);//SHORT=0
+ result.write(0);//SHORT=0
+
+ }
+
+ /**
+ * Write the tag compression.
+ *
+ * @param result The stream to write to.
+ * @param type The type to write.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writeTagCompression(OutputStream result, int type) throws IOException
+ {
+ // TAG-ID 103
+ result.write(1);
+ result.write(3);
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LEGNTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+ // TAG-VALUE
+ //@todo typ eintragen; hier immer 4
+ result.write(0);
+ if (type < 0)
+ {
+ result.write(4);// G4
+ }
+ else if (type == 0)
+ {
+ result.write(3);// G3-1D
+ }
+ else
+ {
+ result.write(2);// G3-2D
+ }
+ result.write(0);
+ result.write(0);
+
+ }
+
+ private void writeTagPhotometric(OutputStream result, boolean blackIs1) throws IOException
+ {
+ // TAG-ID 106
+ result.write(1);
+ result.write(6);
+
+
+ // TAG-TYPE SHORT
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LENGTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE
+ result.write(0);
+ if (blackIs1)
+ {
+ result.write(1);
+ }
+ else
+ {
+ result.write(0);
+ }
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagStripOffset(OutputStream result, int value) throws IOException
+ {
+ // TAG-ID 111
+ result.write(1);
+ result.write(17);
+
+ // TAG-TYPE LONG=4
+ result.write(0);
+ result.write(4);
+
+
+ // TAG-LENGTH=1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE = 8 //VOR TAG-DICTIONARY
+ int i1 = value/16777216;//=value/(256*256*256)
+ int i2 = (value-i1*16777216)/65536;
+ int i3 = (value-i1*16777216-i2*65536)/256;
+ int i4 = value % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+
+ }
+
+ private void writeTagSamplesPerPixel(OutputStream result, int value) throws IOException
+ {
+ // TAG-ID 115
+ result.write(1);
+ result.write(21);
+
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LENGTH=1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE
+ result.write(value / 256);
+ result.write(value % 256);
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagRowsPerStrip(OutputStream result, int value) throws IOException
+ {
+ // TAG-ID 116
+ result.write(1);
+ result.write(22);
+
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LENGTH=1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE
+ result.write(value / 256);
+ result.write(value % 256);
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagStripByteCount(OutputStream result, int value) throws IOException
+ {
+ //@todo value auswerten
+ // TAG-ID 117
+ result.write(1);
+ result.write(23);
+
+ // TAG-TYPE LONG=4
+ result.write(0);
+ result.write(4);
+
+
+ // TAG-LENGTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+ // TAG-VALUE
+ int i1 = value/16777216;//=value/(256*256*256)
+ int i2 = (value-i1*16777216)/65536;
+ int i3 = (value-i1*16777216-i2*65536)/256;
+ int i4 = value % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+
+ }
+
+ private void writeTagXRes(OutputStream result, int value1, int value2) throws IOException
+ {
+ // TAG-ID 11A
+ result.write(1);
+ result.write(26);
+
+ // TAG-TYPE RATIONAL=5
+ result.write(0);
+ result.write(5);
+
+ // TAG-LENGTH=1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE=OFFSET TO RATIONAL
+ int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
+ int i1 = valueOffset/16777216;//=value/(256*256*256)
+ int i2 = (valueOffset-i1*16777216)/65536;
+ int i3 = (valueOffset-i1*16777216-i2*65536)/256;
+ int i4 = valueOffset % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+
+ i1 = value1 /16777216;
+ i2 = (value1-i1*16777216)/65536;
+ i3 = (value1-i1*16777216 - i2*65536)/256;
+ i4 = value1 % 256;
+ tailer.write(i1);
+ tailer.write(i2);
+ tailer.write(i3);
+ tailer.write(i4);
+
+ i1 = value2 /16777216;
+ i2 = (value2-i1*16777216)/65536;
+ i3 = (value2-i1*16777216 - i2*65536)/256;
+ i4 = value2 % 256;
+ tailer.write(i1);
+ tailer.write(i2);
+ tailer.write(i3);
+ tailer.write(i4);
+
+ tailingBytesCount += 8;
+ }
+
+ private void writeTagYRes(OutputStream result, int value1, int value2) throws IOException
+ {
+ // TAG-ID 11B
+ result.write(1);
+ result.write(27);
+
+
+ // TAG-TYPE RATIONAL=5
+ result.write(0);
+ result.write(5);
+
+ // TAG-LENGTH=1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+
+ // TAG-VALUE=OFFSET TO RATIONAL
+ int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
+ int i1 = valueOffset/16777216;//=value/(256*256*256)
+ int i2 = (valueOffset-i1*16777216)/65536;
+ int i3 = (valueOffset-i1*16777216-i2*65536)/256;
+ int i4 = valueOffset % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+
+ i1 = value1 /16777216;
+ i2 = (value1-i1*16777216)/65536;
+ i3 = (value1-i1*16777216 - i2*65536)/256;
+ i4 = value1 % 256;
+ tailer.write(i1);
+ tailer.write(i2);
+ tailer.write(i3);
+ tailer.write(i4);
+
+ i1 = value2 /16777216;
+ i2 = (value2-i1*16777216)/65536;
+ i3 = (value2-i1*16777216 - i2*65536)/256;
+ i4 = value2 % 256;
+ tailer.write(i1);
+ tailer.write(i2);
+ tailer.write(i3);
+ tailer.write(i4);
+
+ tailingBytesCount += 8;
+ }
+
+ private void writeTagResolutionUnit(OutputStream result, int value) throws IOException
+ {
+ // TAG-ID 128
+ result.write(1);
+ result.write(40);
+
+ // TAG-TYPE SHORT=3
+ result.write(0);
+ result.write(3);
+
+ // TAG-LENGTH = 1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+ // TAG-VALUE
+ result.write(value/256);
+ result.write(value%256);
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagOrientation(OutputStream result, int value) throws IOException
+ {
+ // TAG-ID 112
+ result.write(1);
+ result.write(18);
+
+ // TAG-TYPE SHORT = 3
+ result.write(0);
+ result.write(3);
+
+
+ // TAG-LENGTH=1
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(1);
+
+ // TAG-VALUE
+ result.write(value / 256);
+ result.write(value % 256);
+ result.write(0);// SHORT=0
+ result.write(0);// SHORT=0
+
+ }
+
+ private void writeTagTailer(OutputStream result) throws IOException
+ {
+ // END OF TAG-DICTIONARY
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(0);
+
+ // TAILER WITH VALUES OF RATIONALFIELD's
+ result.write(tailer.toByteArray());
+ }
+
+ private void writeTagSoftware(OutputStream result, byte[] text) throws IOException
+ {
+ // TAG-ID 131
+ result.write(1);
+ result.write(49);
+
+ // TAG-TYPE ASCII=2
+ result.write(0);
+ result.write(2);
+
+
+ // TAG-LENGTH=id.length+1
+ result.write(0);
+ result.write(0);
+ result.write((text.length + 1) / 256);
+ result.write((text.length + 1) % 256);
+
+ // TAG-VALUE
+ int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
+ int i1 = valueOffset/16777216;//=value/(256*256*256)
+ int i2 = (valueOffset-i1*16777216)/65536;
+ int i3 = (valueOffset-i1*16777216-i2*65536)/256;
+ int i4 = valueOffset % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+
+
+ tailer.write(text);
+ tailer.write(0);
+ tailingBytesCount += text.length + 1;
+ }
+
+ private void writeTagDateTime(OutputStream result, Date date) throws IOException
+ {
+ // TAG-ID 132
+ result.write(1);
+ result.write(50);
+
+
+ // TAG-TYPE ASCII=2
+ result.write(0);
+ result.write(2);
+
+
+ // TAG-LENGTH=20
+ result.write(0);
+ result.write(0);
+ result.write(0);
+ result.write(20);
+
+
+ // TAG-VALUE
+ int valueOffset = offset + 6 + 12 * TAG_COUNT + tailer.size();
+ int i1 = valueOffset/16777216;//=value/(256*256*256)
+ int i2 = (valueOffset-i1*16777216)/65536;
+ int i3 = (valueOffset-i1*16777216-i2*65536)/256;
+ int i4 = valueOffset % 256;
+ result.write(i1);
+ result.write(i2);
+ result.write(i3);
+ result.write(i4);
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ String datetime = sdf.format(date);
+ tailer.write(datetime.getBytes());
+ tailer.write(0);
+
+ tailingBytesCount += 20;
+ }
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData
+ * The raw data to encode.
+ * @param result
+ * The place to write to encoded results to.
+ * @param options
+ * The options to use to encode the data.
+ *
+ * @throws IOException
+ * If there is an error compressing the stream.
+ */
+ public void encode(InputStream rawData, OutputStream result, COSDictionary options) throws IOException
+ {
+ log.warn("Warning: CCITTFaxDecode.encode is not implemented yet, skipping this stream.");
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/DCTFilter.java b/src/main/java/org/pdfbox/filter/DCTFilter.java
new file mode 100644
index 0000000..8f371ea
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/DCTFilter.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is the used for the DCTDecode filter.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class DCTFilter implements Filter
+{
+ private static Logger log = Logger.getLogger( DCTFilter.class );
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData The compressed byte stream.
+ * @param result The place to write the uncompressed byte stream.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error decompressing the stream.
+ */
+ public void decode( InputStream compressedData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ log.warn( "Warning: DCTFilter.decode is not implemented yet, skipping this stream." );
+ }
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData The raw data to encode.
+ * @param result The place to write to encoded results to.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error compressing the stream.
+ */
+ public void encode( InputStream rawData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ log.warn( "Warning: DCTFilter.encode is not implemented yet, skipping this stream." );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/Filter.java b/src/main/java/org/pdfbox/filter/Filter.java
new file mode 100644
index 0000000..8de3f81
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/Filter.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is the interface that will be used to apply filters to a byte stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public interface Filter
+{
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData The compressed byte stream.
+ * @param result The place to write the uncompressed byte stream.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error decompressing the stream.
+ */
+ public void decode( InputStream compressedData, OutputStream result, COSDictionary options ) throws IOException;
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData The raw data to encode.
+ * @param result The place to write to encoded results to.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error compressing the stream.
+ */
+ public void encode( InputStream rawData, OutputStream result, COSDictionary options ) throws IOException;
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/FilterManager.java b/src/main/java/org/pdfbox/filter/FilterManager.java
new file mode 100644
index 0000000..87b1ad4
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/FilterManager.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.IOException;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pdfbox.cos.COSName;
+
+/**
+ * This will contain manage all the different types of filters that are available.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.12 $
+ */
+public class FilterManager
+{
+ private Map filters = new HashMap();
+
+ /**
+ * Constructor.
+ */
+ public FilterManager()
+ {
+ Filter flateFilter = new FlateFilter();
+ Filter dctFilter = new DCTFilter();
+ Filter ccittFaxFilter = new CCITTFaxDecodeFilter();
+ Filter lzwFilter = new LZWFilter();
+ Filter asciiHexFilter = new ASCIIHexFilter();
+ Filter ascii85Filter = new ASCII85Filter();
+ Filter runLengthFilter = new RunLengthDecodeFilter();
+
+ addFilter( COSName.FLATE_DECODE, flateFilter );
+ addFilter( COSName.FLATE_DECODE_ABBREVIATION, flateFilter );
+ addFilter( COSName.DCT_DECODE, dctFilter );
+ addFilter( COSName.DCT_DECODE_ABBREVIATION, dctFilter );
+ addFilter( COSName.CCITTFAX_DECODE, ccittFaxFilter );
+ addFilter( COSName.CCITTFAX_DECODE_ABBREVIATION, ccittFaxFilter );
+ addFilter( COSName.LZW_DECODE, lzwFilter );
+ addFilter( COSName.LZW_DECODE_ABBREVIATION, lzwFilter );
+ addFilter( COSName.ASCII_HEX_DECODE, asciiHexFilter );
+ addFilter( COSName.ASCII_HEX_DECODE_ABBREVIATION, asciiHexFilter );
+ addFilter( COSName.ASCII85_DECODE, ascii85Filter );
+ addFilter( COSName.ASCII85_DECODE_ABBREVIATION, ascii85Filter );
+ addFilter( COSName.RUN_LENGTH_DECODE, runLengthFilter );
+ addFilter( COSName.RUN_LENGTH_DECODE_ABBREVIATION, runLengthFilter );
+
+ }
+
+ /**
+ * This will get all of the filters that are available in the system.
+ *
+ * @return All available filters in the system.
+ */
+ public Collection getFilters()
+ {
+ return filters.values();
+ }
+
+ /**
+ * This will add an available filter.
+ *
+ * @param filterName The name of the filter.
+ * @param filter The filter to use.
+ */
+ public void addFilter( COSName filterName, Filter filter )
+ {
+ filters.put( filterName, filter );
+ }
+
+ /**
+ * This will get a filter by name.
+ *
+ * @param filterName The name of the filter to retrieve.
+ *
+ * @return The filter that matches the name.
+ *
+ * @throws IOException If the filter could not be found.
+ */
+ public Filter getFilter( COSName filterName ) throws IOException
+ {
+ Filter filter = (Filter)filters.get( filterName );
+ if( filter == null )
+ {
+ throw new IOException( "Unknown stream filter:" + filterName );
+ }
+
+ return filter;
+ }
+
+ /**
+ * This will get a filter by name.
+ *
+ * @param filterName The name of the filter to retrieve.
+ *
+ * @return The filter that matches the name.
+ *
+ * @throws IOException If the filter could not be found.
+ */
+ public Filter getFilter( String filterName ) throws IOException
+ {
+ return getFilter( COSName.getPDFName( filterName ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/FlateFilter.java b/src/main/java/org/pdfbox/filter/FlateFilter.java
new file mode 100644
index 0000000..c239686
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/FlateFilter.java
@@ -0,0 +1,303 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is the used for the FlateDecode filter.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author Marcel Kammer
+ * @version $Revision: 1.9 $
+ */
+public class FlateFilter implements Filter
+{
+ private static final int BUFFER_SIZE = 2048;
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData
+ * The compressed byte stream.
+ * @param result
+ * The place to write the uncompressed byte stream.
+ * @param options
+ * The options to use to encode the data.
+ *
+ * @throws IOException
+ * If there is an error decompressing the stream.
+ */
+
+ public void decode(InputStream compressedData, OutputStream result, COSDictionary options) throws IOException
+ {
+ COSDictionary dict = (COSDictionary) options.getDictionaryObject("DecodeParms");
+ int predictor = -1;
+ int colors = -1;
+ int bitsPerPixel = -1;
+ int columns = -1;
+ InflaterInputStream decompressor = null;
+ ByteArrayInputStream bais = null;
+ ByteArrayOutputStream baos = null;
+ if (dict!=null)
+ {
+ predictor = dict.getInt("Predictor");
+ colors = dict.getInt("Colors");
+ bitsPerPixel = options.getInt("BitsPerComponent");
+ columns = dict.getInt("Columns");
+ }
+
+ try
+ {
+ // Decompress data to temporary ByteArrayOutputStream
+ decompressor = new InflaterInputStream(compressedData);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int amountRead;
+
+ // Decode data using given predictor
+ if (predictor==-1 || predictor == 1 && predictor == 10)
+ {
+ // decoding not needed
+ while ((amountRead = decompressor.read(buffer, 0, BUFFER_SIZE)) != -1)
+ {
+ result.write(buffer, 0, amountRead);
+ }
+ }
+ else
+ {
+ if (colors==-1 || bitsPerPixel==-1 || columns==-1)
+ {
+ throw new IOException("Could not read all parameters to decode image");
+ }
+
+ baos = new ByteArrayOutputStream();
+ while ((amountRead = decompressor.read(buffer, 0, BUFFER_SIZE)) != -1)
+ {
+ baos.write(buffer, 0, amountRead);
+ }
+ baos.flush();
+
+ // Copy data to ByteArrayInputStream for reading
+ bais = new ByteArrayInputStream(baos.toByteArray());
+ baos.close();
+ baos = null;
+
+ byte[] decodedData = decodePredictor(predictor, colors, bitsPerPixel, columns, bais);
+ bais.close();
+ bais = new ByteArrayInputStream(decodedData);
+
+ // write decoded data to result
+ while ((amountRead = bais.read(buffer)) != -1)
+ {
+ result.write(buffer, 0, amountRead);
+ }
+ bais.close();
+ bais = null;
+ }
+
+
+ result.flush();
+ }
+ finally
+ {
+ if (decompressor != null)
+ {
+ decompressor.close();
+ }
+ if (bais != null)
+ {
+ bais.close();
+ }
+ if (baos != null)
+ {
+ baos.close();
+ }
+ }
+ }
+
+ private byte[] decodePredictor(int predictor, int colors, int bitsPerComponent, int columns, InputStream data)
+ throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[2048];
+
+ if (predictor == 1 || predictor == 10)
+ {
+ // No prediction or PNG NONE
+ int i = 0;
+ while ((i = data.read(buffer)) != -1)
+ {
+ baos.write(buffer, 0, i);
+ }
+ }
+ else
+ {
+ // calculate sizes
+ int bpp = (colors * bitsPerComponent + 7) / 8;
+ int rowlength = (columns * colors * bitsPerComponent + 7) / 8 + bpp;
+ byte[] actline = new byte[rowlength];
+ byte[] lastline = new byte[rowlength];// Initialize lastline with
+ // Zeros according to
+ // PNG-specification
+ boolean done = false;
+ int linepredictor = predictor;
+
+ while (!done)
+ {
+ if (predictor == 15)
+ {
+ linepredictor = data.read();// read per line predictor
+ if (linepredictor == -1)
+ {
+ done = true;// reached EOF
+ break;
+ }
+ else
+ {
+ linepredictor += 10; // add 10 to tread value 1 as 11
+ }
+ // (instead of PRED NONE) and 2
+ // as 12 (instead of PRED TIFF)
+ }
+
+ // read line
+ int i = 0;
+ int offset = bpp;
+ while (offset < rowlength && ((i = data.read(actline, offset, rowlength - offset)) != -1))
+ {
+ offset += i;
+ }
+
+ // Do prediction as specified in PNG-Specification 1.2
+ switch (linepredictor)
+ {
+ case 2:// PRED TIFF SUB
+ /**
+ * @todo decode tiff
+ */
+ throw new IOException("TIFF-Predictor not supported");
+ case 11:// PRED SUB
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int sub = actline[p] & 0xff;
+ int left = actline[p - bpp] & 0xff;
+ actline[p] = (byte) (sub + left);
+ }
+ break;
+ case 12:// PRED UP
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int up = actline[p] & 0xff;
+ int prior = lastline[p] & 0xff;
+ actline[p] = (byte) (up + prior);
+ }
+ break;
+ case 13:// PRED AVG
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int avg = actline[p] & 0xff;
+ int left = actline[p - bpp] & 0xff;
+ int up = lastline[p] & 0xff;
+ actline[p] = (byte) (avg + ((left + up) / 2));
+ }
+ break;
+ case 14:// PRED PAETH
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int paeth = actline[p] & 0xff;
+ int a = actline[p - bpp] & 0xff;// left
+ int b = lastline[p] & 0xff;// upper
+ int c = lastline[p - bpp] & 0xff;// upperleft
+ int value = a + b - c;
+ int absa = Math.abs(value - a);
+ int absb = Math.abs(value - b);
+ int absc = Math.abs(value - c);
+
+ if (absa <= absb && absa <= absc)
+ {
+ actline[p] = (byte) (paeth + absa);
+ }
+ else if (absb <= absc)
+ {
+ actline[p] += (byte) (paeth + absb);
+ }
+ else
+ {
+ actline[p] += (byte) (paeth + absc);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ lastline = actline;
+ baos.write(actline, bpp, actline.length - bpp);
+ }
+ }
+
+ return baos.toByteArray();
+ }
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData
+ * The raw data to encode.
+ * @param result
+ * The place to write to encoded results to.
+ * @param options
+ * The options to use to encode the data.
+ *
+ * @throws IOException
+ * If there is an error compressing the stream.
+ */
+ public void encode(InputStream rawData, OutputStream result, COSDictionary options) throws IOException
+ {
+ DeflaterOutputStream out = new DeflaterOutputStream(result);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int amountRead = 0;
+ while ((amountRead = rawData.read(buffer, 0, BUFFER_SIZE)) != -1)
+ {
+ out.write(buffer, 0, amountRead);
+ }
+ out.close();
+ result.flush();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/LZWDictionary.java b/src/main/java/org/pdfbox/filter/LZWDictionary.java
new file mode 100644
index 0000000..075dab8
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/LZWDictionary.java
@@ -0,0 +1,215 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is the used for the LZWDecode filter. This represents the dictionary mappings
+ * between codes and their values.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+class LZWDictionary
+{
+ private Map codeToData = new HashMap();
+ private LZWNode root = new LZWNode();
+
+ private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ private long nextCode = 258;
+ private int codeSize = 9;
+
+ /**
+ * constructor.
+ */
+ public LZWDictionary()
+ {
+ for( long i=0; i<256; i++ )
+ {
+ LZWNode node = new LZWNode();
+ node.setCode( i );
+ root.setNode( (byte)i, node );
+ codeToData.put( new Long( i ), new byte[]{ (byte)i } );
+ }
+ }
+
+ /**
+ * This will get the value for the code. It will return null if the code is not
+ * defined.
+ *
+ * @param code The key to the data.
+ *
+ * @return The data that is mapped to the code.
+ */
+ public byte[] getData( long code )
+ {
+ return (byte[])codeToData.get( new Long( code ) );
+ }
+
+ /**
+ * This will take a visit from a byte[]. This will create new code entries as
+ * necessary.
+ *
+ * @param data The byte to get a visit from.
+ *
+ * @throws IOException If there is an error visiting this data.
+ */
+ public void visit( byte[] data ) throws IOException
+ {
+ for( int i=0; i<data.length; i++ )
+ {
+ visit( data[i] );
+ }
+ }
+
+ /**
+ * This will take a visit from a byte. This will create new code entries as
+ * necessary.
+ *
+ * @param data The byte to get a visit from.
+ *
+ * @throws IOException If there is an error visiting this data.
+ */
+ public void visit( byte data ) throws IOException
+ {
+ buffer.write( data );
+ byte[] curBuffer = buffer.toByteArray();
+ LZWNode previous = null;
+ LZWNode current = root;
+ boolean createNewCode = false;
+ for( int i=0; i<curBuffer.length && current != null; i++ )
+ {
+ previous = current;
+ current = current.getNode( curBuffer[i] );
+ if( current == null )
+ {
+ createNewCode = true;
+ current = new LZWNode();
+ previous.setNode( curBuffer[i], current );
+ }
+ }
+ if( createNewCode )
+ {
+ long code = nextCode++;
+ current.setCode( code );
+ codeToData.put( new Long( code ), curBuffer );
+
+ /**
+ System.out.print( "Adding " + code + "='" );
+ for( int i=0; i<curBuffer.length; i++ )
+ {
+ String hex = Integer.toHexString( ((curBuffer[i]+256)%256) );
+ if( hex.length() <=1 )
+ {
+ hex = "0" + hex;
+ }
+ if( i != curBuffer.length -1 )
+ {
+ hex += " ";
+ }
+ System.out.print( hex.toUpperCase() );
+ }
+ System.out.println( "'" );
+ **/
+ buffer.reset();
+ buffer.write( data );
+ resetCodeSize();
+ }
+ }
+
+ /**
+ * This will get the next code that will be created.
+ *
+ * @return The next code to be created.
+ */
+ public long getNextCode()
+ {
+ return nextCode;
+ }
+
+ /**
+ * This will get the size of the code in bits, 9, 10, or 11.
+ *
+ * @return The size of the code in bits.
+ */
+ public int getCodeSize()
+ {
+ return codeSize;
+ }
+
+ /**
+ * This will determine the code size.
+ */
+ private void resetCodeSize()
+ {
+ if( nextCode >= 2048 )
+ {
+ codeSize = 12;
+ }
+ else if( nextCode >= 1024 )
+ {
+ codeSize = 11;
+ }
+ else if( nextCode >= 512 )
+ {
+ codeSize = 10;
+ }
+ else
+ {
+ codeSize = 9;
+ }
+ }
+
+ /**
+ * This will crear the internal buffer that the dictionary uses.
+ */
+ public void clear()
+ {
+ buffer.reset();
+ }
+
+ /**
+ * This will folow the path to the data node.
+ *
+ * @param data The path to the node.
+ *
+ * @return The node that resides at that path.
+ */
+ public LZWNode getNode( byte[] data )
+ {
+ return root.getNode( data );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/LZWFilter.java b/src/main/java/org/pdfbox/filter/LZWFilter.java
new file mode 100644
index 0000000..e8ba003
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/LZWFilter.java
@@ -0,0 +1,235 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.StreamCorruptedException;
+
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.io.NBitInputStream;
+import org.pdfbox.io.NBitOutputStream;
+
+/**
+ * This is the used for the LZWDecode filter.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.13 $
+ */
+public class LZWFilter implements Filter
+{
+
+ /**
+ * The LZW clear table code.
+ */
+ public static final long CLEAR_TABLE = 256;
+ /**
+ * The LZW end of data code.
+ */
+ public static final long EOD = 257;
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData The compressed byte stream.
+ * @param result The place to write the uncompressed byte stream.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error decompressing the stream.
+ */
+ public void decode( InputStream compressedData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ //log.debug("decode( )");
+ NBitInputStream in = null;
+ in = new NBitInputStream( compressedData );
+ in.setBitsInChunk( 9 );
+ LZWDictionary dic = new LZWDictionary();
+ byte firstByte = 0;
+ long nextCommand = 0;
+ while( (nextCommand = in.read() ) != EOD )
+ {
+ // log.debug( "decode - nextCommand=" + nextCommand + ", bitsInChunk: " + in.getBitsInChunk());
+
+ if( nextCommand == CLEAR_TABLE )
+ {
+ in.setBitsInChunk( 9 );
+ dic = new LZWDictionary();
+ }
+ else
+ {
+ byte[] data = dic.getData( nextCommand );
+ if( data == null )
+ {
+ dic.visit( firstByte );
+ data = dic.getData( nextCommand );
+ dic.clear();
+ }
+ if( data == null )
+ {
+ throw new StreamCorruptedException( "Error: data is null" );
+ }
+ dic.visit(data);
+
+ //log.debug( "decode - dic.getNextCode(): " + dic.getNextCode());
+
+ if( dic.getNextCode() >= 2047 )
+ {
+ in.setBitsInChunk( 12 );
+ }
+ else if( dic.getNextCode() >= 1023 )
+ {
+ in.setBitsInChunk( 11 );
+ }
+ else if( dic.getNextCode() >= 511 )
+ {
+ in.setBitsInChunk( 10 );
+ }
+ else
+ {
+ in.setBitsInChunk( 9 );
+ }
+ /**
+ if( in.getBitsInChunk() != dic.getCodeSize() )
+ {
+ in.unread( nextCommand );
+ in.setBitsInChunk( dic.getCodeSize() );
+ System.out.print( "Switching " + nextCommand + " to " );
+ nextCommand = in.read();
+ System.out.println( "" + nextCommand );
+ data = dic.getData( nextCommand );
+ }**/
+ firstByte = data[0];
+ result.write( data );
+ }
+ }
+ result.flush();
+ }
+
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData The raw data to encode.
+ * @param result The place to write to encoded results to.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error compressing the stream.
+ */
+ public void encode( InputStream rawData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ //log.debug("encode( )");
+ PushbackInputStream input = new PushbackInputStream( rawData, 4096 );
+ LZWDictionary dic = new LZWDictionary();
+ NBitOutputStream out = new NBitOutputStream( result );
+ out.setBitsInChunk( 9 ); //initially nine
+ out.write( CLEAR_TABLE );
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int byteRead = 0;
+ for( int i=0; (byteRead = input.read()) != -1; i++ )
+ {
+ //log.debug( "byteRead = '" + (char)byteRead + "' (0x" + Integer.toHexString(byteRead) + "), i=" + i);
+ buffer.write( byteRead );
+ dic.visit( (byte)byteRead );
+ out.setBitsInChunk( dic.getCodeSize() );
+
+ //log.debug( "Getting node '" + new String( buffer.toByteArray() ) + "', buffer.size = " + buffer.size() );
+ LZWNode node = dic.getNode( buffer.toByteArray() );
+ int nextByte = input.read();
+ if( nextByte != -1 )
+ {
+ //log.debug( "nextByte = '" + (char)nextByte + "' (0x" + Integer.toHexString(nextByte) + ")");
+ LZWNode next = node.getNode( (byte)nextByte );
+ if( next == null )
+ {
+ //log.debug("encode - No next node, writing node and resetting buffer (" +
+ // " node.getCode: " + node.getCode() + ")" +
+ // " bitsInChunk: " + out.getBitsInChunk() +
+ // ")");
+ out.write( node.getCode() );
+ buffer.reset();
+ }
+
+ input.unread( nextByte );
+ }
+ else
+ {
+ //log.debug("encode - EOF on lookahead: writing node, resetting buffer, and terminating read loop (" +
+ // " node.getCode: " + node.getCode() + ")" +
+ // " bitsInChunk: " + out.getBitsInChunk() +
+ // ")");
+ out.write( node.getCode() );
+ buffer.reset();
+ break;
+ }
+
+ if( dic.getNextCode() == 4096 )
+ {
+ //log.debug("encode - Clearing dictionary and unreading pending buffer data (" +
+ // " bitsInChunk: " + out.getBitsInChunk() +
+ // ")");
+ out.write( CLEAR_TABLE );
+ dic = new LZWDictionary();
+ input.unread( buffer.toByteArray() );
+ buffer.reset();
+ }
+ }
+
+ // Fix the code size based on the fact that we are writing the EOD
+ //
+ if( dic.getNextCode() >= 2047 )
+ {
+ out.setBitsInChunk( 12 );
+ }
+ else if( dic.getNextCode() >= 1023 )
+ {
+ out.setBitsInChunk( 11 );
+ }
+ else if( dic.getNextCode() >= 511 )
+ {
+ out.setBitsInChunk( 10 );
+ }
+ else
+ {
+ out.setBitsInChunk( 9 );
+ }
+
+ //log.debug("encode - Writing EOD (" +
+ // " bitsInChunk: " + out.getBitsInChunk() +
+ // ")");
+ out.write( EOD );
+ out.close();
+ result.flush();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/LZWNode.java b/src/main/java/org/pdfbox/filter/LZWNode.java
new file mode 100644
index 0000000..563ea5d
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/LZWNode.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is the used for the LZWDecode filter.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+class LZWNode
+{
+ private long code;
+ private Map subNodes = new HashMap();
+
+ /**
+ * This will get the number of children.
+ *
+ * @return The number of children.
+ */
+ public int childCount()
+ {
+ return subNodes.size();
+ }
+
+ /**
+ * This will set the node for a particular byte.
+ *
+ * @param b The byte for that node.
+ * @param node The node to add.
+ */
+ public void setNode( byte b, LZWNode node )
+ {
+ subNodes.put( new Byte( b ), node );
+ }
+
+ /**
+ * This will get the node that is a direct sub node of this node.
+ *
+ * @param data The byte code to the node.
+ *
+ * @return The node at that value if it exists.
+ */
+ public LZWNode getNode( byte data )
+ {
+ return (LZWNode)subNodes.get( new Byte( data ) );
+ }
+
+
+ /**
+ * This will traverse the tree until it gets to the sub node.
+ * This will return null if the node does not exist.
+ *
+ * @param data The path to the node.
+ *
+ * @return The node that resides at the data path.
+ */
+ public LZWNode getNode( byte[] data )
+ {
+ LZWNode current = this;
+ for( int i=0; i<data.length && current != null; i++ )
+ {
+ current = current.getNode( data[i] );
+ }
+ return current;
+ }
+
+ /** Getter for property code.
+ * @return Value of property code.
+ */
+ public long getCode()
+ {
+ return code;
+ }
+
+ /** Setter for property code.
+ * @param codeValue New value of property code.
+ */
+ public void setCode(long codeValue)
+ {
+ code = codeValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/RunLengthDecodeFilter.java b/src/main/java/org/pdfbox/filter/RunLengthDecodeFilter.java
new file mode 100644
index 0000000..d1fe1c1
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/RunLengthDecodeFilter.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is a filter for the RunLength Decoder.
+ *
+ * From the PDF Reference
+ * <pre>
+ * The RunLengthDecode filter decodes data that has been encoded in a simple
+ * byte-oriented format based on run length. The encoded data is a sequence of
+ * runs, where each run consists of a length byte followed by 1 to 128 bytes of data. If
+ * the length byte is in the range 0 to 127, the following length + 1 (1 to 128) bytes
+ * are copied literally during decompression. If length is in the range 129 to 255, the
+ * following single byte is to be copied 257 ? length (2 to 128) times during decompression.
+ * A length value of 128 denotes EOD.
+ *
+ * The compression achieved by run-length encoding depends on the input data. In
+ * the best case (all zeros), a compression of approximately 64:1 is achieved for long
+ * files. The worst case (the hexadecimal sequence 00 alternating with FF) results in
+ * an expansion of 127:128.
+ * </pre>
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.3 $
+ */
+public class RunLengthDecodeFilter implements Filter
+{
+ private static Logger log = Logger.getLogger( RunLengthDecodeFilter.class );
+
+ private static final int RUN_LENGTH_EOD = 128;
+
+ /**
+ * Constructor.
+ */
+ public RunLengthDecodeFilter()
+ {
+ //default constructor
+ }
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData The compressed byte stream.
+ * @param result The place to write the uncompressed byte stream.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error decompressing the stream.
+ */
+ public void decode( InputStream compressedData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ int dupAmount = -1;
+ byte[] buffer = new byte[128];
+ while( (dupAmount = compressedData.read()) != -1 && dupAmount != RUN_LENGTH_EOD )
+ {
+ if( dupAmount <= 127 )
+ {
+ int amountToCopy = dupAmount+1;
+ int compressedRead = 0;
+ while( amountToCopy > 0 )
+ {
+ compressedRead = compressedData.read( buffer, 0, amountToCopy );
+ result.write( buffer, 0, compressedRead );
+ amountToCopy -= compressedRead;
+ }
+ }
+ else
+ {
+ int dupByte = compressedData.read();
+ for( int i=0; i<257-dupAmount; i++ )
+ {
+ result.write( dupByte );
+ }
+ }
+ }
+ }
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData The raw data to encode.
+ * @param result The place to write to encoded results to.
+ * @param options The options to use to encode the data.
+ *
+ * @throws IOException If there is an error compressing the stream.
+ */
+ public void encode( InputStream rawData, OutputStream result, COSDictionary options ) throws IOException
+ {
+ log.warn( "Warning: RunLengthDecodeFilter.encode is not implemented yet, skipping this stream." );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/filter/package.html b/src/main/java/org/pdfbox/filter/package.html
new file mode 100644
index 0000000..36dbb7d
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package will hold the PDFBox implementations of the filters that are used in PDF documents.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/io/ASCII85InputStream.java b/src/main/java/org/pdfbox/io/ASCII85InputStream.java
new file mode 100644
index 0000000..863d64d
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/ASCII85InputStream.java
@@ -0,0 +1,269 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * This class represents an ASCII85 stream.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.5 $
+ */
+public class ASCII85InputStream extends FilterInputStream
+{
+ private int index;
+ private int n;
+ private boolean eof;
+
+ private byte[] ascii;
+ private byte[] b;
+
+ /**
+ * Constructor.
+ *
+ * @param is The input stream to actually read from.
+ */
+ public ASCII85InputStream( InputStream is )
+ {
+ super(is);
+ index = 0;
+ n = 0;
+ eof = false;
+ ascii = new byte[5];
+ b = new byte[4];
+ }
+
+ /**
+ * This will read the next byte from the stream.
+ *
+ * @return The next byte read from the stream.
+ *
+ * @throws IOException If there is an error reading from the wrapped stream.
+ */
+ public final int read() throws IOException
+ {
+ if( index >= n )
+ {
+ if(eof)
+ {
+ return -1;
+ }
+ index = 0;
+ int k;
+ byte z;
+ do
+ {
+ int zz=(byte)in.read();
+ if(zz==-1)
+ {
+ eof=true;
+ return -1;
+ }
+ z = (byte)zz;
+ } while( z=='\n' || z=='\r' || z==' ');
+
+ if (z == '~' || z=='x')
+ {
+ eof=true;
+ ascii=b=null;
+ n = 0;
+ return -1;
+ }
+ else if (z == 'z')
+ {
+ b[0]=b[1]=b[2]=b[3]=0;
+ n = 4;
+ }
+ else
+ {
+ ascii[0]=z; // may be EOF here....
+ for (k=1;k<5;++k)
+ {
+ do
+ {
+ int zz=(byte)in.read();
+ if(zz==-1)
+ {
+ eof=true;
+ return -1;
+ }
+ z=(byte)zz;
+ } while ( z=='\n' || z=='\r' || z==' ' );
+ ascii[k]=z;
+ if (z == '~' || z=='x')
+ {
+ break;
+ }
+ }
+ n = k - 1;
+ if ( n==0 )
+ {
+ eof = true;
+ ascii = null;
+ b = null;
+ return -1;
+ }
+ if ( k < 5 )
+ {
+ for (++k; k < 5; ++k )
+ {
+ ascii[k] = 0x21;
+ }
+ eof=true;
+ }
+ // decode stream
+ long t=0;
+ for ( k=0; k<5; ++k)
+ {
+ z=(byte)(ascii[k] - 0x21);
+ if (z < 0 || z > 93)
+ {
+ n = 0;
+ eof = true;
+ ascii = null;
+ b = null;
+ throw new IOException("Invalid data in Ascii85 stream");
+ }
+ t = (t * 85L) + z;
+ }
+ for ( k = 3; k>=0; --k)
+ {
+ b[k] = (byte)(t & 0xFFL);
+ t>>>=8;
+ }
+ }
+ }
+ return b[index++] & 0xFF;
+ }
+
+ /**
+ * This will read a chunk of data.
+ *
+ * @param data The buffer to write data to.
+ * @param offset The offset into the data stream.
+ * @param len The number of byte to attempt to read.
+ *
+ * @return The number of bytes actually read.
+ *
+ * @throws IOException If there is an error reading data from the underlying stream.
+ */
+ public final int read(byte[] data, int offset, int len) throws IOException
+ {
+ if(eof && index>=n)
+ {
+ return -1;
+ }
+ for(int i=0;i<len;i++)
+ {
+ if(index<n)
+ {
+ data[i+offset]=b[index++];
+ }
+ else
+ {
+ int t = read();
+ if ( t == -1 )
+ {
+ return i;
+ }
+ data[i+offset]=(byte)t;
+ }
+ }
+ return len;
+ }
+
+ /**
+ * This will close the underlying stream and release any resources.
+ *
+ * @throws IOException If there is an error closing the underlying stream.
+ */
+ public void close() throws IOException
+ {
+ ascii = null;
+ eof = true;
+ b = null;
+ super.close();
+ }
+
+ /**
+ * non supported interface methods.
+ *
+ * @return False always.
+ */
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+ /**
+ * Unsupported.
+ *
+ * @param nValue ignored.
+ *
+ * @return Always zero.
+ */
+ public long skip(long nValue)
+ {
+ return 0;
+ }
+
+ /**
+ * Unsupported.
+ *
+ * @return Always zero.
+ */
+ public int available()
+ {
+ return 0;
+ }
+
+ /**
+ * Unsupported.
+ *
+ * @param readlimit ignored.
+ */
+ public void mark(int readlimit)
+ {
+ }
+
+ /**
+ * Unsupported.
+ *
+ * @throws IOException telling that this is an unsupported action.
+ */
+ public void reset() throws IOException
+ {
+ throw new IOException("Reset is not supported");
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/ASCII85OutputStream.java b/src/main/java/org/pdfbox/io/ASCII85OutputStream.java
new file mode 100644
index 0000000..a59f2df
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/ASCII85OutputStream.java
@@ -0,0 +1,304 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class represents an ASCII85 output stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class ASCII85OutputStream extends FilterOutputStream
+{
+
+ private int lineBreak;
+ private int count;
+
+ private byte[] indata;
+ private byte[] outdata;
+
+ /**
+ * Function produces five ASCII printing characters from
+ * four bytes of binary data.
+ */
+ private int maxline;
+ private boolean flushed;
+ private char terminator;
+
+ /**
+ * Constructor.
+ *
+ * @param out The output stream to write to.
+ */
+ public ASCII85OutputStream( OutputStream out )
+ {
+ super( out );
+ lineBreak = 36*2;
+ maxline = 36*2;
+ count=0;
+ indata=new byte[4];
+ outdata=new byte[5];
+ flushed=true;
+ terminator='~';
+ }
+
+ /**
+ * This will set the terminating character.
+ *
+ * @param term The terminating character.
+ */
+ public void setTerminator(char term)
+ {
+ if(term<118 || term>126 || term=='z')
+ {
+ throw new IllegalArgumentException("Terminator must be 118-126 excluding z");
+ }
+ terminator=term;
+ }
+
+ /**
+ * This will get the terminating character.
+ *
+ * @return The terminating character.
+ */
+ public char getTerminator()
+ {
+ return terminator;
+ }
+
+ /**
+ * This will set the line length that will be used.
+ *
+ * @param l The length of the line to use.
+ */
+ public void setLineLength(int l)
+ {
+ if( lineBreak > l )
+ {
+ lineBreak = l;
+ }
+ maxline=l;
+ }
+
+ /**
+ * This will get the length of the line.
+ *
+ * @return The line length attribute.
+ */
+ public int getLineLength()
+ {
+ return maxline;
+ }
+
+ /**
+ * This will transform the next four ascii bytes.
+ */
+ private final void transformASCII85()
+ {
+ long word;
+ word=( (((indata[0] << 8) | (indata[1] &0xFF)) << 16) |
+ ( (indata[2] & 0xFF) << 8) | (indata[3] & 0xFF)
+ ) & 0xFFFFFFFFL;
+ // System.out.println("word=0x"+Long.toString(word,16)+" "+word);
+
+ if (word == 0 )
+ {
+ outdata[0]=(byte)'z';
+ outdata[1]=0;
+ return;
+ }
+ long x;
+ x=word/(85L*85L*85L*85L);
+ // System.out.println("x0="+x);
+ outdata[0]=(byte)(x+'!');
+ word-=x*85L*85L*85L*85L;
+
+ x=word/(85L*85L*85L);
+ // System.out.println("x1="+x);
+ outdata[1]=(byte)(x+'!');
+ word-=x*85L*85L*85L;
+
+ x=word/(85L*85L);
+ // System.out.println("x2="+x);
+ outdata[2]=(byte)(x+'!');
+ word-=x*85L*85L;
+
+ x=word/85L;
+ // System.out.println("x3="+x);
+ outdata[3]=(byte)(x+'!');
+
+ // word-=x*85L;
+
+ // System.out.println("x4="+(word % 85L));
+ outdata[4]=(byte)((word%85L)+'!');
+ }
+
+ /**
+ * This will write a single byte.
+ *
+ * @param b The byte to write.
+ *
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public final void write(int b) throws IOException
+ {
+ flushed=false;
+ indata[count++]=(byte)b;
+ if(count < 4 )
+ {
+ return;
+ }
+ transformASCII85();
+ for(int i=0;i<5;i++)
+ {
+ if(outdata[i]==0)
+ {
+ break;
+ }
+ out.write(outdata[i]);
+ if(--lineBreak==0)
+ {
+ out.write('\n');
+ lineBreak=maxline;
+ }
+ }
+ count = 0;
+ }
+
+ /**
+ * This will write a chunk of data to the stream.
+ *
+ * @param b The byte buffer to read from.
+ * @param off The offset into the buffer.
+ * @param sz The number of bytes to read from the buffer.
+ *
+ * @throws IOException If there is an error writing to the underlying stream.
+ */
+ public final void write(byte[] b,int off, int sz) throws IOException
+ {
+ for(int i=0;i<sz;i++)
+ {
+ if(count < 3)
+ {
+ indata[count++]=b[off+i];
+ }
+ else
+ {
+ write(b[off+i]);
+ }
+ }
+ }
+
+ /**
+ * This will flush the data to the stream.
+ *
+ * @throws IOException If there is an error writing the data to the stream.
+ */
+ public final void flush() throws IOException
+ {
+ if(flushed)
+ {
+ return;
+ }
+ if(count > 0 )
+ {
+ for( int i=count; i<4; i++ )
+ {
+ indata[i]=0;
+ }
+ transformASCII85();
+ if(outdata[0]=='z')
+ {
+ for(int i=0;i<5;i++) // expand 'z',
+ {
+ outdata[i]=(byte)'!';
+ }
+ }
+ for(int i=0;i<count+1;i++)
+ {
+ out.write(outdata[i]);
+ if(--lineBreak==0)
+ {
+ out.write('\n');
+ lineBreak=maxline;
+ }
+ }
+ }
+ if(--lineBreak==0)
+ {
+ out.write('\n');
+ }
+ out.write(terminator);
+ out.write('\n');
+ count = 0;
+ lineBreak=maxline;
+ flushed=true;
+ super.flush();
+ }
+
+ /**
+ * This will close the stream.
+ *
+ * @throws IOException If there is an error closing the wrapped stream.
+ */
+ public void close() throws IOException
+ {
+ try
+ {
+ super.close();
+ }
+ finally
+ {
+ indata=outdata=null;
+ }
+ }
+
+ /**
+ * This will flush the stream.
+ *
+ * @throws Throwable If there is an error.
+ */
+ protected void finalize() throws Throwable
+ {
+ try
+ {
+ flush();
+ }
+ finally
+ {
+ super.finalize();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/ByteArrayPushBackInputStream.java b/src/main/java/org/pdfbox/io/ByteArrayPushBackInputStream.java
new file mode 100644
index 0000000..e72cbbb
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/ByteArrayPushBackInputStream.java
@@ -0,0 +1,404 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * PushBackInputStream for byte arrays.
+ *
+ * The inheritance from PushBackInputStream is only to avoid the
+ * introduction of an interface with all PushBackInputStream
+ * methods. The parent PushBackInputStream is not used in any way and
+ * all methods are overridden. (Thus when adding new methods to PushBackInputStream
+ * override them in this class as well!)
+ * unread() is limited to the number of bytes already read from this stream (i.e.
+ * the current position in the array). This limitation usually poses no problem
+ * to a parser, but allows for some optimization since only one array has to
+ * be dealt with.
+ *
+ * Note: This class is not thread safe. Clients must provide synchronization
+ * if needed.
+ *
+ * Note: Calling unread() after mark() will cause (part of) the unread data to be
+ * read again after reset(). Thus do not call unread() between mark() and reset().
+ *
+ * @author Andreas Weiss (andreas.weiss@switzerland.org)
+ * @version $Revision: 1.2 $
+ */
+public class ByteArrayPushBackInputStream extends PushBackInputStream
+{
+ private byte[] data;
+ private int datapos;
+ private int datalen;
+ private int save;
+
+ // dummy for base class constructor
+ private static final InputStream DUMMY = new ByteArrayInputStream("".getBytes());
+
+ /**
+ * Constructor.
+ * @param input Data to read from. Note that calls to unread() will
+ * modify this array! If this is not desired, pass a copy.
+ *
+ * @throws IOException If there is an IO error.
+ */
+ public ByteArrayPushBackInputStream(byte[] input) throws IOException
+ {
+ super(DUMMY, 1);
+ data = input;
+ datapos = 0;
+ save = datapos;
+ datalen = input != null ? input.length : 0;
+ }
+
+ /**
+ * This will peek at the next byte.
+ *
+ * @return The next byte on the stream, leaving it as available to read.
+ */
+ public int peek()
+ {
+ try
+ {
+ // convert negative values to 128..255
+ return (data[datapos] + 0x100) & 0xff;
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ // could check this before, but this is a rare case
+ // and this method is called sufficiently often to justify this
+ // optimization
+ return -1;
+ }
+ }
+
+ /**
+ * A simple test to see if we are at the end of the stream.
+ *
+ * @return true if we are at the end of the stream.
+ */
+ public boolean isEOF()
+ {
+ return datapos >= datalen;
+ }
+
+ /**
+ * Save the state of this stream.
+ * @param readlimit Has no effect.
+ * @see InputStream#mark(int)
+ */
+ public void mark(int readlimit)
+ {
+ if (false)
+ {
+ ++readlimit; // avoid unused param warning
+ }
+ save = datapos;
+ }
+
+ /**
+ * Check if mark is supported.
+ * @return Always true.
+ * @see InputStream#markSupported()
+ */
+ public boolean markSupported()
+ {
+ return true;
+ }
+
+ /**
+ * Restore the state of this stream to the last saveState call.
+ * @see InputStream#reset()
+ */
+ public void reset()
+ {
+ datapos = save;
+ }
+
+ /** Available bytes.
+ * @see InputStream#available()
+ * @return Available bytes.
+ */
+ public int available()
+ {
+ int av = datalen - datapos;
+ return av > 0 ? av : 0;
+ }
+
+ /** Totally available bytes in the underlying array.
+ * @return Available bytes.
+ */
+ public int size()
+ {
+ return datalen;
+ }
+
+ /**
+ * Pushes back a byte.
+ * After this method returns, the next byte to be read will have the value (byte)by.
+ * @param by the int value whose low-order byte is to be pushed back.
+ * @throws IOException - If there is not enough room in the buffer for the byte.
+ * @see java.io.PushbackInputStream#unread(int)
+ */
+ public void unread(int by) throws IOException
+ {
+ if (datapos == 0)
+ {
+ throw new IOException("ByteArrayParserInputStream.unread(int): " +
+ "cannot unread 1 byte at buffer position " + datapos);
+ }
+ --datapos;
+ data[datapos] = (byte)by;
+ }
+
+ /**
+ * Pushes back a portion of an array of bytes by copying it to the
+ * front of the pushback buffer. After this method returns, the next byte
+ * to be read will have the value b[off], the byte after that will have
+ * the value b[off+1], and so forth.
+ * @param buffer the byte array to push back.
+ * @param off the start offset of the data.
+ * @param len the number of bytes to push back.
+ * @throws IOException If there is not enough room in the pushback buffer
+ * for the specified number of bytes.
+ * @see java.io.PushbackInputStream#unread(byte[], int, int)
+ */
+ public void unread(byte[] buffer, int off, int len) throws IOException
+ {
+ if (len <= 0 || off >= buffer.length)
+ {
+ return;
+ }
+ if (off < 0)
+ {
+ off = 0;
+ }
+ if (len > buffer.length)
+ {
+ len = buffer.length;
+ }
+ localUnread(buffer, off, len);
+ }
+
+ /**
+ * Pushes back a portion of an array of bytes by copying it to the
+ * front of the pushback buffer. After this method returns, the next byte
+ * to be read will have the value buffer[0], the byte after that will have
+ * the value buffer[1], and so forth.
+ * @param buffer the byte array to push back.
+ * @throws IOException If there is not enough room in the pushback buffer
+ * for the specified number of bytes.
+ * @see java.io.PushbackInputStream#unread(byte[])
+ */
+ public void unread(byte[] buffer) throws IOException
+ {
+ localUnread(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Pushes back a portion of an array of bytes by copying it to the
+ * front of the pushback buffer. After this method returns, the next byte
+ * to be read will have the value buffer[off], the byte after that will have
+ * the value buffer[off+1], and so forth.
+ * Internal method that assumes off and len to be valid.
+ * @param buffer the byte array to push back.
+ * @param off the start offset of the data.
+ * @param len the number of bytes to push back.
+ * @throws IOException If there is not enough room in the pushback buffer
+ * for the specified number of bytes.
+ * @see java.io.PushbackInputStream#unread(byte[], int, int)
+ */
+ private void localUnread(byte[] buffer, int off, int len) throws IOException
+ {
+ if (datapos < len)
+ {
+ throw new IOException("ByteArrayParserInputStream.unread(int): " +
+ "cannot unread " + len +
+ " bytes at buffer position " + datapos);
+ }
+ datapos -= len;
+ System.arraycopy(buffer, off, data, datapos, len);
+ }
+
+ /**
+ * Read a byte.
+ * @see InputStream#read()
+ * @return Byte read or -1 if no more bytes are available.
+ */
+ public int read()
+ {
+ try
+ {
+ // convert negative values to 128..255
+ return (data[datapos++] + 0x100) & 0xff;
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ // could check this before, but this is a rare case
+ // and this method is called sufficiently often to justify this
+ // optimization
+ datapos = datalen;
+ return -1;
+ }
+ }
+
+ /**
+ * Read a number of bytes.
+ * @see InputStream#read(byte[])
+ * @param buffer the buffer into which the data is read.
+ * @return the total number of bytes read into the buffer, or -1 if there
+ * is no more data because the end of the stream has been reached.
+ */
+ public int read(byte[] buffer)
+ {
+ return localRead(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Read a number of bytes.
+ * @see InputStream#read(byte[], int, int)
+ * @param buffer the buffer into which the data is read.
+ * @param off the start offset in array buffer at which the data is written.
+ * @param len the maximum number of bytes to read.
+ * @return the total number of bytes read into the buffer, or -1 if there
+ * is no more data because the end of the stream has been reached.
+ */
+ public int read(byte[] buffer, int off, int len)
+ {
+ if (len <= 0 || off >= buffer.length)
+ {
+ return 0;
+ }
+ if (off < 0)
+ {
+ off = 0;
+ }
+ if (len > buffer.length)
+ {
+ len = buffer.length;
+ }
+ return localRead(buffer, off, len);
+ }
+
+
+ /**
+ * Read a number of bytes. Internal method that assumes off and len to be
+ * valid.
+ * @see InputStream#read(byte[], int, int)
+ * @param buffer the buffer into which the data is read.
+ * @param off the start offset in array buffer at which the data is written.
+ * @param len the maximum number of bytes to read.
+ * @return the total number of bytes read into the buffer, or -1 if there
+ * is no more data because the end of the stream has been reached.
+ */
+ public int localRead(byte[] buffer, int off, int len)
+ {
+ if (len == 0)
+ {
+ return 0; // must return 0 even if at end!
+ }
+ else if (datapos >= datalen)
+ {
+ return -1;
+ }
+ else
+ {
+ int newpos = datapos + len;
+ if (newpos > datalen)
+ {
+ newpos = datalen;
+ len = newpos - datapos;
+ }
+ System.arraycopy(data, datapos, buffer, off, len);
+ datapos = newpos;
+ return len;
+ }
+ }
+
+ /**
+ * Skips over and discards n bytes of data from this input stream.
+ * The skip method may, for a variety of reasons, end up skipping over some
+ * smaller number of bytes, possibly 0. This may result from any of a number
+ * of conditions; reaching end of file before n bytes have been skipped is
+ * only one possibility. The actual number of bytes skipped is returned.
+ * If n is negative, no bytes are skipped.
+ * @param num the number of bytes to be skipped.
+ * @return the actual number of bytes skipped.
+ * @see InputStream#skip(long)
+ */
+ public long skip(long num)
+ {
+ if (num <= 0)
+ {
+ return 0;
+ }
+ else
+ {
+ long newpos = datapos + num;
+ if (newpos >= datalen)
+ {
+ num = datalen - datapos;
+ datapos = datalen;
+ }
+ else
+ {
+ datapos = (int)newpos;
+ }
+ return num;
+ }
+ }
+
+ /** Position the stream at a given index. Positioning the stream
+ * at position size() will cause the next call to read() to return -1.
+ *
+ * @param newpos Position in the underlying array. A negative value will be
+ * interpreted as 0, a value greater than size() as size().
+ * @return old position.
+ */
+ public int seek(int newpos)
+ {
+ if (newpos < 0)
+ {
+ newpos = 0;
+ }
+ else if (newpos > datalen)
+ {
+ newpos = datalen;
+ }
+ int oldpos = pos;
+ pos = newpos;
+ return oldpos;
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/io/FastByteArrayOutputStream.java b/src/main/java/org/pdfbox/io/FastByteArrayOutputStream.java
new file mode 100644
index 0000000..1e5fdc6
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/FastByteArrayOutputStream.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * An byte array output stream that allows direct access to the byte array.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class FastByteArrayOutputStream extends ByteArrayOutputStream
+{
+ /**
+ * Constructor.
+ *
+ * @param size An initial size of the stream.
+ */
+ public FastByteArrayOutputStream( int size )
+ {
+ super( size );
+ }
+
+ /**
+ * This will get the underlying byte array.
+ *
+ * @return The underlying byte array at this moment in time.
+ */
+ public byte[] getByteArray()
+ {
+ return buf;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/NBitInputStream.java b/src/main/java/org/pdfbox/io/NBitInputStream.java
new file mode 100644
index 0000000..d8254b2
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/NBitInputStream.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * This is an n-bit input stream. This means that you can read chunks of data
+ * as any number of bits, not just 8 bits like the regular input stream. Just set the
+ * number of bits that you would like to read on each call. The default is 8.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class NBitInputStream
+{
+ private int bitsInChunk;
+ private InputStream in;
+
+ private int currentByte;
+ private int bitsLeftInCurrentByte;
+
+ /**
+ * Constructor.
+ *
+ * @param is The input stream to read from.
+ */
+ public NBitInputStream( InputStream is )
+ {
+ in = is;
+ bitsLeftInCurrentByte = 0;
+ bitsInChunk = 8;
+ }
+
+ /**
+ * This will unread some data.
+ *
+ * @param data The data to put back into the stream.
+ */
+ public void unread( long data )
+ {
+ data <<= bitsLeftInCurrentByte;
+ currentByte |= data;
+ bitsLeftInCurrentByte += bitsInChunk;
+ }
+
+ /**
+ * This will read the next n bits from the stream and return the unsigned
+ * value of those bits.
+ *
+ * @return The next n bits from the stream.
+ *
+ * @throws IOException If there is an error reading from the underlying stream.
+ */
+ public long read() throws IOException
+ {
+ long retval = 0;
+ for( int i=0; i<bitsInChunk && retval != -1; i++ )
+ {
+ if( bitsLeftInCurrentByte == 0 )
+ {
+ currentByte = in.read();
+ bitsLeftInCurrentByte = 8;
+ }
+ if( currentByte == -1 )
+ {
+ retval = -1;
+ }
+ else
+ {
+ retval <<= 1;
+ retval |= ((currentByte >> (bitsLeftInCurrentByte-1))&0x1);
+ bitsLeftInCurrentByte--;
+ }
+ }
+ return retval;
+ }
+
+ /** Getter for property bitsToRead.
+ * @return Value of property bitsToRead.
+ */
+ public int getBitsInChunk()
+ {
+ return bitsInChunk;
+ }
+
+ /** Setter for property bitsToRead.
+ * @param bitsInChunkValue New value of property bitsToRead.
+ */
+ public void setBitsInChunk(int bitsInChunkValue)
+ {
+ bitsInChunk = bitsInChunkValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/NBitOutputStream.java b/src/main/java/org/pdfbox/io/NBitOutputStream.java
new file mode 100644
index 0000000..f944b8a
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/NBitOutputStream.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * This is an n-bit output stream. This means that you write data in n-bit chunks.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class NBitOutputStream
+{
+ private int bitsInChunk;
+ private OutputStream out;
+
+ private int currentByte;
+ private int positionInCurrentByte;
+
+ /**
+ * Constructor.
+ *
+ * @param os The output stream to write to.
+ */
+ public NBitOutputStream( OutputStream os )
+ {
+ out = os;
+ currentByte = 0;
+ positionInCurrentByte = 7;
+ }
+
+ /**
+ * This will write the next n-bits to the stream.
+ *
+ * @param chunk The next chunk of data to write.
+ *
+ * @throws IOException If there is an error writing the chunk.
+ */
+ public void write( long chunk ) throws IOException
+ {
+ long bitToWrite;
+ for( int i=(bitsInChunk-1); i>=0; i-- )
+ {
+ bitToWrite = (chunk >> i) & 0x1;
+ bitToWrite <<= positionInCurrentByte;
+ currentByte |= bitToWrite;
+ positionInCurrentByte--;
+ if( positionInCurrentByte < 0 )
+ {
+ out.write( currentByte );
+ currentByte = 0;
+ positionInCurrentByte = 7;
+ }
+ }
+ }
+
+ /**
+ * This will close the stream.
+ *
+ * @throws IOException if there is an error closing the stream.
+ */
+ public void close() throws IOException
+ {
+ if( positionInCurrentByte < 7 )
+ {
+ out.write( currentByte );
+ }
+ }
+
+ /** Getter for property bitsToRead.
+ * @return Value of property bitsToRead.
+ */
+ public int getBitsInChunk()
+ {
+ return bitsInChunk;
+ }
+
+ /** Setter for property bitsToRead.
+ * @param bitsInChunkValue New value of property bitsToRead.
+ */
+ public void setBitsInChunk(int bitsInChunkValue)
+ {
+ bitsInChunk = bitsInChunkValue;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/PushBackInputStream.java b/src/main/java/org/pdfbox/io/PushBackInputStream.java
new file mode 100644
index 0000000..d519900
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/PushBackInputStream.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * A simple subclass that adds a few convience methods.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PushBackInputStream extends java.io.PushbackInputStream
+{
+
+ /**
+ * Constructor.
+ *
+ * @param input The input stream.
+ * @param size The size of the push back buffer.
+ *
+ * @throws IOException If there is an error with the stream.
+ */
+ public PushBackInputStream( InputStream input, int size ) throws IOException
+ {
+ super( input, size );
+ if( input == null )
+ {
+ throw new IOException( "Error: input was null" );
+ }
+ }
+
+ /**
+ * This will peek at the next byte.
+ *
+ * @return The next byte on the stream, leaving it as available to read.
+ *
+ * @throws IOException If there is an error reading the next byte.
+ */
+ public int peek() throws IOException
+ {
+ int result = read();
+ if( result != -1 )
+ {
+ unread( result );
+ }
+ return result;
+ }
+
+ /**
+ * A simple test to see if we are at the end of the stream.
+ *
+ * @return true if we are at the end of the stream.
+ *
+ * @throws IOException If there is an error reading the next byte.
+ */
+ public boolean isEOF() throws IOException
+ {
+ int peek = peek();
+ return peek == -1;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/RandomAccessFileInputStream.java b/src/main/java/org/pdfbox/io/RandomAccessFileInputStream.java
new file mode 100644
index 0000000..6589bcb
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/RandomAccessFileInputStream.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * This class allows a section of a RandomAccessFile to be accessed as an
+ * input stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class RandomAccessFileInputStream extends InputStream
+{
+ private RandomAccessFile file;
+ private long currentPosition;
+ private long endPosition;
+
+ /**
+ * Constructor.
+ *
+ * @param raFile The file to read the data from.
+ * @param startPosition The position in the file that this stream starts.
+ * @param length The length of the input stream.
+ */
+ public RandomAccessFileInputStream( RandomAccessFile raFile, long startPosition, long length )
+ {
+ file = raFile;
+ currentPosition = startPosition;
+ endPosition = currentPosition+length;
+ }
+ /**
+ * @see InputStream#available()
+ */
+ public int available()
+ {
+ return (int)(endPosition - currentPosition);
+ }
+ /**
+ * @see InputStream#close()
+ */
+ public void close()
+ {
+ //do nothing because we want to leave the random access file open.
+ }
+ /**
+ * @see InputStream#read()
+ */
+ public int read() throws IOException
+ {
+ synchronized(file)
+ {
+ int retval = -1;
+ if( currentPosition < endPosition )
+ {
+ file.seek( currentPosition );
+ currentPosition++;
+ retval = file.read();
+ }
+ return retval;
+ }
+ }
+ /**
+ * @see InputStream#read( byte[], int, int )
+ */
+ public int read( byte[] b, int offset, int length ) throws IOException
+ {
+ //only allow a read of the amount available.
+ if( length > available() )
+ {
+ length = available();
+ }
+ int amountRead = -1;
+ //only read if there are bytes actually available, otherwise
+ //return -1 if the EOF has been reached.
+ if( available() > 0 )
+ {
+ synchronized(file)
+ {
+ file.seek( currentPosition );
+ amountRead = file.read( b, offset, length );
+ }
+ }
+ //update the current cursor position.
+ if( amountRead > 0 )
+ {
+ currentPosition += amountRead;
+ }
+ return amountRead;
+ }
+
+ /**
+ * @see InputStream#skip( long )
+ */
+ public long skip( long amountToSkip )
+ {
+ long amountSkipped = Math.min( amountToSkip, available() );
+ currentPosition+= amountSkipped;
+ return amountSkipped;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/RandomAccessFileOutputStream.java b/src/main/java/org/pdfbox/io/RandomAccessFileOutputStream.java
new file mode 100644
index 0000000..b2e7511
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/RandomAccessFileOutputStream.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSNumber;
+
+/**
+ * This will write to a RandomAccessFile in the filesystem and keep track
+ * of the position it is writing to and the length of the stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class RandomAccessFileOutputStream extends OutputStream
+{
+ private RandomAccessFile file;
+ private long position;
+ private long lengthWritten = 0;
+ private COSBase expectedLength = null;
+
+ /**
+ * Constructor to create an output stream that will write to the end of a
+ * random access file.
+ *
+ * @param raf The file to write to.
+ *
+ * @throws IOException If there is a problem accessing the raf.
+ */
+ public RandomAccessFileOutputStream( RandomAccessFile raf ) throws IOException
+ {
+ file = raf;
+ //first get the position that we will be writing to
+ position = raf.length();
+ }
+
+ /**
+ * This will get the position in the RAF that the stream was written
+ * to.
+ *
+ * @return The position in the raf where the file can be obtained.
+ */
+ public long getPosition()
+ {
+ return position;
+ }
+
+ /**
+ * The number of bytes written to the stream.
+ *
+ * @return The number of bytes read to the stream.
+ */
+ public long getLength()
+ {
+ long length = -1;
+ if( expectedLength instanceof COSNumber )
+ {
+ length = ((COSNumber)expectedLength).intValue();
+ }
+ else if( expectedLength instanceof COSObject &&
+ ((COSObject)expectedLength).getObject() instanceof COSNumber )
+ {
+ length = ((COSNumber)((COSObject)expectedLength).getObject()).intValue();
+ }
+ if( length == -1 )
+ {
+ length = lengthWritten;
+ }
+ return length;
+ }
+
+ /**
+ * @see OutputStream#write( byte[], int, int )
+ */
+ public void write( byte[] b, int offset, int length ) throws IOException
+ {
+ file.seek( position+lengthWritten );
+ lengthWritten += length;
+ file.write( b, offset, length );
+
+ }
+ /**
+ * @see OutputStream#write( int )
+ */
+ public void write( int b ) throws IOException
+ {
+ file.seek( position+lengthWritten );
+ lengthWritten++;
+ file.write( b );
+ }
+
+ /**
+ * This will get the length that the PDF document specified this stream
+ * should be. This may not match the number of bytes read.
+ *
+ * @return The expected length.
+ */
+ public COSBase getExpectedLength()
+ {
+ return expectedLength;
+ }
+
+ /**
+ * This will set the expected length of this stream.
+ *
+ * @param value The expected value.
+ */
+ public void setExpectedLength(COSBase value)
+ {
+ expectedLength = value;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/io/package.html b/src/main/java/org/pdfbox/io/package.html
new file mode 100644
index 0000000..12261f4
--- /dev/null
+++ b/src/main/java/org/pdfbox/io/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains IO streams.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/package.html b/src/main/java/org/pdfbox/package.html
new file mode 100644
index 0000000..f040f68
--- /dev/null
+++ b/src/main/java/org/pdfbox/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package holds executable classes that interact with the PDFBox application.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdfparser/BaseParser.java b/src/main/java/org/pdfbox/pdfparser/BaseParser.java
new file mode 100644
index 0000000..3937025
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfparser/BaseParser.java
@@ -0,0 +1,1369 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfparser;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.io.ByteArrayPushBackInputStream;
+import org.pdfbox.io.PushBackInputStream;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.persistence.util.COSObjectKey;
+import org.apache.log4j.Logger;
+
+/**
+ * This class is used to contain parsing logic that will be used by both the
+ * PDFParser and the COSStreamParser.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.57 $
+ */
+public abstract class BaseParser
+{
+ private static Logger log = Logger.getLogger(BaseParser.class);
+
+ /**
+ * This is a byte array that will be used for comparisons.
+ */
+ public static final byte[] ENDSTREAM =
+ new byte[] {101,110,100,115,116,114,101,97,109};//"endstream".getBytes( "ISO-8859-1" );
+
+ /**
+ * This is a byte array that will be used for comparisons.
+ */
+ public static final String DEF = "def";
+
+ /**
+ * This is the stream that will be read from.
+ */
+ //protected PushBackByteArrayStream pdfSource;
+ protected PushBackInputStream pdfSource;
+
+ /**
+ * moved xref here, is a persistence construct
+ * maybe not needed anyway when not read from behind with delayed
+ * access to objects.
+ */
+ private List xrefs = new ArrayList();
+
+ private COSDocument document;
+
+ /**
+ * Constructor.
+ *
+ * @param input The input stream to read the data from.
+ *
+ * @throws IOException If there is an error reading the input stream.
+ */
+ public BaseParser( InputStream input) throws IOException
+ {
+ //pdfSource = new PushBackByteArrayStream( input );
+ pdfSource = new PushBackInputStream( new BufferedInputStream( input, 16384 ), 4096 );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param input The array to read the data from.
+ *
+ * @throws IOException If there is an error reading the byte data.
+ */
+ protected BaseParser(byte[] input) throws IOException
+ {
+ pdfSource = new ByteArrayPushBackInputStream(input);
+ }
+
+ /**
+ * Set the document for this stream.
+ *
+ * @param doc The current document.
+ */
+ public void setDocument( COSDocument doc )
+ {
+ document = doc;
+ }
+
+ private static boolean isHexDigit(char ch)
+ {
+ return (ch >= '0' && ch <= '9') ||
+ (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F');
+ // the line below can lead to problems with certain versions of the IBM JIT compiler
+ // (and is slower anyway)
+ //return (HEXDIGITS.indexOf(ch) != -1);
+ }
+
+ /**
+ * This will parse a PDF dictionary value.
+ *
+ * @return The parsed Dictionary object.
+ *
+ * @throws IOException If there is an error parsing the dictionary object.
+ */
+ private COSBase parseCOSDictionaryValue() throws IOException
+ {
+
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSDictionaryValue() " + pdfSource );
+ }
+ COSBase retval = null;
+ COSBase number = parseDirObject();
+ skipSpaces();
+ char next = (char)pdfSource.peek();
+ if( next >= '0' && next <= '9' )
+ {
+ COSBase generationNumber = parseDirObject();
+ skipSpaces();
+ char r = (char)pdfSource.read();
+ if( r != 'R' )
+ {
+ throw new IOException( "expected='R' actual='" + r + "' " + pdfSource );
+ }
+ COSObjectKey key = new COSObjectKey(((COSInteger) number).intValue(),
+ ((COSInteger) generationNumber).intValue());
+ retval = document.getObjectFromPool(key);
+ }
+ else
+ {
+ retval = number;
+ }
+ return retval;
+ }
+
+ /**
+ * This will parse a PDF dictionary.
+ *
+ * @return The parsed dictionary.
+ *
+ * @throws IOException IF there is an error reading the stream.
+ */
+ protected COSDictionary parseCOSDictionary() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSDictionary() " + pdfSource );
+ }
+ char c = (char)pdfSource.read();
+ if( c != '<')
+ {
+ throw new IOException( "expected='<' actual='" + c + "'" );
+ }
+ c = (char)pdfSource.read();
+ if( c != '<')
+ {
+ throw new IOException( "expected='<' actual='" + c + "' " + pdfSource );
+ }
+ skipSpaces();
+ COSDictionary obj = new COSDictionary();
+ boolean done = false;
+ while( !done )
+ {
+ skipSpaces();
+ c = (char)pdfSource.peek();
+ if( c == '>')
+ {
+ done = true;
+ }
+ else
+ {
+ COSName key = parseCOSName();
+ COSBase value = parseCOSDictionaryValue();
+ skipSpaces();
+ if( ((char)pdfSource.peek()) == 'd' )
+ {
+ //if the next string is 'def' then we are parsing a cmap stream
+ //and want to ignore it, otherwise throw an exception.
+ String potentialDEF = readString();
+ if( !potentialDEF.equals( DEF ) )
+ {
+ pdfSource.unread( potentialDEF.getBytes() );
+ }
+ else
+ {
+ skipSpaces();
+ }
+ }
+
+ if( value == null )
+ {
+ throw new IOException("Bad Dictionary Declaration " + pdfSource );
+ }
+ obj.setItem( key, value );
+ }
+ }
+ char ch = (char)pdfSource.read();
+ if( ch != '>' )
+ {
+ throw new IOException( "expected='>' actual='" + ch + "'" );
+ }
+ ch = (char)pdfSource.read();
+ if( ch != '>' )
+ {
+ throw new IOException( "expected='>' actual='" + ch + "'" );
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSDictionary() done peek='" + pdfSource.peek() + "'" );
+ }
+ return obj;
+ }
+
+ /**
+ * This will read a COSStream from the input stream.
+ *
+ * @param file The file to write the stream to when reading.
+ * @param dic The dictionary that goes with this stream.
+ *
+ * @return The parsed pdf stream.
+ *
+ * @throws IOException If there is an error reading the stream.
+ */
+ protected COSStream parseCOSStream( COSDictionary dic, RandomAccessFile file ) throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSStream() " + pdfSource );
+ }
+ COSStream stream = new COSStream( dic, file );
+ OutputStream out = null;
+ try
+ {
+ String streamString = readString();
+ //long streamLength;
+
+ if (!streamString.equals("stream"))
+ {
+ throw new IOException("expected='stream' actual='" + streamString + "'");
+ }
+
+ //PDF Ref 3.2.7 A stream must be followed by either
+ //a CRLF or LF but nothing else.
+
+ int whitespace = pdfSource.read();
+
+ //see brother_scan_cover.pdf, it adds whitespaces
+ //after the stream but before the start of the
+ //data, so just read those first
+ while (whitespace == 0x20)
+ {
+ whitespace = pdfSource.read();
+ }
+
+ if( whitespace == 0x0D )
+ {
+ whitespace = pdfSource.read();
+ if( whitespace != 0x0A )
+ {
+ pdfSource.unread( whitespace );
+ //The spec says this is invalid but it happens in the real
+ //world so we must support it.
+ //throw new IOException("expected='0x0A' actual='0x" +
+ // Integer.toHexString(whitespace) + "' " + pdfSource);
+ }
+ }
+ else if (whitespace == 0x0A)
+ {
+ //that is fine
+ }
+ else
+ {
+ //we are in an error.
+ //but again we will do a lenient parsing and just assume that everything
+ //is fine
+ pdfSource.unread( whitespace );
+ //throw new IOException("expected='0x0D or 0x0A' actual='0x" +
+ //Integer.toHexString(whitespace) + "' " + pdfSource);
+
+ }
+
+
+ COSBase streamLength = dic.getDictionaryObject(COSName.LENGTH);
+ long length = -1;
+ if( streamLength instanceof COSNumber )
+ {
+ length = ((COSNumber)streamLength).intValue();
+ }
+ else if( streamLength instanceof COSObject &&
+ ((COSObject)streamLength).getObject() instanceof COSNumber )
+ {
+ length = ((COSNumber)((COSObject)streamLength).getObject()).intValue();
+ }
+
+ //length = -1;
+ //streamLength = null;
+
+ //Need to keep track of the
+ out = stream.createFilteredStream( streamLength );
+ String endStream = null;
+ //the length is wrong in some pdf documents which means
+ //that PDFBox must basically ignore it in order to be able to read
+ //the most number of PDF documents. This of course is a penalty hit,
+ //maybe I could implement a faster parser.
+ /**if( length != -1 )
+ {
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ int totalAmountRead = 0;
+ while( amountRead != -1 && totalAmountRead < length )
+ {
+ int maxAmountToRead = Math.min(buffer.length, (int)(length-totalAmountRead));
+ amountRead = pdfSource.read(buffer,0,maxAmountToRead);
+ totalAmountRead += amountRead;
+ if( amountRead != -1 )
+ {
+ out.write( buffer, 0, amountRead );
+ }
+ }
+ }
+ else
+ {**/
+ readUntilEndStream( out );
+ /**}*/
+ skipSpaces();
+ endStream = readString();
+
+ if (!endStream.equals("endstream"))
+ {
+ readUntilEndStream( out );
+ endStream = readString();
+ if( !endStream.equals( "endstream" ) )
+ {
+ throw new IOException("expected='endstream' actual='" + endStream + "' " + pdfSource);
+ }
+ }
+ }
+ finally
+ {
+ if( out != null )
+ {
+ out.close();
+ }
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSStream() done" );
+ }
+ return stream;
+ }
+
+ private void readUntilEndStream( OutputStream out ) throws IOException
+ {
+ int currentIndex = 0;
+ int byteRead = 0;
+ //this is the additional bytes buffered but not written
+ int additionalBytes=0;
+ byte[] buffer = new byte[ENDSTREAM.length+additionalBytes];
+ int writeIndex = 0;
+ while(!cmpCircularBuffer( buffer, currentIndex, ENDSTREAM ) && byteRead != -1 )
+ {
+ writeIndex = currentIndex - buffer.length;
+ if( writeIndex >= 0 )
+ {
+ out.write( buffer[writeIndex%buffer.length] );
+ }
+ byteRead = pdfSource.read();
+ buffer[currentIndex%buffer.length] = (byte)byteRead;
+ currentIndex++;
+ }
+
+ //we want to ignore the end of the line data when reading a stream
+ //so will make an attempt to ignore it.
+ /*writeIndex = currentIndex - buffer.length;
+ if( buffer[writeIndex%buffer.length] == 13 &&
+ buffer[(writeIndex+1)%buffer.length] == 10 )
+ {
+ //then ignore the newline before the endstream
+ }
+ else if( buffer[(writeIndex+1)%buffer.length] == 10 )
+ {
+ //Then first byte is data, second byte is newline
+ out.write( buffer[writeIndex%buffer.length] );
+ }
+ else
+ {
+ out.write( buffer[writeIndex%buffer.length] );
+ out.write( buffer[(writeIndex+1)%buffer.length] );
+ }*/
+
+ /**
+ * Old way of handling newlines before endstream
+ for( int i=0; i<additionalBytes; i++ )
+ {
+ writeIndex = currentIndex - buffer.length;
+ if( writeIndex >=0 &&
+ //buffer[writeIndex%buffer.length] != 10 &&
+ buffer[writeIndex%buffer.length] != 13 )
+ {
+ out.write( buffer[writeIndex%buffer.length] );
+ }
+ currentIndex++;
+ }
+ */
+ pdfSource.unread( ENDSTREAM );
+
+ }
+
+ /**
+ * This basically checks to see if the next compareTo.length bytes of the
+ * buffer match the compareTo byte array.
+ */
+ private boolean cmpCircularBuffer( byte[] buffer, int currentIndex, byte[] compareTo )
+ {
+ int cmpLen = compareTo.length;
+ int buflen = buffer.length;
+ boolean match = true;
+ int off = currentIndex-cmpLen;
+ if( off < 0 )
+ {
+ match = false;
+ }
+ for( int i=0; match && i<cmpLen; ++i )
+ {
+ match = buffer[(off+i)%buflen] == compareTo[i];
+ }
+ return match;
+ }
+
+ /**
+ * This will parse a PDF string.
+ *
+ * @return The parsed PDF string.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected COSString parseCOSString() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSString() " + pdfSource );
+ }
+ char nextChar = (char)pdfSource.read();
+ COSString retval = new COSString();
+ char openBrace;
+ char closeBrace;
+ if( nextChar == '(' )
+ {
+ openBrace = '(';
+ closeBrace = ')';
+ }
+ else if( nextChar == '<' )
+ {
+ openBrace = '<';
+ closeBrace = '>';
+ }
+ else
+ {
+ throw new IOException( "parseCOSString string should start with '(' or '<' and not '" +
+ nextChar + "' " + pdfSource );
+ }
+
+ //This is the number of braces read
+ //
+ int braces = 1;
+ int c = pdfSource.read();
+ while( braces > 0 && c != -1)
+ {
+ char ch = (char)c;
+ int nextc = -2; // not yet read
+ //if( log.isDebugEnabled() )
+ //{
+ // log.debug( "Parsing COSString character '" + c + "' code=" + (int)c );
+ //}
+
+ if(ch == closeBrace)
+ {
+ braces--;
+ byte[] nextThreeBytes = new byte[3];
+ int amountRead = pdfSource.read(nextThreeBytes);
+
+ //lets handle the special case seen in Bull River Rules and Regulations.pdf
+ //The dictionary looks like this
+ // 2 0 obj
+ // <<
+ // /Type /Info
+ // /Creator (PaperPort http://www.scansoft.com)
+ // /Producer (sspdflib 1.0 http://www.scansoft.com)
+ // /Title ( (5)
+ // /Author ()
+ // /Subject ()
+ //
+ // Notice the /Title, the braces are not even but they should
+ // be. So lets assume that if we encounter an this scenario
+ // <end_brace><new_line><opening_slash> then that
+ // means that there is an error in the pdf and assume that
+ // was the end of the document.
+ if( amountRead == 3 )
+ {
+ if( nextThreeBytes[0] == 0x0d &&
+ nextThreeBytes[1] == 0x0a &&
+ nextThreeBytes[2] == 0x2f )
+ {
+ braces = 0;
+ }
+ }
+ pdfSource.unread( nextThreeBytes, 0, amountRead );
+ if( braces != 0 )
+ {
+ retval.append( ch );
+ }
+ }
+ else if( ch == openBrace )
+ {
+ braces++;
+ retval.append( ch );
+ }
+ else if( ch == '\\' )
+ {
+ //patched by ram
+ char next = (char)pdfSource.read();
+ switch(next)
+ {
+ case 'n':
+ retval.append( '\n' );
+ break;
+ case 'r':
+ retval.append( '\r' );
+ break;
+ case 't':
+ retval.append( '\t' );
+ break;
+ case 'b':
+ retval.append( '\b' );
+ break;
+ case 'f':
+ retval.append( '\f' );
+ break;
+ case '(':
+ case ')':
+ case '\\':
+ retval.append( next );
+ break;
+ case 10:
+ case 13:
+ //this is a break in the line so ignore it and the newline and continue
+ c = pdfSource.read();
+ while( isEOL(c) && c != -1)
+ {
+ c = pdfSource.read();
+ }
+ nextc = c;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ StringBuffer octal = new StringBuffer();
+ octal.append( next );
+ c = pdfSource.read();
+ char digit = (char)c;
+ if( digit >= '0' && digit <= '7' )
+ {
+ octal.append( digit );
+ c = pdfSource.read();
+ digit = (char)c;
+ if( digit >= '0' && digit <= '7' )
+ {
+ octal.append( digit );
+ }
+ else
+ {
+ nextc = c;
+ }
+ }
+ else
+ {
+ nextc = c;
+ }
+
+ int character = 0;
+ try
+ {
+ character = Integer.parseInt( octal.toString(), 8 );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error: Expected octal character, actual='" + octal + "'" );
+ }
+ retval.append( character );
+ break;
+ }
+ default:
+ {
+ retval.append( '\\' );
+ retval.append( next );
+ //another ficken problem with PDF's, sometimes the \ doesn't really
+ //mean escape like the PDF spec says it does, sometimes is should be literal
+ //which is what we will assume here.
+ //throw new IOException( "Unexpected break sequence '" + next + "' " + pdfSource );
+ }
+ }
+ }
+ else
+ {
+ if( openBrace == '<' )
+ {
+ if( isHexDigit(ch) )
+ {
+ retval.append( ch );
+ }
+ }
+ else
+ {
+ retval.append( ch );
+ }
+ }
+ if (nextc != -2)
+ {
+ c = nextc;
+ }
+ else
+ {
+ c = pdfSource.read();
+ }
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ if( openBrace == '<' )
+ {
+ retval = COSString.createFromHexString( retval.getString() );
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSString() done parsed=" + retval );
+ }
+ return retval;
+ }
+
+ /**
+ * This will parse a PDF array object.
+ *
+ * @return The parsed PDF array.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ protected COSArray parseCOSArray() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSArray() " + pdfSource );
+ }
+ char ch = (char)pdfSource.read();
+ if( ch != '[')
+ {
+ throw new IOException( "expected='[' actual='" + ch + "'" );
+ }
+ COSArray po = new COSArray();
+ COSBase pbo = null;
+ skipSpaces();
+ int i = 0;
+ while( ((i = pdfSource.peek()) > 0) && ((char)i != ']') )
+ {
+ pbo = parseDirObject();
+ if( pbo instanceof COSObject )
+ {
+ COSInteger genNumber = (COSInteger)po.remove( po.size() -1 );
+ COSInteger number = (COSInteger)po.remove( po.size() -1 );
+ COSObjectKey key = new COSObjectKey(number.intValue(), genNumber.intValue());
+ pbo = document.getObjectFromPool(key);
+ }
+ if( pbo != null )
+ {
+ po.add( pbo );
+ }
+ else
+ {
+ //it could be a bad object in the array which is just skipped
+ }
+ skipSpaces();
+ }
+ pdfSource.read(); //read ']'
+ skipSpaces();
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSArray() done peek='" + (char)pdfSource.peek() + "'" );
+ }
+ return po;
+ }
+
+ /**
+ * Determine if a character terminates a PDF name.
+ *
+ * @param ch The character
+ * @return <code>true</code> if the character terminates a PDF name, otherwise <code>false</code>.
+ */
+ protected boolean isEndOfName(char ch)
+ {
+ return (ch == ' ' || ch == 13 || ch == 10 || ch == 9 || ch == '>' || ch == '<'
+ || ch == '[' || ch =='/' || ch ==']' || ch ==')' || ch =='(' ||
+ ch == -1 //EOF
+ );
+ }
+
+ /**
+ * This will parse a PDF name from the stream.
+ *
+ * @return The parsed PDF name.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected COSName parseCOSName() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseCOSName() " + pdfSource );
+ }
+ COSName retval = null;
+ int c = pdfSource.read();
+ if( (char)c != '/')
+ {
+ throw new IOException("expected='/' actual='" + (char)c + "'-" + c + " " + pdfSource );
+ }
+ // costruisce il nome
+ StringBuffer buffer = new StringBuffer();
+ c = pdfSource.read();
+ while( c != -1 )
+ {
+ char ch = (char)c;
+ if(ch == '#')
+ {
+ char ch1 = (char)pdfSource.read();
+ char ch2 = (char)pdfSource.read();
+
+ // Prior to PDF v1.2, the # was not a special character. Also,
+ // it has been observed that various PDF tools do not follow the
+ // spec with respect to the # escape, even though they report
+ // PDF versions of 1.2 or later. The solution here is that we
+ // interpret the # as an escape only when it is followed by two
+ // valid hex digits.
+ //
+ if (isHexDigit(ch1) && isHexDigit(ch2))
+ {
+ String hex = "" + ch1 + ch2;
+ try
+ {
+ buffer.append( (char) Integer.parseInt(hex, 16));
+ }
+ catch (NumberFormatException e)
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("isHexDigit(ch1)=" + isHexDigit(ch1) + ", isHexDigit(ch2)=" + isHexDigit(ch2));
+ }
+ throw new IOException("Error: expected hex number, actual='" + hex + "'");
+ }
+ c = pdfSource.read();
+ }
+ else
+ {
+ pdfSource.unread(ch2);
+ c = ch1;
+ buffer.append( ch );
+ }
+ }
+ else if (isEndOfName(ch))
+ {
+ break;
+ }
+ else
+ {
+ buffer.append( ch );
+ c = pdfSource.read();
+ }
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ retval = COSName.getPDFName( buffer.toString() );
+ return retval;
+ }
+
+ /**
+ * This will parse a boolean object from the stream.
+ *
+ * @return The parsed boolean object.
+ *
+ * @throws IOException If an IO error occurs during parsing.
+ */
+ protected COSBoolean parseBoolean() throws IOException
+ {
+ COSBoolean retval = null;
+ char c = (char)pdfSource.peek();
+ if( c == 't' )
+ {
+ byte[] trueArray = new byte[ 4 ];
+ int amountRead = pdfSource.read( trueArray, 0, 4 );
+ String trueString = new String( trueArray, 0, amountRead );
+ if( !trueString.equals( "true" ) )
+ {
+ throw new IOException( "Error parsing boolean: expected='true' actual='" + trueString + "'" );
+ }
+ else
+ {
+ retval = COSBoolean.TRUE;
+ }
+ }
+ else if( c == 'f' )
+ {
+ byte[] falseArray = new byte[ 5 ];
+ int amountRead = pdfSource.read( falseArray, 0, 5 );
+ String falseString = new String( falseArray, 0, amountRead );
+ if( !falseString.equals( "false" ) )
+ {
+ throw new IOException( "Error parsing boolean: expected='true' actual='" + falseString + "'" );
+ }
+ else
+ {
+ retval = COSBoolean.FALSE;
+ }
+ }
+ else
+ {
+ throw new IOException( "Error parsing boolean expected='t or f' actual='" + c + "'" );
+ }
+ return retval;
+ }
+
+ /**
+ * This will parse a directory object from the stream.
+ *
+ * @return The parsed object.
+ *
+ * @throws IOException If there is an error during parsing.
+ */
+ protected COSBase parseDirObject() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseDirObject() " + pdfSource );
+ }
+ COSBase retval = null;
+
+ skipSpaces();
+ int nextByte = pdfSource.peek();
+ char c = (char)nextByte;
+ switch(c)
+ {
+ case '<':
+ {
+ int leftBracket = pdfSource.read();//pull off first left bracket
+ c = (char)pdfSource.peek(); //check for second left bracket
+ pdfSource.unread( leftBracket );
+ if(c == '<')
+ {
+
+ retval = parseCOSDictionary();
+ skipSpaces();
+ }
+ else
+ {
+ retval = parseCOSString();
+ }
+ break;
+ }
+ case '[': // array
+ {
+ retval = parseCOSArray();
+ break;
+ }
+ case '(':
+ retval = parseCOSString();
+ break;
+ case '/': // name
+ retval = parseCOSName();
+ break;
+ case 'n': // null
+ {
+ String nullString = readString();
+ if( !nullString.equals( "null") )
+ {
+ throw new IOException("Expected='null' actual='" + nullString + "'");
+ }
+ retval = COSNull.NULL;
+ break;
+ }
+ case 't':
+ {
+ byte[] trueBytes = new byte[4];
+ int amountRead = pdfSource.read( trueBytes, 0, 4 );
+ String trueString = new String( trueBytes, 0, amountRead );
+ if( trueString.equals( "true" ) )
+ {
+ retval = COSBoolean.TRUE;
+ }
+ else
+ {
+ throw new IOException( "expected true actual='" + trueString + "' " + pdfSource );
+ }
+ break;
+ }
+ case 'f':
+ {
+ byte[] falseBytes = new byte[5];
+ int amountRead = pdfSource.read( falseBytes, 0, 5 );
+ String falseString = new String( falseBytes, 0, amountRead );
+ if( falseString.equals( "false" ) )
+ {
+ retval = COSBoolean.FALSE;
+ }
+ else
+ {
+ throw new IOException( "expected false actual='" + falseString + "' " + pdfSource );
+ }
+ break;
+ }
+ case 'R':
+ pdfSource.read();
+ retval = new COSObject(null);
+ break;
+ case (char)-1:
+ return null;
+ default:
+ {
+ if( Character.isDigit(c) || c == '-' || c == '+' || c == '.')
+ {
+ StringBuffer buf = new StringBuffer();
+ int ic = pdfSource.read();
+ c = (char)ic;
+ while( Character.isDigit( c )||
+ c == '-' ||
+ c == '+' ||
+ c == '.' ||
+ c == 'E' ||
+ c == 'e' )
+ {
+ buf.append( c );
+ ic = pdfSource.read();
+ c = (char)ic;
+ }
+ if( ic != -1 )
+ {
+ pdfSource.unread( ic );
+ }
+ retval = COSNumber.get( buf.toString() );
+ }
+ else
+ {
+ //This is not suppose to happen, but we will allow for it
+ //so we are more compatible with POS writers that don't
+ //follow the spec
+ String badString = readString();
+ //throw new IOException( "Unknown dir object c='" + c +
+ //"' peek='" + (char)pdfSource.peek() + "' " + pdfSource );
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseDirObject() bad DIR object found. ignoring: '" + badString + "'");
+ }
+ if( badString == null || badString.length() == 0 )
+ {
+ int peek = pdfSource.peek();
+ // we can end up in an infinite loop otherwise
+ throw new IOException( "Unknown dir object c='" + c +
+ "' cInt=" + (int)c + " peek='" + (char)peek + "' peekInt=" + peek + " " + pdfSource );
+ }
+
+ }
+ }
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug("parseDirObject() done retval=" +retval );
+ }
+ return retval;
+ }
+
+ /**
+ * This will read the next string from the stream.
+ *
+ * @return The string that was read from the stream.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected String readString() throws IOException
+ {
+ skipSpaces();
+ StringBuffer buffer = new StringBuffer();
+ int c = pdfSource.read();
+ while( !isEndOfName((char)c) && !isClosing(c) && c != -1 )
+ {
+ buffer.append( (char)c );
+ c = pdfSource.read();
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * This will read bytes until the end of line marker occurs.
+ *
+ * @param theString The next expected string in the stream.
+ *
+ * @return The characters between the current position and the end of the line.
+ *
+ * @throws IOException If there is an error reading from the stream or theString does not match what was read.
+ */
+ protected String readExpectedString( String theString ) throws IOException
+ {
+ int c = pdfSource.read();
+ while( isWhitespace(c) && c != -1)
+ {
+ c = pdfSource.read();
+ }
+ StringBuffer buffer = new StringBuffer( theString.length() );
+ int charsRead = 0;
+ while( !isEOL(c) && c != -1 && charsRead < theString.length() )
+ {
+ char next = (char)c;
+ buffer.append( next );
+ if( theString.charAt( charsRead ) == next )
+ {
+ charsRead++;
+ }
+ else
+ {
+ throw new IOException( "Error: Expected to read '" + theString +
+ "' instead started reading '" +buffer.toString() + "'" );
+ }
+ c = pdfSource.read();
+ }
+ while( isEOL(c) && c != -1 )
+ {
+ c = pdfSource.read();
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * This will read the next string from the stream up to a certain length.
+ *
+ * @param length The length to stop reading at.
+ *
+ * @return The string that was read from the stream of length 0 to length.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected String readString( int length ) throws IOException
+ {
+ skipSpaces();
+
+ int c = pdfSource.read();
+
+ //average string size is around 2 and the normal string buffer size is
+ //about 16 so lets save some space.
+ StringBuffer buffer = new StringBuffer(length);
+ while( !isWhitespace(c) && !isClosing(c) && c != -1 && buffer.length() < length &&
+ c != '[' &&
+ c != '<' &&
+ c != '(' &&
+ c != '/' )
+ {
+ buffer.append( (char)c );
+ c = pdfSource.read();
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * This will tell if the next character is a closing brace( close of PDF array ).
+ *
+ * @return true if the next byte is ']', false otherwise.
+ *
+ * @throws IOException If an IO error occurs.
+ */
+ protected boolean isClosing() throws IOException
+ {
+ return isClosing(pdfSource.peek());
+ }
+
+ /**
+ * This will tell if the next character is a closing brace( close of PDF array ).
+ *
+ * @param c The character to check against end of line
+ * @return true if the next byte is ']', false otherwise.
+ */
+ protected boolean isClosing(int c)
+ {
+ return c == ']';
+ }
+
+ /**
+ * This will read bytes until the end of line marker occurs.
+ *
+ * @return The characters between the current position and the end of the line.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected String readLine() throws IOException
+ {
+ int c = pdfSource.read();
+ while(isWhitespace(c) && c != -1)
+ {
+ c = pdfSource.read();
+ }
+ StringBuffer buffer = new StringBuffer( 11 );
+
+ while( !isEOL(c) && c != -1 )
+ {
+ buffer.append( (char)c );
+ c = pdfSource.read();
+ }
+ while( isEOL(c) && c != -1 )
+ {
+ c = pdfSource.read();
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * This will tell if the next byte to be read is an end of line byte.
+ *
+ * @return true if the next byte is 0x0A or 0x0D.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected boolean isEOL() throws IOException
+ {
+ return isEOL(pdfSource.peek());
+ }
+
+ /**
+ * This will tell if the next byte to be read is an end of line byte.
+ *
+ * @param c The character to check against end of line
+ * @return true if the next byte is 0x0A or 0x0D.
+ */
+ protected boolean isEOL(int c)
+ {
+ return c == 10 || c == 13;
+ }
+
+ /**
+ * This will tell if the next byte is whitespace or not.
+ *
+ * @return true if the next byte in the stream is a whitespace character.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected boolean isWhitespace() throws IOException
+ {
+ return isWhitespace( pdfSource.peek() );
+ }
+
+ /**
+ * This will tell if the next byte is whitespace or not.
+ *
+ * @param c The character to check against whitespace
+ *
+ * @return true if the next byte in the stream is a whitespace character.
+ */
+ protected boolean isWhitespace( int c )
+ {
+ return c == 0 || c == 9 || c == 12 || c == 10
+ || c == 13 || c == 32;
+ }
+
+ /**
+ * This will skip all spaces and comments that are present.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected void skipSpaces() throws IOException
+ {
+ //log( "skipSpaces() " + pdfSource );
+ int c = pdfSource.read();
+ // identical to, but faster as: isWhiteSpace(c) || c == 37
+ while(c == 0 || c == 9 || c == 12 || c == 10
+ || c == 13 || c == 32 || c == 37)//37 is the % character, a comment
+ {
+ if ( c == 37 )
+ {
+ // skip past the comment section
+ c = pdfSource.read();
+ while(!isEOL(c) && c != -1)
+ {
+ c = pdfSource.read();
+ }
+ }
+ else
+ {
+ c = pdfSource.read();
+ }
+ }
+ if (c != -1)
+ {
+ pdfSource.unread(c);
+ }
+ //log( "skipSpaces() done peek='" + (char)pdfSource.peek() + "'" );
+ }
+
+ /**
+ * this will compare two byte arrays.
+ *
+ * @param first The first byte array to compare.
+ * @param second The second byte array to compare.
+ *
+ * @return true if both arrays are the same AND forall i : first[i] = second[i]
+ */
+ private boolean cmpArray( byte[] first, byte[] second )
+ {
+ return cmpArray( first, 0, second );
+ }
+
+ /**
+ * This will compare two arrays for equality.
+ *
+ * @param first The first array to compare.
+ * @param firstOffset The first byte to start comparing.
+ * @param second The second array to compare.
+ */
+ private boolean cmpArray( byte[] first, int firstOffset, byte[] second )
+ {
+ boolean retval = true;
+ if( first.length-firstOffset >= second.length )
+ {
+ int arrayLength = second.length;
+ for( int i =0; i<arrayLength && retval; i++ )
+ {
+ retval = retval && first[ firstOffset + i ] == second[ i ];
+ }
+ }
+ else
+ {
+ retval = false;
+ }
+ return retval;
+ }
+
+ /**
+ * This will read an integer from the stream.
+ *
+ * @return The integer that was read from the stream.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected int readInt() throws IOException
+ {
+ skipSpaces();
+ int retval = 0;
+
+ int lastByte = 0;
+ StringBuffer intBuffer = new StringBuffer();
+ while( (lastByte = pdfSource.read() ) != 32 &&
+ lastByte != 10 &&
+ lastByte != 13 &&
+ lastByte != 0 && //See sourceforge bug 853328
+ lastByte != -1 )
+ {
+ intBuffer.append( (char)lastByte );
+ }
+ try
+ {
+ retval = Integer.parseInt( intBuffer.toString() );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error: Expected an integer type, actual='" + intBuffer + "'" );
+ }
+ return retval;
+ }
+
+ /**
+ * This will add an xref.
+ *
+ * @param xref The xref to add.
+ */
+ public void addXref( PDFXref xref )
+ {
+ xrefs.add(xref);
+ }
+
+ /**
+ * This will get all of the xrefs.
+ *
+ * @return A list of all xrefs.
+ */
+ public List getXrefs()
+ {
+ return xrefs;
+ }
+
+ /**
+ * This will set the xrefs for this parser.
+ *
+ * @param newXrefs The xrefs for this parser.
+ */
+ private void setXrefs( List newXrefs )
+ {
+ xrefs = newXrefs;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfparser/PDFObjectStreamParser.java b/src/main/java/org/pdfbox/pdfparser/PDFObjectStreamParser.java
new file mode 100644
index 0000000..6fb7563
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfparser/PDFObjectStreamParser.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfparser;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+
+
+import org.apache.log4j.Logger;
+
+/**
+ * This will parse a PDF 1.5 object stream and extract all of the objects from the stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDFObjectStreamParser extends BaseParser
+{
+ private static Logger log = Logger.getLogger(PDFObjectStreamParser.class);
+ private List streamObjects = null;
+ private List objectNumbers = null;
+ private COSStream stream;
+
+ /**
+ * Constructor.
+ *
+ * @param strm The stream to parse.
+ * @param doc The document for the current parsing.
+ *
+ * @throws IOException If there is an error initializing the stream.
+ */
+ public PDFObjectStreamParser( COSStream strm, COSDocument doc ) throws IOException
+ {
+ super( strm.getUnfilteredStream() );
+ setDocument( doc );
+ stream = strm;
+ }
+
+ /**
+ * This will parse the tokens in the stream. This will close the
+ * stream when it is finished parsing.
+ *
+ * @throws IOException If there is an error while parsing the stream.
+ */
+ public void parse() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parse() start" );
+ }
+
+ try
+ {
+ //need to first parse the header.
+ int numberOfObjects = stream.getInt( "N" );
+ objectNumbers = new ArrayList( numberOfObjects );
+ streamObjects = new ArrayList( numberOfObjects );
+ for( int i=0; i<numberOfObjects; i++ )
+ {
+ int objectNumber = readInt();
+ int offset = readInt();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "objNum:" + objectNumber + " offset:" + offset );
+ }
+ objectNumbers.add( new Integer( objectNumber ) );
+ }
+ COSObject object = null;
+ COSBase cosObject = null;
+ int objectCounter = 0;
+ while( (cosObject = parseDirObject()) != null )
+ {
+ object = new COSObject(cosObject);
+ object.setGenerationNumber( COSInteger.ZERO );
+ COSInteger objNum =
+ new COSInteger( ((Integer)objectNumbers.get( objectCounter)).intValue() );
+ object.setObjectNumber( objNum );
+ streamObjects.add( object );
+ objectCounter++;
+ }
+ }
+ finally
+ {
+ pdfSource.close();
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parse() end" );
+ }
+ }
+
+ /**
+ * This will get the objects that were parsed from the stream.
+ *
+ * @return All of the objects in the stream.
+ */
+ public List getObjects()
+ {
+ return streamObjects;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfparser/PDFParser.java b/src/main/java/org/pdfbox/pdfparser/PDFParser.java
new file mode 100644
index 0000000..d655ef1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfparser/PDFParser.java
@@ -0,0 +1,557 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfparser;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.util.Iterator;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.exceptions.WrappedIOException;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+
+import org.pdfbox.persistence.util.COSObjectKey;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class will handle the parsing of the PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.47 $
+ */
+public class PDFParser extends BaseParser
+{
+ private static Logger log = Logger.getLogger( PDFParser.class );
+ private static final int SPACE_BYTE = 32;
+
+ private static final String PDF_HEADER = "%PDF-";
+ private COSDocument document;
+
+ /**
+ * Temp file directory.
+ */
+ private File tempDirectory = new File( System.getProperty( "java.io.tmpdir" ) );
+
+ private RandomAccessFile raf = null;
+
+ /**
+ * Constructor.
+ *
+ * @param input The input stream that contains the PDF document.
+ *
+ * @throws IOException If there is an error initializing the stream.
+ */
+ public PDFParser( InputStream input ) throws IOException
+ {
+ this(input, null);
+ }
+
+ /**
+ * Constructor to allow control over RandomAccessFile.
+ * @param input The input stream that contains the PDF document.
+ * @param rafi The RandomAccessFile to be used in internal COSDocument
+ *
+ * @throws IOException If there is an error initializing the stream.
+ */
+ public PDFParser(InputStream input, RandomAccessFile rafi)
+ throws IOException
+ {
+ super(input);
+ this.raf = rafi;
+ }
+
+ /**
+ * This is the directory where pdfbox will create a temporary file
+ * for storing pdf document stream in. By default this directory will
+ * be the value of the system property java.io.tmpdir.
+ *
+ * @param tmpDir The directory to create scratch files needed to store
+ * pdf document streams.
+ */
+ public void setTempDirectory( File tmpDir )
+ {
+ tempDirectory = tmpDir;
+ }
+
+ /**
+ * This will prase the stream and create the PDF document. This will close
+ * the stream when it is done parsing.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public void parse() throws IOException
+ {
+ try
+ {
+ if ( raf == null )
+ {
+ document = new COSDocument( tempDirectory );
+ }
+ else
+ {
+ document = new COSDocument( raf );
+ }
+ setDocument( document );
+ String header = readLine();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Header=" + header );
+ }
+ document.setHeaderString( header );
+
+ if( header.length() < PDF_HEADER.length()+1 )
+ {
+ throw new IOException( "Error: Header is corrupt '" + header + "'" );
+ }
+
+ //sometimes there are some garbage bytes in the header before the header
+ //actually starts, so lets try to find the header first.
+ int headerStart = header.indexOf( PDF_HEADER );
+
+ //greater than zero because if it is zero then
+ //there is no point of trimming
+ if( headerStart > 0 )
+ {
+ //trim off any leading characters
+ header = header.substring( headerStart, header.length() );
+ }
+
+ try
+ {
+ float pdfVersion = Float.parseFloat(
+ header.substring( PDF_HEADER.length(), Math.min( header.length(), PDF_HEADER.length()+3) ) );
+ document.setVersion( pdfVersion );
+ }
+ catch( NumberFormatException e )
+ {
+ throw new IOException( "Error getting pdf version:" + e );
+ }
+
+ skipHeaderFillBytes();
+
+
+ Object nextObject;
+ boolean wasLastParsedObjectAnXref = false;
+ try
+ {
+ while( (nextObject = parseObject()) != null )
+ {
+ if( nextObject instanceof PDFXref )
+ {
+ PDFXref xref = (PDFXref)nextObject;
+ addXref(xref);
+ wasLastParsedObjectAnXref = true;
+ }
+ else
+ {
+ wasLastParsedObjectAnXref = false;
+ }
+ skipSpaces();
+ }
+ if( document.getTrailer() == null )
+ {
+ COSDictionary trailer = new COSDictionary();
+ Iterator xrefIter = document.getObjectsByType( "XRef" ).iterator();
+ while( xrefIter.hasNext() )
+ {
+ COSStream next = (COSStream)((COSObject)xrefIter.next()).getObject();
+ trailer.addAll( next );
+ }
+ document.setTrailer( trailer );
+ }
+ if( !document.isEncrypted() )
+ {
+ document.dereferenceObjectStreams();
+ }
+ }
+ catch( IOException e )
+ {
+ if( wasLastParsedObjectAnXref )
+ {
+ log.debug( "Skipping some garbage", e );
+ //Then we assume that there is just random garbage after
+ //the xref, not sure why the PDF spec allows this but it does.
+ }
+ else
+ {
+ //some other error so just pass it along
+ throw e;
+ }
+ }
+ }
+ catch( Throwable t )
+ {
+ //so if the PDF is corrupt then close the document and clear
+ //all resources to it
+ if( document != null )
+ {
+ document.close();
+ }
+ if( t instanceof IOException )
+ {
+ throw (IOException)t;
+ }
+ else
+ {
+ throw new WrappedIOException( t );
+ }
+ }
+ finally
+ {
+ pdfSource.close();
+ }
+ }
+
+ /**
+ * This will skip a header's binary fill bytes. This is in accordance to
+ * PDF Specification 1.5 pg 68 section 3.4.1 "Syntax.File Structure.File Header"
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected void skipHeaderFillBytes() throws IOException
+ {
+ skipSpaces();
+ int c = pdfSource.peek();
+
+ if( !Character.isDigit( (char)c ) )
+ {
+ // Fill bytes conform with PDF reference (but without comment sign)
+ // => skip until EOL
+ readLine();
+ }
+ // else: no fill bytes
+ }
+
+ /**
+ * This will get the document that was parsed. parse() must be called before this is called.
+ * When you are done with this document you must call close() on it to release
+ * resources.
+ *
+ * @return The document that was parsed.
+ *
+ * @throws IOException If there is an error getting the document.
+ */
+ public COSDocument getDocument() throws IOException
+ {
+ if( document == null )
+ {
+ throw new IOException( "You must call parse() before calling getDocument()" );
+ }
+ return document;
+ }
+
+ /**
+ * This will get the PD document that was parsed. When you are done with
+ * this document you must call close() on it to release resources.
+ *
+ * @return The document at the PD layer.
+ *
+ * @throws IOException If there is an error getting the document.
+ */
+ public PDDocument getPDDocument() throws IOException
+ {
+ return new PDDocument( getDocument() );
+ }
+
+ /**
+ * This will get the FDF document that was parsed. When you are done with
+ * this document you must call close() on it to release resources.
+ *
+ * @return The document at the PD layer.
+ *
+ * @throws IOException If there is an error getting the document.
+ */
+ public FDFDocument getFDFDocument() throws IOException
+ {
+ return new FDFDocument( getDocument() );
+ }
+
+ /**
+ * This will parse a document object from the stream.
+ *
+ * @return The parsed object.
+ *
+ * @throws IOException If an IO error occurs.
+ */
+ private Object parseObject() throws IOException
+ {
+ Object object = null;
+ char peekedChar = (char)pdfSource.peek();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "PDFParser.parseObject() peek='" + peekedChar + "'" );
+ }
+ if( pdfSource.isEOF() )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Skipping because of EOF" );
+ //end of file we will return a null object and call it a day.
+ }
+ }
+ else if( peekedChar == 'x' ||
+ peekedChar == 't' ||
+ peekedChar == 's')
+ {
+ //System.out.println( "parseObject() parsing xref" );
+
+ //FDF documents do not always have the xref
+ if( peekedChar == 'x' || peekedChar == 't' )
+ {
+ object = parseXrefSection();
+ }
+
+ //if peeked char is xref or startxref
+ if( peekedChar == 'x' || peekedChar == 's')
+ {
+ skipSpaces();
+ while( pdfSource.peek() == 'x' )
+ {
+ parseXrefSection();
+ }
+ String startxref = readString();
+ if( !startxref.equals( "startxref" ) )
+ {
+ throw new IOException( "expected='startxref' actual='" + startxref + "' " + pdfSource );
+ }
+ skipSpaces();
+ //read some integer that is in the stream but PDFBox doesn't use
+ readInt();
+ }
+
+ //This MUST be readLine because readString strips out comments
+ //and it will think that %% is a comment in from of the EOF
+ String eof = readExpectedString( "%%EOF" );
+ if( eof.indexOf( "%%EOF" )== -1 && !pdfSource.isEOF() )
+ {
+ throw new IOException( "expected='%%EOF' actual='" + eof + "' next=" + readString() +
+ " next=" +readString() );
+ }
+ else if( !pdfSource.isEOF() )
+ {
+ //we might really be at the end of the file, there might just be some crap at the
+ //end of the file.
+ if( pdfSource.available() < 1000 )
+ {
+ //We need to determine if we are at the end of the file.
+ byte[] data = new byte[ 1000 ];
+
+ int amountRead = pdfSource.read( data );
+ if( amountRead != -1 )
+ {
+ pdfSource.unread( data, 0, amountRead );
+ }
+ boolean atEndOfFile = true;//we assume yes unless we find another.
+ for( int i=0; i<amountRead-3 && atEndOfFile; i++ )
+ {
+ atEndOfFile = !(data[i] == 'E' &&
+ data[i+1] == 'O' &&
+ data[i+2] == 'F' );
+ }
+ if( atEndOfFile )
+ {
+ while( pdfSource.read( data, 0, data.length ) != -1 )
+ {
+ //read until done.
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ int number;
+ int genNum;
+ String objectKey = null;
+ try
+ {
+ number = readInt();
+ }
+ catch( IOException e )
+ {
+ //ok for some reason "GNU Ghostscript 5.10" puts two endobj
+ //statements after an object, of course this is nonsense
+ //but because we want to support as many PDFs as possible
+ //we will simply try again
+ number = readInt();
+ }
+ skipSpaces();
+ genNum = readInt();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Parsing object (" + number + "," + genNum + ")" );
+ }
+
+ objectKey = readString( 3 );
+ //System.out.println( "parseObject() num=" + number + " genNumber=" + genNum + " key='" + objectKey + "'" );
+ if( !objectKey.equals( "obj" ) )
+ {
+ throw new IOException("expected='obj' actual='" + objectKey + "' " + pdfSource );
+ }
+
+ skipSpaces();
+ COSBase pb = parseDirObject();
+ String endObjectKey = readString();
+ if( endObjectKey.equals( "stream" ) )
+ {
+ pdfSource.unread( endObjectKey.getBytes() );
+ pdfSource.unread( ' ' );
+ if( pb instanceof COSDictionary )
+ {
+ pb = parseCOSStream( (COSDictionary)pb, getDocument().getScratchFile() );
+ }
+ else
+ {
+ // this is not legal
+ // the combination of a dict and the stream/endstream forms a complete stream object
+ throw new IOException("stream not preceded by dictionary");
+ }
+ endObjectKey = readString();
+ }
+ COSObjectKey key = new COSObjectKey( number, genNum );
+ COSObject pdfObject = document.getObjectFromPool( key );
+ object = pdfObject;
+ pdfObject.setObject(pb);
+
+ if( !endObjectKey.equals( "endobj" ) )
+ {
+ if( !pdfSource.isEOF() )
+ {
+ try
+ {
+ //It is possible that the endobj is missing, there
+ //are several PDFs out there that do that so skip it and move on.
+ Float.parseFloat( endObjectKey );
+ pdfSource.unread( SPACE_BYTE );
+ pdfSource.unread( endObjectKey.getBytes() );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Missing endobj, found '" + endObjectKey +
+ "' instead, assuming that endobj is not present and will continue parsing." );
+ }
+ }
+ catch( NumberFormatException e )
+ {
+ //we will try again incase there was some garbage which
+ //some writers will leave behind.
+ String secondEndObjectKey = readString();
+ if( !secondEndObjectKey.equals( "endobj" ) )
+ {
+ throw new IOException("expected='endobj' firstReadAttempt='" + endObjectKey + "' " +
+ "secondReadAttempt='" + secondEndObjectKey + "' " + pdfSource);
+ }
+ }
+ }
+ }
+ skipSpaces();
+
+ }
+ //System.out.println( "parsed=" + object );
+ return object;
+ }
+
+
+ /**
+ * This will parse the xref table and trailers from the stream.
+ *
+ * @return a new PDFXref
+ *
+ * @throws IOException If an IO error occurs.
+ */
+ protected PDFXref parseXrefSection() throws IOException
+ {
+ int[] params = new int[2];
+ parseXrefTable(params);
+ parseTrailer();
+
+ return new PDFXref(params[0], params[1]);
+ }
+
+ /**
+ * This will parse the xref table from the stream.
+ *
+ * It stores the starting object number and the count
+ *
+ * @param params The start and count parameters
+ *
+ * @throws IOException If an IO error occurs.
+ */
+ protected void parseXrefTable(int[] params) throws IOException
+ {
+ String nextLine = null;
+
+ nextLine = readLine();
+ if( nextLine.equals( "xref" ) )
+ {
+ params[0] = readInt();
+ params[1] = readInt();
+ nextLine = readString();
+ }
+ skipSpaces();
+ while( !nextLine.equals( "trailer" ) && !pdfSource.isEOF() && !isEndOfName((char)pdfSource.peek()))
+ {
+ //skip past all the xref entries.
+ nextLine = readString();
+ skipSpaces();
+ }
+ skipSpaces();
+ }
+
+ private void parseTrailer() throws IOException
+ {
+ COSDictionary parsedTrailer = parseCOSDictionary();
+ COSDictionary docTrailer = document.getTrailer();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parsedTrailer=" + parsedTrailer );
+ log.debug( "docTrailer=" + docTrailer );
+ }
+ if( docTrailer == null )
+ {
+ document.setTrailer( parsedTrailer );
+ }
+ else
+ {
+ docTrailer.addAll( parsedTrailer );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdfparser/PDFStreamParser.java b/src/main/java/org/pdfbox/pdfparser/PDFStreamParser.java
new file mode 100644
index 0000000..d59c5a4
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfparser/PDFStreamParser.java
@@ -0,0 +1,403 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfparser;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.ImageParameters;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This will parse a PDF byte stream and extract operands and such.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.29 $
+ */
+public class PDFStreamParser extends BaseParser
+{
+ private static Logger log = Logger.getLogger(PDFStreamParser.class);
+ private List streamObjects = new ArrayList( 100 );
+ private RandomAccessFile file;
+ private PDFOperator lastBIToken = null;
+
+ /**
+ * Constructor that takes a stream to parse.
+ *
+ * @param stream The stream to read data from.
+ * @param raf The random access file.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public PDFStreamParser( InputStream stream, RandomAccessFile raf ) throws IOException
+ {
+ super( stream );
+ file = raf;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param stream The stream to parse.
+ *
+ * @throws IOException If there is an error initializing the stream.
+ */
+ public PDFStreamParser( COSStream stream ) throws IOException
+ {
+ this( stream.getUnfilteredStream(), stream.getScratchFile() );
+ }
+
+ /**
+ * This will parse the tokens in the stream. This will close the
+ * stream when it is finished parsing.
+ *
+ * @throws IOException If there is an error while parsing the stream.
+ */
+ public void parse() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parse() start" );
+ }
+
+ try
+ {
+ Object token = null;
+ while( (token = parseNextToken()) != null )
+ {
+ streamObjects.add( token );
+ }
+ }
+ finally
+ {
+ pdfSource.close();
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parse() end" );
+ }
+ }
+
+ /**
+ * This will get the tokens that were parsed from the stream.
+ *
+ * @return All of the tokens in the stream.
+ */
+ public List getTokens()
+ {
+ return streamObjects;
+ }
+
+ /**
+ * This will parse the next token in the stream.
+ *
+ * @return The next token in the stream or null if there are no more tokens in the stream.
+ *
+ * @throws IOException If an io error occurs while parsing the stream.
+ */
+ private Object parseNextToken() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parseNextToken() start" );
+ }
+ Object retval = null;
+
+ skipSpaces();
+ int nextByte = pdfSource.peek();
+ if( ((byte)nextByte) == -1 )
+ {
+ return null;
+ }
+ char c = (char)nextByte;
+ switch(c)
+ {
+ case '<':
+ {
+ int leftBracket = pdfSource.read();//pull off first left bracket
+ c = (char)pdfSource.peek(); //check for second left bracket
+ pdfSource.unread( leftBracket ); //put back first bracket
+ if(c == '<')
+ {
+
+ COSDictionary pod = parseCOSDictionary();
+ skipSpaces();
+ if((char)pdfSource.peek() == 's')
+ {
+ retval = parseCOSStream( pod, file );
+ }
+ else
+ {
+ retval = pod;
+ }
+ }
+ else
+ {
+ retval = parseCOSString();
+ }
+ break;
+ }
+ case '[': // array
+ {
+ retval = parseCOSArray();
+ break;
+ }
+ case '(': // string
+ retval = parseCOSString();
+ break;
+ case '/': // name
+ retval = parseCOSName();
+ break;
+ case 'n': // null
+ {
+ String nullString = readString();
+ if( nullString.equals( "null") )
+ {
+ retval = COSNull.NULL;
+ }
+ else
+ {
+ retval = PDFOperator.getOperator( nullString );
+ }
+ break;
+ }
+ case 't':
+ case 'f':
+ {
+ String next = readString();
+ if( next.equals( "true" ) )
+ {
+ retval = COSBoolean.TRUE;
+ break;
+ }
+ else if( next.equals( "false" ) )
+ {
+ retval = COSBoolean.FALSE;
+ }
+ else
+ {
+ retval = PDFOperator.getOperator( next );
+ }
+ break;
+ }
+ case 'R':
+ {
+ String line = readString();
+ if( line.equals( "R" ) )
+ {
+ retval = new COSObject( null );
+ }
+ else
+ {
+ retval = PDFOperator.getOperator( line );
+ }
+ break;
+ }
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ case '+':
+ case '.':
+ {
+ if( Character.isDigit(c) || c == '-' || c == '+' || c == '.')
+ {
+ StringBuffer buf = new StringBuffer();
+ while( Character.isDigit(( c = (char)pdfSource.peek()) )|| c== '-' || c== '+' || c =='.' )
+ {
+ buf.append( c );
+ pdfSource.read();
+ }
+ retval = COSNumber.get( buf.toString() );
+ }
+ else
+ {
+ throw new IOException( "Unknown dir object c='" + c +
+ "' peek='" + (char)pdfSource.peek() + "' " + pdfSource );
+ }
+ break;
+ }
+ case 'B':
+ {
+ String next = readString();
+ retval = PDFOperator.getOperator( next );
+
+ if( next.equals( "BI" ) )
+ {
+ lastBIToken = (PDFOperator)retval;
+ COSDictionary imageParams = new COSDictionary();
+ lastBIToken.setImageParameters( new ImageParameters( imageParams ) );
+ Object nextToken = null;
+ while( (nextToken = parseNextToken()) instanceof COSName )
+ {
+ Object value = parseNextToken();
+ imageParams.setItem( (COSName)nextToken, (COSBase)value );
+ }
+ //final token will be the image data, maybe??
+ PDFOperator imageData = (PDFOperator)nextToken;
+ lastBIToken.setImageData( imageData.getImageData() );
+ }
+ break;
+ }
+ case 'I':
+ {
+ ImageParameters imageParams = lastBIToken.getImageParameters();
+ int expectedBytes = (int)Math.ceil(imageParams.getHeight() * imageParams.getWidth() *
+ (imageParams.getBitsPerComponent()/8) );
+ //Special case for ID operator
+ String id = "" + (char)pdfSource.read() + (char)pdfSource.read();
+ if( !id.equals( "ID" ) )
+ {
+ throw new IOException( "Error: Expected operator 'ID' actual='" + id + "'" );
+ }
+ ByteArrayOutputStream imageData = new ByteArrayOutputStream();
+ boolean foundEnd = false;
+ if( this.isWhitespace() )
+ {
+ //pull off the whitespace character
+ pdfSource.read();
+ }
+ int twoBytesAgo = 0;
+ int lastByte = pdfSource.read();
+ int currentByte = pdfSource.read();
+ int count = 0;
+ //PDF spec is kinda unclear about this. Should a whitespace
+ //always appear before EI? Not sure, I found a PDF
+ //(UnderstandingWebSphereClassLoaders.pdf) which has EI as part
+ //of the image data and will stop parsing prematurely if there is
+ //not a check for <whitespace>EI<whitespace>.
+ while( !(isWhitespace( twoBytesAgo ) &&
+ lastByte == 'E' &&
+ currentByte == 'I' &&
+ isWhitespace() //&&
+ //amyuni2_05d__pdf1_3_acro4x.pdf has image data that
+ //is compressed, so expectedBytes is useless here.
+ //count >= expectedBytes
+ ) &&
+ !pdfSource.isEOF() )
+ {
+ imageData.write( lastByte );
+ twoBytesAgo = lastByte;
+ lastByte = currentByte;
+ currentByte = pdfSource.read();
+ count++;
+ }
+ pdfSource.unread( 'I' ); //unread the EI operator
+ pdfSource.unread( 'E' );
+ retval = PDFOperator.getOperator( "ID" );
+ ((PDFOperator)retval).setImageData( imageData.toByteArray() );
+ break;
+ }
+ case ']':
+ {
+ // some ']' around without its previous '['
+ // this means a PDF is somewhat corrupt but we will continue to parse.
+ pdfSource.read();
+ retval = COSNull.NULL; // must be a better solution than null...
+ break;
+ }
+ default:
+ {
+ //we must be an operator
+ String operator = readOperator();
+ if( operator.trim().length() == 0 )
+ {
+ //we have a corrupt stream, stop reading here
+ retval = null;
+ }
+ else
+ {
+ retval = PDFOperator.getOperator( operator );
+ }
+ }
+
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "parseNextToken() retval=" + retval + " peek=" + (char)pdfSource.peek() + " end" );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will read an operator from the stream.
+ *
+ * @return The operator that was read from the stream.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ protected String readOperator() throws IOException
+ {
+ skipSpaces();
+
+ //average string size is around 2 and the normal string buffer size is
+ //about 16 so lets save some space.
+ StringBuffer buffer = new StringBuffer(4);
+ while(
+ !isWhitespace() &&
+ !isClosing() &&
+ !pdfSource.isEOF() &&
+ pdfSource.peek() != (int)'[' &&
+ pdfSource.peek() != (int)'<' &&
+ pdfSource.peek() != (int)'(' &&
+ pdfSource.peek() != (int)'/' &&
+ (pdfSource.peek() < (int)'0' ||
+ pdfSource.peek() > (int)'9' ) )
+ {
+ buffer.append( (char)pdfSource.read() );
+ }
+ return buffer.toString();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfparser/PDFXref.java b/src/main/java/org/pdfbox/pdfparser/PDFXref.java
new file mode 100644
index 0000000..abe0f35
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfparser/PDFXref.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfparser;
+
+/**
+ * This class represents a PDF xref.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDFXref
+{
+
+ private long count;
+ private long start;
+
+ /**
+ * constructor.
+ *
+ * @param startValue The start attribute.
+ * @param countValue The count attribute.
+ */
+ public PDFXref( long startValue, long countValue )
+ {
+ setStart( startValue );
+ setCount( countValue );
+ }
+
+ /**
+ * This will get the count attribute.
+ *
+ * @return The count.
+ */
+ public long getCount()
+ {
+ return count;
+ }
+
+ /**
+ * This will get the start attribute.
+ *
+ * @return The start.
+ */
+ public long getStart()
+ {
+ return start;
+ }
+
+ /**
+ * This will set the count attribute.
+ *
+ * @param newCount The new count.
+ */
+ private void setCount(long newCount)
+ {
+ count = newCount;
+ }
+
+ /**
+ * This will set the start attribute.
+ *
+ * @param newStart The new start attribute.
+ */
+ private void setStart(long newStart)
+ {
+ start = newStart;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfparser/package.html b/src/main/java/org/pdfbox/pdfparser/package.html
new file mode 100644
index 0000000..fe012c1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfparser/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The pdfparser package contains classes to parse PDF documents and objects within the document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java b/src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java
new file mode 100644
index 0000000..e229587
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+/**
+ * This is a simple class that will contain an index and a value.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class ArrayEntry
+{
+ private int index;
+ private Object value;
+
+ /**
+ * This will get the value for this entry.
+ *
+ * @return The value for this entry.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This will set the value for this entry.
+ *
+ * @param val the new value for this entry.
+ */
+ public void setValue(Object val)
+ {
+ this.value = val;
+ }
+
+ /**
+ * This will get the index of the array entry.
+ *
+ * @return The 0-based index into the array
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * This will set the index value.
+ *
+ * @param i The new index value.
+ */
+ public void setIndex(int i)
+ {
+ index = i;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/MapEntry.java b/src/main/java/org/pdfbox/pdfviewer/MapEntry.java
new file mode 100644
index 0000000..9fd73e5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/MapEntry.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import org.pdfbox.cos.COSName;
+
+
+/**
+ * This is a simple class that will contain a key and a value.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class MapEntry
+{
+ private Object key;
+ private Object value;
+
+ /**
+ * Get the key for this entry.
+ *
+ * @return The entry's key.
+ */
+ public Object getKey()
+ {
+ return key;
+ }
+
+ /**
+ * This will set the key for this entry.
+ *
+ * @param k the new key for this entry.
+ */
+ public void setKey(Object k)
+ {
+ key = k;
+ }
+
+ /**
+ * This will get the value for this entry.
+ *
+ * @return The value for this entry.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This will set the value for this entry.
+ *
+ * @param val the new value for this entry.
+ */
+ public void setValue(Object val)
+ {
+ this.value = val;
+ }
+
+ /**
+ * This will output a string representation of this class.
+ *
+ * @return A string representation of this class.
+ */
+ public String toString()
+ {
+ String retval = null;
+ if( key instanceof COSName )
+ {
+ retval = ((COSName)key).getName();
+ }
+ else
+ {
+ retval = "" +key;
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java b/src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java
new file mode 100644
index 0000000..1d28bd3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+
+import java.io.IOException;
+
+import javax.swing.JPanel;
+
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+/**
+ * This is a simple JPanel that can be used to display a PDF page.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDFPagePanel extends JPanel
+{
+
+ private PDPage page;
+ private PageDrawer drawer = null;
+ private Dimension pageDimension = null;
+
+ /**
+ * Constructor.
+ *
+ * @throws IOException If there is an error creating the Page drawing objects.
+ */
+ public PDFPagePanel() throws IOException
+ {
+ drawer = new PageDrawer();
+ }
+
+ /**
+ * This will set the page that should be displayed in this panel.
+ *
+ * @param pdfPage The page to draw.
+ */
+ public void setPage( PDPage pdfPage )
+ {
+ page = pdfPage;
+ PDRectangle pageSize = page.findMediaBox();
+ int rotation = page.findRotation();
+ pageDimension = pageSize.createDimension();
+ if( rotation == 90 || rotation == 270 )
+ {
+ pageDimension = new Dimension( pageDimension.height, pageDimension.width );
+ }
+ setSize( pageDimension );
+ setBackground( java.awt.Color.white );
+ }
+
+ /**
+ * @see JPanel#paint( Graphics )
+ */
+ public void paint(Graphics g )
+ {
+ try
+ {
+ g.setColor( getBackground() );
+ g.fillRect( 0, 0, getWidth(), getHeight() );
+ drawer.drawPage( g, page, pageDimension );
+ }
+ catch( IOException e )
+ {
+ e.printStackTrace();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java b/src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java
new file mode 100644
index 0000000..98c1f5a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Component;
+
+import javax.swing.JTree;
+
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+/**
+ * A class to render tree cells for the pdfviewer.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDFTreeCellRenderer extends DefaultTreeCellRenderer
+{
+ /**
+ * @see DefaultTreeCellRenderer#getTreeCellRendererComponent( JTree, Object, boolean,
+ * boolean, boolean, int, boolean )
+ */
+ public Component getTreeCellRendererComponent(
+ JTree tree,
+ Object nodeValue,
+ boolean isSelected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean componentHasFocus)
+ {
+ nodeValue = convertToTreeObject( nodeValue );
+ return super.getTreeCellRendererComponent( tree, nodeValue, isSelected, expanded, leaf,
+ row, componentHasFocus );
+ }
+
+ private Object convertToTreeObject( Object nodeValue )
+ {
+ if( nodeValue instanceof MapEntry )
+ {
+ MapEntry entry = (MapEntry)nodeValue;
+ COSName key = (COSName)entry.getKey();
+ COSBase value = (COSBase)entry.getValue();
+ nodeValue = key.getName() + ":" + convertToTreeObject( value );
+ }
+ else if( nodeValue instanceof COSFloat )
+ {
+ nodeValue = "" + ((COSFloat)nodeValue).floatValue();
+ }
+ else if( nodeValue instanceof COSInteger )
+ {
+ nodeValue = "" + ((COSInteger)nodeValue).intValue();
+ }
+ else if( nodeValue instanceof COSString )
+ {
+ nodeValue = ((COSString)nodeValue).getString();
+ }
+ else if( nodeValue instanceof COSName )
+ {
+ nodeValue = ((COSName)nodeValue).getName();
+ }
+ else if( nodeValue instanceof ArrayEntry )
+ {
+ ArrayEntry entry = (ArrayEntry)nodeValue;
+ nodeValue = "[" + entry.getIndex() + "]" + convertToTreeObject( entry.getValue() );
+ }
+ else if( nodeValue instanceof COSNull )
+ {
+ nodeValue = "null";
+ }
+ else if( nodeValue instanceof COSDictionary )
+ {
+ COSDictionary dict = (COSDictionary)nodeValue;
+ if( nodeValue instanceof COSStream )
+ {
+ nodeValue = "Stream";
+ }
+ else
+ {
+ nodeValue = "Dictionary";
+ }
+
+ COSName type = (COSName)dict.getDictionaryObject( COSName.TYPE );
+ if( type != null )
+ {
+ nodeValue = nodeValue + "(" + type.getName();
+ COSName subType = (COSName)dict.getDictionaryObject( COSName.SUBTYPE );
+ if( subType != null )
+ {
+ nodeValue = nodeValue + ":" + subType.getName();
+ }
+
+ nodeValue = nodeValue + ")";
+ }
+ }
+ else if( nodeValue instanceof COSArray )
+ {
+ nodeValue="Array";
+ }
+ else if( nodeValue instanceof COSString )
+ {
+ nodeValue = ((COSString)nodeValue).getString();
+ }
+ return nodeValue;
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java b/src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java
new file mode 100644
index 0000000..368a2ca
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java
@@ -0,0 +1,332 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+/**
+ * A tree model that uses a cos document.
+ *
+ *
+ * @author wurtz
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeModel;
+
+//import java.awt.event.*;
+import javax.swing.event.TreeModelListener;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSObject;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class to model a PDF document as a tree structure.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+public class PDFTreeModel implements TreeModel
+{
+ private PDDocument document;
+
+ /**
+ * constructor.
+ */
+ public PDFTreeModel()
+ {
+ //default constructor
+ }
+
+ /**
+ * Constructor to take a document.
+ *
+ * @param doc The document to display in the tree.
+ */
+ public PDFTreeModel(PDDocument doc)
+ {
+ setDocument(doc);
+ }
+
+ /**
+ * Set the document to display in the tree.
+ *
+ * @param doc The document to display in the tree.
+ */
+ public void setDocument(PDDocument doc)
+ {
+ document = doc;
+ }
+ /**
+ * Adds a listener for the <code>TreeModelEvent</code>
+ * posted after the tree changes.
+ *
+ * @param l the listener to add
+ * @see #removeTreeModelListener
+ *
+ */
+ public void addTreeModelListener(TreeModelListener l)
+ {
+ //required for interface
+ }
+
+ /**
+ * Returns the child of <code>parent</code> at index <code>index</code>
+ * in the parent's
+ * child array. <code>parent</code> must be a node previously obtained
+ * from this data source. This should not return <code>null</code>
+ * if <code>index</code>
+ * is a valid index for <code>parent</code> (that is <code>index >= 0 &&
+ * index < getChildCount(parent</code>)).
+ *
+ * @param parent a node in the tree, obtained from this data source
+ * @param index The index into the parent object to location the child object.
+ * @return the child of <code>parent</code> at index <code>index</code>
+ *
+ */
+ public Object getChild(Object parent, int index)
+ {
+ Object retval = null;
+ if( parent instanceof COSArray )
+ {
+ ArrayEntry entry = new ArrayEntry();
+ entry.setIndex( index );
+ entry.setValue( ((COSArray)parent).getObject( index ) );
+ retval = entry;
+ }
+ else if( parent instanceof COSDictionary )
+ {
+ COSDictionary dict = ((COSDictionary)parent);
+ List keys = dict.keyList();
+ Collections.sort( keys );
+ Object key = keys.get( index );
+ Object value = dict.getDictionaryObject( (COSName)key );
+ MapEntry entry = new MapEntry();
+ entry.setKey( key );
+ entry.setValue( value );
+ retval = entry;
+ }
+ else if( parent instanceof MapEntry )
+ {
+ retval = getChild( ((MapEntry)parent).getValue(), index );
+ }
+ else if( parent instanceof ArrayEntry )
+ {
+ retval = getChild( ((ArrayEntry)parent).getValue(), index );
+ }
+ else if( parent instanceof COSDocument )
+ {
+ retval = ((COSDocument)parent).getObjects().get( index );
+ }
+ else if( parent instanceof COSObject )
+ {
+ retval = ((COSObject)parent).getObject();
+ }
+ else
+ {
+ throw new RuntimeException( "Unknown COS type " + parent.getClass().getName() );
+ }
+ return retval;
+ }
+
+ /** Returns the number of children of <code>parent</code>.
+ * Returns 0 if the node
+ * is a leaf or if it has no children. <code>parent</code> must be a node
+ * previously obtained from this data source.
+ *
+ * @param parent a node in the tree, obtained from this data source
+ * @return the number of children of the node <code>parent</code>
+ *
+ */
+ public int getChildCount(Object parent)
+ {
+ int retval = 0;
+ if( parent instanceof COSArray )
+ {
+ retval = ((COSArray)parent).size();
+ }
+ else if( parent instanceof COSDictionary )
+ {
+ retval = ((COSDictionary)parent).size();
+ }
+ else if( parent instanceof MapEntry )
+ {
+ retval = getChildCount( ((MapEntry)parent).getValue() );
+ }
+ else if( parent instanceof ArrayEntry )
+ {
+ retval = getChildCount( ((ArrayEntry)parent).getValue() );
+ }
+ else if( parent instanceof COSDocument )
+ {
+ retval = ((COSDocument)parent).getObjects().size();
+ }
+ else if( parent instanceof COSObject )
+ {
+ retval = 1;
+ }
+ return retval;
+ }
+
+ /** Returns the index of child in parent. If <code>parent</code>
+ * is <code>null</code> or <code>child</code> is <code>null</code>,
+ * returns -1.
+ *
+ * @param parent a note in the tree, obtained from this data source
+ * @param child the node we are interested in
+ * @return the index of the child in the parent, or -1 if either
+ * <code>child</code> or <code>parent</code> are <code>null</code>
+ *
+ */
+ public int getIndexOfChild(Object parent, Object child)
+ {
+ int retval = -1;
+ if( parent != null && child != null )
+ {
+ if( parent instanceof COSArray )
+ {
+ COSArray array = (COSArray)parent;
+ if( child instanceof ArrayEntry )
+ {
+ ArrayEntry arrayEntry = (ArrayEntry)child;
+ retval = arrayEntry.getIndex();
+ }
+ else
+ {
+ retval = array.indexOf( (COSBase)child );
+ }
+ }
+ else if( parent instanceof COSDictionary )
+ {
+ MapEntry entry = (MapEntry)child;
+ COSDictionary dict = (COSDictionary)parent;
+ List keys = dict.keyList();
+ Collections.sort( keys );
+ for( int i=0; retval == -1 && i<keys.size(); i++ )
+ {
+ if( keys.get( i ).equals( entry.getKey() ) )
+ {
+ retval = i;
+ }
+ }
+ }
+ else if( parent instanceof MapEntry )
+ {
+ retval = getIndexOfChild( ((MapEntry)parent).getValue(), child );
+ }
+ else if( parent instanceof ArrayEntry )
+ {
+ retval = getIndexOfChild( ((ArrayEntry)parent).getValue(), child );
+ }
+ else if( parent instanceof COSDocument )
+ {
+ retval = ((COSDocument)parent).getObjects().indexOf( child );
+ }
+ else if( parent instanceof COSObject )
+ {
+ retval = 0;
+ }
+ else
+ {
+ throw new RuntimeException( "Unknown COS type " + parent.getClass().getName() );
+ }
+ }
+ return retval;
+ }
+
+ /** Returns the root of the tree. Returns <code>null</code>
+ * only if the tree has no nodes.
+ *
+ * @return the root of the tree
+ *
+ */
+ public Object getRoot()
+ {
+ return document.getDocument().getTrailer();
+ }
+
+ /** Returns <code>true</code> if <code>node</code> is a leaf.
+ * It is possible for this method to return <code>false</code>
+ * even if <code>node</code> has no children.
+ * A directory in a filesystem, for example,
+ * may contain no files; the node representing
+ * the directory is not a leaf, but it also has no children.
+ *
+ * @param node a node in the tree, obtained from this data source
+ * @return true if <code>node</code> is a leaf
+ *
+ */
+ public boolean isLeaf(Object node)
+ {
+ boolean isLeaf = !(node instanceof COSDictionary ||
+ node instanceof COSArray ||
+ node instanceof COSDocument ||
+ 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
+ * <code>addTreeModelListener</code>.
+ *
+ * @see #addTreeModelListener
+ * @param l the listener to remove
+ *
+ */
+
+ public void removeTreeModelListener(TreeModelListener l)
+ {
+ //required for interface
+ }
+
+ /** Messaged when the user has altered the value for the item identified
+ * by <code>path</code> to <code>newValue</code>.
+ * If <code>newValue</code> signifies a truly new value
+ * the model should post a <code>treeNodesChanged</code> event.
+ *
+ * @param path path to the node that the user has altered
+ * @param newValue the new value from the TreeCellEditor
+ *
+ */
+ public void valueForPathChanged(TreePath path, Object newValue)
+ {
+ //required for interface
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PageDrawer.java b/src/main/java/org/pdfbox/pdfviewer/PageDrawer.java
new file mode 100644
index 0000000..7c39f7b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PageDrawer.java
@@ -0,0 +1,268 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+
+import java.awt.geom.GeneralPath;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.font.PDFont;
+
+import org.pdfbox.util.PDFStreamEngine;
+import org.pdfbox.util.ResourceLoader;
+import org.pdfbox.util.TextPosition;
+
+/**
+ * This will paint a page in a PDF document to a graphics context.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.17 $
+ */
+public class PageDrawer extends PDFStreamEngine
+{
+
+ private Graphics2D graphics;
+ private Dimension pageSize;
+ private PDPage page;
+
+ private List lineSubPaths = new ArrayList();
+ private GeneralPath linePath = new GeneralPath();
+ private Color strokingColor = Color.BLACK;
+ private Color nonStrokingColor = Color.BLACK;
+
+ /**
+ * Default constructor, loads properties from file.
+ *
+ * @throws IOException If there is an error loading properties from the file.
+ */
+ public PageDrawer() throws IOException
+ {
+ super( ResourceLoader.loadProperties( "Resources/PageDrawer.properties" ) );
+ }
+
+ /**
+ * This will draw the page to the requested context.
+ *
+ * @param g The graphics context to draw onto.
+ * @param p The page to draw.
+ * @param pageDimension The size of the page to draw.
+ *
+ * @throws IOException If there is an IO error while drawing the page.
+ */
+ public void drawPage( Graphics g, PDPage p, Dimension pageDimension ) throws IOException
+ {
+ graphics = (Graphics2D)g;
+ page = p;
+ pageSize = pageDimension;
+
+ graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ processStream( page, page.findResources(), page.getContents().getStream() );
+ // Transformations should be done in order
+ // 1 - Translate
+ // 2 - Rotate
+ // 3 - Scale
+ // Refer to PDFReference p176 (or 188 in xpdf)
+ //AffineTransform transform = graphics.getTransform();
+ //transform.setToTranslate( 0, page.findMediaBox().getHeight()/2 );
+ //transform.setToRotation((double)p.getRotation());
+ //transform.setTransform( 1, 0, 0, 1, 0, 0 );
+ //transform.setToScale( 1, 1 );
+
+ //AffineTransform rotation = graphics.getTransform();
+ //rotation.rotate( (page.findRotation() * Math.PI) / 180d );
+ //graphics.setTransform( rotation );
+ }
+
+ /**
+ * You should override this method if you want to perform an action when a
+ * string is being shown.
+ *
+ * @param text The string to display.
+ */
+ protected void showCharacter( TextPosition text )
+ {
+ //should use colorspaces for the font color but for now assume that
+ //the font color is black
+ try
+ {
+ graphics.setColor( Color.black );
+ PDFont font = text.getFont();
+ font.drawString( text.getCharacter(), graphics, text.getFontSize(), text.getXScale(), text.getYScale(),
+ text.getX(), text.getY() );
+ }
+ catch( IOException io )
+ {
+ io.printStackTrace();
+ }
+ }
+
+ /**
+ * Get the graphics that we are currently drawing on.
+ *
+ * @return The graphics we are drawing on.
+ */
+ public Graphics2D getGraphics()
+ {
+ return graphics;
+ }
+
+ /**
+ * Get the page that is currently being drawn.
+ *
+ * @return The page that is being drawn.
+ */
+ public PDPage getPage()
+ {
+ return page;
+ }
+
+ /**
+ * Get the size of the page that is currently being drawn.
+ *
+ * @return The size of the page that is being drawn.
+ */
+ public Dimension getPageSize()
+ {
+ return pageSize;
+ }
+
+ /**
+ * Fix the y coordinate based on page rotation.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @return The updated y coordinate.
+ */
+ public double fixY( double x, double y )
+ {
+ double retval = y;
+ int rotation = page.findRotation();
+ if( rotation == 0 )
+ {
+ retval = pageSize.getHeight() - y;
+ }
+ else if( rotation == 90 )
+ {
+ retval = y;
+ }
+ return retval;
+ }
+
+ /**
+ * Get the current line path to be drawn.
+ *
+ * @return The current line path to be drawn.
+ */
+ public GeneralPath getLinePath()
+ {
+ return linePath;
+ }
+
+ /**
+ * Set the line path to draw.
+ *
+ * @param newLinePath Set the line path to draw.
+ */
+ public void setLinePath(GeneralPath newLinePath)
+ {
+ linePath = newLinePath;
+ }
+
+ /**
+ * Get the current list of line paths to be drawn.
+ *
+ * @return The current list of line paths to be drawn.
+ */
+ public List getLineSubPaths()
+ {
+ return lineSubPaths;
+ }
+
+ /**
+ * Set the list of line paths to draw.
+ *
+ * @param newLineSubPaths Set the list of line paths to draw.
+ */
+ public void setLineSubPaths(List newLineSubPaths)
+ {
+ lineSubPaths = newLineSubPaths;
+ }
+
+ /**
+ * Get the non stroking color.
+ *
+ * @return The non stroking color.
+ */
+ public Color getNonStrokingColor()
+ {
+ return nonStrokingColor;
+ }
+
+ /**
+ * Set the non stroking color.
+ *
+ * @param newNonStrokingColor The non stroking color.
+ */
+ public void setNonStrokingColor(Color newNonStrokingColor)
+ {
+ nonStrokingColor = newNonStrokingColor;
+ }
+
+ /**
+ * Get the stroking color.
+ *
+ * @return The stroking color.
+ */
+ public Color getStrokingColor()
+ {
+ return strokingColor;
+ }
+
+ /**
+ * Set the stroking color.
+ *
+ * @param newStrokingColor The stroking color.
+ */
+ public void setStrokingColor(Color newStrokingColor)
+ {
+ strokingColor = newStrokingColor;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PageWrapper.java b/src/main/java/org/pdfbox/pdfviewer/PageWrapper.java
new file mode 100644
index 0000000..b2dc275
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PageWrapper.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Dimension;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.io.IOException;
+
+import javax.swing.JPanel;
+
+import org.pdfbox.PDFReader;
+import org.pdfbox.pdmodel.PDPage;
+
+/**
+ * A class to handle some prettyness around a single PDF page.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.4 $
+ */
+public class PageWrapper implements MouseMotionListener
+{
+ private JPanel pageWrapper = new JPanel();
+ private PDFPagePanel pagePanel = null;
+ private PDFReader reader = null;
+
+ private static final int SPACE_AROUND_DOCUMENT = 20;
+
+ /**
+ * Constructor.
+ *
+ * @param aReader The reader application that holds this page.
+ *
+ * @throws IOException If there is an error creating the page drawing objects.
+ */
+ public PageWrapper( PDFReader aReader ) throws IOException
+ {
+ reader = aReader;
+ pagePanel = new PDFPagePanel();
+ pageWrapper.setLayout( null );
+ pageWrapper.add( pagePanel );
+ pagePanel.setLocation( SPACE_AROUND_DOCUMENT, SPACE_AROUND_DOCUMENT );
+ pageWrapper.setBorder( javax.swing.border.LineBorder.createBlackLineBorder() );
+ pagePanel.addMouseMotionListener( this );
+ }
+
+ /**
+ * This will display the PDF page in this component.
+ *
+ * @param page The PDF page to display.
+ */
+ public void displayPage( PDPage page )
+ {
+ pagePanel.setPage( page );
+ pagePanel.setPreferredSize( pagePanel.getSize() );
+ Dimension d = pagePanel.getSize();
+ d.width+=(SPACE_AROUND_DOCUMENT*2);
+ d.height+=(SPACE_AROUND_DOCUMENT*2);
+
+ pageWrapper.setPreferredSize( d );
+ pageWrapper.validate();
+ }
+
+ /**
+ * This will get the JPanel that can be displayed.
+ *
+ * @return The panel with the displayed PDF page.
+ */
+ public JPanel getPanel()
+ {
+ return pageWrapper;
+ }
+
+ /**
+ * @see MouseMotionListener#mouseDragged(MouseEvent)
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ //do nothing when mouse moves.
+ }
+
+ /**
+ * @see MouseMotionListener#mouseMoved( MouseEvent )
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ //reader.getBottomStatusPanel().getStatusLabel().setText( e.getX() + "," + (pagePanel.getHeight() - e.getY()) );
+ reader.getBottomStatusPanel().getStatusLabel().setText( e.getX() + "," + e.getY() );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java b/src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java
new file mode 100644
index 0000000..516d2c2
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Dimension;
+
+import javax.swing.JPanel;
+
+import javax.swing.JLabel;
+import java.awt.FlowLayout;
+/**
+ * A panel to display at the bottom of the window for status and other stuff.
+ *
+ * @author Ben Litchfield (ben@pdfbox.org)
+ * @version $Revision: 1.1 $
+ */
+public class ReaderBottomPanel extends JPanel
+{
+
+ private JLabel statusLabel = null;
+
+ /**
+ * This is the default constructor.
+ */
+ public ReaderBottomPanel()
+ {
+ super();
+ initialize();
+ }
+
+ /**
+ * This method initializes this.
+ */
+ private void initialize()
+ {
+ FlowLayout flowLayout1 = new FlowLayout();
+ this.setLayout(flowLayout1);
+ this.setComponentOrientation(java.awt.ComponentOrientation.LEFT_TO_RIGHT);
+ this.setPreferredSize( new Dimension( 1000, 20 ) );
+ flowLayout1.setAlignment(java.awt.FlowLayout.LEFT);
+ this.add(getStatusLabel(), null);
+ }
+
+ /**
+ * This method initializes status label.
+ *
+ * @return javax.swing.JLabel
+ */
+ public JLabel getStatusLabel()
+ {
+ if (statusLabel == null)
+ {
+ statusLabel = new JLabel();
+ statusLabel.setText("Ready");
+ }
+ return statusLabel;
+ }
+ }
diff --git a/src/main/java/org/pdfbox/pdfviewer/package.html b/src/main/java/org/pdfbox/pdfviewer/package.html
new file mode 100644
index 0000000..85ec4b6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The pdfviewer package contains classes to graphically display information about a PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdfwriter/COSStandardOutputStream.java b/src/main/java/org/pdfbox/pdfwriter/COSStandardOutputStream.java
new file mode 100644
index 0000000..1881bf2
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfwriter/COSStandardOutputStream.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfwriter;
+
+
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * simple output stream with some minor features for generating "pretty"
+ * pdf files.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.4 $
+ */
+public class COSStandardOutputStream extends FilterOutputStream
+{
+
+ /**
+ * To be used when 2 byte sequence is enforced.
+ */
+ public static final byte[] CRLF = "\r\n".getBytes();
+
+ /**
+ * Line feed character.
+ */
+ public static final byte[] LF = "\n".getBytes();
+
+ /**
+ * standard line separator on this platform.
+ */
+ public static final byte[] EOL = System.getProperty("line.separator").getBytes();
+
+ // current byte pos in the output stream
+ private long pos = 0;
+ // flag to prevent generating two newlines in sequence
+ private boolean onNewLine = false;
+
+ /**
+ * COSOutputStream constructor comment.
+ *
+ * @param out The underlying stream to write to.
+ */
+ public COSStandardOutputStream(OutputStream out)
+ {
+ super(out);
+ }
+ /**
+ * This will get the current position in the stream.
+ *
+ * @return The current position in the stream.
+ */
+ public long getPos()
+ {
+ return pos;
+ }
+ /**
+ * This will tell if we are on a newling.
+ *
+ * @return true If we are on a newline.
+ */
+ public boolean isOnNewLine()
+ {
+ return onNewLine;
+ }
+ /**
+ * This will set a flag telling if we are on a newline.
+ *
+ * @param newOnNewLine The new value for the onNewLine attribute.
+ */
+ public void setOnNewLine(boolean newOnNewLine)
+ {
+ onNewLine = newOnNewLine;
+ }
+ /**
+ * This will set the position in the stream.
+ *
+ * @param newPos The new position in the stream.
+ */
+ private void setPos(int newPos)
+ {
+ pos = newPos;
+ }
+
+ /**
+ * This will write some byte to the stream.
+ *
+ * @param b The source byte array.
+ * @param off The offset into the array to start writing.
+ * @param len The number of bytes to write.
+ *
+ * @throws IOException If the underlying stream throws an exception.
+ */
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ setOnNewLine(false);
+ out.write(b, off, len);
+ pos += len;
+ }
+
+ /**
+ * This will write a single byte to the stream.
+ *
+ * @param b The byte to write to the stream.
+ *
+ * @throws IOException If there is an error writing to the underlying stream.
+ */
+ public void write(int b) throws IOException
+ {
+ setOnNewLine(false);
+ out.write(b);
+ pos++;
+ }
+
+ /**
+ * This will write a CRLF to the stream.
+ *
+ * @throws IOException If there is an error writing the data to the stream.
+ */
+ public void writeCRLF() throws IOException
+ {
+ write(CRLF);
+ // setOnNewLine(true);
+ }
+
+ /**
+ * This will write an EOL to the stream.
+ *
+ * @throws IOException If there is an error writing to the stream
+ */
+ public void writeEOL() throws IOException
+ {
+ if (!isOnNewLine())
+ {
+ write(EOL);
+ setOnNewLine(true);
+ }
+ }
+
+ /**
+ * This will write a Linefeed to the stream.
+ *
+ * @throws IOException If there is an error writing to the underlying stream.
+ */
+ public void writeLF() throws IOException
+ {
+ write(LF);
+ // setOnNewLine(true);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfwriter/COSWriter.java b/src/main/java/org/pdfbox/pdfwriter/COSWriter.java
new file mode 100644
index 0000000..fa9cf48
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfwriter/COSWriter.java
@@ -0,0 +1,1091 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfwriter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+
+import org.pdfbox.persistence.util.COSObjectKey;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.ICOSVisitor;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.encryption.DocumentEncryption;
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.exceptions.CryptographyException;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSNull;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.apache.log4j.Logger;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * this class acts on a in-memory representation of a pdf document.
+ *
+ * todo no support for incremental updates
+ * todo single xref section only
+ * todo no linearization
+ *
+ * @author Michael Traut
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.32 $
+ */
+public class COSWriter implements ICOSVisitor
+{
+
+ private static Logger log = Logger.getLogger( COSWriter.class );
+ /**
+ * The dictionary open token.
+ */
+ public static final byte[] DICT_OPEN = "<<".getBytes();
+ /**
+ * The dictionary close token.
+ */
+ public static final byte[] DICT_CLOSE = ">>".getBytes();
+ /**
+ * space character.
+ */
+ public static final byte[] SPACE = " ".getBytes();
+ /**
+ * The start to a PDF comment.
+ */
+ public static final byte[] COMMENT = "%".getBytes();
+
+ /**
+ * The output version of the PDF.
+ */
+ public static final byte[] VERSION = "PDF-1.4".getBytes();
+ /**
+ * Garbage bytes used to create the PDF header.
+ */
+ public static final byte[] GARBAGE = new byte[] {(byte)0xf6, (byte)0xe4, (byte)0xfc, (byte)0xdf};
+ /**
+ * The EOF constant.
+ */
+ public static final byte[] EOF = "%%EOF".getBytes();
+ // pdf tokens
+
+ /**
+ * The reference token.
+ */
+ public static final byte[] REFERENCE = "R".getBytes();
+ /**
+ * The XREF token.
+ */
+ public static final byte[] XREF = "xref".getBytes();
+ /**
+ * The xref free token.
+ */
+ public static final byte[] XREF_FREE = "f".getBytes();
+ /**
+ * The xref used token.
+ */
+ public static final byte[] XREF_USED = "n".getBytes();
+ /**
+ * The trailer token.
+ */
+ public static final byte[] TRAILER = "trailer".getBytes();
+ /**
+ * The start xref token.
+ */
+ public static final byte[] STARTXREF = "startxref".getBytes();
+ /**
+ * The starting object token.
+ */
+ public static final byte[] OBJ = "obj".getBytes();
+ /**
+ * The end object token.
+ */
+ public static final byte[] ENDOBJ = "endobj".getBytes();
+ /**
+ * The array open token.
+ */
+ public static final byte[] ARRAY_OPEN = "[".getBytes();
+ /**
+ * The array close token.
+ */
+ public static final byte[] ARRAY_CLOSE = "]".getBytes();
+ /**
+ * The open stream token.
+ */
+ public static final byte[] STREAM = "stream".getBytes();
+ /**
+ * The close stream token.
+ */
+ public static final byte[] ENDSTREAM = "endstream".getBytes();
+
+ private NumberFormat formatXrefOffset = new DecimalFormat("0000000000");
+ /**
+ * The decimal format for the xref object generation number data.
+ */
+ private NumberFormat formatXrefGeneration = new DecimalFormat("00000");
+
+ private NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US );
+
+ // the stream where we create the pdf output
+ private OutputStream output;
+ // the stream used to write standard cos data
+ private COSStandardOutputStream standardOutput;
+
+ // the start position of the x ref section
+ private long startxref = 0;
+
+ // the current object number
+ private long number = 0;
+
+ // maps the object to the keys generated in the writer
+ // these are used for indirect refrences in other objects
+ //A hashtable is used on purpose over a hashmap
+ //so that null entries will not get added.
+ private Map objectKeys = new Hashtable();
+
+ // the list of x ref entries to be made so far
+ private List xRefEntries = new ArrayList();
+
+ //A list of objects to write.
+ private List objectsToWrite = new ArrayList();
+
+ //a list of objects already written
+ private Set writtenObjects = new HashSet();
+ //An 'actual' is any COSBase that is not a COSObject.
+ //need to keep a list of the actuals that are added
+ //as well as the objects because there is a problem
+ //when adding a COSObject and then later adding
+ //the actual for that object, so we will track
+ //actuals separately.
+ private Set actualsAdded = new HashSet();
+
+ private COSObjectKey currentObjectKey = null;
+ private PDDocument document = null;
+ private DocumentEncryption enc = null;
+
+ /**
+ * COSWriter constructor comment.
+ *
+ * @param os The wrapped output stream.
+ */
+ public COSWriter(OutputStream os)
+ {
+ super();
+ setOutput(os);
+ setStandardOutput(new COSStandardOutputStream(getOutput()));
+ formatDecimal.setMaximumFractionDigits( 10 );
+ formatDecimal.setGroupingUsed( false );
+ }
+ /**
+ * add an entry in the x ref table for later dump.
+ *
+ * @param entry The new entry to add.
+ */
+ protected void addXRefEntry(COSWriterXRefEntry entry)
+ {
+ getXRefEntries().add(entry);
+ }
+
+ /**
+ * This will close the stream.
+ *
+ * @throws IOException If the underlying stream throws an exception.
+ */
+ public void close() throws IOException
+ {
+ if (getStandardOutput() != null)
+ {
+ getStandardOutput().close();
+ }
+ if (getOutput() != null)
+ {
+ getOutput().close();
+ }
+ }
+
+ /**
+ * This will get the current object number.
+ *
+ * @return The current object number.
+ */
+ protected long getNumber()
+ {
+ return number;
+ }
+
+ /**
+ * This will get all available object keys.
+ *
+ * @return A map of all object keys.
+ */
+ public java.util.Map getObjectKeys()
+ {
+ return objectKeys;
+ }
+
+ /**
+ * This will get the output stream.
+ *
+ * @return The output stream.
+ */
+ protected java.io.OutputStream getOutput()
+ {
+ return output;
+ }
+
+ /**
+ * This will get the standard output stream.
+ *
+ * @return The standard output stream.
+ */
+ protected COSStandardOutputStream getStandardOutput()
+ {
+ return standardOutput;
+ }
+
+ /**
+ * This will get the current start xref.
+ *
+ * @return The current start xref.
+ */
+ protected long getStartxref()
+ {
+ return startxref;
+ }
+ /**
+ * This will get the xref entries.
+ *
+ * @return All available xref entries.
+ */
+ protected java.util.List getXRefEntries()
+ {
+ return xRefEntries;
+ }
+
+ /**
+ * This will set the current object number.
+ *
+ * @param newNumber The new object number.
+ */
+ protected void setNumber(long newNumber)
+ {
+ number = newNumber;
+ }
+
+ /**
+ * This will set the output stream.
+ *
+ * @param newOutput The new output stream.
+ */
+ private void setOutput( OutputStream newOutput )
+ {
+ output = newOutput;
+ }
+
+ /**
+ * This will set the standard output stream.
+ *
+ * @param newStandardOutput The new standard output stream.
+ */
+ private void setStandardOutput(COSStandardOutputStream newStandardOutput)
+ {
+ standardOutput = newStandardOutput;
+ }
+
+ /**
+ * This will set the start xref.
+ *
+ * @param newStartxref The new start xref attribute.
+ */
+ protected void setStartxref(long newStartxref)
+ {
+ startxref = newStartxref;
+ }
+
+ /**
+ * This will write the body of the document.
+ *
+ * @param doc The document to write the body for.
+ *
+ * @throws IOException If there is an error writing the data.
+ * @throws COSVisitorException If there is an error generating the data.
+ */
+ protected void doWriteBody(COSDocument doc) throws IOException, COSVisitorException
+ {
+ COSDictionary trailer = doc.getTrailer();
+ COSDictionary root = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
+ COSDictionary info = (COSDictionary)trailer.getDictionaryObject( COSName.getPDFName( "Info" ) );
+ COSDictionary encrypt = (COSDictionary)trailer.getDictionaryObject( COSName.getPDFName( "Encrypt" ) );
+ if( root != null )
+ {
+ addObjectToWrite( root );
+ }
+ if( info != null )
+ {
+ addObjectToWrite( info );
+ }
+
+
+ while( objectsToWrite.size() > 0 )
+ {
+ COSBase nextObject = (COSBase)objectsToWrite.remove( 0 );
+ doWriteObject( nextObject );
+ }
+
+ document.clearWillEncryptWhenSaving();
+ if( encrypt != null )
+ {
+ addObjectToWrite( encrypt );
+ }
+
+ while( objectsToWrite.size() > 0 )
+ {
+ COSBase nextObject = (COSBase)objectsToWrite.remove( 0 );
+ doWriteObject( nextObject );
+ }
+
+ // write all objects
+ /**
+ for (Iterator i = doc.getObjects().iterator(); i.hasNext();)
+ {
+ COSObject obj = (COSObject) i.next();
+ doWriteObject(obj);
+ }**/
+ }
+
+ private void addObjectToWrite( COSBase object )
+ {
+ COSBase actual = object;
+ if( actual instanceof COSObject )
+ {
+ actual = ((COSObject)actual).getObject();
+ }
+
+ if( !writtenObjects.contains( object ) &&
+ !objectsToWrite.contains( object ) &&
+ !actualsAdded.contains( actual ) )
+ {
+ objectsToWrite.add( object );
+ if( actual != null )
+ {
+ actualsAdded.add( actual );
+ }
+ }
+ }
+
+ /**
+ * This will write a COS object.
+ *
+ * @param obj The object to write.
+ *
+ * @throws COSVisitorException If there is an error visiting objects.
+ */
+ public void doWriteObject( COSBase obj ) throws COSVisitorException
+ {
+ try
+ {
+ writtenObjects.add( obj );
+ // find the physical reference
+ currentObjectKey = getObjectKey( obj );
+ // add a x ref entry
+ addXRefEntry( new COSWriterXRefEntry(getStandardOutput().getPos(), obj, currentObjectKey));
+ // write the object
+ getStandardOutput().write(String.valueOf(currentObjectKey.getNumber()).getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(String.valueOf(currentObjectKey.getGeneration()).getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(OBJ);
+ getStandardOutput().writeEOL();
+ obj.accept( this );
+ getStandardOutput().writeEOL();
+ getStandardOutput().write(ENDOBJ);
+ getStandardOutput().writeEOL();
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * This will write the header to the PDF document.
+ *
+ * @param doc The document to get the data from.
+ *
+ * @throws IOException If there is an error writing to the stream.
+ */
+ protected void doWriteHeader(COSDocument doc) throws IOException
+ {
+ getStandardOutput().write( doc.getHeaderString().getBytes() );
+ getStandardOutput().writeEOL();
+ getStandardOutput().write(COMMENT);
+ getStandardOutput().write(GARBAGE);
+ getStandardOutput().writeEOL();
+ }
+
+
+ /**
+ * This will write the trailer to the PDF document.
+ *
+ * @param doc The document to create the trailer for.
+ *
+ * @throws IOException If there is an IOError while writing the document.
+ * @throws COSVisitorException If there is an error while generating the data.
+ */
+ protected void doWriteTrailer(COSDocument doc) throws IOException, COSVisitorException
+ {
+ getStandardOutput().write(TRAILER);
+ getStandardOutput().writeEOL();
+
+ COSDictionary trailer = doc.getTrailer();
+ //sort xref, needed only if object keys not regenerated
+ Collections.sort(getXRefEntries());
+ COSWriterXRefEntry lastEntry = (COSWriterXRefEntry)getXRefEntries().get( getXRefEntries().size()-1);
+ trailer.setInt(COSName.getPDFName("Size"), (int)lastEntry.getKey().getNumber()+1);
+ trailer.removeItem( COSName.PREV );
+ /**
+ COSObject catalog = doc.getCatalog();
+ if (catalog != null)
+ {
+ trailer.setItem(COSName.getPDFName("Root"), catalog);
+ }
+ */
+ trailer.accept(this);
+
+ getStandardOutput().write(STARTXREF);
+ getStandardOutput().writeEOL();
+ getStandardOutput().write(String.valueOf(getStartxref()).getBytes());
+ getStandardOutput().writeEOL();
+ getStandardOutput().write(EOF);
+ }
+
+ /**
+ * write the x ref section for the pdf file
+ *
+ * currently, the pdf is reconstructed from the scratch, so we write a single section
+ *
+ * todo support for incremental writing?
+ *
+ * @param doc The document to write the xref from.
+ *
+ * @throws IOException If there is an error writing the data to the stream.
+ */
+ protected void doWriteXRef(COSDocument doc) throws IOException
+ {
+ String offset;
+ String generation;
+
+ // sort xref, needed only if object keys not regenerated
+ Collections.sort(getXRefEntries());
+ COSWriterXRefEntry lastEntry = (COSWriterXRefEntry)getXRefEntries().get( getXRefEntries().size()-1 );
+
+ // remember the position where x ref is written
+ setStartxref(getStandardOutput().getPos());
+ //
+ getStandardOutput().write(XREF);
+ getStandardOutput().writeEOL();
+ // write start object number and object count for this x ref section
+ // we assume starting from scratch
+ getStandardOutput().write(String.valueOf(0).getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(String.valueOf(lastEntry.getKey().getNumber() + 1).getBytes());
+ getStandardOutput().writeEOL();
+ // write initial start object with ref to first deleted object and magic generation number
+ offset = formatXrefOffset.format(0);
+ generation = formatXrefGeneration.format(65535);
+ getStandardOutput().write(offset.getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(generation.getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(XREF_FREE);
+ getStandardOutput().writeCRLF();
+ // write entry for every object
+ long lastObjectNumber = 0;
+ for (Iterator i = getXRefEntries().iterator(); i.hasNext();)
+ {
+ COSWriterXRefEntry entry = (COSWriterXRefEntry) i.next();
+ while( lastObjectNumber<entry.getKey().getNumber()-1 )
+ {
+ offset = formatXrefOffset.format(0);
+ generation = formatXrefGeneration.format(65535);
+ getStandardOutput().write(offset.getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(generation.getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(XREF_FREE);
+ getStandardOutput().writeCRLF();
+ lastObjectNumber++;
+ }
+ lastObjectNumber = entry.getKey().getNumber();
+ offset = formatXrefOffset.format(entry.getOffset());
+ generation = formatXrefGeneration.format(entry.getKey().getGeneration());
+ getStandardOutput().write(offset.getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(generation.getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(entry.isFree() ? XREF_FREE : XREF_USED);
+ getStandardOutput().writeCRLF();
+ }
+ }
+
+ /**
+ * This will get the object key for the object.
+ *
+ * @param obj The object to get the key for.
+ *
+ * @return The object key for the object.
+ */
+ private COSObjectKey getObjectKey( COSBase obj )
+ {
+ COSBase actual = obj;
+ if( actual instanceof COSObject )
+ {
+ actual = ((COSObject)obj).getObject();
+ }
+ COSObjectKey key = null;
+ if( actual != null )
+ {
+ key = (COSObjectKey)objectKeys.get(actual);
+ }
+ if( key == null )
+ {
+ key = (COSObjectKey)objectKeys.get(obj);
+ }
+ if (key == null)
+ {
+ setNumber(getNumber()+1);
+ key = new COSObjectKey(getNumber(),0);
+ objectKeys.put(obj, key);
+ if( actual != null )
+ {
+ objectKeys.put(actual, key);
+ }
+ }
+ return key;
+ }
+
+ /**
+ * visitFromArray method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromArray( COSArray obj ) throws COSVisitorException
+ {
+ try
+ {
+ int count = 0;
+ getStandardOutput().write(ARRAY_OPEN);
+ for (Iterator i = obj.iterator(); i.hasNext();)
+ {
+ COSBase current = (COSBase) i.next();
+ if( current instanceof COSDictionary )
+ {
+ addObjectToWrite( current );
+ writeReference( current );
+ }
+ else if( current instanceof COSObject )
+ {
+ COSBase subValue = ((COSObject)current).getObject();
+ if( subValue instanceof COSDictionary || subValue == null )
+ {
+ addObjectToWrite( current );
+ writeReference( current );
+ }
+ else
+ {
+ subValue.accept( this );
+ }
+ }
+ else if( current == null )
+ {
+ COSNull.NULL.accept( this );
+ }
+ else
+ {
+ current.accept(this);
+ }
+ count++;
+ if (i.hasNext())
+ {
+ if (count % 10 == 0)
+ {
+ getStandardOutput().writeEOL();
+ }
+ else
+ {
+ getStandardOutput().write(SPACE);
+ }
+ }
+ }
+ getStandardOutput().write(ARRAY_CLOSE);
+ getStandardOutput().writeEOL();
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromBoolean method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromBoolean(COSBoolean obj) throws COSVisitorException
+ {
+
+ try
+ {
+ obj.writePDF( getStandardOutput() );
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromDictionary method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromDictionary(COSDictionary obj) throws COSVisitorException
+ {
+ try
+ {
+ getStandardOutput().write(DICT_OPEN);
+ getStandardOutput().writeEOL();
+ for (Iterator i = obj.keyList().iterator(); i.hasNext();)
+ {
+ COSName name = (COSName) i.next();
+ COSBase value = obj.getItem(name);
+ if (value != null)
+ {
+ // this is purely defensive, if entry is set to null instead of removed
+ if( value != null )
+ {
+ name.accept(this);
+ getStandardOutput().write(SPACE);
+ }
+
+
+ if( value == null )
+ {
+ log.debug( "Value is null" );
+ //then we won't write anything, there are a couple cases
+ //were the value of an entry in the COSDictionary will
+ //be a dangling reference that points to nothing
+ //so we will just not write out the entry if that is the case
+ }
+ else if( value instanceof COSDictionary )
+ {
+ addObjectToWrite( value );
+ writeReference( value );
+ }
+ else if( value instanceof COSObject )
+ {
+ COSBase subValue = ((COSObject)value).getObject();
+ if( subValue instanceof COSDictionary || subValue == null )
+ {
+ addObjectToWrite( value );
+ writeReference( value );
+ }
+ else
+ {
+ subValue.accept( this );
+ }
+ }
+ else
+ {
+ value.accept(this);
+ }
+ getStandardOutput().writeEOL();
+
+ }
+ }
+ getStandardOutput().write(DICT_CLOSE);
+ getStandardOutput().writeEOL();
+ return null;
+ }
+ catch( IOException e )
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * The visit from document method.
+ *
+ * @param doc The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromDocument(COSDocument doc) throws COSVisitorException
+ {
+ try
+ {
+ doWriteHeader(doc);
+ doWriteBody(doc);
+ doWriteXRef(doc);
+ doWriteTrailer(doc);
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromFloat method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromFloat(COSFloat obj) throws COSVisitorException
+ {
+
+ try
+ {
+ obj.writePDF( getStandardOutput() );
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromFloat method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromInt(COSInteger obj) throws COSVisitorException
+ {
+ try
+ {
+ obj.writePDF( getStandardOutput() );
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromName method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromName(COSName obj) throws COSVisitorException
+ {
+ try
+ {
+ obj.writePDF( getStandardOutput() );
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromNull method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromNull(COSNull obj) throws COSVisitorException
+ {
+ try
+ {
+ obj.writePDF( getStandardOutput() );
+ return null;
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromObjRef method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ */
+ public void writeReference(COSBase obj) throws COSVisitorException
+ {
+ try
+ {
+ COSObjectKey key = getObjectKey(obj);
+ getStandardOutput().write(String.valueOf(key.getNumber()).getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(String.valueOf(key.getGeneration()).getBytes());
+ getStandardOutput().write(SPACE);
+ getStandardOutput().write(REFERENCE);
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromStream method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ *
+ * @return null
+ */
+ public Object visitFromStream(COSStream obj) throws COSVisitorException
+ {
+ try
+ {
+ if( document.willEncryptWhenSaving() )
+ {
+ enc.decryptStream( obj, currentObjectKey.getNumber(), currentObjectKey.getGeneration() );
+ }
+
+ InputStream input = obj.getFilteredStream();
+ // set the length of the stream and write stream dictionary
+ COSObject lengthObject = new COSObject( null );
+
+ obj.setItem(COSName.LENGTH, lengthObject);
+ //obj.accept(this);
+ // write the stream content
+ visitFromDictionary( obj );
+ getStandardOutput().write(STREAM);
+ getStandardOutput().writeCRLF();
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ int totalAmountWritten = 0;
+ while( (amountRead = input.read(buffer,0,1024)) != -1 )
+ {
+ getStandardOutput().write( buffer, 0, amountRead );
+ totalAmountWritten += amountRead;
+ }
+ lengthObject.setObject( new COSInteger( totalAmountWritten ) );
+ getStandardOutput().writeCRLF();
+ getStandardOutput().write(ENDSTREAM);
+ getStandardOutput().writeEOL();
+ return null;
+ }
+ catch( Exception e )
+ {
+ throw new COSVisitorException(e);
+ }
+ }
+
+ /**
+ * visitFromString method comment.
+ *
+ * @param obj The object that is being visited.
+ *
+ * @return null
+ *
+ * @throws COSVisitorException If there is an exception while visiting this object.
+ */
+ public Object visitFromString(COSString obj) throws COSVisitorException
+ {
+ try
+ {
+ if( document.willEncryptWhenSaving() )
+ {
+ try
+ {
+ enc.decryptString( obj, currentObjectKey.getNumber(), currentObjectKey.getGeneration() );
+ }
+ catch( Exception e )
+ {
+ throw new COSVisitorException( e );
+ }
+ }
+ obj.writePDF( getStandardOutput() );
+ }
+ catch (IOException e)
+ {
+ throw new COSVisitorException(e);
+ }
+ return null;
+ }
+
+ /**
+ * This will write the pdf document.
+ *
+ * @param doc The document to write.
+ *
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void write(COSDocument doc) throws COSVisitorException
+ {
+ PDDocument pdDoc = new PDDocument( doc );
+ write( pdDoc );
+ }
+
+ /**
+ * This will write the pdf document.
+ *
+ * @param doc The document to write.
+ *
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void write(PDDocument doc) throws COSVisitorException
+ {
+ document = doc;
+ if( document.willEncryptWhenSaving() )
+ {
+ try
+ {
+ enc = new DocumentEncryption(document);
+ enc.initForEncryption();
+ }
+ catch( CryptographyException e )
+ {
+ throw new COSVisitorException( e );
+ }
+ catch( IOException e )
+ {
+ throw new COSVisitorException( e );
+ }
+ }
+ COSDocument cosDoc = document.getDocument();
+ COSDictionary trailer = cosDoc.getTrailer();
+ COSArray idArray = (COSArray)trailer.getDictionaryObject( "ID" );
+ if( idArray == null )
+ {
+ try
+ {
+
+ //algothim says to use time/path/size/values in doc to generate
+ //the id. We don't have path or size, so do the best we can
+ MessageDigest md = MessageDigest.getInstance( "MD5" );
+ md.update( Long.toString( System.currentTimeMillis()).getBytes() );
+ COSDictionary info = (COSDictionary)trailer.getDictionaryObject( "Info" );
+ if( info != null )
+ {
+ Iterator values = info.getValues().iterator();
+ while( values.hasNext() )
+ {
+ md.update( values.next().toString().getBytes() );
+ }
+ }
+ idArray = new COSArray();
+ COSString id = new COSString( md.digest() );
+ idArray.add( id );
+ idArray.add( id );
+ trailer.setItem( "ID", idArray );
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ throw new COSVisitorException( e );
+ }
+ }
+
+ /*
+ List objects = doc.getObjects();
+ Iterator iter = objects.iterator();
+ long maxNumber = 0;
+ while( iter.hasNext() )
+ {
+ COSObject object = (COSObject)iter.next();
+ if( object.getObjectNumber() != null &&
+ object.getGenerationNumber() != null )
+ {
+ COSObjectKey key = new COSObjectKey( object.getObjectNumber().longValue(),
+ object.getGenerationNumber().longValue() );
+ objectKeys.put( object.getObject(), key );
+ objectKeys.put( object, key );
+ maxNumber = Math.max( key.getNumber(), maxNumber );
+ setNumber( maxNumber );
+ }
+ }*/
+ cosDoc.accept(this);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfwriter/COSWriterXRefEntry.java b/src/main/java/org/pdfbox/pdfwriter/COSWriterXRefEntry.java
new file mode 100644
index 0000000..94ac2c0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfwriter/COSWriterXRefEntry.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfwriter;
+
+import org.pdfbox.persistence.util.COSObjectKey;
+
+import org.pdfbox.cos.COSBase;
+
+/**
+ * this is en entry in the xref section of the physical pdf document
+ * generated by the COSWriter.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.6 $
+ */
+public class COSWriterXRefEntry implements Comparable
+{
+ private long offset;
+ private COSBase object;
+ private COSObjectKey key;
+ private boolean free = false;
+
+
+
+ /**
+ * @see Comparable#compareTo( Object )
+ */
+ public int compareTo(Object obj)
+ {
+ if (obj instanceof COSWriterXRefEntry)
+ {
+ return (int)(getKey().getNumber() - ((COSWriterXRefEntry)obj).getKey().getNumber());
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ /**
+ * This will get the Object key.
+ *
+ * @return The object key.
+ */
+ public COSObjectKey getKey()
+ {
+ return key;
+ }
+
+
+
+ /**
+ * This will get the offset into the document.
+ *
+ * @return The offset into the document.
+ */
+ public long getOffset()
+ {
+ return offset;
+ }
+
+ /**
+ * Gets the xref 'free' attribute.
+ *
+ * @return The free attribute.
+ */
+ public boolean isFree()
+ {
+ return free;
+ }
+
+ /**
+ * This will set the free attribute.
+ *
+ * @param newFree The newly freed attribute.
+ */
+ public void setFree(boolean newFree)
+ {
+ free = newFree;
+ }
+
+ /**
+ * This will set the object key.
+ *
+ * @param newKey The new object key.
+ */
+ private void setKey(COSObjectKey newKey)
+ {
+ key = newKey;
+ }
+
+
+
+ /**
+ * The offset attribute.
+ *
+ * @param newOffset The new value for the offset.
+ */
+ public void setOffset(long newOffset)
+ {
+ offset = newOffset;
+ }
+
+ /**
+ * COSWriterXRefEntry constructor comment.
+ *
+ * @param start The start attribute.
+ * @param obj The COS object that this entry represents.
+ * @param keyValue The key to the COS object.
+ */
+ public COSWriterXRefEntry(long start, COSBase obj, COSObjectKey keyValue)
+ {
+ super();
+ setOffset(start);
+ setObject(obj);
+ setKey(keyValue);
+ }
+
+ /**
+ * This will get the object.
+ *
+ * @return The object.
+ */
+ public COSBase getObject()
+ {
+ return object;
+ }
+
+ /**
+ * This will set the object for this xref.
+ *
+ * @param newObject The object that is being set.
+ */
+ private void setObject(COSBase newObject)
+ {
+ object = newObject;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfwriter/ContentStreamWriter.java b/src/main/java/org/pdfbox/pdfwriter/ContentStreamWriter.java
new file mode 100644
index 0000000..97b41b1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfwriter/ContentStreamWriter.java
@@ -0,0 +1,200 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfwriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.util.ImageParameters;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * A class that will take a list of tokens and write out a stream with them.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.7 $
+ */
+public class ContentStreamWriter
+{
+ private OutputStream output;
+ /**
+ * space character.
+ */
+ public static final byte[] SPACE = new byte[] { 32 };
+
+ /**
+ * standard line separator on this platform.
+ */
+ public static final byte[] EOL = System.getProperty("line.separator").getBytes();
+
+ /**
+ * This will create a new content stream writer.
+ *
+ * @param out The stream to write the data to.
+ */
+ public ContentStreamWriter( OutputStream out )
+ {
+ output = out;
+ }
+
+ /**
+ * This will write out the list of tokens to the stream.
+ *
+ * @param tokens The tokens to write to the stream.
+ * @param start The start index into the list of tokens.
+ * @param end The end index into the list of tokens.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writeTokens( List tokens, int start, int end ) throws IOException
+ {
+ for( int i=start; i<end; i++ )
+ {
+ Object o = tokens.get( i );
+ writeObject( o );
+ //write a space between each object.
+ output.write( 32 );
+ }
+ output.flush();
+ }
+
+ private void writeObject( Object o ) throws IOException
+ {
+ if( o instanceof COSString )
+ {
+ ((COSString)o).writePDF( output );
+ }
+ else if( o instanceof COSFloat )
+ {
+ ((COSFloat)o).writePDF( output );
+ }
+ else if( o instanceof COSInteger )
+ {
+ ((COSInteger)o).writePDF( output );
+ }
+ else if( o instanceof COSBoolean )
+ {
+ ((COSBoolean)o).writePDF( output );
+ }
+ else if( o instanceof COSName )
+ {
+ ((COSName)o).writePDF( output );
+ }
+ else if( o instanceof COSArray )
+ {
+ COSArray array = (COSArray)o;
+ output.write(COSWriter.ARRAY_OPEN);
+ for( int i=0; i<array.size(); i++ )
+ {
+ writeObject( array.get( i ) );
+ output.write( SPACE );
+ }
+
+ output.write( COSWriter.ARRAY_CLOSE );
+ }
+ else if( o instanceof COSDictionary )
+ {
+ COSDictionary obj = (COSDictionary)o;
+ output.write( COSWriter.DICT_OPEN );
+ for (Iterator i = obj.keyList().iterator(); i.hasNext();)
+ {
+ COSName name = (COSName) i.next();
+ COSBase value = obj.getItem(name);
+ if (value != null)
+ {
+ writeObject( name );
+ output.write( SPACE );
+
+ writeObject( value );
+
+ output.write( SPACE );
+
+ }
+ }
+ output.write( COSWriter.DICT_CLOSE );
+ output.write( SPACE );
+ }
+ else if( o instanceof PDFOperator )
+ {
+ PDFOperator op = (PDFOperator)o;
+ if( op.getOperation().equals( "BI" ) )
+ {
+ output.write( "BI".getBytes() );
+ ImageParameters params = op.getImageParameters();
+ COSDictionary dic = params.getDictionary();
+ Iterator iter = dic.keyList().iterator();
+ while( iter.hasNext() )
+ {
+ COSName key = (COSName)iter.next();
+ Object value = dic.getDictionaryObject( key );
+ key.writePDF( output );
+ output.write( SPACE );
+ writeObject( value );
+ output.write( EOL );
+ }
+ output.write( "ID".getBytes() );
+ output.write( EOL );
+ output.write( op.getImageData() );
+ }
+ else
+ {
+ output.write( op.getOperation().getBytes() );
+ output.write( EOL );
+ }
+ }
+ else
+ {
+ throw new IOException( "Error:Unknown type in content stream:" + o );
+ }
+ }
+
+ /**
+ * This will write out the list of tokens to the stream.
+ *
+ * @param tokens The tokens to write to the stream.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void writeTokens( List tokens ) throws IOException
+ {
+ writeTokens( tokens, 0, tokens.size() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdfwriter/package.html b/src/main/java/org/pdfbox/pdfwriter/package.html
new file mode 100644
index 0000000..f62d897
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfwriter/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This is the persistence layer used to write the PDFBox documents to a stream.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/PDDestinationNameTreeNode.java b/src/main/java/org/pdfbox/pdmodel/PDDestinationNameTreeNode.java
new file mode 100644
index 0000000..6c8956c
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDDestinationNameTreeNode.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.PDNameTreeNode;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
+
+/**
+ * This class holds all of the name trees that are available at the document level.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class PDDestinationNameTreeNode extends PDNameTreeNode
+{
+ /**
+ * Constructor.
+ */
+ public PDDestinationNameTreeNode()
+ {
+ super( PDPageDestination.class );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dic The COS dictionary.
+ */
+ public PDDestinationNameTreeNode( COSDictionary dic )
+ {
+ super( dic, PDPageDestination.class );
+ }
+
+ /**
+ * @see PDNameTreeNode#convertCOSToPD( COSBase )
+ */
+ protected Object convertCOSToPD( COSBase base ) throws IOException
+ {
+ COSBase destination = base;
+ if( base instanceof COSDictionary )
+ {
+ //the destination is sometimes stored in the D dictionary
+ //entry instead of being directly an array, so just dereference
+ //it for now
+ destination = ((COSDictionary)base).getDictionaryObject( "D" );
+ }
+ return PDDestination.create( destination );
+ }
+
+ /**
+ * @see PDNameTreeNode#createChildNode( COSDictionary )
+ */
+ protected PDNameTreeNode createChildNode( COSDictionary dic )
+ {
+ return new PDDestinationNameTreeNode(dic);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDDocument.java b/src/main/java/org/pdfbox/pdmodel/PDDocument.java
new file mode 100644
index 0000000..94150fb
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDDocument.java
@@ -0,0 +1,725 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import java.awt.print.PageFormat;
+import java.awt.print.Pageable;
+import java.awt.print.Paper;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterIOException;
+import java.awt.print.PrinterJob;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.encryption.PDFEncryption;
+import org.pdfbox.encryption.DocumentEncryption;
+
+import org.pdfbox.exceptions.COSVisitorException;
+import org.pdfbox.exceptions.CryptographyException;
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.pdmodel.encryption.PDEncryptionDictionary;
+import org.pdfbox.pdmodel.encryption.PDEncryptionManager;
+import org.pdfbox.pdmodel.encryption.PDStandardEncryption;
+
+/**
+ * This is the in-memory representation of the PDF document. You need to call
+ * close() on this object when you are done using it!!
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.35 $
+ */
+public class PDDocument implements Pageable
+{
+ private COSDocument document;
+ private boolean encryptOnSave = false;
+ private String encryptUserPassword = null;
+ private String encryptOwnerPassword = null;
+
+ //cached values
+ private PDDocumentInformation documentInformation;
+ private PDDocumentCatalog documentCatalog;
+
+ //The encParameters will be cached here. When the document is decrypted then
+ //the COSDocument will not have an "Encrypt" dictionary anymore and this object
+ //must be used.
+ private PDEncryptionDictionary encParameters = null;
+ /**
+ * This will tell if the document was decrypted with the master password.
+ */
+ private boolean decryptedWithOwnerPassword = false;
+
+ /**
+ * Constructor, creates a new PDF Document with no pages. You need to add
+ * at least one page for the document to be valid.
+ *
+ * @throws IOException If there is an error creating this document.
+ */
+ public PDDocument() throws IOException
+ {
+ document = new COSDocument();
+
+ //First we need a trailer
+ COSDictionary trailer = new COSDictionary();
+ document.setTrailer( trailer );
+
+ //Next we need the root dictionary.
+ COSDictionary rootDictionary = new COSDictionary();
+ trailer.setItem( COSName.ROOT, rootDictionary );
+ rootDictionary.setItem( COSName.TYPE, COSName.CATALOG );
+ rootDictionary.setItem( COSName.VERSION, COSName.getPDFName( "1.4" ) );
+
+ //next we need the pages tree structure
+ COSDictionary pages = new COSDictionary();
+ rootDictionary.setItem( COSName.PAGES, pages );
+ pages.setItem( COSName.TYPE, COSName.PAGES );
+ COSArray kidsArray = new COSArray();
+ pages.setItem( COSName.KIDS, kidsArray );
+ pages.setItem( COSName.COUNT, new COSInteger( 0 ) );
+ }
+
+ /**
+ * This will add a page to the document. This is a convenience method, that
+ * will add the page to the root of the hierarchy and set the parent of the
+ * page to the root.
+ *
+ * @param page The page to add to the document.
+ */
+ public void addPage( PDPage page )
+ {
+ PDPageNode rootPages = getDocumentCatalog().getPages();
+ rootPages.getKids().add( page );
+ page.setParent( rootPages );
+ rootPages.updateCount();
+ }
+
+ /**
+ * Remove the page from the document.
+ *
+ * @param page The page to remove from the document.
+ *
+ * @return true if the page was found false otherwise.
+ */
+ public boolean removePage( PDPage page )
+ {
+ PDPageNode parent = page.getParent();
+ boolean retval = parent.getKids().remove( page );
+ if( retval )
+ {
+ //do a recursive updateCount starting at the root
+ //of the document
+ getDocumentCatalog().getPages().updateCount();
+ }
+ return retval;
+ }
+
+ /**
+ * Remove the page from the document.
+ *
+ * @param pageNumber 0 based index to page number.
+ * @return true if the page was found false otherwise.
+ */
+ public boolean removePage( int pageNumber )
+ {
+ boolean removed = false;
+ List allPages = getDocumentCatalog().getAllPages();
+ if( allPages.size() > pageNumber)
+ {
+ PDPage page = (PDPage)allPages.get( pageNumber );
+ removed = removePage( page );
+ }
+ return removed;
+ }
+
+ /**
+ * This will import and copy the contents from another location. Currently
+ * the content stream is stored in a scratch file. The scratch file is
+ * associated with the document. If you are adding a page to this document
+ * from another document and want to copy the contents to this document's
+ * scratch file then use this method otherwise just use the addPage method.
+ *
+ * @param page The page to import.
+ * @return The page that was imported.
+ *
+ * @throws IOException If there is an error copying the page.
+ */
+ public PDPage importPage( PDPage page ) throws IOException
+ {
+ PDPage importedPage = new PDPage( new COSDictionary( page.getCOSDictionary() ) );
+ InputStream is = null;
+ OutputStream os = null;
+ try
+ {
+ PDStream src = page.getContents();
+ PDStream dest = new PDStream( new COSStream( src.getStream(), document.getScratchFile() ) );
+ importedPage.setContents( dest );
+ os = dest.createOutputStream();
+
+ byte[] buf = new byte[10240];
+ int amountRead = 0;
+ is = src.createInputStream();
+ while((amountRead = is.read(buf,0,10240)) > -1)
+ {
+ os.write(buf, 0, amountRead);
+ }
+ addPage( importedPage );
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ if( os != null )
+ {
+ os.close();
+ }
+ }
+ return importedPage;
+
+ }
+
+ /**
+ * Constructor that uses an existing document. The COSDocument that
+ * is passed in must be valid.
+ *
+ * @param doc The COSDocument that this document wraps.
+ */
+ public PDDocument( COSDocument doc )
+ {
+ document = doc;
+ }
+
+ /**
+ * This will get the low level document.
+ *
+ * @return The document that this layer sits on top of.
+ */
+ public COSDocument getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * This will get the document info dictionary. This is guaranteed to not return null.
+ *
+ * @return The documents /Info dictionary
+ */
+ public PDDocumentInformation getDocumentInformation()
+ {
+ if( documentInformation == null )
+ {
+ COSDictionary trailer = document.getTrailer();
+ COSDictionary infoDic = (COSDictionary)trailer.getDictionaryObject( COSName.INFO );
+ if( infoDic == null )
+ {
+ infoDic = new COSDictionary();
+ trailer.setItem( COSName.INFO, infoDic );
+ }
+ documentInformation = new PDDocumentInformation( infoDic );
+ }
+ return documentInformation;
+ }
+
+ /**
+ * This will set the document information for this document.
+ *
+ * @param info The updated document information.
+ */
+ public void setDocumentInformation( PDDocumentInformation info )
+ {
+ documentInformation = info;
+ document.getTrailer().setItem( COSName.INFO, info.getDictionary() );
+ }
+
+ /**
+ * This will get the document CATALOG. This is guaranteed to not return null.
+ *
+ * @return The documents /Root dictionary
+ */
+ public PDDocumentCatalog getDocumentCatalog()
+ {
+ if( documentCatalog == null )
+ {
+ COSDictionary trailer = document.getTrailer();
+ COSDictionary infoDic = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
+ if( infoDic == null )
+ {
+ documentCatalog = new PDDocumentCatalog( this );
+ }
+ else
+ {
+ documentCatalog = new PDDocumentCatalog( this, infoDic );
+ }
+
+ }
+ return documentCatalog;
+ }
+
+ /**
+ * This will tell if this document is encrypted or not.
+ *
+ * @return true If this document is encrypted.
+ */
+ public boolean isEncrypted()
+ {
+ return document.isEncrypted();
+ }
+
+ /**
+ * This will get the encryption dictionary for this document. This will still
+ * return the parameters if the document was decrypted. If the document was
+ * never encrypted then this will return null. As the encryption architecture
+ * in PDF documents is plugable this returns an abstract class, but the only
+ * supported subclass at this time is a PDStandardEncryption object.
+ *
+ * @return The encryption dictionary(most likely a PDStandardEncryption object)
+ *
+ * @throws IOException If there is an error determining which security handler to use.
+ */
+ public PDEncryptionDictionary getEncryptionDictionary() throws IOException
+ {
+ if( encParameters == null )
+ {
+ encParameters = PDEncryptionManager.getEncryptionDictionary( document.getEncryptionDictionary() );
+ }
+ return encParameters;
+ }
+
+ /**
+ * This will set the encryption dictionary for this document.
+ *
+ * @param encDictionary The encryption dictionary(most likely a PDStandardEncryption object)
+ *
+ * @throws IOException If there is an error determining which security handler to use.
+ */
+ public void setEncryptionDictionary( PDEncryptionDictionary encDictionary ) throws IOException
+ {
+ encParameters = encDictionary;
+ }
+
+ /**
+ * This will determine if this is the user password. This only applies when
+ * the document is encrypted and uses standard encryption.
+ *
+ * @param password The plain text user password.
+ *
+ * @return true If the password passed in matches the user password used to encrypt the document.
+ *
+ * @throws IOException If there is an error determining if it is the user password.
+ * @throws CryptographyException If there is an error in the encryption algorithms.
+ */
+ public boolean isUserPassword( String password ) throws IOException, CryptographyException
+ {
+ boolean retval = false;
+ if( password == null )
+ {
+ password = "";
+ }
+ PDFEncryption encryptor = new PDFEncryption();
+ PDEncryptionDictionary encryptionDictionary = getEncryptionDictionary();
+ if( encryptionDictionary == null )
+ {
+ throw new IOException( "Error: Document is not encrypted" );
+ }
+ else
+ {
+ if( encryptionDictionary instanceof PDStandardEncryption )
+ {
+ COSString documentID = (COSString)document.getDocumentID().get(0);
+ PDStandardEncryption standard = (PDStandardEncryption)encryptionDictionary;
+ retval = encryptor.isUserPassword(
+ password.getBytes(),
+ standard.getUserKey(),
+ standard.getOwnerKey(),
+ standard.getPermissions(),
+ documentID.getBytes(),
+ standard.getRevision(),
+ standard.getLength()/8 );
+ }
+ else
+ {
+ throw new IOException( "Error: Encyption dictionary is not 'Standard'" +
+ encryptionDictionary.getClass().getName() );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will determine if this is the owner password. This only applies when
+ * the document is encrypted and uses standard encryption.
+ *
+ * @param password The plain text owner password.
+ *
+ * @return true If the password passed in matches the owner password used to encrypt the document.
+ *
+ * @throws IOException If there is an error determining if it is the user password.
+ * @throws CryptographyException If there is an error in the encryption algorithms.
+ */
+ public boolean isOwnerPassword( String password ) throws IOException, CryptographyException
+ {
+ boolean retval = false;
+ if( password == null )
+ {
+ password = "";
+ }
+ PDFEncryption encryptor = new PDFEncryption();
+ PDEncryptionDictionary encryptionDictionary = getEncryptionDictionary();
+ if( encryptionDictionary == null )
+ {
+ throw new IOException( "Error: Document is not encrypted" );
+ }
+ else
+ {
+ if( encryptionDictionary instanceof PDStandardEncryption )
+ {
+ COSString documentID = (COSString)document.getDocumentID().get( 0 );
+ PDStandardEncryption standard = (PDStandardEncryption)encryptionDictionary;
+ retval = encryptor.isOwnerPassword(
+ password.getBytes(),
+ standard.getUserKey(),
+ standard.getOwnerKey(),
+ standard.getPermissions(),
+ documentID.getBytes(),
+ standard.getRevision(),
+ standard.getLength()/8 );
+ }
+ else
+ {
+ throw new IOException( "Error: Encyption dictionary is not 'Standard'" +
+ encryptionDictionary.getClass().getName() );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will decrypt a document.
+ *
+ * @param password Either the user or owner password.
+ *
+ * @throws CryptographyException If there is an error decrypting the document.
+ * @throws IOException If there is an error getting the stream data.
+ * @throws InvalidPasswordException If the password is not a user or owner password.
+ */
+ public void decrypt( String password ) throws CryptographyException, IOException, InvalidPasswordException
+ {
+ decryptedWithOwnerPassword = isOwnerPassword( password );
+ DocumentEncryption decryptor = new DocumentEncryption( this );
+ decryptor.decryptDocument( password );
+ document.dereferenceObjectStreams();
+ }
+
+ /**
+ * This will tell if the document was decrypted with the master password. This
+ * entry is invalid if the PDF was not decrypted.
+ *
+ * @return true if the pdf was decrypted with the master password.
+ */
+ public boolean wasDecryptedWithOwnerPassword()
+ {
+ return decryptedWithOwnerPassword;
+ }
+
+ /**
+ * This will <b>mark</b> a document to be encrypted. The actual encryption
+ * will occur when the document is saved.
+ *
+ * @param ownerPassword The owner password to encrypt the document.
+ * @param userPassword The user password to encrypt the document.
+ *
+ * @throws CryptographyException If an error occurs during encryption.
+ * @throws IOException If there is an error accessing the data.
+ */
+ public void encrypt( String ownerPassword, String userPassword )
+ throws CryptographyException, IOException
+ {
+ encryptOnSave = true;
+ encryptOwnerPassword = ownerPassword;
+ encryptUserPassword = userPassword;
+ }
+
+
+ /**
+ * The owner password that was passed into the encrypt method. You should
+ * never use this method. This will not longer be valid once encryption
+ * has occured.
+ *
+ * @return The owner password passed to the encrypt method.
+ */
+ public String getOwnerPasswordForEncryption()
+ {
+ return encryptOwnerPassword;
+ }
+
+ /**
+ * The user password that was passed into the encrypt method. You should
+ * never use this method. This will not longer be valid once encryption
+ * has occured.
+ *
+ * @return The user password passed to the encrypt method.
+ */
+ public String getUserPasswordForEncryption()
+ {
+ return encryptUserPassword;
+ }
+
+ /**
+ * Internal method do determine if the document will be encrypted when it is saved.
+ *
+ * @return True if encrypt has been called and the document
+ * has not been saved yet.
+ */
+ public boolean willEncryptWhenSaving()
+ {
+ return encryptOnSave;
+ }
+
+ /**
+ * This shoule only be called by the COSWriter after encryption has completed.
+ *
+ */
+ public void clearWillEncryptWhenSaving()
+ {
+ encryptOnSave = false;
+ }
+
+ /**
+ * This will load a document from a file.
+ *
+ * @param filename The name of the file to load.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static PDDocument load( String filename ) throws IOException
+ {
+ return load( new BufferedInputStream( new FileInputStream( filename ) ) );
+ }
+
+ /**
+ * This will load a document from a file.
+ *
+ * @param file The name of the file to load.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static PDDocument load( File file ) throws IOException
+ {
+ return load( new BufferedInputStream( new FileInputStream( file ) ) );
+ }
+
+ /**
+ * This will load a document from an input stream.
+ *
+ * @param input The stream that contains the document.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static PDDocument load( InputStream input ) throws IOException
+ {
+ PDFParser parser = new PDFParser( input );
+ parser.parse();
+ return parser.getPDDocument();
+ }
+
+ /**
+ * This will save this document to the filesystem.
+ *
+ * @param fileName The file to save as.
+ *
+ * @throws IOException If there is an error saving the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void save( String fileName ) throws IOException, COSVisitorException
+ {
+ save( new FileOutputStream( fileName ) );
+ }
+
+ /**
+ * This will save the document to an output stream.
+ *
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void save( OutputStream output ) throws IOException, COSVisitorException
+ {
+ //update the count in case any pages have been added behind the scenes.
+ getDocumentCatalog().getPages().updateCount();
+ COSWriter writer = null;
+ try
+ {
+ writer = new COSWriter( output );
+ writer.write( this );
+ writer.close();
+ }
+ finally
+ {
+ if( writer != null )
+ {
+ writer.close();
+ }
+ }
+
+ }
+
+ /**
+ * This will return the total page count of the PDF document. Note: This method
+ * is deprecated in favor of the getNumberOfPages method. The getNumberOfPages is
+ * a required interface method of the Pageable interface. This method will
+ * be removed in a future version of PDFBox!!
+ *
+ * @return The total number of pages in the PDF document.
+ * @deprecated Use the getNumberOfPages method instead!
+ */
+ public int getPageCount()
+ {
+ return getNumberOfPages();
+ }
+
+ /**
+ * @see Pageable#getNumberOfPages()
+ */
+ public int getNumberOfPages()
+ {
+ PDDocumentCatalog cat = getDocumentCatalog();
+ return (int)cat.getPages().getCount();
+ }
+
+ /**
+ * @see Pageable#getPageFormat(int)
+ */
+ public PageFormat getPageFormat(int pageIndex)
+ {
+ PDPage page = (PDPage)getDocumentCatalog().getAllPages().get( pageIndex );
+ PDRectangle mediaBox = page.findMediaBox();
+ PageFormat format = new PageFormat();
+ Paper paper = new Paper();
+ //hmm the imageable area might need to be the CropBox instead
+ //of the media box???
+ paper.setImageableArea( 0,0,mediaBox.getWidth(),mediaBox.getHeight());
+ paper.setSize( mediaBox.getWidth(), mediaBox.getHeight() );
+ format.setPaper( paper );
+ return format;
+ }
+
+ /**
+ * @see Pageable#getPrintable(int)
+ */
+ public Printable getPrintable(int pageIndex)
+ {
+ return (Printable)getDocumentCatalog().getAllPages().get( pageIndex );
+ }
+
+ /**
+ * This will send the PDF document to a printer. The printing functionality
+ * depends on the org.pdfbox.pdfviewer.PageDrawer functionality. The PageDrawer
+ * is a work in progress and some PDFs will print correctly and some will
+ * not. This is a convenience method to create the java.awt.print.PrinterJob.
+ * The PDDocument implements the java.awt.print.Pageable interface and
+ * PDPage implementes the java.awt.print.Printable interface, so advanced printing
+ * capabilities can be done by using those interfaces instead of this method.
+ *
+ * @throws PrinterException If there is an error while sending the PDF to
+ * the printer, or you do not have permissions to print this document.
+ */
+ public void print() throws PrinterException
+ {
+ PDEncryptionDictionary encDictionary = null;
+ try
+ {
+ encDictionary = getEncryptionDictionary();
+ }
+ catch( IOException io )
+ {
+ throw new PrinterIOException( io );
+ }
+
+ //only care about standard encryption and if it was decrypted with the
+ //user password
+ if( encDictionary instanceof PDStandardEncryption &&
+ !wasDecryptedWithOwnerPassword() )
+ {
+ PDStandardEncryption stdEncryption = (PDStandardEncryption)encDictionary;
+ if( !stdEncryption.canPrint() )
+ {
+ throw new PrinterException( "You do not have permission to print this document." );
+ }
+ }
+
+ PrinterJob printJob = PrinterJob.getPrinterJob();
+ printJob.setPageable(this);
+ if( printJob.printDialog() )
+ {
+ printJob.print();
+ }
+ }
+
+ /**
+ * This will close the underlying COSDocument object.
+ *
+ * @throws IOException If there is an error releasing resources.
+ */
+ public void close() throws IOException
+ {
+ document.close();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDDocumentCatalog.java b/src/main/java/org/pdfbox/pdmodel/PDDocumentCatalog.java
new file mode 100644
index 0000000..552d404
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDDocumentCatalog.java
@@ -0,0 +1,378 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDMetadata;
+import org.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+import org.pdfbox.pdmodel.interactive.action.PDActionFactory;
+import org.pdfbox.pdmodel.interactive.action.PDDocumentCatalogAdditionalActions;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
+import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
+
+import org.pdfbox.pdmodel.interactive.pagenavigation.PDThread;
+import org.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences;
+
+/**
+ * This class represents the acroform of a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.16 $
+ */
+public class PDDocumentCatalog implements COSObjectable
+{
+ private COSDictionary root;
+ private PDDocument document;
+
+ private PDAcroForm acroForm = null;
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document that this catalog is part of.
+ */
+ public PDDocumentCatalog( PDDocument doc )
+ {
+ document = doc;
+ root = new COSDictionary();
+ root.setItem( COSName.TYPE, new COSString( "Catalog" ) );
+ document.getDocument().getTrailer().setItem( COSName.ROOT, root );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document that this catalog is part of.
+ * @param rootDictionary The root dictionary that this object wraps.
+ */
+ public PDDocumentCatalog( PDDocument doc, COSDictionary rootDictionary )
+ {
+ document = doc;
+ root = rootDictionary;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return root;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return root;
+ }
+
+ /**
+ * This will get the documents acroform. This will return null if
+ * no acroform is part of the document.
+ *
+ * @return The documents acroform.
+ */
+ public PDAcroForm getAcroForm()
+ {
+ if( acroForm == null )
+ {
+ COSDictionary acroFormDic =
+ (COSDictionary)root.getDictionaryObject( COSName.ACRO_FORM );
+ if( acroFormDic == null )
+ {
+ acroForm = new PDAcroForm( document );
+ root.setItem( COSName.ACRO_FORM, acroForm.getDictionary() );
+ }
+ else
+ {
+ acroForm = new PDAcroForm( document, acroFormDic );
+ }
+ }
+ return acroForm;
+ }
+
+ /**
+ * This will get the root node for the pages.
+ *
+ * @return The parent page node.
+ */
+ public PDPageNode getPages()
+ {
+ return new PDPageNode( (COSDictionary)root.getDictionaryObject( COSName.PAGES ) );
+ }
+
+ /**
+ * The PDF document contains a hierarchical structure of PDPageNode and PDPages, which
+ * is mostly just a way to store this information. This method will return a flat list
+ * of all PDPage objects in this document.
+ *
+ * @return A list of PDPage objects.
+ */
+ public List getAllPages()
+ {
+ List retval = new ArrayList();
+ PDPageNode rootNode = getPages();
+ //old (slower):
+ //getPageObjects( rootNode, retval );
+ rootNode.getAllKids(retval);
+ return retval;
+ }
+
+ /**
+ * Get the viewer preferences associated with this document or null if they
+ * do not exist.
+ *
+ * @return The document's viewer preferences.
+ */
+ public PDViewerPreferences getViewerPreferences()
+ {
+ PDViewerPreferences retval = null;
+ COSDictionary dict = (COSDictionary)root.getDictionaryObject( "ViewerPreferences" );
+ if( dict != null )
+ {
+ retval = new PDViewerPreferences( dict );
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the viewer preferences.
+ *
+ * @param prefs The new viewer preferences.
+ */
+ public void setViewerPreferences( PDViewerPreferences prefs )
+ {
+ root.setItem( "ViewerPreferences", prefs );
+ }
+
+ /**
+ * Get the outline associated with this document or null if it
+ * does not exist.
+ *
+ * @return The document's outline.
+ */
+ public PDDocumentOutline getDocumentOutline()
+ {
+ PDDocumentOutline retval = null;
+ COSDictionary dict = (COSDictionary)root.getDictionaryObject( "Outlines" );
+ if( dict != null )
+ {
+ retval = new PDDocumentOutline( dict );
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the document outlines.
+ *
+ * @param outlines The new document outlines.
+ */
+ public void setDocumentOutline( PDDocumentOutline outlines )
+ {
+ root.setItem( "Outlines", outlines );
+ }
+
+ /**
+ * Get the list threads for this pdf document.
+ *
+ * @return A list of PDThread objects.
+ */
+ public List getThreads()
+ {
+ COSArray array = (COSArray)root.getDictionaryObject( "Threads" );
+ if( array == null )
+ {
+ array = new COSArray();
+ root.setItem( "Threads", array );
+ }
+ List pdObjects = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ pdObjects.add( new PDThread( (COSDictionary)array.getObject( i ) ) );
+ }
+ return new COSArrayList( pdObjects, array );
+ }
+
+ /**
+ * Set the list of threads for this pdf document.
+ *
+ * @param threads The list of threads, or null to clear it.
+ */
+ public void setThreads( List threads )
+ {
+ root.setItem( "Threads", COSArrayList.converterToCOSArray( threads ) );
+ }
+
+ /**
+ * Get the metadata that is part of the document catalog. This will
+ * return null if there is no meta data for this object.
+ *
+ * @return The metadata for this object.
+ */
+ public PDMetadata getMetadata()
+ {
+ PDMetadata retval = null;
+ COSStream stream = (COSStream)root.getDictionaryObject( "Metadata" );
+ if( stream != null )
+ {
+ retval = new PDMetadata( stream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the metadata for this object. This can be null.
+ *
+ * @param meta The meta data for this object.
+ */
+ public void setMetadata( PDMetadata meta )
+ {
+ root.setItem( "Metadata", meta );
+ }
+
+ /**
+ * Set the Document Open Action for this object.
+ *
+ * @param action The action you want to perform.
+ */
+ public void setOpenAction( PDAction action )
+ {
+ root.setItem( COSName.getPDFName("OpenAction"), action );
+ }
+
+ /**
+ * Get the Document Open Action for this object.
+ *
+ * @return The action to perform when the document is opened.
+ */
+ public PDAction getOpenAction()
+ {
+ PDAction action = null;
+ COSDictionary actionDic = (COSDictionary) root.getDictionaryObject("OpenAction");
+
+ action = PDActionFactory.createAction(actionDic);
+
+ return action;
+ }
+ /**
+ * @return The Additional Actions for this Document
+ */
+ public PDDocumentCatalogAdditionalActions getActions()
+ {
+ COSDictionary addAct = (COSDictionary) root.getDictionaryObject("AA");
+ if (addAct == null)
+ {
+ addAct = new COSDictionary();
+ root.setItem("AA", addAct);
+ }
+ return new PDDocumentCatalogAdditionalActions(addAct);
+ }
+
+ /**
+ * Set the additional actions for the document.
+ *
+ * @param actions The actions that are associated with this document.
+ */
+ public void setActions( PDDocumentCatalogAdditionalActions actions )
+ {
+ root.setItem("AA", actions );
+ }
+
+ /**
+ * @return The names dictionary for this document or null if none exist.
+ */
+ public PDDocumentNameDictionary getNames()
+ {
+ PDDocumentNameDictionary nameDic = null;
+ COSDictionary names = (COSDictionary) root.getDictionaryObject("Names");
+ if(names != null)
+ {
+ nameDic = new PDDocumentNameDictionary(this,names);
+ }
+ return nameDic;
+ }
+
+ /**
+ * Set the names dictionary for the document.
+ *
+ * @param names The names dictionary that is associated with this document.
+ */
+ public void setNames( PDDocumentNameDictionary names )
+ {
+ root.setItem("Names", names );
+ }
+
+ /**
+ * Get info about doc's usage of tagged features. This will return
+ * null if there is no information.
+ *
+ * @return The new mark info.
+ */
+ public PDMarkInfo getMarkInfo()
+ {
+ PDMarkInfo retval = null;
+ COSDictionary dic = (COSDictionary)root.getDictionaryObject( "MarkInfo" );
+ if( dic != null )
+ {
+ retval = new PDMarkInfo( dic );
+ }
+ return retval;
+ }
+
+ /**
+ * Set information about the doc's usage of tagged features.
+ *
+ * @param markInfo The new MarkInfo data.
+ */
+ public void setMarkInfo( PDMarkInfo markInfo )
+ {
+ root.setItem( "MarkInfo", markInfo );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDDocumentInformation.java b/src/main/java/org/pdfbox/pdmodel/PDDocumentInformation.java
new file mode 100644
index 0000000..a30e90c
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDDocumentInformation.java
@@ -0,0 +1,297 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import java.io.IOException;
+
+import java.util.Calendar;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This is the document metadata. Each getXXX method will return the entry if
+ * it exists or null if it does not exist. If you pass in null for the setXXX
+ * method then it will clear the value.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.10 $
+ */
+public class PDDocumentInformation implements COSObjectable
+{
+ private static final COSName TITLE = COSName.getPDFName( "Title" );
+ private static final COSName AUTHOR = COSName.getPDFName( "Author" );
+ private static final COSName SUBJECT = COSName.getPDFName( "Subject" );
+ private static final COSName KEYWORDS = COSName.getPDFName( "Keywords" );
+ private static final COSName CREATOR = COSName.getPDFName( "Creator" );
+ private static final COSName PRODUCER = COSName.getPDFName( "Producer" );
+ private static final COSName CREATION_DATE = COSName.getPDFName( "CreationDate" );
+ private static final COSName MODIFICATION_DATE = COSName.getPDFName( "ModDate" );
+ private static final COSName TRAPPED = COSName.getPDFName( "Trapped" );
+ private COSDictionary info;
+
+
+ /**
+ * Default Constructor.
+ */
+ public PDDocumentInformation()
+ {
+ info = new COSDictionary();
+ }
+
+ /**
+ * Constructor that is used for a preexisting dictionary.
+ *
+ * @param dic The underlying dictionary.
+ */
+ public PDDocumentInformation( COSDictionary dic )
+ {
+ info = dic;
+ }
+
+ /**
+ * This will get the underlying dictionary that this object wraps.
+ *
+ * @return The underlying info dictionary.
+ */
+ public COSDictionary getDictionary()
+ {
+ return info;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return info;
+ }
+
+ /**
+ * This will get the title of the document. This will return null if no title exists.
+ *
+ * @return The title of the document.
+ */
+ public String getTitle()
+ {
+ return info.getString( TITLE );
+ }
+
+ /**
+ * This will set the title of the document.
+ *
+ * @param title The new title for the document.
+ */
+ public void setTitle( String title )
+ {
+ info.setString( TITLE, title );
+ }
+
+ /**
+ * This will get the author of the document. This will return null if no author exists.
+ *
+ * @return The author of the document.
+ */
+ public String getAuthor()
+ {
+ return info.getString( AUTHOR );
+ }
+
+ /**
+ * This will set the author of the document.
+ *
+ * @param author The new author for the document.
+ */
+ public void setAuthor( String author )
+ {
+ info.setString( AUTHOR, author );
+ }
+
+ /**
+ * This will get the subject of the document. This will return null if no subject exists.
+ *
+ * @return The subject of the document.
+ */
+ public String getSubject()
+ {
+ return info.getString( SUBJECT );
+ }
+
+ /**
+ * This will set the subject of the document.
+ *
+ * @param subject The new subject for the document.
+ */
+ public void setSubject( String subject )
+ {
+ info.setString( SUBJECT, subject );
+ }
+
+ /**
+ * This will get the keywords of the document. This will return null if no keywords exists.
+ *
+ * @return The keywords of the document.
+ */
+ public String getKeywords()
+ {
+ return info.getString( KEYWORDS );
+ }
+
+ /**
+ * This will set the keywords of the document.
+ *
+ * @param keywords The new keywords for the document.
+ */
+ public void setKeywords( String keywords )
+ {
+ info.setString( KEYWORDS, keywords );
+ }
+
+ /**
+ * This will get the creator of the document. This will return null if no creator exists.
+ *
+ * @return The creator of the document.
+ */
+ public String getCreator()
+ {
+ return info.getString( CREATOR );
+ }
+
+ /**
+ * This will set the creator of the document.
+ *
+ * @param creator The new creator for the document.
+ */
+ public void setCreator( String creator )
+ {
+ info.setString( CREATOR, creator );
+ }
+
+ /**
+ * This will get the producer of the document. This will return null if no producer exists.
+ *
+ * @return The producer of the document.
+ */
+ public String getProducer()
+ {
+ return info.getString( PRODUCER );
+ }
+
+ /**
+ * This will set the producer of the document.
+ *
+ * @param producer The new producer for the document.
+ */
+ public void setProducer( String producer )
+ {
+ info.setString( PRODUCER, producer );
+ }
+
+ /**
+ * This will get the creation date of the document. This will return null if no creation date exists.
+ *
+ * @return The creation date of the document.
+ *
+ * @throws IOException If there is an error creating the date.
+ */
+ public Calendar getCreationDate() throws IOException
+ {
+ return info.getDate( CREATION_DATE );
+ }
+
+ /**
+ * This will set the creation date of the document.
+ *
+ * @param date The new creation date for the document.
+ */
+ public void setCreationDate( Calendar date )
+ {
+ info.setDate( CREATION_DATE, date );
+ }
+
+ /**
+ * This will get the modification date of the document. This will return null if no modification date exists.
+ *
+ * @return The modification date of the document.
+ *
+ * @throws IOException If there is an error creating the date.
+ */
+ public Calendar getModificationDate() throws IOException
+ {
+ return info.getDate( MODIFICATION_DATE );
+ }
+
+ /**
+ * This will set the modification date of the document.
+ *
+ * @param date The new modification date for the document.
+ */
+ public void setModificationDate( Calendar date )
+ {
+ info.setDate( MODIFICATION_DATE, date );
+ }
+
+ /**
+ * This will get the trapped value for the document.
+ * This will return null if one is not found.
+ *
+ * @return The trapped value for the document.
+ */
+ public String getTrapped()
+ {
+ return info.getNameAsString( TRAPPED );
+ }
+
+ /**
+ * This will set the trapped of the document. This will be
+ * 'True', 'False', or 'Unknown'.
+ *
+ * @param value The new trapped value for the document.
+ */
+ public void setTrapped( String value )
+ {
+ if( value != null &&
+ !value.equals( "True" ) &&
+ !value.equals( "False" ) &&
+ !value.equals( "Unknown" ) )
+ {
+ throw new RuntimeException( "Valid values for trapped are " +
+ "'True', 'False', or 'Unknown'" );
+ }
+
+ info.setName( TRAPPED, value );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDDocumentNameDictionary.java b/src/main/java/org/pdfbox/pdmodel/PDDocumentNameDictionary.java
new file mode 100644
index 0000000..f343d7a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDDocumentNameDictionary.java
@@ -0,0 +1,164 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This class holds all of the name trees that are available at the document level.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class PDDocumentNameDictionary implements COSObjectable
+{
+ private COSDictionary nameDictionary;
+ private PDDocumentCatalog catalog;
+
+ /**
+ * Constructor.
+ *
+ * @param cat The document catalog that this dictionary is part of.
+ */
+ public PDDocumentNameDictionary( PDDocumentCatalog cat )
+ {
+ nameDictionary = new COSDictionary();
+ catalog = cat;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cat The document that this dictionary is part of.
+ * @param names The names dictionary.
+ */
+ public PDDocumentNameDictionary( PDDocumentCatalog cat, COSDictionary names )
+ {
+ catalog = cat;
+ nameDictionary = names;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return nameDictionary;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos dictionary for this object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return nameDictionary;
+ }
+
+ /**
+ * Get the destination named tree node. The value in this name tree will be PDDestination
+ * objects.
+ *
+ * @return The destination name tree node.
+ */
+ public PDDestinationNameTreeNode getDests()
+ {
+ PDDestinationNameTreeNode dests = null;
+
+ COSDictionary dic = (COSDictionary)nameDictionary.getDictionaryObject( "Dests" );
+
+ //The document catalog also contains the Dests entry sometimes
+ //so check there as well.
+ if( dic == null )
+ {
+ dic = (COSDictionary)catalog.getCOSDictionary().getDictionaryObject( "Dests" );
+ }
+
+ if( dic != null )
+ {
+ dests = new PDDestinationNameTreeNode( dic );
+ }
+
+
+ return dests;
+ }
+
+ /**
+ * Set the named destinations that are associated with this document.
+ *
+ * @param dests The destination names.
+ */
+ public void setDests( PDDestinationNameTreeNode dests )
+ {
+ nameDictionary.setItem( "Dests", dests );
+ //The dests can either be in the document catalog or in the
+ //names dictionary, PDFBox will just maintain the one in the
+ //names dictionary for now unless there is a reason to do
+ //something else.
+ //clear the potentially out of date Dests reference.
+ catalog.getCOSDictionary().setItem( "Dests", (COSObjectable)null);
+ }
+
+ /**
+ * Get the embedded files named tree node. The value in this name tree will be PDComplexFileSpecification
+ * objects.
+ *
+ * @return The embedded files name tree node.
+ */
+ public PDEmbeddedFilesNameTreeNode getEmbeddedFiles()
+ {
+ PDEmbeddedFilesNameTreeNode retval = null;
+
+ COSDictionary dic = (COSDictionary)nameDictionary.getDictionaryObject( "EmbeddedFiles" );
+
+ if( dic != null )
+ {
+ retval = new PDEmbeddedFilesNameTreeNode( dic );
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the named embedded files that are associated with this document.
+ *
+ * @param ef The new embedded files
+ */
+ public void setEmbeddedFiles( PDEmbeddedFilesNameTreeNode ef )
+ {
+ nameDictionary.setItem( "EmbeddedFiles", ef );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java b/src/main/java/org/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java
new file mode 100644
index 0000000..11c1103
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.PDNameTreeNode;
+import org.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
+
+/**
+ * This class holds all of the name trees that are available at the document level.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class PDEmbeddedFilesNameTreeNode extends PDNameTreeNode
+{
+ /**
+ * Constructor.
+ */
+ public PDEmbeddedFilesNameTreeNode()
+ {
+ super( PDComplexFileSpecification.class );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dic The COS dictionary.
+ */
+ public PDEmbeddedFilesNameTreeNode( COSDictionary dic )
+ {
+ super( dic, PDComplexFileSpecification.class );
+ }
+
+ /**
+ * @see PDNameTreeNode#convertCOSToPD( COSBase )
+ */
+ protected Object convertCOSToPD( COSBase base ) throws IOException
+ {
+ COSBase destination = base;
+ if( base instanceof COSDictionary )
+ {
+ //the destination is sometimes stored in the D dictionary
+ //entry instead of being directly an array, so just dereference
+ //it for now
+ destination = ((COSDictionary)base).getDictionaryObject( "D" );
+ }
+ return new PDComplexFileSpecification( (COSDictionary)destination );
+ }
+
+ /**
+ * @see PDNameTreeNode#createChildNode( COSDictionary )
+ */
+ protected PDNameTreeNode createChildNode( COSDictionary dic )
+ {
+ return new PDEmbeddedFilesNameTreeNode(dic);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDPage.java b/src/main/java/org/pdfbox/pdmodel/PDPage.java
new file mode 100644
index 0000000..3739a5a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDPage.java
@@ -0,0 +1,776 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDMetadata;
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+import org.pdfbox.pdmodel.interactive.action.PDPageAdditionalActions;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
+import org.pdfbox.pdmodel.interactive.pagenavigation.PDThreadBead;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.print.PageFormat;
+import java.awt.print.Paper;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterIOException;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+/**
+ * This represents a single page in a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.24 $
+ */
+public class PDPage implements COSObjectable, Printable
+{
+ private COSDictionary page;
+
+ /**
+ * A page size of LETTER or 8.5x11.
+ */
+ public static final PDRectangle PAGE_SIZE_LETTER = new PDRectangle( 612, 792 );
+
+
+ /**
+ * Creates a new instance of PDPage with a size of 8.5x11.
+ */
+ public PDPage()
+ {
+ page = new COSDictionary();
+ page.setItem( COSName.TYPE, COSName.PAGE );
+ setMediaBox( PAGE_SIZE_LETTER );
+ }
+
+ /**
+ * Creates a new instance of PDPage.
+ *
+ * @param pageDic The existing page dictionary.
+ */
+ public PDPage( COSDictionary pageDic )
+ {
+ page = pageDic;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return page;
+ }
+
+ /**
+ * This will get the underlying dictionary that this class acts on.
+ *
+ * @return The underlying dictionary for this class.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return page;
+ }
+
+
+ /**
+ * This is the parent page node. The parent is a required element of the
+ * page. This will be null until this page is added to the document.
+ *
+ * @return The parent to this page.
+ */
+ public PDPageNode getParent()
+ {
+ PDPageNode parent = null;
+ COSDictionary parentDic = (COSDictionary)page.getDictionaryObject( COSName.PARENT );
+ if( parentDic != null )
+ {
+ parent = new PDPageNode( parentDic );
+ }
+ return parent;
+ }
+
+ /**
+ * This will set the parent of this page.
+ *
+ * @param parent The parent to this page node.
+ */
+ public void setParent( PDPageNode parent )
+ {
+ page.setItem( COSName.PARENT, parent.getDictionary() );
+ }
+
+ /**
+ * This will update the last modified time for the page object.
+ */
+ public void updateLastModified()
+ {
+ page.setDate( "LastModified", new GregorianCalendar() );
+ }
+
+ /**
+ * This will get the date that the content stream was last modified. This
+ * may return null.
+ *
+ * @return The date the content stream was last modified.
+ *
+ * @throws IOException If there is an error accessing the date information.
+ */
+ public Calendar getLastModified() throws IOException
+ {
+ return page.getDate( "LastModified" );
+ }
+
+ /**
+ * This will get the resources at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findResources() should probably used.
+ * This will return null if no resources are available at this level.
+ *
+ * @return The resources at this level in the hierarchy.
+ */
+ public PDResources getResources()
+ {
+ PDResources retval = null;
+ COSDictionary resources = (COSDictionary)page.getDictionaryObject( COSName.RESOURCES );
+ if( resources != null )
+ {
+ retval = new PDResources( resources );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the resources for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The resources at this level in the hierarchy.
+ */
+ public PDResources findResources()
+ {
+ PDResources retval = getResources();
+ PDPageNode parent = getParent();
+ if( retval == null && parent != null )
+ {
+ retval = parent.findResources();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the resources for this page.
+ *
+ * @param resources The new resources for this page.
+ */
+ public void setResources( PDResources resources )
+ {
+ page.setItem( COSName.RESOURCES, resources );
+ }
+
+ /**
+ * A rectangle, expressed
+ * in default user space units, defining the boundaries of the physical
+ * medium on which the page is intended to be displayed or printed
+ *
+ * This will get the MediaBox at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findMediaBox() should probably used.
+ * This will return null if no MediaBox are available at this level.
+ *
+ * @return The MediaBox at this level in the hierarchy.
+ */
+ public PDRectangle getMediaBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.MEDIA_BOX );
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the MediaBox for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The MediaBox at this level in the hierarchy.
+ */
+ public PDRectangle findMediaBox()
+ {
+ PDRectangle retval = getMediaBox();
+ PDPageNode parent = getParent();
+ if( retval == null && parent != null )
+ {
+ retval = parent.findMediaBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the mediaBox for this page.
+ *
+ * @param mediaBox The new mediaBox for this page.
+ */
+ public void setMediaBox( PDRectangle mediaBox )
+ {
+ if( mediaBox == null )
+ {
+ page.removeItem( COSName.MEDIA_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.MEDIA_BOX, mediaBox.getCOSArray() );
+ }
+ }
+
+ /**
+ * A rectangle, expressed in default user space units,
+ * defining the visible region of default user space. When the page is displayed
+ * or printed, its contents are to be clipped (cropped) to this rectangle
+ * and then imposed on the output medium in some implementationdefined
+ * manner
+ *
+ * This will get the CropBox at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findCropBox() should probably used.
+ * This will return null if no CropBox is available at this level.
+ *
+ * @return The CropBox at this level in the hierarchy.
+ */
+ public PDRectangle getCropBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.CROP_BOX);
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the CropBox for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The CropBox at this level in the hierarchy.
+ */
+ public PDRectangle findCropBox()
+ {
+ PDRectangle retval = getCropBox();
+ PDPageNode parent = getParent();
+ if( retval == null && parent != null )
+ {
+ retval = findParentCropBox( parent );
+ }
+
+ //default value for cropbox is the media box
+ if( retval == null )
+ {
+ retval = findMediaBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will search for a crop box in the parent and return null if it is not
+ * found. It will NOT default to the media box if it cannot be found.
+ *
+ * @param node The node
+ */
+ private PDRectangle findParentCropBox( PDPageNode node )
+ {
+ PDRectangle rect = node.getCropBox();
+ PDPageNode parent = node.getParent();
+ if( rect == null && parent != null )
+ {
+ rect = findParentCropBox( parent );
+ }
+ return rect;
+ }
+
+ /**
+ * This will set the CropBox for this page.
+ *
+ * @param cropBox The new CropBox for this page.
+ */
+ public void setCropBox( PDRectangle cropBox )
+ {
+ if( cropBox == null )
+ {
+ page.removeItem( COSName.CROP_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.CROP_BOX, cropBox.getCOSArray() );
+ }
+ }
+
+ /**
+ * A rectangle, expressed in default user space units, defining
+ * the region to which the contents of the page should be clipped
+ * when output in a production environment. The default is the CropBox.
+ *
+ * @return The BleedBox attribute.
+ */
+ public PDRectangle getBleedBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.BLEED_BOX );
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ else
+ {
+ retval = findCropBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the BleedBox for this page.
+ *
+ * @param bleedBox The new BleedBox for this page.
+ */
+ public void setBleedBox( PDRectangle bleedBox )
+ {
+ if( bleedBox == null )
+ {
+ page.removeItem( COSName.BLEED_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.BLEED_BOX, bleedBox.getCOSArray() );
+ }
+ }
+
+ /**
+ * A rectangle, expressed in default user space units, defining
+ * the intended dimensions of the finished page after trimming.
+ * The default is the CropBox.
+ *
+ * @return The TrimBox attribute.
+ */
+ public PDRectangle getTrimBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.TRIM_BOX );
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ else
+ {
+ retval = findCropBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the TrimBox for this page.
+ *
+ * @param trimBox The new TrimBox for this page.
+ */
+ public void setTrimBox( PDRectangle trimBox )
+ {
+ if( trimBox == null )
+ {
+ page.removeItem( COSName.TRIM_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.TRIM_BOX, trimBox.getCOSArray() );
+ }
+ }
+
+ /**
+ * A rectangle, expressed in default user space units, defining
+ * the extent of the page's meaningful content (including potential
+ * white space) as intended by the page's creator The default isthe CropBox.
+ *
+ * @return The ArtBox attribute.
+ */
+ public PDRectangle getArtBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.ART_BOX );
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ else
+ {
+ retval = findCropBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the ArtBox for this page.
+ *
+ * @param artBox The new ArtBox for this page.
+ */
+ public void setArtBox( PDRectangle artBox )
+ {
+ if( artBox == null )
+ {
+ page.removeItem( COSName.ART_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.ART_BOX, artBox.getCOSArray() );
+ }
+ }
+
+
+ //todo BoxColorInfo
+ //todo Contents
+
+ /**
+ * A value representing the rotation. This will be null if not set at this level
+ * The number of degrees by which the page should
+ * be rotated clockwise when displayed or printed. The value must be a multiple
+ * of 90.
+ *
+ * This will get the rotation at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findRotation() should probably used.
+ * This will return null if no rotation is available at this level.
+ *
+ * @return The rotation at this level in the hierarchy.
+ */
+ public Integer getRotation()
+ {
+ Integer retval = null;
+ COSNumber value = (COSNumber)page.getDictionaryObject( COSName.ROTATE );
+ if( value != null )
+ {
+ retval = new Integer( value.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the rotation for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The rotation at this level in the hierarchy.
+ */
+ public int findRotation()
+ {
+ int retval = 0;
+ Integer rotation = getRotation();
+ if( rotation != null )
+ {
+ retval = rotation.intValue();
+ }
+ else
+ {
+ PDPageNode parent = getParent();
+ if( parent != null )
+ {
+ retval = parent.findRotation();
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the rotation for this page.
+ *
+ * @param rotation The new rotation for this page.
+ */
+ public void setRotation( int rotation )
+ {
+ page.setItem( COSName.ROTATE, new COSInteger( rotation ) );
+ }
+
+ /**
+ * This will get the contents of the PDF Page, in the case that the contents
+ * of the page is an array then then the entire array of streams will be
+ * be wrapped and appear as a single stream.
+ *
+ * @return The page content stream.
+ *
+ * @throws IOException If there is an error obtaining the stream.
+ */
+ public PDStream getContents() throws IOException
+ {
+ return PDStream.createFromCOS( page.getDictionaryObject( COSName.CONTENTS ) );
+ }
+
+ /**
+ * This will set the contents of this page.
+ *
+ * @param contents The new contents of the page.
+ */
+ public void setContents( PDStream contents )
+ {
+ page.setItem( COSName.CONTENTS, contents );
+ }
+
+ /**
+ * This will get a list of PDThreadBead objects, which are article threads in the
+ * document. This will return an empty list of there are no thread beads.
+ *
+ * @return A list of article threads on this page.
+ */
+ public List getThreadBeads()
+ {
+ COSArray beads = (COSArray)page.getDictionaryObject( COSName.B );
+ if( beads == null )
+ {
+ beads = new COSArray();
+ }
+ List pdObjects = new ArrayList();
+ for( int i=0; i<beads.size(); i++)
+ {
+ COSDictionary beadDic = (COSDictionary)beads.getObject( i );
+ PDThreadBead bead = null;
+ //in some cases the bead is null
+ if( beadDic != null )
+ {
+ bead = new PDThreadBead( beadDic );
+ }
+ pdObjects.add( bead );
+ }
+ return new COSArrayList(pdObjects, beads);
+
+ }
+
+ /**
+ * This will set the list of thread beads.
+ *
+ * @param beads A list of PDThreadBead objects or null.
+ */
+ public void setThreadBeads( List beads )
+ {
+ page.setItem( COSName.B, COSArrayList.converterToCOSArray( beads ) );
+ }
+
+ /**
+ * Get the metadata that is part of the document catalog. This will
+ * return null if there is no meta data for this object.
+ *
+ * @return The metadata for this object.
+ */
+ public PDMetadata getMetadata()
+ {
+ PDMetadata retval = null;
+ COSStream stream = (COSStream)page.getDictionaryObject( COSName.METADATA );
+ if( stream != null )
+ {
+ retval = new PDMetadata( stream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the metadata for this object. This can be null.
+ *
+ * @param meta The meta data for this object.
+ */
+ public void setMetadata( PDMetadata meta )
+ {
+ page.setItem( COSName.METADATA, meta );
+ }
+
+ /**
+ * Convert this page to an output image.
+ *
+ * @return A graphical representation of this page.
+ *
+ * @throws IOException If there is an error drawing to the image.
+ */
+ public BufferedImage convertToImage() throws IOException
+ {
+ int scaling = 2;
+ int rotation = findRotation();
+ PDRectangle mBox = findMediaBox();
+ int width = (int)(mBox.getWidth());//*2);
+ int height = (int)(mBox.getHeight());//*2);
+ if( rotation == 90 || rotation == 270 )
+ {
+ int tmp = width;
+ width = height;
+ height = tmp;
+ }
+ Dimension pageDimension = new Dimension( width, height );
+
+ //note we are doing twice as many pixels because
+ //the default size is not really good resolution,
+ //so create an image that is twice the size
+ //and let the client scale it down.
+ BufferedImage retval =
+ new BufferedImage( width*scaling, height*scaling, BufferedImage.TYPE_BYTE_INDEXED );
+ Graphics2D graphics = (Graphics2D)retval.getGraphics();
+ graphics.setColor( Color.WHITE );
+ graphics.fillRect(0,0,width*scaling, height*scaling);
+ graphics.scale( scaling, scaling );
+ PageDrawer drawer = new PageDrawer();
+ drawer.drawPage( graphics, this, pageDimension );
+
+
+ return retval;
+ }
+
+ /**
+ * Get the page actions.
+ *
+ * @return The Actions for this Page
+ */
+ public PDPageAdditionalActions getActions()
+ {
+ COSDictionary addAct = (COSDictionary) page.getDictionaryObject(COSName.AA);
+ if (addAct == null)
+ {
+ addAct = new COSDictionary();
+ page.setItem(COSName.AA, addAct);
+ }
+ return new PDPageAdditionalActions(addAct);
+ }
+
+ /**
+ * Set the page actions.
+ *
+ * @param actions The actions for the page.
+ */
+ public void setActions( PDPageAdditionalActions actions )
+ {
+ page.setItem( COSName.AA, actions );
+ }
+
+ /**
+ * This will return a list of the Annotations for this page.
+ *
+ * @return List of the PDAnnotation objects.
+ *
+ * @throws IOException If there is an error while creating the annotations.
+ */
+ public List getAnnotations() throws IOException
+ {
+ COSArrayList retval = null;
+ COSArray annots = (COSArray)page.getDictionaryObject(COSName.ANNOTS);
+ if (annots == null)
+ {
+ annots = new COSArray();
+ page.setItem(COSName.ANNOTS, annots);
+ retval = new COSArrayList(new ArrayList(), annots);
+ }
+ else
+ {
+ List actuals = new ArrayList();
+
+ for (int i=0; i < annots.size(); i++)
+ {
+ COSBase item = annots.getObject(i);
+ actuals.add( PDAnnotation.createAnnotation( item ) );
+ }
+ retval = new COSArrayList(actuals, annots);
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of annotations.
+ *
+ * @param annots The new list of annotations.
+ */
+ public void setAnnotations( List annots )
+ {
+ page.setItem( COSName.ANNOTS, COSArrayList.converterToCOSArray( annots ) );
+ }
+
+ /**
+ * @see Printable#print(java.awt.Graphics, java.awt.print.PageFormat, int)
+ */
+ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
+ throws PrinterException
+ {
+ int retval = Printable.NO_SUCH_PAGE;
+ if( pageIndex == 0 )
+ {
+ try
+ {
+ retval = Printable.PAGE_EXISTS;
+ PageDrawer drawer = new PageDrawer();
+ PDRectangle pageSize = findMediaBox();
+ Paper paper = new Paper();
+
+ paper.setImageableArea( 0, 0, pageSize.getWidth(), pageSize.getHeight());
+ pageFormat.setPaper( paper );
+ drawer.drawPage( graphics, this, pageSize.createDimension() );
+
+ }
+ catch( IOException io )
+ {
+ throw new PrinterIOException( io );
+ }
+
+ }
+ return retval;
+ }
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals( Object other )
+ {
+ return other instanceof PDPage && ((PDPage)other).getCOSObject() == this.getCOSObject();
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return this.getCOSDictionary().hashCode();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDPageNode.java b/src/main/java/org/pdfbox/pdmodel/PDPageNode.java
new file mode 100644
index 0000000..9567d39
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDPageNode.java
@@ -0,0 +1,459 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSInteger;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This represents a page node in a pdf document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class PDPageNode implements COSObjectable
+{
+ private COSDictionary page;
+
+ /**
+ * Creates a new instance of PDPage.
+ */
+ public PDPageNode()
+ {
+ page = new COSDictionary();
+ page.setItem( COSName.TYPE, COSName.PAGES );
+ page.setItem( COSName.KIDS, new COSArray() );
+ page.setItem( COSName.COUNT, new COSInteger( 0 ) );
+ }
+
+ /**
+ * Creates a new instance of PDPage.
+ *
+ * @param pages The dictionary pages.
+ */
+ public PDPageNode( COSDictionary pages )
+ {
+ page = pages;
+ }
+
+ /**
+ * This will update the count attribute of the page node. This only needs to
+ * be called if you add or remove pages. The PDDocument will call this for you
+ * when you use the PDDocumnet persistence methods. So, basically most clients
+ * will never need to call this.
+ *
+ * @return The update count for this node.
+ */
+ public long updateCount()
+ {
+ long totalCount = 0;
+ List kids = getKids();
+ Iterator kidIter = kids.iterator();
+ while( kidIter.hasNext() )
+ {
+ Object next = kidIter.next();
+ if( next instanceof PDPage )
+ {
+ totalCount++;
+ }
+ else
+ {
+ PDPageNode node = (PDPageNode)next;
+ totalCount += node.updateCount();
+ }
+ }
+ page.setItem( COSName.COUNT, new COSInteger( totalCount ) );
+ return totalCount;
+ }
+
+ /**
+ * This will get the count of descendent page objects.
+ *
+ * @return The total number of descendent page objects.
+ */
+ public long getCount()
+ {
+ return ((COSNumber)page.getDictionaryObject( COSName.COUNT )).intValue();
+ }
+
+ /**
+ * This will get the underlying dictionary that this class acts on.
+ *
+ * @return The underlying dictionary for this class.
+ */
+ public COSDictionary getDictionary()
+ {
+ return page;
+ }
+
+ /**
+ * This is the parent page node.
+ *
+ * @return The parent to this page.
+ */
+ public PDPageNode getParent()
+ {
+ PDPageNode parent = null;
+ COSDictionary parentDic = (COSDictionary)page.getDictionaryObject( COSName.PARENT );
+ if( parentDic != null )
+ {
+ parent = new PDPageNode( parentDic );
+ }
+ return parent;
+ }
+
+ /**
+ * This will set the parent of this page.
+ *
+ * @param parent The parent to this page node.
+ */
+ public void setParent( PDPageNode parent )
+ {
+ page.setItem( COSName.PARENT, parent.getDictionary() );
+ }
+
+ /**
+ * @see COSObjectable#getCOSObject()
+ */
+ public COSBase getCOSObject()
+ {
+ return page;
+ }
+
+ /**
+ * This will return all kids of this node, either PDPageNode or PDPage.
+ *
+ * @return All direct descendents of this node.
+ */
+ public List getKids()
+ {
+ List actuals = new ArrayList();
+ COSArray kids = getAllKids(actuals, page, false);
+ return new COSArrayList( actuals, kids );
+ }
+
+ /**
+ * This will return all kids of this node as PDPage.
+ *
+ * @param result All direct and indirect descendents of this node are added to this list.
+ */
+ public void getAllKids(List result)
+ {
+ getAllKids(result, page, true);
+ }
+
+ /**
+ * This will return all kids of the given page node as PDPage.
+ *
+ * @param result All direct and optionally indirect descendents of this node are added to this list.
+ * @param page Page dictionary of a page node.
+ * @param recurse if true indirect descendents are processed recursively
+ */
+ private static COSArray getAllKids(List result, COSDictionary page, boolean recurse)
+ {
+ COSArray kids = (COSArray)page.getDictionaryObject( COSName.KIDS );
+
+ for( int i=0; i<kids.size(); i++ )
+ {
+ COSBase obj = kids.getObject( i );
+ if (obj instanceof COSDictionary)
+ {
+ COSDictionary kid = (COSDictionary)obj;
+ if( COSName.PAGE.equals( kid.getDictionaryObject( COSName.TYPE ) ) )
+ {
+ result.add( new PDPage( kid ) );
+ }
+ else
+ {
+ if (recurse)
+ {
+ getAllKids(result, kid, recurse);
+ }
+ else
+ {
+ result.add( new PDPageNode( kid ) );
+ }
+ }
+ }
+ }
+ return kids;
+ }
+
+ /**
+ * This will get the resources at this page node and not look up the hierarchy.
+ * This attribute is inheritable, and findResources() should probably used.
+ * This will return null if no resources are available at this level.
+ *
+ * @return The resources at this level in the hierarchy.
+ */
+ public PDResources getResources()
+ {
+ PDResources retval = null;
+ COSDictionary resources = (COSDictionary)page.getDictionaryObject( COSName.RESOURCES );
+ if( resources != null )
+ {
+ retval = new PDResources( resources );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the resources for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The resources at this level in the hierarchy.
+ */
+ public PDResources findResources()
+ {
+ PDResources retval = getResources();
+ PDPageNode parent = getParent();
+ if( retval == null && parent != null )
+ {
+ retval = parent.findResources();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the resources for this page.
+ *
+ * @param resources The new resources for this page.
+ */
+ public void setResources( PDResources resources )
+ {
+ if( resources == null )
+ {
+ page.removeItem( COSName.RESOURCES );
+ }
+ else
+ {
+ page.setItem( COSName.RESOURCES, resources.getCOSDictionary() );
+ }
+ }
+
+ /**
+ * This will get the MediaBox at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findMediaBox() should probably used.
+ * This will return null if no MediaBox are available at this level.
+ *
+ * @return The MediaBox at this level in the hierarchy.
+ */
+ public PDRectangle getMediaBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.MEDIA_BOX );
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the MediaBox for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The MediaBox at this level in the hierarchy.
+ */
+ public PDRectangle findMediaBox()
+ {
+ PDRectangle retval = getMediaBox();
+ PDPageNode parent = getParent();
+ if( retval == null && parent != null )
+ {
+ retval = parent.findMediaBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the mediaBox for this page.
+ *
+ * @param mediaBox The new mediaBox for this page.
+ */
+ public void setMediaBox( PDRectangle mediaBox )
+ {
+ if( mediaBox == null )
+ {
+ page.removeItem( COSName.MEDIA_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.MEDIA_BOX , mediaBox.getCOSArray() );
+ }
+ }
+
+/**
+ * This will get the CropBox at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findCropBox() should probably used.
+ * This will return null if no CropBox is available at this level.
+ *
+ * @return The CropBox at this level in the hierarchy.
+ */
+ public PDRectangle getCropBox()
+ {
+ PDRectangle retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( COSName.CROP_BOX );
+ if( array != null )
+ {
+ retval = new PDRectangle( array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the CropBox for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The CropBox at this level in the hierarchy.
+ */
+ public PDRectangle findCropBox()
+ {
+ PDRectangle retval = getCropBox();
+ PDPageNode parent = getParent();
+ if( retval == null && parent != null )
+ {
+ retval = findParentCropBox( parent );
+ }
+
+ //default value for cropbox is the media box
+ if( retval == null )
+ {
+ retval = findMediaBox();
+ }
+ return retval;
+ }
+
+ /**
+ * This will search for a crop box in the parent and return null if it is not
+ * found. It will NOT default to the media box if it cannot be found.
+ *
+ * @param node The node
+ */
+ private PDRectangle findParentCropBox( PDPageNode node )
+ {
+ PDRectangle rect = node.getCropBox();
+ PDPageNode parent = node.getParent();
+ if( rect == null && parent != null )
+ {
+ rect = findParentCropBox( node );
+ }
+ return rect;
+ }
+
+ /**
+ * This will set the CropBox for this page.
+ *
+ * @param cropBox The new CropBox for this page.
+ */
+ public void setCropBox( PDRectangle cropBox )
+ {
+ if( cropBox == null )
+ {
+ page.removeItem( COSName.CROP_BOX );
+ }
+ else
+ {
+ page.setItem( COSName.CROP_BOX, cropBox.getCOSArray() );
+ }
+ }
+
+ /**
+ * A value representing the rotation. This will be null if not set at this level
+ * The number of degrees by which the page should
+ * be rotated clockwise when displayed or printed. The value must be a multiple
+ * of 90.
+ *
+ * This will get the rotation at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findRotation() should probably used.
+ * This will return null if no rotation is available at this level.
+ *
+ * @return The rotation at this level in the hierarchy.
+ */
+ public Integer getRotation()
+ {
+ Integer retval = null;
+ COSNumber value = (COSNumber)page.getDictionaryObject( COSName.ROTATE );
+ if( value != null )
+ {
+ retval = new Integer( (int)value.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will find the rotation for this page by looking up the hierarchy until
+ * it finds them.
+ *
+ * @return The rotation at this level in the hierarchy.
+ */
+ public int findRotation()
+ {
+ int retval = 0;
+ Integer rotation = getRotation();
+ if( rotation != null )
+ {
+ retval = rotation.intValue();
+ }
+ else
+ {
+ PDPageNode parent = getParent();
+ if( parent != null )
+ {
+ retval = parent.findRotation();
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the rotation for this page.
+ *
+ * @param rotation The new rotation for this page.
+ */
+ public void setRotation( int rotation )
+ {
+ page.setItem( COSName.ROTATE, new COSInteger( rotation ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/PDResources.java b/src/main/java/org/pdfbox/pdmodel/PDResources.java
new file mode 100644
index 0000000..9494b69
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/PDResources.java
@@ -0,0 +1,313 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.common.COSDictionaryMap;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.pdmodel.font.PDFontFactory;
+
+import org.pdfbox.pdmodel.graphics.PDExtendedGraphicsState;
+
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory;
+
+import org.pdfbox.pdmodel.graphics.xobject.PDXObject;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+
+/**
+ * This represents a set of resources available at the page/pages/stream level.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.13 $
+ */
+public class PDResources implements COSObjectable
+{
+ private COSDictionary resources;
+
+ /**
+ * Default constructor.
+ */
+ public PDResources()
+ {
+ resources = new COSDictionary();
+ }
+
+ /**
+ * Prepopulated resources.
+ *
+ * @param resourceDictionary The cos dictionary for this resource.
+ */
+ public PDResources( COSDictionary resourceDictionary )
+ {
+ resources = resourceDictionary;
+ }
+
+ /**
+ * This will get the underlying dictionary.
+ *
+ * @return The dictionary for these resources.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return resources;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return resources;
+ }
+
+ /**
+ * This will get the map of fonts. This will never return null. The keys are string
+ * and the values are PDFont objects.
+ *
+ * @return The map of fonts.
+ *
+ * @throws IOException If there is an error getting the fonts.
+ */
+ public Map getFonts() throws IOException
+ {
+ Map retval = null;
+ COSDictionary fonts = (COSDictionary)resources.getDictionaryObject( COSName.FONT );
+
+ if( fonts == null )
+ {
+ fonts = new COSDictionary();
+ resources.setItem( COSName.FONT, fonts );
+ }
+
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, fonts );
+ Iterator fontNames = fonts.keyList().iterator();
+ while( fontNames.hasNext() )
+ {
+ COSName fontName = (COSName)fontNames.next();
+ COSBase font = fonts.getDictionaryObject( fontName );
+ //data-000174.pdf contains a font that is a COSArray, looks to be an error in the
+ //PDF, we will just ignore entries that are not dictionaries.
+ if( font instanceof COSDictionary )
+ {
+ COSDictionary fontDictionary = (COSDictionary)font;
+ actuals.put( fontName.getName(), PDFontFactory.createFont( fontDictionary ) );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the map of PDXObjects that are in the resource dictionary.
+ *
+ * @return The map of xobjects.
+ *
+ * @throws IOException If there is an error creating the xobjects.
+ */
+ public Map getXObjects() throws IOException
+ {
+ Map retval = null;
+ COSDictionary xobjects = (COSDictionary)resources.getDictionaryObject( "XObject" );
+
+ if( xobjects == null )
+ {
+ xobjects = new COSDictionary();
+ resources.setItem( "XObject", xobjects );
+ }
+
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, xobjects );
+ Iterator imageNames = xobjects.keyList().iterator();
+ while( imageNames.hasNext() )
+ {
+ COSName objName = (COSName)imageNames.next();
+ COSBase cosObject = xobjects.getDictionaryObject(objName);
+ PDXObject xobject = PDXObject.createXObject( cosObject );
+ if( xobject !=null )
+ {
+ actuals.put( objName.getName(), xobject);
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the map of images. An empty map will be returned if there
+ * are no underlying images.
+ * So far the keys are COSName of the image
+ * and the value is the corresponding PDXObjectImage.
+ *
+ * @author By BM
+ * @return The map of images.
+ * @throws IOException If there is an error writing the picture.
+ */
+ public Map getImages() throws IOException
+ {
+ Map retval = null;
+ COSDictionary images = (COSDictionary)resources.getDictionaryObject( "XObject" );
+
+ if( images == null )
+ {
+ images = new COSDictionary();
+ resources.setItem( "XObject", images );
+ }
+
+ if( images != null )
+ {
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, images );
+ Iterator imageNames = images.keyList().iterator();
+ while( imageNames.hasNext() )
+ {
+ COSName imageName = (COSName)imageNames.next();
+ COSStream image = (COSStream)(images.getDictionaryObject(imageName));
+
+ COSName subType =(COSName)image.getDictionaryObject(COSName.SUBTYPE);
+ if( subType.equals(COSName.IMAGE) )
+ {
+ PDXObjectImage ximage = (PDXObjectImage)PDXObject.createXObject( image );
+ if( ximage !=null )
+ {
+ actuals.put( imageName.getName(), ximage);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the map of fonts.
+ *
+ * @param fonts The new map of fonts.
+ */
+ public void setFonts( Map fonts )
+ {
+ resources.setItem( COSName.FONT, COSDictionaryMap.convert( fonts ) );
+ }
+
+ /**
+ * This will get the map of colorspaces. This will return null if the underlying
+ * resources dictionary does not have a colorspace dictionary. The keys are string
+ * and the values are PDColorSpace objects.
+ *
+ * @return The map of colorspaces.
+ *
+ * @throws IOException If there is an error getting the colorspaces.
+ */
+ public Map getColorSpaces() throws IOException
+ {
+ Map retval = null;
+ COSDictionary colorspaces = (COSDictionary)resources.getDictionaryObject( COSName.getPDFName( "ColorSpace" ) );
+
+ if( colorspaces != null )
+ {
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, colorspaces );
+ Iterator csNames = colorspaces.keyList().iterator();
+ while( csNames.hasNext() )
+ {
+ COSName csName = (COSName)csNames.next();
+ COSBase cs = colorspaces.getDictionaryObject( csName );
+ actuals.put( csName.getName(), PDColorSpaceFactory.createColorSpace( cs ) );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the map of colorspaces.
+ *
+ * @param colorspaces The new map of colorspaces.
+ */
+ public void setColorSpaces( Map colorspaces )
+ {
+ resources.setItem( COSName.getPDFName( "ColorSpace" ), COSDictionaryMap.convert( colorspaces ) );
+ }
+
+ /**
+ * This will get the map of graphic states. This will return null if the underlying
+ * resources dictionary does not have a graphics dictionary. The keys are the graphic state
+ * name as a String and the values are PDExtendedGraphicsState objects.
+ *
+ * @return The map of extended graphic state objects.
+ */
+ public Map getGraphicsStates()
+ {
+ Map retval = null;
+ COSDictionary states = (COSDictionary)resources.getDictionaryObject( COSName.getPDFName( "ExtGState" ) );
+
+ if( states != null )
+ {
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, states );
+ Iterator names = states.keyList().iterator();
+ while( names.hasNext() )
+ {
+ COSName name = (COSName)names.next();
+ COSDictionary dictionary = (COSDictionary)states.getDictionaryObject( name );
+ actuals.put( name.getName(), new PDExtendedGraphicsState( dictionary ) );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the map of graphics states.
+ *
+ * @param states The new map of states.
+ */
+ public void setGraphicsStates( Map states )
+ {
+ Iterator iter = states.keySet().iterator();
+ COSDictionary dic = new COSDictionary();
+ while( iter.hasNext() )
+ {
+ String name = (String)iter.next();
+ PDExtendedGraphicsState state = (PDExtendedGraphicsState)states.get( name );
+ dic.setItem( COSName.getPDFName( name ), state.getCOSObject() );
+ }
+ resources.setItem( COSName.getPDFName( "ExtGState" ), dic );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/COSArrayList.java b/src/main/java/org/pdfbox/pdmodel/common/COSArrayList.java
new file mode 100644
index 0000000..bb3648a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/COSArrayList.java
@@ -0,0 +1,643 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+import org.pdfbox.cos.COSNumber;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * This is an implementation of a List that will sync its contents to a COSArray.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.12 $
+ */
+public class COSArrayList implements List
+{
+ private COSArray array;
+ private List actual;
+
+ private COSDictionary parentDict;
+ private String dictKey;
+
+ /**
+ * Constructor.
+ *
+ * @param actualList The list of standard java objects
+ * @param cosArray The COS array object to sync to.
+ */
+ public COSArrayList( List actualList, COSArray cosArray )
+ {
+ actual = actualList;
+ array = cosArray;
+ }
+
+ /**
+ * This is a really special constructor. Sometimes the PDF spec says
+ * that a dictionary entry can either be a single item or an array of those
+ * items. But in the PDModel interface we really just want to always return
+ * a java.util.List. In the case were we get the list and never modify it
+ * we don't want to convert to COSArray and put one element, unless we append
+ * to the list. So here we are going to create this object with a single
+ * item instead of a list, but allow more items to be added and then converted
+ * to an array.
+ *
+ * @param actualObject The PDModel object.
+ * @param item The COS Model object.
+ * @param dictionary The dictionary that holds the item, and will hold the array if an item is added.
+ * @param dictionaryKey The key into the dictionary to set the item.
+ */
+ public COSArrayList( Object actualObject, COSBase item, COSDictionary dictionary, String dictionaryKey )
+ {
+ array = new COSArray();
+ array.add( item );
+ actual = new ArrayList();
+ actual.add( actualObject );
+
+ parentDict = dictionary;
+ dictKey = dictionaryKey;
+ }
+
+ /**
+ * @see List#size()
+ */
+ public int size()
+ {
+ return actual.size();
+ }
+
+ /**
+ * @see List#isEmpty()
+ */
+ public boolean isEmpty()
+ {
+ return actual.isEmpty();
+ }
+
+ /**
+ * @see List#contains( Object )
+ */
+ public boolean contains(Object o)
+ {
+ return actual.contains(o);
+ }
+
+ /**
+ * @see List#iterator()
+ */
+ public Iterator iterator()
+ {
+ return actual.iterator();
+ }
+
+ /**
+ * @see List#toArray()
+ */
+ public Object[] toArray()
+ {
+ return actual.toArray();
+ }
+
+ /**
+ * @see List#toArray( Object[] )
+ */
+ public Object[] toArray(Object[] a)
+ {
+ return actual.toArray(a);
+
+ }
+
+ /**
+ * @see List#add( Object )
+ */
+ public boolean add(Object o)
+ {
+ //when adding if there is a parentDict then change the item
+ //in the dictionary from a single item to an array.
+ if( parentDict != null )
+ {
+ parentDict.setItem( dictKey, array );
+ //clear the parent dict so it doesn't happen again, there might be
+ //a usecase for keeping the parentDict around but not now.
+ parentDict = null;
+ }
+ //string is a special case because we can't subclass to be COSObjectable
+ if( o instanceof String )
+ {
+ array.add( new COSString( (String)o ) );
+ }
+ else if( o instanceof DualCOSObjectable )
+ {
+ DualCOSObjectable dual = (DualCOSObjectable)o;
+ array.add( dual.getFirstCOSObject() );
+ array.add( dual.getSecondCOSObject() );
+ }
+ else
+ {
+ array.add( ((COSObjectable)o).getCOSObject() );
+ }
+ return actual.add(o);
+ }
+
+ /**
+ * @see List#remove( Object )
+ */
+ public boolean remove(Object o)
+ {
+ boolean retval = true;
+ int index = actual.indexOf( o );
+ if( index >= 0 )
+ {
+ actual.remove( index );
+ array.remove( index );
+ }
+ else
+ {
+ retval = false;
+ }
+ return retval;
+ }
+
+ /**
+ * @see List#containsAll( Collection )
+ */
+ public boolean containsAll(Collection c)
+ {
+ return actual.containsAll( c );
+ }
+
+ /**
+ * @see List#addAll( Collection )
+ */
+ public boolean addAll(Collection c)
+ {
+ //when adding if there is a parentDict then change the item
+ //in the dictionary from a single item to an array.
+ if( parentDict != null && c.size() > 0)
+ {
+ parentDict.setItem( dictKey, array );
+ //clear the parent dict so it doesn't happen again, there might be
+ //a usecase for keeping the parentDict around but not now.
+ parentDict = null;
+ }
+ array.addAll( toCOSObjectList( c ) );
+ return actual.addAll( c );
+ }
+
+ /**
+ * @see List#addAll( int, Collection )
+ */
+ public boolean addAll(int index, Collection c)
+ {
+ //when adding if there is a parentDict then change the item
+ //in the dictionary from a single item to an array.
+ if( parentDict != null && c.size() > 0)
+ {
+ parentDict.setItem( dictKey, array );
+ //clear the parent dict so it doesn't happen again, there might be
+ //a usecase for keeping the parentDict around but not now.
+ parentDict = null;
+ }
+
+ if( c.size() >0 && c.toArray()[0] instanceof DualCOSObjectable )
+ {
+ array.addAll( index*2, toCOSObjectList( c ) );
+ }
+ else
+ {
+ array.addAll( index, toCOSObjectList( c ) );
+ }
+ return actual.addAll( index, c );
+ }
+
+ /**
+ * This will take an array of COSNumbers and return a COSArrayList of
+ * java.lang.Integer values.
+ *
+ * @param intArray The existing integer Array.
+ *
+ * @return A list that is part of the core Java collections.
+ */
+ public static List convertIntegerCOSArrayToList( COSArray intArray )
+ {
+ List numbers = new ArrayList();
+ for( int i=0; i<intArray.size(); i++ )
+ {
+ numbers.add( new Integer( ((COSNumber)intArray.get( i )).intValue() ) );
+ }
+ return new COSArrayList( numbers, intArray );
+ }
+
+ /**
+ * This will take an array of COSNumbers and return a COSArrayList of
+ * java.lang.Float values.
+ *
+ * @param floatArray The existing float Array.
+ *
+ * @return The list of Float objects.
+ */
+ public static List convertFloatCOSArrayToList( COSArray floatArray )
+ {
+ List retval = null;
+ if( floatArray != null )
+ {
+ List numbers = new ArrayList();
+ for( int i=0; i<floatArray.size(); i++ )
+ {
+ numbers.add( new Float( ((COSNumber)floatArray.get( i )).floatValue() ) );
+ }
+ retval = new COSArrayList( numbers, floatArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will take an array of COSName and return a COSArrayList of
+ * java.lang.String values.
+ *
+ * @param nameArray The existing name Array.
+ *
+ * @return The list of String objects.
+ */
+ public static List convertCOSNameCOSArrayToList( COSArray nameArray )
+ {
+ List retval = null;
+ if( nameArray != null )
+ {
+ List names = new ArrayList();
+ for( int i=0; i<nameArray.size(); i++ )
+ {
+ names.add( ((COSName)nameArray.getObject( i )).getName() );
+ }
+ retval = new COSArrayList( names, nameArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will take an array of COSString and return a COSArrayList of
+ * java.lang.String values.
+ *
+ * @param stringArray The existing name Array.
+ *
+ * @return The list of String objects.
+ */
+ public static List convertCOSStringCOSArrayToList( COSArray stringArray )
+ {
+ List retval = null;
+ if( stringArray != null )
+ {
+ List string = new ArrayList();
+ for( int i=0; i<stringArray.size(); i++ )
+ {
+ string.add( ((COSString)stringArray.getObject( i )).getString() );
+ }
+ retval = new COSArrayList( string, stringArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will take an list of string objects and return a COSArray of COSName
+ * objects.
+ *
+ * @param strings A list of strings
+ *
+ * @return An array of COSName objects
+ */
+ public static COSArray convertStringListToCOSNameCOSArray( List strings )
+ {
+ COSArray retval = new COSArray();
+ for( int i=0; i<strings.size(); i++ )
+ {
+ Object next = strings.get( i );
+ if( next instanceof COSName )
+ {
+ retval.add( (COSName)next );
+ }
+ else
+ {
+ retval.add( COSName.getPDFName( (String)next ) );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will take an list of string objects and return a COSArray of COSName
+ * objects.
+ *
+ * @param strings A list of strings
+ *
+ * @return An array of COSName objects
+ */
+ public static COSArray convertStringListToCOSStringCOSArray( List strings )
+ {
+ COSArray retval = new COSArray();
+ for( int i=0; i<strings.size(); i++ )
+ {
+ retval.add( new COSString( (String)strings.get( i ) ) );
+ }
+ return retval;
+ }
+
+ /**
+ * This will convert a list of COSObjectables to an
+ * array list of COSBase objects.
+ *
+ * @param cosObjectableList A list of COSObjectable.
+ *
+ * @return A list of COSBase.
+ */
+ public static COSArray converterToCOSArray( List cosObjectableList )
+ {
+ COSArray array = null;
+ if( cosObjectableList != null )
+ {
+ array = new COSArray();
+ Iterator iter = cosObjectableList.iterator();
+ while( iter.hasNext() )
+ {
+ Object next = iter.next();
+ if( next instanceof String )
+ {
+ array.add( new COSString( (String)next ) );
+ }
+ else if( next instanceof Integer || next instanceof Long )
+ {
+ array.add( new COSInteger( ((Number)next).longValue() ) );
+ }
+ else if( next instanceof Float || next instanceof Double )
+ {
+ array.add( new COSFloat( ((Number)next).floatValue() ) );
+ }
+ else if( next instanceof COSObjectable )
+ {
+ COSObjectable object = (COSObjectable)next;
+ array.add( object.getCOSObject() );
+ }
+ else if( next instanceof DualCOSObjectable )
+ {
+ DualCOSObjectable object = (DualCOSObjectable)next;
+ array.add( object.getFirstCOSObject() );
+ array.add( object.getSecondCOSObject() );
+ }
+ else if( next == null )
+ {
+ array.add( COSNull.NULL );
+ }
+ else
+ {
+ throw new RuntimeException( "Error: Don't know how to convert type to COSBase '" +
+ next.getClass().getName() + "'" );
+ }
+ }
+ }
+ return array;
+ }
+
+ private List toCOSObjectList( Collection list )
+ {
+ List cosObjects = new ArrayList();
+ Iterator iter = list.iterator();
+ while( iter.hasNext() )
+ {
+ Object next = iter.next();
+ if( next instanceof String )
+ {
+ cosObjects.add( new COSString( (String)next ) );
+ }
+ else if( next instanceof DualCOSObjectable )
+ {
+ DualCOSObjectable object = (DualCOSObjectable)next;
+ array.add( object.getFirstCOSObject() );
+ array.add( object.getSecondCOSObject() );
+ }
+ else
+ {
+ COSObjectable cos = (COSObjectable)next;
+ cosObjects.add( cos.getCOSObject() );
+ }
+ }
+ return cosObjects;
+ }
+
+ /**
+ * @see List#removeAll( Collection )
+ */
+ public boolean removeAll(Collection c)
+ {
+ array.removeAll( toCOSObjectList( c ) );
+ return actual.removeAll( c );
+ }
+
+ /**
+ * @see List#retainAll( Collection )
+ */
+ public boolean retainAll(Collection c)
+ {
+ array.retainAll( toCOSObjectList( c ) );
+ return actual.retainAll( c );
+ }
+
+ /**
+ * @see List#clear()
+ */
+ public void clear()
+ {
+ //when adding if there is a parentDict then change the item
+ //in the dictionary from a single item to an array.
+ if( parentDict != null )
+ {
+ parentDict.setItem( dictKey, (COSBase)null );
+ }
+ actual.clear();
+ array.clear();
+ }
+
+ /**
+ * @see List#equals( Object )
+ */
+ public boolean equals(Object o)
+ {
+ return actual.equals( o );
+ }
+
+ /**
+ * @see List#hashCode()
+ */
+ public int hashCode()
+ {
+ return actual.hashCode();
+ }
+
+ /**
+ * @see List#get( int )
+ */
+ public Object get(int index)
+ {
+ return actual.get( index );
+
+ }
+
+ /**
+ * @see List#set( int, Object )
+ */
+ public Object set(int index, Object element)
+ {
+ if( element instanceof String )
+ {
+ COSString item = new COSString( (String)element );
+ if( parentDict != null && index == 0 )
+ {
+ parentDict.setItem( dictKey, item );
+ }
+ array.set( index, item );
+ }
+ else if( element instanceof DualCOSObjectable )
+ {
+ DualCOSObjectable dual = (DualCOSObjectable)element;
+ array.set( index*2, dual.getFirstCOSObject() );
+ array.set( index*2+1, dual.getSecondCOSObject() );
+ }
+ else
+ {
+ if( parentDict != null && index == 0 )
+ {
+ parentDict.setItem( dictKey, ((COSObjectable)element).getCOSObject() );
+ }
+ array.set( index, ((COSObjectable)element).getCOSObject() );
+ }
+ return actual.set( index, element );
+ }
+
+ /**
+ * @see List#add( int, Object )
+ */
+ public void add(int index, Object element)
+ {
+ //when adding if there is a parentDict then change the item
+ //in the dictionary from a single item to an array.
+ if( parentDict != null )
+ {
+ parentDict.setItem( dictKey, array );
+ //clear the parent dict so it doesn't happen again, there might be
+ //a usecase for keeping the parentDict around but not now.
+ parentDict = null;
+ }
+ actual.add( index, element );
+ if( element instanceof String )
+ {
+ array.add( index, new COSString( (String)element ) );
+ }
+ else if( element instanceof DualCOSObjectable )
+ {
+ DualCOSObjectable dual = (DualCOSObjectable)element;
+ array.add( index*2, dual.getFirstCOSObject() );
+ array.add( index*2+1, dual.getSecondCOSObject() );
+ }
+ else
+ {
+ array.add( index, ((COSObjectable)element).getCOSObject() );
+ }
+ }
+
+ /**
+ * @see List#remove( int )
+ */
+ public Object remove(int index)
+ {
+ if( array.size() > index && array.get( index ) instanceof DualCOSObjectable )
+ {
+ //remove both objects
+ array.remove( index );
+ array.remove( index );
+ }
+ else
+ {
+ array.remove( index );
+ }
+ return actual.remove( index );
+ }
+
+ /**
+ * @see List#indexOf( Object )
+ */
+ public int indexOf(Object o)
+ {
+ return actual.indexOf( o );
+ }
+
+ /**
+ * @see List#lastIndexOf( Object )
+ */
+ public int lastIndexOf(Object o)
+ {
+ return actual.indexOf( o );
+
+ }
+
+ /**
+ * @see List#listIterator()
+ */
+ public ListIterator listIterator()
+ {
+ return actual.listIterator();
+ }
+
+ /**
+ * @see List#listIterator( int )
+ */
+ public ListIterator listIterator(int index)
+ {
+ return actual.listIterator( index );
+ }
+
+ /**
+ * @see List#subList( int, int )
+ */
+ public List subList(int fromIndex, int toIndex)
+ {
+ return actual.subList( fromIndex, toIndex );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/COSDictionaryMap.java b/src/main/java/org/pdfbox/pdmodel/common/COSDictionaryMap.java
new file mode 100644
index 0000000..ca3821d
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/COSDictionaryMap.java
@@ -0,0 +1,278 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import java.io.IOException;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is a Map that will automatically sync the contents to a COSDictionary.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.9 $
+ */
+public class COSDictionaryMap implements Map
+{
+ private COSDictionary map;
+ private Map actuals;
+
+ /**
+ * Constructor for this map.
+ *
+ * @param actualsMap The map with standard java objects as values.
+ * @param dicMap The map with COSBase objects as values.
+ */
+ public COSDictionaryMap( Map actualsMap, COSDictionary dicMap )
+ {
+ actuals = actualsMap;
+ map = dicMap;
+ }
+
+
+ /**
+ * @see java.util.Map#size()
+ */
+ public int size()
+ {
+ return map.size();
+ }
+
+ /**
+ * @see java.util.Map#isEmpty()
+ */
+ public boolean isEmpty()
+ {
+ return size() == 0;
+ }
+
+ /**
+ * @see java.util.Map#containsKey()
+ */
+ public boolean containsKey(Object key)
+ {
+ return map.keyList().contains( key );
+ }
+
+ /**
+ * @see java.util.Map#containsValue()
+ */
+ public boolean containsValue(Object value)
+ {
+ return actuals.containsValue( value );
+ }
+
+ /**
+ * @see java.util.Map#get()
+ */
+ public Object get(Object key)
+ {
+ return actuals.get( key );
+ }
+
+ /**
+ * @see java.util.Map#put()
+ */
+ public Object put(Object key, Object value)
+ {
+ COSObjectable object = (COSObjectable)value;
+
+ map.setItem( COSName.getPDFName( (String)key ), object.getCOSObject() );
+ return actuals.put( key, value );
+ }
+
+ /**
+ * @see java.util.Map#remove()
+ */
+ public Object remove(Object key)
+ {
+ map.removeItem( COSName.getPDFName( (String)key ) );
+ return actuals.remove( key );
+ }
+
+ /**
+ * @see java.util.Map#putAll()
+ */
+ public void putAll(Map t)
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear()
+ {
+ map.clear();
+ actuals.clear();
+ }
+
+ /**
+ * @see java.util.Map#keySet()
+ */
+ public Set keySet()
+ {
+ return actuals.keySet();
+ }
+
+ /**
+ * @see java.util.Map#values()
+ */
+ public Collection values()
+ {
+ return actuals.values();
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set entrySet()
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * @see java.util.Map#equals()
+ */
+ public boolean equals(Object o)
+ {
+ boolean retval = false;
+ if( o instanceof COSDictionaryMap )
+ {
+ COSDictionaryMap other = (COSDictionaryMap)o;
+ retval = other.map.equals( this.map );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get a string representation of this map.
+ *
+ * @return A human readable form of this map.
+ */
+ public String toString()
+ {
+ return actuals.toString();
+ }
+
+ /**
+ * @see java.util.Map#hashCode()
+ */
+ public int hashCode()
+ {
+ return map.hashCode();
+ }
+
+ /**
+ * This will take a map&lt;java.lang.String,org.pdfbox.pdmodel.COSObjectable&gt;
+ * and convert it into a COSDictionary&lt;COSName,COSBase&gt;.
+ *
+ * @param someMap A map containing COSObjectables
+ *
+ * @return A proper COSDictionary
+ */
+ public static COSDictionary convert( Map someMap )
+ {
+ Iterator iter = someMap.keySet().iterator();
+ COSDictionary dic = new COSDictionary();
+ while( iter.hasNext() )
+ {
+ String name = (String)iter.next();
+ COSObjectable object = (COSObjectable)someMap.get( name );
+ dic.setItem( COSName.getPDFName( name ), object.getCOSObject() );
+ }
+ return dic;
+ }
+
+ /**
+ * This will take a COS dictionary and convert it into COSDictionaryMap. All cos
+ * objects will be converted to their primitive form.
+ *
+ * @param map The COS mappings.
+ * @return A standard java map.
+ * @throws IOException If there is an error during the conversion.
+ */
+ public static COSDictionaryMap convertBasicTypesToMap( COSDictionary map ) throws IOException
+ {
+ COSDictionaryMap retval = null;
+ if( map != null )
+ {
+ Map actualMap = new HashMap();
+ Iterator keyIter = map.keyList().iterator();
+ while( keyIter.hasNext() )
+ {
+ COSName key = (COSName)keyIter.next();
+ COSBase cosObj = map.getDictionaryObject( key );
+ Object actualObject = null;
+ if( cosObj instanceof COSString )
+ {
+ actualObject = ((COSString)cosObj).getString();
+ }
+ else if( cosObj instanceof COSInteger )
+ {
+ actualObject = new Integer( ((COSInteger)cosObj).intValue() );
+ }
+ else if( cosObj instanceof COSName )
+ {
+ actualObject = ((COSName)cosObj).getName();
+ }
+ else if( cosObj instanceof COSFloat )
+ {
+ actualObject = new Float( ((COSInteger)cosObj).floatValue() );
+ }
+ else if( cosObj instanceof COSBoolean )
+ {
+ actualObject = ((COSBoolean)cosObj).getValue() ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else
+ {
+ throw new IOException( "Error:unknown type of object to convert:" + cosObj );
+ }
+ actualMap.put( key.getName(), actualObject );
+ }
+ retval = new COSDictionaryMap( actualMap, map );
+ }
+
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/COSObjectable.java b/src/main/java/org/pdfbox/pdmodel/common/COSObjectable.java
new file mode 100644
index 0000000..5bf7bfc
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/COSObjectable.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This is an interface used to get/create the underlying COSObject.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public interface COSObjectable
+{
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject();
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/COSStreamArray.java b/src/main/java/org/pdfbox/pdmodel/common/COSStreamArray.java
new file mode 100644
index 0000000..87dbb6a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/COSStreamArray.java
@@ -0,0 +1,304 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.SequenceInputStream;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.ICOSVisitor;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdfparser.PDFStreamParser;
+
+/**
+ * This will take an array of streams and sequence them together.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class COSStreamArray extends COSStream
+{
+ private COSArray streams;
+
+ /**
+ * The first stream will be used to delegate some of the methods for this
+ * class.
+ */
+ private COSStream firstStream;
+
+ /**
+ * Constructor.
+ *
+ * @param array The array of COSStreams to concatenate together.
+ */
+ public COSStreamArray( COSArray array )
+ {
+ super( new COSDictionary(), null );
+ streams = array;
+ if( array.size() > 0 )
+ {
+ firstStream = (COSStream)array.getObject( 0 );
+ }
+ }
+ /**
+ * This will get the scratch file associated with this stream.
+ *
+ * @return The scratch file where this stream is being stored.
+ */
+ public RandomAccessFile getScratchFile()
+ {
+ return firstStream.getScratchFile();
+ }
+
+ /**
+ * This will get an object from this streams dictionary.
+ *
+ * @param key The key to the object.
+ *
+ * @return The dictionary object with the key or null if one does not exist.
+ */
+ public COSBase getItem( COSName key )
+ {
+ return firstStream.getItem( key );
+ }
+
+ /**
+ * This will get an object from this streams dictionary and dereference it
+ * if necessary.
+ *
+ * @param key The key to the object.
+ *
+ * @return The dictionary object with the key or null if one does not exist.
+ */
+ public COSBase getDictionaryObject( COSName key )
+ {
+ return firstStream.getDictionaryObject( key );
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ String result = "COSStream{}";
+ return result;
+ }
+
+ /**
+ * This will get all the tokens in the stream.
+ *
+ * @return All of the tokens in the stream.
+ *
+ * @throws IOException If there is an error parsing the stream.
+ */
+ public List getStreamTokens() throws IOException
+ {
+ List retval = null;
+ if( streams.size() > 0 )
+ {
+ PDFStreamParser parser = new PDFStreamParser( this );
+ parser.parse();
+ retval = parser.getTokens();
+ }
+ else
+ {
+ retval = new ArrayList();
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the dictionary that is associated with this stream.
+ *
+ * @return the object that is associated with this stream.
+ */
+ public COSDictionary getDictionary()
+ {
+ return firstStream;
+ }
+
+ /**
+ * This will get the stream with all of the filters applied.
+ *
+ * @return the bytes of the physical (endoced) stream
+ *
+ * @throws IOException when encoding/decoding causes an exception
+ */
+ public InputStream getFilteredStream() throws IOException
+ {
+ throw new IOException( "Error: Not allowed to get filtered stream from array of streams." );
+ /**
+ Vector inputStreams = new Vector();
+ byte[] inbetweenStreamBytes = "\n".getBytes();
+
+ for( int i=0;i<streams.size(); i++ )
+ {
+ COSStream stream = (COSStream)streams.getObject( i );
+ }
+
+ return new SequenceInputStream( inputStreams.elements() );
+ **/
+ }
+
+ /**
+ * This will get the logical content stream with none of the filters.
+ *
+ * @return the bytes of the logical (decoded) stream
+ *
+ * @throws IOException when encoding/decoding causes an exception
+ */
+ public InputStream getUnfilteredStream() throws IOException
+ {
+ Vector inputStreams = new Vector();
+ byte[] inbetweenStreamBytes = "\n".getBytes();
+
+ for( int i=0;i<streams.size(); i++ )
+ {
+ COSStream stream = (COSStream)streams.getObject( i );
+ inputStreams.add( stream.getUnfilteredStream() );
+ //handle the case where there is no whitespace in the
+ //between streams in the contents array, without this
+ //it is possible that two operators will get concatenated
+ //together
+ inputStreams.add( new ByteArrayInputStream( inbetweenStreamBytes ) );
+ }
+
+ return new SequenceInputStream( inputStreams.elements() );
+ }
+
+ /**
+ * visitor pattern double dispatch method.
+ *
+ * @param visitor The object to notify when visiting this object.
+ * @return any object, depending on the visitor implementation, or null
+ * @throws COSVisitorException If an error occurs while visiting this object.
+ */
+ public Object accept(ICOSVisitor visitor) throws COSVisitorException
+ {
+ return streams.accept( visitor );
+ }
+
+
+ /**
+ * This will return the filters to apply to the byte stream
+ * the method will return.
+ * - null if no filters are to be applied
+ * - a COSName if one filter is to be applied
+ * - a COSArray containing COSNames if multiple filters are to be applied
+ *
+ * @return the COSBase object representing the filters
+ */
+ public COSBase getFilters()
+ {
+ return firstStream.getFilters();
+ }
+
+ /**
+ * This will create a new stream for which filtered byte should be
+ * written to. You probably don't want this but want to use the
+ * createUnfilteredStream, which is used to write raw bytes to.
+ *
+ * @return A stream that can be written to.
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public OutputStream createFilteredStream() throws IOException
+ {
+ return firstStream.createFilteredStream();
+ }
+
+ /**
+ * This will create a new stream for which filtered byte should be
+ * written to. You probably don't want this but want to use the
+ * createUnfilteredStream, which is used to write raw bytes to.
+ *
+ * @param expectedLength An entry where a length is expected.
+ *
+ * @return A stream that can be written to.
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public OutputStream createFilteredStream( COSBase expectedLength ) throws IOException
+ {
+ return firstStream.createFilteredStream( expectedLength );
+ }
+
+ /**
+ * set the filters to be applied to the stream.
+ *
+ * @param filters The filters to set on this stream.
+ *
+ * @throws IOException If there is an error clearing the old filters.
+ */
+ public void setFilters(COSBase filters) throws IOException
+ {
+ //should this be allowed? Should this
+ //propagate to all streams in the array?
+ firstStream.setFilters( filters );
+ }
+
+ /**
+ * This will create an output stream that can be written to.
+ *
+ * @return An output stream which raw data bytes should be written to.
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public OutputStream createUnfilteredStream() throws IOException
+ {
+ return firstStream.createUnfilteredStream();
+ }
+
+ /**
+ * Appends a new stream to the array that represents this object's stream.
+ *
+ * @param streamToAppend The stream to append.
+ */
+ public void appendStream(COSStream streamToAppend)
+ {
+ streams.add(streamToAppend);
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/DualCOSObjectable.java b/src/main/java/org/pdfbox/pdmodel/common/DualCOSObjectable.java
new file mode 100644
index 0000000..9c70d15
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/DualCOSObjectable.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This is an interface to represent a PDModel object that holds two COS objects.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public interface DualCOSObjectable
+{
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getFirstCOSObject();
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getSecondCOSObject();
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDMatrix.java b/src/main/java/org/pdfbox/pdmodel/common/PDMatrix.java
new file mode 100644
index 0000000..ffee9e8
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDMatrix.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+
+/**
+ * This class will be used for matrix manipulation.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDMatrix implements Cloneable, COSObjectable
+{
+ private COSArray matrix;
+
+ /**
+ * Constructor.
+ */
+ public PDMatrix()
+ {
+ matrix = new COSArray();
+ matrix.add( new COSFloat( 1.0f ) );
+ matrix.add( new COSFloat( 0.0f ) );
+ matrix.add( new COSFloat( 0.0f ) );
+ matrix.add( new COSFloat( 0.0f ) );
+ matrix.add( new COSFloat( 1.0f ) );
+ matrix.add( new COSFloat( 0.0f ) );
+ matrix.add( new COSFloat( 0.0f ) );
+ matrix.add( new COSFloat( 0.0f ) );
+ matrix.add( new COSFloat( 1.0f ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param array The array that describes the matrix.
+ */
+ public PDMatrix( COSArray array )
+ {
+ matrix = array;
+ }
+
+ /**
+ * This will get the underlying array value.
+ *
+ * @return The cos object that this object wraps.
+ */
+ public COSArray getCOSArray()
+ {
+ return matrix;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return matrix;
+ }
+
+
+ /**
+ * This will get a matrix value at some point.
+ *
+ * @param row The row to get the value from.
+ * @param column The column to get the value from.
+ *
+ * @return The value at the row/column position.
+ */
+ public float getValue( int row, int column )
+ {
+ return ((COSNumber)matrix.get( row*3 + column )).floatValue();
+ }
+
+ /**
+ * This will set a value at a position.
+ *
+ * @param row The row to set the value at.
+ * @param column the column to set the value at.
+ * @param value The value to set at the position.
+ */
+ public void setValue( int row, int column, float value )
+ {
+ matrix.set( row*3+column, new COSFloat( value ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDMemoryStream.java b/src/main/java/org/pdfbox/pdmodel/common/PDMemoryStream.java
new file mode 100644
index 0000000..61cbfbf
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDMemoryStream.java
@@ -0,0 +1,284 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
+
+/**
+ * A PDStream represents a stream in a PDF document. Streams are tied to a single
+ * PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDMemoryStream extends PDStream
+{
+ private byte[] data;
+
+ /**
+ * This will create a new PDStream object.
+ *
+ * @param buffer The data for this in memory stream.
+ */
+ public PDMemoryStream( byte[] buffer )
+ {
+ data = buffer;
+ }
+
+
+
+ /**
+ * If there are not compression filters on the current stream then this
+ * will add a compression filter, flate compression for example.
+ */
+ public void addCompression()
+ {
+ //no compression to add
+ }
+
+
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ throw new UnsupportedOperationException( "not supported for memory stream" );
+ }
+
+ /**
+ * This will get a stream that can be written to.
+ *
+ * @return An output stream to write data to.
+ *
+ * @throws IOException If an IO error occurs during writing.
+ */
+ public OutputStream createOutputStream() throws IOException
+ {
+ throw new UnsupportedOperationException( "not supported for memory stream" );
+ }
+
+ /**
+ * This will get a stream that can be read from.
+ *
+ * @return An input stream that can be read from.
+ *
+ * @throws IOException If an IO error occurs during reading.
+ */
+ public InputStream createInputStream() throws IOException
+ {
+ return new ByteArrayInputStream( data );
+ }
+
+ /**
+ * This will get a stream with some filters applied but not others. This is useful
+ * when doing images, ie filters = [flate,dct], we want to remove flate but leave dct
+ *
+ * @param stopFilters A list of filters to stop decoding at.
+ * @return A stream with decoded data.
+ * @throws IOException If there is an error processing the stream.
+ */
+ public InputStream getPartiallyFilteredStream( List stopFilters ) throws IOException
+ {
+ return createInputStream();
+ }
+
+ /**
+ * Get the cos stream associated with this object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSStream getStream()
+ {
+ throw new UnsupportedOperationException( "not supported for memory stream" );
+ }
+
+ /**
+ * This will get the length of the filtered/compressed stream. This is readonly in the
+ * PD Model and will be managed by this class.
+ *
+ * @return The length of the filtered stream.
+ */
+ public int getLength()
+ {
+ return data.length;
+ }
+
+ /**
+ * This will get the list of filters that are associated with this stream. Or
+ * null if there are none.
+ * @return A list of all encoding filters to apply to this stream.
+ */
+ public List getFilters()
+ {
+ return null;
+ }
+
+ /**
+ * This will set the filters that are part of this stream.
+ *
+ * @param filters The filters that are part of this stream.
+ */
+ public void setFilters( List filters )
+ {
+ throw new UnsupportedOperationException( "not supported for memory stream" );
+ }
+
+ /**
+ * Get the list of decode parameters. Each entry in the list will refer to
+ * an entry in the filters list.
+ *
+ * @return The list of decode parameters.
+ *
+ * @throws IOException if there is an error retrieving the parameters.
+ */
+ public List getDecodeParams() throws IOException
+ {
+ return null;
+ }
+
+ /**
+ * This will set the list of decode params.
+ *
+ * @param decodeParams The list of decode params.
+ */
+ public void setDecodeParams( List decodeParams )
+ {
+ //do nothing
+ }
+
+ /**
+ * This will get the file specification for this stream. This is only
+ * required for external files.
+ *
+ * @return The file specification.
+ */
+ public PDFileSpecification getFile()
+ {
+ return null;
+ }
+
+ /**
+ * Set the file specification.
+ * @param f The file specification.
+ */
+ public void setFile( PDFileSpecification f )
+ {
+ //do nothing.
+ }
+
+ /**
+ * This will get the list of filters that are associated with this stream. Or
+ * null if there are none.
+ * @return A list of all encoding filters to apply to this stream.
+ */
+ public List getFileFilters()
+ {
+ return null;
+ }
+
+ /**
+ * This will set the filters that are part of this stream.
+ *
+ * @param filters The filters that are part of this stream.
+ */
+ public void setFileFilters( List filters )
+ {
+ //do nothing.
+ }
+
+ /**
+ * Get the list of decode parameters. Each entry in the list will refer to
+ * an entry in the filters list.
+ *
+ * @return The list of decode parameters.
+ *
+ * @throws IOException if there is an error retrieving the parameters.
+ */
+ public List getFileDecodeParams() throws IOException
+ {
+ return null;
+ }
+
+ /**
+ * This will set the list of decode params.
+ *
+ * @param decodeParams The list of decode params.
+ */
+ public void setFileDecodeParams( List decodeParams )
+ {
+ //do nothing
+ }
+
+ /**
+ * This will copy the stream into a byte array.
+ *
+ * @return The byte array of the filteredStream
+ * @throws IOException When getFilteredStream did not work
+ */
+ public byte[] getByteArray() throws IOException
+ {
+ return data;
+ }
+
+ /**
+ * Get the metadata that is part of the document catalog. This will
+ * return null if there is no meta data for this object.
+ *
+ * @return The metadata for this object.
+ */
+ public PDMetadata getMetadata()
+ {
+ return null;
+ }
+
+ /**
+ * Set the metadata for this object. This can be null.
+ *
+ * @param meta The meta data for this object.
+ */
+ public void setMetadata( PDMetadata meta )
+ {
+ //do nothing
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDMetadata.java b/src/main/java/org/pdfbox/pdmodel/common/PDMetadata.java
new file mode 100644
index 0000000..77be580
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDMetadata.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+/**
+ * This class represents metadata for various objects in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDMetadata extends PDStream
+{
+
+ /**
+ * This will create a new PDMetadata object.
+ *
+ * @param document The document that the stream will be part of.
+ */
+ public PDMetadata( PDDocument document )
+ {
+ super( document );
+ getStream().setName( "Type", "Metadata" );
+ getStream().setName( "Subtype", "XML" );
+ }
+
+ /**
+ * Constructor. Reads all data from the input stream and embeds it into the
+ * document, this will close the InputStream.
+ *
+ * @param doc The document that will hold the stream.
+ * @param str The stream parameter.
+ * @param filtered True if the stream already has a filter applied.
+ * @throws IOException If there is an error creating the stream in the document.
+ */
+ public PDMetadata( PDDocument doc, InputStream str, boolean filtered ) throws IOException
+ {
+ super( doc, str, filtered );
+ getStream().setName( "Type", "Metadata" );
+ getStream().setName( "Subtype", "XML" );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param str The stream parameter.
+ */
+ public PDMetadata( COSStream str )
+ {
+ super( str );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDNameTreeNode.java b/src/main/java/org/pdfbox/pdmodel/common/PDNameTreeNode.java
new file mode 100644
index 0000000..4a79d10
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDNameTreeNode.java
@@ -0,0 +1,337 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSString;
+
+/**
+ * This class represends a PDF Name tree. See the PDF Reference 1.5 section 3.8.5
+ * for more details.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class PDNameTreeNode implements COSObjectable
+{
+ private COSDictionary node;
+ private Class valueType = null;
+
+ /**
+ * Constructor.
+ *
+ * @param valueClass The PD Model type of object that is the value.
+ */
+ public PDNameTreeNode( Class valueClass )
+ {
+ node = new COSDictionary();
+ valueType = valueClass;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dict The dictionary that holds the name information.
+ * @param valueClass The PD Model type of object that is the value.
+ */
+ public PDNameTreeNode( COSDictionary dict, Class valueClass )
+ {
+ node = dict;
+ valueType = valueClass;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return node;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return node;
+ }
+
+ /**
+ * Return the children of this node. This list will contain PDNameTreeNode objects.
+ *
+ * @return The list of children or null if there are no children.
+ */
+ public List getKids()
+ {
+
+ List retval = null;
+ COSArray kids = (COSArray)node.getDictionaryObject( "Kids" );
+ if( kids != null )
+ {
+ List pdObjects = new ArrayList();
+ for( int i=0; i<kids.size(); i++ )
+ {
+ pdObjects.add( createChildNode( (COSDictionary)kids.getObject(i) ) );
+ }
+ retval = new COSArrayList(pdObjects,kids);
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the children of this named tree.
+ *
+ * @param kids The children of this named tree.
+ */
+ public void setKids( List kids )
+ {
+ node.setItem( "Kids", COSArrayList.converterToCOSArray( kids ) );
+ }
+
+ /**
+ * The name to retrieve.
+ *
+ * @param name The name in the tree.
+ *
+ * @return The value of the name in the tree.
+ *
+ * @throws IOException If an there is a problem creating the destinations.
+ */
+ public Object getValue( String name ) throws IOException
+ {
+ Object retval = null;
+ Map names = getNames();
+ if( names != null )
+ {
+ retval = names.get( name );
+ }
+ else
+ {
+ List kids = getKids();
+ for( int i=0; i<kids.size() && retval == null; i++ )
+ {
+ PDNameTreeNode childNode = (PDNameTreeNode)kids.get( i );
+ if( childNode.getLowerLimit().compareTo( name ) <= 0 &&
+ childNode.getUpperLimit().compareTo( name ) >= 0 )
+ {
+ retval = childNode.getValue( name );
+ }
+ }
+ }
+ return retval;
+ }
+
+
+ /**
+ * This will return a map of names. The key will be a java.lang.String the value will
+ * depend on where this class is being used.
+ *
+ * @return A map of cos objects.
+ *
+ * @throws IOException If there is an error while creating the sub types.
+ */
+ public Map getNames() throws IOException
+ {
+ Map names = null;
+ COSArray namesArray = (COSArray)node.getDictionaryObject( "Names" );
+ if( namesArray != null )
+ {
+ names = new HashMap();
+ for( int i=0; i<namesArray.size(); i+=2 )
+ {
+ COSString key = (COSString)namesArray.getObject(i);
+ COSBase cosValue = namesArray.getObject( i+1 );
+ Object pdValue = convertCOSToPD( cosValue );
+
+ names.put( key.getString(), pdValue );
+ }
+ names = Collections.unmodifiableMap(names);
+ }
+
+ return names;
+ }
+
+ /**
+ * Method to convert the COS value in the name tree to the PD Model object. The
+ * default implementation will simply use reflection to create the correct object
+ * type. Subclasses can do whatever they want.
+ *
+ * @param base The COS object to convert.
+ * @return The converted PD Model object.
+ * @throws IOException If there is an error during creation.
+ */
+ protected Object convertCOSToPD( COSBase base ) throws IOException
+ {
+ Object retval = null;
+ try
+ {
+ Constructor ctor = valueType.getConstructor( new Class[] { base.getClass() } );
+ retval = ctor.newInstance( new Object[] { base } );
+ }
+ catch( Throwable t )
+ {
+ throw new IOException( "Error while trying to create value in named tree:" + t.getMessage());
+
+ }
+ return retval;
+ }
+
+ /**
+ * Create a child node object.
+ *
+ * @param dic The dictionary for the child node object to refer to.
+ * @return The new child node object.
+ */
+ protected PDNameTreeNode createChildNode( COSDictionary dic )
+ {
+ return new PDNameTreeNode(dic,valueType);
+ }
+
+ /**
+ * Set the names of for this node. The keys should be java.lang.String and the
+ * values must be a COSObjectable. This method will set the appropriate upper and lower
+ * limits based on the keys in the map.
+ *
+ * @param names The map of names to objects.
+ */
+ public void setNames( Map names )
+ {
+ if( names == null )
+ {
+ node.setItem( "Names", (COSObjectable)null );
+ node.setItem( "Limits", (COSObjectable)null);
+ }
+ else
+ {
+ List keys = new ArrayList( names.keySet() );
+ Collections.sort( keys );
+ COSArray array = new COSArray();
+ for( int i=0; i<keys.size(); i++ )
+ {
+ String key = (String)keys.get(i);
+ array.add( new COSString( key ) );
+ COSObjectable obj = (COSObjectable)names.get( key );
+ array.add( obj );
+ }
+ String lower = null;
+ String upper = null;
+ if( keys.size() > 0 )
+ {
+ lower = (String)keys.get( 0 );
+ upper = (String)keys.get( keys.size()-1 );
+ }
+ setUpperLimit( upper );
+ setLowerLimit( lower );
+ node.setItem( "Names", array );
+ }
+ }
+
+ /**
+ * Get the highest value for a key in the name map.
+ *
+ * @return The highest value for a key in the map.
+ */
+ public String getUpperLimit()
+ {
+ String retval = null;
+ COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+ if( arr != null )
+ {
+ retval = arr.getString( 1 );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the highest value for the key in the map.
+ *
+ * @param upper The new highest value for a key in the map.
+ */
+ private void setUpperLimit( String upper )
+ {
+ COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+ if( arr == null )
+ {
+ arr = new COSArray();
+ arr.add( null );
+ arr.add( null );
+ }
+ arr.setString( 1, upper );
+ }
+
+ /**
+ * Get the lowest value for a key in the name map.
+ *
+ * @return The lowest value for a key in the map.
+ */
+ public String getLowerLimit()
+ {
+ String retval = null;
+ COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+ if( arr != null )
+ {
+ retval = arr.getString( 0 );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the lowest value for the key in the map.
+ *
+ * @param lower The new lowest value for a key in the map.
+ */
+ private void setLowerLimit( String lower )
+ {
+ COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+ if( arr == null )
+ {
+ arr = new COSArray();
+ arr.add( null );
+ arr.add( null );
+ }
+ arr.setString( 0, lower );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDNamedTextStream.java b/src/main/java/org/pdfbox/pdmodel/common/PDNamedTextStream.java
new file mode 100644
index 0000000..2321683
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDNamedTextStream.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * A named text stream is a combination of a name and a PDTextStream object. This
+ * is used in name trees.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDNamedTextStream implements DualCOSObjectable
+{
+ private COSName streamName;
+ private PDTextStream stream;
+
+ /**
+ * Constructor.
+ */
+ public PDNamedTextStream()
+ {
+ //default constructor
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name The name of the stream.
+ * @param str The stream.
+ */
+ public PDNamedTextStream( COSName name, COSBase str )
+ {
+ streamName = name;
+ stream = PDTextStream.createTextStream( str );
+ }
+
+ /**
+ * The name of the named text stream.
+ *
+ * @return The stream name.
+ */
+ public String getName()
+ {
+ String name = null;
+ if( streamName != null )
+ {
+ name = streamName.getName();
+ }
+ return name;
+ }
+
+ /**
+ * This will set the name of the named text stream.
+ *
+ * @param name The name of the named text stream.
+ */
+ public void setName( String name )
+ {
+ streamName = COSName.getPDFName( name );
+ }
+
+ /**
+ * This will get the stream.
+ *
+ * @return The stream associated with this name.
+ */
+ public PDTextStream getStream()
+ {
+ return stream;
+ }
+
+ /**
+ * This will set the stream.
+ *
+ * @param str The stream associated with this name.
+ */
+ public void setStream( PDTextStream str )
+ {
+ stream = str;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getFirstCOSObject()
+ {
+ return streamName;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getSecondCOSObject()
+ {
+ COSBase retval = null;
+ if( stream != null )
+ {
+ retval = stream.getCOSObject();
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDObjectStream.java b/src/main/java/org/pdfbox/pdmodel/common/PDObjectStream.java
new file mode 100644
index 0000000..3d31c1a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDObjectStream.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+
+
+/**
+ * A PDStream represents a stream in a PDF document. Streams are tied to a single
+ * PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDObjectStream extends PDStream
+{
+
+ /**
+ * Constructor.
+ *
+ * @param str The stream parameter.
+ */
+ public PDObjectStream( COSStream str )
+ {
+ super( str );
+ }
+
+ /**
+ * This will create a new PDStream object.
+ *
+ * @param document The document that the stream will be part of.
+ * @return A new stream object.
+ */
+ public static PDObjectStream createStream( PDDocument document )
+ {
+ COSStream cosStream = new COSStream( document.getDocument().getScratchFile() );
+ PDObjectStream strm = new PDObjectStream( cosStream );
+ strm.getStream().setName( "Type", "ObjStm" );
+ return strm;
+ }
+
+ /**
+ * Get the type of this object, should always return "ObjStm".
+ *
+ * @return The type of this object.
+ */
+ public String getType()
+ {
+ return getStream().getNameAsString( "Type" );
+ }
+
+ /**
+ * Get the number of compressed object.
+ *
+ * @return The number of compressed objects.
+ */
+ public int getNumberOfObjects()
+ {
+ return getStream().getInt( "N", 0 );
+ }
+
+ /**
+ * Set the number of objects.
+ *
+ * @param n The new number of objects.
+ */
+ public void setNumberOfObjects( int n )
+ {
+ getStream().setInt( "N", n );
+ }
+
+ /**
+ * The byte offset (in the decoded stream) of the first compressed object.
+ *
+ * @return The byte offset to the first object.
+ */
+ public int getFirstByteOffset()
+ {
+ return getStream().getInt( "First", 0 );
+ }
+
+ /**
+ * The byte offset (in the decoded stream) of the first compressed object.
+ *
+ * @param n The byte offset to the first object.
+ */
+ public void setFirstByteOffset( int n )
+ {
+ getStream().setInt( "First", n );
+ }
+
+ /**
+ * A reference to an object stream, of which the current object stream is
+ * considered an extension.
+ *
+ * @return The object that this stream is an extension.
+ */
+ public PDObjectStream getExtends()
+ {
+ PDObjectStream retval = null;
+ COSStream stream = (COSStream)getStream().getDictionaryObject( "Extends" );
+ if( stream != null )
+ {
+ retval = new PDObjectStream( stream );
+ }
+ return retval;
+
+ }
+
+ /**
+ * A reference to an object stream, of which the current object stream is
+ * considered an extension.
+ *
+ * @param stream The object stream extension.
+ */
+ public void setExtends( PDObjectStream stream )
+ {
+ getStream().setItem( "Extends", stream );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDRange.java b/src/main/java/org/pdfbox/pdmodel/common/PDRange.java
new file mode 100644
index 0000000..2493d87
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDRange.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+
+/**
+ * This class will be used to signify a range. a(min) <= a* <= a(max)
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDRange implements COSObjectable
+{
+ private COSArray rangeArray;
+ private int startingIndex;
+
+ /**
+ * Constructor with an initial range of 0..1.
+ */
+ public PDRange()
+ {
+ rangeArray = new COSArray();
+ rangeArray.add( new COSFloat( 0.0f ) );
+ rangeArray.add( new COSFloat( 1.0f ) );
+ startingIndex = 0;
+ }
+
+ /**
+ * Constructor assumes a starting index of 0.
+ *
+ * @param range The array that describes the range.
+ */
+ public PDRange( COSArray range )
+ {
+ rangeArray = range;
+ }
+
+ /**
+ * Constructor with an index into an array. Because some arrays specify
+ * multiple ranges ie [ 0,1, 0,2, 2,3 ] It is convenient for this
+ * class to take an index into an array. So if you want this range to
+ * represent 0,2 in the above example then you would say <code>new PDRange( array, 2 )</code>.
+ *
+ * @param range The array that describes the index
+ * @param index The index into the array for the start of the range.
+ */
+ public PDRange( COSArray range, int index )
+ {
+ rangeArray = range;
+ startingIndex = index;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return rangeArray;
+ }
+
+ /**
+ * This will get the underlying array value.
+ *
+ * @return The cos object that this object wraps.
+ */
+ public COSArray getCOSArray()
+ {
+ return rangeArray;
+ }
+
+ /**
+ * This will get the minimum value of the range.
+ *
+ * @return The min value.
+ */
+ public float getMin()
+ {
+ COSNumber min = (COSNumber)rangeArray.getObject( startingIndex );
+ return min.floatValue();
+ }
+
+ /**
+ * This will set the minimum value for the range.
+ *
+ * @param min The new minimum for the range.
+ */
+ public void setMin( float min )
+ {
+ rangeArray.set( startingIndex, new COSFloat( min ) );
+ }
+
+ /**
+ * This will get the maximum value of the range.
+ *
+ * @return The max value.
+ */
+ public float getMax()
+ {
+ COSNumber max = (COSNumber)rangeArray.getObject( startingIndex+1 );
+ return max.floatValue();
+ }
+
+ /**
+ * This will set the maximum value for the range.
+ *
+ * @param max The new maximum for the range.
+ */
+ public void setMax( float max )
+ {
+ rangeArray.set( startingIndex+1, new COSFloat( max ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDRectangle.java b/src/main/java/org/pdfbox/pdmodel/common/PDRectangle.java
new file mode 100644
index 0000000..b9f3267
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDRectangle.java
@@ -0,0 +1,295 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.util.BoundingBox;
+
+import java.awt.Dimension;
+
+/**
+ * This represents a rectangle in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.10 $
+ */
+public class PDRectangle implements COSObjectable
+{
+ private COSArray rectArray;
+
+ /**
+ * Constructor.
+ *
+ * Initializes to 0,0,0,0
+ */
+ public PDRectangle()
+ {
+ rectArray = new COSArray();
+ rectArray.add( new COSFloat( 0.0f ) );
+ rectArray.add( new COSFloat( 0.0f ) );
+ rectArray.add( new COSFloat( 0.0f ) );
+ rectArray.add( new COSFloat( 0.0f ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param width The width of the rectangle.
+ * @param height The height of the rectangle.
+ */
+ public PDRectangle( float width, float height )
+ {
+ rectArray = new COSArray();
+ rectArray.add( new COSFloat( 0.0f ) );
+ rectArray.add( new COSFloat( 0.0f ) );
+ rectArray.add( new COSFloat( width ) );
+ rectArray.add( new COSFloat( height ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param box The non PD bouding box.
+ */
+ public PDRectangle( BoundingBox box )
+ {
+ rectArray = new COSArray();
+ rectArray.add( new COSFloat( box.getLowerLeftX() ) );
+ rectArray.add( new COSFloat( box.getLowerLeftY() ) );
+ rectArray.add( new COSFloat( box.getUpperRightX() ) );
+ rectArray.add( new COSFloat( box.getUpperRightY() ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param array An array of numbers as specified in the PDF Reference for a rectangle type.
+ */
+ public PDRectangle( COSArray array )
+ {
+ rectArray = array;
+ }
+
+ /**
+ * Method to determine if the x/y point is inside this rectangle.
+ * @param x The x-coordinate to test.
+ * @param y The y-coordinate to test.
+ * @return True if the point is inside this rectangle.
+ */
+ public boolean contains( float x, float y )
+ {
+ float llx = getLowerLeftX();
+ float urx = getUpperRightX();
+ float lly = getLowerLeftY();
+ float ury = getUpperRightY();
+ return x >= llx && x <= urx &&
+ y >= lly && y <= ury;
+ }
+
+ /**
+ * This will create a translated rectangle based off of this rectangle, such
+ * that the new rectangle retains the same dimensions(height/width), but the
+ * lower left x,y values are zero. <br />
+ * 100, 100, 400, 400 (llx, lly, urx, ury ) <br />
+ * will be translated to 0,0,300,300
+ *
+ * @return A new rectangle that has been translated back to the origin.
+ */
+ public PDRectangle createRetranslatedRectangle()
+ {
+ PDRectangle retval = new PDRectangle();
+ retval.setUpperRightX( getWidth() );
+ retval.setUpperRightY( getHeight() );
+ return retval;
+ }
+
+ /**
+ * This will get the underlying array for this rectangle.
+ *
+ * @return The cos array.
+ */
+ public COSArray getCOSArray()
+ {
+ return rectArray;
+ }
+
+ /**
+ * This will get the lower left x coordinate.
+ *
+ * @return The lower left x.
+ */
+ public float getLowerLeftX()
+ {
+ return ((COSNumber)rectArray.get(0)).floatValue();
+ }
+
+ /**
+ * This will set the lower left x coordinate.
+ *
+ * @param value The lower left x.
+ */
+ public void setLowerLeftX(float value)
+ {
+ rectArray.set(0, new COSFloat( value ) );
+ }
+
+ /**
+ * This will get the lower left y coordinate.
+ *
+ * @return The lower left y.
+ */
+ public float getLowerLeftY()
+ {
+ return ((COSNumber)rectArray.get(1)).floatValue();
+ }
+
+ /**
+ * This will set the lower left y coordinate.
+ *
+ * @param value The lower left y.
+ */
+ public void setLowerLeftY(float value)
+ {
+ rectArray.set(1, new COSFloat( value ) );
+ }
+
+ /**
+ * This will get the upper right x coordinate.
+ *
+ * @return The upper right x .
+ */
+ public float getUpperRightX()
+ {
+ return ((COSNumber)rectArray.get(2)).floatValue();
+ }
+
+ /**
+ * This will set the upper right x coordinate.
+ *
+ * @param value The upper right x .
+ */
+ public void setUpperRightX(float value)
+ {
+ rectArray.set(2, new COSFloat( value ) );
+ }
+
+ /**
+ * This will get the upper right y coordinate.
+ *
+ * @return The upper right y.
+ */
+ public float getUpperRightY()
+ {
+ return ((COSNumber)rectArray.get(3)).floatValue();
+ }
+
+ /**
+ * This will set the upper right y coordinate.
+ *
+ * @param value The upper right y.
+ */
+ public void setUpperRightY(float value)
+ {
+ rectArray.set(3, new COSFloat( value ) );
+ }
+
+ /**
+ * This will get the width of this rectangle as calculated by
+ * upperRightX - lowerLeftX.
+ *
+ * @return The width of this rectangle.
+ */
+ public float getWidth()
+ {
+ return getUpperRightX() - getLowerLeftX();
+ }
+
+ /**
+ * This will get the height of this rectangle as calculated by
+ * upperRightY - lowerLeftY.
+ *
+ * @return The height of this rectangle.
+ */
+ public float getHeight()
+ {
+ return getUpperRightY() - getLowerLeftY();
+ }
+
+ /**
+ * A convenience method to create a dimension object for AWT operations.
+ *
+ * @return A dimension that matches the width and height of this rectangle.
+ */
+ public Dimension createDimension()
+ {
+ return new Dimension( (int)getWidth(), (int)getHeight() );
+ }
+
+ /**
+ * This will move the rectangle the given relative amount.
+ *
+ * @param horizontalAmount positive values will move rectangle to the right, negative's to the left.
+ * @param verticalAmount positive values will move the rectangle up, negative's down.
+ */
+ public void move(float horizontalAmount, float verticalAmount)
+ {
+ setUpperRightX(getUpperRightX() + horizontalAmount);
+ setLowerLeftX(getLowerLeftX() + horizontalAmount);
+ setUpperRightY(getUpperRightY() + verticalAmount);
+ setLowerLeftY(getLowerLeftY() + verticalAmount);
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return rectArray;
+ }
+
+
+ /**
+ * This will return a string representation of this rectangle.
+ *
+ * @return This object as a string.
+ */
+ public String toString()
+ {
+ return "[" + getLowerLeftX() + "," + getLowerLeftY() + "," +
+ getUpperRightX() + "," + getUpperRightY() +"]";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDStream.java b/src/main/java/org/pdfbox/pdmodel/common/PDStream.java
new file mode 100644
index 0000000..74398b2
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDStream.java
@@ -0,0 +1,538 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.filter.Filter;
+import org.pdfbox.filter.FilterManager;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
+
+/**
+ * A PDStream represents a stream in a PDF document. Streams are tied to a single
+ * PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.14 $
+ */
+public class PDStream implements COSObjectable
+{
+ private COSStream stream;
+
+ /**
+ * This will create a new PDStream object.
+ */
+ protected PDStream()
+ {
+ //should only be called by PDMemoryStream
+ }
+
+ /**
+ * This will create a new PDStream object.
+ *
+ * @param document The document that the stream will be part of.
+ */
+ public PDStream( PDDocument document )
+ {
+ stream = new COSStream( document.getDocument().getScratchFile() );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param str The stream parameter.
+ */
+ public PDStream( COSStream str )
+ {
+ stream = str;
+ }
+
+ /**
+ * Constructor. Reads all data from the input stream and embeds it into the
+ * document, this will close the InputStream.
+ *
+ * @param doc The document that will hold the stream.
+ * @param str The stream parameter.
+ * @throws IOException If there is an error creating the stream in the document.
+ */
+ public PDStream( PDDocument doc, InputStream str ) throws IOException
+ {
+ this( doc, str, false );
+ }
+
+ /**
+ * Constructor. Reads all data from the input stream and embeds it into the
+ * document, this will close the InputStream.
+ *
+ * @param doc The document that will hold the stream.
+ * @param str The stream parameter.
+ * @param filtered True if the stream already has a filter applied.
+ * @throws IOException If there is an error creating the stream in the document.
+ */
+ public PDStream( PDDocument doc, InputStream str, boolean filtered ) throws IOException
+ {
+ OutputStream output = null;
+ try
+ {
+ stream = new COSStream( doc.getDocument().getScratchFile() );
+ if( filtered )
+ {
+ output = stream.createFilteredStream();
+ }
+ else
+ {
+ output = stream.createUnfilteredStream();
+ }
+ byte[] buffer = new byte[ 1024 ];
+ int amountRead = -1;
+ while( (amountRead = str.read(buffer)) != -1 )
+ {
+ output.write( buffer, 0, amountRead );
+ }
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.close();
+ }
+ if( str != null )
+ {
+ str.close();
+ }
+ }
+ }
+
+ /**
+ * If there are not compression filters on the current stream then this
+ * will add a compression filter, flate compression for example.
+ */
+ public void addCompression()
+ {
+ List filters = getFilters();
+ if( filters == null )
+ {
+ filters = new ArrayList();
+ filters.add( COSName.FLATE_DECODE );
+ setFilters( filters );
+ }
+ }
+
+ /**
+ * Create a pd stream from either a regular COSStream on a COSArray of cos streams.
+ * @param base Either a COSStream or COSArray.
+ * @return A PDStream or null if base is null.
+ * @throws IOException If there is an error creating the PDStream.
+ */
+ public static PDStream createFromCOS( COSBase base ) throws IOException
+ {
+ PDStream retval = null;
+ if( base instanceof COSStream )
+ {
+ retval = new PDStream( (COSStream)base );
+ }
+ else if( base instanceof COSArray )
+ {
+ retval = new PDStream( new COSStreamArray( (COSArray)base ) );
+ }
+ else
+ {
+ if( base != null )
+ {
+ throw new IOException( "Contents are unknown type:" + base.getClass().getName() );
+ }
+ }
+ return retval;
+ }
+
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return stream;
+ }
+
+ /**
+ * This will get a stream that can be written to.
+ *
+ * @return An output stream to write data to.
+ *
+ * @throws IOException If an IO error occurs during writing.
+ */
+ public OutputStream createOutputStream() throws IOException
+ {
+ return stream.createUnfilteredStream();
+ }
+
+ /**
+ * This will get a stream that can be read from.
+ *
+ * @return An input stream that can be read from.
+ *
+ * @throws IOException If an IO error occurs during reading.
+ */
+ public InputStream createInputStream() throws IOException
+ {
+ return stream.getUnfilteredStream();
+ }
+
+ /**
+ * This will get a stream with some filters applied but not others. This is useful
+ * when doing images, ie filters = [flate,dct], we want to remove flate but leave dct
+ *
+ * @param stopFilters A list of filters to stop decoding at.
+ * @return A stream with decoded data.
+ * @throws IOException If there is an error processing the stream.
+ */
+ public InputStream getPartiallyFilteredStream( List stopFilters ) throws IOException
+ {
+ FilterManager manager = stream.getFilterManager();
+ InputStream is = stream.getFilteredStream();
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ List filters = getFilters();
+ Iterator iter = filters.iterator();
+ String nextFilter = null;
+ boolean done = false;
+ while( iter.hasNext() && !done )
+ {
+ os.reset();
+ nextFilter = (String)iter.next();
+ if( stopFilters.contains( nextFilter ) )
+ {
+ done = true;
+ }
+ else
+ {
+ Filter filter = manager.getFilter( COSName.getPDFName(nextFilter) );
+ filter.decode( is, os, stream );
+ is = new ByteArrayInputStream( os.toByteArray() );
+ }
+ }
+ return is;
+ }
+
+ /**
+ * Get the cos stream associated with this object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSStream getStream()
+ {
+ return stream;
+ }
+
+ /**
+ * This will get the length of the filtered/compressed stream. This is readonly in the
+ * PD Model and will be managed by this class.
+ *
+ * @return The length of the filtered stream.
+ */
+ public int getLength()
+ {
+ return stream.getInt( "Length", 0 );
+ }
+
+ /**
+ * This will get the list of filters that are associated with this stream. Or
+ * null if there are none.
+ * @return A list of all encoding filters to apply to this stream.
+ */
+ public List getFilters()
+ {
+ List retval = null;
+ COSBase filters = stream.getFilters();
+ if( filters instanceof COSName )
+ {
+ COSName name = (COSName)filters;
+ retval = new COSArrayList( name.getName(), name, stream, "Filter" );
+ }
+ else if( filters instanceof COSArray )
+ {
+ retval = COSArrayList.convertCOSNameCOSArrayToList( (COSArray)filters );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the filters that are part of this stream.
+ *
+ * @param filters The filters that are part of this stream.
+ */
+ public void setFilters( List filters )
+ {
+ COSBase obj = COSArrayList.convertStringListToCOSNameCOSArray( filters );
+ stream.setItem( "Filter", obj );
+ }
+
+ /**
+ * Get the list of decode parameters. Each entry in the list will refer to
+ * an entry in the filters list.
+ *
+ * @return The list of decode parameters.
+ *
+ * @throws IOException if there is an error retrieving the parameters.
+ */
+ public List getDecodeParams() throws IOException
+ {
+ List retval = null;
+
+ COSBase dp = stream.getDictionaryObject( "DecodeParms" );
+ if( dp == null )
+ {
+ //See PDF Ref 1.5 implementation note 7, the DP is sometimes used instead.
+ dp = stream.getDictionaryObject( "DP" );
+ }
+ if( dp instanceof COSDictionary )
+ {
+ Map map = COSDictionaryMap.convertBasicTypesToMap( (COSDictionary)dp );
+ retval = new COSArrayList(map, dp, stream, "DecodeParams" );
+ }
+ else if( dp instanceof COSArray )
+ {
+ COSArray array = (COSArray)dp;
+ List actuals = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ actuals.add(
+ COSDictionaryMap.convertBasicTypesToMap(
+ (COSDictionary)array.getObject( i ) ) );
+ }
+ retval = new COSArrayList(actuals, array);
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the list of decode params.
+ *
+ * @param decodeParams The list of decode params.
+ */
+ public void setDecodeParams( List decodeParams )
+ {
+ stream.setItem(
+ "DecodeParams", COSArrayList.converterToCOSArray( decodeParams ) );
+ }
+
+ /**
+ * This will get the file specification for this stream. This is only
+ * required for external files.
+ *
+ * @return The file specification.
+ */
+ public PDFileSpecification getFile()
+ {
+ COSBase f = stream.getDictionaryObject( "F" );
+ PDFileSpecification retval = PDFileSpecification.createFS( f );
+ return retval;
+ }
+
+ /**
+ * Set the file specification.
+ * @param f The file specification.
+ */
+ public void setFile( PDFileSpecification f )
+ {
+ stream.setItem( "F", f );
+ }
+
+ /**
+ * This will get the list of filters that are associated with this stream. Or
+ * null if there are none.
+ * @return A list of all encoding filters to apply to this stream.
+ */
+ public List getFileFilters()
+ {
+ List retval = null;
+ COSBase filters = stream.getDictionaryObject( "FFilter" );
+ if( filters instanceof COSName )
+ {
+ COSName name = (COSName)filters;
+ retval = new COSArrayList( name.getName(), name, stream, "FFilter" );
+ }
+ else if( filters instanceof COSArray )
+ {
+ retval = COSArrayList.convertCOSNameCOSArrayToList( (COSArray)filters );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the filters that are part of this stream.
+ *
+ * @param filters The filters that are part of this stream.
+ */
+ public void setFileFilters( List filters )
+ {
+ COSBase obj = COSArrayList.convertStringListToCOSNameCOSArray( filters );
+ stream.setItem( "FFilter", obj );
+ }
+
+ /**
+ * Get the list of decode parameters. Each entry in the list will refer to
+ * an entry in the filters list.
+ *
+ * @return The list of decode parameters.
+ *
+ * @throws IOException if there is an error retrieving the parameters.
+ */
+ public List getFileDecodeParams() throws IOException
+ {
+ List retval = null;
+
+ COSBase dp = stream.getDictionaryObject( "FDecodeParms" );
+ if( dp instanceof COSDictionary )
+ {
+ Map map = COSDictionaryMap.convertBasicTypesToMap( (COSDictionary)dp );
+ retval = new COSArrayList(map, dp, stream, "FDecodeParams" );
+ }
+ else if( dp instanceof COSArray )
+ {
+ COSArray array = (COSArray)dp;
+ List actuals = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ actuals.add(
+ COSDictionaryMap.convertBasicTypesToMap(
+ (COSDictionary)array.getObject( i ) ) );
+ }
+ retval = new COSArrayList(actuals, array);
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the list of decode params.
+ *
+ * @param decodeParams The list of decode params.
+ */
+ public void setFileDecodeParams( List decodeParams )
+ {
+ stream.setItem(
+ "FDecodeParams", COSArrayList.converterToCOSArray( decodeParams ) );
+ }
+
+ /**
+ * This will copy the stream into a byte array.
+ *
+ * @return The byte array of the filteredStream
+ * @throws IOException When getFilteredStream did not work
+ */
+ public byte[] getByteArray() throws IOException
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ byte[] buf = new byte[1024];
+ InputStream is = null;
+ try
+ {
+ is = createInputStream();
+ int amountRead = -1;
+ while( (amountRead = is.read( buf )) != -1)
+ {
+ output.write( buf, 0, amountRead );
+ }
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ }
+ return output.toByteArray();
+ }
+
+ /**
+ * A convenience method to get this stream as a string. Uses
+ * the default system encoding.
+ *
+ * @return a String representation of this (input) stream, with the
+ * platform default encoding.
+ *
+ * @throws IOException if there is an error while converting the stream
+ * to a string.
+ */
+ public String getInputStreamAsString() throws IOException
+ {
+ byte[] bStream = getByteArray();
+ return new String(bStream);
+ }
+
+ /**
+ * Get the metadata that is part of the document catalog. This will
+ * return null if there is no meta data for this object.
+ *
+ * @return The metadata for this object.
+ */
+ public PDMetadata getMetadata()
+ {
+ PDMetadata retval = null;
+ COSStream mdStream = (COSStream)stream.getDictionaryObject( "Metadata" );
+ if( mdStream != null )
+ {
+ retval = new PDMetadata( mdStream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the metadata for this object. This can be null.
+ *
+ * @param meta The meta data for this object.
+ */
+ public void setMetadata( PDMetadata meta )
+ {
+ stream.setItem( "Metadata", meta );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/PDTextStream.java b/src/main/java/org/pdfbox/pdmodel/common/PDTextStream.java
new file mode 100644
index 0000000..c7c5ca1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/PDTextStream.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+/**
+ * A PDTextStream class is used when the PDF specification supports either
+ * a string or a stream for the value of an object. This is usually when
+ * a value could be large or small, for example a JavaScript method. This
+ * class will help abstract that and give a single unified interface to
+ * those types of fields.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDTextStream implements COSObjectable
+{
+ private COSString string;
+ private COSStream stream;
+
+ /**
+ * Constructor.
+ *
+ * @param str The string parameter.
+ */
+ public PDTextStream( COSString str )
+ {
+ string = str;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param str The string parameter.
+ */
+ public PDTextStream( String str )
+ {
+ string = new COSString( str );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param str The stream parameter.
+ */
+ public PDTextStream( COSStream str )
+ {
+ stream = str;
+ }
+
+ /**
+ * This will create the text stream object. base must either be a string
+ * or a stream.
+ *
+ * @param base The COS text stream object.
+ *
+ * @return A PDTextStream that wraps the base object.
+ */
+ public static PDTextStream createTextStream( COSBase base )
+ {
+ PDTextStream retval = null;
+ if( base instanceof COSString )
+ {
+ retval = new PDTextStream( (COSString) base );
+ }
+ else if( base instanceof COSStream )
+ {
+ retval = new PDTextStream( (COSStream)base );
+ }
+ return retval;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ COSBase retval = null;
+ if( string == null )
+ {
+ retval = stream;
+ }
+ else
+ {
+ retval = string;
+ }
+ return retval;
+ }
+
+ /**
+ * This will get this value as a string. If this is a stream then it
+ * will load the entire stream into memory, so you should only do this when
+ * the stream is a manageable size.
+ *
+ * @return This value as a string.
+ *
+ * @throws IOException If an IO error occurs while accessing the stream.
+ */
+ public String getAsString() throws IOException
+ {
+ String retval = null;
+ if( string != null )
+ {
+ retval = string.getString();
+ }
+ else
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[ 1024 ];
+ int amountRead = -1;
+ InputStream is = stream.getUnfilteredStream();
+ while( (amountRead = is.read( buffer ) ) != -1 )
+ {
+ out.write( buffer, 0, amountRead );
+ }
+ retval = new String( out.toByteArray() );
+ }
+ return retval;
+ }
+
+ /**
+ * This is the preferred way of getting data with this class as it uses
+ * a stream object.
+ *
+ * @return The stream object.
+ *
+ * @throws IOException If an IO error occurs while accessing the stream.
+ */
+ public InputStream getAsStream() throws IOException
+ {
+ InputStream retval = null;
+ if( string != null )
+ {
+ retval = new ByteArrayInputStream( string.getBytes() );
+ }
+ else
+ {
+ retval = stream.getUnfilteredStream();
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java
new file mode 100644
index 0000000..12618f1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java
@@ -0,0 +1,326 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common.filespecification;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSStream;
+
+/**
+ * This represents a file specification.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class PDComplexFileSpecification extends PDFileSpecification
+{
+ private COSDictionary fs;
+
+ /**
+ * Default Constructor.
+ */
+ public PDComplexFileSpecification()
+ {
+ fs = new COSDictionary();
+ fs.setName( "Type", "Filespec" );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dict The dictionary that fulfils this file specification.
+ */
+ public PDComplexFileSpecification( COSDictionary dict )
+ {
+ fs = dict;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return fs;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return fs;
+ }
+
+ /**
+ * This will get the file name.
+ *
+ * @return The file name.
+ */
+ public String getFile()
+ {
+ return fs.getString( "F" );
+ }
+
+ /**
+ * This will set the file name.
+ *
+ * @param file The name of the file.
+ */
+ public void setFile( String file )
+ {
+ fs.setString( "F", file );
+ }
+
+ /**
+ * This will get the name representing a Dos file.
+ *
+ * @return The file name.
+ */
+ public String getFileDos()
+ {
+ return fs.getString( "DOS" );
+ }
+
+ /**
+ * This will set name representing a dos file.
+ *
+ * @param file The name of the file.
+ */
+ public void setFileDos( String file )
+ {
+ fs.setString( "DOS", file );
+ }
+
+ /**
+ * This will get the name representing a Mac file.
+ *
+ * @return The file name.
+ */
+ public String getFileMac()
+ {
+ return fs.getString( "Mac" );
+ }
+
+ /**
+ * This will set name representing a Mac file.
+ *
+ * @param file The name of the file.
+ */
+ public void setFileMac( String file )
+ {
+ fs.setString( "Mac", file );
+ }
+
+ /**
+ * This will get the name representing a Unix file.
+ *
+ * @return The file name.
+ */
+ public String getFileUnix()
+ {
+ return fs.getString( "Unix" );
+ }
+
+ /**
+ * This will set name representing a Unix file.
+ *
+ * @param file The name of the file.
+ */
+ public void setFileUnix( String file )
+ {
+ fs.setString( "Unix", file );
+ }
+
+ /**
+ * Tell if the underlying file is volatile and should not be cached by the
+ * reader application. Default: false
+ *
+ * @param fileIsVolatile The new value for the volatility of the file.
+ */
+ public void setVolatile( boolean fileIsVolatile )
+ {
+ fs.setBoolean( "V", fileIsVolatile );
+ }
+
+ /**
+ * Get if the file is volatile. Default: false
+ *
+ * @return True if the file is volatile attribute is set.
+ */
+ public boolean isVolatile()
+ {
+ return fs.getBoolean( "V", false );
+ }
+
+ /**
+ * Get the embedded file.
+ *
+ * @return The embedded file for this file spec.
+ */
+ public PDEmbeddedFile getEmbeddedFile()
+ {
+ PDEmbeddedFile file = null;
+ COSStream stream = (COSStream)fs.getObjectFromPath( "EF/F" );
+ if( stream != null )
+ {
+ file = new PDEmbeddedFile( stream );
+ }
+ return file;
+ }
+
+ /**
+ * Set the embedded file for this spec.
+ *
+ * @param file The file to be embedded.
+ */
+ public void setEmbeddedFile( PDEmbeddedFile file )
+ {
+ COSDictionary ef = (COSDictionary)fs.getDictionaryObject( "EF" );
+ if( ef == null && file != null )
+ {
+ ef = new COSDictionary();
+ fs.setItem( "EF", ef );
+ }
+ if( ef != null )
+ {
+ ef.setItem( "F", file );
+ }
+ }
+
+ /**
+ * Get the embedded dos file.
+ *
+ * @return The embedded file for this file spec.
+ */
+ public PDEmbeddedFile getEmbeddedFileDos()
+ {
+ PDEmbeddedFile file = null;
+ COSStream stream = (COSStream)fs.getObjectFromPath( "EF/DOS" );
+ if( stream != null )
+ {
+ file = new PDEmbeddedFile( stream );
+ }
+ return file;
+ }
+
+ /**
+ * Set the embedded dos file for this spec.
+ *
+ * @param file The dos file to be embedded.
+ */
+ public void setEmbeddedFileDos( PDEmbeddedFile file )
+ {
+ COSDictionary ef = (COSDictionary)fs.getDictionaryObject( "DOS" );
+ if( ef == null && file != null )
+ {
+ ef = new COSDictionary();
+ fs.setItem( "EF", ef );
+ }
+ if( ef != null )
+ {
+ ef.setItem( "DOS", file );
+ }
+ }
+
+ /**
+ * Get the embedded Mac file.
+ *
+ * @return The embedded file for this file spec.
+ */
+ public PDEmbeddedFile getEmbeddedFileMac()
+ {
+ PDEmbeddedFile file = null;
+ COSStream stream = (COSStream)fs.getObjectFromPath( "EF/Mac" );
+ if( stream != null )
+ {
+ file = new PDEmbeddedFile( stream );
+ }
+ return file;
+ }
+
+ /**
+ * Set the embedded Mac file for this spec.
+ *
+ * @param file The Mac file to be embedded.
+ */
+ public void setEmbeddedFileMac( PDEmbeddedFile file )
+ {
+ COSDictionary ef = (COSDictionary)fs.getDictionaryObject( "Mac" );
+ if( ef == null && file != null )
+ {
+ ef = new COSDictionary();
+ fs.setItem( "EF", ef );
+ }
+ if( ef != null )
+ {
+ ef.setItem( "Mac", file );
+ }
+ }
+
+ /**
+ * Get the embedded Unix file.
+ *
+ * @return The embedded file for this file spec.
+ */
+ public PDEmbeddedFile getEmbeddedFileUnix()
+ {
+ PDEmbeddedFile file = null;
+ COSStream stream = (COSStream)fs.getObjectFromPath( "EF/Unix" );
+ if( stream != null )
+ {
+ file = new PDEmbeddedFile( stream );
+ }
+ return file;
+ }
+
+ /**
+ * Set the embedded Unix file for this spec.
+ *
+ * @param file The Unix file to be embedded.
+ */
+ public void setEmbeddedFileUnix( PDEmbeddedFile file )
+ {
+ COSDictionary ef = (COSDictionary)fs.getDictionaryObject( "Unix" );
+ if( ef == null && file != null )
+ {
+ ef = new COSDictionary();
+ fs.setItem( "EF", ef );
+ }
+ if( ef != null )
+ {
+ ef.setItem( "Unix", file );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java
new file mode 100644
index 0000000..1f4a288
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java
@@ -0,0 +1,298 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common.filespecification;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * This represents an embedded file in a file specification.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class PDEmbeddedFile extends PDStream
+{
+
+ /**
+ * @see PDStream#PDStream(PDDocument)
+ */
+ public PDEmbeddedFile( PDDocument document )
+ {
+ super( document );
+ getStream().setName( "Type", "EmbeddedFile" );
+
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param str The stream parameter.
+ */
+ public PDEmbeddedFile( COSStream str )
+ {
+ super( str );
+ }
+
+ /**
+ * @see PDStream#PDStream(PDDocument, InputStream )
+ */
+ public PDEmbeddedFile( PDDocument doc, InputStream str ) throws IOException
+ {
+ super( doc, str );
+ getStream().setName( "Type", "EmbeddedFile" );
+ }
+
+ /**
+ * @see PDStream#PDStream(PDDocument, InputStream, boolean)
+ */
+ public PDEmbeddedFile( PDDocument doc, InputStream str, boolean filtered ) throws IOException
+ {
+ super( doc, str, filtered );
+ getStream().setName( "Type", "EmbeddedFile" );
+ }
+
+ /**
+ * Set the subtype for this embedded file. This should be a mime type value. Optional.
+ *
+ * @param mimeType The mimeType for the file.
+ */
+ public void setSubtype( String mimeType )
+ {
+ getStream().setName( "Subtype", mimeType );
+ }
+
+ /**
+ * Get the subtype(mimetype) for the embedded file.
+ *
+ * @return The type of embedded file.
+ */
+ public String getSubtype()
+ {
+ return getStream().getNameAsString( "Subtype" );
+ }
+
+ /**
+ * Get the size of the embedded file.
+ *
+ * @return The size of the embedded file.
+ */
+ public int getSize()
+ {
+ return getStream().getEmbeddedInt( "Params", "Size" );
+ }
+
+ /**
+ * Set the size of the embedded file.
+ *
+ * @param size The size of the embedded file.
+ */
+ public void setSize( int size )
+ {
+ getStream().setEmbeddedInt( "Params", "Size", size );
+ }
+
+ /**
+ * Get the creation date of the embedded file.
+ *
+ * @return The Creation date.
+ * @throws IOException If there is an error while constructing the date.
+ */
+ public Calendar getCreationDate() throws IOException
+ {
+ return getStream().getEmbeddedDate( "Params", "CreationDate" );
+ }
+
+ /**
+ * Set the creation date.
+ *
+ * @param creation The new creation date.
+ */
+ public void setCreationDate( Calendar creation )
+ {
+ getStream().setEmbeddedDate( "Params", "CreationDate", creation );
+ }
+
+ /**
+ * Get the mod date of the embedded file.
+ *
+ * @return The mod date.
+ * @throws IOException If there is an error while constructing the date.
+ */
+ public Calendar getModDate() throws IOException
+ {
+ return getStream().getEmbeddedDate( "Params", "ModDate" );
+ }
+
+ /**
+ * Set the mod date.
+ *
+ * @param mod The new creation mod.
+ */
+ public void setModDate( Calendar mod )
+ {
+ getStream().setEmbeddedDate( "Params", "ModDate", mod );
+ }
+
+ /**
+ * Get the check sum of the embedded file.
+ *
+ * @return The check sum of the file.
+ */
+ public String getCheckSum()
+ {
+ return getStream().getEmbeddedString( "Params", "CheckSum" );
+ }
+
+ /**
+ * Set the check sum.
+ *
+ * @param checksum The checksum of the file.
+ */
+ public void setCheckSum( String checksum )
+ {
+ getStream().setEmbeddedString( "Params", "CheckSum", checksum );
+ }
+
+ /**
+ * Get the mac subtype.
+ *
+ * @return The mac subtype.
+ */
+ public String getMacSubtype()
+ {
+ String retval = null;
+ COSDictionary params = (COSDictionary)getStream().getDictionaryObject( "Params" );
+ if( params != null )
+ {
+ retval = params.getEmbeddedString( "Mac", "Subtype" );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the mac subtype.
+ *
+ * @param macSubtype The mac subtype.
+ */
+ public void setMacSubtype( String macSubtype )
+ {
+ COSDictionary params = (COSDictionary)getStream().getDictionaryObject( "Params" );
+ if( params == null && macSubtype != null )
+ {
+ params = new COSDictionary();
+ getStream().setItem( "Params", params );
+ }
+ if( params != null )
+ {
+ params.setEmbeddedString( "Mac", "Subtype", macSubtype );
+ }
+ }
+
+ /**
+ * Get the mac Creator.
+ *
+ * @return The mac Creator.
+ */
+ public String getMacCreator()
+ {
+ String retval = null;
+ COSDictionary params = (COSDictionary)getStream().getDictionaryObject( "Params" );
+ if( params != null )
+ {
+ retval = params.getEmbeddedString( "Mac", "Creator" );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the mac Creator.
+ *
+ * @param macCreator The mac Creator.
+ */
+ public void setMacCreator( String macCreator )
+ {
+ COSDictionary params = (COSDictionary)getStream().getDictionaryObject( "Params" );
+ if( params == null && macCreator != null )
+ {
+ params = new COSDictionary();
+ getStream().setItem( "Params", params );
+ }
+ if( params != null )
+ {
+ params.setEmbeddedString( "Mac", "Creator", macCreator );
+ }
+ }
+
+ /**
+ * Get the mac ResFork.
+ *
+ * @return The mac ResFork.
+ */
+ public String getMacResFork()
+ {
+ String retval = null;
+ COSDictionary params = (COSDictionary)getStream().getDictionaryObject( "Params" );
+ if( params != null )
+ {
+ retval = params.getEmbeddedString( "Mac", "ResFork" );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the mac ResFork.
+ *
+ * @param macResFork The mac ResFork.
+ */
+ public void setMacResFork( String macResFork )
+ {
+ COSDictionary params = (COSDictionary)getStream().getDictionaryObject( "Params" );
+ if( params == null && macResFork != null )
+ {
+ params = new COSDictionary();
+ getStream().setItem( "Params", params );
+ }
+ if( params != null )
+ {
+ params.setEmbeddedString( "Mac", "ResFork", macResFork);
+ }
+ }
+
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java
new file mode 100644
index 0000000..2028247
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common.filespecification;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents a file specification.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public abstract class PDFileSpecification implements COSObjectable
+{
+
+ /**
+ * A file specfication can either be a COSString or a COSDictionary. This
+ * will create the file specification either way.
+ *
+ * @param base The cos object that describes the fs.
+ *
+ * @return The file specification for the COSBase object.
+ */
+ public static PDFileSpecification createFS( COSBase base )
+ {
+ PDFileSpecification retval = null;
+ if( base instanceof COSString )
+ {
+ retval = new PDSimpleFileSpecification( (COSString)base );
+ }
+ else if( base instanceof COSDictionary )
+ {
+ retval = new PDComplexFileSpecification( (COSDictionary)base );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the file name.
+ *
+ * @return The file name.
+ */
+ public abstract String getFile();
+
+ /**
+ * This will set the file name.
+ *
+ * @param file The name of the file.
+ */
+ public abstract void setFile( String file );
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java
new file mode 100644
index 0000000..b885a78
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.common.filespecification;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSString;
+
+/**
+ * A file specification that is just a string.
+ *
+ * @author blitchfield
+ * @version $Revision: 1.1 $
+ */
+public class PDSimpleFileSpecification extends PDFileSpecification
+{
+ private COSString file;
+
+ /**
+ * Constructor.
+ *
+ */
+ public PDSimpleFileSpecification()
+ {
+ file = new COSString( "" );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fileName The file that this spec represents.
+ */
+ public PDSimpleFileSpecification( COSString fileName )
+ {
+ file = fileName;
+ }
+
+ /**
+ * This will get the file name.
+ *
+ * @return The file name.
+ */
+ public String getFile()
+ {
+ return file.getString();
+ }
+
+ /**
+ * This will set the file name.
+ *
+ * @param fileName The name of the file.
+ */
+ public void setFile( String fileName )
+ {
+ file = new COSString( fileName );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return file;
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/common/filespecification/package.html b/src/main/java/org/pdfbox/pdmodel/common/filespecification/package.html
new file mode 100644
index 0000000..ca87d22
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/filespecification/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The file specification package defines classes that are used for the PDF File Specification logic.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/common/package.html b/src/main/java/org/pdfbox/pdmodel/common/package.html
new file mode 100644
index 0000000..9d81599
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/common/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+High level PD classes that are used throughout several packages are placed in the PDModel common package.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java b/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java
new file mode 100644
index 0000000..47a8ff3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.documentinterchange.logicalstructure;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * The MarkInfo provides additional information relevant to specialized
+ * uses of structured documents.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class PDMarkInfo implements COSObjectable
+{
+ private COSDictionary dictionary;
+
+ /**
+ * Default Constructor.
+ *
+ */
+ public PDMarkInfo()
+ {
+ dictionary = new COSDictionary();
+ }
+
+ /**
+ * Constructor for an existing MarkInfo element.
+ *
+ * @param dic The existing dictionary.
+ */
+ public PDMarkInfo( COSDictionary dic )
+ {
+ dictionary = dic;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return dictionary;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ /**
+ * Tells if this is a tagged PDF.
+ *
+ * @return true If this is a tagged PDF.
+ */
+ public boolean isMarked()
+ {
+ return dictionary.getBoolean( "Marked", false );
+ }
+
+ /**
+ * Set if this is a tagged PDF.
+ *
+ * @param value The new marked value.
+ */
+ public void setMarked( boolean value )
+ {
+ dictionary.setBoolean( "Marked", value );
+ }
+
+ /**
+ * Tells if structure elements use user properties.
+ *
+ * @return A boolean telling if the structure elements use user properties.
+ */
+ public boolean usesUserProperties()
+ {
+ return dictionary.getBoolean( "UserProperties", false );
+ }
+
+ /**
+ * Set if the structure elements contain user properties.
+ *
+ * @param userProps The new value for this property.
+ */
+ public void setUserProperties( boolean userProps )
+ {
+ dictionary.setBoolean( "UserProperties", userProps );
+ }
+
+ /**
+ * Tells if this PDF contain 'suspect' tags. See PDF Reference 1.6
+ * section 10.6 "Logical Structure" for more information about this property.
+ *
+ * @return true if the suspect flag has been set.
+ */
+ public boolean isSuspect()
+ {
+ return dictionary.getBoolean( "Suspects", false );
+ }
+
+ /**
+ * Set the value of the suspects property. See PDF Reference 1.6
+ * section 10.6 "Logical Structure" for more information about this
+ * property.
+ *
+ * @param suspect The new "Suspects" value.
+ */
+ public void setSuspect( boolean suspect )
+ {
+ dictionary.setBoolean( "Suspects", false );
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java b/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java
new file mode 100644
index 0000000..9760b1b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.documentinterchange.logicalstructure;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * A structure element.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDStructureElement implements COSObjectable
+{
+ private COSDictionary dictionary;
+
+ /**
+ * Default Constructor.
+ *
+ */
+ public PDStructureElement()
+ {
+ dictionary = new COSDictionary();
+ }
+
+ /**
+ * Constructor for an existing structure element.
+ *
+ * @param dic The existing dictionary.
+ */
+ public PDStructureElement( COSDictionary dic )
+ {
+ dictionary = dic;
+ }
+
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return dictionary;
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html b/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html
new file mode 100644
index 0000000..7039586
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The logical structure package provides a mechanism for incorporating
+structural information about a document's content into a PDF file.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java b/src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java
new file mode 100644
index 0000000..4f092ef
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java
@@ -0,0 +1,222 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.documentinterchange.prepress;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.graphics.PDLineDashPattern;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+
+/**
+ * The Box Style specifies visual characteristics for displaying box areas.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class PDBoxStyle implements COSObjectable
+{
+ /**
+ * Style for guideline.
+ */
+ public static final String GUIDELINE_STYLE_SOLID = "S";
+ /**
+ * Style for guideline.
+ */
+ public static final String GUIDELINE_STYLE_DASHED = "D";
+
+ private COSDictionary dictionary;
+
+ /**
+ * Default Constructor.
+ *
+ */
+ public PDBoxStyle()
+ {
+ dictionary = new COSDictionary();
+ }
+
+ /**
+ * Constructor for an existing BoxStyle element.
+ *
+ * @param dic The existing dictionary.
+ */
+ public PDBoxStyle( COSDictionary dic )
+ {
+ dictionary = dic;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return dictionary;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ /**
+ * Get the color to be used for the guidelines. This is guaranteed to
+ * not return null. The color space will always be DeviceRGB and the
+ * default color is [0,0,0].
+ *
+ *@return The guideline color.
+ */
+ public PDColorSpaceInstance getGuidelineColor()
+ {
+ COSArray colorValues = (COSArray)dictionary.getDictionaryObject( "C" );
+ if( colorValues == null )
+ {
+ colorValues = new COSArray();
+ colorValues.add( COSInteger.ZERO );
+ colorValues.add( COSInteger.ZERO );
+ colorValues.add( COSInteger.ZERO );
+ dictionary.setItem( "C", colorValues );
+ }
+ PDColorSpaceInstance instance = new PDColorSpaceInstance( colorValues );
+ instance.setColorSpace( PDDeviceRGB.INSTANCE );
+ return instance;
+ }
+
+ /**
+ * Set the color space instance for this box style. This must be a
+ * PDDeviceRGB!
+ *
+ * @param color The new colorspace value.
+ */
+ public void setGuideLineColor( PDColorSpaceInstance color )
+ {
+ COSArray values = null;
+ if( color != null )
+ {
+ values = color.getCOSColorSpaceValue();
+ }
+ dictionary.setItem( "C", values );
+ }
+
+ /**
+ * Get the width of the of the guideline in default user space units.
+ * The default is 1.
+ *
+ * @return The width of the guideline.
+ */
+ public float getGuidelineWidth()
+ {
+ return dictionary.getFloat( "W", 1 );
+ }
+
+ /**
+ * Set the guideline width.
+ *
+ * @param width The width in default user space units.
+ */
+ public void setGuidelineWidth( float width )
+ {
+ dictionary.setFloat( "W", width );
+ }
+
+ /**
+ * Get the style for the guideline. The default is "S" for solid.
+ *
+ * @return The guideline style.
+ * @see PDBoxStyle#GUIDELINE_STYLE_DASHED
+ * @see PDBoxStyle#GUIDELINE_STYLE_SOLID
+ */
+ public String getGuidelineStyle()
+ {
+ return dictionary.getNameAsString( "S", GUIDELINE_STYLE_SOLID );
+ }
+
+ /**
+ * Set the style for the box.
+ *
+ * @param style The style for the box line.
+ * @see PDBoxStyle#GUIDELINE_STYLE_DASHED
+ * @see PDBoxStyle#GUIDELINE_STYLE_SOLID
+ */
+ public void setGuidelineStyle( String style )
+ {
+ dictionary.setName( "S", style );
+ }
+
+ /**
+ * Get the line dash pattern for this box style. This is guaranteed to not
+ * return null. The default is [3],0.
+ *
+ * @return The line dash pattern.
+ */
+ public PDLineDashPattern getLineDashPattern()
+ {
+ PDLineDashPattern pattern = null;
+ COSArray d = (COSArray)dictionary.getDictionaryObject( "D" );
+ if( d == null )
+ {
+ d = new COSArray();
+ d.add( new COSInteger(3) );
+ dictionary.setItem( "D", d );
+ }
+ COSArray lineArray = new COSArray();
+ lineArray.add( d );
+ //dash phase is not specified and assumed to be zero.
+ lineArray.add( new COSInteger( 0 ) );
+ pattern = new PDLineDashPattern( lineArray );
+ return pattern;
+ }
+
+ /**
+ * Set the line dash pattern associated with this box style.
+ *
+ * @param pattern The patter for this box style.
+ */
+ public void setLineDashPattern( PDLineDashPattern pattern )
+ {
+ COSArray array = null;
+ if( pattern != null )
+ {
+ array = pattern.getCOSDashPattern();
+ }
+ dictionary.setItem( "D", array );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/package.html b/src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/package.html
new file mode 100644
index 0000000..8285a3f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/documentinterchange/prepress/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains classes for prepress support in PDFBox.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java b/src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java
new file mode 100644
index 0000000..1e3b25f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java
@@ -0,0 +1,648 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.edit;
+
+import java.awt.Color;
+import java.awt.color.ColorSpace;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.common.COSStreamArray;
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+
+/**
+ * This class will is a convenience for creating page content streams. You MUST
+ * call close() when you are finished with this object.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.14 $
+ */
+public class PDPageContentStream
+{
+ private PDPage page;
+ private OutputStream output;
+ private boolean inTextMode = false;
+ private Map fontMappings = new HashMap();
+ private Map imageMappings = new HashMap();
+ private int fontNumber = 1;
+ private int imageNumber = 1;
+ private PDResources resources;
+
+ //cached storage component for getting color values
+ private float[] colorComponents = new float[4];
+
+ private NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US );
+
+ private static final String BEGIN_TEXT = "BT\n";
+ private static final String END_TEXT = "ET\n";
+ private static final String SET_FONT = "Tf\n";
+ private static final String MOVE_TEXT_POSITION = "Td\n";
+ private static final String SHOW_TEXT = "Tj\n";
+
+ private static final String SAVE_GRAPHICS_STATE = "q\n";
+ private static final String RESTORE_GRAPHICS_STATE = "Q\n";
+ private static final String CONCATENATE_MATRIX = "cm\n";
+ private static final String XOBJECT_DO = "Do\n";
+ private static final String RG_STROKING = "RG\n";
+ private static final String RG_NON_STROKING = "rg\n";
+ private static final String K_STROKING = "K\n";
+ private static final String K_NON_STROKING = "k\n";
+ private static final String G_STROKING = "G\n";
+ private static final String G_NON_STROKING = "g\n";
+ private static final String APPEND_RECTANGLE = "re\n";
+ private static final String FILL = "f\n";
+
+
+ private static final int SPACE = 32;
+
+
+ /**
+ * Create a new PDPage content stream.
+ *
+ * @param document The document the page is part of.
+ * @param sourcePage The page to write the contents to.
+ * @throws IOException If there is an error writing to the page contents.
+ */
+ public PDPageContentStream( PDDocument document, PDPage sourcePage ) throws IOException
+ {
+ this(document,sourcePage,false,true);
+ }
+
+ /**
+ * Create a new PDPage content stream.
+ *
+ * @param document The document the page is part of.
+ * @param sourcePage The page to write the contents to.
+ * @param appendContent Indicates whether content will be overwritten. If false all previous content is deleted.
+ * @param compress Tell if the content stream should compress the page contents.
+ * @throws IOException If there is an error writing to the page contents.
+ */
+ public PDPageContentStream( PDDocument document, PDPage sourcePage, boolean appendContent, boolean compress )
+ throws IOException
+ {
+ page = sourcePage;
+ resources = page.getResources();
+ if( resources == null )
+ {
+ resources = new PDResources();
+ page.setResources( resources );
+ }
+ // If request specifies the need to append to the document
+ if(appendContent)
+ {
+ // Get the pdstream from the source page instead of creating a new one
+ PDStream contents = sourcePage.getContents();
+
+ // Create a pdstream to append new content
+ PDStream contentsToAppend = new PDStream( document );
+
+ // This will be the resulting COSStreamArray after existing and new streams are merged
+ COSStreamArray compoundStream = null;
+
+ // If contents is already an array, a new stream is simply appended to it
+ if(contents.getStream() instanceof COSStreamArray)
+ {
+ compoundStream = (COSStreamArray)contents.getStream();
+ compoundStream.appendStream( contentsToAppend.getStream());
+ }
+ else
+ {
+ // Creates the COSStreamArray and adds the current stream plus a new one to it
+ COSArray newArray = new COSArray();
+ newArray.add(contents.getCOSObject());
+ newArray.add(contentsToAppend.getCOSObject());
+ compoundStream = new COSStreamArray(newArray);
+ }
+
+ if( compress )
+ {
+ List filters = new ArrayList();
+ filters.add( COSName.FLATE_DECODE );
+ contentsToAppend.setFilters( filters );
+ }
+
+ // Sets the compoundStream as page contents
+ sourcePage.setContents( new PDStream(compoundStream) );
+ output = contentsToAppend.createOutputStream();
+ }
+ else
+ {
+ PDStream contents = new PDStream( document );
+ if( compress )
+ {
+ List filters = new ArrayList();
+ filters.add( COSName.FLATE_DECODE );
+ contents.setFilters( filters );
+ }
+ sourcePage.setContents( contents );
+ output = contents.createOutputStream();
+ }
+ formatDecimal.setMaximumFractionDigits( 10 );
+ formatDecimal.setGroupingUsed( false );
+ }
+
+ /**
+ * Begin some text operations.
+ *
+ * @throws IOException If there is an error writing to the stream or if you attempt to
+ * nest beginText calls.
+ */
+ public void beginText() throws IOException
+ {
+ if( inTextMode )
+ {
+ throw new IOException( "Error: Nested beginText() calls are not allowed." );
+ }
+ appendRawCommands( BEGIN_TEXT );
+ inTextMode = true;
+ }
+
+ /**
+ * End some text operations.
+ *
+ * @throws IOException If there is an error writing to the stream or if you attempt to
+ * nest endText calls.
+ */
+ public void endText() throws IOException
+ {
+ if( !inTextMode )
+ {
+ throw new IOException( "Error: You must call beginText() before calling endText." );
+ }
+ appendRawCommands( END_TEXT );
+ inTextMode = false;
+ }
+
+ /**
+ * Set the font to draw text with.
+ *
+ * @param font The font to use.
+ * @param fontSize The font size to draw the text.
+ * @throws IOException If there is an error writing the font information.
+ */
+ public void setFont( PDFont font, float fontSize ) throws IOException
+ {
+ String fontMapping = (String)fontMappings.get( font );
+ if( fontMapping == null )
+ {
+ fontMapping = "F" + fontNumber++;
+ fontMappings.put( font, fontMapping );
+ resources.getFonts().put( fontMapping, font );
+ }
+ appendRawCommands( "/");
+ appendRawCommands( fontMapping );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( fontSize ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( SET_FONT );
+ }
+
+ /**
+ * Draw an image at the x,y coordinates, with the default size of the image.
+ *
+ * @param image The image to draw.
+ * @param x The x-coordinate to draw the image.
+ * @param y The y-coordinate to draw the image.
+ *
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void drawImage( PDXObjectImage image, int x, int y ) throws IOException
+ {
+ drawImage( image, x, y, image.getWidth(), image.getHeight() );
+ }
+
+ /**
+ * Draw an image at the x,y coordinates and a certain width and height.
+ *
+ * @param image The image to draw.
+ * @param x The x-coordinate to draw the image.
+ * @param y The y-coordinate to draw the image.
+ * @param width The width of the image to draw.
+ * @param height The height of the image to draw.
+ *
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void drawImage( PDXObjectImage image, int x, int y, int width, int height ) throws IOException
+ {
+ String imageMapping = (String)imageMappings.get( image );
+ if( imageMapping == null )
+ {
+ imageMapping = "Im" + imageNumber++;
+ imageMappings.put( image, imageMapping );
+ resources.getImages().put( imageMapping, image );
+ }
+ appendRawCommands( SAVE_GRAPHICS_STATE );
+ appendRawCommands( formatDecimal.format( width ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( 0 ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( 0 ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( height ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( x ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( CONCATENATE_MATRIX );
+ appendRawCommands( SPACE );
+ appendRawCommands( "/" );
+ appendRawCommands( imageMapping );
+ appendRawCommands( SPACE );
+ appendRawCommands( XOBJECT_DO );
+ appendRawCommands( SPACE );
+ appendRawCommands( RESTORE_GRAPHICS_STATE );
+ }
+
+ /**
+ * The Td operator.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @throws IOException If there is an error writing to the stream.
+ */
+ public void moveTextPositionByAmount( float x, float y ) throws IOException
+ {
+ if( !inTextMode )
+ {
+ throw new IOException( "Error: must call beginText() before moveTextPositionByAmount");
+ }
+ appendRawCommands( formatDecimal.format( x ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( MOVE_TEXT_POSITION );
+ }
+
+ /**
+ * This will draw a string at the current location on the screen.
+ *
+ * @param text The text to draw.
+ * @throws IOException If an io exception occurs.
+ */
+ public void drawString( String text ) throws IOException
+ {
+ if( !inTextMode )
+ {
+ throw new IOException( "Error: must call beginText() before drawString");
+ }
+ COSString string = new COSString( text );
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ string.writePDF( buffer );
+ appendRawCommands( new String( buffer.toByteArray(), "ISO-8859-1"));
+ appendRawCommands( SPACE );
+ appendRawCommands( SHOW_TEXT );
+ }
+
+ /**
+ * Set the stroking color, specified as RGB.
+ *
+ * @param color The color to set.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setStrokingColor( Color color ) throws IOException
+ {
+ ColorSpace colorSpace = color.getColorSpace();
+ if( colorSpace.getType() == ColorSpace.TYPE_RGB )
+ {
+ setStrokingColor( color.getRed(), color.getGreen(), color.getBlue() );
+ }
+ else if( colorSpace.getType() == ColorSpace.TYPE_GRAY )
+ {
+ color.getColorComponents( colorComponents );
+ setStrokingColor( colorComponents[0] );
+ }
+ else if( colorSpace.getType() == ColorSpace.TYPE_CMYK )
+ {
+ color.getColorComponents( colorComponents );
+ setStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] );
+ }
+ else
+ {
+ throw new IOException( "Error: unknown colorspace:" + colorSpace );
+ }
+ }
+
+ /**
+ * Set the non stroking color, specified as RGB.
+ *
+ * @param color The color to set.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setNonStrokingColor( Color color ) throws IOException
+ {
+ ColorSpace colorSpace = color.getColorSpace();
+ if( colorSpace.getType() == ColorSpace.TYPE_RGB )
+ {
+ setNonStrokingColor( color.getRed(), color.getGreen(), color.getBlue() );
+ }
+ else if( colorSpace.getType() == ColorSpace.TYPE_GRAY )
+ {
+ color.getColorComponents( colorComponents );
+ setNonStrokingColor( colorComponents[0] );
+ }
+ else if( colorSpace.getType() == ColorSpace.TYPE_CMYK )
+ {
+ color.getColorComponents( colorComponents );
+ setNonStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] );
+ }
+ else
+ {
+ throw new IOException( "Error: unknown colorspace:" + colorSpace );
+ }
+ }
+
+ /**
+ * Set the stroking color, specified as RGB, 0-255.
+ *
+ * @param r The red value.
+ * @param g The green value.
+ * @param b The blue value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setStrokingColor( int r, int g, int b ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( r/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( g/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( b/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( RG_STROKING );
+ }
+
+ /**
+ * Set the stroking color, specified as CMYK, 0-255.
+ *
+ * @param c The cyan value.
+ * @param m The magenta value.
+ * @param y The yellow value.
+ * @param k The black value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setStrokingColor( int c, int m, int y, int k) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( c/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( m/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( k/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( K_STROKING );
+ }
+
+ /**
+ * Set the stroking color, specified as CMYK, 0.0-1.0.
+ *
+ * @param c The cyan value.
+ * @param m The magenta value.
+ * @param y The yellow value.
+ * @param k The black value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setStrokingColor( double c, double m, double y, double k) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( c ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( m ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( k ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( K_STROKING );
+ }
+
+ /**
+ * Set the stroking color, specified as grayscale, 0-255.
+ *
+ * @param g The gray value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setStrokingColor( int g ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( g/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( G_STROKING );
+ }
+
+ /**
+ * Set the stroking color, specified as Grayscale 0.0-1.0.
+ *
+ * @param g The gray value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setStrokingColor( double g ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( g ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( G_STROKING );
+ }
+
+ /**
+ * Set the non stroking color, specified as RGB, 0-255.
+ *
+ * @param r The red value.
+ * @param g The green value.
+ * @param b The blue value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setNonStrokingColor( int r, int g, int b ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( r/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( g/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( b/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( RG_NON_STROKING );
+ }
+
+ /**
+ * Set the non stroking color, specified as CMYK, 0-255.
+ *
+ * @param c The cyan value.
+ * @param m The magenta value.
+ * @param y The yellow value.
+ * @param k The black value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setNonStrokingColor( int c, int m, int y, int k) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( c/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( m/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( k/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( K_NON_STROKING );
+ }
+
+ /**
+ * Set the non stroking color, specified as CMYK, 0.0-1.0.
+ *
+ * @param c The cyan value.
+ * @param m The magenta value.
+ * @param y The yellow value.
+ * @param k The black value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setNonStrokingColor( double c, double m, double y, double k) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( c ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( m ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( k ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( K_NON_STROKING );
+ }
+
+ /**
+ * Set the non stroking color, specified as grayscale, 0-255.
+ *
+ * @param g The gray value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setNonStrokingColor( int g ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( g/255d ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( G_NON_STROKING );
+ }
+
+ /**
+ * Set the non stroking color, specified as Grayscale 0.0-1.0.
+ *
+ * @param g The gray value.
+ * @throws IOException If an IO error occurs while writing to the stream.
+ */
+ public void setNonStrokingColor( double g ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( g ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( G_NON_STROKING );
+ }
+
+ /**
+ * Draw a rectangle on the page using the current non stroking color.
+ *
+ * @param x The lower left x coordinate.
+ * @param y The lower left y coordinate.
+ * @param width The width of the rectangle.
+ * @param height The height of the rectangle.
+ * @throws IOException If there is an error while drawing on the screen.
+ */
+ public void fillRect( float x, float y, float width, float height ) throws IOException
+ {
+ appendRawCommands( formatDecimal.format( x ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( y ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( width ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( formatDecimal.format( height ) );
+ appendRawCommands( SPACE );
+ appendRawCommands( APPEND_RECTANGLE );
+ appendRawCommands( FILL );
+ }
+
+
+ /**
+ * This will append raw commands to the content stream.
+ *
+ * @param commands The commands to append to the stream.
+ * @throws IOException If an error occurs while writing to the stream.
+ */
+ public void appendRawCommands( String commands ) throws IOException
+ {
+ appendRawCommands( commands.getBytes( "ISO-8859-1" ) );
+ }
+
+ /**
+ * This will append raw commands to the content stream.
+ *
+ * @param commands The commands to append to the stream.
+ * @throws IOException If an error occurs while writing to the stream.
+ */
+ public void appendRawCommands( byte[] commands ) throws IOException
+ {
+ output.write( commands );
+ }
+
+ /**
+ * This will append raw commands to the content stream.
+ *
+ * @param data Append a raw byte to the stream.
+ *
+ * @throws IOException If an error occurs while writing to the stream.
+ */
+ public void appendRawCommands( int data ) throws IOException
+ {
+ output.write( data );
+ }
+
+ /**
+ * Close the content stream. This must be called when you are done with this
+ * object.
+ * @throws IOException If the underlying stream has a problem being written to.
+ */
+ public void close() throws IOException
+ {
+ output.close();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/edit/package.html b/src/main/java/org/pdfbox/pdmodel/edit/package.html
new file mode 100644
index 0000000..f6b0ffd
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/edit/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The PDModel edit package will be used to store classes for creating page content.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java b/src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java
new file mode 100644
index 0000000..08f18fb
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java
@@ -0,0 +1,193 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.encryption;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+
+/**
+ * This represents the base class for encryption dictionaries. All PDF implementations
+ * are expected to implement the Standard encryption algorithm, but others can be plugged in.
+ *
+ * See PDF Reference 1.4 section "3.5 Encryption"
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public abstract class PDEncryptionDictionary
+{
+ /**
+ * See PDF Reference 1.4 Table 3.13.
+ */
+ public static final int VERSION0_UNDOCUMENTED_UNSUPPORTED = 0;
+ /**
+ * See PDF Reference 1.4 Table 3.13.
+ */
+ public static final int VERSION1_40_BIT_ALGORITHM = 1;
+ /**
+ * See PDF Reference 1.4 Table 3.13.
+ */
+ public static final int VERSION2_VARIABLE_LENGTH_ALGORITHM = 2;
+ /**
+ * See PDF Reference 1.4 Table 3.13.
+ */
+ public static final int VERSION3_UNPUBLISHED_ALGORITHM = 3;
+ /**
+ * See PDF Reference 1.4 Table 3.13.
+ */
+ public static final int VERSION4_SECURITY_HANDLER = 4;
+
+ /**
+ * The default security handler.
+ */
+ public static final String DEFAULT_NAME = "Standard";
+
+ /**
+ * The default length for the encryption key.
+ */
+ public static final int DEFAULT_LENGTH = 40;
+
+ /**
+ * The default version, according to the PDF Reference.
+ */
+ public static final int DEFAULT_VERSION = VERSION0_UNDOCUMENTED_UNSUPPORTED;
+
+ /**
+ * The cos model wrapped object.
+ */
+ protected COSDictionary encryptionDictionary = null;
+
+ /**
+ * Constructor.
+ *
+ * @param dictionary The pre-existing encryption dictionary.
+ */
+ protected PDEncryptionDictionary( COSDictionary dictionary )
+ {
+ encryptionDictionary = dictionary;
+ }
+
+ /**
+ * Constructor, sub classes need to fill out the required fields.
+ */
+ protected PDEncryptionDictionary()
+ {
+ encryptionDictionary = new COSDictionary();
+ setLength( DEFAULT_LENGTH );
+ setVersion( DEFAULT_VERSION );
+ }
+
+ /**
+ * This will get the dictionary associated with this encryption dictionary.
+ *
+ * @return The COS dictionary that this object wraps.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return encryptionDictionary;
+ }
+
+ /**
+ * Read-only field of the encryption filter name. The default value is
+ * "Standard" for the built in security handler.
+ *
+ * @return The name of the encryption handler.
+ */
+ public String getFilter()
+ {
+ String filter = DEFAULT_NAME;
+ COSName cosFilter = (COSName)encryptionDictionary.getDictionaryObject( COSName.FILTER );
+ if( cosFilter != null )
+ {
+ filter = cosFilter.getName();
+ }
+ return filter;
+ }
+
+ /**
+ * This will return the V entry of the encryption dictionary.<br /><br />
+ * See PDF Reference 1.4 Table 3.13.
+ *
+ * @return The encryption version to use.
+ */
+ public int getVersion()
+ {
+ int version = DEFAULT_VERSION;
+ COSNumber cosVersion = (COSNumber)encryptionDictionary.getDictionaryObject( COSName.getPDFName( "V" ) );
+ if( cosVersion != null )
+ {
+ version = cosVersion.intValue();
+ }
+ return version;
+ }
+
+ /**
+ * This will set the V entry of the encryption dictionary.<br /><br />
+ * See PDF Reference 1.4 Table 3.13. <br /><br/>
+ * <b>Note: This value is used to decrypt the pdf document. If you change this when
+ * the document is encrypted then decryption will fail!.</b>
+ *
+ * @param version The new encryption version.
+ */
+ public void setVersion( int version )
+ {
+ encryptionDictionary.setItem( COSName.getPDFName( "V" ), new COSInteger( version ) );
+ }
+
+ /**
+ * This will return the Length entry of the encryption dictionary.<br /><br />
+ * The length in <b>bits</b> for the encryption algorithm. This will return a multiple of 8.
+ *
+ * @return The length in bits for the encryption algorithm
+ */
+ public int getLength()
+ {
+ int length = DEFAULT_LENGTH;
+ COSNumber cosLength = (COSNumber)encryptionDictionary.getDictionaryObject( COSName.LENGTH );
+ if( cosLength != null )
+ {
+ length = cosLength.intValue();
+ }
+ return length;
+ }
+
+ /**
+ * This will set the number of bits to use for the encryption algorithm.
+ *
+ * @param length The new key length.
+ */
+ public void setLength( int length )
+ {
+ encryptionDictionary.setItem( COSName.LENGTH, new COSInteger( length ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionManager.java b/src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionManager.java
new file mode 100644
index 0000000..9dfc36e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/encryption/PDEncryptionManager.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.encryption;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import java.io.IOException;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class will handle loading of the different security handlers.
+ *
+ * See PDF Reference 1.4 section "3.5 Encryption"
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDEncryptionManager
+{
+ private static Map handlerMap = Collections.synchronizedMap( new HashMap() );
+
+ static
+ {
+ registerSecurityHandler( PDStandardEncryption.FILTER_NAME, PDStandardEncryption.class );
+ }
+
+ private PDEncryptionManager()
+ {
+ }
+
+ /**
+ * This will allow the user to register new security handlers when unencrypting a
+ * document.
+ *
+ * @param filterName As described in the encryption dictionary.
+ * @param handlerClass A subclass of PDEncryptionDictionary that has a constructor that takes
+ * a COSDictionary.
+ */
+ public static void registerSecurityHandler( String filterName, Class handlerClass )
+ {
+ handlerMap.put( COSName.getPDFName( filterName ), handlerClass );
+ }
+
+ /**
+ * This will get the correct security handler for the encryption dictionary.
+ *
+ * @param dictionary The encryption dictionary.
+ *
+ * @return An implementation of PDEncryptionDictionary(PDStandardEncryption for most cases).
+ *
+ * @throws IOException If a security handler could not be found.
+ */
+ public static PDEncryptionDictionary getEncryptionDictionary( COSDictionary dictionary )
+ throws IOException
+ {
+ Object retval = null;
+ if( dictionary != null )
+ {
+ COSName filter = (COSName)dictionary.getDictionaryObject( COSName.FILTER );
+ Class handlerClass = (Class)handlerMap.get( filter );
+ if( handlerClass == null )
+ {
+ throw new IOException( "No handler for security handler '" + filter.getName() + "'" );
+ }
+ else
+ {
+ try
+ {
+ Constructor ctor = handlerClass.getConstructor( new Class[] {
+ COSDictionary.class
+ } );
+ retval = ctor.newInstance( new Object[] {
+ dictionary
+ } );
+ }
+ catch( NoSuchMethodException e )
+ {
+ throw new IOException( e.getMessage() );
+ }
+ catch( InstantiationException e )
+ {
+ throw new IOException( e.getMessage() );
+ }
+ catch( IllegalAccessException e )
+ {
+ throw new IOException( e.getMessage() );
+ }
+ catch( InvocationTargetException e )
+ {
+ throw new IOException( e.getMessage() );
+ }
+ }
+ }
+ return (PDEncryptionDictionary)retval;
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/encryption/PDStandardEncryption.java b/src/main/java/org/pdfbox/pdmodel/encryption/PDStandardEncryption.java
new file mode 100644
index 0000000..5b3be3b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/encryption/PDStandardEncryption.java
@@ -0,0 +1,416 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.encryption;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSString;
+
+import java.io.IOException;
+
+/**
+ * This class holds information that is related to the standard PDF encryption.
+ *
+ * See PDF Reference 1.4 section "3.5 Encryption"
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDStandardEncryption extends PDEncryptionDictionary
+{
+ /**
+ * The 'Filter' name for this security handler.
+ */
+ public static final String FILTER_NAME = "Standard";
+
+ /**
+ * The default revision of one is not specified.
+ */
+ public static final int DEFAULT_REVISION = 3;
+
+ /**
+ * Encryption revision 2.
+ */
+ public static final int REVISION2 = 2;
+ /**
+ * Encryption revision 3.
+ */
+ public static final int REVISION3 = 3;
+ /**
+ * Encryption revision 4.
+ */
+ public static final int REVISION4 = 4;
+
+ /**
+ * The default set of permissions which is to allow all.
+ */
+ public static final int DEFAULT_PERMISSIONS = 0xFFFFFFFF ^ 3;//bits 0 & 1 need to be zero
+
+ private static final int PRINT_BIT = 3;
+ private static final int MODIFICATION_BIT = 4;
+ private static final int EXTRACT_BIT = 5;
+ private static final int MODIFY_ANNOTATIONS_BIT = 6;
+ private static final int FILL_IN_FORM_BIT = 9;
+ private static final int EXTRACT_FOR_ACCESSIBILITY_BIT = 10;
+ private static final int ASSEMBLE_DOCUMENT_BIT = 11;
+ private static final int DEGRADED_PRINT_BIT = 12;
+
+ /**
+ * Default constructor that uses Version 2, Revision 3, 40 bit encryption,
+ * all permissions allowed.
+ */
+ public PDStandardEncryption()
+ {
+ super();
+ encryptionDictionary.setItem( COSName.FILTER, COSName.getPDFName( FILTER_NAME ) );
+ setVersion( PDEncryptionDictionary.VERSION1_40_BIT_ALGORITHM );
+ setRevision( PDStandardEncryption.REVISION2 );
+ setPermissions( DEFAULT_PERMISSIONS );
+ }
+
+ /**
+ * Constructor from existing dictionary.
+ *
+ * @param dict The existing encryption dictionary.
+ */
+ public PDStandardEncryption( COSDictionary dict )
+ {
+ super( dict );
+ }
+
+ /**
+ * This will return the R entry of the encryption dictionary.<br /><br />
+ * See PDF Reference 1.4 Table 3.14.
+ *
+ * @return The encryption revision to use.
+ */
+ public int getRevision()
+ {
+ int revision = DEFAULT_VERSION;
+ COSNumber cosRevision = (COSNumber)encryptionDictionary.getDictionaryObject( COSName.getPDFName( "R" ) );
+ if( cosRevision != null )
+ {
+ revision = cosRevision.intValue();
+ }
+ return revision;
+ }
+
+ /**
+ * This will set the R entry of the encryption dictionary.<br /><br />
+ * See PDF Reference 1.4 Table 3.14. <br /><br/>
+ *
+ * <b>Note: This value is used to decrypt the pdf document. If you change this when
+ * the document is encrypted then decryption will fail!.</b>
+ *
+ * @param revision The new encryption version.
+ */
+ public void setRevision( int revision )
+ {
+ encryptionDictionary.setItem( COSName.getPDFName( "R" ), new COSInteger( revision ) );
+ }
+
+ /**
+ * This will get the O entry in the standard encryption dictionary.
+ *
+ * @return A 32 byte array or null if there is no owner key.
+ */
+ public byte[] getOwnerKey()
+ {
+ byte[] o = null;
+ COSString owner = (COSString)encryptionDictionary.getDictionaryObject( COSName.getPDFName( "O" ) );
+ if( owner != null )
+ {
+ o = owner.getBytes();
+ }
+ return o;
+ }
+
+ /**
+ * This will set the O entry in the standard encryption dictionary.
+ *
+ * @param o A 32 byte array or null if there is no owner key.
+ *
+ * @throws IOException If there is an error setting the data.
+ */
+ public void setOwnerKey( byte[] o ) throws IOException
+ {
+ COSString owner = new COSString();
+ owner.append( o );
+ encryptionDictionary.setItem( COSName.getPDFName( "O" ), owner );
+ }
+
+ /**
+ * This will get the U entry in the standard encryption dictionary.
+ *
+ * @return A 32 byte array or null if there is no user key.
+ */
+ public byte[] getUserKey()
+ {
+ byte[] u = null;
+ COSString user = (COSString)encryptionDictionary.getDictionaryObject( COSName.getPDFName( "U" ) );
+ if( user != null )
+ {
+ u = user.getBytes();
+ }
+ return u;
+ }
+
+ /**
+ * This will set the U entry in the standard encryption dictionary.
+ *
+ * @param u A 32 byte array.
+ *
+ * @throws IOException If there is an error setting the data.
+ */
+ public void setUserKey( byte[] u ) throws IOException
+ {
+ COSString user = new COSString();
+ user.append( u );
+ encryptionDictionary.setItem( COSName.getPDFName( "U" ), user );
+ }
+
+ /**
+ * This will get the permissions bit mask.
+ *
+ * @return The permissions bit mask.
+ */
+ public int getPermissions()
+ {
+ int permissions = 0;
+ COSInteger p = (COSInteger)encryptionDictionary.getDictionaryObject( COSName.getPDFName( "P" ) );
+ if( p != null )
+ {
+ permissions = p.intValue();
+ }
+ return permissions;
+ }
+
+ /**
+ * This will set the permissions bit mask.
+ *
+ * @param p The new permissions bit mask
+ */
+ public void setPermissions( int p )
+ {
+ encryptionDictionary.setItem( COSName.getPDFName( "P" ), new COSInteger( p ) );
+ }
+
+ private boolean isPermissionBitOn( int bit )
+ {
+ return (getPermissions() & (1 << (bit-1))) != 0;
+ }
+
+ private boolean setPermissionBit( int bit, boolean value )
+ {
+ int permissions = getPermissions();
+ if( value )
+ {
+ permissions = permissions | (1 << (bit-1));
+ }
+ else
+ {
+ permissions = permissions & (0xFFFFFFFF ^ (1 << (bit-1)));
+ }
+ setPermissions( permissions );
+
+ return (getPermissions() & (1 << (bit-1))) != 0;
+ }
+
+ /**
+ * This will tell if the user can print.
+ *
+ * @return true If supplied with the user password they are allowed to print.
+ */
+ public boolean canPrint()
+ {
+ return isPermissionBitOn( PRINT_BIT );
+ }
+
+ /**
+ * Set if the user can print.
+ *
+ * @param allowPrinting A boolean determining if the user can print.
+ */
+ public void setCanPrint( boolean allowPrinting )
+ {
+ setPermissionBit( PRINT_BIT, allowPrinting );
+ }
+
+ /**
+ * This will tell if the user can modify contents of the document.
+ *
+ * @return true If supplied with the user password they are allowed to modify the document
+ */
+ public boolean canModify()
+ {
+ return isPermissionBitOn( MODIFICATION_BIT );
+ }
+
+ /**
+ * Set if the user can modify the document.
+ *
+ * @param allowModifications A boolean determining if the user can modify the document.
+ */
+ public void setCanModify( boolean allowModifications )
+ {
+ setPermissionBit( MODIFICATION_BIT, allowModifications );
+ }
+
+ /**
+ * This will tell if the user can extract text and images from the PDF document.
+ *
+ * @return true If supplied with the user password they are allowed to extract content
+ * from the PDF document
+ */
+ public boolean canExtractContent()
+ {
+ return isPermissionBitOn( EXTRACT_BIT );
+ }
+
+ /**
+ * Set if the user can extract content from the document.
+ *
+ * @param allowExtraction A boolean determining if the user can extract content
+ * from the document.
+ */
+ public void setCanExtractContent( boolean allowExtraction )
+ {
+ setPermissionBit( EXTRACT_BIT, allowExtraction );
+ }
+
+ /**
+ * This will tell if the user can add/modify text annotations, fill in interactive forms fields.
+ *
+ * @return true If supplied with the user password they are allowed to modify annotations.
+ */
+ public boolean canModifyAnnotations()
+ {
+ return isPermissionBitOn( MODIFY_ANNOTATIONS_BIT );
+ }
+
+ /**
+ * Set if the user can modify annotations.
+ *
+ * @param allowAnnotationModification A boolean determining if the user can modify annotations.
+ */
+ public void setCanModifyAnnotations( boolean allowAnnotationModification )
+ {
+ setPermissionBit( MODIFY_ANNOTATIONS_BIT, allowAnnotationModification );
+ }
+
+ /**
+ * This will tell if the user can fill in interactive forms.
+ *
+ * @return true If supplied with the user password they are allowed to fill in form fields.
+ */
+ public boolean canFillInForm()
+ {
+ return isPermissionBitOn( FILL_IN_FORM_BIT );
+ }
+
+ /**
+ * Set if the user can fill in interactive forms.
+ *
+ * @param allowFillingInForm A boolean determining if the user can fill in interactive forms.
+ */
+ public void setCanFillInForm( boolean allowFillingInForm )
+ {
+ setPermissionBit( FILL_IN_FORM_BIT, allowFillingInForm );
+ }
+
+ /**
+ * This will tell if the user can extract text and images from the PDF document
+ * for accessibility purposes.
+ *
+ * @return true If supplied with the user password they are allowed to extract content
+ * from the PDF document
+ */
+ public boolean canExtractForAccessibility()
+ {
+ return isPermissionBitOn( EXTRACT_FOR_ACCESSIBILITY_BIT );
+ }
+
+ /**
+ * Set if the user can extract content from the document for accessibility purposes.
+ *
+ * @param allowExtraction A boolean determining if the user can extract content
+ * from the document.
+ */
+ public void setCanExtractForAccessibility( boolean allowExtraction )
+ {
+ setPermissionBit( EXTRACT_FOR_ACCESSIBILITY_BIT, allowExtraction );
+ }
+
+ /**
+ * This will tell if the user can insert/rotate/delete pages.
+ *
+ * @return true If supplied with the user password they are allowed to extract content
+ * from the PDF document
+ */
+ public boolean canAssembleDocument()
+ {
+ return isPermissionBitOn( ASSEMBLE_DOCUMENT_BIT );
+ }
+
+ /**
+ * Set if the user can insert/rotate/delete pages.
+ *
+ * @param allowAssembly A boolean determining if the user can assemble the document.
+ */
+ public void setCanAssembleDocument( boolean allowAssembly )
+ {
+ setPermissionBit( ASSEMBLE_DOCUMENT_BIT, allowAssembly );
+ }
+
+ /**
+ * This will tell if the user can print the document in a degraded format.
+ *
+ * @return true If supplied with the user password they are allowed to print the
+ * document in a degraded format.
+ */
+ public boolean canPrintDegraded()
+ {
+ return isPermissionBitOn( DEGRADED_PRINT_BIT );
+ }
+
+ /**
+ * Set if the user can print the document in a degraded format.
+ *
+ * @param allowAssembly A boolean determining if the user can print the
+ * document in a degraded format.
+ */
+ public void setCanPrintDegraded( boolean allowAssembly )
+ {
+ setPermissionBit( DEGRADED_PRINT_BIT, allowAssembly );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/encryption/package.html b/src/main/java/org/pdfbox/pdmodel/encryption/package.html
new file mode 100644
index 0000000..a458817
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/encryption/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The encryption package will handle the PDF document security handlers and the functionality of pluggable security handlers.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFAnnotation.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFAnnotation.java
new file mode 100644
index 0000000..3d121e7
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFAnnotation.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents an FDF annotation that is part of the FDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class FDFAnnotation implements COSObjectable
+{
+ private COSDictionary annot;
+
+ /**
+ * Default constructor.
+ */
+ public FDFAnnotation()
+ {
+ annot = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The FDF annotation.
+ */
+ public FDFAnnotation( COSDictionary a )
+ {
+ annot = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return annot;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return annot;
+ }
+
+ /**
+ * This will get the page number or null if it does not exist.
+ *
+ * @return The page number.
+ */
+ public Integer getPage()
+ {
+ Integer retval = null;
+ COSNumber page = (COSNumber)annot.getDictionaryObject( "Page" );
+ if( page != null )
+ {
+ retval = new Integer( page.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the page.
+ *
+ * @param page The page number.
+ */
+ public void setPage( int page )
+ {
+ annot.setItem( "Page", new COSInteger( page ) );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFCatalog.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFCatalog.java
new file mode 100644
index 0000000..f6959ee
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFCatalog.java
@@ -0,0 +1,195 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+
+import org.w3c.dom.Element;
+
+/**
+ * This represents an FDF catalog that is part of the FDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class FDFCatalog implements COSObjectable
+{
+ private COSDictionary catalog;
+
+ /**
+ * Default constructor.
+ */
+ public FDFCatalog()
+ {
+ catalog = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cat The FDF documents catalog.
+ */
+ public FDFCatalog( COSDictionary cat )
+ {
+ catalog = cat;
+ }
+
+ /**
+ * This will create an FDF catalog from an XFDF XML document.
+ *
+ * @param element The XML document that contains the XFDF data.
+ * @throws IOException If there is an error reading from the dom.
+ */
+ public FDFCatalog( Element element ) throws IOException
+ {
+ this();
+ FDFDictionary fdfDict = new FDFDictionary( element );
+ setFDF( fdfDict );
+ }
+
+ /**
+ * This will write this element as an XML document.
+ *
+ * @param output The stream to write the xml to.
+ *
+ * @throws IOException If there is an error writing the XML.
+ */
+ public void writeXML( Writer output ) throws IOException
+ {
+ FDFDictionary fdf = getFDF();
+ fdf.writeXML( output );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return catalog;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return catalog;
+ }
+
+ /**
+ * This will get the version that was specified in the catalog dictionary.
+ *
+ * @return The FDF version.
+ */
+ public String getVersion()
+ {
+ return catalog.getNameAsString( "Version" );
+ }
+
+ /**
+ * This will set the version of the FDF document.
+ *
+ * @param version The new version for the FDF document.
+ */
+ public void setVersion( String version )
+ {
+ catalog.setName( "Version", version );
+ }
+
+ /**
+ * This will get the FDF dictionary.
+ *
+ * @return The FDF dictionary.
+ */
+ public FDFDictionary getFDF()
+ {
+ COSDictionary fdf = (COSDictionary)catalog.getDictionaryObject( "FDF" );
+ FDFDictionary retval = null;
+ if( fdf != null )
+ {
+ retval = new FDFDictionary( fdf );
+ }
+ else
+ {
+ retval = new FDFDictionary();
+ setFDF( retval );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the FDF document.
+ *
+ * @param fdf The new FDF dictionary.
+ */
+ public void setFDF( FDFDictionary fdf )
+ {
+ catalog.setItem( "FDF", fdf );
+ }
+
+ /**
+ * This will get the signature or null if there is none.
+ *
+ * @return The signature.
+ */
+ public PDSignature getSignature()
+ {
+ PDSignature signature = null;
+ COSDictionary sig = (COSDictionary)catalog.getDictionaryObject( "Sig" );
+ if( sig != null )
+ {
+ signature = new PDSignature( sig );
+ }
+ return signature;
+ }
+
+ /**
+ * This will set the signature that is associated with this catalog.
+ *
+ * @param sig The new signature.
+ */
+ public void setSignature( PDSignature sig )
+ {
+ catalog.setItem( "Sig", sig );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFDictionary.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFDictionary.java
new file mode 100644
index 0000000..ab25bc7
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFDictionary.java
@@ -0,0 +1,465 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
+import org.pdfbox.pdmodel.common.filespecification.PDSimpleFileSpecification;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * This represents an FDF dictionary that is part of the FDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class FDFDictionary implements COSObjectable
+{
+ private COSDictionary fdf;
+
+ /**
+ * Default constructor.
+ */
+ public FDFDictionary()
+ {
+ fdf = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fdfDictionary The FDF documents catalog.
+ */
+ public FDFDictionary( COSDictionary fdfDictionary )
+ {
+ fdf = fdfDictionary;
+ }
+
+ /**
+ * This will create an FDF dictionary from an XFDF XML document.
+ *
+ * @param fdfXML The XML document that contains the XFDF data.
+ * @throws IOException If there is an error reading from the dom.
+ */
+ public FDFDictionary( Element fdfXML ) throws IOException
+ {
+ this();
+ NodeList nodeList = fdfXML.getChildNodes();
+ for( int i=0; i<nodeList.getLength(); i++ )
+ {
+ Node node = nodeList.item( i );
+ if( node instanceof Element )
+ {
+ Element child = (Element)node;
+ if( child.getTagName().equals( "f" ) )
+ {
+ PDSimpleFileSpecification fs = new PDSimpleFileSpecification();
+ fs.setFile( child.getAttribute( "href" ) );
+
+ }
+ else if( child.getTagName().equals( "ids" ) )
+ {
+ COSArray ids = new COSArray();
+ String original = child.getAttribute( "original" );
+ String modified = child.getAttribute( "modified" );
+ ids.add( COSString.createFromHexString( original ) );
+ ids.add( COSString.createFromHexString( modified ) );
+ setID( ids );
+ }
+ else if( child.getTagName().equals( "fields" ) )
+ {
+ NodeList fields = child.getElementsByTagName( "field" );
+ List fieldList = new ArrayList();
+ for( int f=0; f<fields.getLength(); f++ )
+ {
+ fieldList.add( new FDFField( (Element)fields.item( f ) ) );
+ }
+ setFields( fieldList );
+ }
+ }
+ }
+ }
+
+ /**
+ * This will write this element as an XML document.
+ *
+ * @param output The stream to write the xml to.
+ *
+ * @throws IOException If there is an error writing the XML.
+ */
+ public void writeXML( Writer output ) throws IOException
+ {
+ PDFileSpecification fs = this.getFile();
+ if( fs != null )
+ {
+ output.write( "<f href=\"" + fs.getFile() + "\" />\n" );
+ }
+ COSArray ids = this.getID();
+ if( ids != null )
+ {
+ COSString original = (COSString)ids.getObject( 0 );
+ COSString modified = (COSString)ids.getObject( 1 );
+ output.write( "<ids original=\"" + original.getHexString() + "\" " );
+ output.write( "modified=\"" + modified.getHexString() + "\" />\n");
+ }
+ List fields = getFields();
+ if( fields != null && fields.size() > 0 )
+ {
+ output.write( "<fields>\n" );
+ for( int i=0; i<fields.size(); i++ )
+ {
+ ((FDFField)fields.get( i )).writeXML( output );
+ }
+ output.write( "</fields>\n" );
+ }
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return fdf;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return fdf;
+ }
+
+ /**
+ * The source file or target file: the PDF document file that
+ * this FDF file was exported from or is intended to be imported into.
+ *
+ * @return The F entry of the FDF dictionary.
+ */
+ public PDFileSpecification getFile()
+ {
+ return PDFileSpecification.createFS( fdf.getDictionaryObject( "F" ) );
+ }
+
+ /**
+ * This will set the file specification.
+ *
+ * @param fs The file specification.
+ */
+ public void setFile( PDFileSpecification fs )
+ {
+ fdf.setItem( "F", fs );
+ }
+
+ /**
+ * This is the FDF id.
+ *
+ * @return The FDF ID.
+ */
+ public COSArray getID()
+ {
+ return (COSArray)fdf.getDictionaryObject( "ID" );
+ }
+
+ /**
+ * This will set the FDF id.
+ *
+ * @param id The new id for the FDF.
+ */
+ public void setID( COSArray id )
+ {
+ fdf.setItem( "ID", id );
+ }
+
+ /**
+ * This will get the list of FDF Fields. This will return a list of FDFField
+ * objects.
+ *
+ * @return A list of FDF fields.
+ */
+ public List getFields()
+ {
+ List retval = null;
+ COSArray fieldArray = (COSArray)fdf.getDictionaryObject( "Fields" );
+ if( fieldArray != null )
+ {
+ List fields = new ArrayList();
+ for( int i=0; i<fieldArray.size(); i++ )
+ {
+ fields.add( new FDFField( (COSDictionary)fieldArray.getObject( i ) ) );
+ }
+ retval = new COSArrayList( fields, fieldArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of fields. This should be a list of FDFField objects.
+ *
+ * @param fields The list of fields.
+ */
+ public void setFields( List fields )
+ {
+ fdf.setItem( "Fields", COSArrayList.converterToCOSArray( fields ) );
+ }
+
+ /**
+ * This will get the status string to be displayed as the result of an
+ * action.
+ *
+ * @return The status.
+ */
+ public String getStatus()
+ {
+ return fdf.getString( "Status" );
+ }
+
+ /**
+ * This will set the status string.
+ *
+ * @param status The new status string.
+ */
+ public void setStatus( String status )
+ {
+ fdf.setString( "Status", status );
+ }
+
+ /**
+ * This will get the list of FDF Pages. This will return a list of FDFPage objects.
+ *
+ * @return A list of FDF pages.
+ */
+ public List getPages()
+ {
+ List retval = null;
+ COSArray pageArray = (COSArray)fdf.getDictionaryObject( "Pages" );
+ if( pageArray != null )
+ {
+ List pages = new ArrayList();
+ for( int i=0; i<pageArray.size(); i++ )
+ {
+ pages.add( new FDFPage( (COSDictionary)pageArray.get( i ) ) );
+ }
+ retval = new COSArrayList( pages, pageArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of pages. This should be a list of FDFPage objects.
+ *
+ *
+ * @param pages The list of pages.
+ */
+ public void setPages( List pages )
+ {
+ fdf.setItem( "Pages", COSArrayList.converterToCOSArray( pages ) );
+ }
+
+ /**
+ * The encoding to be used for a FDF field. The default is PDFDocEncoding
+ * and this method will never return null.
+ *
+ * @return The encoding value.
+ */
+ public String getEncoding()
+ {
+ String encoding = fdf.getNameAsString( "Encoding" );
+ if( encoding == null )
+ {
+ encoding = "PDFDocEncoding";
+ }
+ return encoding;
+
+ }
+
+ /**
+ * This will set the encoding.
+ *
+ * @param encoding The new encoding.
+ */
+ public void setEncoding( String encoding )
+ {
+ fdf.setName( "Encoding", encoding );
+ }
+
+ /**
+ * This will get the list of FDF Annotations. This will return a list of FDFAnnotation objects
+ * or null if the entry is not set.
+ *
+ * @return A list of FDF annotations.
+ */
+ public List getAnnotations()
+ {
+ List retval = null;
+ COSArray annotArray = (COSArray)fdf.getDictionaryObject( "Annots" );
+ if( annotArray != null )
+ {
+ List annots = new ArrayList();
+ for( int i=0; i<annotArray.size(); i++ )
+ {
+ annots.add( new FDFAnnotation( (COSDictionary)annotArray.getObject( i ) ) );
+ }
+ retval = new COSArrayList( annots, annotArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of annotations. This should be a list of FDFAnnotation objects.
+ *
+ *
+ * @param annots The list of annotations.
+ */
+ public void setAnnotations( List annots )
+ {
+ fdf.setItem( "Annots", COSArrayList.converterToCOSArray( annots ) );
+ }
+
+ /**
+ * This will get the incremental updates since the PDF was last opened.
+ *
+ * @return The differences entry of the FDF dictionary.
+ */
+ public COSStream getDifferences()
+ {
+ return (COSStream)fdf.getDictionaryObject( "Differences" );
+ }
+
+ /**
+ * This will set the differences stream.
+ *
+ * @param diff The new differences stream.
+ */
+ public void setDifferences( COSStream diff )
+ {
+ fdf.setItem( "Differences", diff );
+ }
+
+ /**
+ * This will get the target frame in the browser to open this document.
+ *
+ * @return The target frame.
+ */
+ public String getTarget()
+ {
+ return fdf.getString( "Target" );
+ }
+
+ /**
+ * This will set the target frame in the browser to open this document.
+ *
+ * @param target The new target frame.
+ */
+ public void setTarget( String target )
+ {
+ fdf.setString( "Target", target );
+ }
+
+ /**
+ * This will get the list of embedded FDF entries, or null if the entry is null.
+ * This will return a list of PDFileSpecification objects.
+ *
+ * @return A list of embedded FDF files.
+ */
+ public List getEmbeddedFDFs()
+ {
+ List retval = null;
+ COSArray embeddedArray = (COSArray)fdf.getDictionaryObject( "EmbeddedFDFs" );
+ if( embeddedArray != null )
+ {
+ List embedded = new ArrayList();
+ for( int i=0; i<embeddedArray.size(); i++ )
+ {
+ embedded.add( PDFileSpecification.createFS( embeddedArray.get( i ) ) );
+ }
+ retval = new COSArrayList( embedded, embeddedArray );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of embedded FDFs. This should be a list of
+ * PDFileSpecification objects.
+ *
+ *
+ * @param embedded The list of embedded FDFs.
+ */
+ public void setEmbeddedFDFs( List embedded )
+ {
+ fdf.setItem( "EmbeddedFDFs", COSArrayList.converterToCOSArray( embedded ) );
+ }
+
+ /**
+ * This will get the java script entry.
+ *
+ * @return The java script entry describing javascript commands.
+ */
+ public FDFJavaScript getJavaScript()
+ {
+ FDFJavaScript fs = null;
+ COSDictionary dic = (COSDictionary)fdf.getDictionaryObject( "JavaScript" );
+ if( dic != null )
+ {
+ fs = new FDFJavaScript( dic );
+ }
+ return fs;
+ }
+
+ /**
+ * This will set the JavaScript entry.
+ *
+ * @param js The javascript entries.
+ */
+ public void setJavaScript( FDFJavaScript js )
+ {
+ fdf.setItem( "JavaScript", js );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFDocument.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFDocument.java
new file mode 100644
index 0000000..8350314
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFDocument.java
@@ -0,0 +1,377 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.exceptions.COSVisitorException;
+
+import org.pdfbox.pdfparser.PDFParser;
+
+import org.pdfbox.pdfwriter.COSWriter;
+
+import org.pdfbox.util.XMLUtil;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * This is the in-memory representation of the FDF document. You need to call
+ * close() on this object when you are done using it!!
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.5 $
+ */
+public class FDFDocument
+{
+ private COSDocument document;
+
+ /**
+ * Constructor, creates a new FDF document.
+ *
+ * @throws IOException If there is an error creating this document.
+ */
+ public FDFDocument() throws IOException
+ {
+ document = new COSDocument();
+ document.setHeaderString( "%FDF-1.2" );
+
+ //First we need a trailer
+ document.setTrailer( new COSDictionary() );
+
+ //Next we need the root dictionary.
+ FDFCatalog catalog = new FDFCatalog();
+ setCatalog( catalog );
+ }
+
+ /**
+ * Constructor that uses an existing document. The COSDocument that
+ * is passed in must be valid.
+ *
+ * @param doc The COSDocument that this document wraps.
+ */
+ public FDFDocument( COSDocument doc )
+ {
+ document = doc;
+ }
+
+ /**
+ * This will create an FDF document from an XFDF XML document.
+ *
+ * @param doc The XML document that contains the XFDF data.
+ * @throws IOException If there is an error reading from the dom.
+ */
+ public FDFDocument( Document doc ) throws IOException
+ {
+ this();
+ Element xfdf = doc.getDocumentElement();
+ if( !xfdf.getNodeName().equals( "xfdf" ) )
+ {
+ throw new IOException( "Error while importing xfdf document, " +
+ "root should be 'xfdf' and not '" + xfdf.getNodeName() + "'" );
+ }
+ FDFCatalog cat = new FDFCatalog( xfdf );
+ setCatalog( cat );
+ }
+
+ /**
+ * This will write this element as an XML document.
+ *
+ * @param output The stream to write the xml to.
+ *
+ * @throws IOException If there is an error writing the XML.
+ */
+ public void writeXML( Writer output ) throws IOException
+ {
+ output.write( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
+ output.write( "<xfdf xmlns=\"http://ns.adobe.com/xfdf/\" xml:space=\"preserve\">\n" );
+
+ getCatalog().writeXML( output );
+
+ output.write( "</xfdf>\n" );
+ }
+
+
+
+ /**
+ * This will get the low level document.
+ *
+ * @return The document that this layer sits on top of.
+ */
+ public COSDocument getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * This will get the FDF Catalog. This is guaranteed to not return null.
+ *
+ * @return The documents /Root dictionary
+ */
+ public FDFCatalog getCatalog()
+ {
+ FDFCatalog retval = null;
+ COSDictionary trailer = document.getTrailer();
+ COSDictionary root = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
+ if( root == null )
+ {
+ retval = new FDFCatalog();
+ setCatalog( retval );
+ }
+ else
+ {
+ retval = new FDFCatalog( root );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the FDF catalog for this FDF document.
+ *
+ * @param cat The FDF catalog.
+ */
+ public void setCatalog( FDFCatalog cat )
+ {
+ COSDictionary trailer = document.getTrailer();
+ trailer.setItem( COSName.ROOT, cat );
+ }
+
+ /**
+ * This will load a document from a file.
+ *
+ * @param filename The name of the file to load.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static FDFDocument load( String filename ) throws IOException
+ {
+ return load( new BufferedInputStream( new FileInputStream( filename ) ) );
+ }
+
+ /**
+ * This will load a document from a file.
+ *
+ * @param file The name of the file to load.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static FDFDocument load( File file ) throws IOException
+ {
+ return load( new BufferedInputStream( new FileInputStream( file ) ) );
+ }
+
+ /**
+ * This will load a document from an input stream.
+ *
+ * @param input The stream that contains the document.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static FDFDocument load( InputStream input ) throws IOException
+ {
+ PDFParser parser = new PDFParser( input );
+ parser.parse();
+ return parser.getFDFDocument();
+ }
+
+ /**
+ * This will load a document from a file.
+ *
+ * @param filename The name of the file to load.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static FDFDocument loadXFDF( String filename ) throws IOException
+ {
+ return loadXFDF( new BufferedInputStream( new FileInputStream( filename ) ) );
+ }
+
+ /**
+ * This will load a document from a file.
+ *
+ * @param file The name of the file to load.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static FDFDocument loadXFDF( File file ) throws IOException
+ {
+ return loadXFDF( new BufferedInputStream( new FileInputStream( file ) ) );
+ }
+
+ /**
+ * This will load a document from an input stream.
+ *
+ * @param input The stream that contains the document.
+ *
+ * @return The document that was loaded.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public static FDFDocument loadXFDF( InputStream input ) throws IOException
+ {
+ Document doc = XMLUtil.parse( input );
+ return new FDFDocument( doc );
+ }
+
+ /**
+ * This will save this document to the filesystem.
+ *
+ * @param fileName The file to save as.
+ *
+ * @throws IOException If there is an error saving the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void save( File fileName ) throws IOException, COSVisitorException
+ {
+ save( new FileOutputStream( fileName ) );
+ }
+
+ /**
+ * This will save this document to the filesystem.
+ *
+ * @param fileName The file to save as.
+ *
+ * @throws IOException If there is an error saving the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void save( String fileName ) throws IOException, COSVisitorException
+ {
+ save( new FileOutputStream( fileName ) );
+ }
+
+ /**
+ * This will save the document to an output stream.
+ *
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void save( OutputStream output ) throws IOException, COSVisitorException
+ {
+ COSWriter writer = null;
+ try
+ {
+ writer = new COSWriter( output );
+ writer.write( document );
+ writer.close();
+ }
+ finally
+ {
+ if( writer != null )
+ {
+ writer.close();
+ }
+ }
+ }
+
+ /**
+ * This will save this document to the filesystem.
+ *
+ * @param fileName The file to save as.
+ *
+ * @throws IOException If there is an error saving the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void saveXFDF( File fileName ) throws IOException, COSVisitorException
+ {
+ saveXFDF( new BufferedWriter( new FileWriter( fileName ) ) );
+ }
+
+ /**
+ * This will save this document to the filesystem.
+ *
+ * @param fileName The file to save as.
+ *
+ * @throws IOException If there is an error saving the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void saveXFDF( String fileName ) throws IOException, COSVisitorException
+ {
+ saveXFDF( new BufferedWriter( new FileWriter( fileName ) ) );
+ }
+
+ /**
+ * This will save the document to an output stream and close the stream.
+ *
+ * @param output The stream to write to.
+ *
+ * @throws IOException If there is an error writing the document.
+ * @throws COSVisitorException If an error occurs while generating the data.
+ */
+ public void saveXFDF( Writer output ) throws IOException, COSVisitorException
+ {
+ try
+ {
+ writeXML( output );
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.close();
+ }
+ }
+ }
+
+ /**
+ * This will close the underlying COSDocument object.
+ *
+ * @throws IOException If there is an error releasing resources.
+ */
+ public void close() throws IOException
+ {
+ document.close();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFField.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFField.java
new file mode 100644
index 0000000..35b90ed
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFField.java
@@ -0,0 +1,763 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.PDTextStream;
+
+import org.pdfbox.pdmodel.interactive.action.PDActionFactory;
+import org.pdfbox.pdmodel.interactive.action.PDAdditionalActions;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+
+import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+
+import org.pdfbox.util.XMLUtil;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * This represents an FDF field that is part of the FDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class FDFField implements COSObjectable
+{
+ private COSDictionary field;
+
+ /**
+ * Default constructor.
+ */
+ public FDFField()
+ {
+ field = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param f The FDF field.
+ */
+ public FDFField( COSDictionary f )
+ {
+ field = f;
+ }
+
+ /**
+ * This will create an FDF field from an XFDF XML document.
+ *
+ * @param fieldXML The XML document that contains the XFDF data.
+ * @throws IOException If there is an error reading from the dom.
+ */
+ public FDFField( Element fieldXML ) throws IOException
+ {
+ this();
+ this.setPartialFieldName( fieldXML.getAttribute( "name" ) );
+ NodeList nodeList = fieldXML.getChildNodes();
+ List kids = new ArrayList();
+ for( int i=0; i<nodeList.getLength(); i++ )
+ {
+ Node node = nodeList.item( i );
+ if( node instanceof Element )
+ {
+ Element child = (Element)node;
+ if( child.getTagName().equals( "value" ) )
+ {
+ setValue( XMLUtil.getNodeValue( child ) );
+ }
+ else if( child.getTagName().equals( "value-richtext" ) )
+ {
+ setRichText( new PDTextStream( XMLUtil.getNodeValue( child ) ) );
+ }
+ else if( child.getTagName().equals( "field" ) )
+ {
+ kids.add( new FDFField( child ) );
+ }
+ }
+ }
+ if( kids.size() > 0 )
+ {
+ setKids( kids );
+ }
+
+ }
+
+ /**
+ * This will write this element as an XML document.
+ *
+ * @param output The stream to write the xml to.
+ *
+ * @throws IOException If there is an error writing the XML.
+ */
+ public void writeXML( Writer output ) throws IOException
+ {
+ output.write( "<field name=\"" + getPartialFieldName() + "\">\n");
+ Object value = getValue();
+ if( value != null )
+ {
+ output.write( "<value>" + value + "</value>\n" );
+ }
+ PDTextStream rt = getRichText();
+ if( rt != null )
+ {
+ output.write( "<value-richtext>" + rt.getAsString() + "</value-richtext>\n" );
+ }
+ List kids = getKids();
+ if( kids != null )
+ {
+ for( int i=0; i<kids.size(); i++ )
+ {
+ ((FDFField)kids.get( i ) ).writeXML( output );
+ }
+ }
+ output.write( "</field>\n");
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return field;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return field;
+ }
+
+ /**
+ * This will get the list of kids. This will return a list of FDFField objects.
+ * This will return null if the underlying list is null.
+ *
+ * @return The list of kids.
+ */
+ public List getKids()
+ {
+ COSArray kids = (COSArray)field.getDictionaryObject( "Kids" );
+ List retval = null;
+ if( kids != null )
+ {
+ List actuals = new ArrayList();
+ for( int i=0; i<kids.size(); i++ )
+ {
+ actuals.add( new FDFField( (COSDictionary)kids.getObject( i ) ) );
+ }
+ retval = new COSArrayList( actuals, kids );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of kids.
+ *
+ * @param kids A list of FDFField objects.
+ */
+ public void setKids( List kids )
+ {
+ field.setItem( "Kids", COSArrayList.converterToCOSArray( kids ) );
+ }
+
+ /**
+ * This will get the "T" entry in the field dictionary. A partial field
+ * name. Where the fully qualified field name is a concatenation of
+ * the parent's fully qualified field name and "." as a separator. For example<br/>
+ * Address.State<br />
+ * Address.City<br />
+ *
+ * @return The partial field name.
+ */
+ public String getPartialFieldName()
+ {
+ return field.getString( "T" );
+ }
+
+ /**
+ * This will set the partial field name.
+ *
+ * @param partial The partial field name.
+ */
+ public void setPartialFieldName( String partial )
+ {
+ field.setString( "T", partial );
+ }
+
+ /**
+ * This will set the value for the field. This will return type will either be <br />
+ * String : Checkboxes, Radio Button <br />
+ * java.util.List of strings: Choice Field
+ * PDTextStream: Textfields
+ *
+ * @return The value of the field.
+ *
+ * @throws IOException If there is an error getting the value.
+ */
+ public Object getValue() throws IOException
+ {
+ Object retval = null;
+ COSBase value = field.getDictionaryObject( "V" );
+ if( value instanceof COSName )
+ {
+ retval = ((COSName)value).getName();
+ }
+ else if( value instanceof COSArray )
+ {
+ retval = COSArrayList.convertCOSStringCOSArrayToList( (COSArray)value );
+ }
+ else if( value instanceof COSString || value instanceof COSStream )
+ {
+ retval = PDTextStream.createTextStream( value );
+ }
+ else if( value == null )
+ {
+ //Ok, value is null so do nothing
+ }
+ else
+ {
+ throw new IOException( "Error:Unknown type for field import" + value );
+ }
+ return retval;
+ }
+
+ /**
+ * You should pass in a string, or a java.util.List of strings to set the
+ * value.
+ *
+ * @param value The value that should populate when imported.
+ *
+ * @throws IOException If there is an error setting the value.
+ */
+ public void setValue( Object value ) throws IOException
+ {
+ COSBase cos = null;
+ if( value instanceof List )
+ {
+ cos = COSArrayList.convertStringListToCOSStringCOSArray( (List)value );
+ }
+ else if( value instanceof String )
+ {
+ cos = COSName.getPDFName( (String)value );
+ }
+ else if( value instanceof COSObjectable )
+ {
+ cos = ((COSObjectable)value).getCOSObject();
+ }
+ else if( value == null )
+ {
+ //do nothing and let cos remain null as well.
+ }
+ else
+ {
+ throw new IOException( "Error:Unknown type for field import" + value );
+ }
+ field.setItem( "V", cos );
+ }
+
+ /**
+ * This will get the Ff entry of the cos dictionary. If it it not present then
+ * this method will return null.
+ *
+ * @return The field flags.
+ */
+ public Integer getFieldFlags()
+ {
+ Integer retval = null;
+ COSNumber ff = (COSNumber)field.getDictionaryObject( "Ff" );
+ if( ff != null )
+ {
+ retval = new Integer( ff.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The Ff entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the field flags.
+ */
+ public void setFieldFlags( Integer ff )
+ {
+ COSInteger value = null;
+ if( ff != null )
+ {
+ value = new COSInteger( ff.intValue() );
+ }
+ field.setItem( "Ff", value );
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The Ff entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the field flags.
+ */
+ public void setFieldFlags( int ff )
+ {
+ field.setItem( "Ff", new COSInteger( ff ) );
+ }
+
+ /**
+ * This will get the SetFf entry of the cos dictionary. If it it not present then
+ * this method will return null.
+ *
+ * @return The field flags.
+ */
+ public Integer getSetFieldFlags()
+ {
+ Integer retval = null;
+ COSNumber ff = (COSNumber)field.getDictionaryObject( "SetFf" );
+ if( ff != null )
+ {
+ retval = new Integer( ff.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The SetFf entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "set field flags".
+ */
+ public void setSetFieldFlags( Integer ff )
+ {
+ COSInteger value = null;
+ if( ff != null )
+ {
+ value = new COSInteger( ff.intValue() );
+ }
+ field.setItem( "SetFf", value );
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The SetFf entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "set field flags".
+ */
+ public void setSetFieldFlags( int ff )
+ {
+ field.setItem( "SetFf", new COSInteger( ff ) );
+ }
+
+ /**
+ * This will get the ClrFf entry of the cos dictionary. If it it not present then
+ * this method will return null.
+ *
+ * @return The field flags.
+ */
+ public Integer getClearFieldFlags()
+ {
+ Integer retval = null;
+ COSNumber ff = (COSNumber)field.getDictionaryObject( "ClrFf" );
+ if( ff != null )
+ {
+ retval = new Integer( ff.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The ClrFf entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "clear field flags".
+ */
+ public void setClearFieldFlags( Integer ff )
+ {
+ COSInteger value = null;
+ if( ff != null )
+ {
+ value = new COSInteger( ff.intValue() );
+ }
+ field.setItem( "ClrFf", value );
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The ClrFf entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "clear field flags".
+ */
+ public void setClearFieldFlags( int ff )
+ {
+ field.setItem( "ClrFf", new COSInteger( ff ) );
+ }
+
+ /**
+ * This will get the F entry of the cos dictionary. If it it not present then
+ * this method will return null.
+ *
+ * @return The widget field flags.
+ */
+ public Integer getWidgetFieldFlags()
+ {
+ Integer retval = null;
+ COSNumber f = (COSNumber)field.getDictionaryObject( "F" );
+ if( f != null )
+ {
+ retval = new Integer( f.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the widget field flags that are associated with this field. The F entry
+ * in the FDF field dictionary.
+ *
+ * @param f The new value for the field flags.
+ */
+ public void setWidgetFieldFlags( Integer f )
+ {
+ COSInteger value = null;
+ if( f != null )
+ {
+ value = new COSInteger( f.intValue() );
+ }
+ field.setItem( "F", value );
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The F entry
+ * in the FDF field dictionary.
+ *
+ * @param f The new value for the field flags.
+ */
+ public void setWidgetFieldFlags( int f )
+ {
+ field.setItem( "F", new COSInteger( f ) );
+ }
+
+ /**
+ * This will get the SetF entry of the cos dictionary. If it it not present then
+ * this method will return null.
+ *
+ * @return The field flags.
+ */
+ public Integer getSetWidgetFieldFlags()
+ {
+ Integer retval = null;
+ COSNumber ff = (COSNumber)field.getDictionaryObject( "SetF" );
+ if( ff != null )
+ {
+ retval = new Integer( ff.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the widget field flags that are associated with this field. The SetF entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "set widget field flags".
+ */
+ public void setSetWidgetFieldFlags( Integer ff )
+ {
+ COSInteger value = null;
+ if( ff != null )
+ {
+ value = new COSInteger( ff.intValue() );
+ }
+ field.setItem( "SetF", value );
+ }
+
+ /**
+ * This will get the widget field flags that are associated with this field. The SetF entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "set widget field flags".
+ */
+ public void setSetWidgetFieldFlags( int ff )
+ {
+ field.setItem( "SetF", new COSInteger( ff ) );
+ }
+
+ /**
+ * This will get the ClrF entry of the cos dictionary. If it it not present then
+ * this method will return null.
+ *
+ * @return The widget field flags.
+ */
+ public Integer getClearWidgetFieldFlags()
+ {
+ Integer retval = null;
+ COSNumber ff = (COSNumber)field.getDictionaryObject( "ClrF" );
+ if( ff != null )
+ {
+ retval = new Integer( ff.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The ClrF entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "clear widget field flags".
+ */
+ public void setClearWidgetFieldFlags( Integer ff )
+ {
+ COSInteger value = null;
+ if( ff != null )
+ {
+ value = new COSInteger( ff.intValue() );
+ }
+ field.setItem( "ClrF", value );
+ }
+
+ /**
+ * This will get the field flags that are associated with this field. The ClrF entry
+ * in the FDF field dictionary.
+ *
+ * @param ff The new value for the "clear field flags".
+ */
+ public void setClearWidgetFieldFlags( int ff )
+ {
+ field.setItem( "ClrF", new COSInteger( ff ) );
+ }
+
+ /**
+ * This will get the appearance dictionary that specifies the appearance of
+ * a pushbutton field.
+ *
+ * @return The AP entry of this dictionary.
+ */
+ public PDAppearanceDictionary getAppearanceDictionary()
+ {
+ PDAppearanceDictionary retval = null;
+ COSDictionary dict = (COSDictionary)field.getDictionaryObject( "AP" );
+ if( dict != null )
+ {
+ retval = new PDAppearanceDictionary( dict );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the appearance dictionary.
+ *
+ * @param ap The apperance dictionary.
+ */
+ public void setAppearanceDictionary( PDAppearanceDictionary ap )
+ {
+ field.setItem( "AP", ap );
+ }
+
+ /**
+ * This will get named page references..
+ *
+ * @return The named page references.
+ */
+ public FDFNamedPageReference getAppearanceStreamReference()
+ {
+ FDFNamedPageReference retval = null;
+ COSDictionary ref = (COSDictionary)field.getDictionaryObject( "APRef" );
+ if( ref != null )
+ {
+ retval = new FDFNamedPageReference( ref );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the named page references.
+ *
+ * @param ref The named page references.
+ */
+ public void setAppearanceStreamReference( FDFNamedPageReference ref )
+ {
+ field.setItem( "APRef", ref );
+ }
+
+ /**
+ * This will get the icon fit that is associated with this field.
+ *
+ * @return The IF entry.
+ */
+ public FDFIconFit getIconFit()
+ {
+ FDFIconFit retval = null;
+ COSDictionary dic = (COSDictionary)field.getDictionaryObject( "IF" );
+ if( dic != null )
+ {
+ retval = new FDFIconFit( dic );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the icon fit entry.
+ *
+ * @param fit The icon fit object.
+ */
+ public void setIconFit( FDFIconFit fit )
+ {
+ field.setItem( "IF", fit );
+ }
+
+ /**
+ * This will return a list of options for a choice field. The value in the
+ * list will be 1 of 2 types. java.lang.String or FDFOptionElement.
+ *
+ * @return A list of all options.
+ */
+ public List getOptions()
+ {
+ List retval = null;
+ COSArray array = (COSArray)field.getDictionaryObject( "Opt" );
+ if( array != null )
+ {
+ List objects = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ COSBase next = array.getObject( i );
+ if( next instanceof COSString )
+ {
+ objects.add( ((COSString)next).getString() );
+ }
+ else
+ {
+ COSArray value = (COSArray)next;
+ objects.add( new FDFOptionElement( value ) );
+ }
+ }
+ retval = new COSArrayList( objects, array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the options for the choice field. The objects in the list
+ * should either be java.lang.String or FDFOptionElement.
+ *
+ * @param options The options to set.
+ */
+ public void setOptions( List options )
+ {
+ COSArray value = COSArrayList.converterToCOSArray( options );
+ field.setItem( "Opt", value );
+ }
+
+ /**
+ * This will get the action that is associated with this field.
+ *
+ * @return The A entry in the field dictionary.
+ */
+ public PDAction getAction()
+ {
+ return PDActionFactory.createAction( (COSDictionary)field.getDictionaryObject( "A" ) );
+ }
+
+ /**
+ * This will set the action that is associated with this field.
+ *
+ * @param a The new action.
+ */
+ public void setAction( PDAction a )
+ {
+ field.setItem( "A", a );
+ }
+
+ /**
+ * This will get a list of additional actions that will get executed based
+ * on events.
+ *
+ * @return The AA entry in this field dictionary.
+ */
+ public PDAdditionalActions getAdditionalActions()
+ {
+ PDAdditionalActions retval = null;
+ COSDictionary dict = (COSDictionary)field.getDictionaryObject( "AA" );
+ if( dict != null )
+ {
+ retval = new PDAdditionalActions( dict );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the additional actions that are associated with this field.
+ *
+ * @param aa The additional actions.
+ */
+ public void setAdditionalActions( PDAdditionalActions aa )
+ {
+ field.setItem( "AA", aa );
+ }
+
+ /**
+ * This will set the rich text that is associated with this field.
+ *
+ * @return The rich text XHTML stream.
+ */
+ public PDTextStream getRichText()
+ {
+ COSBase rv = field.getDictionaryObject( "RV" );
+ return PDTextStream.createTextStream( rv );
+ }
+
+ /**
+ * This will set the rich text value.
+ *
+ * @param rv The rich text value for the stream.
+ */
+ public void setRichText( PDTextStream rv )
+ {
+ field.setItem( "RV", rv );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFIconFit.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFIconFit.java
new file mode 100644
index 0000000..c765bfc
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFIconFit.java
@@ -0,0 +1,227 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDRange;
+
+/**
+ * This represents an Icon fit dictionary for an FDF field.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class FDFIconFit implements COSObjectable
+{
+ private COSDictionary fit;
+
+ /**
+ * A scale option.
+ */
+ public static final String SCALE_OPTION_ALWAYS = "A";
+ /**
+ * A scale option.
+ */
+ public static final String SCALE_OPTION_ONLY_WHEN_ICON_IS_BIGGER = "B";
+ /**
+ * A scale option.
+ */
+ public static final String SCALE_OPTION_ONLY_WHEN_ICON_IS_SMALLER = "S";
+ /**
+ * A scale option.
+ */
+ public static final String SCALE_OPTION_NEVER = "N";
+
+ /**
+ * Scale to fill with of annotation, disregarding aspect ratio.
+ */
+ public static final String SCALE_TYPE_ANAMORPHIC = "A";
+ /**
+ * Scale to fit width or height, smaller of two, while retaining aspect ration.
+ */
+ public static final String SCALE_TYPE_PROPORTIONAL = "P";
+
+
+
+ /**
+ * Default constructor.
+ */
+ public FDFIconFit()
+ {
+ fit = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param f The icon fit dictionary.
+ */
+ public FDFIconFit( COSDictionary f )
+ {
+ fit = f;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return fit;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return fit;
+ }
+
+ /**
+ * This will get the scale option. See the SCALE_OPTION_XXX constants. This
+ * is guaranteed to never return null. Default: Always
+ *
+ * @return The scale option.
+ */
+ public String getScaleOption()
+ {
+ String retval = fit.getNameAsString( "SW" );
+ if( retval == null )
+ {
+ retval = SCALE_OPTION_ALWAYS;
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the scale option for the icon. Set the SCALE_OPTION_XXX constants.
+ *
+ * @param option The scale option.
+ */
+ public void setScaleOption( String option )
+ {
+ fit.setName( "SW", option );
+ }
+
+ /**
+ * This will get the scale type. See the SCALE_TYPE_XXX constants. This is
+ * guaranteed to never return null. Default: Proportional
+ *
+ * @return The scale type.
+ */
+ public String getScaleType()
+ {
+ String retval = fit.getNameAsString( "S" );
+ if( retval == null )
+ {
+ retval = SCALE_TYPE_PROPORTIONAL;
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the scale type. See the SCALE_TYPE_XXX constants.
+ *
+ * @param scale The scale type.
+ */
+ public void setScaleType( String scale )
+ {
+ fit.setName( "S", scale );
+ }
+
+ /**
+ * This is guaranteed to never return null.<br />
+ *
+ * To quote the PDF Spec
+ * "An array of two numbers between 0.0 and 1.0 indicating the fraction of leftover
+ * space to allocate at the left and bottom of the icon. A value of [0.0 0.0] positions the
+ * icon at the bottom-left corner of the annotation rectangle; a value of [0.5 0.5] centers it
+ * within the rectangle. This entry is used only if the icon is scaled proportionally. Default
+ * value: [0.5 0.5]."
+ *
+ * @return The fractional space to allocate.
+ */
+ public PDRange getFractionalSpaceToAllocate()
+ {
+ PDRange retval = null;
+ COSArray array = (COSArray)fit.getDictionaryObject( "A" );
+ if( array == null )
+ {
+ retval = new PDRange();
+ retval.setMin( .5f );
+ retval.setMax( .5f );
+ setFractionalSpaceToAllocate( retval );
+ }
+ else
+ {
+ retval = new PDRange( array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set frational space to allocate.
+ *
+ * @param space The space to allocate.
+ */
+ public void setFractionalSpaceToAllocate( PDRange space )
+ {
+ fit.setItem( "A", space );
+ }
+
+ /**
+ * This will tell if the icon should scale to fit the annotation bounds. Default: false
+ *
+ * @return A flag telling if the icon should scale.
+ */
+ public boolean shouldScaleToFitAnnotation()
+ {
+ return fit.getBoolean( "FB", false );
+ }
+
+ /**
+ * This will tell the icon to scale.
+ *
+ * @param value The flag value.
+ */
+ public void setScaleToFitAnnotation( boolean value )
+ {
+ fit.setBoolean( "FB", value );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFJavaScript.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFJavaScript.java
new file mode 100644
index 0000000..dfa6b0f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFJavaScript.java
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.PDTextStream;
+import org.pdfbox.pdmodel.common.PDNamedTextStream;
+
+/**
+ * This represents an FDF JavaScript dictionary that is part of the FDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class FDFJavaScript implements COSObjectable
+{
+ private COSDictionary js;
+
+ /**
+ * Default constructor.
+ */
+ public FDFJavaScript()
+ {
+ js = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param javaScript The FDF java script.
+ */
+ public FDFJavaScript( COSDictionary javaScript )
+ {
+ js = javaScript;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return js;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return js;
+ }
+
+ /**
+ * This will get the javascript that is executed before the import.
+ *
+ * @return Some javascript code.
+ */
+ public PDTextStream getBefore()
+ {
+ return PDTextStream.createTextStream( js.getDictionaryObject( "Before" ) );
+ }
+
+ /**
+ * This will set the javascript code the will get execute before the import.
+ *
+ * @param before A reference to some javascript code.
+ */
+ public void setBefore( PDTextStream before )
+ {
+ js.setItem( "Before", before );
+ }
+
+ /**
+ * This will get the javascript that is executed after the import.
+ *
+ * @return Some javascript code.
+ */
+ public PDTextStream getAfter()
+ {
+ return PDTextStream.createTextStream( js.getDictionaryObject( "After" ) );
+ }
+
+ /**
+ * This will set the javascript code the will get execute after the import.
+ *
+ * @param after A reference to some javascript code.
+ */
+ public void setAfter( PDTextStream after )
+ {
+ js.setItem( "After", after );
+ }
+
+ /**
+ * This will return a list of PDNamedTextStream objects. This is the "Doc"
+ * entry of the pdf document. These will be added to the PDF documents
+ * javascript name tree. This will not return null.
+ *
+ * @return A list of all named javascript entries.
+ */
+ public List getNamedJavaScripts()
+ {
+ List retval = null;
+ COSArray array = (COSArray)js.getDictionaryObject( "Doc" );
+ List namedStreams = new ArrayList();
+ if( array == null )
+ {
+ array = new COSArray();
+ js.setItem( "Doc", array );
+ }
+ for( int i=0; i<array.size(); i++ )
+ {
+ COSName name = (COSName)array.get( i );
+ i++;
+ COSBase stream = array.get( i );
+ PDNamedTextStream namedStream = new PDNamedTextStream( name, stream );
+ namedStreams.add( namedStream );
+ }
+ return new COSArrayList( namedStreams, array );
+ }
+
+ /**
+ * This should be a list of PDNamedTextStream objects.
+ *
+ * @param namedStreams The named streams.
+ */
+ public void setNamedJavaScripts( List namedStreams )
+ {
+ COSArray array = COSArrayList.converterToCOSArray( namedStreams );
+ js.setItem( "Doc", array );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFNamedPageReference.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFNamedPageReference.java
new file mode 100644
index 0000000..c03689b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFNamedPageReference.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.pdmodel.common.filespecification.PDFileSpecification;;
+
+/**
+ * This represents an FDF named page reference that is part of the FDF field.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class FDFNamedPageReference implements COSObjectable
+{
+ private COSDictionary ref;
+
+ /**
+ * Default constructor.
+ */
+ public FDFNamedPageReference()
+ {
+ ref = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param r The FDF named page reference dictionary.
+ */
+ public FDFNamedPageReference( COSDictionary r )
+ {
+ ref = r;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return ref;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return ref;
+ }
+
+ /**
+ * This will get the name of the referenced page. A required parameter.
+ *
+ * @return The name of the referenced page.
+ */
+ public String getName()
+ {
+ return ref.getString( "Name" );
+ }
+
+ /**
+ * This will set the name of the referenced page.
+ *
+ * @param name The referenced page name.
+ */
+ public void setName( String name )
+ {
+ ref.setString( "Name", name );
+ }
+
+ /**
+ * This will get the file specification of this reference. An optional parameter.
+ *
+ * @return The F entry for this dictionary.
+ */
+ public PDFileSpecification getFileSpecification()
+ {
+ COSBase fs = ref.getDictionaryObject( "F" );
+ return PDFileSpecification.createFS( fs );
+ }
+
+ /**
+ * This will set the file specification for this named page reference.
+ *
+ * @param fs The file specification to set.
+ */
+ public void setFileSpecification( PDFileSpecification fs )
+ {
+ ref.setItem( "F", fs );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFOptionElement.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFOptionElement.java
new file mode 100644
index 0000000..d82a715
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFOptionElement.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents an object that can be used in a Field's Opt entry to represent
+ * an available option and a default appearance string.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class FDFOptionElement implements COSObjectable
+{
+ private COSArray option;
+
+ /**
+ * Default constructor.
+ */
+ public FDFOptionElement()
+ {
+ option = new COSArray();
+ option.add( new COSString( "" ) );
+ option.add( new COSString( "" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param o The option element.
+ */
+ public FDFOptionElement( COSArray o )
+ {
+ option = o;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return option;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSArray getCOSArray()
+ {
+ return option;
+ }
+
+ /**
+ * This will get the string of one of the available options. A required element.
+ *
+ * @return An available option.
+ */
+ public String getOption()
+ {
+ return ((COSString)option.getObject( 0 ) ).getString();
+ }
+
+ /**
+ * This will set the string for an available option.
+ *
+ * @param opt One of the available options.
+ */
+ public void setOption( String opt )
+ {
+ option.set( 0, new COSString( opt ) );
+ }
+
+ /**
+ * This will get the string of default appearance string. A required element.
+ *
+ * @return A default appearance string.
+ */
+ public String getDefaultAppearanceString()
+ {
+ return ((COSString)option.getObject( 1 ) ).getString();
+ }
+
+ /**
+ * This will set the default appearance string.
+ *
+ * @param da The default appearance string.
+ */
+ public void setDefaultAppearanceString( String da )
+ {
+ option.set( 1, new COSString( da ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFPage.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFPage.java
new file mode 100644
index 0000000..1f5e0b9
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFPage.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents an FDF page that is part of the FDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class FDFPage implements COSObjectable
+{
+ private COSDictionary page;
+
+ /**
+ * Default constructor.
+ */
+ public FDFPage()
+ {
+ page = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param p The FDF page.
+ */
+ public FDFPage( COSDictionary p )
+ {
+ page = p;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return page;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return page;
+ }
+
+ /**
+ * This will get a list of FDFTemplage objects that describe the named pages
+ * that serve as templates.
+ *
+ * @return A list of templates.
+ */
+ public List getTemplates()
+ {
+ List retval = null;
+ COSArray array = (COSArray)page.getDictionaryObject( "Templates" );
+ if( array != null )
+ {
+ List objects = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ objects.add( new FDFTemplate( (COSDictionary)array.getObject( i ) ) );
+ }
+ retval = new COSArrayList( objects, array );
+ }
+ return retval;
+ }
+
+ /**
+ * A list of FDFTemplate objects.
+ *
+ * @param templates A list of templates for this Page.
+ */
+ public void setTemplates( List templates )
+ {
+ page.setItem( "Templates", COSArrayList.converterToCOSArray( templates ) );
+ }
+
+ /**
+ * This will get the FDF page info object.
+ *
+ * @return The Page info.
+ */
+ public FDFPageInfo getPageInfo()
+ {
+ FDFPageInfo retval = null;
+ COSDictionary dict = (COSDictionary)page.getDictionaryObject( "Info" );
+ if( dict != null )
+ {
+ retval = new FDFPageInfo( dict );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the page info.
+ *
+ * @param info The new page info dictionary.
+ */
+ public void setPageInfo( FDFPageInfo info )
+ {
+ page.setItem( "Info", info );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFPageInfo.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFPageInfo.java
new file mode 100644
index 0000000..e30441e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFPageInfo.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents an FDF page info that is part of the FDF page.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class FDFPageInfo implements COSObjectable
+{
+ private COSDictionary pageInfo;
+
+ /**
+ * Default constructor.
+ */
+ public FDFPageInfo()
+ {
+ pageInfo = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param p The FDF page.
+ */
+ public FDFPageInfo( COSDictionary p )
+ {
+ pageInfo = p;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return pageInfo;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return pageInfo;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/FDFTemplate.java b/src/main/java/org/pdfbox/pdmodel/fdf/FDFTemplate.java
new file mode 100644
index 0000000..217ca30
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/FDFTemplate.java
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.fdf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents an FDF template that is part of the FDF page.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class FDFTemplate implements COSObjectable
+{
+ private COSDictionary template;
+
+ /**
+ * Default constructor.
+ */
+ public FDFTemplate()
+ {
+ template = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param t The FDF page template.
+ */
+ public FDFTemplate( COSDictionary t )
+ {
+ template = t;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return template;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return template;
+ }
+
+ /**
+ * This is the template reference.
+ *
+ * @return The template reference.
+ */
+ public FDFNamedPageReference getTemplateReference()
+ {
+ FDFNamedPageReference retval = null;
+ COSDictionary dict = (COSDictionary)template.getDictionaryObject( "TRef" );
+ if( dict != null )
+ {
+ retval = new FDFNamedPageReference( dict );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the template reference.
+ *
+ * @param tRef The template reference.
+ */
+ public void setTemplateReference( FDFNamedPageReference tRef )
+ {
+ template.setItem( "TRef", tRef );
+ }
+
+ /**
+ * This will get a list of fields that are part of this template.
+ *
+ * @return A list of fields.
+ */
+ public List getFields()
+ {
+ List retval = null;
+ COSArray array = (COSArray)template.getDictionaryObject( "Fields" );
+ if( array != null )
+ {
+ List fields = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ fields.add( new FDFField( (COSDictionary)array.getObject( i ) ) );
+ }
+ retval = new COSArrayList( fields, array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a list of fields for this template.
+ *
+ * @param fields The list of fields to set for this template.
+ */
+ public void setFields( List fields )
+ {
+ template.setItem( "Fields", COSArrayList.converterToCOSArray( fields ) );
+ }
+
+ /**
+ * A flag telling if the fields imported from the template may be renamed if there are conflicts.
+ *
+ * @return A flag telling if the fields can be renamed.
+ */
+ public boolean shouldRename()
+ {
+ return template.getBoolean( "Rename", false );
+ }
+
+ /**
+ * This will set if the fields can be renamed.
+ *
+ * @param value The flag value.
+ */
+ public void setRename( boolean value )
+ {
+ template.setBoolean( "Rename", value );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/fdf/package.html b/src/main/java/org/pdfbox/pdmodel/fdf/package.html
new file mode 100644
index 0000000..e2b6fea
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/fdf/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The fdf package will handle all of the logic used for FDF objects inside of the PDF/FDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDCIDFont.java b/src/main/java/org/pdfbox/pdmodel/font/PDCIDFont.java
new file mode 100644
index 0000000..e13b821
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDCIDFont.java
@@ -0,0 +1,248 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.awt.Graphics;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This is implementation for the CIDFontType0/CIDFontType2 Fonts.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public abstract class PDCIDFont extends PDFont
+{
+ private static Logger log = Logger.getLogger(PDCIDFont.class);
+
+
+ private Map widthCache = new HashMap();
+
+ /**
+ * Constructor.
+ */
+ public PDCIDFont()
+ {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDCIDFont( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+
+ /**
+ * @see PDFont#drawString( String, Graphics, float, float, float, float, float )
+ */
+ public void drawString( String string, Graphics g, float fontSize,
+ float xScale, float yScale, float x, float y )
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * This will get the fonts bouding box.
+ *
+ * @return The fonts bouding box.
+ *
+ * @throws IOException If there is an error getting the font bounding box.
+ */
+ public PDRectangle getFontBoundingBox() throws IOException
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * This will get the default width. The default value for the default width is 1000.
+ *
+ * @return The default width for the glyphs in this font.
+ */
+ public long getDefaultWidth()
+ {
+ long dw = 1000;
+ COSNumber number = (COSNumber)font.getDictionaryObject( COSName.getPDFName( "DW" ) );
+ if( number != null )
+ {
+ dw = number.intValue();
+ }
+ return dw;
+ }
+
+ /**
+ * This will set the default width for the glyphs of this font.
+ *
+ * @param dw The default width.
+ */
+ public void setDefaultWidth( long dw )
+ {
+ font.setItem( COSName.getPDFName( "DW" ), new COSInteger( dw ) );
+ }
+
+ /**
+ * This will get the font width for a character.
+ *
+ * @param c The character code to get the width for.
+ * @param offset The offset into the array.
+ * @param length The length of the data.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public float getFontWidth( byte[] c, int offset, int length ) throws IOException
+ {
+
+ float retval = 0.0f;
+ int code = getCodeFromArray( c, offset, length );
+
+ Float widthFloat = (Float)widthCache.get( new Integer( code ) );
+ if( widthFloat == null )
+ {
+ COSArray widths = (COSArray)font.getDictionaryObject( COSName.getPDFName( "W" ) );
+
+ if( widths != null )
+ {
+ boolean foundWidth = false;
+ for( int i=0; !foundWidth && i<widths.size(); i++ )
+ {
+ COSNumber firstCode = (COSNumber)widths.getObject( i++ );
+ COSBase next = widths.getObject( i );
+ if( next instanceof COSArray )
+ {
+ COSArray array = (COSArray)next;
+ if( code >= firstCode.intValue() &&
+ code < firstCode.intValue() + array.size() )
+ {
+ COSNumber rangeWidth =
+ (COSNumber)array.get( code - firstCode.intValue() );
+ retval = rangeWidth.floatValue();
+ foundWidth = true;
+ }
+ }
+ else
+ {
+ COSNumber secondCode = (COSNumber)next;
+ i++;
+ COSNumber rangeWidth = (COSNumber)widths.getObject( i );
+ if( code >= firstCode.intValue() &&
+ code <= secondCode.intValue() )
+ {
+ retval = rangeWidth.floatValue();
+ foundWidth = true;
+ }
+ }
+ }
+ widthCache.put( new Integer( code ), new Float( retval ) );
+ }
+ }
+ else
+ {
+ retval = widthFloat.floatValue();
+ }
+
+ if(log.isDebugEnabled() )
+ {
+ log.debug( "PDCIDFontType0Font.getFontWidth( code=" + code +" ) retval=" +retval );
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the average font width for all characters.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public float getAverageFontWidth() throws IOException
+ {
+ float totalWidths = 0.0f;
+ float characterCount = 0.0f;
+ float defaultWidth = getDefaultWidth();
+ COSArray widths = (COSArray)font.getDictionaryObject( COSName.getPDFName( "W" ) );
+
+ if( widths != null )
+ {
+ for( int i=0; i<widths.size(); i++ )
+ {
+ COSNumber firstCode = (COSNumber)widths.getObject( i++ );
+ COSBase next = widths.getObject( i );
+ float nextWidth=0.0f;
+ if( next instanceof COSArray )
+ {
+ COSArray array = (COSArray)next;
+ for( int j=0; j<array.size(); j++ )
+ {
+ COSNumber width = (COSNumber)array.get( j );
+ totalWidths+=width.floatValue();
+ characterCount += 1;
+ }
+ }
+ else
+ {
+ COSNumber secondCode = (COSNumber)next;
+ i++;
+ COSNumber rangeWidth = (COSNumber)widths.getObject( i );
+ if( rangeWidth.floatValue() > 0 )
+ {
+ totalWidths += rangeWidth.floatValue();
+ characterCount += 1;
+ }
+ }
+ }
+ }
+ float average = totalWidths / characterCount;
+ if( average <= 0 )
+ {
+ average = defaultWidth;
+ }
+ return average;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType0Font.java b/src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType0Font.java
new file mode 100644
index 0000000..4c6589c
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType0Font.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This is implementation of the CIDFontType0 Font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDCIDFontType0Font extends PDCIDFont
+{
+ private static Logger log = Logger.getLogger(PDCIDFontType0Font.class);
+
+ /**
+ * Constructor.
+ */
+ public PDCIDFontType0Font()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.getPDFName( "CIDFontType0" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDCIDFontType0Font( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType2Font.java b/src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType2Font.java
new file mode 100644
index 0000000..bda6c4a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDCIDFontType2Font.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This is implementation of the CIDFontType2 Font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDCIDFontType2Font extends PDCIDFont
+{
+ private static Logger log = Logger.getLogger(PDCIDFontType2Font.class);
+
+ /**
+ * Constructor.
+ */
+ public PDCIDFontType2Font()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.getPDFName( "CIDFontType2" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDCIDFontType2Font( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDFont.java b/src/main/java/org/pdfbox/pdmodel/font/PDFont.java
new file mode 100644
index 0000000..593c8b0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDFont.java
@@ -0,0 +1,863 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.pdfbox.afmparser.AFMParser;
+
+import org.pdfbox.afmtypes.FontMetric;
+
+import org.pdfbox.cmapparser.CMapParser;
+
+import org.pdfbox.cmaptypes.CMap;
+
+import org.pdfbox.encoding.AFMEncoding;
+import org.pdfbox.encoding.DictionaryEncoding;
+import org.pdfbox.encoding.Encoding;
+import org.pdfbox.encoding.EncodingManager;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDMatrix;
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import org.pdfbox.util.ResourceLoader;
+
+import org.apache.log4j.Logger;
+
+import java.awt.Graphics;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * This is the base class for all PDF fonts.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.32 $
+ */
+public abstract class PDFont implements COSObjectable
+{
+ private static Logger log = Logger.getLogger(PDFont.class);
+
+ /**
+ * The cos dictionary for this font.
+ */
+ protected COSDictionary font;
+
+ /**
+ * This is only used if this is a font object and it has an encoding.
+ */
+ private Encoding fontEncoding = null;
+ /**
+ * This is only used if this is a font object and it has an encoding and it is
+ * a type0 font with a cmap.
+ */
+ private CMap cmap = null;
+
+ private static Map afmResources = null;
+ private static Map cmapObjects = null;
+ private static Map afmObjects = null;
+ private static Map cmapSubstitutions = null;
+
+ static
+ {
+ //these are read-only once they are created
+ afmResources = new HashMap();
+ cmapSubstitutions = new HashMap();
+
+ //these are read-write
+ cmapObjects = Collections.synchronizedMap( new HashMap() );
+ afmObjects = Collections.synchronizedMap( new HashMap() );
+
+
+ afmResources.put( COSName.getPDFName( "Courier-Bold" ), "Resources/afm/Courier-Bold.afm" );
+ afmResources.put( COSName.getPDFName( "Courier-BoldOblique" ), "Resources/afm/Courier-BoldOblique.afm" );
+ afmResources.put( COSName.getPDFName( "Courier" ), "Resources/afm/Courier.afm" );
+ afmResources.put( COSName.getPDFName( "Courier-Oblique" ), "Resources/afm/Courier-Oblique.afm" );
+ afmResources.put( COSName.getPDFName( "Helvetica" ), "Resources/afm/Helvetica.afm" );
+ afmResources.put( COSName.getPDFName( "Helvetica-Bold" ), "Resources/afm/Helvetica-Bold.afm" );
+ afmResources.put( COSName.getPDFName( "Helvetica-BoldOblique" ), "Resources/afm/Helvetica-BoldOblique.afm" );
+ afmResources.put( COSName.getPDFName( "Helvetica-Oblique" ), "Resources/afm/Helvetica-Oblique.afm" );
+ afmResources.put( COSName.getPDFName( "Symbol" ), "Resources/afm/Symbol.afm" );
+ afmResources.put( COSName.getPDFName( "Times-Bold" ), "Resources/afm/Times-Bold.afm" );
+ afmResources.put( COSName.getPDFName( "Times-BoldItalic" ), "Resources/afm/Times-BoldItalic.afm" );
+ afmResources.put( COSName.getPDFName( "Times-Italic" ), "Resources/afm/Times-Italic.afm" );
+ afmResources.put( COSName.getPDFName( "Times-Roman" ), "Resources/afm/Times-Roman.afm" );
+ afmResources.put( COSName.getPDFName( "ZapfDingbats" ), "Resources/afm/ZapfDingbats.afm" );
+
+ cmapSubstitutions.put( "ETenms-B5-H", "ETen-B5-H" );
+ cmapSubstitutions.put( "ETenms-B5-V", "ETen-B5-V" );
+ }
+
+ /**
+ * This will clear AFM resources that are stored statically.
+ * This is usually not a problem unless you want to reclaim
+ * resources for a long running process.
+ *
+ * SPECIAL NOTE: The font calculations are currently in COSObject, which
+ * is where they will reside until PDFont is mature enough to take them over.
+ * PDFont is the appropriate place for them and not in COSObject but we need font
+ * calculations for text extractaion. THIS METHOD WILL BE MOVED OR REMOVED
+ * TO ANOTHER LOCATION IN A FUTURE VERSION OF PDFBOX.
+ */
+ public static void clearResources()
+ {
+ afmObjects.clear();
+ cmapObjects.clear();
+ }
+
+ /**
+ * Constructor.
+ */
+ public PDFont()
+ {
+ font = new COSDictionary();
+ font.setItem( COSName.TYPE, COSName.FONT );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDFont( COSDictionary fontDictionary )
+ {
+ font = fontDictionary;
+ }
+
+ /**
+ * @see COSObjectable#getCOSObject()
+ */
+ public COSBase getCOSObject()
+ {
+ return font;
+ }
+
+ /**
+ * This will get the font width for a character.
+ *
+ * @param c The character code to get the width for.
+ * @param offset The offset into the array.
+ * @param length The length of the data.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public abstract float getFontWidth( byte[] c, int offset, int length ) throws IOException;
+
+ /**
+ * This will get the width of this string for this font.
+ *
+ * @param string The string to get the width of.
+ *
+ * @return The width of the string in 1000 units of text space, ie 333 567...
+ *
+ * @throws IOException If there is an error getting the width information.
+ */
+ public float getStringWidth( String string ) throws IOException
+ {
+ byte[] data = string.getBytes();
+ float totalWidth = 0;
+ for( int i=0; i<data.length; i++ )
+ {
+ totalWidth+=getFontWidth( data, i, 1 );
+ }
+ return totalWidth;
+ }
+
+ /**
+ * This will get the average font width for all characters.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public abstract float getAverageFontWidth() throws IOException;
+
+ /**
+ * This will draw a string on a canvas using the font.
+ *
+ * @param string The string to draw.
+ * @param g The graphics to draw onto.
+ * @param fontSize The size of the font to draw.
+ * @param xScale The x scaling percent.
+ * @param yScale The y scaling percent.
+ * @param x The x coordinate to draw at.
+ * @param y The y coordinate to draw at.
+ *
+ * @throws IOException If there is an error drawing the specific string.
+ */
+ public abstract void drawString( String string, Graphics g, float fontSize,
+ float xScale, float yScale, float x, float y ) throws IOException;
+
+ /**
+ * Used for multibyte encodings.
+ *
+ * @param data The array of data.
+ * @param offset The offset into the array.
+ * @param length The number of bytes to use.
+ *
+ * @return The int value of data from the array.
+ */
+ protected int getCodeFromArray( byte[] data, int offset, int length )
+ {
+ int code = 0;
+ for( int i=0; i<length; i++ )
+ {
+ code <<= 8;
+ code = (data[offset+i]+256)%256;
+ }
+ return code;
+ }
+
+ /**
+ * This will attempt to get the font width from an AFM file.
+ *
+ * @param code The character code we are trying to get.
+ *
+ * @return The font width from the AFM file.
+ *
+ * @throws IOException if we cannot find the width.
+ */
+ protected float getFontWidthFromAFMFile( int code ) throws IOException
+ {
+ float retval = 0;
+ FontMetric metric = getAFM();
+ if( metric != null )
+ {
+ Encoding encoding = getEncoding();
+ COSName characterName = encoding.getName( code );
+ retval = metric.getCharacterWidth( characterName.getName() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will attempt to get the average font width from an AFM file.
+ *
+ * @return The average font width from the AFM file.
+ *
+ * @throws IOException if we cannot find the width.
+ */
+ protected float getAverageFontWidthFromAFMFile() throws IOException
+ {
+ float retval = 0;
+ FontMetric metric = getAFM();
+ if( metric != null )
+ {
+ retval = metric.getAverageCharacterWidth();
+ }
+ return retval;
+ }
+
+ /**
+ * This will get an AFM object if one exists.
+ *
+ * @return The afm object from the name.
+ *
+ * @throws IOException If there is an error getting the AFM object.
+ */
+ protected FontMetric getAFM() throws IOException
+ {
+ COSName name = (COSName)font.getDictionaryObject( COSName.BASE_FONT );
+ FontMetric result = null;
+ if( name != null )
+ {
+ result = (FontMetric)afmObjects.get( name );
+ if( result == null )
+ {
+ String resource = (String)afmResources.get( name );
+ if( log.isDebugEnabled() )
+ {
+ log.debug("resource: " + resource + ", name: " + name.getName());
+ }
+ if( resource == null )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "resource is null" );
+ }
+ //ok for now
+ //throw new IOException( "Unknown AFM font '" + name.getName() + "'" );
+ }
+ else
+ {
+ InputStream afmStream = ResourceLoader.loadResource( resource );
+ if( afmStream == null )
+ {
+ throw new IOException( "Can't handle font width:" + resource );
+ }
+ AFMParser parser = new AFMParser( afmStream );
+ parser.parse();
+ result = parser.getResult();
+ afmObjects.put( name, result );
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This will perform the encoding of a character if needed.
+ *
+ * @param c The character to encode.
+ * @param offset The offset into the array to get the data
+ * @param length The number of bytes to read.
+ *
+ * @return The value of the encoded character.
+ *
+ * @throws IOException If there is an error during the encoding.
+ */
+ public String encode( byte[] c, int offset, int length ) throws IOException
+ {
+ String retval = null;
+ COSName fontSubtype = (COSName)font.getDictionaryObject( COSName.SUBTYPE );
+ String fontSubtypeName = fontSubtype.getName();
+ if( fontSubtypeName.equals( "Type0" ) ||
+ fontSubtypeName.equals( "Type1" ) ||
+ fontSubtypeName.equals( "TrueType" ))
+ {
+ if( cmap == null )
+ {
+ if( font.getDictionaryObject( COSName.TO_UNICODE ) != null )
+ {
+ COSStream toUnicode = (COSStream)font.getDictionaryObject( COSName.TO_UNICODE );
+ if( toUnicode != null )
+ {
+ parseCmap( toUnicode.getUnfilteredStream(), null );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Getting embedded CMAP Stream from ToUnicode" );
+ }
+ }
+ }
+ else
+ {
+ COSBase encoding = font.getDictionaryObject( COSName.ENCODING );
+ if( encoding instanceof COSStream )
+ {
+ COSStream encodingStream = (COSStream)encoding;
+ parseCmap( encodingStream.getUnfilteredStream(), null );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Getting embedded CMAP Stream from encoding" );
+ }
+ }
+ else if( fontSubtypeName.equals( "Type0" ) &&
+ encoding instanceof COSName )
+ {
+ COSName encodingName = (COSName)encoding;
+ cmap = (CMap)cmapObjects.get( encodingName );
+ if( cmap != null )
+ {
+ cmap = (CMap)cmapObjects.get( encodingName );
+ }
+ else
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Getting CMAP Stream from resource" );
+ }
+ String cmapName = encodingName.getName();
+ cmapName = performCMAPSubstitution( cmapName );
+ String resourceName = "Resources/cmap/" + cmapName;
+ parseCmap( ResourceLoader.loadResource( resourceName ), encodingName );
+ if( cmap == null && !encodingName.getName().equals( COSName.IDENTITY_H.getName() ) )
+ {
+ throw new IOException( "Error: Could not find predefined " +
+ "CMAP file for '" + encodingName.getName() + "'" );
+ }
+ }
+ }
+ else if( encoding instanceof COSName ||
+ encoding instanceof COSDictionary )
+ {
+ Encoding currentFontEncoding = getEncoding();
+ if( currentFontEncoding != null )
+ {
+ retval = currentFontEncoding.getCharacter( getCodeFromArray( c, offset, length ) );
+ }
+ }
+ else
+ {
+ COSDictionary fontDescriptor =
+ (COSDictionary)font.getDictionaryObject( COSName.FONT_DESC );
+ if( fontSubtypeName.equals( "TrueType" ) &&
+ fontDescriptor != null &&
+ (fontDescriptor.getDictionaryObject( COSName.FONT_FILE )!= null ||
+ fontDescriptor.getDictionaryObject( COSName.FONT_FILE2 ) != null ||
+ fontDescriptor.getDictionaryObject( COSName.FONT_FILE3 ) != null ) )
+ {
+ //If we are using an embedded font then there is not much we can do besides
+ //return the same character codes.
+ //retval = new String( c,offset, length );
+ retval = getStringFromArray( c, offset, length );
+ }
+ else
+ {
+ //this case will be handled below after checking the cmap
+ }
+ }
+ }
+
+
+ }
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "retval=" + retval + " cmap=" + cmap);
+ }
+ if( retval == null && cmap != null )
+ {
+
+ retval = cmap.lookup( c, offset, length );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "cmap.lookup(" +c + ")='" +retval + "'" );
+ }
+ }
+ //if we havn't found a value yet and
+ //we are still on the first byte and
+ //there is no cmap or the cmap does not have 2 byte mappings then try to encode
+ //using fallback methods.
+ if( retval == null &&
+ length == 1 &&
+ (cmap == null || !cmap.hasTwoByteMappings()))
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "No CMAP: Using fallback method");
+ }
+ Encoding encoding = getEncoding();
+ if( encoding != null )
+ {
+ retval = encoding.getCharacter( getCodeFromArray( c, offset, length ) );
+ }
+ if( retval == null )
+ {
+ retval = getStringFromArray( c, offset, length );
+ }
+ }
+ return retval;
+ }
+
+ private static final String[] SINGLE_CHAR_STRING = new String[256];
+ private static final String[][] DOUBLE_CHAR_STRING = new String[256][256];
+ static
+ {
+ for( int i=0; i<256; i++ )
+ {
+ SINGLE_CHAR_STRING[i] = new String( new byte[] {(byte)i} );
+ for( int j=0; j<256; j++ )
+ {
+ DOUBLE_CHAR_STRING[i][j] = new String( new byte[] {(byte)i, (byte)j} );
+ }
+ }
+ }
+
+ private static String getStringFromArray( byte[] c, int offset, int length ) throws IOException
+ {
+ String retval = null;
+ if( length == 1 )
+ {
+ retval = SINGLE_CHAR_STRING[(c[offset]+256)%256];
+ }
+ else if( length == 2 )
+ {
+ retval = DOUBLE_CHAR_STRING[(c[offset]+256)%256][(c[offset+1]+256)%256];
+ }
+ else
+ {
+ throw new IOException( "Error:Unknown character length:" + length );
+ }
+ return retval;
+ }
+
+ /**
+ * Some cmap names are synonyms for other CMAPs. If that is the case
+ * then this method will perform that substitution.
+ *
+ * @param cmapName The name of the cmap to attempt to look up.
+ *
+ * @return Either the original name or the substituted name.
+ */
+ private String performCMAPSubstitution( String cmapName )
+ {
+ String retval = (String)cmapSubstitutions.get( cmapName );
+ if( retval == null )
+ {
+ //if there is no substitution then just return the same value.
+ retval = cmapName;
+ }
+ return retval;
+ }
+
+ private void parseCmap( InputStream cmapStream, COSName encodingName ) throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Parsing a new CMAP for font:" + font );
+ }
+ if( cmapStream != null )
+ {
+ CMapParser parser = new CMapParser( cmapStream, null );
+ parser.parse();
+ cmap = parser.getResult();
+ if( encodingName != null )
+ {
+ cmapObjects.put( encodingName, cmap );
+ }
+ }
+ }
+
+ /**
+ * The will set the encoding for this font.
+ *
+ * @param enc The font encoding.
+ */
+ public void setEncoding( Encoding enc )
+ {
+ font.setItem( COSName.ENCODING, enc );
+ fontEncoding = enc;
+ }
+
+ /**
+ * This will get or create the encoder.
+ *
+ * modified by Christophe Huault : DGBS Strasbourg huault@free.fr october 2004
+ *
+ * @return The encoding to use.
+ *
+ * @throws IOException If there is an error getting the encoding.
+ */
+ public Encoding getEncoding() throws IOException
+ {
+ EncodingManager manager = new EncodingManager();
+ if( fontEncoding == null )
+ {
+ COSBase encoding = font.getDictionaryObject( COSName.ENCODING );
+ if( encoding == null )
+ {
+ FontMetric metric = getAFM();
+ if( metric != null )
+ {
+ fontEncoding = new AFMEncoding( metric );
+ }
+ if( fontEncoding == null )
+ {
+ fontEncoding = manager.getStandardEncoding();
+ }
+ }
+ /**
+ * Si la clé /Encoding existe dans le dictionnaire fonte il y a deux possibilités :
+ * 1er cas : elle est associé à une reference contenant un dictionnaire de type encoding.
+ * Ce dictionnaire PDF est représenté par un DictionaryEncoding.
+ * If the /Encoding Key does exist in the font dictionary, there are two cases :
+ * case one : The value associated with /Encoding is a reference to a dictionary.
+ * This dictionary is represented by an instance of DictionaryEncoding class
+ */
+ else if( encoding instanceof COSDictionary )
+ {
+ COSDictionary encodingDic = (COSDictionary)encoding;
+ //Let's see if the encoding dictionary has a base encoding
+ //If it does not then we will attempt to get it from the font
+ //file
+ COSName baseEncodingName = (COSName) encodingDic.getDictionaryObject(
+ COSName.BASE_ENCODING);
+ //on ajoute une entrée /BaseEncoding dans /Encoding uniquement si elle en est absente
+ //if not find in Encoding dictinary target, we try to find it from else where
+ if( baseEncodingName == null)
+ {
+ COSName fontEncodingFromFile = getEncodingFromFont();
+ encodingDic.setItem(
+ COSName.BASE_ENCODING,
+ fontEncodingFromFile );
+ }
+ fontEncoding = new DictionaryEncoding( encodingDic );
+ }
+ else if( encoding instanceof COSName )
+ {
+ if( !encoding.equals( COSName.IDENTITY_H ) )
+ {
+ fontEncoding = manager.getEncoding( (COSName)encoding );
+ }
+ }
+ else
+ {
+ throw new IOException( "Unexpected encoding type:" + encoding.getClass().getName() );
+ }
+ }
+ return fontEncoding;
+ }
+
+ /**
+ * This will always return "Font" for fonts.
+ *
+ * @return The type of object that this is.
+ */
+ public String getType()
+ {
+ return font.getNameAsString( COSName.TYPE );
+ }
+
+ /**
+ * This will get the subtype of font, Type1, Type3, ...
+ *
+ * @return The type of font that this is.
+ */
+ public String getSubType()
+ {
+ return font.getNameAsString( COSName.SUBTYPE );
+ }
+
+ /**
+ * The PostScript name of the font.
+ *
+ * @return The postscript name of the font.
+ */
+ public String getBaseFont()
+ {
+ return font.getNameAsString( COSName.BASE_FONT );
+ }
+
+ /**
+ * Set the PostScript name of the font.
+ *
+ * @param baseFont The postscript name for the font.
+ */
+ public void setBaseFont( String baseFont )
+ {
+ font.setName( COSName.BASE_FONT, baseFont );
+ }
+
+ /**
+ * The code for the first char or -1 if there is none.
+ *
+ * @return The code for the first character.
+ */
+ public int getFirstChar()
+ {
+ return font.getInt( COSName.FIRST_CHAR, -1 );
+ }
+
+ /**
+ * Set the first character this font supports.
+ *
+ * @param firstChar The first character.
+ */
+ public void setFirstChar( int firstChar )
+ {
+ font.setInt( COSName.FIRST_CHAR, firstChar );
+ }
+
+ /**
+ * The code for the last char or -1 if there is none.
+ *
+ * @return The code for the last character.
+ */
+ public int getLastChar()
+ {
+ return font.getInt( COSName.LAST_CHAR, -1 );
+ }
+
+ /**
+ * Set the last character this font supports.
+ *
+ * @param lastChar The last character.
+ */
+ public void setLastChar( int lastChar )
+ {
+ font.setInt( COSName.LAST_CHAR, lastChar );
+ }
+
+ /**
+ * The widths of the characters. This will be null for the standard 14 fonts.
+ *
+ * @return The widths of the characters.
+ */
+ public List getWidths()
+ {
+ COSArray array = (COSArray)font.getDictionaryObject( COSName.WIDTHS );
+ return COSArrayList.convertFloatCOSArrayToList( array );
+ }
+
+ /**
+ * Set the widths of the characters code.
+ *
+ * @param widths The widths of the character codes.
+ */
+ public void setWidths( List widths )
+ {
+ font.setItem( COSName.WIDTHS, COSArrayList.converterToCOSArray( widths ) );
+ }
+
+ /**
+ * This will get the matrix that is used to transform glyph space to
+ * text space. By default there are 1000 glyph units to 1 text space
+ * unit, but type3 fonts can use any value.
+ *
+ * Note:If this is a type3 font then it can be modified via the PDType3Font.setFontMatrix, otherwise this
+ * is a read-only property.
+ *
+ * @return The matrix to transform from glyph space to text space.
+ */
+ public PDMatrix getFontMatrix()
+ {
+ PDMatrix matrix = null;
+ COSArray array = (COSArray)font.getDictionaryObject( COSName.FONT_MATRIX );
+ if( array == null )
+ {
+ array = new COSArray();
+ array.add( new COSFloat( 0.001f ) );
+ array.add( COSNumber.ZERO );
+ array.add( COSNumber.ZERO );
+ array.add( new COSFloat( 0.001f ) );
+ array.add( COSNumber.ZERO );
+ array.add( COSNumber.ZERO );
+ }
+ matrix = new PDMatrix(array);
+
+ return matrix;
+ }
+
+ /**
+ * Try to get the encoding for the font and add it to the target
+ * the target must be an an Encoding Dictionary.
+ *
+ * added by Christophe Huault : DGBS Strasbourg huault@free.fr october 2004
+ *
+ * @return The encoding from the font.
+ *
+ * @throws IOException If there is an error reading the file.
+ */
+ private COSName getEncodingFromFont() throws IOException
+ {
+ //This whole section of code needs to be replaced with an actual
+ //type1 font parser!!
+
+
+ COSName retvalue = null;
+ //recuperer le programme de fonte dans son stream qui doit se trouver
+ //dans le flux référencé par à la clé FileFont lui même situé dans
+ //le dictionnaire associé à /FontDescriptor du dictionnaire de type /Font courrant
+ //get the font program in the stream which should be located in
+ //the /FileFont Stream object himself in the /FontDescriptior of the current
+ //font dictionary
+ COSDictionary fontDescriptor = (COSDictionary) font.getDictionaryObject(
+ COSName.FONT_DESC);
+ if( fontDescriptor != null )
+ {
+ COSStream fontFile = (COSStream) fontDescriptor.getDictionaryObject(
+ COSName.FONT_FILE);
+ if( fontFile != null )
+ {
+ BufferedReader in =
+ new BufferedReader(new InputStreamReader(fontFile.getUnfilteredStream()));
+ /**
+ * this section parse the FileProgram stream searching for a /Encoding entry
+ * the research stop if the entry "currentdict end" is reach or after 100 lignes
+ */
+ StringTokenizer st = null;
+ boolean found = false;
+ String line = "";
+ String key = null;
+ for( int i = 0; null!=( line = in.readLine() ) &&
+ i < 40 &&
+ !line.equals("currentdict end")
+ && !found; i++)
+ {
+ st = new StringTokenizer(line);
+ if( st.hasMoreTokens() )
+ {
+ key = st.nextToken();
+ if(key.equals("/Encoding") && st.hasMoreTokens() )
+ {
+ COSName value = COSName.getPDFName( st.nextToken() );
+ found = true;
+ if( value.equals( COSName.MAC_ROMAN_ENCODING ) ||
+ value.equals( COSName.PDF_DOC_ENCODING ) ||
+ value.equals( COSName.STANDARD_ENCODING ) ||
+ value.equals( COSName.WIN_ANSI_ENCODING ) )
+ {
+ //value is expected to be one of the encodings
+ //ie. StandardEncoding,WinAnsiEncoding,MacRomanEncoding,PDFDocEncoding
+ retvalue = value;
+ }
+ }
+ }
+ }
+ }
+ }
+ return retvalue;
+ }
+
+ /**
+ * This will get the fonts bouding box.
+ *
+ * @return The fonts bouding box.
+ *
+ * @throws IOException If there is an error getting the bounding box.
+ */
+ public abstract PDRectangle getFontBoundingBox() throws IOException;
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals( Object other )
+ {
+ return other instanceof PDFont && ((PDFont)other).getCOSObject() == this.getCOSObject();
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return this.getCOSObject().hashCode();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptor.java b/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptor.java
new file mode 100644
index 0000000..59668ce
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptor.java
@@ -0,0 +1,530 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+/**
+ * This class represents an interface to the font description. This will depend
+ * on the font type for the actual implementation. If it is a AFM/cmap/or embedded font.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.1 $
+ */
+public abstract class PDFontDescriptor
+{
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_FIXED_PITCH = 1;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_SERIF = 2;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_SYMBOLIC = 3;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_SCRIPT = 4;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_NON_SYMBOLIC = 6;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_ITALIC = 7;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_ALL_CAP = 17;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_SMALL_CAP = 18;
+ /**
+ * A font descriptor flag. See PDF Reference for description.
+ */
+ private static final int FLAG_FORCE_BOLD = 19;
+
+
+ /**
+ * Get the font name.
+ *
+ * @return The name of the font.
+ */
+ public abstract String getFontName();
+
+ /**
+ * This will set the font name.
+ *
+ * @param fontName The new name for the font.
+ */
+ public abstract void setFontName( String fontName );
+
+ /**
+ * A string representing the preferred font family.
+ *
+ * @return The font family.
+ */
+ public abstract String getFontFamily();
+
+ /**
+ * This will set the font family.
+ *
+ * @param fontFamily The font family.
+ */
+ public abstract void setFontFamily( String fontFamily );
+
+ /**
+ * A string representing the preferred font stretch.
+ * According to the PDF Spec:
+ * The font stretch value; it must be one of the following (ordered from
+ * narrowest to widest): UltraCondensed, ExtraCondensed, Condensed, SemiCondensed,
+ * Normal, SemiExpanded, Expanded, ExtraExpanded or UltraExpanded.
+ *
+ * @return The font stretch.
+ */
+ public abstract String getFontStretch();
+
+ /**
+ * This will set the font stretch.
+ *
+ * @param fontStretch The font stretch
+ */
+ public abstract void setFontStretch( String fontStretch );
+
+ /**
+ * The weight of the font. According to the PDF spec "possible values are
+ * 100, 200, 300, 400, 500, 600, 700, 800 or 900" Where a higher number is
+ * more weight and appears to be more bold.
+ *
+ * @return The font weight.
+ */
+ public abstract float getFontWeight();
+
+ /**
+ * Set the weight of the font.
+ *
+ * @param fontWeight The new weight of the font.
+ */
+ public abstract void setFontWeight( float fontWeight );
+
+ /**
+ * This will get the font flags.
+ *
+ * @return The font flags.
+ */
+ public abstract int getFlags();
+
+ /**
+ * This will set the font flags.
+ *
+ * @param flags The new font flags.
+ */
+ public abstract void setFlags( int flags );
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isFixedPitch()
+ {
+ return isFlagBitOn( FLAG_FIXED_PITCH );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setFixedPitch( boolean flag )
+ {
+ setFlagBit( FLAG_FIXED_PITCH, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isSerif()
+ {
+ return isFlagBitOn( FLAG_SERIF );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setSerif( boolean flag )
+ {
+ setFlagBit( FLAG_SERIF, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isSymbolic()
+ {
+ return isFlagBitOn( FLAG_SYMBOLIC );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setSymbolic( boolean flag )
+ {
+ setFlagBit( FLAG_SYMBOLIC, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isScript()
+ {
+ return isFlagBitOn( FLAG_SCRIPT );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setScript( boolean flag )
+ {
+ setFlagBit( FLAG_SCRIPT, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isNonSymbolic()
+ {
+ return isFlagBitOn( FLAG_NON_SYMBOLIC );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setNonSymbolic( boolean flag )
+ {
+ setFlagBit( FLAG_NON_SYMBOLIC, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isItalic()
+ {
+ return isFlagBitOn( FLAG_ITALIC );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setItalic( boolean flag )
+ {
+ setFlagBit( FLAG_ITALIC, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isAllCap()
+ {
+ return isFlagBitOn( FLAG_ALL_CAP);
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setAllCap( boolean flag )
+ {
+ setFlagBit( FLAG_ALL_CAP, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isSmallCap()
+ {
+ return isFlagBitOn( FLAG_SMALL_CAP );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setSmallCap( boolean flag )
+ {
+ setFlagBit( FLAG_SMALL_CAP, flag );
+ }
+
+ /**
+ * A convenience method that checks the flag bit.
+ *
+ * @return The flag value.
+ */
+ public boolean isForceBold()
+ {
+ return isFlagBitOn( FLAG_FORCE_BOLD );
+ }
+
+ /**
+ * A convenience method that sets the flag bit.
+ *
+ * @param flag The flag value.
+ */
+ public void setForceBold( boolean flag )
+ {
+ setFlagBit( FLAG_FORCE_BOLD, flag );
+ }
+
+ private boolean isFlagBitOn( int bit )
+ {
+ return (getFlags() & (1 << (bit-1))) != 0;
+ }
+
+ private void setFlagBit( int bit, boolean value )
+ {
+ int flags = getFlags();
+ if( value )
+ {
+ flags = flags| (1 << (bit-1));
+ }
+ else
+ {
+ flags = flags & (0xFFFFFFFF ^ (1 << (bit-1)));
+ }
+ setFlags( flags );
+ }
+
+ /**
+ * This will get the fonts bouding box.
+ *
+ * @return The fonts bouding box.
+ */
+ public abstract PDRectangle getFontBoundingBox();
+
+ /**
+ * Set the fonts bounding box.
+ *
+ * @param rect The new bouding box.
+ */
+ public abstract void setFontBoundingBox( PDRectangle rect );
+
+ /**
+ * This will get the italic angle for the font.
+ *
+ * @return The italic angle.
+ */
+ public abstract float getItalicAngle();
+
+ /**
+ * This will set the italic angle for the font.
+ *
+ * @param angle The new italic angle for the font.
+ */
+ public abstract void setItalicAngle( float angle );
+
+ /**
+ * This will get the ascent for the font.
+ *
+ * @return The ascent.
+ */
+ public abstract float getAscent();
+
+ /**
+ * This will set the ascent for the font.
+ *
+ * @param ascent The new ascent for the font.
+ */
+ public abstract void setAscent( float ascent );
+
+ /**
+ * This will get the descent for the font.
+ *
+ * @return The descent.
+ */
+ public abstract float getDescent();
+
+ /**
+ * This will set the descent for the font.
+ *
+ * @param descent The new descent for the font.
+ */
+ public abstract void setDescent( float descent );
+
+ /**
+ * This will get the leading for the font.
+ *
+ * @return The leading.
+ */
+ public abstract float getLeading();
+
+ /**
+ * This will set the leading for the font.
+ *
+ * @param leading The new leading for the font.
+ */
+ public abstract void setLeading( float leading );
+
+ /**
+ * This will get the CapHeight for the font.
+ *
+ * @return The cap height.
+ */
+ public abstract float getCapHeight();
+
+ /**
+ * This will set the cap height for the font.
+ *
+ * @param capHeight The new cap height for the font.
+ */
+ public abstract void setCapHeight( float capHeight );
+
+ /**
+ * This will get the x height for the font.
+ *
+ * @return The x height.
+ */
+ public abstract float getXHeight();
+
+ /**
+ * This will set the x height for the font.
+ *
+ * @param xHeight The new x height for the font.
+ */
+ public abstract void setXHeight( float xHeight );
+
+ /**
+ * This will get the stemV for the font.
+ *
+ * @return The stem v value.
+ */
+ public abstract float getStemV();
+
+ /**
+ * This will set the stem V for the font.
+ *
+ * @param stemV The new stem v for the font.
+ */
+ public abstract void setStemV( float stemV );
+
+ /**
+ * This will get the stemH for the font.
+ *
+ * @return The stem h value.
+ */
+ public abstract float getStemH();
+
+ /**
+ * This will set the stem H for the font.
+ *
+ * @param stemH The new stem h for the font.
+ */
+ public abstract void setStemH( float stemH );
+
+ /**
+ * This will get the average width for the font. This is part of the
+ * definition in the font description. If it is not present then PDFBox
+ * will make an attempt to calculate it.
+ *
+ * @return The average width value.
+ *
+ * @throws IOException If there is an error calculating the average width.
+ */
+ public abstract float getAverageWidth() throws IOException;
+
+ /**
+ * This will set the average width for the font.
+ *
+ * @param averageWidth The new average width for the font.
+ */
+ public abstract void setAverageWidth( float averageWidth );
+
+ /**
+ * This will get the max width for the font.
+ *
+ * @return The max width value.
+ */
+ public abstract float getMaxWidth();
+
+ /**
+ * This will set the max width for the font.
+ *
+ * @param maxWidth The new max width for the font.
+ */
+ public abstract void setMaxWidth( float maxWidth );
+
+ /**
+ * This will get the character set for the font.
+ *
+ * @return The character set value.
+ */
+ public abstract String getCharSet();
+
+ /**
+ * This will set the character set for the font.
+ *
+ * @param charSet The new character set for the font.
+ */
+ public abstract void setCharacterSet( String charSet );
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorAFM.java b/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorAFM.java
new file mode 100644
index 0000000..3326b74
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorAFM.java
@@ -0,0 +1,446 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.io.IOException;
+
+import org.pdfbox.afmtypes.FontMetric;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import org.pdfbox.util.BoundingBox;
+
+/**
+ * This class represents the font descriptor when the font information
+ * is coming from an AFM file.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.1 $
+ */
+public class PDFontDescriptorAFM extends PDFontDescriptor
+{
+ private FontMetric afm;
+
+ /**
+ * Constructor.
+ *
+ * @param afmFile The AFM file.
+ */
+ public PDFontDescriptorAFM( FontMetric afmFile )
+ {
+ afm = afmFile;
+ }
+
+ /**
+ * Get the font name.
+ *
+ * @return The name of the font.
+ */
+ public String getFontName()
+ {
+ return afm.getFontName();
+ }
+
+ /**
+ * This will set the font name.
+ *
+ * @param fontName The new name for the font.
+ */
+ public void setFontName( String fontName )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * A string representing the preferred font family.
+ *
+ * @return The font family.
+ */
+ public String getFontFamily()
+ {
+ return afm.getFamilyName();
+ }
+
+ /**
+ * This will set the font family.
+ *
+ * @param fontFamily The font family.
+ */
+ public void setFontFamily( String fontFamily )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * The weight of the font. According to the PDF spec "possible values are
+ * 100, 200, 300, 400, 500, 600, 700, 800 or 900" Where a higher number is
+ * more weight and appears to be more bold.
+ *
+ * @return The font weight.
+ */
+ public float getFontWeight()
+ {
+ String weight = afm.getWeight();
+ float retval = 500;
+ if( weight != null && weight.equalsIgnoreCase( "bold" ) )
+ {
+ retval = 900;
+ }
+ else if( weight != null && weight.equalsIgnoreCase( "light" ) )
+ {
+ retval = 100;
+ }
+ return retval;
+ }
+
+ /**
+ * Set the weight of the font.
+ *
+ * @param fontWeight The new weight of the font.
+ */
+ public void setFontWeight( float fontWeight )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * A string representing the preferred font stretch.
+ *
+ * @return The font stretch.
+ */
+ public String getFontStretch()
+ {
+ return null;
+ }
+
+ /**
+ * This will set the font stretch.
+ *
+ * @param fontStretch The font stretch
+ */
+ public void setFontStretch( String fontStretch )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the font flags.
+ *
+ * @return The font flags.
+ */
+ public int getFlags()
+ {
+ //I believe that the only flag that AFM supports is the is fixed pitch
+ return afm.isFixedPitch() ? 1 : 0;
+ }
+
+ /**
+ * This will set the font flags.
+ *
+ * @param flags The new font flags.
+ */
+ public void setFlags( int flags )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the fonts bouding box.
+ *
+ * @return The fonts bouding box.
+ */
+ public PDRectangle getFontBoundingBox()
+ {
+ BoundingBox box = afm.getFontBBox();
+ PDRectangle retval = null;
+ if( box != null )
+ {
+ retval = new PDRectangle( box );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the fonts bounding box.
+ *
+ * @param rect The new bouding box.
+ */
+ public void setFontBoundingBox( PDRectangle rect )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the italic angle for the font.
+ *
+ * @return The italic angle.
+ */
+ public float getItalicAngle()
+ {
+ return afm.getItalicAngle();
+ }
+
+ /**
+ * This will set the italic angle for the font.
+ *
+ * @param angle The new italic angle for the font.
+ */
+ public void setItalicAngle( float angle )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the ascent for the font.
+ *
+ * @return The ascent.
+ */
+ public float getAscent()
+ {
+ return afm.getAscender();
+ }
+
+ /**
+ * This will set the ascent for the font.
+ *
+ * @param ascent The new ascent for the font.
+ */
+ public void setAscent( float ascent )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the descent for the font.
+ *
+ * @return The descent.
+ */
+ public float getDescent()
+ {
+ return afm.getDescender();
+ }
+
+ /**
+ * This will set the descent for the font.
+ *
+ * @param descent The new descent for the font.
+ */
+ public void setDescent( float descent )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the leading for the font.
+ *
+ * @return The leading.
+ */
+ public float getLeading()
+ {
+ //AFM does not support setting the leading so we will just ignore it.
+ return 0f;
+ }
+
+ /**
+ * This will set the leading for the font.
+ *
+ * @param leading The new leading for the font.
+ */
+ public void setLeading( float leading )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the CapHeight for the font.
+ *
+ * @return The cap height.
+ */
+ public float getCapHeight()
+ {
+ return afm.getCapHeight();
+ }
+
+ /**
+ * This will set the cap height for the font.
+ *
+ * @param capHeight The new cap height for the font.
+ */
+ public void setCapHeight( float capHeight )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the x height for the font.
+ *
+ * @return The x height.
+ */
+ public float getXHeight()
+ {
+ return afm.getXHeight();
+ }
+
+ /**
+ * This will set the x height for the font.
+ *
+ * @param xHeight The new x height for the font.
+ */
+ public void setXHeight( float xHeight )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the stemV for the font.
+ *
+ * @return The stem v value.
+ */
+ public float getStemV()
+ {
+ //afm does not have a stem v
+ return 0;
+ }
+
+ /**
+ * This will set the stem V for the font.
+ *
+ * @param stemV The new stem v for the font.
+ */
+ public void setStemV( float stemV )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the stemH for the font.
+ *
+ * @return The stem h value.
+ */
+ public float getStemH()
+ {
+ //afm does not have a stem h
+ return 0;
+ }
+
+ /**
+ * This will set the stem H for the font.
+ *
+ * @param stemH The new stem h for the font.
+ */
+ public void setStemH( float stemH )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the average width for the font.
+ *
+ * @return The average width value.
+ *
+ * @throws IOException If there is an error calculating the average width.
+ */
+ public float getAverageWidth() throws IOException
+ {
+ return afm.getAverageCharacterWidth();
+ }
+
+ /**
+ * This will set the average width for the font.
+ *
+ * @param averageWidth The new average width for the font.
+ */
+ public void setAverageWidth( float averageWidth )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the max width for the font.
+ *
+ * @return The max width value.
+ */
+ public float getMaxWidth()
+ {
+ //afm does not support max width;
+ return 0;
+ }
+
+ /**
+ * This will set the max width for the font.
+ *
+ * @param maxWidth The new max width for the font.
+ */
+ public void setMaxWidth( float maxWidth )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the missing width for the font.
+ *
+ * @return The missing width value.
+ */
+ public float getMissingWidth()
+ {
+ return 0;
+ }
+
+ /**
+ * This will set the missing width for the font.
+ *
+ * @param missingWidth The new missing width for the font.
+ */
+ public void setMissingWidth( float missingWidth )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+
+ /**
+ * This will get the character set for the font.
+ *
+ * @return The character set value.
+ */
+ public String getCharSet()
+ {
+ return afm.getCharacterSet();
+ }
+
+ /**
+ * This will set the character set for the font.
+ *
+ * @param charSet The new character set for the font.
+ */
+ public void setCharacterSet( String charSet )
+ {
+ throw new UnsupportedOperationException( "The AFM Font descriptor is immutable" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java b/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java
new file mode 100644
index 0000000..f3bf61e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java
@@ -0,0 +1,580 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * This class represents an implementation to the font descriptor that gets its
+ * information from a COS Dictionary.
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.2 $
+ */
+public class PDFontDescriptorDictionary extends PDFontDescriptor
+{
+ private COSDictionary dic;
+
+ /**
+ * Constructor.
+ */
+ public PDFontDescriptorDictionary()
+ {
+ dic = new COSDictionary();
+ dic.setName( "Type", "FontDescriptor" );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param desc The wrapped COS Dictionary.
+ */
+ public PDFontDescriptorDictionary( COSDictionary desc )
+ {
+ dic = desc;
+ }
+
+ /**
+ * This will get the dictionary for this object.
+ *
+ * @return The COS dictionary.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return dic;
+ }
+
+ /**
+ * Get the font name.
+ *
+ * @return The name of the font.
+ */
+ public String getFontName()
+ {
+ String retval = null;
+ COSName name = (COSName)dic.getDictionaryObject( COSName.getPDFName( "FontName" ) );
+ if( name != null )
+ {
+ retval = name.getName();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the font name.
+ *
+ * @param fontName The new name for the font.
+ */
+ public void setFontName( String fontName )
+ {
+ COSName name = null;
+ if( fontName != null )
+ {
+ name = COSName.getPDFName( fontName );
+ }
+ dic.setItem( COSName.getPDFName( "FontName" ), name );
+ }
+
+ /**
+ * A string representing the preferred font family.
+ *
+ * @return The font family.
+ */
+ public String getFontFamily()
+ {
+ String retval = null;
+ COSString name = (COSString)dic.getDictionaryObject( COSName.getPDFName( "FontFamily" ) );
+ if( name != null )
+ {
+ retval = name.getString();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the font family.
+ *
+ * @param fontFamily The font family.
+ */
+ public void setFontFamily( String fontFamily )
+ {
+ COSString name = null;
+ if( fontFamily != null )
+ {
+ name = new COSString( fontFamily );
+ }
+ dic.setItem( COSName.getPDFName( "FontFamily" ), name );
+ }
+
+ /**
+ * The weight of the font. According to the PDF spec "possible values are
+ * 100, 200, 300, 400, 500, 600, 700, 800 or 900" Where a higher number is
+ * more weight and appears to be more bold.
+ *
+ * @return The font weight.
+ */
+ public float getFontWeight()
+ {
+ return dic.getFloat( "FontWeight",0 );
+ }
+
+ /**
+ * Set the weight of the font.
+ *
+ * @param fontWeight The new weight of the font.
+ */
+ public void setFontWeight( float fontWeight )
+ {
+ dic.setFloat( "FontWeight", fontWeight );
+ }
+
+ /**
+ * A string representing the preferred font stretch.
+ * According to the PDF Spec:
+ * The font stretch value; it must be one of the following (ordered from
+ * narrowest to widest): UltraCondensed, ExtraCondensed, Condensed, SemiCondensed,
+ * Normal, SemiExpanded, Expanded, ExtraExpanded or UltraExpanded.
+ *
+ * @return The stretch of the font.
+ */
+ public String getFontStretch()
+ {
+ String retval = null;
+ COSName name = (COSName)dic.getDictionaryObject( COSName.getPDFName( "FontStretch" ) );
+ if( name != null )
+ {
+ retval = name.getName();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the font stretch.
+ *
+ * @param fontStretch The new stretch for the font.
+ */
+ public void setFontStretch( String fontStretch )
+ {
+ COSName name = null;
+ if( fontStretch != null )
+ {
+ name = COSName.getPDFName( fontStretch );
+ }
+ dic.setItem( COSName.getPDFName( "FontStretch" ), name );
+ }
+
+ /**
+ * This will get the font flags.
+ *
+ * @return The font flags.
+ */
+ public int getFlags()
+ {
+ return dic.getInt( "Flags", 0 );
+ }
+
+ /**
+ * This will set the font flags.
+ *
+ * @param flags The new font flags.
+ */
+ public void setFlags( int flags )
+ {
+ dic.setInt( "Flags", flags );
+ }
+
+ /**
+ * This will get the fonts bouding box.
+ *
+ * @return The fonts bouding box.
+ */
+ public PDRectangle getFontBoundingBox()
+ {
+ COSArray rect = (COSArray)dic.getDictionaryObject( COSName.getPDFName( "FontBBox" ) );
+ PDRectangle retval = null;
+ if( rect != null )
+ {
+ retval = new PDRectangle( rect );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the fonts bounding box.
+ *
+ * @param rect The new bouding box.
+ */
+ public void setFontBoundingBox( PDRectangle rect )
+ {
+ COSArray array = null;
+ if( rect != null )
+ {
+ array = rect.getCOSArray();
+ }
+ dic.setItem( COSName.getPDFName( "FontBBox" ), array );
+ }
+
+ /**
+ * This will get the italic angle for the font.
+ *
+ * @return The italic angle.
+ */
+ public float getItalicAngle()
+ {
+ return dic.getFloat( "ItalicAngle", 0 );
+ }
+
+ /**
+ * This will set the italic angle for the font.
+ *
+ * @param angle The new italic angle for the font.
+ */
+ public void setItalicAngle( float angle )
+ {
+ dic.setFloat( "ItalicAngle", angle );
+ }
+
+ /**
+ * This will get the ascent for the font.
+ *
+ * @return The ascent.
+ */
+ public float getAscent()
+ {
+ return dic.getFloat( "Ascent", 0 );
+ }
+
+ /**
+ * This will set the ascent for the font.
+ *
+ * @param ascent The new ascent for the font.
+ */
+ public void setAscent( float ascent )
+ {
+ dic.setFloat( "Ascent", ascent );
+ }
+
+ /**
+ * This will get the descent for the font.
+ *
+ * @return The descent.
+ */
+ public float getDescent()
+ {
+ return dic.getFloat( "Descent", 0 );
+ }
+
+ /**
+ * This will set the descent for the font.
+ *
+ * @param descent The new descent for the font.
+ */
+ public void setDescent( float descent )
+ {
+ dic.setFloat( "Descent", descent );
+ }
+
+ /**
+ * This will get the leading for the font.
+ *
+ * @return The leading.
+ */
+ public float getLeading()
+ {
+ return dic.getFloat( "Leading", 0 );
+ }
+
+ /**
+ * This will set the leading for the font.
+ *
+ * @param leading The new leading for the font.
+ */
+ public void setLeading( float leading )
+ {
+ dic.setFloat( "Leading", leading );
+ }
+
+ /**
+ * This will get the CapHeight for the font.
+ *
+ * @return The cap height.
+ */
+ public float getCapHeight()
+ {
+ return dic.getFloat( "CapHeight", 0 );
+ }
+
+ /**
+ * This will set the cap height for the font.
+ *
+ * @param capHeight The new cap height for the font.
+ */
+ public void setCapHeight( float capHeight )
+ {
+ dic.setFloat( "CapHeight", capHeight );
+ }
+
+ /**
+ * This will get the x height for the font.
+ *
+ * @return The x height.
+ */
+ public float getXHeight()
+ {
+ return dic.getFloat( "XHeight", 0 );
+ }
+
+ /**
+ * This will set the x height for the font.
+ *
+ * @param xHeight The new x height for the font.
+ */
+ public void setXHeight( float xHeight )
+ {
+ dic.setFloat( "XHeight", xHeight );
+ }
+
+ /**
+ * This will get the stemV for the font.
+ *
+ * @return The stem v value.
+ */
+ public float getStemV()
+ {
+ return dic.getFloat( "StemV", 0 );
+ }
+
+ /**
+ * This will set the stem V for the font.
+ *
+ * @param stemV The new stem v for the font.
+ */
+ public void setStemV( float stemV )
+ {
+ dic.setFloat( "StemV", stemV );
+ }
+
+ /**
+ * This will get the stemH for the font.
+ *
+ * @return The stem h value.
+ */
+ public float getStemH()
+ {
+ return dic.getFloat( "StemH", 0 );
+ }
+
+ /**
+ * This will set the stem H for the font.
+ *
+ * @param stemH The new stem h for the font.
+ */
+ public void setStemH( float stemH )
+ {
+ dic.setFloat( "StemH", stemH );
+ }
+
+ /**
+ * This will get the average width for the font.
+ *
+ * @return The average width value.
+ */
+ public float getAverageWidth()
+ {
+ return dic.getFloat( "AvgWidth", 0 );
+ }
+
+ /**
+ * This will set the average width for the font.
+ *
+ * @param averageWidth The new average width for the font.
+ */
+ public void setAverageWidth( float averageWidth )
+ {
+ dic.setFloat( "AvgWidth", averageWidth );
+ }
+
+ /**
+ * This will get the max width for the font.
+ *
+ * @return The max width value.
+ */
+ public float getMaxWidth()
+ {
+ return dic.getFloat( "MaxWidth", 0 );
+ }
+
+ /**
+ * This will set the max width for the font.
+ *
+ * @param maxWidth The new max width for the font.
+ */
+ public void setMaxWidth( float maxWidth )
+ {
+ dic.setFloat( "MaxWidth", maxWidth );
+ }
+
+ /**
+ * This will get the missing width for the font.
+ *
+ * @return The missing width value.
+ */
+ public float getMissingWidth()
+ {
+ return dic.getFloat( "MissingWidth", 0 );
+ }
+
+ /**
+ * This will set the missing width for the font.
+ *
+ * @param missingWidth The new missing width for the font.
+ */
+ public void setMissingWidth( float missingWidth )
+ {
+ dic.setFloat( "MissingWidth", missingWidth );
+ }
+
+ /**
+ * This will get the character set for the font.
+ *
+ * @return The character set value.
+ */
+ public String getCharSet()
+ {
+ String retval = null;
+ COSString name = (COSString)dic.getDictionaryObject( COSName.getPDFName( "CharSet" ) );
+ if( name != null )
+ {
+ retval = name.getString();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the character set for the font.
+ *
+ * @param charSet The new character set for the font.
+ */
+ public void setCharacterSet( String charSet )
+ {
+ COSString name = null;
+ if( charSet != null )
+ {
+ name = new COSString( charSet );
+ }
+ dic.setItem( COSName.getPDFName( "CharSet" ), name );
+ }
+
+ /**
+ * A stream containing a Type 1 font program.
+ *
+ * @return A stream containing a Type 1 font program.
+ */
+ public PDStream getFontFile()
+ {
+ PDStream retval = null;
+ COSStream stream = (COSStream)dic.getDictionaryObject( "FontFile" );
+ if( stream != null )
+ {
+ retval = new PDStream( stream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the type 1 font program.
+ *
+ * @param type1Stream The type 1 stream.
+ */
+ public void setFontFile( PDStream type1Stream )
+ {
+ dic.setItem( "FontFile", type1Stream );
+ }
+
+ /**
+ * A stream containing a true type font program.
+ *
+ * @return A stream containing a true type font program.
+ */
+ public PDStream getFontFile2()
+ {
+ PDStream retval = null;
+ COSStream stream = (COSStream)dic.getDictionaryObject( "FontFile2" );
+ if( stream != null )
+ {
+ retval = new PDStream( stream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the true type font program.
+ *
+ * @param ttfStream The true type stream.
+ */
+ public void setFontFile2( PDStream ttfStream )
+ {
+ dic.setItem( "FontFile2", ttfStream );
+ }
+
+ /**
+ * A stream containing a font program that is not true type or type 1.
+ *
+ * @return A stream containing a font program.
+ */
+ public PDStream getFontFile3()
+ {
+ PDStream retval = null;
+ COSStream stream = (COSStream)dic.getDictionaryObject( "FontFile3" );
+ if( stream != null )
+ {
+ retval = new PDStream( stream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set a stream containing a font program that is not true type or type 1.
+ *
+ * @param stream The font program stream.
+ */
+ public void setFontFile3( PDStream stream )
+ {
+ dic.setItem( "FontFile3", stream );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDFontFactory.java b/src/main/java/org/pdfbox/pdmodel/font/PDFontFactory.java
new file mode 100644
index 0000000..329a8bf
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDFontFactory.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This will create the correct type of font based on information in the dictionary.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDFontFactory
+{
+ /**
+ * private constructor, should only use static methods in this class.
+ */
+ private PDFontFactory()
+ {
+ }
+
+ /**
+ * This will create the correct font based on information in the dictionary.
+ *
+ * @param dic The populated dictionary.
+ *
+ * @return The corrent implementation for the font.
+ *
+ * @throws IOException If the dictionary is not valid.
+ */
+ public static PDFont createFont( COSDictionary dic ) throws IOException
+ {
+ PDFont retval = null;
+
+ COSName type = (COSName)dic.getDictionaryObject( COSName.TYPE );
+ if( !type.equals( COSName.FONT ) )
+ {
+ throw new IOException( "Cannot create font if /Type is not /Font. Actual=" +type );
+ }
+
+ COSName subType = (COSName)dic.getDictionaryObject( COSName.SUBTYPE );
+ if( subType.equals( COSName.getPDFName( "Type1" ) ) )
+ {
+ retval = new PDType1Font( dic );
+ }
+ else if( subType.equals( COSName.getPDFName( "MMType1" ) ) )
+ {
+ retval = new PDMMType1Font( dic );
+ }
+ else if( subType.equals( COSName.getPDFName( "TrueType" ) ) )
+ {
+ retval = new PDTrueTypeFont( dic );
+ }
+ else if( subType.equals( COSName.getPDFName( "Type3" ) ) )
+ {
+ retval = new PDType3Font( dic );
+ }
+ else if( subType.equals( COSName.getPDFName( "Type0" ) ) )
+ {
+ retval = new PDType0Font( dic );
+ }
+ else if( subType.equals( COSName.getPDFName( "CIDFontType0" ) ) )
+ {
+ retval = new PDCIDFontType0Font( dic );
+ }
+ else if( subType.equals( COSName.getPDFName( "CIDFontType2" ) ) )
+ {
+ retval = new PDCIDFontType2Font( dic );
+ }
+ else
+ {
+ throw new IOException( "Unknown font subtype=" + subType );
+ }
+
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDMMType1Font.java b/src/main/java/org/pdfbox/pdmodel/font/PDMMType1Font.java
new file mode 100644
index 0000000..a0dffba
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDMMType1Font.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is implementation of the Multiple Master Type1 Font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDMMType1Font extends PDSimpleFont
+{
+ /**
+ * Constructor.
+ */
+ public PDMMType1Font()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.getPDFName( "MMType1" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDMMType1Font( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDSimpleFont.java b/src/main/java/org/pdfbox/pdmodel/font/PDSimpleFont.java
new file mode 100644
index 0000000..a1356ba
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDSimpleFont.java
@@ -0,0 +1,239 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.awt.Graphics;
+
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.afmtypes.FontMetric;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSInteger;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * This class contains implementation details of the simple pdf fonts.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.11 $
+ */
+public abstract class PDSimpleFont extends PDFont
+{
+ private static Logger log = Logger.getLogger( PDSimpleFont.class );
+ /**
+ * Constructor.
+ */
+ public PDSimpleFont()
+ {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDSimpleFont( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+
+ /**
+ * @see PDFont#drawString( String, Graphics, float, float, float, float, float )
+ */
+ public void drawString( String string, Graphics g, float fontSize,
+ float xScale, float yScale, float x, float y ) throws IOException
+ {
+ log.warn( "Not yet implemented:" + getClass().getName() );
+ }
+
+ /**
+ * This will get the font width for a character.
+ *
+ * @param c The character code to get the width for.
+ * @param offset The offset into the array.
+ * @param length The length of the data.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public float getFontWidth( byte[] c, int offset, int length ) throws IOException
+ {
+ float fontWidth = 0;
+ int code = getCodeFromArray( c, offset, length );
+
+ //hmm should this be in a subclass??
+ COSInteger firstChar = (COSInteger)font.getDictionaryObject( COSName.FIRST_CHAR );
+ COSInteger lastChar = (COSInteger)font.getDictionaryObject( COSName.LAST_CHAR );
+ if( firstChar != null && lastChar != null )
+ {
+ long first = firstChar.intValue();
+ long last = lastChar.intValue();
+ if( code >= first && code <= last && font.getDictionaryObject( COSName.WIDTHS ) != null )
+ {
+ COSArray widthArray = (COSArray)font.getDictionaryObject( COSName.WIDTHS );
+ COSNumber fontWidthObject = (COSNumber)widthArray.get( (int)(code - first) );
+ fontWidth = fontWidthObject.floatValue();
+ }
+ else
+ {
+ fontWidth = getFontWidthFromAFMFile( code );
+ }
+ }
+ else
+ {
+ fontWidth = getFontWidthFromAFMFile( code );
+ }
+ return fontWidth;
+ }
+
+ /**
+ * This will get the average font width for all characters.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public float getAverageFontWidth() throws IOException
+ {
+ float average = 0.0f;
+ float totalWidth = 0.0f;
+ float characterCount = 0.0f;
+ COSArray widths = (COSArray)font.getDictionaryObject( COSName.WIDTHS );
+ if( widths != null )
+ {
+ for( int i=0; i<widths.size(); i++ )
+ {
+ COSNumber fontWidth = (COSNumber)widths.getObject( i );
+ if( fontWidth.floatValue() > 0 )
+ {
+ totalWidth += fontWidth.floatValue();
+ characterCount += 1;
+ }
+ }
+ }
+
+ if( totalWidth > 0 )
+ {
+ average = totalWidth / characterCount;
+ }
+ else
+ {
+ average = getAverageFontWidthFromAFMFile();
+ }
+ return average;
+ }
+
+ /**
+ * This will get the font descriptor for this font.
+ *
+ * @return The font descriptor for this font.
+ *
+ * @throws IOException If there is an error parsing an AFM file, or unable to
+ * create a PDFontDescriptor object.
+ */
+ public PDFontDescriptor getFontDescriptor() throws IOException
+ {
+ PDFontDescriptor retval = null;
+ COSDictionary fd = (COSDictionary)font.getDictionaryObject( COSName.getPDFName( "FontDescriptor" ) );
+ if( fd == null )
+ {
+ FontMetric afm = getAFM();
+ if( afm == null )
+ {
+ throw new IOException( "Error: Can't create font descriptor file" );
+ }
+ retval = new PDFontDescriptorAFM( afm );
+ }
+ else
+ {
+ retval = new PDFontDescriptorDictionary( fd );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the font descriptor.
+ *
+ * @param fontDescriptor The font descriptor.
+ */
+ public void setFontDescriptor( PDFontDescriptorDictionary fontDescriptor )
+ {
+ COSDictionary dic = null;
+ if( fontDescriptor != null )
+ {
+ dic = fontDescriptor.getCOSDictionary();
+ }
+ font.setItem( COSName.getPDFName( "FontDescriptor" ), dic );
+ }
+
+ /**
+ * This will get the ToUnicode stream.
+ *
+ * @return The ToUnicode stream.
+ * @throws IOException If there is an error getting the stream.
+ */
+ public PDStream getToUnicode() throws IOException
+ {
+ return PDStream.createFromCOS( font.getDictionaryObject( "ToUnicode" ) );
+ }
+
+ /**
+ * This will set the ToUnicode stream.
+ *
+ * @param unicode The unicode stream.
+ */
+ public void setToUnicode( PDStream unicode )
+ {
+ font.setItem( "ToUnicode", unicode );
+ }
+
+ /**
+ * This will get the fonts bounding box.
+ *
+ * @return The fonts bouding box.
+ *
+ * @throws IOException If there is an error getting the bounding box.
+ */
+ public PDRectangle getFontBoundingBox() throws IOException
+ {
+ return getFontDescriptor().getFontBoundingBox();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDTrueTypeFont.java b/src/main/java/org/pdfbox/pdmodel/font/PDTrueTypeFont.java
new file mode 100644
index 0000000..d462255
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDTrueTypeFont.java
@@ -0,0 +1,437 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.encoding.WinAnsiEncoding;
+
+import org.pdfbox.ttf.CMAPEncodingEntry;
+import org.pdfbox.ttf.CMAPTable;
+import org.pdfbox.ttf.GlyphData;
+import org.pdfbox.ttf.GlyphTable;
+import org.pdfbox.ttf.HeaderTable;
+import org.pdfbox.ttf.HorizontalHeaderTable;
+import org.pdfbox.ttf.HorizontalMetricsTable;
+import org.pdfbox.ttf.MemoryTTFDataStream;
+import org.pdfbox.ttf.NamingTable;
+import org.pdfbox.ttf.NameRecord;
+import org.pdfbox.ttf.OS2WindowsMetricsTable;
+import org.pdfbox.ttf.PostScriptTable;
+import org.pdfbox.ttf.TTFParser;
+import org.pdfbox.ttf.TrueTypeFont;
+import org.pdfbox.util.ResourceLoader;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This is the TrueType implementation of fonts.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.14 $
+ */
+public class PDTrueTypeFont extends PDSimpleFont
+{
+ /**
+ * This is the key to a property in the Resources/PDFBox_External_Fonts.properties file
+ * to load a Font when a mapping does not exist for the current font.
+ */
+ public static final String UNKNOWN_FONT = "UNKNOWN_FONT";
+
+ private Font awtFont = null;
+
+ private static Properties externalFonts = new Properties();
+ private static Map loadedExternalFonts = new HashMap();
+
+ static
+ {
+ try
+ {
+ ResourceLoader.loadProperties( "Resources/PDFBox_External_Fonts.properties", externalFonts );
+ }
+ catch( IOException io )
+ {
+ io.printStackTrace();
+ throw new RuntimeException( "Error loading font resources" );
+ }
+ }
+
+
+ /**
+ * Constructor.
+ */
+ public PDTrueTypeFont()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.TRUE_TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDTrueTypeFont( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+
+ /**
+ * This will load a TTF font from a font file.
+ *
+ * @param doc The PDF document that will hold the embedded font.
+ * @param file The file on the filesystem that holds the font file.
+ * @return A true type font.
+ * @throws IOException If there is an error loading the file data.
+ */
+ public static PDTrueTypeFont loadTTF( PDDocument doc, String file ) throws IOException
+ {
+ return loadTTF( doc, new File( file ) );
+ }
+
+ /**
+ * This will load a TTF to be embedding into a document.
+ *
+ * @param doc The PDF document that will hold the embedded font.
+ * @param file A TTF file stream.
+ * @return A PDF TTF.
+ * @throws IOException If there is an error loading the data.
+ */
+ public static PDTrueTypeFont loadTTF( PDDocument doc, File file ) throws IOException
+ {
+ PDTrueTypeFont retval = new PDTrueTypeFont();
+ PDFontDescriptorDictionary fd = new PDFontDescriptorDictionary();
+ PDStream fontStream = new PDStream(doc, new FileInputStream( file ), false );
+ fontStream.getStream().setInt( COSName.LENGTH1, (int)file.length() );
+ fontStream.addCompression();
+ fd.setFontFile2( fontStream );
+ retval.setFontDescriptor( fd );
+ //only support winansi encoding right now, should really
+ //just use Identity-H with unicode mapping
+ retval.setEncoding( new WinAnsiEncoding() );
+ TrueTypeFont ttf = null;
+ try
+ {
+ TTFParser parser = new TTFParser();
+ ttf = parser.parseTTF( file );
+ NamingTable naming = ttf.getNaming();
+ List records = naming.getNameRecords();
+ for( int i=0; i<records.size(); i++ )
+ {
+ NameRecord nr = (NameRecord)records.get( i );
+ if( nr.getNameId() == NameRecord.NAME_POSTSCRIPT_NAME )
+ {
+ retval.setBaseFont( nr.getString() );
+ fd.setFontName( nr.getString() );
+ }
+ else if( nr.getNameId() == NameRecord.NAME_FONT_FAMILY_NAME )
+ {
+ fd.setFontFamily( nr.getString() );
+ }
+ }
+
+ OS2WindowsMetricsTable os2 = ttf.getOS2Windows();
+ fd.setNonSymbolic( true );
+ switch( os2.getFamilyClass() )
+ {
+ case OS2WindowsMetricsTable.FAMILY_CLASS_SYMBOLIC:
+ fd.setSymbolic( true );
+ fd.setNonSymbolic( false );
+ break;
+ case OS2WindowsMetricsTable.FAMILY_CLASS_SCRIPTS:
+ fd.setScript( true );
+ break;
+ case OS2WindowsMetricsTable.FAMILY_CLASS_CLAREDON_SERIFS:
+ case OS2WindowsMetricsTable.FAMILY_CLASS_FREEFORM_SERIFS:
+ case OS2WindowsMetricsTable.FAMILY_CLASS_MODERN_SERIFS:
+ case OS2WindowsMetricsTable.FAMILY_CLASS_OLDSTYLE_SERIFS:
+ case OS2WindowsMetricsTable.FAMILY_CLASS_SLAB_SERIFS:
+ fd.setSerif( true );
+ break;
+ default:
+ //do nothing
+ }
+ switch( os2.getWidthClass() )
+ {
+ case OS2WindowsMetricsTable.WIDTH_CLASS_ULTRA_CONDENSED:
+ fd.setFontStretch( "UltraCondensed" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_EXTRA_CONDENSED:
+ fd.setFontStretch( "ExtraCondensed" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_CONDENSED:
+ fd.setFontStretch( "Condensed" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_SEMI_CONDENSED:
+ fd.setFontStretch( "SemiCondensed" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_MEDIUM:
+ fd.setFontStretch( "Normal" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_SEMI_EXPANDED:
+ fd.setFontStretch( "SemiExpanded" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_EXPANDED:
+ fd.setFontStretch( "Expanded" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_EXTRA_EXPANDED:
+ fd.setFontStretch( "ExtraExpanded" );
+ break;
+ case OS2WindowsMetricsTable.WIDTH_CLASS_ULTRA_EXPANDED:
+ fd.setFontStretch( "UltraExpanded" );
+ break;
+ default:
+ //do nothing
+ }
+ fd.setFontWeight( os2.getWeightClass() );
+
+ //todo retval.setFixedPitch
+ //todo retval.setNonSymbolic
+ //todo retval.setItalic
+ //todo retval.setAllCap
+ //todo retval.setSmallCap
+ //todo retval.setForceBold
+
+ HeaderTable header = ttf.getHeader();
+ PDRectangle rect = new PDRectangle();
+ rect.setLowerLeftX( header.getXMin() * 1000f/header.getUnitsPerEm() );
+ rect.setLowerLeftY( header.getYMin() * 1000f/header.getUnitsPerEm() );
+ rect.setUpperRightX( header.getXMax() * 1000f/header.getUnitsPerEm() );
+ rect.setUpperRightY( header.getYMax() * 1000f/header.getUnitsPerEm() );
+ fd.setFontBoundingBox( rect );
+
+ HorizontalHeaderTable hHeader = ttf.getHorizontalHeader();
+ fd.setAscent( hHeader.getAscender() * 1000f/header.getUnitsPerEm() );
+ fd.setDescent( hHeader.getDescender() * 1000f/header.getUnitsPerEm() );
+
+ GlyphTable glyphTable = ttf.getGlyph();
+ GlyphData[] glyphs = glyphTable.getGlyphs();
+
+ PostScriptTable ps = ttf.getPostScript();
+ fd.setFixedPitch( ps.getIsFixedPitch() > 0 );
+ fd.setItalicAngle( ps.getItalicAngle() );
+
+ String[] names = ps.getGlyphNames();
+ if( names != null )
+ {
+ for( int i=0; i<names.length; i++ )
+ {
+ //if we have a capital H then use that, otherwise use the
+ //tallest letter
+ if( names[i].equals( "H" ) )
+ {
+ fd.setCapHeight( (glyphs[i].getBoundingBox().getUpperRightY()* 1000f)/
+ header.getUnitsPerEm() );
+ }
+ if( names[i].equals( "x" ) )
+ {
+ fd.setXHeight( (glyphs[i].getBoundingBox().getUpperRightY()* 1000f)/header.getUnitsPerEm() );
+ }
+ }
+ }
+
+ //hmm there does not seem to be a clear definition for StemV,
+ //this is close enough and I am told it doesn't usually get used.
+ fd.setStemV( (fd.getFontBoundingBox().getWidth() * .13f) );
+
+
+ CMAPTable cmapTable = ttf.getCMAP();
+ CMAPEncodingEntry[] cmaps = cmapTable.getCmaps();
+ int[] glyphToCCode = null;
+ for( int i=0; i<cmaps.length; i++ )
+ {
+ if( cmaps[i].getPlatformId() == CMAPTable.PLATFORM_WINDOWS &&
+ cmaps[i].getPlatformEncodingId() == CMAPTable.ENCODING_UNICODE )
+ {
+ glyphToCCode = cmaps[i].getGlyphIdToCharacterCode();
+ }
+ }
+ int firstChar = 0;
+ /**
+ for( int i=0; i<glyphToCCode.length; i++ )
+ {
+ if( glyphToCCode[i] != 0 )
+ {
+ firstChar = Math.min( glyphToCCode[i], firstChar );
+ }
+ }*/
+
+ int maxWidths=256;
+ HorizontalMetricsTable hMet = ttf.getHorizontalMetrics();
+ int[] widthValues = hMet.getAdvanceWidth();
+ List widths = new ArrayList( widthValues.length );
+ Integer zero = new Integer( 250 );
+ for( int i=0; i<widthValues.length && i<maxWidths; i++ )
+ {
+ widths.add( zero );
+ }
+ for( int i=0; i<widthValues.length; i++ )
+ {
+ if(glyphToCCode[i]-firstChar < widths.size() &&
+ glyphToCCode[i]-firstChar >= 0 &&
+ widths.get( glyphToCCode[i]-firstChar) == zero )
+ {
+ widths.set( glyphToCCode[i]-firstChar,
+ new Integer( (int)(widthValues[i]* 1000f)/header.getUnitsPerEm() ) );
+ }
+ }
+ retval.setWidths( widths );
+
+ retval.setFirstChar( firstChar );
+ retval.setLastChar( firstChar + widths.size()-1 );
+
+ }
+ finally
+ {
+ if( ttf != null )
+ {
+ ttf.close();
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * @see PDFont#drawString( String, Graphics, float, float, float, float, float )
+ */
+ public void drawString( String string, Graphics g, float fontSize,
+ float xScale, float yScale, float x, float y ) throws IOException
+ {
+ PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary)getFontDescriptor();
+ if( awtFont == null )
+ {
+ PDStream ttfStream = fd.getFontFile2();
+ String fontName = fd.getFontName();
+ awtFont = Font.getFont( fontName, null );
+ if( ttfStream == null )
+ {
+ //throw new IOException( "Error:TTF Stream is null");
+ // Embedded true type programs are optional,
+ // if there is no stream, we must use an external
+ // file.
+ ttfStream = getExternalFontFile2( fd );
+ }
+ if( ttfStream == null )
+ {
+ //if we can't find a font then just fake it.
+ awtFont = new Font("Arial", Font.PLAIN, 1 );
+ }
+ else
+ {
+ try
+ {
+ awtFont = Font.createFont( Font.TRUETYPE_FONT, ttfStream.createInputStream() );
+ }
+ catch( FontFormatException e )
+ {
+ throw new IOException( e.getMessage() );
+ }
+ }
+ }
+ AffineTransform at = new AffineTransform();
+ at.scale( xScale, yScale );
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ g2d.setFont( awtFont.deriveFont( at ).deriveFont( fontSize ) );
+ g2d.drawString( string, (int)x, (int)y );
+ }
+
+ /**
+ * Permit to load an external TTF Font program file
+ *
+ * Created by Pascal Allain
+ * Vertical7 Inc.
+ *
+ * @param fd The font descriptor currently used
+ *
+ * @return A PDStream with the Font File program, null if fd is null
+ *
+ * @throws IOException If the font is not found
+ */
+ private PDStream getExternalFontFile2(PDFontDescriptorDictionary fd)
+ throws IOException
+ {
+ PDStream retval = null;
+
+ if ( fd != null )
+ {
+ String baseFont = getBaseFont();
+ String fontResource = externalFonts.getProperty( UNKNOWN_FONT );
+ if( (baseFont != null) &&
+ (externalFonts.containsKey(baseFont)) )
+ {
+ fontResource = externalFonts.getProperty(baseFont);
+ }
+ if( fontResource != null )
+ {
+ TrueTypeFont extTTF = (TrueTypeFont)loadedExternalFonts.get( baseFont );
+ if( extTTF == null )
+ {
+ TTFParser ttfParser = new TTFParser();
+ InputStream is = ResourceLoader.loadResource( fontResource );
+ if( is == null )
+ {
+ throw new IOException( "Error missing font resource '" + externalFonts.get(baseFont) + "'" );
+ }
+ MemoryTTFDataStream stream = new MemoryTTFDataStream( is );
+ extTTF = ttfParser.parseTTF( stream );
+ loadedExternalFonts.put( baseFont, extTTF );
+ }
+ retval = extTTF.getPDStream();
+ }
+ }
+
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDType0Font.java b/src/main/java/org/pdfbox/pdmodel/font/PDType0Font.java
new file mode 100644
index 0000000..010df33
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDType0Font.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.awt.Graphics;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import java.io.IOException;
+
+/**
+ * This is implementation of the Type0 Font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class PDType0Font extends PDFont
+{
+ /**
+ * Constructor.
+ */
+ public PDType0Font()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.getPDFName( "Type0" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDType0Font( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+
+ /**
+ * @see PDFont#drawString( String, Graphics, float, float, float, float, float )
+ */
+ public void drawString( String string, Graphics g, float fontSize,
+ float xScale, float yScale, float x, float y )
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * This will get the fonts bouding box.
+ *
+ * @return The fonts bouding box.
+ *
+ * @throws IOException If there is an error getting the bounding box.
+ */
+ public PDRectangle getFontBoundingBox() throws IOException
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * This will get the font width for a character.
+ *
+ * @param c The character code to get the width for.
+ * @param offset The offset into the array.
+ * @param length The length of the data.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public float getFontWidth( byte[] c, int offset, int length ) throws IOException
+ {
+ COSArray descendantFontArray =
+ (COSArray)font.getDictionaryObject( COSName.getPDFName( "DescendantFonts" ) );
+
+ COSDictionary descendantFontDictionary = (COSDictionary)descendantFontArray.getObject( 0 );
+ PDFont descendentFont = PDFontFactory.createFont( descendantFontDictionary );
+
+ return descendentFont.getFontWidth( c, offset, length );
+ }
+
+ /**
+ * This will get the average font width for all characters.
+ *
+ * @return The width is in 1000 unit of text space, ie 333 or 777
+ *
+ * @throws IOException If an error occurs while parsing.
+ */
+ public float getAverageFontWidth() throws IOException
+ {
+ COSArray descendantFontArray =
+ (COSArray)font.getDictionaryObject( COSName.getPDFName( "DescendantFonts" ) );
+
+ COSDictionary descendantFontDictionary = (COSDictionary)descendantFontArray.getObject( 0 );
+ PDFont descendentFont = PDFontFactory.createFont( descendantFontDictionary );
+
+ return descendentFont.getAverageFontWidth();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDType1AfmPfbFont.java b/src/main/java/org/pdfbox/pdmodel/font/PDType1AfmPfbFont.java
new file mode 100644
index 0000000..89fdc5e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDType1AfmPfbFont.java
@@ -0,0 +1,206 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+
+package org.pdfbox.pdmodel.font;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.pdfbox.afmparser.AFMParser;
+import org.pdfbox.afmtypes.CharMetric;
+import org.pdfbox.afmtypes.FontMetric;
+import org.pdfbox.encoding.AFMEncoding;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+import org.pdfbox.pfb.PfbParser;
+
+/**
+ * This is implementation of the Type1 Font
+ * with a afm and a pfb file.
+ *
+ * @author <a href="mailto:m.g.n@gmx.de">Michael Niedermair</a>
+ * @version $Revision: 1.3 $
+ */
+public class PDType1AfmPfbFont extends PDType1Font
+{
+ /**
+ * the buffersize.
+ */
+ private static final int BUFFERSIZE = 0xffff;
+
+ /**
+ * The font descriptor.
+ */
+ private PDFontDescriptorDictionary fd;
+
+ /**
+ * the font metric.
+ */
+ private FontMetric metric;
+
+ /**
+ * Create a new object.
+ * @param doc The PDF document that will hold the embedded font.
+ * @param afmname The font filename.
+ * @throws IOException If there is an error loading the data.
+ */
+ public PDType1AfmPfbFont(final PDDocument doc, final String afmname)
+ throws IOException
+ {
+
+ super();
+
+ InputStream afmin = new BufferedInputStream(
+ new FileInputStream(afmname), BUFFERSIZE);
+ String pfbname = afmname.replaceAll(".AFM", "").replaceAll(".afm", "")
+ + ".pfb";
+ InputStream pfbin = new BufferedInputStream(
+ new FileInputStream(pfbname), BUFFERSIZE);
+
+ load(doc, afmin, pfbin);
+ }
+
+ /**
+ * Create a new object.
+ * @param doc The PDF document that will hold the embedded font.
+ * @param afm The afm input.
+ * @param pfb The pfb input.
+ * @throws IOException If there is an error loading the data.
+ */
+ public PDType1AfmPfbFont(final PDDocument doc, final InputStream afm, final InputStream pfb)
+ throws IOException
+ {
+ super();
+
+ load(doc, afm, pfb);
+ }
+
+ /**
+ * This will load a afm and pfb to be embedding into a document.
+ *
+ * @param doc The PDF document that will hold the embedded font.
+ * @param afm The afm input.
+ * @param pfb The pfb input.
+ * @throws IOException If there is an error loading the data.
+ */
+ private void load(final PDDocument doc, final InputStream afm,
+ final InputStream pfb) throws IOException
+ {
+
+ fd = new PDFontDescriptorDictionary();
+ setFontDescriptor(fd);
+
+ // read the pfb
+ PfbParser pfbparser = new PfbParser(pfb);
+ pfb.close();
+
+ PDStream fontStream = new PDStream(doc, pfbparser.getInputStream(),
+ false);
+ fontStream.getStream().setInt("Length", pfbparser.size());
+ for (int i = 0; i < pfbparser.getLengths().length; i++)
+ {
+ fontStream.getStream().setInt("Length" + (i + 1),
+ pfbparser.getLengths()[i]);
+ }
+ fontStream.addCompression();
+ fd.setFontFile(fontStream);
+
+ // read the afm
+ AFMParser parser = new AFMParser(afm);
+ parser.parse();
+ metric = parser.getResult();
+ setEncoding(new AFMEncoding(metric));
+
+ // set the values
+ setBaseFont(metric.getFontName());
+ fd.setFontName(metric.getFontName());
+ fd.setFontFamily(metric.getFamilyName());
+ fd.setNonSymbolic(true);
+ fd.setFontBoundingBox(new PDRectangle(metric.getFontBBox()));
+ fd.setItalicAngle(metric.getItalicAngle());
+ fd.setAscent(metric.getAscender());
+ fd.setDescent(metric.getDescender());
+ fd.setCapHeight(metric.getCapHeight());
+ fd.setXHeight(metric.getXHeight());
+ fd.setAverageWidth(metric.getAverageCharacterWidth());
+ fd.setCharacterSet(metric.getCharacterSet());
+
+ // get firstchar, lastchar
+ int firstchar = 255;
+ int lastchar = 0;
+
+ // widths
+ List listmetric = metric.getCharMetrics();
+
+ int maxWidths = 256;
+ List widths = new ArrayList(maxWidths);
+ Integer zero = new Integer(0);
+ Iterator iter = listmetric.iterator();
+ while (iter.hasNext())
+ {
+ CharMetric m = (CharMetric) iter.next();
+ int n = m.getCharacterCode();
+ if (n > 0)
+ {
+ firstchar = Math.min(firstchar, n);
+ lastchar = Math.max(lastchar, n);
+ if (m.getWx() > 0)
+ {
+ float width = m.getWx();
+ widths.add(new Float(width));
+ }
+ else
+ {
+ widths.add(zero);
+ }
+ }
+ }
+ setFirstChar(firstchar);
+ setLastChar(lastchar);
+ setWidths(widths);
+
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.font.PDSimpleFont#getFontDescriptor()
+ */
+ public PDFontDescriptor getFontDescriptor() throws IOException
+ {
+ return fd;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDType1Font.java b/src/main/java/org/pdfbox/pdmodel/font/PDType1Font.java
new file mode 100644
index 0000000..891f7c9
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDType1Font.java
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is implementation of the Type1 Font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.10 $
+ */
+public class PDType1Font extends PDSimpleFont
+{
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font TIMES_ROMAN = new PDType1Font( "Times-Roman" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font TIMES_BOLD = new PDType1Font( "Times-Bold" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font TIMES_ITALIC = new PDType1Font( "Times-Italic" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font TIMES_BOLD_ITALIC = new PDType1Font( "Times-BoldItalic" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font HELVETICA = new PDType1Font( "Helvetica" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font HELVETICA_BOLD = new PDType1Font( "Helvetica-Bold" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font HELVETICA_OBLIQUE = new PDType1Font( "Helvetica-Oblique" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font HELVETICA_BOLD_OBLIQUE = new PDType1Font( "Helvetica-BoldOblique" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font COURIER = new PDType1Font( "Courier" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font COURIER_BOLD = new PDType1Font( "Courier-Bold" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font COURIER_OBLIQUE = new PDType1Font( "Courier-Oblique" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font COURIER_BOLD_OBLIQUE = new PDType1Font( "Courier-BoldOblique" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font SYMBOL = new PDType1Font( "Symbol" );
+ /**
+ * Standard Base 14 Font.
+ */
+ public static final PDType1Font ZAPF_DINGBATS = new PDType1Font( "ZapfDingbats" );
+
+
+ private static final Map STANDARD_14 = new HashMap();
+ static
+ {
+ STANDARD_14.put( TIMES_ROMAN.getBaseFont(), TIMES_ROMAN );
+ STANDARD_14.put( TIMES_BOLD.getBaseFont(), TIMES_BOLD );
+ STANDARD_14.put( TIMES_ITALIC.getBaseFont(), TIMES_ITALIC );
+ STANDARD_14.put( TIMES_BOLD_ITALIC.getBaseFont(), TIMES_BOLD_ITALIC );
+ STANDARD_14.put( HELVETICA.getBaseFont(), HELVETICA );
+ STANDARD_14.put( HELVETICA_BOLD.getBaseFont(), HELVETICA_BOLD );
+ STANDARD_14.put( HELVETICA_OBLIQUE.getBaseFont(), HELVETICA_OBLIQUE );
+ STANDARD_14.put( HELVETICA_BOLD_OBLIQUE.getBaseFont(), HELVETICA_BOLD_OBLIQUE );
+ STANDARD_14.put( COURIER.getBaseFont(), COURIER );
+ STANDARD_14.put( COURIER_BOLD.getBaseFont(), COURIER_BOLD );
+ STANDARD_14.put( COURIER_OBLIQUE.getBaseFont(), COURIER_OBLIQUE );
+ STANDARD_14.put( COURIER_BOLD_OBLIQUE.getBaseFont(), COURIER_BOLD_OBLIQUE );
+ STANDARD_14.put( SYMBOL.getBaseFont(), SYMBOL );
+ STANDARD_14.put( ZAPF_DINGBATS.getBaseFont(), ZAPF_DINGBATS );
+ }
+
+ private Font awtFont = null;
+
+ /**
+ * Constructor.
+ */
+ public PDType1Font()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.getPDFName( "Type1" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDType1Font( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param baseFont The base font for this font.
+ */
+ public PDType1Font( String baseFont )
+ {
+ this();
+ setBaseFont( baseFont );
+ }
+
+ /**
+ * A convenience method to get one of the standard 14 font from name.
+ *
+ * @param name The name of the font to get.
+ *
+ * @return The font that matches the name or null if it does not exist.
+ */
+ public static PDType1Font getStandardFont( String name )
+ {
+ return (PDType1Font)STANDARD_14.get( name );
+ }
+
+ /**
+ * This will get the names of the standard 14 fonts.
+ *
+ * @return An array of the names of the standard 14 fonts.
+ */
+ public static String[] getStandard14Names()
+ {
+ return (String[])STANDARD_14.keySet().toArray( new String[14] );
+ }
+
+ /**
+ * @see PDFont#drawString( String, Graphics, float, float, float, float, float )
+ */
+ public void drawString( String string, Graphics g, float fontSize,
+ float xScale, float yScale, float x, float y ) throws IOException
+ {
+ if( awtFont == null )
+ {
+ String baseFont = this.getBaseFont();
+ if( baseFont.equals( TIMES_ROMAN.getBaseFont() ) )
+ {
+ awtFont = new Font( "Times New Roman", Font.PLAIN, 1 );
+ }
+ else if( baseFont.equals( TIMES_ITALIC.getBaseFont() ) )
+ {
+ awtFont = new Font( "Times New Roman", Font.ITALIC, 1 );
+ }
+ else if( baseFont.equals( TIMES_BOLD.getBaseFont() ) )
+ {
+ awtFont = new Font( "Times New Roman", Font.BOLD, 1 );
+ }
+ else if( baseFont.equals( TIMES_BOLD_ITALIC.getBaseFont() ) )
+ {
+ awtFont = new Font( "Times New Roman", Font.BOLD | Font.ITALIC, 1 );
+ }
+ else if( baseFont.equals( HELVETICA.getBaseFont() ) )
+ {
+ awtFont = new Font( "Helvetica", Font.PLAIN, 1 );
+ }
+ else if( baseFont.equals( HELVETICA_BOLD.getBaseFont() ) )
+ {
+ awtFont = new Font( "Helvetica", Font.BOLD, 1 );
+ }
+ else if( baseFont.equals( HELVETICA_BOLD_OBLIQUE.getBaseFont() ) )
+ {
+ awtFont = new Font( "Helvetica", Font.BOLD | Font.ITALIC, 1 );
+ }
+ else if( baseFont.equals( HELVETICA_OBLIQUE.getBaseFont() ) )
+ {
+ awtFont = new Font( "Helvetica", Font.ITALIC, 1 );
+ }
+ else if( baseFont.equals( COURIER.getBaseFont() ) )
+ {
+ awtFont = new Font( "Courier", Font.PLAIN, 1 );
+ }
+ else if( baseFont.equals( COURIER_BOLD.getBaseFont() ) )
+ {
+ awtFont = new Font( "Courier", Font.BOLD, 1 );
+ }
+ else if( baseFont.equals( COURIER_BOLD_OBLIQUE.getBaseFont() ) )
+ {
+ awtFont = new Font( "Courier", Font.BOLD | Font.ITALIC, 1 );
+ }
+ else if( baseFont.equals( COURIER_OBLIQUE.getBaseFont() ) )
+ {
+ awtFont = new Font( "Courier", Font.ITALIC, 1 );
+ }
+ else if( baseFont.equals( SYMBOL.getBaseFont() ) )
+ {
+ awtFont = new Font( "Symbol", Font.PLAIN, 1 );
+ }
+ else if( baseFont.equals( ZAPF_DINGBATS.getBaseFont() ) )
+ {
+ awtFont = new Font( "ZapfDingbats", Font.PLAIN, 1 );
+ }
+ else
+ {
+ awtFont = new Font( "Arial", Font.PLAIN, 1 );
+ //throw new IOException( "Not yet implemented:" + getClass().getName() + " " +
+ //this.getBaseFont() +
+ //" " + this + " " + TIMES_ROMAN );
+ }
+ }
+ AffineTransform at = new AffineTransform();
+ at.scale( xScale, yScale );
+
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ g2d.setFont( awtFont.deriveFont( at ).deriveFont( fontSize ) );
+ //g2d.getFontRenderContext().getTransform().scale( xScale, yScale );
+
+ g2d.drawString( string, (int)x, (int)y );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/PDType3Font.java b/src/main/java/org/pdfbox/pdmodel/font/PDType3Font.java
new file mode 100644
index 0000000..5f9c363
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/PDType3Font.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.pdmodel.common.PDMatrix;
+
+import java.awt.Graphics;
+import java.awt.Image;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is implementation of the Type3 Font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class PDType3Font extends PDSimpleFont
+{
+ private static Logger log = Logger.getLogger( PDType3Font.class );
+
+ //A map of character code to java.awt.Image for the glyph
+ private Map images = new HashMap();
+
+ /**
+ * Constructor.
+ */
+ public PDType3Font()
+ {
+ super();
+ font.setItem( COSName.SUBTYPE, COSName.getPDFName( "Type3" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fontDictionary The font dictionary according to the PDF specification.
+ */
+ public PDType3Font( COSDictionary fontDictionary )
+ {
+ super( fontDictionary );
+ }
+
+ /**
+ * Type3 fonts have their glyphs defined as a content stream. This
+ * will create the image that represents that character
+ *
+ * @throws IOException If there is an error creating the image.
+ */
+ private Image createImageIfNecessary( char character ) throws IOException
+ {
+ Character c = new Character( character );
+ Image retval = (Image)images.get( c );
+ if( retval == null )
+ {
+ COSDictionary charProcs = (COSDictionary)font.getDictionaryObject( COSName.getPDFName( "CharProcs" ) );
+ COSStream stream = (COSStream)charProcs.getDictionaryObject( COSName.getPDFName( "" + character ) );
+ if( stream != null )
+ {
+ Type3StreamParser parser = new Type3StreamParser();
+ retval = parser.createImage( stream );
+ images.put( c, retval );
+ }
+ else
+ {
+ log.warn( "Error font type 3 image stream is null" );
+ }
+ }
+ return retval;
+
+ }
+
+ /**
+ * This will draw a string on a canvas using the font.
+ *
+ * @param string The string to draw.
+ * @param g The graphics to draw onto.
+ * @param fontSize The size of the font to draw.
+ * @param x The x coordinate to draw at.
+ * @param y The y coordinate to draw at.
+ *
+ * @throws IOException If there is an error drawing the image on the screen.
+ */
+ public void drawString( String string, Graphics g, float fontSize, float x, float y ) throws IOException
+ {
+ //if( string.equals( "V" )|| string.equals( "o" ) )
+ {
+ for(int i=0; i<string.length(); i++)
+ {
+ //todo need to use image observers and such
+ char c = string.charAt( i );
+ Image image = createImageIfNecessary( c );
+ if( image != null )
+ {
+ int newWidth = (int)(.12*image.getWidth(null));
+ int newHeight = (int)(.12*image.getHeight(null));
+ if( newWidth > 0 && newHeight > 0 )
+ {
+ image = image.getScaledInstance( newWidth, newHeight, Image.SCALE_SMOOTH );
+ g.drawImage( image, (int)x, (int)y, null );
+ x+=newWidth;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the font matrix for this type3 font.
+ *
+ * @param matrix The font matrix for this type3 font.
+ */
+ public void setFontMatrix( PDMatrix matrix )
+ {
+ font.setItem( "FontMatrix", matrix );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/Type3StreamParser.java b/src/main/java/org/pdfbox/pdmodel/font/Type3StreamParser.java
new file mode 100644
index 0000000..a2adc1f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/Type3StreamParser.java
@@ -0,0 +1,607 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.font;
+
+import java.awt.Image;
+
+import java.io.IOException;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.graphics.xobject.PDInlinedImage;
+
+import org.pdfbox.util.BoundingBox;
+import org.pdfbox.util.ImageParameters;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.PDFStreamEngine;
+
+/**
+ * This class will handle creating an image for a type 3 glyph.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+public class Type3StreamParser extends PDFStreamEngine
+{
+ private PDInlinedImage image = null;
+ private BoundingBox box = null;
+
+
+ /**
+ * This will parse a type3 stream and create an image from it.
+ *
+ * @param type3Stream The stream containing the operators to draw the image.
+ *
+ * @return The image that was created.
+ *
+ * @throws IOException If there is an error processing the stream.
+ */
+ public Image createImage( COSStream type3Stream ) throws IOException
+ {
+ processStream( null, null, type3Stream );
+ return image.createImage();
+ }
+
+ /**
+ * This is used to handle an operation.
+ *
+ * @param operator The operation to perform.
+ * @param arguments The list of arguments.
+ *
+ * @throws IOException If there is an error processing the operation.
+ */
+ protected void processOperator( PDFOperator operator, List arguments ) throws IOException
+ {
+ super.processOperator( operator, arguments );
+ String operation = operator.getOperation();
+ /**
+ if( operation.equals( "b" ) )
+ {
+ //Close, fill, and stroke path using nonzero winding number rule
+ }
+ else if( operation.equals( "B" ) )
+ {
+ //Fill and stroke path using nonzero winding number rule
+ }
+ else if( operation.equals( "b*" ) )
+ {
+ //Close, fill, and stroke path using even-odd rule
+ }
+ else if( operation.equals( "B*" ) )
+ {
+ //Fill and stroke path using even-odd rule
+ }
+ else if( operation.equals( "BDC" ) )
+ {
+ //(PDF 1.2) Begin marked-content sequence with property list
+ }
+ else **/if( operation.equals( "BI" ) )
+ {
+ ImageParameters params = operator.getImageParameters();
+ image = new PDInlinedImage();
+ image.setImageParameters( params );
+ image.setImageData( operator.getImageData() );
+ //begin inline image object
+ }/**
+ else if( operation.equals( "BMC" ) )
+ {
+ //(PDF 1.2) Begin marked-content sequence
+ }
+ else if( operation.equals( "BT" ) )
+ {
+ log.debug( "<BT>" );
+ textMatrix = new Matrix();
+ textLineMatrix = new Matrix();
+ }
+ else if( operation.equals( "BX" ) )
+ {
+ //(PDF 1.1) Begin compatibility section
+ }
+ else if( operation.equals( "c" ) )
+ {
+ //Append curved segment to path (three control points)
+ }
+ else if( operation.equals( "cm" ) )
+ {
+ }
+ else if( operation.equals( "cs" ) )
+ {
+ }
+ else if( operation.equals( "CS" ) )
+ {
+ }
+ else if( operation.equals( "d" ) )
+ {
+ //Set the line dash pattern in the graphics state
+ }
+ else */if( operation.equals( "d0" ) )
+ {
+ //set glyph with for a type3 font
+ //COSNumber horizontalWidth = (COSNumber)arguments.get( 0 );
+ //COSNumber verticalWidth = (COSNumber)arguments.get( 1 );
+ //width = horizontalWidth.intValue();
+ //height = verticalWidth.intValue();
+ }
+ else if( operation.equals( "d1" ) )
+ {
+ //set glyph with and bounding box for type 3 font
+ //COSNumber horizontalWidth = (COSNumber)arguments.get( 0 );
+ //COSNumber verticalWidth = (COSNumber)arguments.get( 1 );
+ COSNumber llx = (COSNumber)arguments.get( 2 );
+ COSNumber lly = (COSNumber)arguments.get( 3 );
+ COSNumber urx = (COSNumber)arguments.get( 4 );
+ COSNumber ury = (COSNumber)arguments.get( 5 );
+
+ //width = horizontalWidth.intValue();
+ //height = verticalWidth.intValue();
+ box = new BoundingBox();
+ box.setLowerLeftX( llx.floatValue() );
+ box.setLowerLeftY( lly.floatValue() );
+ box.setUpperRightX( urx.floatValue() );
+ box.setUpperRightY( ury.floatValue() );
+ }/*
+ else if( operation.equals( "Do" ) )
+ {
+ //invoke named object.
+ }
+ else if( operation.equals( "DP" ) )
+ {
+ //(PDF 1.2) De.ne marked-content point with property list
+ }
+ else if( operation.equals( "EI" ) )
+ {
+ //end inline image object
+ }
+ else if( operation.equals( "EMC" ) )
+ {
+ //End inline image object
+ }
+ else if( operation.equals( "ET" ) )
+ {
+ log.debug( "<ET>" );
+ textMatrix = null;
+ textLineMatrix = null;
+ }
+ else if( operation.equals( "EX" ) )
+ {
+ //(PDF 1.1) End compatibility section
+ }
+ else if( operation.equals( "f" ) )
+ {
+ //Fill the path, using the nonzero winding number rule to determine the region to .ll
+ }
+ else if( operation.equals( "F" ) )
+ {
+ }
+ else if( operation.equals( "f*" ) )
+ {
+ //Fill path using even-odd rule
+ }
+ else if( operation.equals( "g" ) )
+ {
+ }
+ else if( operation.equals( "G" ) )
+ {
+ }
+ else if( operation.equals( "gs" ) )
+ {
+ }
+ else if( operation.equals( "h" ) )
+ {
+ //close subpath
+ }
+ else if( operation.equals( "i" ) )
+ {
+ //set flatness tolerance, not sure what this does
+ }
+ else if( operation.equals( "ID" ) )
+ {
+ //begin inline image data
+ }
+ else if( operation.equals( "j" ) )
+ {
+ //Set the line join style in the graphics state
+ //System.out.println( "<j>" );
+ }
+ else if( operation.equals( "J" ) )
+ {
+ //Set the line cap style in the graphics state
+ //System.out.println( "<J>" );
+ }
+ else if( operation.equals( "k" ) )
+ {
+ //Set CMYK color for nonstroking operations
+ }
+ else if( operation.equals( "K" ) )
+ {
+ //Set CMYK color for stroking operations
+ }
+ else if( operation.equals( "l" ) )
+ {
+ //append straight line segment from the current point to the point.
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ linePath.lineTo( x.floatValue(), pageSize.getHeight()-y.floatValue() );
+ }
+ else if( operation.equals( "m" ) )
+ {
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ linePath.reset();
+ linePath.moveTo( x.floatValue(), pageSize.getHeight()-y.floatValue() );
+ //System.out.println( "<m x=\"" + x.getValue() + "\" y=\"" + y.getValue() + "\" >" );
+ }
+ else if( operation.equals( "M" ) )
+ {
+ //System.out.println( "<M>" );
+ }
+ else if( operation.equals( "MP" ) )
+ {
+ //(PDF 1.2) Define marked-content point
+ }
+ else if( operation.equals( "n" ) )
+ {
+ //End path without .lling or stroking
+ //System.out.println( "<n>" );
+ }
+ else if( operation.equals( "q" ) )
+ {
+ //save graphics state
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "<" + operation + "> - save state" );
+ }
+ graphicsStack.push(graphicsState.clone());
+ }
+ else if( operation.equals( "Q" ) )
+ {
+ //restore graphics state
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "<" + operation + "> - restore state" );
+ }
+ graphicsState = (PDGraphicsState)graphicsStack.pop();
+ }
+ else if( operation.equals( "re" ) )
+ {
+ }
+ else if( operation.equals( "rg" ) )
+ {
+ //Set RGB color for nonstroking operations
+ }
+ else if( operation.equals( "RG" ) )
+ {
+ //Set RGB color for stroking operations
+ }
+ else if( operation.equals( "ri" ) )
+ {
+ //Set color rendering intent
+ }
+ else if( operation.equals( "s" ) )
+ {
+ //Close and stroke path
+ }
+ else if( operation.equals( "S" ) )
+ {
+ graphics.draw( linePath );
+ }
+ else if( operation.equals( "sc" ) )
+ {
+ //set color for nonstroking operations
+ //System.out.println( "<sc>" );
+ }
+ else if( operation.equals( "SC" ) )
+ {
+ //set color for stroking operations
+ //System.out.println( "<SC>" );
+ }
+ else if( operation.equals( "scn" ) )
+ {
+ //set color for nonstroking operations special
+ }
+ else if( operation.equals( "SCN" ) )
+ {
+ //set color for stroking operations special
+ }
+ else if( operation.equals( "sh" ) )
+ {
+ //(PDF 1.3) Paint area de.ned by shading pattern
+ }
+ else if( operation.equals( "T*" ) )
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("<T* graphicsState.getTextState().getLeading()=\"" +
+ graphicsState.getTextState().getLeading() + "\">");
+ }
+ //move to start of next text line
+ if( graphicsState.getTextState().getLeading() == 0 )
+ {
+ graphicsState.getTextState().setLeading( -.01f );
+ }
+ Matrix td = new Matrix();
+ td.setValue( 2, 1, -1 * graphicsState.getTextState().getLeading() * textMatrix.getValue(1,1));
+ textLineMatrix = textLineMatrix.multiply( td );
+ textMatrix = textLineMatrix.copy();
+ }
+ else if( operation.equals( "Tc" ) )
+ {
+ //set character spacing
+ COSNumber characterSpacing = (COSNumber)arguments.get( 0 );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<Tc characterSpacing=\"" + characterSpacing.floatValue() + "\" />");
+ }
+ graphicsState.getTextState().setCharacterSpacing( characterSpacing.floatValue() );
+ }
+ else if( operation.equals( "Td" ) )
+ {
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<Td x=\"" + x.floatValue() + "\" y=\"" + y.floatValue() + "\">");
+ }
+ Matrix td = new Matrix();
+ td.setValue( 2, 0, x.floatValue() * textMatrix.getValue(0,0) );
+ td.setValue( 2, 1, y.floatValue() * textMatrix.getValue(1,1) );
+ //log.debug( "textLineMatrix before " + textLineMatrix );
+ textLineMatrix = textLineMatrix.multiply( td );
+ //log.debug( "textLineMatrix after " + textLineMatrix );
+ textMatrix = textLineMatrix.copy();
+ }
+ else if( operation.equals( "TD" ) )
+ {
+ //move text position and set leading
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<TD x=\"" + x.floatValue() + "\" y=\"" + y.floatValue() + "\">");
+ }
+ graphicsState.getTextState().setLeading( -1 * y.floatValue() );
+ Matrix td = new Matrix();
+ td.setValue( 2, 0, x.floatValue() * textMatrix.getValue(0,0) );
+ td.setValue( 2, 1, y.floatValue() * textMatrix.getValue(1,1) );
+ //log.debug( "textLineMatrix before " + textLineMatrix );
+ textLineMatrix = textLineMatrix.multiply( td );
+ //log.debug( "textLineMatrix after " + textLineMatrix );
+ textMatrix = textLineMatrix.copy();
+ }
+ else if( operation.equals( "Tf" ) )
+ {
+ //set font and size
+ COSName fontName = (COSName)arguments.get( 0 );
+ graphicsState.getTextState().setFontSize( ((COSNumber)arguments.get( 1 ) ).floatValue() );
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("<Tf font=\"" + fontName.getName() + "\" size=\"" +
+ graphicsState.getTextState().getFontSize() + "\">");
+ }
+
+ //old way
+ //graphicsState.getTextState().getFont() = (COSObject)stream.getDictionaryObject( fontName );
+ //if( graphicsState.getTextState().getFont() == null )
+ //{
+ // graphicsState.getTextState().getFont() = (COSObject)graphicsState.getTextState().getFont()
+ // Dictionary.getItem( fontName );
+ //}
+ graphicsState.getTextState().setFont( (PDFont)fonts.get( fontName.getName() ) );
+ if( graphicsState.getTextState().getFont() == null )
+ {
+ throw new IOException( "Error: Could not find font(" + fontName + ") in map=" + fonts );
+ }
+ //log.debug( "Font Resource=" + fontResource );
+ //log.debug( "Current Font=" + graphicsState.getTextState().getFont() );
+ //log.debug( "graphicsState.getTextState().getFontSize()=" + graphicsState.getTextState().getFontSize() );
+ }
+ else if( operation.equals( "Tj" ) )
+ {
+ COSString string = (COSString)arguments.get( 0 );
+ TextPosition pos = showString( string.getBytes() );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<Tj string=\"" + string.getString() + "\">");
+ }
+ }
+ else if( operation.equals( "TJ" ) )
+ {
+ Matrix td = new Matrix();
+
+ COSArray array = (COSArray)arguments.get( 0 );
+ for( int i=0; i<array.size(); i++ )
+ {
+ COSBase next = array.get( i );
+ if( next instanceof COSNumber )
+ {
+ float value = -1*
+ (((COSNumber)next).floatValue()/1000) *
+ graphicsState.getTextState().getFontSize() *
+ textMatrix.getValue(1,1);
+
+ if (log.isDebugEnabled())
+ {
+ log.debug( "<TJ(" + i + ") value=\"" + value +
+ "\", param=\"" + ((COSNumber)next).floatValue() +
+ "\", fontsize=\"" + graphicsState.getTextState().getFontSize() + "\">" );
+ }
+ td.setValue( 2, 0, value );
+ textMatrix = textMatrix.multiply( td );
+ }
+ else if( next instanceof COSString )
+ {
+ TextPosition pos = showString( ((COSString)next).getBytes() );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<TJ(" + i + ") string=\"" + pos.getString() + "\">");
+ }
+ }
+ else
+ {
+ throw new IOException( "Unknown type in array for TJ operation:" + next );
+ }
+ }
+ }
+ else if( operation.equals( "TL" ) )
+ {
+ COSNumber leading = (COSNumber)arguments.get( 0 );
+ graphicsState.getTextState().setLeading( leading.floatValue() );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<TL leading=\"" + leading.floatValue() + "\" >");
+ }
+ }
+ else if( operation.equals( "Tm" ) )
+ {
+ //Set text matrix and text line matrix
+ COSNumber a = (COSNumber)arguments.get( 0 );
+ COSNumber b = (COSNumber)arguments.get( 1 );
+ COSNumber c = (COSNumber)arguments.get( 2 );
+ COSNumber d = (COSNumber)arguments.get( 3 );
+ COSNumber e = (COSNumber)arguments.get( 4 );
+ COSNumber f = (COSNumber)arguments.get( 5 );
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("<Tm " +
+ "a=\"" + a.floatValue() + "\" " +
+ "b=\"" + b.floatValue() + "\" " +
+ "c=\"" + c.floatValue() + "\" " +
+ "d=\"" + d.floatValue() + "\" " +
+ "e=\"" + e.floatValue() + "\" " +
+ "f=\"" + f.floatValue() + "\" >");
+ }
+
+ textMatrix = new Matrix();
+ textMatrix.setValue( 0, 0, a.floatValue() );
+ textMatrix.setValue( 0, 1, b.floatValue() );
+ textMatrix.setValue( 1, 0, c.floatValue() );
+ textMatrix.setValue( 1, 1, d.floatValue() );
+ textMatrix.setValue( 2, 0, e.floatValue() );
+ textMatrix.setValue( 2, 1, f.floatValue() );
+ textLineMatrix = textMatrix.copy();
+ }
+ else if( operation.equals( "Tr" ) )
+ {
+ //Set text rendering mode
+ //System.out.println( "<Tr>" );
+ }
+ else if( operation.equals( "Ts" ) )
+ {
+ //Set text rise
+ //System.out.println( "<Ts>" );
+ }
+ else if( operation.equals( "Tw" ) )
+ {
+ //set word spacing
+ COSNumber wordSpacing = (COSNumber)arguments.get( 0 );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<Tw wordSpacing=\"" + wordSpacing.floatValue() + "\" />");
+ }
+ graphicsState.getTextState().setWordSpacing( wordSpacing.floatValue() );
+ }
+ else if( operation.equals( "Tz" ) )
+ {
+ //Set horizontal text scaling
+ }
+ else if( operation.equals( "v" ) )
+ {
+ //Append curved segment to path (initial point replicated)
+ }
+ else if( operation.equals( "w" ) )
+ {
+ //Set the line width in the graphics state
+ //System.out.println( "<w>" );
+ }
+ else if( operation.equals( "W" ) )
+ {
+ //Set clipping path using nonzero winding number rule
+ //System.out.println( "<W>" );
+ }
+ else if( operation.equals( "W*" ) )
+ {
+ //Set clipping path using even-odd rule
+ }
+ else if( operation.equals( "y" ) )
+ {
+ //Append curved segment to path (final point replicated)
+ }
+ else if( operation.equals( "'" ) )
+ {
+ // Move to start of next text line, and show text
+ //
+ COSString string = (COSString)arguments.get( 0 );
+ if (log.isDebugEnabled())
+ {
+ log.debug("<' string=\"" + string.getString() + "\">");
+ }
+
+ Matrix td = new Matrix();
+ td.setValue( 2, 1, -1 * graphicsState.getTextState().getLeading() * textMatrix.getValue(1,1));
+ textLineMatrix = textLineMatrix.multiply( td );
+ textMatrix = textLineMatrix.copy();
+
+ showString( string.getBytes() );
+ }
+ else if( operation.equals( "\"" ) )
+ {
+ //Set word and character spacing, move to next line, and show text
+ //
+ COSNumber wordSpacing = (COSNumber)arguments.get( 0 );
+ COSNumber characterSpacing = (COSNumber)arguments.get( 1 );
+ COSString string = (COSString)arguments.get( 2 );
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("<\" wordSpacing=\"" + wordSpacing +
+ "\", characterSpacing=\"" + characterSpacing +
+ "\", string=\"" + string.getString() + "\">");
+ }
+
+ graphicsState.getTextState().setCharacterSpacing( characterSpacing.floatValue() );
+ graphicsState.getTextState().setWordSpacing( wordSpacing.floatValue() );
+
+ Matrix td = new Matrix();
+ td.setValue( 2, 1, -1 * graphicsState.getTextState().getLeading() * textMatrix.getValue(1,1));
+ textLineMatrix = textLineMatrix.multiply( td );
+ textMatrix = textLineMatrix.copy();
+
+ showString( string.getBytes() );
+ }*/
+ }
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/font/package.html b/src/main/java/org/pdfbox/pdmodel/font/package.html
new file mode 100644
index 0000000..4e8d27e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/font/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+Classes to deal with font functionality in a PDF Document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java b/src/main/java/org/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java
new file mode 100644
index 0000000..3fe9c40
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java
@@ -0,0 +1,724 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSBoolean;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+
+/**
+ * This class represents the graphics state dictionary that is stored in the PDF document.
+ * The PDGraphicsStateValue holds the current runtime values as a stream is being executed.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDExtendedGraphicsState implements COSObjectable
+{
+ private static final COSName LW = COSName.getPDFName( "LW" );
+ private static final COSName LC = COSName.getPDFName( "LC" );
+ private static final COSName LJ = COSName.getPDFName( "LJ" );
+ private static final COSName ML = COSName.getPDFName( "ML" );
+ private static final COSName D = COSName.getPDFName( "D" );
+ private static final COSName RI = COSName.getPDFName( "RI" );
+ private static final COSName OP = COSName.getPDFName( "OP" );
+ private static final COSName OP_NS = COSName.getPDFName( "op" );
+ private static final COSName OPM = COSName.getPDFName( "OPM" );
+ private static final COSName FONT = COSName.getPDFName( "Font" );
+ private static final COSName FL = COSName.getPDFName( "FL" );
+ private static final COSName SM = COSName.getPDFName( "SM" );
+ private static final COSName SA = COSName.getPDFName( "SA" );
+ private static final COSName CA = COSName.getPDFName( "CA" );
+ private static final COSName CA_NS = COSName.getPDFName( "ca" );
+ private static final COSName AIS = COSName.getPDFName( "AIS" );
+ private static final COSName TK = COSName.getPDFName( "TK" );
+
+ /**
+ * Rendering intent constants, see PDF Reference 1.5 Section 4.5.4 Rendering Intents.
+ */
+ public static final String RENDERING_INTENT_ABSOLUTE_COLORIMETRIC = "AbsoluteColorimetric";
+ /**
+ * Rendering intent constants, see PDF Reference 1.5 Section 4.5.4 Rendering Intents.
+ */
+ public static final String RENDERING_INTENT_RELATIVE_COLORIMETRIC = "RelativeColorimetric";
+ /**
+ * Rendering intent constants, see PDF Reference 1.5 Section 4.5.4 Rendering Intents.
+ */
+ public static final String RENDERING_INTENT_SATURATION = "Saturation";
+ /**
+ * Rendering intent constants, see PDF Reference 1.5 Section 4.5.4 Rendering Intents.
+ */
+ public static final String RENDERING_INTENT_PERCEPTUAL = "Perceptual";
+
+
+ private COSDictionary graphicsState;
+
+ /**
+ * Default constructor, creates blank graphics state.
+ */
+ public PDExtendedGraphicsState()
+ {
+ graphicsState = new COSDictionary();
+ graphicsState.setItem( COSName.TYPE, COSName.getPDFName( "ExtGState" ) );
+ }
+
+ /**
+ * Create a graphics state from an existing dictionary.
+ *
+ * @param dictionary The existing graphics state.
+ */
+ public PDExtendedGraphicsState( COSDictionary dictionary )
+ {
+ graphicsState = dictionary;
+ }
+
+ /**
+ * This will implement the gs operator.
+ *
+ * @param gs The state to copy this dictionaries values into.
+ *
+ * @throws IOException If there is an error copying font information.
+ */
+ public void copyIntoGraphicsState( PDGraphicsState gs ) throws IOException
+ {
+ Iterator keys = graphicsState.keyList().iterator();
+ while( keys.hasNext() )
+ {
+ COSName key = (COSName)keys.next();
+ if( key.equals( LW ) )
+ {
+ gs.setLineWidth( getLineWidth().doubleValue() );
+ }
+ else if( key.equals( LC ) )
+ {
+ gs.setLineCap( getLineCapStyle().intValue() );
+ }
+ else if( key.equals( LJ ) )
+ {
+ gs.setLineJoin( getLineJoinStyle().intValue() );
+ }
+ else if( key.equals( ML ) )
+ {
+ gs.setMiterLimit( getMiterLimit().doubleValue() );
+ }
+ else if( key.equals( D ) )
+ {
+ gs.setLineDashPattern( getLineDashPattern() );
+ }
+ else if( key.equals( RI ) )
+ {
+ gs.setRenderingIntent( getRenderingIntent() );
+ }
+ else if( key.equals( OPM ) )
+ {
+ gs.setOverprintMode( getOverprintMode().doubleValue() );
+ }
+ else if( key.equals( FONT ) )
+ {
+ PDFontSetting setting = getFontSetting();
+ gs.getTextState().setFont( setting.getFont() );
+ gs.getTextState().setFontSize( setting.getFontSize() );
+ }
+ else if( key.equals( FL ) )
+ {
+ gs.setFlatness( getFlatnessTolerance().floatValue() );
+ }
+ else if( key.equals( SM ) )
+ {
+ gs.setSmoothness( getSmoothnessTolerance().floatValue() );
+ }
+ else if( key.equals( SA ) )
+ {
+ gs.setStrokeAdjustment( getAutomaticStrokeAdjustment().booleanValue() );
+ }
+ else if( key.equals( CA ) )
+ {
+ gs.setAlphaConstants( getStrokingAlpaConstant().floatValue() );
+ }/**
+ else if( key.equals( CA_NS ) )
+ {
+ }**/
+ else if( key.equals( AIS ) )
+ {
+ gs.setAlphaSource( getAlpaSourceFlag().booleanValue() );
+ }
+ else if( key.equals( TK ) )
+ {
+ gs.getTextState().setKnockoutFlag( getTextKnockoutFlag().booleanValue() );
+ }
+ }
+ }
+
+ /**
+ * This will get the underlying dictionary that this class acts on.
+ *
+ * @return The underlying dictionary for this class.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return graphicsState;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return graphicsState;
+ }
+
+ /**
+ * This will get the line width. This will return null if there is no line width
+ *
+ * @return null or the LW value of the dictionary.
+ */
+ public Float getLineWidth()
+ {
+ return getFloatItem( LW );
+ }
+
+ /**
+ * This will set the line width.
+ *
+ * @param width The line width for the object.
+ */
+ public void setLineWidth( Float width )
+ {
+ setFloatItem( LW, width );
+ }
+
+ /**
+ * This will get the line cap style.
+ *
+ * @return null or the LC value of the dictionary.
+ */
+ public Long getLineCapStyle()
+ {
+ return getLongItem( LC );
+ }
+
+ /**
+ * This will set the line cap style for the graphics state.
+ *
+ * @param style The new line cap style to set.
+ */
+ public void setLineCapStyle( Long style )
+ {
+ setLongItem( LC, style );
+ }
+
+ /**
+ * This will get the line join style.
+ *
+ * @return null or the LJ value in the dictionary.
+ */
+ public Long getLineJoinStyle()
+ {
+ return getLongItem( LJ );
+ }
+
+ /**
+ * This will set the line join style.
+ *
+ * @param style The new line join style.
+ */
+ public void setLineJoinStyle( Long style )
+ {
+ setLongItem( LJ, style );
+ }
+
+
+ /**
+ * This will get the miter limit.
+ *
+ * @return null or the ML value in the dictionary.
+ */
+ public Float getMiterLimit()
+ {
+ return getFloatItem( ML );
+ }
+
+ /**
+ * This will set the miter limit for the graphics state.
+ *
+ * @param miterLimit The new miter limit value
+ */
+ public void setMiterLimit( Float miterLimit )
+ {
+ setFloatItem( ML, miterLimit );
+ }
+
+ /**
+ * This will get the dash pattern.
+ *
+ * @return null or the D value in the dictionary.
+ */
+ public PDLineDashPattern getLineDashPattern()
+ {
+ PDLineDashPattern retval = null;
+ COSArray dp = (COSArray)graphicsState.getDictionaryObject( D );
+ if( dp != null )
+ {
+ retval = new PDLineDashPattern( dp );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the dash pattern for the graphics state.
+ *
+ * @param dashPattern The dash pattern
+ */
+ public void setLineDashPattern( PDLineDashPattern dashPattern )
+ {
+ graphicsState.setItem( D, dashPattern.getCOSObject() );
+ }
+
+ /**
+ * This will get the rendering intent.
+ *
+ * @return null or the RI value in the dictionary.
+ */
+ public String getRenderingIntent()
+ {
+ String retval = null;
+ COSName ri = (COSName)graphicsState.getDictionaryObject( RI );
+ if( ri != null )
+ {
+ retval = ((COSName)ri).getName();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the rendering intent for the graphics state.
+ *
+ * @param ri The new rendering intent
+ */
+ public void setRenderingIntent( String ri )
+ {
+ COSName intent = null;
+ if( ri != null )
+ {
+ intent = COSName.getPDFName( ri );
+ }
+ graphicsState.setItem( RI, intent );
+ }
+
+ /**
+ * This will get the overprint control.
+ *
+ * @return The overprint control or null if one has not been set.
+ */
+ public Boolean getStrokingOverprintControl()
+ {
+ return getBooleanItem( OP );
+ }
+
+ /**
+ * This will get the overprint control(OP).
+ *
+ * @param op The overprint control.
+ */
+ public void setStrokingOverprintControl( Boolean op )
+ {
+ setBooleanItem( OP, op );
+ }
+
+ /**
+ * This will get the overprint control for non stroking operations. If this
+ * value is null then the regular overprint control value will be returned.
+ *
+ * @return The overprint control or null if one has not been set.
+ */
+ public Boolean getNonStrokingOverprintControl()
+ {
+ Boolean retval = getBooleanItem( OP_NS );
+ if( retval == null )
+ {
+ retval = getStrokingOverprintControl();
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the overprint control(OP).
+ *
+ * @param op The overprint control.
+ */
+ public void setNonStrokingOverprintControl( Boolean op )
+ {
+ setBooleanItem( OP_NS, op );
+ }
+
+ /**
+ * This will get the overprint control mode.
+ *
+ * @return The overprint control mode or null if one has not been set.
+ */
+ public Float getOverprintMode()
+ {
+ return getFloatItem( OPM );
+ }
+
+ /**
+ * This will get the overprint mode(OPM).
+ *
+ * @param overprintMode The overprint mode
+ */
+ public void setOverprintMode( Float overprintMode )
+ {
+ setFloatItem( OPM, overprintMode );
+ }
+
+ /**
+ * This will get the font setting of the graphics state.
+ *
+ * @return The font setting.
+ */
+ public PDFontSetting getFontSetting()
+ {
+ PDFontSetting setting = null;
+ COSArray font = (COSArray)graphicsState.getDictionaryObject( FONT );
+ if( font != null )
+ {
+ setting = new PDFontSetting( font );
+ }
+ return setting;
+ }
+
+ /**
+ * This will set the font setting for this graphics state.
+ *
+ * @param fs The new font setting.
+ */
+ public void setFontSetting( PDFontSetting fs )
+ {
+ graphicsState.setItem( FONT, fs );
+ }
+
+ /**
+ * This will get the flatness tolerance.
+ *
+ * @return The flatness tolerance or null if one has not been set.
+ */
+ public Float getFlatnessTolerance()
+ {
+ return getFloatItem( FL );
+ }
+
+ /**
+ * This will get the flatness tolerance.
+ *
+ * @param flatness The new flatness tolerance
+ */
+ public void setFlatnessTolerance( Float flatness )
+ {
+ setFloatItem( FL, flatness );
+ }
+
+ /**
+ * This will get the smothness tolerance.
+ *
+ * @return The smothness tolerance or null if one has not been set.
+ */
+ public Float getSmoothnessTolerance()
+ {
+ return getFloatItem( SM );
+ }
+
+ /**
+ * This will get the smoothness tolerance.
+ *
+ * @param smoothness The new smoothness tolerance
+ */
+ public void setSmoothnessTolerance( Float smoothness )
+ {
+ setFloatItem( SM, smoothness );
+ }
+
+ /**
+ * This will get the automatic stroke adjustment flag.
+ *
+ * @return The automatic stroke adjustment flag or null if one has not been set.
+ */
+ public Boolean getAutomaticStrokeAdjustment()
+ {
+ return getBooleanItem( SA );
+ }
+
+ /**
+ * This will get the automatic stroke adjustment flag.
+ *
+ * @param sa The new automatic stroke adjustment flag.
+ */
+ public void setAutomaticStrokeAdjustment( Boolean sa )
+ {
+ setBooleanItem( SA, sa );
+ }
+
+ /**
+ * This will get the stroking alpha constant.
+ *
+ * @return The stroking alpha constant or null if one has not been set.
+ */
+ public Float getStrokingAlpaConstant()
+ {
+ return getFloatItem( CA );
+ }
+
+ /**
+ * This will get the stroking alpha constant.
+ *
+ * @param alpha The new stroking alpha constant.
+ */
+ public void setStrokingAlphaConstant( Float alpha )
+ {
+ setFloatItem( CA, alpha );
+ }
+
+ /**
+ * This will get the non stroking alpha constant.
+ *
+ * @return The non stroking alpha constant or null if one has not been set.
+ */
+ public Float getNonStrokingAlpaConstant()
+ {
+ return getFloatItem( CA_NS );
+ }
+
+ /**
+ * This will get the non stroking alpha constant.
+ *
+ * @param alpha The new non stroking alpha constant.
+ */
+ public void setNonStrokingAlphaConstant( Float alpha )
+ {
+ setFloatItem( CA_NS, alpha );
+ }
+
+ /**
+ * This will get the alpha source flag.
+ *
+ * @return The alpha source flag.
+ */
+ public Boolean getAlpaSourceFlag()
+ {
+ return getBooleanItem( AIS );
+ }
+
+ /**
+ * This will get the alpha source flag.
+ *
+ * @param alpha The alpha source flag.
+ */
+ public void setAlphaSourceFlag( Boolean alpha )
+ {
+ setBooleanItem( AIS, alpha );
+ }
+
+ /**
+ * This will get the text knockout flag.
+ *
+ * @return The text knockout flag.
+ */
+ public Boolean getTextKnockoutFlag()
+ {
+ return getBooleanItem( TK );
+ }
+
+ /**
+ * This will get the text knockout flag.
+ *
+ * @param tk The text knockout flag.
+ */
+ public void setTextKnockoutFlag( Boolean tk )
+ {
+ setBooleanItem( TK, tk );
+ }
+
+ /**
+ * This will get a float item from the dictionary.
+ *
+ * @param key The key to the item.
+ *
+ * @return The value for that item.
+ */
+ private Float getFloatItem( COSName key )
+ {
+ Float retval = null;
+ COSNumber value = (COSNumber)graphicsState.getDictionaryObject( key );
+ if( value != null )
+ {
+ retval = new Float( value.floatValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a float object.
+ *
+ * @param key The key to the data that we are setting.
+ * @param value The value that we are setting.
+ */
+ private void setFloatItem( COSName key, Float value )
+ {
+ if( value == null )
+ {
+ graphicsState.removeItem( key );
+ }
+ else
+ {
+ graphicsState.setItem( key, new COSFloat( value.floatValue() ) );
+ }
+ }
+
+ /**
+ * This will get a integer item from the dictionary.
+ *
+ * @param key The key to the item.
+ *
+ * @return The value for that item.
+ */
+ private Integer getIntegerItem( COSName key )
+ {
+ Integer retval = null;
+ COSNumber value = (COSNumber)graphicsState.getDictionaryObject( key );
+ if( value != null )
+ {
+ retval = new Integer( value.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a integer object.
+ *
+ * @param key The key to the data that we are setting.
+ * @param value The value that we are setting.
+ */
+ private void setIntegerItem( COSName key, Integer value )
+ {
+ if( value == null )
+ {
+ graphicsState.removeItem( key );
+ }
+ else
+ {
+ graphicsState.setItem( key, new COSInteger( value.intValue() ) );
+ }
+ }
+
+ /**
+ * This will get an int item from the dictionary.
+ *
+ * @param key The key to the item.
+ *
+ * @return The value for that item.
+ */
+ private Long getLongItem( COSName key )
+ {
+ Long retval = null;
+ COSNumber value = (COSNumber)graphicsState.getDictionaryObject( key );
+ if( value != null )
+ {
+ retval = new Long( value.intValue() );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an integer object.
+ *
+ * @param key The key to the data that we are setting.
+ * @param value The value that we are setting.
+ */
+ private void setLongItem( COSName key, Long value )
+ {
+ if( value == null )
+ {
+ graphicsState.removeItem( key );
+ }
+ else
+ {
+ graphicsState.setItem( key, new COSInteger( value.longValue() ) );
+ }
+ }
+
+ /**
+ * This will get a boolean item from the dictionary.
+ *
+ * @param key The key to the item.
+ *
+ * @return The value for that item.
+ */
+ private Boolean getBooleanItem( COSName key )
+ {
+ Boolean retval = null;
+ COSBoolean value = (COSBoolean)graphicsState.getDictionaryObject( key );
+ if( value != null )
+ {
+ retval = value.getValueAsObject();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an boolean object.
+ *
+ * @param key The key to the data that we are setting.
+ * @param value The value that we are setting.
+ */
+ private void setBooleanItem( COSName key, Boolean value )
+ {
+ if( value == null )
+ {
+ graphicsState.removeItem( key );
+ }
+ else
+ {
+ graphicsState.setItem( key, COSBoolean.getBoolean( value ) );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/PDFontSetting.java b/src/main/java/org/pdfbox/pdmodel/graphics/PDFontSetting.java
new file mode 100644
index 0000000..039609f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/PDFontSetting.java
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDFontFactory;
+
+import java.io.IOException;
+
+/**
+ * This class represents a font setting used for the graphics state. A font setting is a font and a
+ * font size. Maybe there is a better name for this?
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDFontSetting implements COSObjectable
+{
+ private COSArray fontSetting = null;
+
+ /**
+ * Creates a blank font setting, font will be null, size will be 1.
+ */
+ public PDFontSetting()
+ {
+ fontSetting = new COSArray();
+ fontSetting.add( null );
+ fontSetting.add( new COSFloat( 1 ) );
+ }
+
+ /**
+ * Constructs a font setting from an existing array.
+ *
+ * @param fs The new font setting value.
+ */
+ public PDFontSetting( COSArray fs )
+ {
+ fontSetting = fs;
+ }
+
+ /**
+ * @see COSObjectable#getCOSObject()
+ */
+ public COSBase getCOSObject()
+ {
+ return fontSetting;
+ }
+
+ /**
+ * This will get the font for this font setting.
+ *
+ * @return The font for this setting of null if one was not found.
+ *
+ * @throws IOException If there is an error getting the font.
+ */
+ public PDFont getFont() throws IOException
+ {
+ PDFont retval = null;
+ COSBase font = fontSetting.get( 0 );
+ if( font instanceof COSDictionary )
+ {
+ retval = PDFontFactory.createFont( (COSDictionary)font );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the font for this font setting.
+ *
+ * @param font The new font.
+ */
+ public void setFont( PDFont font )
+ {
+ fontSetting.set( 0, font );
+ }
+
+ /**
+ * This will get the size of the font.
+ *
+ * @return The size of the font.
+ */
+ public float getFontSize()
+ {
+ COSNumber size = (COSNumber)fontSetting.get( 1 );
+ return size.floatValue();
+ }
+
+ /**
+ * This will set the size of the font.
+ *
+ * @param size The new size of the font.
+ */
+ public void setFontSize( float size )
+ {
+ fontSetting.set( 1, new COSFloat( size ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/PDGraphicsState.java b/src/main/java/org/pdfbox/pdmodel/graphics/PDGraphicsState.java
new file mode 100644
index 0000000..4115198
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/PDGraphicsState.java
@@ -0,0 +1,438 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics;
+
+import org.pdfbox.util.Matrix;
+
+import org.pdfbox.pdmodel.text.PDTextState;
+
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+
+/**
+ * This class will hold the current state of the graphics parameters when executing a
+ * content stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDGraphicsState implements Cloneable
+{
+ private Matrix currentTransformationMatrix = new Matrix();
+
+ //Here are some attributes of the Graphics state, but have not been created yet.
+ //
+ //clippingPath
+ private PDColorSpaceInstance strokingColorSpace = new PDColorSpaceInstance();
+ private PDColorSpaceInstance nonStrokingColorSpace = new PDColorSpaceInstance();
+ private PDTextState textState = new PDTextState();
+ private double lineWidth = 0;
+ private int lineCap = 0;
+ private int lineJoin = 0;
+ private double miterLimit = 0;
+ private PDLineDashPattern lineDashPattern;
+ private String renderingIntent;
+ private boolean strokeAdjustment = false;
+ //blend mode
+ //soft mask
+ private double alphaConstants = 0;
+ private boolean alphaSource = false;
+
+ //DEVICE DEPENDENT parameters
+ private boolean overprint = false;
+ private double overprintMode = 0;
+ //black generation
+ //undercolor removal
+ //transfer
+ //halftone
+ private double flatness = 1.0;
+ private double smoothness = 0;
+
+ /**
+ * Get the value of the CTM.
+ *
+ * @return The current transformation matrix.
+ */
+ public Matrix getCurrentTransformationMatrix()
+ {
+ return currentTransformationMatrix;
+ }
+
+ /**
+ * Set the value of the CTM.
+ *
+ * @param value The current transformation matrix.
+ */
+ public void setCurrentTransformationMatrix(Matrix value)
+ {
+ currentTransformationMatrix = value;
+ }
+
+ /**
+ * Get the value of the line width.
+ *
+ * @return The current line width.
+ */
+ public double getLineWidth()
+ {
+ return lineWidth;
+ }
+
+ /**
+ * set the value of the line width.
+ *
+ * @param value The current line width.
+ */
+ public void setLineWidth(double value)
+ {
+ lineWidth = value;
+ }
+
+ /**
+ * Get the value of the line cap.
+ *
+ * @return The current line cap.
+ */
+ public int getLineCap()
+ {
+ return lineCap;
+ }
+
+ /**
+ * set the value of the line cap.
+ *
+ * @param value The current line cap.
+ */
+ public void setLineCap(int value)
+ {
+ lineCap = value;
+ }
+
+ /**
+ * Get the value of the line join.
+ *
+ * @return The current line join value.
+ */
+ public int getLineJoin()
+ {
+ return lineJoin;
+ }
+
+ /**
+ * Get the value of the line join.
+ *
+ * @param value The current line join
+ */
+ public void setLineJoin(int value)
+ {
+ lineJoin = value;
+ }
+
+ /**
+ * Get the value of the miter limit.
+ *
+ * @return The current miter limit.
+ */
+ public double getMiterLimit()
+ {
+ return miterLimit;
+ }
+
+ /**
+ * set the value of the miter limit.
+ *
+ * @param value The current miter limit.
+ */
+ public void setMiterLimit(double value)
+ {
+ miterLimit = value;
+ }
+
+ /**
+ * Get the value of the stroke adjustment parameter.
+ *
+ * @return The current stroke adjustment.
+ */
+ public boolean isStrokeAdjustment()
+ {
+ return strokeAdjustment;
+ }
+
+ /**
+ * set the value of the stroke adjustment.
+ *
+ * @param value The value of the stroke adjustment parameter.
+ */
+ public void setStrokeAdjustment(boolean value)
+ {
+ strokeAdjustment = value;
+ }
+
+ /**
+ * Get the value of the alpha constants property.
+ *
+ * @return The value of the alpha constants parameter.
+ */
+ public double getAlphaConstants()
+ {
+ return alphaConstants;
+ }
+
+ /**
+ * set the value of the alpha constants property.
+ *
+ * @param value The value of the alpha constants parameter.
+ */
+ public void setAlphaConstants(double value)
+ {
+ alphaConstants = value;
+ }
+
+ /**
+ * get the value of the alpha source property.
+ *
+ * @return The value of the alpha source parameter.
+ */
+ public boolean isAlphaSource()
+ {
+ return alphaSource;
+ }
+
+ /**
+ * set the value of the alpha source property.
+ *
+ * @param value The value of the alpha source parameter.
+ */
+ public void setAlphaSource(boolean value)
+ {
+ alphaSource = value;
+ }
+
+ /**
+ * get the value of the overprint property.
+ *
+ * @return The value of the overprint parameter.
+ */
+ public boolean isOverprint()
+ {
+ return overprint;
+ }
+
+ /**
+ * set the value of the overprint property.
+ *
+ * @param value The value of the overprint parameter.
+ */
+ public void setOverprint(boolean value)
+ {
+ overprint = value;
+ }
+
+ /**
+ * get the value of the overprint mode property.
+ *
+ * @return The value of the overprint mode parameter.
+ */
+ public double getOverprintMode()
+ {
+ return overprintMode;
+ }
+
+ /**
+ * set the value of the overprint mode property.
+ *
+ * @param value The value of the overprint mode parameter.
+ */
+ public void setOverprintMode(double value)
+ {
+ overprintMode = value;
+ }
+
+ /**
+ * get the value of the flatness property.
+ *
+ * @return The value of the flatness parameter.
+ */
+ public double getFlatness()
+ {
+ return flatness;
+ }
+
+ /**
+ * set the value of the flatness property.
+ *
+ * @param value The value of the flatness parameter.
+ */
+ public void setFlatness(double value)
+ {
+ flatness = value;
+ }
+
+ /**
+ * get the value of the smoothness property.
+ *
+ * @return The value of the smoothness parameter.
+ */
+ public double getSmoothness()
+ {
+ return smoothness;
+ }
+
+ /**
+ * set the value of the smoothness property.
+ *
+ * @param value The value of the smoothness parameter.
+ */
+ public void setSmoothness(double value)
+ {
+ smoothness = value;
+ }
+
+ /**
+ * This will get the graphics text state.
+ *
+ * @return The graphics text state.
+ */
+ public PDTextState getTextState()
+ {
+ return textState;
+ }
+
+ /**
+ * This will set the graphics text state.
+ *
+ * @param value The graphics text state.
+ */
+ public void setTextState(PDTextState value)
+ {
+ textState = value;
+ }
+
+ /**
+ * This will get the current line dash pattern.
+ *
+ * @return The line dash pattern.
+ */
+ public PDLineDashPattern getLineDashPattern()
+ {
+ return lineDashPattern;
+ }
+
+ /**
+ * This will set the current line dash pattern.
+ *
+ * @param value The new line dash pattern.
+ */
+ public void setLineDashPattern(PDLineDashPattern value)
+ {
+ lineDashPattern = value;
+ }
+
+ /**
+ * This will get the rendering intent.
+ *
+ * @see PDExtendedGraphicsState
+ *
+ * @return The rendering intent
+ */
+ public String getRenderingIntent()
+ {
+ return renderingIntent;
+ }
+
+ /**
+ * This will set the rendering intent.
+ *
+ * @param value The new rendering intent.
+ */
+ public void setRenderingIntent(String value)
+ {
+ renderingIntent = value;
+ }
+
+ /**
+ * @see Object#clone()
+ */
+ public Object clone()
+ {
+ PDGraphicsState clone = null;
+ try
+ {
+ clone = (PDGraphicsState)super.clone();
+ clone.setTextState( (PDTextState)textState.clone() );
+ clone.setCurrentTransformationMatrix( currentTransformationMatrix.copy() );
+ }
+ catch( CloneNotSupportedException e )
+ {
+ e.printStackTrace();
+ }
+ return clone;
+ }
+
+ /**
+ * This will get the current stroking colorspace.
+ *
+ * @return The current stroking colorspace.
+ */
+ public PDColorSpaceInstance getStrokingColorSpace()
+ {
+ return strokingColorSpace;
+ }
+
+ /**
+ * This will set the current stroking colorspace.
+ *
+ * @param value The new stroking colorspace instance.
+ */
+ public void setStrokingColorSpace(PDColorSpaceInstance value)
+ {
+ strokingColorSpace = value;
+ }
+
+ /**
+ * This will get the nonstroking color space instance.
+ *
+ * @return The colorspace instance.
+ */
+ public PDColorSpaceInstance getNonStrokingColorSpace()
+ {
+ return nonStrokingColorSpace;
+ }
+
+ /**
+ * This will set the non-stroking colorspace instance.
+ *
+ * @param value The non-stroking colorspace instance.
+ */
+ public void setNonStrokingColorSpace(PDColorSpaceInstance value)
+ {
+ nonStrokingColorSpace = value;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/PDLineDashPattern.java b/src/main/java/org/pdfbox/pdmodel/graphics/PDLineDashPattern.java
new file mode 100644
index 0000000..a67d5e4
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/PDLineDashPattern.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import java.util.List;
+
+/**
+ * This class represents the line dash pattern for a graphics state. See PDF
+ * Reference 1.5 section 4.3.2
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.4 $
+ */
+public class PDLineDashPattern implements COSObjectable
+{
+ private COSArray lineDashPattern = null;
+
+ /**
+ * Creates a blank line dash pattern. With no dashes and a phase of 0.
+ */
+ public PDLineDashPattern()
+ {
+ lineDashPattern = new COSArray();
+ lineDashPattern.add( new COSArray() );
+ lineDashPattern.add( new COSInteger( 0 ) );
+ }
+
+ /**
+ * Constructs a line dash pattern from an existing array.
+ *
+ * @param ldp The existing line dash pattern.
+ */
+ public PDLineDashPattern( COSArray ldp )
+ {
+ lineDashPattern = ldp;
+ }
+
+ /**
+ * @see COSObjectable#getCOSObject()
+ */
+ public COSBase getCOSObject()
+ {
+ return lineDashPattern;
+ }
+
+ /**
+ * This will get the line dash pattern phase. The dash phase specifies the
+ * distance into the dash pattern at which to start the dash.
+ *
+ * @return The line dash pattern phase.
+ */
+ public int getPhaseStart()
+ {
+ COSNumber phase = (COSNumber)lineDashPattern.get( 1 );
+ return phase.intValue();
+ }
+
+ /**
+ * This will set the line dash pattern phase.
+ *
+ * @param phase The new line dash patter phase.
+ */
+ public void setPhaseStart( int phase )
+ {
+ lineDashPattern.set( 1, new COSInteger( phase ) );
+ }
+
+ /**
+ * This will return a list of java.lang.Integer objects that represent the line
+ * dash pattern appearance.
+ *
+ * @return The line dash pattern.
+ */
+ public List getDashPattern()
+ {
+ COSArray dashPatterns = (COSArray)lineDashPattern.get( 0 );
+ return COSArrayList.convertIntegerCOSArrayToList( dashPatterns );
+ }
+
+ /**
+ * Get the line dash pattern as a COS object.
+ *
+ * @return The cos array line dash pattern.
+ */
+ public COSArray getCOSDashPattern()
+ {
+ return (COSArray)lineDashPattern.get( 0 );
+ }
+
+ /**
+ * This will replace the existing line dash pattern.
+ *
+ * @param dashPattern A list of java.lang.Integer objects.
+ */
+ public void setDashPattern( List dashPattern )
+ {
+ lineDashPattern.set( 0, COSArrayList.converterToCOSArray( dashPattern ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalGray.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalGray.java
new file mode 100644
index 0000000..fc6f84e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalGray.java
@@ -0,0 +1,240 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+import java.io.IOException;
+
+/**
+ * This class represents a Cal Gray color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDCalGray extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "CalGray";
+
+ private COSArray array;
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ */
+ public PDCalGray()
+ {
+ array = new COSArray();
+ dictionary = new COSDictionary();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( dictionary );
+ }
+
+ /**
+ * Constructor with array.
+ *
+ * @param gray The underlying color space.
+ */
+ public PDCalGray( COSArray gray )
+ {
+ array = gray;
+ dictionary = (COSDictionary)array.getObject( 1 );
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return 1;
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return array;
+ }
+
+ /**
+ * This will get the gamma value. If none is present then the default of 1
+ * will be returned.
+ *
+ * @return The gamma value.
+ */
+ public float getGamma()
+ {
+ float retval = 1.0f;
+ COSNumber gamma = (COSNumber)dictionary.getDictionaryObject( COSName.getPDFName( "Gamma" ) );
+ if( gamma != null )
+ {
+ retval = gamma.floatValue();
+ }
+ return retval;
+ }
+
+ /**
+ * Set the gamma value.
+ *
+ * @param value The new gamma value.
+ */
+ public void setGamma( float value )
+ {
+ dictionary.setItem( COSName.getPDFName( "Gamma" ), new COSFloat( value ) );
+ }
+
+ /**
+ * This will return the whitepoint tristimulus. As this is a required field
+ * this will never return null. A default of 1,1,1 will be returned if the
+ * pdf does not have any values yet.
+ *
+ * @return The whitepoint tristimulus.
+ */
+ public PDTristimulus getWhitepoint()
+ {
+ PDTristimulus retval = null;
+ COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "WhitePoint" ) );
+ if( wp == null )
+ {
+ wp.add( new COSFloat( 1.0f ) );
+ wp.add( new COSFloat( 1.0f ) );
+ wp.add( new COSFloat( 1.0f ) );
+ dictionary.setItem( COSName.getPDFName( "WhitePoint" ), wp );
+ }
+ return new PDTristimulus( wp );
+ }
+
+ /**
+ * This will set the whitepoint tristimulus. As this is a required field
+ * this null should not be passed into this function.
+ *
+ * @param wp The whitepoint tristimulus.
+ */
+ public void setWhitepoint( PDTristimulus wp )
+ {
+ COSBase wpArray = wp.getCOSObject();
+ if( wpArray != null )
+ {
+ dictionary.setItem( COSName.getPDFName( "WhitePoint" ), wpArray );
+ }
+ }
+
+ /**
+ * This will return the BlackPoint tristimulus. This is an optional field but
+ * has defaults so this will never return null.
+ * A default of 0,0,0 will be returned if the pdf does not have any values yet.
+ *
+ * @return The blackpoint tristimulus.
+ */
+ public PDTristimulus getBlackPoint()
+ {
+ PDTristimulus retval = null;
+ COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "BlackPoint" ) );
+ if( bp == null )
+ {
+ bp.add( new COSFloat( 0.0f ) );
+ bp.add( new COSFloat( 0.0f ) );
+ bp.add( new COSFloat( 0.0f ) );
+ dictionary.setItem( COSName.getPDFName( "BlackPoint" ), bp );
+ }
+ return new PDTristimulus( bp );
+ }
+
+ /**
+ * This will set the BlackPoint tristimulus. As this is a required field
+ * this null should not be passed into this function.
+ *
+ * @param bp The BlackPoint tristimulus.
+ */
+ public void setBlackPoint( PDTristimulus bp )
+ {
+ COSBase bpArray = null;
+ if( bp != null )
+ {
+ bpArray = bp.getCOSObject();
+ }
+ dictionary.setItem( COSName.getPDFName( "BlackPoint" ), bpArray );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalRGB.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalRGB.java
new file mode 100644
index 0000000..6aa4eb1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDCalRGB.java
@@ -0,0 +1,289 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.PDMatrix;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+import java.io.IOException;
+
+/**
+ * This class represents a Cal RGB color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDCalRGB extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "CalRGB";
+
+ private COSArray array;
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ */
+ public PDCalRGB()
+ {
+ array = new COSArray();
+ dictionary = new COSDictionary();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( dictionary );
+ }
+
+ /**
+ * Constructor with array.
+ *
+ * @param rgb The underlying color space.
+ */
+ public PDCalRGB( COSArray rgb )
+ {
+ array = rgb;
+ dictionary = (COSDictionary)array.getObject( 1 );
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return 3;
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return array;
+ }
+
+ /**
+ * This will return the whitepoint tristimulus. As this is a required field
+ * this will never return null. A default of 1,1,1 will be returned if the
+ * pdf does not have any values yet.
+ *
+ * @return The whitepoint tristimulus.
+ */
+ public PDTristimulus getWhitepoint()
+ {
+ PDTristimulus retval = null;
+ COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "WhitePoint" ) );
+ if( wp == null )
+ {
+ wp.add( new COSFloat( 1.0f ) );
+ wp.add( new COSFloat( 1.0f ) );
+ wp.add( new COSFloat( 1.0f ) );
+ dictionary.setItem( COSName.getPDFName( "WhitePoint" ), wp );
+ }
+ return new PDTristimulus( wp );
+ }
+
+ /**
+ * This will set the whitepoint tristimulus. As this is a required field
+ * this null should not be passed into this function.
+ *
+ * @param wp The whitepoint tristimulus.
+ */
+ public void setWhitepoint( PDTristimulus wp )
+ {
+ COSBase wpArray = wp.getCOSObject();
+ if( wpArray != null )
+ {
+ dictionary.setItem( COSName.getPDFName( "WhitePoint" ), wpArray );
+ }
+ }
+
+ /**
+ * This will return the BlackPoint tristimulus. This is an optional field but
+ * has defaults so this will never return null.
+ * A default of 0,0,0 will be returned if the pdf does not have any values yet.
+ *
+ * @return The blackpoint tristimulus.
+ */
+ public PDTristimulus getBlackPoint()
+ {
+ PDTristimulus retval = null;
+ COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "BlackPoint" ) );
+ if( bp == null )
+ {
+ bp.add( new COSFloat( 0.0f ) );
+ bp.add( new COSFloat( 0.0f ) );
+ bp.add( new COSFloat( 0.0f ) );
+ dictionary.setItem( COSName.getPDFName( "BlackPoint" ), bp );
+ }
+ return new PDTristimulus( bp );
+ }
+
+ /**
+ * This will set the BlackPoint tristimulus. As this is a required field
+ * this null should not be passed into this function.
+ *
+ * @param bp The BlackPoint tristimulus.
+ */
+ public void setBlackPoint( PDTristimulus bp )
+ {
+
+ COSBase bpArray = null;
+ if( bp != null )
+ {
+ bpArray = bp.getCOSObject();
+ }
+ dictionary.setItem( COSName.getPDFName( "BlackPoint" ), bpArray );
+ }
+
+ /**
+ * This will get the gamma value. If none is present then the default of 1,1,1
+ * will be returned.
+ *
+ * @return The gamma value.
+ */
+ public PDGamma getGamma()
+ {
+ COSArray gamma = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "Gamma" ) );
+ if( gamma == null )
+ {
+ gamma = new COSArray();
+ gamma.add( new COSFloat( 1.0f ) );
+ gamma.add( new COSFloat( 1.0f ) );
+ gamma.add( new COSFloat( 1.0f ) );
+ dictionary.setItem( COSName.getPDFName( "Gamma" ), gamma );
+ }
+ return new PDGamma( gamma );
+ }
+
+ /**
+ * Set the gamma value.
+ *
+ * @param value The new gamma value.
+ */
+ public void setGamma( PDGamma value )
+ {
+ COSArray gamma = null;
+ if( value != null )
+ {
+ gamma = (COSArray)value.getCOSArray();
+ }
+ dictionary.setItem( COSName.getPDFName( "Gamma" ), gamma );
+ }
+
+ /**
+ * This will get the linear interpretation array. This is guaranteed to not
+ * return null. If the underlying dictionary contains null then the identity
+ * matrix will be returned.
+ *
+ * @return The linear interpretation matrix.
+ */
+ public PDMatrix getLinearInterpretation()
+ {
+ PDMatrix retval = null;
+ COSArray matrix = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "Matrix" ) );
+ if( matrix == null )
+ {
+ retval = new PDMatrix();
+ setLinearInterpretation( retval );
+ }
+ else
+ {
+ retval = new PDMatrix( matrix );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the linear interpretation matrix. Passing in null will
+ * clear the matrix.
+ *
+ * @param matrix The new linear interpretation matrix.
+ */
+ public void setLinearInterpretation( PDMatrix matrix )
+ {
+ COSArray matrixArray = null;
+ if( matrix != null )
+ {
+ matrixArray = matrix.getCOSArray();
+ }
+ dictionary.setItem( COSName.getPDFName( "Matrix" ), matrixArray );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpace.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpace.java
new file mode 100644
index 0000000..33ac0a1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpace.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import java.io.IOException;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+
+/**
+ * This class represents a color space in a pdf document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public abstract class PDColorSpace implements COSObjectable
+{
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public abstract String getName();
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public abstract int getNumberOfComponents() throws IOException;
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return COSName.getPDFName( getName() );
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public abstract ColorSpace createColorSpace() throws IOException;
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public abstract ColorModel createColorModel( int bpc ) throws IOException;
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java
new file mode 100644
index 0000000..8c9aad5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * This class represents a color space in a pdf document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.10 $
+ */
+public final class PDColorSpaceFactory
+{
+ /**
+ * Private constructor for utility classes.
+ */
+ private PDColorSpaceFactory()
+ {
+ //utility class should not be implemented
+ }
+
+ /**
+ * This will create the correct color space given the name.
+ *
+ * @param colorSpace The color space object.
+ *
+ * @return The color space.
+ *
+ * @throws IOException If the color space name is unknown.
+ */
+ public static PDColorSpace createColorSpace( COSBase colorSpace ) throws IOException
+ {
+ PDColorSpace retval = null;
+ if( colorSpace instanceof COSName )
+ {
+ retval = createColorSpace( ((COSName)colorSpace).getName() );
+ }
+ else if( colorSpace instanceof COSArray )
+ {
+ COSArray array = (COSArray)colorSpace;
+ COSName type = (COSName)array.getObject( 0 );
+ if( type.getName().equals( PDCalGray.NAME ) )
+ {
+ retval = new PDCalGray( array );
+ }
+ else if( type.getName().equals( PDCalRGB.NAME ) )
+ {
+ retval = new PDCalRGB( array );
+ }
+ else if( type.getName().equals( PDDeviceN.NAME ) )
+ {
+ retval = new PDDeviceN( array );
+ }
+ else if( type.getName().equals( PDIndexed.NAME ) ||
+ type.getName().equals( PDIndexed.ABBREVIATED_NAME ))
+ {
+ retval = new PDIndexed( array );
+ }
+ else if( type.getName().equals( PDLab.NAME ) )
+ {
+ retval = new PDLab( array );
+ }
+ else if( type.getName().equals( PDSeparation.NAME ) )
+ {
+ retval = new PDSeparation( array );
+ }
+ else if( type.getName().equals( PDICCBased.NAME ) )
+ {
+ retval = new PDICCBased( array );
+ }
+ else if( type.getName().equals( PDPattern.NAME ) )
+ {
+ retval = new PDPattern( array );
+ }
+ else
+ {
+ throw new IOException( "Unknown colorspace array type:" + type );
+ }
+ }
+ else
+ {
+ throw new IOException( "Unknown colorspace type:" + colorSpace );
+ }
+ return retval;
+ }
+
+ /**
+ * This will create the correct color space given the name.
+ *
+ * @param colorSpaceName The name of the colorspace.
+ *
+ * @return The color space.
+ *
+ * @throws IOException If the color space name is unknown.
+ */
+ public static PDColorSpace createColorSpace( String colorSpaceName ) throws IOException
+ {
+ PDColorSpace cs = null;
+ if( colorSpaceName.equals( PDDeviceCMYK.NAME ) ||
+ colorSpaceName.equals( PDDeviceCMYK.ABBREVIATED_NAME ) )
+ {
+ cs = PDDeviceCMYK.INSTANCE;
+ }
+ else if( colorSpaceName.equals( PDDeviceRGB.NAME ) ||
+ colorSpaceName.equals( PDDeviceRGB.ABBREVIATED_NAME ) )
+ {
+ cs = PDDeviceRGB.INSTANCE;
+ }
+ else if( colorSpaceName.equals( PDDeviceGray.NAME ) ||
+ colorSpaceName.equals( PDDeviceGray.ABBREVIATED_NAME ))
+ {
+ cs = new PDDeviceGray();
+ }
+ else if( colorSpaceName.equals( PDLab.NAME ) )
+ {
+ cs = new PDLab();
+ }
+ else if( colorSpaceName.equals( PDPattern.NAME ) )
+ {
+ cs = new PDPattern();
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown colorspace '" + colorSpaceName + "'" );
+ }
+ return cs;
+ }
+
+ /**
+ * This will create the correct color space from a java colorspace.
+ *
+ * @param doc The doc to potentiall write information to.
+ * @param cs The awt colorspace.
+ *
+ * @return The color space.
+ *
+ * @throws IOException If the color space name is unknown.
+ */
+ public static PDColorSpace createColorSpace( PDDocument doc, ColorSpace cs ) throws IOException
+ {
+ PDColorSpace retval = null;
+ if( cs.isCS_sRGB() )
+ {
+ retval = PDDeviceRGB.INSTANCE;
+ }
+ else if( cs instanceof ICC_ColorSpace )
+ {
+ ICC_ColorSpace ics = (ICC_ColorSpace)cs;
+ PDICCBased pdCS = new PDICCBased( doc );
+ retval = pdCS;
+ COSArray ranges = new COSArray();
+ for( int i=0; i<cs.getNumComponents(); i++ )
+ {
+ ranges.add( new COSFloat( ics.getMinValue( i ) ) );
+ ranges.add( new COSFloat( ics.getMaxValue( i ) ) );
+ }
+ PDStream iccData = pdCS.getPDStream();
+ OutputStream output = null;
+ try
+ {
+ output = iccData.createOutputStream();
+ output.write( ics.getProfile().getData() );
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.close();
+ }
+ }
+ pdCS.setNumberOfComponents( cs.getNumComponents() );
+ }
+ else
+ {
+ throw new IOException( "Not yet implemented:" + cs );
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceInstance.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceInstance.java
new file mode 100644
index 0000000..23693df
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDColorSpaceInstance.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+
+/**
+ * This class represents a color space and the color value for that colorspace.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDColorSpaceInstance
+{
+ private PDColorSpace colorSpace = new PDDeviceGray();
+ private COSArray colorSpaceValue = new COSArray();
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDColorSpaceInstance()
+ {
+ //do nothing
+ }
+
+ /**
+ * Create the current color from the colorspace and values.
+ * @return The current awt color.
+ * @throws IOException If there is an error creating the color.
+ */
+ public Color createColor() throws IOException
+ {
+ float[] components = colorSpaceValue.toFloatArray();
+ Color color = new Color( colorSpace.createColorSpace(), components, 1f );
+ return color;
+ }
+
+ /**
+ * Constructor with an existing color set. Default colorspace is PDDeviceGray.
+ *
+ * @param csValues The color space values.
+ */
+ public PDColorSpaceInstance( COSArray csValues )
+ {
+ colorSpaceValue = csValues;
+ }
+
+
+ /**
+ * This will get the current colorspace.
+ *
+ * @return The current colorspace.
+ */
+ public PDColorSpace getColorSpace()
+ {
+ return colorSpace;
+ }
+
+ /**
+ * This will set the current colorspace.
+ *
+ * @param value The new colorspace.
+ */
+ public void setColorSpace(PDColorSpace value)
+ {
+ colorSpace = value;
+ }
+
+ /**
+ * This will get the color space values. Either 1 for gray or 3 for RGB.
+ *
+ * @return The colorspace values.
+ */
+ public float[] getColorSpaceValue()
+ {
+ return colorSpaceValue.toFloatArray();
+ }
+
+ /**
+ * This will get the color space values. Either 1 for gray or 3 for RGB.
+ *
+ * @return The colorspace values.
+ */
+ public COSArray getCOSColorSpaceValue()
+ {
+ return colorSpaceValue;
+ }
+
+ /**
+ * This will update the colorspace values.
+ *
+ * @param value The new colorspace values.
+ */
+ public void setColorSpaceValue(float[] value)
+ {
+ colorSpaceValue.setFloatArray( value );
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java
new file mode 100644
index 0000000..dfbdfe5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.ColorModel;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.pdfbox.util.ResourceLoader;
+
+/**
+ * This class represents a CMYK color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDDeviceCMYK extends PDColorSpace
+{
+ /**
+ * The single instance of this class.
+ */
+ public static final PDDeviceCMYK INSTANCE = new PDDeviceCMYK();
+
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "DeviceCMYK";
+
+ /**
+ * The abbreviated name of this color space.
+ */
+ public static final String ABBREVIATED_NAME = "CMYK";
+
+ private ColorSpace cSpace = null;
+
+ private PDDeviceCMYK()
+ {
+
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return 4;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ if( cSpace == null )
+ {
+ InputStream profile = null;
+ try
+ {
+ profile = ResourceLoader.loadResource( "Resources/colorspace-profiles/CMYK.pf" );
+ ICC_Profile iccProfile = ICC_Profile.getInstance( profile );
+ cSpace = new ICC_ColorSpace( iccProfile );
+ }
+ finally
+ {
+ if( profile != null )
+ {
+ profile.close();
+ }
+ }
+ }
+ return cSpace;
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceGray.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceGray.java
new file mode 100644
index 0000000..a9afddb
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceGray.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.color.ColorSpace;
+
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.Transparency;
+
+import java.io.IOException;
+
+/**
+ * This class represents a Gray color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDDeviceGray extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "DeviceGray";
+
+ /**
+ * The abbreviated name of this color space.
+ */
+ public static final String ABBREVIATED_NAME = "G";
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return 1;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ return ColorSpace.getInstance( ColorSpace.CS_GRAY );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+ int[] nBits = {bpc};
+ ColorModel colorModel = new ComponentColorModel(cs, nBits, false,false,
+ Transparency.OPAQUE,DataBuffer.TYPE_BYTE);
+ return colorModel;
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceN.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceN.java
new file mode 100644
index 0000000..92561e7
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceN.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+import java.io.IOException;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+
+/**
+ * This class represents a DeviceN color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDDeviceN extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "DeviceN";
+
+ private COSArray array;
+
+ /**
+ * Constructor.
+ */
+ public PDDeviceN()
+ {
+ array = new COSArray();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( COSName.getPDFName( "" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param separation The array containing all separation information.
+ */
+ public PDDeviceN( COSArray separation )
+ {
+ array = separation;
+ }
+
+ /**
+ * This will return the name of the color space. For a PDSeparation object
+ * this will always return "Separation"
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return getColorantNames().size();
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * This will get the colorant names. A list of string objects.
+ *
+ * @return A list of colorants
+ */
+ public List getColorantNames()
+ {
+ COSArray names = (COSArray)array.getObject( 1 );
+ return COSArrayList.convertCOSNameCOSArrayToList( names );
+ }
+
+ /**
+ * This will set the list of colorants.
+ *
+ * @param names The list of colorant names.
+ */
+ public void setColorantNames( List names )
+ {
+ COSArray namesArray = COSArrayList.convertStringListToCOSNameCOSArray( names );
+ array.set( 1, namesArray );
+ }
+
+ /**
+ * This will get the alternate color space for this separation.
+ *
+ * @return The alternate color space.
+ *
+ * @throws IOException If there is an error getting the alternate color space.
+ */
+ public PDColorSpace getAlternateColorSpace() throws IOException
+ {
+ COSBase alternate = array.getObject( 2 );
+ return PDColorSpaceFactory.createColorSpace( alternate );
+ }
+
+ /**
+ * This will set the alternate color space.
+ *
+ * @param cs The alternate color space.
+ */
+ public void setAlternateColorSpace( PDColorSpace cs )
+ {
+ COSBase space = null;
+ if( cs != null )
+ {
+ space = cs.getCOSObject();
+ }
+ array.set( 2, space );
+ }
+
+ /**
+ * This will get the tint transform function. At this time the PDModel
+ * does not support functions so we will just return the COSBase object. This
+ * method will change in the future to be a PDModel object.
+ *
+ * @return The tint transform function.
+ */
+ public COSBase getTintTransform()
+ {
+ return array.get( 3 );
+ }
+
+ /**
+ * This will set the tint transform function. At this time the PDModel
+ * does not support functions so we will just return the COSBase object. This
+ * method will change in the future to be a PDModel object.
+ *
+ * @param tint The tint transform function.
+ */
+ public void setTintTransform( COSBase tint )
+ {
+ array.set( 3, tint );
+ }
+
+ /**
+ * This will get the attributes that are associated with the deviceN
+ * color space.
+ *
+ * @return The DeviceN attributes.
+ */
+ public PDDeviceNAttributes getAttributes()
+ {
+ PDDeviceNAttributes retval = null;
+ if( array.size() <5)
+ {
+ retval = new PDDeviceNAttributes();
+ setAttributes( retval );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the color space attributes. If null is passed in then
+ * all attribute will be removed.
+ *
+ * @param attributes The color space attributes.
+ */
+ public void setAttributes( PDDeviceNAttributes attributes )
+ {
+ if( attributes == null )
+ {
+ array.remove( 4 );
+ }
+ else
+ {
+ //make sure array is large enough
+ while( array.size() < 5 )
+ {
+ array.add( COSNull.NULL );
+ }
+ array.set( 4, attributes.getCOSDictionary() );
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceNAttributes.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceNAttributes.java
new file mode 100644
index 0000000..00366df
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceNAttributes.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSDictionaryMap;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This class represents attributes for a DeviceN color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDDeviceNAttributes
+{
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ */
+ public PDDeviceNAttributes()
+ {
+ dictionary = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param attributes A dictionary that has all of the attributes.
+ */
+ public PDDeviceNAttributes( COSDictionary attributes )
+ {
+ dictionary = attributes;
+ }
+
+ /**
+ * This will get the underlying cos dictionary.
+ *
+ * @return The dictionary that this object wraps.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return dictionary;
+ }
+
+ /**
+ * This will get a map of colorants. See the PDF Reference for more details about
+ * this attribute. The map will contain a java.lang.String as the key, a colorant name,
+ * and a PDColorSpace as the value.
+ *
+ * @return The colorant map.
+ *
+ * @throws IOException If there is an error getting the colorspaces.
+ */
+ public Map getColorants() throws IOException
+ {
+ Map actuals = new HashMap();
+ COSDictionary colorants = (COSDictionary)dictionary.getDictionaryObject( COSName.getPDFName( "Colorants" ) );
+ if( colorants == null )
+ {
+ colorants = new COSDictionary();
+ dictionary.setItem( COSName.getPDFName( "Colorants" ), colorants );
+ }
+ Iterator iter = colorants.keyList().iterator();
+ while( iter.hasNext() )
+ {
+ COSName name = (COSName)iter.next();
+ COSBase value = colorants.getDictionaryObject( name );
+ actuals.put( name.getName(), PDColorSpaceFactory.createColorSpace( value ) );
+ }
+ return new COSDictionaryMap( actuals, colorants );
+ }
+
+ /**
+ * This will replace the existing colorant attribute. The key should be strings
+ * and the values should be PDColorSpaces.
+ *
+ * @param colorants The map of colorants.
+ */
+ public void setColorants( Map colorants )
+ {
+ COSDictionary colorantDict = null;
+ if( colorants != null )
+ {
+ colorantDict = COSDictionaryMap.convert( colorants );
+ }
+ dictionary.setItem( COSName.getPDFName( "Colorants" ), colorantDict );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java
new file mode 100644
index 0000000..14c0dc3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.Transparency;
+
+import java.awt.color.ColorSpace;
+
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+
+import java.io.IOException;
+
+/**
+ * This class represents an RGB color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.7 $
+ */
+public class PDDeviceRGB extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "DeviceRGB";
+
+ /**
+ * The abbreviated name of this color space.
+ */
+ public static final String ABBREVIATED_NAME = "RGB";
+
+ /**
+ * This is the single instance of this class.
+ */
+ public static final PDDeviceRGB INSTANCE = new PDDeviceRGB();
+
+ /**
+ * This class is immutable.
+ */
+ private PDDeviceRGB()
+ {
+ //only here to make immutable.
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return 3;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ return ColorSpace.getInstance( ColorSpace.CS_sRGB );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ int[] nbBits = { bpc, bpc, bpc };
+ ComponentColorModel componentColorModel =
+ new ComponentColorModel( createColorSpace(),
+ nbBits,
+ false,
+ false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE );
+
+ return (ColorModel)componentColorModel;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDGamma.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDGamma.java
new file mode 100644
index 0000000..31fa82e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDGamma.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * A gamma array, or collection of three floating point parameters used for
+ * color operations.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDGamma implements COSObjectable
+{
+ private COSArray values = null;
+
+ /**
+ * Constructor. Defaults all values to 0, 0, 0.
+ */
+ public PDGamma()
+ {
+ values = new COSArray();
+ values.add( new COSFloat( 0.0f ) );
+ values.add( new COSFloat( 0.0f ) );
+ values.add( new COSFloat( 0.0f ) );
+ }
+
+ /**
+ * Constructor from COS object.
+ *
+ * @param array The array containing the XYZ values.
+ */
+ public PDGamma( COSArray array )
+ {
+ values = array;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return values;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSArray getCOSArray()
+ {
+ return values;
+ }
+
+ /**
+ * This will get the r value of the tristimulus.
+ *
+ * @return The R value.
+ */
+ public float getR()
+ {
+ return ((COSNumber)values.get( 0 )).floatValue();
+ }
+
+ /**
+ * This will set the r value of the tristimulus.
+ *
+ * @param r The r value for the tristimulus.
+ */
+ public void setR( float r )
+ {
+ values.set( 0, new COSFloat( r ) );
+ }
+
+ /**
+ * This will get the g value of the tristimulus.
+ *
+ * @return The g value.
+ */
+ public float getG()
+ {
+ return ((COSNumber)values.get( 1 )).floatValue();
+ }
+
+ /**
+ * This will set the g value of the tristimulus.
+ *
+ * @param g The g value for the tristimulus.
+ */
+ public void setG( float g )
+ {
+ values.set( 1, new COSFloat( g ) );
+ }
+
+ /**
+ * This will get the b value of the tristimulus.
+ *
+ * @return The B value.
+ */
+ public float getB()
+ {
+ return ((COSNumber)values.get( 2 )).floatValue();
+ }
+
+ /**
+ * This will set the b value of the tristimulus.
+ *
+ * @param b The b value for the tristimulus.
+ */
+ public void setB( float b )
+ {
+ values.set( 2, new COSFloat( b ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDICCBased.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDICCBased.java
new file mode 100644
index 0000000..d8189d0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDICCBased.java
@@ -0,0 +1,343 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.PDRange;
+import org.pdfbox.pdmodel.common.PDStream;
+
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a ICC profile color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDICCBased extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "ICCBased";
+
+ private COSArray array;
+ private PDStream stream;
+
+ /**
+ * Default constructor, creates empty stream.
+ *
+ * @param doc The document to store the icc data.
+ */
+ public PDICCBased( PDDocument doc )
+ {
+ array = new COSArray();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( new PDStream( doc ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param iccArray The ICC stream object.
+ */
+ public PDICCBased( COSArray iccArray )
+ {
+ array = iccArray;
+ stream = new PDStream( (COSStream)iccArray.getObject( 1 ) );
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return array;
+ }
+
+ /**
+ * Get the pd stream for this icc color space.
+ *
+ * @return Get the stream for this icc based color space.
+ */
+ public PDStream getPDStream()
+ {
+ return stream;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ InputStream profile = null;
+ ColorSpace cSpace = null;
+ try
+ {
+ profile = stream.createInputStream();
+ ICC_Profile iccProfile = ICC_Profile.getInstance( profile );
+ cSpace = new ICC_ColorSpace( iccProfile );
+ }
+ finally
+ {
+ if( profile != null )
+ {
+ profile.close();
+ }
+ }
+ return cSpace;
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ int[] nbBits = { bpc, bpc, bpc };
+ ComponentColorModel componentColorModel =
+ new ComponentColorModel( createColorSpace(),
+ nbBits,
+ false,
+ false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE );
+
+ return componentColorModel;
+ }
+
+ /**
+ * This will return the number of color components. As of PDF 1.4 this will
+ * be 1,3,4.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ COSNumber n = (COSNumber)stream.getStream().getDictionaryObject( COSName.getPDFName( "N" ) );
+ return n.intValue();
+ }
+
+ /**
+ * This will set the number of color components.
+ *
+ * @param n The number of color components.
+ */
+ public void setNumberOfComponents( int n )
+ {
+ stream.getStream().setItem( COSName.getPDFName( "N" ), new COSInteger( n ) );
+ }
+
+ /**
+ * This will return a list of alternate color spaces(PDColorSpace) if the display application
+ * does not support this icc stream.
+ *
+ * @return A list of alternate color spaces.
+ *
+ * @throws IOException If there is an error getting the alternate color spaces.
+ */
+ public List getAlternateColorSpaces() throws IOException
+ {
+ COSBase alternate = stream.getStream().getDictionaryObject( COSName.getPDFName( "Alternate" ) );
+ COSArray alternateArray = null;
+ if( alternate == null )
+ {
+ alternateArray = new COSArray();
+ int numComponents = getNumberOfComponents();
+ String csName = null;
+ if( numComponents == 1 )
+ {
+ csName = PDDeviceGray.NAME;
+ }
+ else if( numComponents == 3 )
+ {
+ csName = PDDeviceRGB.NAME;
+ }
+ else if( numComponents == 4 )
+ {
+ csName = PDDeviceCMYK.NAME;
+ }
+ else
+ {
+ throw new IOException( "Unknown colorspace number of components:" + numComponents );
+ }
+ alternateArray.add( COSName.getPDFName( csName ) );
+ }
+ else
+ {
+ if( alternate instanceof COSArray )
+ {
+ alternateArray = (COSArray)alternate;
+ }
+ else if( alternate instanceof COSName )
+ {
+ alternateArray = new COSArray();
+ alternateArray.add( alternate );
+ }
+ else
+ {
+ throw new IOException( "Error: expected COSArray or COSName and not " +
+ alternate.getClass().getName() );
+ }
+ }
+ List retval = new ArrayList();
+ for( int i=0; i<alternateArray.size(); i++ )
+ {
+ retval.add( PDColorSpaceFactory.createColorSpace( alternateArray.get( i ) ) );
+ }
+ return new COSArrayList( retval, alternateArray );
+ }
+
+ /**
+ * This will set the list of alternate color spaces. This should be a list
+ * of PDColorSpace objects.
+ *
+ * @param list The list of colorspace objects.
+ */
+ public void setAlternateColorSpaces( List list )
+ {
+ COSArray altArray = null;
+ if( list != null )
+ {
+ altArray = COSArrayList.converterToCOSArray( list );
+ }
+ stream.getStream().setItem( COSName.getPDFName( "Alternate" ), altArray );
+ }
+
+ private COSArray getRangeArray( int n )
+ {
+ COSArray rangeArray = (COSArray)stream.getStream().getDictionaryObject( COSName.getPDFName( "Range" ) );
+ if( rangeArray == null )
+ {
+ rangeArray = new COSArray();
+ stream.getStream().setItem( COSName.getPDFName( "Range" ), rangeArray );
+ while( rangeArray.size() < n*2 )
+ {
+ rangeArray.add( new COSFloat( -100 ) );
+ rangeArray.add( new COSFloat( 100 ) );
+ }
+ }
+ return rangeArray;
+ }
+
+ /**
+ * This will get the range for a certain component number. This is will never
+ * return null. If it is not present then the range -100 to 100 will
+ * be returned.
+ *
+ * @param n The component number to get the range for.
+ *
+ * @return The range for this component.
+ */
+ public PDRange getRangeForComponent( int n )
+ {
+ COSArray rangeArray = getRangeArray( n );
+ return new PDRange( rangeArray, n );
+ }
+
+ /**
+ * This will set the a range for this color space.
+ *
+ * @param range The new range for the a component.
+ * @param n The component to set the range for.
+ */
+ public void setRangeForComponent( PDRange range, int n )
+ {
+ COSArray rangeArray = getRangeArray( n );
+ rangeArray.set( n*2, new COSFloat( range.getMin() ) );
+ rangeArray.set( n*2+1, new COSFloat( range.getMax() ) );
+ }
+
+ /**
+ * This will get the metadata stream for this object. Null if there is no
+ * metadata stream.
+ *
+ * @return The metadata stream, if it exists.
+ */
+ public COSStream getMetadata()
+ {
+ return (COSStream)stream.getStream().getDictionaryObject( COSName.getPDFName( "Metadata" ) );
+ }
+
+ /**
+ * This will set the metadata stream that is associated with this color space.
+ *
+ * @param metadata The new metadata stream.
+ */
+ public void setMetadata( COSStream metadata )
+ {
+ stream.getStream().setItem( COSName.getPDFName( "Metadata" ), metadata );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDIndexed.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDIndexed.java
new file mode 100644
index 0000000..cd1d9e7
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDIndexed.java
@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+
+import java.io.InputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * This class represents an Indexed color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDIndexed extends PDColorSpace
+{
+
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "Indexed";
+
+ /**
+ * The abbreviated name of this color space.
+ */
+ public static final String ABBREVIATED_NAME = "I";
+
+ private COSArray array;
+
+ /**
+ * Constructor, default DeviceRGB, hival 255.
+ */
+ public PDIndexed()
+ {
+ array = new COSArray();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( COSName.getPDFName( PDDeviceRGB.NAME ) );
+ array.add( new COSInteger( 255 ) );
+ array.add( org.pdfbox.cos.COSNull.NULL );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param indexedArray The array containing the indexed parameters
+ */
+ public PDIndexed( COSArray indexedArray )
+ {
+ array = indexedArray;
+ }
+
+ /**
+ * This will return the number of color components. This will return the
+ * number of color components in the base color.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return getBaseColorSpace().getNumberOfComponents();
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ int size = getHighValue();
+ byte[] index = getLookupData();
+ //for (int i=0;i<index.length;i++) System.out.print(index[i]+" ");
+
+ ColorModel cm = new IndexColorModel(bpc, size+1, index,0,false);
+ return cm;
+ }
+
+ /**
+ * This will get the color space that acts as the index for this color space.
+ *
+ * @return The base color space.
+ *
+ * @throws IOException If there is error creating the base color space.
+ */
+ public PDColorSpace getBaseColorSpace() throws IOException
+ {
+ PDColorSpace retval = null;
+ COSBase base = array.getObject( 1 );
+ if( base instanceof COSName )
+ {
+ retval = PDColorSpaceFactory.createColorSpace( (COSName)base );
+ }
+ else
+ {
+ throw new IOException( "Error:unknown base colorspace" );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the base color space.
+ *
+ * @param base The base color space to use as the index.
+ */
+ public void setBaseColorSpace( PDColorSpace base )
+ {
+ array.set( 1, base.getCOSObject() );
+ }
+
+ /**
+ * Get the highest value for the lookup.
+ *
+ * @return The hival entry.
+ */
+ public int getHighValue()
+ {
+ return ((COSNumber)array.getObject( 2 )).intValue();
+ }
+
+ /**
+ * This will set the highest value that is allowed. This cannot be higher
+ * than 255.
+ *
+ * @param high The highest value for the lookup table.
+ */
+ public void setHighValue( int high )
+ {
+ array.set( 2, new COSInteger( high ) );
+ }
+
+ /**
+ * This will perform a lookup into the color lookup table.
+ *
+ * @param componentNumber The component number, probably 1,2,3,3.
+ * @param lookupIndex The zero-based index into the table, should not exceed the high value.
+ *
+ * @return The value that was from the lookup table.
+ *
+ * @throws IOException If there is an error looking up the color.
+ */
+ public int lookupColor( int componentNumber, int lookupIndex ) throws IOException
+ {
+ COSBase lookupTable = array.getObject( 3 );
+ PDColorSpace baseColor = getBaseColorSpace();
+ byte[] data = getLookupData();
+ int numberOfComponents = baseColor.getNumberOfComponents();
+ return (data[componentNumber*numberOfComponents + lookupIndex]+256)%256;
+ }
+
+ private byte[] getLookupData() throws IOException
+ {
+ COSBase lookupTable = array.getObject( 3 );
+ byte[] data = null;
+ if( lookupTable instanceof COSString )
+ {
+ data = ((COSString)lookupTable).getBytes();
+ }
+ else if( lookupTable instanceof COSStream )
+ {
+ //Data will be small so just load the whole thing into memory for
+ //easier processing
+ COSStream lookupStream = (COSStream)lookupTable;
+ InputStream input = lookupStream.getUnfilteredStream();
+ ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
+ byte[] buffer = new byte[ 1024 ];
+ int amountRead;
+ while( (amountRead = input.read(buffer, 0, buffer.length)) != -1 )
+ {
+ output.write( buffer, 0, amountRead );
+ }
+ data = output.toByteArray();
+ }
+ else if( lookupTable == null )
+ {
+ data = new byte[0];
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown type for lookup table " + lookupTable );
+ }
+ return data;
+ }
+
+ /**
+ * This will set a color in the color lookup table.
+ *
+ * @param componentNumber The component number, probably 1,2,3,3.
+ * @param lookupIndex The zero-based index into the table, should not exceed the high value.
+ * @param color The color that will go into the table.
+ *
+ * @throws IOException If there is an error looking up the color.
+ */
+ public void setLookupColor( int componentNumber, int lookupIndex, int color ) throws IOException
+ {
+ PDColorSpace baseColor = getBaseColorSpace();
+ int numberOfComponents = baseColor.getNumberOfComponents();
+ byte[] data = getLookupData();
+ data[componentNumber*numberOfComponents + lookupIndex] = (byte)color;
+ COSString string = new COSString( data );
+ array.set( 3, string );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDLab.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDLab.java
new file mode 100644
index 0000000..9bd3220
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDLab.java
@@ -0,0 +1,300 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.PDRange;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+import java.io.IOException;
+
+/**
+ * This class represents a Lab color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDLab extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "Lab";
+
+ private COSArray array;
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ */
+ public PDLab()
+ {
+ array = new COSArray();
+ dictionary = new COSDictionary();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( dictionary );
+ }
+
+ /**
+ * Constructor with array.
+ *
+ * @param lab The underlying color space.
+ */
+ public PDLab( COSArray lab )
+ {
+ array = lab;
+ dictionary = (COSDictionary)array.getObject( 1 );
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return array;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ //BJL
+ //hmm is this correct, I am not 100% sure.
+ return 3;
+ }
+
+ /**
+ * This will return the whitepoint tristimulus. As this is a required field
+ * this will never return null. A default of 1,1,1 will be returned if the
+ * pdf does not have any values yet.
+ *
+ * @return The whitepoint tristimulus.
+ */
+ public PDTristimulus getWhitepoint()
+ {
+ PDTristimulus retval = null;
+ COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "WhitePoint" ) );
+ if( wp == null )
+ {
+ wp.add( new COSFloat( 1.0f ) );
+ wp.add( new COSFloat( 1.0f ) );
+ wp.add( new COSFloat( 1.0f ) );
+ dictionary.setItem( COSName.getPDFName( "WhitePoint" ), wp );
+ }
+ return new PDTristimulus( wp );
+ }
+
+ /**
+ * This will set the whitepoint tristimulus. As this is a required field
+ * this null should not be passed into this function.
+ *
+ * @param wp The whitepoint tristimulus.
+ */
+ public void setWhitepoint( PDTristimulus wp )
+ {
+ COSBase wpArray = wp.getCOSObject();
+ if( wpArray != null )
+ {
+ dictionary.setItem( COSName.getPDFName( "WhitePoint" ), wpArray );
+ }
+ }
+
+ /**
+ * This will return the BlackPoint tristimulus. This is an optional field but
+ * has defaults so this will never return null.
+ * A default of 0,0,0 will be returned if the pdf does not have any values yet.
+ *
+ * @return The blackpoint tristimulus.
+ */
+ public PDTristimulus getBlackPoint()
+ {
+ PDTristimulus retval = null;
+ COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "BlackPoint" ) );
+ if( bp == null )
+ {
+ bp.add( new COSFloat( 0.0f ) );
+ bp.add( new COSFloat( 0.0f ) );
+ bp.add( new COSFloat( 0.0f ) );
+ dictionary.setItem( COSName.getPDFName( "BlackPoint" ), bp );
+ }
+ return new PDTristimulus( bp );
+ }
+
+ /**
+ * This will set the BlackPoint tristimulus. As this is a required field
+ * this null should not be passed into this function.
+ *
+ * @param bp The BlackPoint tristimulus.
+ */
+ public void setBlackPoint( PDTristimulus bp )
+ {
+
+ COSBase bpArray = null;
+ if( bp != null )
+ {
+ bpArray = bp.getCOSObject();
+ }
+ dictionary.setItem( COSName.getPDFName( "BlackPoint" ), bpArray );
+ }
+
+ private COSArray getRangeArray()
+ {
+ COSArray range = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "Range" ) );
+ if( range == null )
+ {
+ range = new COSArray();
+ dictionary.setItem( COSName.getPDFName( "Range" ), array );
+ range.add( new COSFloat( -100 ) );
+ range.add( new COSFloat( 100 ) );
+ range.add( new COSFloat( -100 ) );
+ range.add( new COSFloat( 100 ) );
+ }
+ return range;
+ }
+
+ /**
+ * This will get the valid range for the a component. If none is found
+ * then the default will be returned, which is -100 to 100.
+ *
+ * @return The a range.
+ */
+ public PDRange getARange()
+ {
+ COSArray range = getRangeArray();
+ return new PDRange( range, 0 );
+ }
+
+ /**
+ * This will set the a range for this color space.
+ *
+ * @param range The new range for the a component.
+ */
+ public void setARange( PDRange range )
+ {
+ COSArray rangeArray = null;
+ //if null then reset to defaults
+ if( range == null )
+ {
+ rangeArray = getRangeArray();
+ rangeArray.set( 0, new COSFloat( -100 ) );
+ rangeArray.set( 1, new COSFloat( 100 ) );
+ }
+ else
+ {
+ rangeArray = range.getCOSArray();
+ }
+ dictionary.setItem( COSName.getPDFName( "Range" ), rangeArray );
+ }
+
+ /**
+ * This will get the valid range for the b component. If none is found
+ * then the default will be returned, which is -100 to 100.
+ *
+ * @return The b range.
+ */
+ public PDRange getBRange()
+ {
+ COSArray range = getRangeArray();
+ return new PDRange( range, 2 );
+ }
+
+ /**
+ * This will set the b range for this color space.
+ *
+ * @param range The new range for the b component.
+ */
+ public void setBRange( PDRange range )
+ {
+ COSArray rangeArray = null;
+ //if null then reset to defaults
+ if( range == null )
+ {
+ rangeArray = getRangeArray();
+ rangeArray.set( 2, new COSFloat( -100 ) );
+ rangeArray.set( 3, new COSFloat( 100 ) );
+ }
+ else
+ {
+ rangeArray = range.getCOSArray();
+ }
+ dictionary.setItem( COSName.getPDFName( "Range" ), rangeArray );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDPattern.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDPattern.java
new file mode 100644
index 0000000..4c43914
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDPattern.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSName;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+import java.io.IOException;
+
+/**
+ * This class represents a Pattern color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDPattern extends PDColorSpace
+{
+ private COSArray array;
+
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "Pattern";
+
+ /**
+ * Default constructor.
+ */
+ public PDPattern()
+ {
+ array = new COSArray();
+ array.add( COSName.getPDFName( NAME ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param pattern The pattern array.
+ */
+ public PDPattern( COSArray pattern)
+ {
+ array = pattern;
+ }
+
+ /**
+ * This will return the name of the color space.
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return -1;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDSeparation.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDSeparation.java
new file mode 100644
index 0000000..cc92f6f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDSeparation.java
@@ -0,0 +1,198 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This class represents a Separation color space.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDSeparation extends PDColorSpace
+{
+ /**
+ * The name of this color space.
+ */
+ public static final String NAME = "Separation";
+
+ private COSArray array;
+
+ /**
+ * Constructor.
+ */
+ public PDSeparation()
+ {
+ array = new COSArray();
+ array.add( COSName.getPDFName( NAME ) );
+ array.add( COSName.getPDFName( "" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param separation The array containing all separation information.
+ */
+ public PDSeparation( COSArray separation )
+ {
+ array = separation;
+ }
+
+ /**
+ * This will return the name of the color space. For a PDSeparation object
+ * this will always return "Separation"
+ *
+ * @return The name of the color space.
+ */
+ public String getName()
+ {
+ return NAME;
+ }
+
+ /**
+ * This will get the number of components that this color space is made up of.
+ *
+ * @return The number of components in this color space.
+ *
+ * @throws IOException If there is an error getting the number of color components.
+ */
+ public int getNumberOfComponents() throws IOException
+ {
+ return 1;
+ }
+
+ /**
+ * Create a Java colorspace for this colorspace.
+ *
+ * @return A color space that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color space.
+ */
+ public ColorSpace createColorSpace() throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * Create a Java color model for this colorspace.
+ *
+ * @param bpc The number of bits per component.
+ *
+ * @return A color model that can be used for Java AWT operations.
+ *
+ * @throws IOException If there is an error creating the color model.
+ */
+ public ColorModel createColorModel( int bpc ) throws IOException
+ {
+ throw new IOException( "Not implemented" );
+ }
+
+ /**
+ * This will get the separation name.
+ *
+ * @return The name in the separation.
+ */
+ public String getColorantName()
+ {
+ COSName name = (COSName)array.getObject( 1 );
+ return name.getName();
+ }
+
+ /**
+ * This will set the separation name.
+ *
+ * @param name The separation name.
+ */
+ public void setColorantName( String name )
+ {
+ array.set( 1, COSName.getPDFName( name ) );
+ }
+
+ /**
+ * This will get the alternate color space for this separation.
+ *
+ * @return The alternate color space.
+ *
+ * @throws IOException If there is an error getting the alternate color space.
+ */
+ public PDColorSpace getAlternateColorSpace() throws IOException
+ {
+ COSBase alternate = array.getObject( 2 );
+ return PDColorSpaceFactory.createColorSpace( alternate );
+ }
+
+ /**
+ * This will set the alternate color space.
+ *
+ * @param cs The alternate color space.
+ */
+ public void setAlternateColorSpace( PDColorSpace cs )
+ {
+ COSBase space = null;
+ if( cs != null )
+ {
+ space = cs.getCOSObject();
+ }
+ array.set( 2, space );
+ }
+
+ /**
+ * This will get the tint transform function. At this time the PDModel
+ * does not support functions so we will just return the COSBase object. This
+ * method will change in the future to be a PDModel object.
+ *
+ * @return The tint transform function.
+ */
+ public COSBase getTintTransform()
+ {
+ return array.get( 3 );
+ }
+
+ /**
+ * This will set the tint transform function. At this time the PDModel
+ * does not support functions so we will just return the COSBase object. This
+ * method will change in the future to be a PDModel object.
+ *
+ * @param tint The tint transform function.
+ */
+ public void setTintTransform( COSBase tint )
+ {
+ array.set( 3, tint );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/PDTristimulus.java b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDTristimulus.java
new file mode 100644
index 0000000..296b147
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/PDTristimulus.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.color;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * A tristimulus, or collection of three floating point parameters used for
+ * color operations.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDTristimulus implements COSObjectable
+{
+ private COSArray values = null;
+
+ /**
+ * Constructor. Defaults all values to 0, 0, 0.
+ */
+ public PDTristimulus()
+ {
+ values = new COSArray();
+ values.add( new COSFloat( 0.0f ) );
+ values.add( new COSFloat( 0.0f ) );
+ values.add( new COSFloat( 0.0f ) );
+ }
+
+ /**
+ * Constructor from COS object.
+ *
+ * @param array The array containing the XYZ values.
+ */
+ public PDTristimulus( COSArray array )
+ {
+ values = array;
+ }
+
+ /**
+ * Constructor from COS object.
+ *
+ * @param array The array containing the XYZ values.
+ */
+ public PDTristimulus( float[] array )
+ {
+ values = new COSArray();
+ for( int i=0; i<array.length && i<3; i++ )
+ {
+ values.add( new COSFloat( array[i] ) );
+ }
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return values;
+ }
+
+ /**
+ * This will get the x value of the tristimulus.
+ *
+ * @return The X value.
+ */
+ public float getX()
+ {
+ return ((COSNumber)values.get( 0 )).floatValue();
+ }
+
+ /**
+ * This will set the x value of the tristimulus.
+ *
+ * @param x The x value for the tristimulus.
+ */
+ public void setX( float x )
+ {
+ values.set( 0, new COSFloat( x ) );
+ }
+
+ /**
+ * This will get the y value of the tristimulus.
+ *
+ * @return The Y value.
+ */
+ public float getY()
+ {
+ return ((COSNumber)values.get( 1 )).floatValue();
+ }
+
+ /**
+ * This will set the y value of the tristimulus.
+ *
+ * @param y The y value for the tristimulus.
+ */
+ public void setY( float y )
+ {
+ values.set( 1, new COSFloat( y ) );
+ }
+
+ /**
+ * This will get the z value of the tristimulus.
+ *
+ * @return The Z value.
+ */
+ public float getZ()
+ {
+ return ((COSNumber)values.get( 2 )).floatValue();
+ }
+
+ /**
+ * This will set the z value of the tristimulus.
+ *
+ * @param z The z value for the tristimulus.
+ */
+ public void setZ( float z )
+ {
+ values.set( 2, new COSFloat( z ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/color/package.html b/src/main/java/org/pdfbox/pdmodel/graphics/color/package.html
new file mode 100644
index 0000000..733efff
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/color/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package deals with colors that are stored in a PDF document.
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/package.html b/src/main/java/org/pdfbox/pdmodel/graphics/package.html
new file mode 100644
index 0000000..3c1f7ca
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The PDModel graphics package deals with graphics states, operations, and parameters within the PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Average.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Average.java
new file mode 100644
index 0000000..46e65dc
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Average.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+/**
+ * We can use raw on the right hand side of
+ * the decoding formula because it is already decoded.
+ *
+ * <code>average(i,j) = raw(i,j) + (raw(i-1,j)+raw(i,j-1)/2</code>
+ *
+ * decoding
+ *
+ * <code>raw(i,j) = avarage(i,j) - (raw(i-1,j)+raw(i,j-1)/2</code>
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ */
+public class Average extends PredictorAlgorithm
+{
+ /**
+ * Not an optimal version, but close to the def.
+ *
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#encodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth() * getBpp();
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[x + destOffset] = (byte) (src[x + srcOffset] - ((leftPixel(
+ src, srcOffset, srcDy, x) + abovePixel(src, srcOffset,
+ srcDy, x)) >>> 2));
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#decodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth() * getBpp();
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[x + destOffset] = (byte) (src[x + srcOffset] + ((leftPixel(
+ dest, destOffset, destDy, x) + abovePixel(dest,
+ destOffset, destDy, x)) >>> 2));
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/None.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/None.java
new file mode 100644
index 0000000..20ec815
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/None.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+/**
+ * The none algorithm.
+ *
+ * <code>None(i,j) = Raw(i,j)</code>
+ *
+ * <code>Raw(i,j) = None(i,j)</code>
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ */
+public class None extends PredictorAlgorithm
+{
+ /**
+ * encode a byte array full of image data using the filter that this object
+ * implements.
+ *
+ * @param src
+ * buffer
+ * @param dest
+ * buffer
+ */
+ public void encode(byte[] src, byte[] dest)
+ {
+ checkBufsiz(dest, src);
+ System.arraycopy(src,0,dest,0,src.length);
+ }
+
+ /**
+ * decode a byte array full of image data using the filter that this object
+ * implements.
+ *
+ * @param src
+ * buffer
+ * @param dest
+ * buffer
+ */
+ public void decode(byte[] src, byte[] dest)
+ {
+ System.arraycopy(src,0,dest,0,src.length);
+ }
+
+
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#encodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth() * getBpp();
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[destOffset + x] = src[srcOffset + x];
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#decodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth() * getBpp();
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[destOffset + x] = src[srcOffset + x];
+ }
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Paeth.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Paeth.java
new file mode 100644
index 0000000..1fddef0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Paeth.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+/**
+ * From http://www.w3.org/TR/PNG-Filters.html: The Paeth filter computes a
+ * simple linear function of the three neighboring pixels (left, above, upper
+ * left), then chooses as predictor the neighboring pixel closest to the
+ * computed value. This technique is due to Alan W. Paeth [PAETH].
+ *
+ * To compute the Paeth filter, apply the following formula to each byte of the
+ * scanline:
+ *
+ * <code>Paeth(i,j) = Raw(i,j) - PaethPredictor(Raw(i-1,j), Raw(i,j-1), Raw(i-1,j-1))</code>
+ *
+ * To decode the Paeth filter
+ *
+ * <code>Raw(i,j) = Paeth(i,j) - PaethPredictor(Raw(i-1,j), Raw(i,j-1), Raw(i-1,j-1))</code>
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ */
+public class Paeth extends PredictorAlgorithm
+{
+ /**
+ * The paeth predictor function.
+ *
+ * This function is taken almost directly from the PNG definition on
+ * http://www.w3.org/TR/PNG-Filters.html
+ *
+ * @param a
+ * left
+ * @param b
+ * above
+ * @param c
+ * upper left
+ * @return The result of the paeth predictor.
+ */
+ public int paethPredictor(int a, int b, int c)
+ {
+ int p = a + b - c; // initial estimate
+ int pa = Math.abs(p - a); // distances to a, b, c
+ int pb = Math.abs(p - b);
+ int pc = Math.abs(p - c);
+ // return nearest of a,b,c,
+ // breaking ties in order a,b,c.
+ if (pa <= pb && pa <= pc)
+ {
+ return a;
+ }
+ else if (pb <= pc)
+ {
+ return b;
+ }
+ else
+ {
+ return c;
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#encodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth() * getBpp();
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[x + destOffset] = (byte) (src[x + srcOffset] - paethPredictor(
+ leftPixel(src, srcOffset, srcDy, x), abovePixel(src,
+ srcOffset, srcDy, x), aboveLeftPixel(src,
+ srcOffset, srcDy, x)));
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#decodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth() * getBpp();
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[x + destOffset] = (byte) (src[x + srcOffset] + paethPredictor(
+ leftPixel(dest, destOffset, destDy, x), abovePixel(dest,
+ destOffset, destDy, x), aboveLeftPixel(dest,
+ destOffset, destDy, x)));
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java
new file mode 100644
index 0000000..11f60f9
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java
@@ -0,0 +1,336 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+import java.util.Random;
+
+/**
+ * Implements different PNG predictor algorithms that is used in PDF files.
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ * @see http://www.w3.org/TR/PNG-Filters.html
+ */
+public abstract class PredictorAlgorithm
+{
+ private int width;
+
+ private int height;
+
+ private int bpp;
+
+ /**
+ * check that buffer sizes matches width,height,bpp. This implementation is
+ * used by most of the filters, but not Uptimum.
+ *
+ * @param src The source buffer.
+ * @param dest The destination buffer.
+ */
+ public void checkBufsiz(byte[] src, byte[] dest)
+ {
+ if (src.length != dest.length)
+ {
+ throw new IllegalArgumentException("src.length != dest.length");
+ }
+ if (src.length != getWidth() * getHeight() * getBpp())
+ {
+ throw new IllegalArgumentException(
+ "src.length != width * height * bpp");
+ }
+ }
+
+ /**
+ * encode line of pixel data in src from srcOffset and width*bpp bytes
+ * forward, put the decoded bytes into dest.
+ *
+ * @param src
+ * raw image data
+ * @param dest
+ * encoded data
+ * @param srcDy
+ * byte offset between lines
+ * @param srcOffset
+ * beginning of line data
+ * @param destDy
+ * byte offset between lines
+ * @param destOffset
+ * beginning of line data
+ */
+ public abstract void encodeLine(byte[] src, byte[] dest, int srcDy,
+ int srcOffset, int destDy, int destOffset);
+
+ /**
+ * decode line of pixel data in src from src_offset and width*bpp bytes
+ * forward, put the decoded bytes into dest.
+ *
+ * @param src
+ * encoded image data
+ * @param dest
+ * raw data
+ * @param srcDy
+ * byte offset between lines
+ * @param srcOffset
+ * beginning of line data
+ * @param destDy
+ * byte offset between lines
+ * @param destOffset
+ * beginning of line data
+ */
+ public abstract void decodeLine(byte[] src, byte[] dest, int srcDy,
+ int srcOffset, int destDy, int destOffset);
+
+ /**
+ * Simple command line program to test the algorithm.
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ Random rnd = new Random();
+ int width = 5;
+ int height = 5;
+ int bpp = 3;
+ byte[] raw = new byte[width * height * bpp];
+ rnd.nextBytes(raw);
+ System.out.println("raw: ");
+ dump(raw);
+ for (int i = 10; i < 15; i++)
+ {
+ byte[] decoded = new byte[width * height * bpp];
+ byte[] encoded = new byte[width * height * bpp];
+
+ PredictorAlgorithm filter = PredictorAlgorithm.getFilter(i);
+ filter.setWidth(width);
+ filter.setHeight(height);
+ filter.setBpp(bpp);
+ filter.encode(raw, encoded);
+ filter.decode(encoded, decoded);
+ System.out.println(filter.getClass().getName());
+ dump(decoded);
+ }
+ }
+
+ /**
+ * Get the left pixel from the buffer.
+ *
+ * @param buf The buffer.
+ * @param offset The offset into the buffer.
+ * @param dy The dy value.
+ * @param x The x value.
+ *
+ * @return The left pixel.
+ */
+ public int leftPixel(byte[] buf, int offset, int dy, int x)
+ {
+ return x >= getBpp() ? buf[offset + x - getBpp()] : 0;
+ }
+
+ /**
+ * Get the above pixel from the buffer.
+ *
+ * @param buf The buffer.
+ * @param offset The offset into the buffer.
+ * @param dy The dy value.
+ * @param x The x value.
+ *
+ * @return The above pixel.
+ */
+ public int abovePixel(byte[] buf, int offset, int dy, int x)
+ {
+ return offset >= dy ? buf[offset + x - dy] : 0;
+ }
+
+ /**
+ * Get the above-left pixel from the buffer.
+ *
+ * @param buf The buffer.
+ * @param offset The offset into the buffer.
+ * @param dy The dy value.
+ * @param x The x value.
+ *
+ * @return The above-left pixel.
+ */
+ public int aboveLeftPixel(byte[] buf, int offset, int dy, int x)
+ {
+ return offset >= dy && x >= getBpp() ? buf[offset + x - dy - getBpp()]
+ : 0;
+ }
+
+ /**
+ * Simple helper to print out a buffer.
+ *
+ * @param raw The bytes to print out.
+ */
+ private static void dump(byte[] raw)
+ {
+ for (int i = 0; i < raw.length; i++)
+ {
+ System.out.print(raw[i] + " ");
+ }
+ System.out.println();
+ }
+
+ /**
+ * @return Returns the bpp.
+ */
+ public int getBpp()
+ {
+ return bpp;
+ }
+
+ /**
+ * @param newBpp
+ * The bpp to set.
+ */
+ public void setBpp(int newBpp)
+ {
+ bpp = newBpp;
+ }
+
+ /**
+ * @return Returns the height.
+ */
+ public int getHeight()
+ {
+ return height;
+ }
+
+ /**
+ * @param newHeight
+ * The height to set.
+ */
+ public void setHeight(int newHeight)
+ {
+ height = newHeight;
+ }
+
+ /**
+ * @return Returns the width.
+ */
+ public int getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * @param newWidth
+ * The width to set.
+ */
+ public void setWidth(int newWidth)
+ {
+ this.width = newWidth;
+ }
+
+
+ /**
+ * encode a byte array full of image data using the filter that this object
+ * implements.
+ *
+ * @param src
+ * buffer
+ * @param dest
+ * buffer
+ */
+ public void encode(byte[] src, byte[] dest)
+ {
+ checkBufsiz(dest, src);
+ int dy = getWidth()*getBpp();
+ for (int y = 0; y < height; y++)
+ {
+ int yoffset = y * dy;
+ encodeLine(src, dest, dy, yoffset, dy, yoffset);
+ }
+ }
+
+ /**
+ * decode a byte array full of image data using the filter that this object
+ * implements.
+ *
+ * @param src
+ * buffer
+ * @param dest
+ * buffer
+ */
+ public void decode(byte[] src, byte[] dest)
+ {
+ checkBufsiz(src, dest);
+ int dy = width * bpp;
+ for (int y = 0; y < height; y++)
+ {
+ int yoffset = y * dy;
+ decodeLine(src, dest, dy, yoffset, dy, yoffset);
+ }
+ }
+
+ /**
+ * @param predictor
+ * <ul>
+ * <li>1 No prediction (the default value)
+ * <li>2 TIFF Predictor 2
+ * <li>10 PNG prediction (on encoding, PNG None on all rows)
+ * <li>11 PNG prediction (on encoding, PNG Sub on all rows)
+ * <li>12 PNG prediction (on encoding, PNG Up on all rows)
+ * <li>13 PNG prediction (on encoding, PNG Average on all rows)
+ * <li>14 PNG prediction (on encoding, PNG Paeth on all rows)
+ * <li>15 PNG prediction (on encoding, PNG optimum)
+ * </ul>
+ *
+ * @return The predictor class based on the predictor code.
+ */
+ public static PredictorAlgorithm getFilter(int predictor)
+ {
+ PredictorAlgorithm filter;
+ switch (predictor)
+ {
+ case 10:
+ filter = new None();
+ break;
+ case 11:
+ filter = new Sub();
+ break;
+ case 12:
+ filter = new Up();
+ break;
+ case 13:
+ filter = new Average();
+ break;
+ case 14:
+ filter = new Paeth();
+ break;
+ case 15:
+ filter = new Uptimum();
+ break;
+ default:
+ filter = new None();
+ }
+ return filter;
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Sub.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Sub.java
new file mode 100644
index 0000000..3959dbe
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Sub.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+/**
+ * The sub algorithm.
+ *
+ * <code>Sub(i,j) = Raw(i,j) - Raw(i-1,j)</code>
+ *
+ * <code>Raw(i,j) = Sub(i,j) + Raw(i-1,j)</code>
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ */
+public class Sub extends PredictorAlgorithm
+{
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#encodeLine(byte[], byte[], int, int, int, int)
+ */
+ public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth()*getBpp();
+ int bpp = getBpp();
+ // case: x < bpp
+ for (int x = 0; x < bpl && x < bpp; x++)
+ {
+ dest[x + destOffset] = src[x + srcOffset];
+ }
+ // otherwise
+ for (int x = getBpp(); x < bpl; x++)
+ {
+ dest[x + destOffset] = (byte) (src[x + srcOffset] - src[x
+ + srcOffset - bpp]);
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#decodeLine(byte[], byte[], int, int, int, int)
+ */
+ public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth()*getBpp();
+ int bpp = getBpp();
+ // case: x < bpp
+ for (int x = 0; x < bpl && x < bpp; x++)
+ {
+ dest[x + destOffset] = src[x + srcOffset];
+ }
+ // otherwise
+ for (int x = getBpp(); x < bpl; x++)
+ {
+ dest[x + destOffset] = (byte) (src[x + srcOffset] + dest[x
+ + destOffset - bpp]);
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Up.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Up.java
new file mode 100644
index 0000000..f1932b4
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Up.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+/**
+ * The up algorithm.
+ *
+ * <code>Up(i,j) = Raw(i,j) - Raw(i,j-1)</code>
+ *
+ * <code>Raw(i,j) = Up(i,j) + Raw(i,j-1)</code>
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ */
+public class Up extends PredictorAlgorithm
+{
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#encodeLine(byte[], byte[], int, int, int, int)
+ */
+ public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ int bpl = getWidth()*getBpp();
+ // case: y = 0;
+ if (srcOffset - srcDy < 0)
+ {
+ if (0 < getHeight())
+ {
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[destOffset + x] = src[srcOffset + x];
+ }
+ }
+ }
+ else
+ {
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[destOffset + x] = (byte) (src[srcOffset + x] - src[srcOffset
+ + x - srcDy]);
+ }
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#decodeLine(byte[], byte[], int, int, int, int)
+ */
+ public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ // case: y = 0;
+ int bpl = getWidth()*getBpp();
+ if (destOffset - destDy < 0)
+ {
+ if (0 < getHeight())
+ {
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[destOffset + x] = src[srcOffset + x];
+ }
+ }
+ }
+ else
+ {
+ for (int x = 0; x < bpl; x++)
+ {
+ dest[destOffset + x] = (byte) (src[srcOffset + x] + dest[destOffset
+ + x - destDy]);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Uptimum.java b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Uptimum.java
new file mode 100644
index 0000000..ac03162
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/Uptimum.java
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.predictor;
+
+/**
+ *
+ *
+ * In an Uptimum encoded image, each line takes up width*bpp+1 bytes. The first
+ * byte holds a number that signifies which algorithm encoded the line.
+ *
+ * @author xylifyx@yahoo.co.uk
+ * @version $Revision: 1.2 $
+ */
+public class Uptimum extends PredictorAlgorithm
+{
+ /**
+ * @see PredictorAlgorithm#checkBufsiz(byte[], byte[])
+ */
+ public void checkBufsiz(byte[] filtered, byte[] raw)
+ {
+ if (filtered.length != (getWidth() * getBpp() + 1) * getHeight())
+ {
+
+ throw new IllegalArgumentException(
+ "filtered.length != (width*bpp + 1) * height, "
+ + filtered.length + " "
+ + (getWidth() * getBpp() + 1) * getHeight()
+ + "w,h,bpp=" + getWidth() + "," + getHeight() + ","
+ + getBpp());
+ }
+ if (raw.length != getWidth() * getHeight() * getBpp())
+ {
+ throw new IllegalArgumentException(
+ "raw.length != width * height * bpp, raw.length="
+ + raw.length + " w,h,bpp=" + getWidth() + ","
+ + getHeight() + "," + getBpp());
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#encodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ throw new UnsupportedOperationException("encodeLine");
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#decodeLine(byte[], byte[],
+ * int, int, int, int)
+ */
+ public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset,
+ int destDy, int destOffset)
+ {
+ throw new UnsupportedOperationException("decodeLine");
+ }
+
+ /**
+ * @see PredictorAlgorithm#encode(byte[], byte[])
+ */
+ public void encode(byte[] src, byte[] dest)
+ {
+ checkBufsiz(dest, src);
+ throw new UnsupportedOperationException("encode");
+ }
+
+ /**
+ * filter indexed by byte code.
+ */
+ PredictorAlgorithm[] filter = { new None(), new Sub(), new Up(), new Average(),
+ new Paeth() };
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#setBpp(int)
+ */
+ public void setBpp(int bpp)
+ {
+ super.setBpp(bpp);
+ for (int i = 0; i < filter.length; i++)
+ {
+ filter[i].setBpp(bpp);
+ }
+ }
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#setHeight(int)
+ */
+ public void setHeight(int height)
+ {
+ super.setHeight(height);
+ for (int i = 0; i < filter.length; i++)
+ {
+ filter[i].setHeight(height);
+ }
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm#setWidth(int)
+ */
+ public void setWidth(int width)
+ {
+ super.setWidth(width);
+ for (int i = 0; i < filter.length; i++)
+ {
+ filter[i].setWidth(width);
+ }
+ }
+
+ /**
+ * @see PredictorAlgorithm#decode(byte[], byte[])
+ */
+ public void decode(byte[] src, byte[] dest)
+ {
+ checkBufsiz(src, dest);
+ int bpl = getWidth() * getBpp();
+ int srcDy = bpl + 1;
+ for (int y = 0; y < getHeight(); y++)
+ {
+ PredictorAlgorithm f = filter[src[y * srcDy]];
+ int srcOffset = y * srcDy + 1;
+ f.decodeLine(src, dest, srcDy, srcOffset, bpl, y * bpl);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/predictor/package.html b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/package.html
new file mode 100644
index 0000000..127d2e0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/predictor/package.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The predictor package contains code for different PNG predictor algorithms that
+are present in PDF documents. These classes are used internally by PDFBox.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDCcitt.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDCcitt.java
new file mode 100644
index 0000000..59387a0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDCcitt.java
@@ -0,0 +1,598 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.PDStream;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceGray;
+
+/**
+ * An image class for CCITT Fax.
+ *
+ * @author paul king
+ * @version $Revision: 1.2 $
+ */
+public class PDCcitt extends PDXObjectImage
+{
+ private static final List FAX_FILTERS = new ArrayList();
+
+ static
+ {
+ FAX_FILTERS.add( COSName.CCITTFAX_DECODE.getName() );
+ FAX_FILTERS.add( COSName.CCITTFAX_DECODE_ABBREVIATION.getName() );
+ }
+
+ /**
+ * Standard constructor.
+ *
+ * @param ccitt The PDStream that already contains all ccitt information.
+ */
+ public PDCcitt(PDStream ccitt)
+ {
+ super(ccitt, "tiff");
+
+ }
+
+ /**
+ * Construct from a tiff file.
+ *
+ * @param doc The document to create the image as part of.
+ * @param raf The random access TIFF file which contains a suitable CCITT compressed image
+ * @throws IOException If there is an error reading the tiff data.
+ */
+
+ public PDCcitt( PDDocument doc, java.io.RandomAccessFile raf ) throws IOException
+ {
+ super( new PDStream(doc),"tiff");
+ // super( new PDStream( doc, null, true ), "tiff" );
+
+ COSDictionary decodeParms = new COSDictionary();
+
+ COSDictionary dic = getCOSStream();
+
+ extractFromTiff(raf, getCOSStream().createFilteredStream(),decodeParms);
+
+ dic.setItem( COSName.FILTER, COSName.CCITTFAX_DECODE);
+ dic.setItem( COSName.SUBTYPE, COSName.IMAGE);
+ dic.setItem( COSName.TYPE, COSName.getPDFName( "XObject" ) );
+ dic.setItem( "DecodeParms", decodeParms);
+
+ setBitsPerComponent( 1 );
+ setColorSpace( new PDDeviceGray() );
+ setWidth( decodeParms.getInt("Columns") );
+ setHeight( decodeParms.getInt("Rows") );
+
+ }
+
+ /**
+ * Returns an image of the CCITT Fax, or null if TIFFs are not supported. (Requires additional JAI Image filters )
+ * @see org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage#getRGBImage()
+ */
+ public BufferedImage getRGBImage() throws IOException
+ {
+ // ImageIO.scanForPlugins();
+ return ImageIO.read(new TiffWrapper(getPDStream().getPartiallyFilteredStream( FAX_FILTERS ),getCOSStream()));
+ }
+
+ /**
+ * This writes a tiff to out.
+ * @see org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage#write2OutputStream(java.io.OutputStream)
+ */
+ public void write2OutputStream(OutputStream out) throws IOException
+ {
+ InputStream data = new TiffWrapper(getPDStream().getPartiallyFilteredStream( FAX_FILTERS ),getCOSStream());
+ byte[] buf = new byte[1024];
+ int amountRead = -1;
+ while( (amountRead = data.read( buf )) != -1 )
+ {
+ out.write( buf, 0, amountRead );
+ }
+ }
+
+ /**
+ * Extract the ccitt stream from the tiff file.
+ *
+ * @param raf - TIFF File
+ * @param os - Stream to write raw ccitt data two
+ * @param parms - COSDictionary which the encoding parameters are added to
+ * @throws IOException If there is an error reading/writing to/from the stream
+ */
+ private void extractFromTiff(RandomAccessFile raf, OutputStream os, COSDictionary parms) throws IOException
+ {
+ try
+ {
+
+ // First check the basic tiff header
+ raf.seek(0);
+ char endianess = (char) raf.read();
+ if ((char) raf.read() != endianess)
+ {
+ throw new IOException("Not a valid tiff file");
+ }
+ //ensure that endianess is either M or I
+ if (endianess != 'M' && endianess != 'I')
+ {
+ throw new IOException("Not a valid tiff file");
+ }
+ int magicNumber = readshort(endianess, raf);
+ if( magicNumber != 42)
+ {
+ throw new IOException("Not a valid tiff file");
+ }
+
+ // Relocate to the first set of tags
+ raf.seek(readlong(endianess, raf));
+
+ int numtags = readshort(endianess, raf);
+
+ // The number 50 is somewhat arbitary, it just stops us load up junk from somewhere and tramping on
+ if (numtags > 50)
+ {
+ throw new IOException("Not a valid tiff file");
+ }
+
+ // Loop through the tags, some will convert to items in the parms dictionary
+ // Other point us to where to find the data stream
+ // The only parm which might change as a result of other options is K, so
+ // We'll deal with that as a special;
+
+ int k=-1000; // Default Non CCITT compression
+ int dataoffset=0;
+ int datalength=0;
+
+ for (int i=0; i < numtags; i++)
+ {
+ int tag = readshort(endianess, raf);
+ int type = readshort(endianess, raf);
+ int count = readlong(endianess, raf);
+ int val = readlong(endianess, raf); // See note
+
+ // Note, we treated that value as a long. The value always occupies 4 bytes
+ // But it might only use the first byte or two. Depending on endianess we might need to correct
+ // Note we ignore all other types, they are of little interest for PDFs/CCITT Fax
+ if (endianess == 'M')
+ {
+ switch (type)
+ {
+ case 1:
+ {
+ val = val >> 24;
+ break; // byte value
+ }
+ case 3:
+ {
+ val = val >> 16;
+ break; // short value
+ }
+ case 4:
+ {
+ break; // long value
+ }
+ default:
+ {
+ //do nothing
+ }
+ }
+ }
+ switch (tag)
+ {
+ case 256:
+ {
+ parms.setInt("Columns",val);
+ break;
+ }
+ case 257:
+ {
+ parms.setInt("Rows",val);
+ break;
+ }
+ case 259:
+ {
+ if (val == 4)
+ {
+ k=-1;
+ }
+ if (val == 3)
+ {
+ k=0;
+ }
+ break; // T6/T4 Compression
+ }
+ case 262:
+ {
+ if (val == 1)
+ {
+ parms.setBoolean("BlackIs1", true);
+ }
+ break;
+ }
+ case 273:
+ {
+ if (count == 1)
+ {
+ dataoffset=val;
+ }
+ break;
+ }
+ case 279:
+ {
+ if (count == 1)
+ {
+ datalength=val;
+ }
+ break;
+ }
+ case 292:
+ {
+ if (val == 1)
+ {
+ k=50; // T4 2D - arbitary K value
+ }
+ break;
+ }
+ case 324:
+ {
+ if (count == 1)
+ {
+ dataoffset=val;
+ }
+ break;
+ }
+ case 325:
+ {
+ if (count == 1)
+ {
+ datalength=val;
+ }
+ break;
+ }
+ default:
+ {
+ //do nothing
+ }
+ }
+ }
+
+ if (k == -1000)
+ {
+ throw new IOException("First image in tiff is not CCITT T4 or T6 compressed");
+ }
+ if (dataoffset == 0)
+ {
+ throw new IOException("First image in tiff is not a single tile/strip");
+ }
+
+ parms.setInt("K",k);
+
+ raf.seek(dataoffset);
+
+ byte[] buf = new byte[8192];
+ int amountRead = -1;
+ while( (amountRead = raf.read( buf,0, Math.min(8192,datalength) )) > 0 )
+ {
+ datalength -= amountRead;
+ os.write( buf, 0, amountRead );
+ }
+
+ }
+ finally
+ {
+ os.close();
+ }
+ }
+
+ private int readshort(char endianess, RandomAccessFile raf) throws IOException
+ {
+ if (endianess == 'I')
+ {
+ return raf.read() | (raf.read() << 8);
+ }
+ return (raf.read() << 8) | raf.read();
+ }
+
+ private int readlong(char endianess, RandomAccessFile raf) throws IOException
+ {
+ if (endianess == 'I')
+ {
+ return raf.read() | (raf.read() << 8) | (raf.read() << 16) | (raf.read() << 24);
+ }
+ return (raf.read() << 24) | (raf.read() << 16) | (raf.read() << 8) | raf.read();
+ }
+
+
+ /**
+ * Extends InputStream to wrap the data from the CCITT Fax with a suitable TIFF Header.
+ * For details see www.tiff.org, which contains useful information including pointers to the
+ * TIFF 6.0 Specification
+ *
+ */
+ private class TiffWrapper extends InputStream
+ {
+
+ private int currentOffset; // When reading, where in the tiffheader are we.
+ private byte[] tiffheader; // Byte array to store tiff header data
+ private InputStream datastream; // Original InputStream
+
+ public TiffWrapper(InputStream rawstream, COSDictionary options)
+ {
+ buildHeader(options);
+ currentOffset=0;
+ datastream = rawstream;
+ }
+
+ // Implement basic methods from InputStream
+
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+ public void reset() throws IOException
+ {
+ throw new IOException("reset not supported");
+ }
+
+ // For simple read, take a byte from the tiffheader array or pass through.
+ public int read() throws IOException
+ {
+ if (currentOffset < tiffheader.length)
+ {
+ return tiffheader[currentOffset++];
+ }
+ return datastream.read();
+ }
+
+ // For read methods only return as many bytes as we have left in the header
+ // if we've exhausted the header, pass through to the InputStream of the raw CCITT data
+ public int read(byte[] data) throws IOException
+ {
+ if (currentOffset < tiffheader.length)
+ {
+ int length = java.lang.Math.min(tiffheader.length - currentOffset, data.length);
+ if (length > 0)
+ {
+ System.arraycopy(tiffheader, currentOffset, data, 0, length);
+ }
+ currentOffset += length;
+ return length;
+ }
+ else
+ {
+ return datastream.read(data);
+ }
+ }
+
+ // For read methods only return as many bytes as we have left in the header
+ // if we've exhausted the header, pass through to the InputStream of the raw CCITT data
+ public int read(byte[] data, int off, int len) throws IOException
+ {
+ if (currentOffset < tiffheader.length)
+ {
+ int length = java.lang.Math.min(tiffheader.length - currentOffset, len);
+ if (length > 0)
+ {
+ System.arraycopy(tiffheader, currentOffset, data, off, length);
+ }
+ currentOffset += length;
+ return length;
+ }
+ else
+ {
+ return datastream.read(data,off,len);
+ }
+ }
+
+ // When skipping if any header data not yet read, only allow to skip what we've in the buffer
+ // Otherwise just pass through.
+ public long skip(long n) throws IOException
+ {
+ if (currentOffset < tiffheader.length)
+ {
+ long length = Math.min(tiffheader.length - currentOffset, n);
+ currentOffset += length;
+ return length;
+ }
+ else
+ {
+ return datastream.skip(n);
+ }
+ }
+
+ // Static data for the beginning of the TIFF header
+ private final byte[] basicHeader = {
+ 'I','I',42,0,8,0,0,0, // File introducer and pointer to first IFD
+ 0,0}; // Number of tags start with two
+
+
+ private int additionalOffset; // Offset in header to additional data
+
+ // Builds up the tiffheader based on the options passed through.
+ private void buildHeader(COSDictionary options)
+ {
+
+ final int numOfTags = 10; // The maximum tags we'll fill
+ final int maxAdditionalData = 24; // The maximum amount of additional data
+ // outside the IFDs. (bytes)
+
+ // The length of the header will be the length of the basic header (10)
+ // plus 12 bytes for each IFD, 4 bytes as a pointer to the next IFD (will be 0)
+ // plus the length of the additional data
+
+ tiffheader = new byte[10 + (12 * numOfTags ) + 4 + maxAdditionalData];
+ java.util.Arrays.fill(tiffheader,(byte)0);
+ System.arraycopy(basicHeader,0,tiffheader,0,basicHeader.length);
+
+ // Additional data outside the IFD starts after the IFD's and pointer to the next IFD (0)
+ additionalOffset = 10 + (12 * numOfTags ) + 4;
+
+ // Now work out the variable values from TIFF defaults,
+ // PDF Defaults and the Dictionary for this XObject
+ short cols = 1728;
+ short rows = 0;
+ short blackis1 = 0;
+ short comptype = 3; // T4 compression
+ long t4options = 0; // Will set if 1d or 2d T4
+
+ COSDictionary decodeParms = (COSDictionary) options.getDictionaryObject("DecodeParms");
+
+ if (decodeParms != null)
+ {
+ cols = (short) decodeParms.getInt("Columns", cols);
+ rows = (short) decodeParms.getInt("Rows", rows);
+ if (decodeParms.getBoolean("BlackIs1", false))
+ {
+ blackis1 = 1;
+ }
+ int k = decodeParms.getInt("K"); // Mandatory parm
+ if (k < 0)
+ {
+ //T6
+ comptype = 4;
+ }
+ if (k > 0)
+ {
+ //T4 2D
+ comptype = 3;
+ t4options = 1;
+ }
+ // else k = 0, leave as default T4 1D compression
+ }
+
+ // If we couldn't get the number of rows, use the main item from XObject
+ if (rows == 0)
+ {
+ rows = (short) options.getInt("Height", rows);
+ }
+
+ // Now put the tags into the tiffheader
+ // These musn't exceed the maximum set above, and by TIFF spec should be sorted into
+ // Numeric sequence.
+
+ addTag(256, cols); // Columns
+ addTag(257, rows); // Rows
+ addTag(259, comptype); // T6
+ addTag(262, blackis1); // Photometric Interpretation
+ addTag(273, (long) tiffheader.length); // Offset to start of image data - updated below
+ addTag(279, (long) options.getInt("Length")); // Length of image data
+ addTag(282, 300, 1); // X Resolution 300 (default unit Inches) This is arbitary
+ addTag(283, 300, 1); // Y Resolution 300 (default unit Inches) This is arbitary
+ if (comptype == 3)
+ {
+ addTag(292, t4options);
+ }
+ addTag(305, "PDFBOX"); // Software generating image
+ }
+
+ /* Tiff types 1 = byte, 2=ascii, 3=short, 4=ulong 5=rational */
+
+ private void addTag(int tag,long value)
+ {
+ // Adds a tag of type 4 (ulong)
+ int count = ++tiffheader[8];
+ int offset = (count-1)*12 + 10;
+ tiffheader[offset]=(byte)(tag & 0xff);
+ tiffheader[offset+1]=(byte)((tag>>8) & 0xff);
+ tiffheader[offset+2]=4; // Type Long
+ tiffheader[offset+4]=1; // One Value
+ tiffheader[offset+8]=(byte)(value & 0xff);
+ tiffheader[offset+9]=(byte)((value>>8) & 0xff);
+ tiffheader[offset+10]=(byte)((value>>16) & 0xff);
+ tiffheader[offset+11]=(byte)((value>>24) & 0xff);
+ }
+
+ private void addTag(int tag, short value)
+ {
+ // Adds a tag of type 3 (short)
+ int count = ++tiffheader[8];
+ int offset = (count-1)*12 + 10;
+ tiffheader[offset]=(byte)(tag & 0xff);
+ tiffheader[offset+1]=(byte)((tag>>8) & 0xff);
+ tiffheader[offset+2]=3; // Type Short
+ tiffheader[offset+4]=1; // One Value
+ tiffheader[offset+8]=(byte)(value & 0xff);
+ tiffheader[offset+9]=(byte)((value>>8) & 0xff);
+ }
+
+ private void addTag(int tag, String value)
+ {
+ // Adds a tag of type 2 (ascii)
+ int count = ++tiffheader[8];
+ int offset = (count-1)*12 + 10;
+ tiffheader[offset]=(byte)(tag & 0xff);
+ tiffheader[offset+1]=(byte)((tag>>8) & 0xff);
+ tiffheader[offset+2]=2; // Type Ascii
+ tiffheader[offset+4]=1; // One Value
+ tiffheader[offset+8]=(byte)(additionalOffset & 0xff);
+ tiffheader[offset+9]=(byte)((additionalOffset>>8) & 0xff);
+ tiffheader[offset+10]=(byte)((additionalOffset>>16) & 0xff);
+ tiffheader[offset+11]=(byte)((additionalOffset>>24) & 0xff);
+ System.arraycopy(value.getBytes(), 0, tiffheader, additionalOffset, value.length());
+ additionalOffset += value.length() + 1;
+ }
+
+ private void addTag(int tag, long numerator, long denominator)
+ {
+ // Adds a tag of type 5 (rational)
+ int count = ++tiffheader[8];
+ int offset = (count-1)*12 + 10;
+ tiffheader[offset]=(byte)(tag & 0xff);
+ tiffheader[offset+1]=(byte)((tag>>8) & 0xff);
+ tiffheader[offset+2]=5; // Type Rational
+ tiffheader[offset+4]=1; // One Value
+ tiffheader[offset+8]=(byte)(additionalOffset & 0xff);
+ tiffheader[offset+9]=(byte)((additionalOffset>>8) & 0xff);
+ tiffheader[offset+10]=(byte)((additionalOffset>>16) & 0xff);
+ tiffheader[offset+11]=(byte)((additionalOffset>>24) & 0xff);
+ tiffheader[additionalOffset++]=(byte) ((numerator) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((numerator>>8) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((numerator>>16) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((numerator>>24) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((denominator) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((denominator>>8) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((denominator>>16) & 0xFF);
+ tiffheader[additionalOffset++]=(byte) ((denominator>>24) & 0xFF);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java
new file mode 100644
index 0000000..009743f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.pdfbox.filter.Filter;
+import org.pdfbox.filter.FilterManager;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.util.ImageParameters;
+
+/**
+ * This class represents an inlined image.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDInlinedImage
+{
+ private ImageParameters params;
+ private byte[] imageData;
+
+ /**
+ * This will get the image parameters.
+ *
+ * @return The image parameters.
+ */
+ public ImageParameters getImageParameters()
+ {
+ return params;
+ }
+
+ /**
+ * This will set the image parameters for this image.
+ *
+ * @param imageParams The imageParams.
+ */
+ public void setImageParameters( ImageParameters imageParams )
+ {
+ params = imageParams;
+ }
+
+ /**
+ * Get the bytes for the image.
+ *
+ * @return The image data.
+ */
+ public byte[] getImageData()
+ {
+ return imageData;
+ }
+
+ /**
+ * Set the bytes that make up the image.
+ *
+ * @param value The image data.
+ */
+ public void setImageData(byte[] value)
+ {
+ imageData = value;
+ }
+
+ /**
+ * This will take the inlined image information and create a java.awt.Image from
+ * it.
+ *
+ * @return The image that this object represents.
+ *
+ * @throws IOException If there is an error creating the image.
+ */
+ public BufferedImage createImage() throws IOException
+ {
+ /*
+ * This was the previous implementation, not sure which is better right now.
+ * byte[] transparentColors = new byte[]{(byte)0xFF,(byte)0xFF};
+ byte[] colors=new byte[]{0, (byte)0xFF};
+ IndexColorModel colorModel = new IndexColorModel( 1, 2, colors, colors, colors, transparentColors );
+ BufferedImage image = new BufferedImage(
+ params.getWidth(),
+ params.getHeight(),
+ BufferedImage.TYPE_BYTE_BINARY,
+ colorModel );
+ DataBufferByte buffer = new DataBufferByte( getImageData(), 1 );
+ WritableRaster raster =
+ Raster.createPackedRaster(
+ buffer,
+ params.getWidth(),
+ params.getHeight(),
+ params.getBitsPerComponent(),
+ new Point(0,0) );
+ image.setData( raster );
+ return image;
+ */
+
+
+ //verify again pci32.pdf before changing below
+ PDColorSpace pcs = params.getColorSpace();
+ ColorModel colorModel = null;
+ if(pcs != null)
+ {
+ colorModel =
+ params.getColorSpace().createColorModel(
+ params.getBitsPerComponent() );
+ }
+ else
+ {
+ byte[] transparentColors = new
+ byte[]{(byte)0xFF,(byte)0xFF};
+ byte[] colors=new byte[]{0, (byte)0xFF};
+ colorModel = new IndexColorModel( 1, 2,
+ colors, colors, colors, transparentColors );
+ }
+ List filters = params.getFilters();
+ byte[] finalData = null;
+ if( filters == null )
+ {
+ finalData = getImageData();
+ }
+ else
+ {
+ ByteArrayInputStream in = new ByteArrayInputStream( getImageData() );
+ ByteArrayOutputStream out = new ByteArrayOutputStream(getImageData().length);
+ FilterManager filterManager = new FilterManager();
+ for( int i=0; filters != null && i<filters.size(); i++ )
+ {
+ out.reset();
+ Filter filter = filterManager.getFilter( (String)filters.get( i ) );
+ filter.decode( in, out, params.getDictionary() );
+ in = new ByteArrayInputStream( out.toByteArray() );
+ }
+ finalData = out.toByteArray();
+ }
+
+ WritableRaster raster = colorModel.createCompatibleWritableRaster( params.getWidth(), params.getHeight() );
+ /* Raster.createPackedRaster(
+ buffer,
+ params.getWidth(),
+ params.getHeight(),
+ params.getBitsPerComponent(),
+ new Point(0,0) );
+ */
+ DataBuffer rasterBuffer = raster.getDataBuffer();
+ if( rasterBuffer instanceof DataBufferByte )
+ {
+ DataBufferByte byteBuffer = (DataBufferByte)rasterBuffer;
+ byte[] data = byteBuffer.getData();
+ System.arraycopy( finalData, 0, data, 0, data.length );
+ }
+ else if( rasterBuffer instanceof DataBufferInt )
+ {
+ DataBufferInt byteBuffer = (DataBufferInt)rasterBuffer;
+ int[] data = byteBuffer.getData();
+ for( int i=0; i<finalData.length; i++ )
+ {
+ data[i] = (finalData[i]+256)%256;
+ }
+ }
+ BufferedImage image = new BufferedImage(
+ colorModel, raster, false, null );
+ image.setData( raster );
+ return image;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDJpeg.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDJpeg.java
new file mode 100644
index 0000000..79ee8e8
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDJpeg.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.PDStream;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+
+/**
+ * An image class for JPegs.
+ *
+ * @author mathiak
+ * @version $Revision: 1.4 $
+ */
+public class PDJpeg extends PDXObjectImage
+{
+
+ private static final List DCT_FILTERS = new ArrayList();
+
+ static
+ {
+ DCT_FILTERS.add( COSName.DCT_DECODE.getName() );
+ DCT_FILTERS.add( COSName.DCT_DECODE_ABBREVIATION.getName() );
+ }
+
+ /**
+ * Standard constructor.
+ *
+ * @param jpeg The COSStream from which to extract the JPeg
+ */
+ public PDJpeg(PDStream jpeg)
+ {
+ super(jpeg, "jpg");
+ }
+
+ /**
+ * Construct from a stream.
+ *
+ * @param doc The document to create the image as part of.
+ * @param is The stream that contains the jpeg data.
+ * @throws IOException If there is an error reading the jpeg data.
+ */
+ public PDJpeg( PDDocument doc, InputStream is ) throws IOException
+ {
+ super( new PDStream( doc, is, true ), "jpg" );
+ COSDictionary dic = getCOSStream();
+ dic.setItem( COSName.FILTER, COSName.DCT_DECODE );
+ dic.setItem( COSName.SUBTYPE, COSName.IMAGE);
+ dic.setItem( COSName.TYPE, COSName.getPDFName( "XObject" ) );
+
+ BufferedImage image = getRGBImage();
+ setBitsPerComponent( 8 );
+ setColorSpace( PDDeviceRGB.INSTANCE );
+ setHeight( image.getHeight() );
+ setWidth( image.getWidth() );
+
+ }
+
+ /**
+ * Construct from a buffered image.
+ *
+ * @param doc The document to create the image as part of.
+ * @param bi The image to convert to a jpeg
+ * @throws IOException If there is an error processing the jpeg data.
+ */
+ public PDJpeg( PDDocument doc, BufferedImage bi ) throws IOException
+ {
+ super( new PDStream( doc ), "jpg" );
+
+ java.io.OutputStream os = getCOSStream().createFilteredStream();
+ try
+ {
+
+ ImageIO.write(bi,"jpeg",os);
+
+ COSDictionary dic = getCOSStream();
+ dic.setItem( COSName.FILTER, COSName.DCT_DECODE );
+ dic.setItem( COSName.SUBTYPE, COSName.IMAGE);
+ dic.setItem( COSName.TYPE, COSName.getPDFName( "XObject" ) );
+
+ setBitsPerComponent( 8 );
+ setColorSpace( PDDeviceRGB.INSTANCE );
+ setHeight( bi.getHeight() );
+ setWidth( bi.getWidth() );
+ }
+ finally
+ {
+ os.close();
+ }
+ }
+
+ /**
+ * Returns an image of the JPeg, or null if JPegs are not supported. (They should be. )
+ * @see org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage#getRGBImage()
+ */
+ public BufferedImage getRGBImage() throws IOException
+ {
+ return ImageIO.read(getPDStream().getPartiallyFilteredStream( DCT_FILTERS ));
+ }
+
+ /**
+ * This writes the JPeg to out.
+ * @see org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage#write2OutputStream(java.io.OutputStream)
+ */
+ public void write2OutputStream(OutputStream out) throws IOException
+ {
+ InputStream data = getPDStream().getPartiallyFilteredStream( DCT_FILTERS );
+ byte[] buf = new byte[1024];
+ int amountRead = -1;
+ while( (amountRead = data.read( buf )) != -1 )
+ {
+ out.write( buf, 0, amountRead );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java
new file mode 100644
index 0000000..96a1f29
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java
@@ -0,0 +1,236 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import java.awt.image.DataBufferByte;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.WritableRaster;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.imageio.ImageIO;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.predictor.PredictorAlgorithm;
+
+/**
+ * This class contains a PixelMap Image.
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author mathiak
+ * @version $Revision: 1.7 $
+ */
+public class PDPixelMap extends PDXObjectImage
+{
+ private BufferedImage image = null;
+
+ /**
+ * Standard constructor. Basically does nothing.
+ * @param pdStream The stream that holds the pixel map.
+ */
+ public PDPixelMap(PDStream pdStream)
+ {
+ super(pdStream, "png");
+ }
+
+ /**
+ * Construct a pixel map image from an AWT image.
+ *
+ * @param doc The PDF document to embed the image in.
+ * @param awtImage The image to read data from.
+ *
+ * @throws IOException If there is an error while embedding this image.
+ */
+ /*
+ * This method is broken and needs to be implemented, any takers?
+ public PDPixelMap(PDDocument doc, BufferedImage awtImage) throws IOException
+ {
+ super( doc, "png");
+ image = awtImage;
+ setWidth( image.getWidth() );
+ setHeight( image.getHeight() );
+
+ ColorModel cm = image.getColorModel();
+ ColorSpace cs = cm.getColorSpace();
+ PDColorSpace pdColorSpace = PDColorSpaceFactory.createColorSpace( doc, cs );
+ setColorSpace( pdColorSpace );
+ //setColorSpace( )
+
+ PDStream stream = getPDStream();
+ OutputStream output = null;
+ try
+ {
+ output = stream.createOutputStream();
+ DataBuffer buffer = awtImage.getRaster().getDataBuffer();
+ if( buffer instanceof DataBufferByte )
+ {
+ DataBufferByte byteBuffer = (DataBufferByte)buffer;
+ byte[] data = byteBuffer.getData();
+ output.write( data );
+ }
+ setBitsPerComponent( cm.getPixelSize() );
+ }
+ finally
+ {
+ if( output != null )
+ {
+ output.close();
+ }
+ }
+ }*/
+
+ /**
+ * Returns a {@link java.awt.image.BufferedImage} of the COSStream
+ * set in the constructor or null if the COSStream could not be encoded.
+ *
+ * @see org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage#getRGBImage()
+ */
+ public BufferedImage getRGBImage() throws IOException
+ {
+ if( image != null )
+ {
+ return image;
+ }
+
+ //byte[] index =
+ //ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ int width = getWidth();
+ int height = getHeight();
+ int bpc = getBitsPerComponent();
+ //COSInteger length =
+ // (COSInteger) stream.getStream().getDictionary().getDictionaryObject(COSName.LENGTH);
+ //byte[] array = new byte[stream.getFilteredStream().];
+ byte[] array = getPDStream().getByteArray();
+
+// Get the ColorModel right
+ PDColorSpace colorspace = getColorSpace();
+ ColorModel cm = colorspace.createColorModel( bpc );
+ WritableRaster raster = cm.createCompatibleWritableRaster( width, height );
+ //DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
+ DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
+ byte[] bufferData = buffer.getData();
+ //System.arraycopy( array, 0, bufferData, 0, array.length );
+ int predictor = getPredictor();
+
+ PredictorAlgorithm filter = PredictorAlgorithm.getFilter(predictor);
+ filter.setWidth(width);
+ filter.setHeight(height);
+ filter.setBpp((bpc * 3) / 8);
+ filter.decode(array, bufferData);
+ image = new BufferedImage(cm, raster, false, null);
+ return image;
+ }
+
+ /**
+ * Writes the image as .png.
+ *
+ * @see org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage#write2OutputStream(java.io.OutputStream)
+ */
+ public void write2OutputStream(OutputStream out) throws IOException
+ {
+ getRGBImage();
+ if (image!=null)
+ {
+ ImageIO.write(image, "png", out);
+ }
+ }
+
+ /**
+ * DecodeParms is an optional parameter for filters.
+ *
+ * It is provided if any of the filters has nondefault parameters. If there
+ * is only one filter it is a dictionary, if there are multiple filters it
+ * is an array with an entry for each filter. An array entry can hold a null
+ * value if only the default values are used or a dictionary with
+ * parameters.
+ *
+ * @return The decoding parameters.
+ *
+ */
+ public COSDictionary getDecodeParams()
+ {
+ COSBase decodeParms = getCOSStream().getDictionaryObject("DecodeParms");
+ if (decodeParms != null)
+ {
+ if (decodeParms instanceof COSDictionary)
+ {
+ return (COSDictionary) decodeParms;
+ }
+ else if (decodeParms instanceof COSArray)
+ {
+ // not implemented yet, which index should we use?
+ return null;//(COSDictionary)((COSArray)decodeParms).get(0);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * A code that selects the predictor algorithm.
+ *
+ * <ul>
+ * <li>1 No prediction (the default value)
+ * <li>2 TIFF Predictor 2
+ * <li>10 PNG prediction (on encoding, PNG None on all rows)
+ * <li>11 PNG prediction (on encoding, PNG Sub on all rows)
+ * <li>12 PNG prediction (on encoding, PNG Up on all rows)
+ * <li>13 PNG prediction (on encoding, PNG Average on all rows)
+ * <li>14 PNG prediction (on encoding, PNG Paeth on all rows)
+ * <li>15 PNG prediction (on encoding, PNG optimum)
+ * </ul>
+ *
+ * Default value: 1.
+ *
+ * @return predictor algorithm code
+ */
+ public int getPredictor()
+ {
+ COSDictionary decodeParms = getDecodeParams();
+ if (decodeParms != null)
+ {
+ int i = decodeParms.getInt("Predictor");
+ if (i != -1)
+ {
+ return i;
+ }
+ }
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObject.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObject.java
new file mode 100644
index 0000000..99ac8a5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObject.java
@@ -0,0 +1,207 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDMetadata;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * The base class for all XObjects in the PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author mathiak
+ * @author Marcel Kammer
+ * @version $Revision: 1.12 $
+ */
+public abstract class PDXObject implements COSObjectable
+{
+ private PDStream xobject;
+
+ /**
+ * Standard constuctor.
+ *
+ * @param xobj The XObject dictionary.
+ */
+ public PDXObject(COSStream xobj)
+ {
+ xobject = new PDStream( xobj );
+ }
+
+ /**
+ * Standard constuctor.
+ *
+ * @param xobj The XObject dictionary.
+ */
+ public PDXObject(PDStream xobj)
+ {
+ xobject = xobj;
+ }
+
+ /**
+ * Standard constuctor.
+ *
+ * @param doc The doc to store the object contents.
+ */
+ public PDXObject(PDDocument doc)
+ {
+ xobject = new PDStream(doc);
+ xobject.getStream().setName( COSName.TYPE, "XObject" );
+ }
+
+ /**
+ * Returns the stream.
+ * @see org.pdfbox.pdmodel.common.COSObjectable#getCOSObject()
+ */
+ public COSBase getCOSObject()
+ {
+ return xobject.getCOSObject();
+ }
+
+ /**
+ * Returns the stream.
+ * @return The stream for this object.
+ */
+ public COSStream getCOSStream()
+ {
+ return xobject.getStream();
+ }
+
+ /**
+ * Returns the stream.
+ * @return The stream for this object.
+ */
+ public PDStream getPDStream()
+ {
+ return xobject;
+ }
+
+ /**
+ * Create the correct xobject from the cos base.
+ *
+ * @param xobject The cos level xobject to create.
+ *
+ * @return a pdmodel xobject
+ * @throws IOException If there is an error creating the xobject.
+ */
+ public static PDXObject createXObject( COSBase xobject ) throws IOException
+ {
+ PDXObject retval = null;
+ if( xobject == null )
+ {
+ retval = null;
+ }
+ else if( xobject instanceof COSStream )
+ {
+ COSStream xstream = (COSStream)xobject;
+ String subtype = xstream.getNameAsString( "Subtype" );
+ if( subtype.equals( PDXObjectImage.SUB_TYPE ) )
+ {
+ PDStream image = new PDStream( xstream );
+ // See if filters are DCT or JPX otherwise treat as Bitmap-like
+ // There might be a problem with several filters, but that's ToDo until
+ // I find an example
+ List filters = image.getFilters();
+ if( filters != null && filters.contains( COSName.DCT_DECODE.getName() ) )
+ {
+ return new PDJpeg(image);
+ }
+ else if ( filters != null && filters.contains( COSName.CCITTFAX_DECODE.getName() ) )
+ {
+ return new PDCcitt(image);
+ }
+ else if( filters != null && filters.contains(COSName.JPX_DECODE.getName()))
+ {
+ //throw new IOException( "JPXDecode has not been implemented for images" );
+ //JPX Decode is not really supported right now, but if we are just doing
+ //text extraction then we don't want to throw an exception, so for now
+ //just return a PDPixelMap, which will break later on if it is
+ //actually used, but for text extraction it is not used.
+ return new PDPixelMap( image );
+
+ }
+ else
+ {
+ retval = new PDPixelMap(image);
+ }
+ }
+ else if( subtype.equals( PDXObjectForm.SUB_TYPE ) )
+ {
+ retval = new PDXObjectForm( xstream );
+ }
+ else
+ {
+ throw new IOException( "Unknown xobject subtype '" + subtype + "'" );
+ }
+ }
+ else
+ {
+ throw new IOException( "Unknown xobject type:" + xobject.getClass().getName() );
+ }
+
+ return retval;
+ }
+
+ /**
+ * Get the metadata that is part of the document catalog. This will
+ * return null if there is no meta data for this object.
+ *
+ * @return The metadata for this object.
+ */
+ public PDMetadata getMetadata()
+ {
+ PDMetadata retval = null;
+ COSStream mdStream = (COSStream)xobject.getStream().getDictionaryObject( "Metadata" );
+ if( mdStream != null )
+ {
+ retval = new PDMetadata( mdStream );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the metadata for this object. This can be null.
+ *
+ * @param meta The meta data for this object.
+ */
+ public void setMetadata( PDMetadata meta )
+ {
+ xobject.getStream().setItem( "Metadata", meta );
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java
new file mode 100644
index 0000000..08858ea
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDResources;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * A form xobject.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.4 $
+ */
+public class PDXObjectForm extends PDXObject
+{
+ /**
+ * The XObject subtype.
+ */
+ public static final String SUB_TYPE = "Form";
+
+ /**
+ * Standard constuctor.
+ *
+ * @param formStream The XObject is passed as a COSStream.
+ */
+ public PDXObjectForm(PDStream formStream)
+ {
+ super( formStream );
+ }
+
+ /**
+ * Standard constuctor.
+ *
+ * @param formStream The XObject is passed as a COSStream.
+ */
+ public PDXObjectForm(COSStream formStream)
+ {
+ super( formStream );
+ }
+
+ /**
+ * This will get the form type, currently 1 is the only form type.
+ *
+ * @return The form type.
+ */
+ public int getFormType()
+ {
+ return getCOSStream().getInt( "FormType",1 );
+ }
+
+ /**
+ * Set the form type.
+ *
+ * @param formType The new form type.
+ */
+ public void setFormType( int formType )
+ {
+ getCOSStream().setInt( "FormType", formType );
+ }
+
+ /**
+ * This will get the resources at this page and not look up the hierarchy.
+ * This attribute is inheritable, and findResources() should probably used.
+ * This will return null if no resources are available at this level.
+ *
+ * @return The resources at this level in the hierarchy.
+ */
+ public PDResources getResources()
+ {
+ PDResources retval = null;
+ COSDictionary resources = (COSDictionary)getCOSStream().getDictionaryObject( COSName.RESOURCES );
+ if( resources != null )
+ {
+ retval = new PDResources( resources );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the resources for this page.
+ *
+ * @param resources The new resources for this page.
+ */
+ public void setResources( PDResources resources )
+ {
+ getCOSStream().setItem( COSName.RESOURCES, resources );
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java
new file mode 100644
index 0000000..6257113
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.graphics.xobject;
+
+import java.awt.image.BufferedImage;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.common.PDStream;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceGray;
+
+/**
+ * The prototype for all PDImages.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author mathiak
+ * @version $Revision: 1.8 $
+ */
+public abstract class PDXObjectImage extends PDXObject
+{
+ /**
+ * The XObject subtype.
+ */
+ public static final String SUB_TYPE = "Image";
+
+ /**
+ * This contains the suffix used when writing to file.
+ */
+ private String suffix;
+
+ /**
+ * Standard constuctor.
+ *
+ * @param imageStream The XObject is passed as a COSStream.
+ * @param fileSuffix The file suffix, jpg/png.
+ */
+ public PDXObjectImage(PDStream imageStream, String fileSuffix)
+ {
+ super( imageStream );
+ suffix = fileSuffix;
+ }
+
+ /**
+ * Standard constuctor.
+ *
+ * @param doc The document to store the stream in.
+ * @param fileSuffix The file suffix, jpg/png.
+ */
+ public PDXObjectImage(PDDocument doc, String fileSuffix)
+ {
+ super( doc );
+ getCOSStream().setName( COSName.SUBTYPE, SUB_TYPE );
+ suffix = fileSuffix;
+ }
+
+ /**
+ * Returns an java.awt.Image, that can be used for display etc.
+ *
+ * @return This PDF object as an AWT image.
+ *
+ * @throws IOException If there is an error creating the image.
+ */
+ public abstract BufferedImage getRGBImage() throws IOException;
+
+ /**
+ * Writes the Image to out.
+ * @param out the OutputStream that the Image is written to.
+ * @throws IOException when somethings wrong with out
+ */
+ public abstract void write2OutputStream(OutputStream out) throws IOException;
+
+ /**
+ * Writes the image to a file with the filename + an appropriate suffix, like "Image.jpg".
+ * The suffix is automatically set by the
+ * @param filename the filename
+ * @throws IOException When somethings wrong with the corresponding file.
+ */
+ public void write2file(String filename) throws IOException
+ {
+ FileOutputStream out = null;
+ try
+ {
+ out = new FileOutputStream(filename + "." + suffix);
+ write2OutputStream(out);
+ out.flush();
+ }
+ finally
+ {
+ if( out != null )
+ {
+ out.close();
+ }
+ }
+ }
+
+ /**
+ * Get the height of the image.
+ *
+ * @return The height of the image.
+ */
+ public int getHeight()
+ {
+ return getCOSStream().getInt( "Height", -1 );
+ }
+
+ /**
+ * Set the height of the image.
+ *
+ * @param height The height of the image.
+ */
+ public void setHeight( int height )
+ {
+ getCOSStream().setInt( "Height", height );
+ }
+
+ /**
+ * Get the width of the image.
+ *
+ * @return The width of the image.
+ */
+ public int getWidth()
+ {
+ return getCOSStream().getInt( "Width", -1 );
+ }
+
+ /**
+ * Set the width of the image.
+ *
+ * @param width The width of the image.
+ */
+ public void setWidth( int width )
+ {
+ getCOSStream().setInt( "Width", width );
+ }
+
+ /**
+ * The bits per component of this image. This will return -1 if one has not
+ * been set.
+ *
+ * @return The number of bits per component.
+ */
+ public int getBitsPerComponent()
+ {
+ return getCOSStream().getInt( new String[] { "BPC", "BitsPerComponent"}, -1 );
+ }
+
+ /**
+ * Set the number of bits per component.
+ *
+ * @param bpc The number of bits per component.
+ */
+ public void setBitsPerComponent( int bpc )
+ {
+ getCOSStream().setInt( "BitsPerComponent", bpc );
+ }
+
+ /**
+ * This will get the color space or null if none exists.
+ *
+ * @return The color space for this image.
+ *
+ * @throws IOException If there is an error getting the colorspace.
+ */
+ public PDColorSpace getColorSpace() throws IOException
+ {
+ COSBase cs = getCOSStream().getDictionaryObject( new String[]{ "CS", "ColorSpace" } );
+ PDColorSpace retval = null;
+ if( cs != null )
+ {
+ retval = PDColorSpaceFactory.createColorSpace( cs );
+ }
+ else
+ {
+ //there are some cases where the 'required' CS value is not present
+ //but we know that it will be grayscale for a CCITT filter.
+ COSBase filter = getCOSStream().getDictionaryObject( "Filter" );
+ if( COSName.CCITTFAX_DECODE.equals( filter ) ||
+ COSName.CCITTFAX_DECODE_ABBREVIATION.equals( filter ) )
+ {
+ retval = new PDDeviceGray();
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the color space for this image.
+ *
+ * @param cs The color space for this image.
+ */
+ public void setColorSpace( PDColorSpace cs )
+ {
+ COSBase base = null;
+ if( cs != null )
+ {
+ base = cs.getCOSObject();
+ }
+ getCOSStream().setItem( COSName.getPDFName( "ColorSpace" ), base );
+ }
+
+ /**
+ * This will get the suffix for this image type, jpg/png.
+ *
+ * @return The image suffix.
+ */
+ public String getSuffix()
+ {
+ return suffix;
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/graphics/xobject/package.html b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/package.html
new file mode 100644
index 0000000..60d3324
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/graphics/xobject/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package deals with images that are stored in a PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/PDActionFactory.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDActionFactory.java
new file mode 100644
index 0000000..bf0c156
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDActionFactory.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action;
+
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionGoTo;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionJavaScript;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionLaunch;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionRemoteGoTo;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionURI;
+
+/**
+ * This class will take a dictionary and determine which type of action to create.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class PDActionFactory
+{
+ /**
+ * Utility Class.
+ */
+ private PDActionFactory()
+ {
+ //utility class
+ }
+
+ /**
+ * This will create the correct type of action based on the type specified
+ * in the dictionary.
+ *
+ * @param action An action dictionary.
+ *
+ * @return An action of the correct type.
+ */
+ public static PDAction createAction( COSDictionary action )
+ {
+ PDAction retval = null;
+ if( action != null )
+ {
+ String type = action.getNameAsString( "S" );
+ if( PDActionJavaScript.SUB_TYPE.equals( type ) )
+ {
+ retval = new PDActionJavaScript( action );
+ }
+ else if( PDActionGoTo.SUB_TYPE.equals( type ) )
+ {
+ retval = new PDActionGoTo( action );
+ }
+ else if( PDActionLaunch.SUB_TYPE.equals( type ) )
+ {
+ retval = new PDActionLaunch( action );
+ }
+ else if( PDActionRemoteGoTo.SUB_TYPE.equals( type ) )
+ {
+ retval = new PDActionRemoteGoTo( action );
+ }
+ else if( PDActionURI.SUB_TYPE.equals( type ) )
+ {
+ retval = new PDActionURI( action );
+ }
+ }
+ return retval;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java
new file mode 100644
index 0000000..fc2f79b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+
+/**
+ * This represents a dictionary of actions that occur due to events.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDAdditionalActions implements COSObjectable
+{
+ private COSDictionary actions;
+
+ /**
+ * Default constructor.
+ */
+ public PDAdditionalActions()
+ {
+ actions = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDAdditionalActions( COSDictionary a )
+ {
+ actions = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return actions;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return actions;
+ }
+
+ /**
+ * Get the F action.
+ *
+ * @return The F action.
+ */
+ public PDAction getF()
+ {
+ return PDActionFactory.createAction( (COSDictionary)actions.getDictionaryObject("F" ) );
+ }
+
+ /**
+ * Set the F action.
+ *
+ * @param action Get the F action.
+ */
+ public void setF( PDAction action )
+ {
+ actions.setItem( "F", action );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java
new file mode 100644
index 0000000..9b9232e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java
@@ -0,0 +1,380 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+
+/**
+ * This class represents an annotation's dictionary of actions
+ * that occur due to events.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.1 $
+ */
+public class PDAnnotationAdditionalActions implements COSObjectable
+{
+ private COSDictionary actions;
+
+ /**
+ * Default constructor.
+ */
+ public PDAnnotationAdditionalActions()
+ {
+ actions = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDAnnotationAdditionalActions( COSDictionary a )
+ {
+ actions = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return actions;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return actions;
+ }
+
+ /**
+ * This will get an action to be performed when the cursor
+ * enters the annotation's active area.
+ *
+ * @return The E entry of annotation's additional actions dictionary.
+ */
+ public PDAction getE()
+ {
+ COSDictionary e = (COSDictionary)actions.getDictionaryObject( "E" );
+ PDAction retval = null;
+ if( e != null )
+ {
+ retval = PDActionFactory.createAction( e );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the cursor
+ * enters the annotation's active area.
+ *
+ * @param e The action to be performed.
+ */
+ public void setE( PDAction e )
+ {
+ actions.setItem( "E", e );
+ }
+
+ /**
+ * This will get an action to be performed when the cursor
+ * exits the annotation's active area.
+ *
+ * @return The X entry of annotation's additional actions dictionary.
+ */
+ public PDAction getX()
+ {
+ COSDictionary x = (COSDictionary)actions.getDictionaryObject( "X" );
+ PDAction retval = null;
+ if( x != null )
+ {
+ retval = PDActionFactory.createAction( x );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the cursor
+ * exits the annotation's active area.
+ *
+ * @param x The action to be performed.
+ */
+ public void setX( PDAction x )
+ {
+ actions.setItem( "X", x );
+ }
+
+ /**
+ * This will get an action to be performed when the mouse button
+ * is pressed inside the annotation's active area.
+ * The name D stands for "down".
+ *
+ * @return The d entry of annotation's additional actions dictionary.
+ */
+ public PDAction getD()
+ {
+ COSDictionary d = (COSDictionary)actions.getDictionaryObject( "D" );
+ PDAction retval = null;
+ if( d != null )
+ {
+ retval = PDActionFactory.createAction( d );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the mouse button
+ * is pressed inside the annotation's active area.
+ * The name D stands for "down".
+ *
+ * @param d The action to be performed.
+ */
+ public void setD( PDAction d )
+ {
+ actions.setItem( "D", d );
+ }
+
+ /**
+ * This will get an action to be performed when the mouse button
+ * is released inside the annotation's active area.
+ * The name U stands for "up".
+ *
+ * @return The U entry of annotation's additional actions dictionary.
+ */
+ public PDAction getU()
+ {
+ COSDictionary u = (COSDictionary)actions.getDictionaryObject( "U" );
+ PDAction retval = null;
+ if( u != null )
+ {
+ retval = PDActionFactory.createAction( u );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the mouse button
+ * is released inside the annotation's active area.
+ * The name U stands for "up".
+ *
+ * @param u The action to be performed.
+ */
+ public void setU( PDAction u )
+ {
+ actions.setItem( "U", u );
+ }
+
+ /**
+ * This will get an action to be performed when the annotation
+ * receives the input focus.
+ *
+ * @return The Fo entry of annotation's additional actions dictionary.
+ */
+ public PDAction getFo()
+ {
+ COSDictionary fo = (COSDictionary)actions.getDictionaryObject( "Fo" );
+ PDAction retval = null;
+ if( fo != null )
+ {
+ retval = PDActionFactory.createAction( fo );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the annotation
+ * receives the input focus.
+ *
+ * @param fo The action to be performed.
+ */
+ public void setFo( PDAction fo )
+ {
+ actions.setItem( "Fo", fo );
+ }
+
+ /**
+ * This will get an action to be performed when the annotation
+ * loses the input focus.
+ * The name Bl stands for "blurred".
+ *
+ * @return The Bl entry of annotation's additional actions dictionary.
+ */
+ public PDAction getBl()
+ {
+ COSDictionary bl = (COSDictionary)actions.getDictionaryObject( "Bl" );
+ PDAction retval = null;
+ if( bl != null )
+ {
+ retval = PDActionFactory.createAction( bl );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the annotation
+ * loses the input focus.
+ * The name Bl stands for "blurred".
+ *
+ * @param bl The action to be performed.
+ */
+ public void setBl( PDAction bl )
+ {
+ actions.setItem( "Bl", bl );
+ }
+
+ /**
+ * This will get an action to be performed when the page containing
+ * the annotation is opened. The action is executed after the O action
+ * in the page's additional actions dictionary and the OpenAction entry
+ * in the document catalog, if such actions are present.
+ *
+ * @return The PO entry of annotation's additional actions dictionary.
+ */
+ public PDAction getPO()
+ {
+ COSDictionary po = (COSDictionary)actions.getDictionaryObject( "PO" );
+ PDAction retval = null;
+ if( po != null )
+ {
+ retval = PDActionFactory.createAction( po );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the page containing
+ * the annotation is opened. The action is executed after the O action
+ * in the page's additional actions dictionary and the OpenAction entry
+ * in the document catalog, if such actions are present.
+ *
+ * @param po The action to be performed.
+ */
+ public void setPO( PDAction po )
+ {
+ actions.setItem( "PO", po );
+ }
+
+ /**
+ * This will get an action to be performed when the page containing
+ * the annotation is closed. The action is executed before the C action
+ * in the page's additional actions dictionary, if present.
+ *
+ * @return The PC entry of annotation's additional actions dictionary.
+ */
+ public PDAction getPC()
+ {
+ COSDictionary pc = (COSDictionary)actions.getDictionaryObject( "PC" );
+ PDAction retval = null;
+ if( pc != null )
+ {
+ retval = PDActionFactory.createAction( pc );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the page containing
+ * the annotation is closed. The action is executed before the C action
+ * in the page's additional actions dictionary, if present.
+ *
+ * @param pc The action to be performed.
+ */
+ public void setPC( PDAction pc )
+ {
+ actions.setItem( "PC", pc );
+ }
+
+ /**
+ * This will get an action to be performed when the page containing
+ * the annotation becomes visible in the viewer application's user interface.
+ *
+ * @return The PV entry of annotation's additional actions dictionary.
+ */
+ public PDAction getPV()
+ {
+ COSDictionary pv = (COSDictionary)actions.getDictionaryObject( "PV" );
+ PDAction retval = null;
+ if( pv != null )
+ {
+ retval = PDActionFactory.createAction( pv );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the page containing
+ * the annotation becomes visible in the viewer application's user interface.
+ *
+ * @param pv The action to be performed.
+ */
+ public void setPV( PDAction pv )
+ {
+ actions.setItem( "PV", pv );
+ }
+
+ /**
+ * This will get an action to be performed when the page containing the annotation
+ * is no longer visible in the viewer application's user interface.
+ *
+ * @return The PI entry of annotation's additional actions dictionary.
+ */
+ public PDAction getPI()
+ {
+ COSDictionary pi = (COSDictionary)actions.getDictionaryObject( "PI" );
+ PDAction retval = null;
+ if( pi != null )
+ {
+ retval = PDActionFactory.createAction( pi );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the page containing the annotation
+ * is no longer visible in the viewer application's user interface.
+ *
+ * @param pi The action to be performed.
+ */
+ public void setPI( PDAction pi )
+ {
+ actions.setItem( "PI", pi );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java
new file mode 100644
index 0000000..6164de1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java
@@ -0,0 +1,238 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+
+/**
+ * This class represents a document catalog's dictionary of actions
+ * that occur due to events.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.1 $
+ */
+public class PDDocumentCatalogAdditionalActions implements COSObjectable
+{
+ private COSDictionary actions;
+
+ /**
+ * Default constructor.
+ */
+ public PDDocumentCatalogAdditionalActions()
+ {
+ actions = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDDocumentCatalogAdditionalActions( COSDictionary a )
+ {
+ actions = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return actions;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return actions;
+ }
+
+ /**
+ * This will get a JavaScript action to be performed
+ * before closing a document.
+ * The name WC stands for "will close".
+ *
+ * @return The WC entry of document catalog's additional actions dictionary.
+ */
+ public PDAction getWC()
+ {
+ COSDictionary wc = (COSDictionary)actions.getDictionaryObject( "WC" );
+ PDAction retval = null;
+ if( wc != null )
+ {
+ retval = PDActionFactory.createAction( wc );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed
+ * before closing a document.
+ * The name WC stands for "will close".
+ *
+ * @param wc The action to be performed.
+ */
+ public void setWC( PDAction wc )
+ {
+ actions.setItem( "WC", wc );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed
+ * before saving a document.
+ * The name WS stands for "will save".
+ *
+ * @return The WS entry of document catalog's additional actions dictionary.
+ */
+ public PDAction getWS()
+ {
+ COSDictionary ws = (COSDictionary)actions.getDictionaryObject( "WS" );
+ PDAction retval = null;
+ if( ws != null )
+ {
+ retval = PDActionFactory.createAction( ws );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed
+ * before saving a document.
+ * The name WS stands for "will save".
+ *
+ * @param ws The action to be performed.
+ */
+ public void setWS( PDAction ws )
+ {
+ actions.setItem( "WS", ws );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed
+ * after saving a document.
+ * The name DS stands for "did save".
+ *
+ * @return The DS entry of document catalog's additional actions dictionary.
+ */
+ public PDAction getDS()
+ {
+ COSDictionary ds = (COSDictionary)actions.getDictionaryObject( "DS" );
+ PDAction retval = null;
+ if( ds != null )
+ {
+ retval = PDActionFactory.createAction( ds );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed
+ * after saving a document.
+ * The name DS stands for "did save".
+ *
+ * @param ds The action to be performed.
+ */
+ public void setDS( PDAction ds )
+ {
+ actions.setItem( "DS", ds );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed
+ * before printing a document.
+ * The name WP stands for "will print".
+ *
+ * @return The WP entry of document catalog's additional actions dictionary.
+ */
+ public PDAction getWP()
+ {
+ COSDictionary wp = (COSDictionary)actions.getDictionaryObject( "WP" );
+ PDAction retval = null;
+ if( wp != null )
+ {
+ retval = PDActionFactory.createAction( wp );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed
+ * before printing a document.
+ * The name WP stands for "will print".
+ *
+ * @param wp The action to be performed.
+ */
+ public void setWP( PDAction wp )
+ {
+ actions.setItem( "WP", wp );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed
+ * after printing a document.
+ * The name DP stands for "did print".
+ *
+ * @return The DP entry of document catalog's additional actions dictionary.
+ */
+ public PDAction getDP()
+ {
+ COSDictionary dp = (COSDictionary)actions.getDictionaryObject( "DP" );
+ PDAction retval = null;
+ if( dp != null )
+ {
+ retval = PDActionFactory.createAction( dp );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed
+ * after printing a document.
+ * The name DP stands for "did print".
+ *
+ * @param dp The action to be performed.
+ */
+ public void setDP( PDAction dp )
+ {
+ actions.setItem( "DP", dp );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java
new file mode 100644
index 0000000..2a594f3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+
+/**
+ * This class represents a form field's dictionary of actions
+ * that occur due to events.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.1 $
+ */
+public class PDFormFieldAdditionalActions implements COSObjectable
+{
+ private COSDictionary actions;
+
+ /**
+ * Default constructor.
+ */
+ public PDFormFieldAdditionalActions()
+ {
+ actions = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDFormFieldAdditionalActions( COSDictionary a )
+ {
+ actions = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return actions;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return actions;
+ }
+
+ /**
+ * This will get a JavaScript action to be performed when the user
+ * types a keystroke into a text field or combo box or modifies the
+ * selection in a scrollable list box. This allows the keystroke to
+ * be checked for validity and rejected or modified.
+ *
+ * @return The K entry of form field's additional actions dictionary.
+ */
+ public PDAction getK()
+ {
+ COSDictionary k = (COSDictionary)actions.getDictionaryObject( "K" );
+ PDAction retval = null;
+ if( k != null )
+ {
+ retval = PDActionFactory.createAction( k );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed when the user
+ * types a keystroke into a text field or combo box or modifies the
+ * selection in a scrollable list box. This allows the keystroke to
+ * be checked for validity and rejected or modified.
+ *
+ * @param k The action to be performed.
+ */
+ public void setK( PDAction k )
+ {
+ actions.setItem( "K", k );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed before
+ * the field is formatted to display its current value. This
+ * allows the field's value to be modified before formatting.
+ *
+ * @return The F entry of form field's additional actions dictionary.
+ */
+ public PDAction getF()
+ {
+ COSDictionary f = (COSDictionary)actions.getDictionaryObject( "F" );
+ PDAction retval = null;
+ if( f != null )
+ {
+ retval = PDActionFactory.createAction( f );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed before
+ * the field is formatted to display its current value. This
+ * allows the field's value to be modified before formatting.
+ *
+ * @param f The action to be performed.
+ */
+ public void setF( PDAction f )
+ {
+ actions.setItem( "F", f );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed
+ * when the field's value is changed. This allows the
+ * new value to be checked for validity.
+ * The name V stands for "validate".
+ *
+ * @return The V entry of form field's additional actions dictionary.
+ */
+ public PDAction getV()
+ {
+ COSDictionary v = (COSDictionary)actions.getDictionaryObject( "V" );
+ PDAction retval = null;
+ if( v != null )
+ {
+ retval = PDActionFactory.createAction( v );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed
+ * when the field's value is changed. This allows the
+ * new value to be checked for validity.
+ * The name V stands for "validate".
+ *
+ * @param v The action to be performed.
+ */
+ public void setV( PDAction v )
+ {
+ actions.setItem( "V", v );
+ }
+
+ /**
+ * This will get a JavaScript action to be performed in order to recalculate
+ * the value of this field when that of another field changes. The order in which
+ * the document's fields are recalculated is defined by the CO entry in the
+ * interactive form dictionary.
+ * The name C stands for "calculate".
+ *
+ * @return The C entry of form field's additional actions dictionary.
+ */
+ public PDAction getC()
+ {
+ COSDictionary c = (COSDictionary)actions.getDictionaryObject( "C" );
+ PDAction retval = null;
+ if( c != null )
+ {
+ retval = PDActionFactory.createAction( c );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a JavaScript action to be performed in order to recalculate
+ * the value of this field when that of another field changes. The order in which
+ * the document's fields are recalculated is defined by the CO entry in the
+ * interactive form dictionary.
+ * The name C stands for "calculate".
+ *
+ * @param c The action to be performed.
+ */
+ public void setC( PDAction c )
+ {
+ actions.setItem( "C", c );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java
new file mode 100644
index 0000000..f15ffa7
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+
+/**
+ * This class represents a page object's dictionary of actions
+ * that occur due to events.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.1 $
+ */
+public class PDPageAdditionalActions implements COSObjectable
+{
+ private COSDictionary actions;
+
+ /**
+ * Default constructor.
+ */
+ public PDPageAdditionalActions()
+ {
+ actions = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDPageAdditionalActions( COSDictionary a )
+ {
+ actions = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return actions;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return actions;
+ }
+
+ /**
+ * This will get an action to be performed when the page
+ * is opened. This action is independent of any that may be
+ * defined by the OpenAction entry in the document catalog,
+ * and is executed after such an action.
+ *
+ * @return The O entry of page object's additional actions dictionary.
+ */
+ public PDAction getO()
+ {
+ COSDictionary o = (COSDictionary)actions.getDictionaryObject( "O" );
+ PDAction retval = null;
+ if( o != null )
+ {
+ retval = PDActionFactory.createAction( o );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the page
+ * is opened. This action is independent of any that may be
+ * defined by the OpenAction entry in the document catalog,
+ * and is executed after such an action.
+ *
+ * @param o The action to be performed.
+ */
+ public void setO( PDAction o )
+ {
+ actions.setItem( "O", o );
+ }
+
+ /**
+ * This will get an action to be performed when the page
+ * is closed. This action applies to the page being closed,
+ * and is executed before any other page opened.
+ *
+ * @return The C entry of page object's additional actions dictionary.
+ */
+ public PDAction getC()
+ {
+ COSDictionary c = (COSDictionary)actions.getDictionaryObject( "C" );
+ PDAction retval = null;
+ if( c != null )
+ {
+ retval = PDActionFactory.createAction( c );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set an action to be performed when the page
+ * is closed. This action applies to the page being closed,
+ * and is executed before any other page opened.
+ *
+ * @param c The action to be performed.
+ */
+ public void setC( PDAction c )
+ {
+ actions.setItem( "C", c );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/action/package.html
new file mode 100644
index 0000000..63cbfc0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package represents actions that can be performed in a PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDAction.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDAction.java
new file mode 100644
index 0000000..21d40fc
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDAction.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.interactive.action.PDActionFactory;
+
+/**
+ * This represents an action that can be executed in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.1 $
+ */
+public abstract class PDAction implements COSObjectable
+{
+ /**
+ * The type of PDF object.
+ */
+ public static final String TYPE = "Action";
+
+ /**
+ * The action dictionary.
+ */
+ protected COSDictionary action;
+
+ /**
+ * Default constructor.
+ */
+ public PDAction()
+ {
+ action = new COSDictionary();
+ setType( TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDAction( COSDictionary a )
+ {
+ action = a;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return action;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return action;
+ }
+
+ /**
+ * This will get the type of PDF object that the actions dictionary describes.
+ * If present must be Action for an action dictionary.
+ *
+ * @return The Type of PDF object.
+ */
+ public String getType()
+ {
+ return action.getNameAsString( "Type" );
+ }
+
+ /**
+ * This will set the type of PDF object that the actions dictionary describes.
+ * If present must be Action for an action dictionary.
+ *
+ * @param type The new Type for the PDF object.
+ */
+ public void setType( String type )
+ {
+ action.setName( "Type", type );
+ }
+
+ /**
+ * This will get the type of action that the actions dictionary describes.
+ * If present, must be Action for an action dictionary.
+ *
+ * @return The S entry of actions dictionary.
+ */
+ public String getSubType()
+ {
+ return action.getNameAsString( "S" );
+ }
+
+ /**
+ * This will set the type of action that the actions dictionary describes.
+ * If present, must be Action for an action dictionary.
+ *
+ * @param s The new type of action.
+ */
+ public void setSubType( String s )
+ {
+ action.setName( "S", s );
+ }
+
+ /**
+ * This will get the next action, or sequence of actions, to be performed after this one.
+ * The value is either a single action dictionary or an array of action dictionaries
+ * to be performed in order.
+ *
+ * @return The Next action or sequence of actions.
+ */
+ public List getNext()
+ {
+ List retval = null;
+ COSBase next = action.getDictionaryObject( "Next" );
+ if( next instanceof COSDictionary )
+ {
+ PDAction pdAction = PDActionFactory.createAction( (COSDictionary) next );
+ retval = new COSArrayList(pdAction, next, action, "Next" );
+ }
+ else if( next instanceof COSArray )
+ {
+ COSArray array = (COSArray)next;
+ List actions = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ actions.add( PDActionFactory.createAction( (COSDictionary) array.getObject( i )));
+ }
+ retval = new COSArrayList( actions, array );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the next action, or sequence of actions, to be performed after this one.
+ * The value is either a single action dictionary or an array of action dictionaries
+ * to be performed in order.
+ *
+ * @param next The Next action or sequence of actions.
+ */
+ public void setNext( List next )
+ {
+ action.setItem( "Next", COSArrayList.converterToCOSArray( next ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionGoTo.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionGoTo.java
new file mode 100644
index 0000000..abf0ab5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionGoTo.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
+
+/**
+ * This represents a go-to action that can be executed in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.1 $
+ */
+public class PDActionGoTo extends PDAction
+{
+ /**
+ * This type of action this object represents.
+ */
+ public static final String SUB_TYPE = "GoTo";
+
+ /**
+ * Default constructor.
+ */
+ public PDActionGoTo()
+ {
+ super();
+ setSubType( SUB_TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDActionGoTo( COSDictionary a )
+ {
+ super( a );
+ }
+
+ /**
+ * This will get the destination to jump to.
+ *
+ * @return The D entry of the specific go-to action dictionary.
+ *
+ * @throws IOException If there is an error creating the destination.
+ */
+ public PDDestination getDestination() throws IOException
+ {
+ return PDDestination.create( getCOSDictionary().getDictionaryObject( "D" ) );
+ }
+
+ /**
+ * This will set the destination to jump to.
+ *
+ * @param d The destination.
+ */
+ public void setDestination( PDDestination d )
+ {
+ getCOSDictionary().setItem( "D", d );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java
new file mode 100644
index 0000000..3148dd3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.pdmodel.common.PDTextStream;
+
+/**
+ * This represents a JavaScript action.
+ *
+ * @author Michael Schwarzenberger (mi2kee@gmail.com)
+ * @version $Revision: 1.1 $
+ */
+public class PDActionJavaScript extends PDAction
+{
+ /**
+ * This type of action this object represents.
+ */
+ public static final String SUB_TYPE = "JavaScript";
+
+ /**
+ * Constructor #1.
+ */
+ public PDActionJavaScript()
+ {
+ super();
+ setSubType( SUB_TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param js Some javascript code.
+ */
+ public PDActionJavaScript( String js )
+ {
+ this();
+ setAction( js );
+ }
+
+ /**
+ * Constructor #2.
+ *
+ * @param a The action dictionary.
+ */
+ public PDActionJavaScript(COSDictionary a)
+ {
+ super(a);
+ }
+
+ /**
+ * @param sAction The JavaScript.
+ */
+ public void setAction(PDTextStream sAction)
+ {
+ action.setItem("JS", sAction);
+ }
+
+ /**
+ * @param sAction The JavaScript.
+ */
+ public void setAction(String sAction)
+ {
+ action.setString("JS", sAction);
+ }
+
+ /**
+ * @return The Javascript Code.
+ */
+ public PDTextStream getAction()
+ {
+ return PDTextStream.createTextStream( action.getDictionaryObject("JS") );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java
new file mode 100644
index 0000000..3c2ef74
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
+
+/**
+ * This represents a launch action that can be executed in a PDF document.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.3 $
+ */
+public class PDActionLaunch extends PDAction
+{
+
+ /**
+ * This type of action this object represents.
+ */
+ public static final String SUB_TYPE = "Launch";
+
+ /**
+ * Default constructor.
+ */
+ public PDActionLaunch()
+ {
+ super();
+ setSubType( SUB_TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDActionLaunch( COSDictionary a )
+ {
+ super( a );
+ }
+
+ /**
+ * This will get the application to be launched or the document
+ * to be opened or printed. It is required if none of the entries
+ * Win, Mac or Unix is present. If this entry is absent and the
+ * viewer application does not understand any of the alternative
+ * entries it should do nothing.
+ *
+ * @return The F entry of the specific launch action dictionary.
+ */
+ public PDFileSpecification getFile()
+ {
+ return PDFileSpecification.createFS( getCOSDictionary().getDictionaryObject( "F" ) );
+ }
+
+ /**
+ * This will set the application to be launched or the document
+ * to be opened or printed. It is required if none of the entries
+ * Win, Mac or Unix is present. If this entry is absent and the
+ * viewer application does not understand any of the alternative
+ * entries it should do nothing.
+ *
+ * @param fs The file specification.
+ */
+ public void setFile( PDFileSpecification fs )
+ {
+ getCOSDictionary().setItem( "F", fs );
+ }
+
+ /**
+ * This will get a dictionary containing Windows-specific launch parameters.
+ *
+ * @return The Win entry of of the specific launch action dictionary.
+ */
+ public PDWindowsLaunchParams getWinLaunchParams()
+ {
+ COSDictionary win = (COSDictionary)action.getDictionaryObject( "Win" );
+ PDWindowsLaunchParams retval = null;
+ if( win != null )
+ {
+ retval = new PDWindowsLaunchParams( win );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set a dictionary containing Windows-specific launch parameters.
+ *
+ * @param win The action to be performed.
+ */
+ public void setWinLaunchParams( PDWindowsLaunchParams win )
+ {
+ action.setItem( "Win", win );
+ }
+
+ /**
+ * This will get the file name to be launched or the document to be opened
+ * or printed, in standard Windows pathname format. If the name string includes
+ * a backslash character (\), the backslash must itself be preceded by a backslash.
+ * This value must be a single string; it is not a file specification.
+ *
+ * @return The F entry of the specific Windows launch parameter dictionary.
+ */
+ public String getF()
+ {
+ return action.getString( "F" );
+ }
+
+ /**
+ * This will set the file name to be launched or the document to be opened
+ * or printed, in standard Windows pathname format. If the name string includes
+ * a backslash character (\), the backslash must itself be preceded by a backslash.
+ * This value must be a single string; it is not a file specification.
+ *
+ * @param f The file name to be launched.
+ */
+ public void setF( String f )
+ {
+ action.setString( "F", f );
+ }
+
+ /**
+ * This will get the string specifying the default directory in standard DOS syntax.
+ *
+ * @return The D entry of the specific Windows launch parameter dictionary.
+ */
+ public String getD()
+ {
+ return action.getString( "D" );
+ }
+
+ /**
+ * This will set the string specifying the default directory in standard DOS syntax.
+ *
+ * @param d The default directory.
+ */
+ public void setD( String d )
+ {
+ action.setString( "D", d );
+ }
+
+ /**
+ * This will get the string specifying the operation to perform:
+ * open to open a document
+ * print to print a document
+ * If the F entry designates an application instead of a document, this entry
+ * is ignored and the application is launched. Default value: open.
+ *
+ * @return The O entry of the specific Windows launch parameter dictionary.
+ */
+ public String getO()
+ {
+ return action.getString( "O" );
+ }
+
+ /**
+ * This will set the string specifying the operation to perform:
+ * open to open a document
+ * print to print a document
+ * If the F entry designates an application instead of a document, this entry
+ * is ignored and the application is launched. Default value: open.
+ *
+ * @param o The operation to perform.
+ */
+ public void setO( String o )
+ {
+ action.setString( "O", o );
+ }
+
+ /**
+ * This will get a parameter string to be passed to the application designated by the F entry.
+ * This entry should be omitted if F designates a document.
+ *
+ * @return The P entry of the specific Windows launch parameter dictionary.
+ */
+ public String getP()
+ {
+ return action.getString( "P" );
+ }
+
+ /**
+ * This will set a parameter string to be passed to the application designated by the F entry.
+ * This entry should be omitted if F designates a document.
+ *
+ * @param p The parameter string.
+ */
+ public void setP( String p )
+ {
+ action.setString( "P", p );
+ }
+
+ /**
+ * This will specify whether to open the destination document in a new window.
+ * If this flag is false, the destination document will replace the current
+ * document in the same window. If this entry is absent, the viewer application
+ * should behave in accordance with the current user preference. This entry is
+ * ignored if the file designated by the F entry is not a PDF document.
+ *
+ * @return A flag specifying whether to open the destination document in a new window.
+ */
+ public boolean shouldOpenInNewWindow()
+ {
+ return action.getBoolean( "NewWindow", true );
+ }
+
+ /**
+ * This will specify the destination document to open in a new window.
+ *
+ * @param value The flag value.
+ */
+ public void setOpenInNewWindow( boolean value )
+ {
+ action.setBoolean( "NewWindow", value );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java
new file mode 100644
index 0000000..73dbc6c
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
+
+/**
+ * This represents a remote go-to action that can be executed in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.2 $
+ */
+public class PDActionRemoteGoTo extends PDAction
+{
+ /**
+ * This type of action this object represents.
+ */
+ public static final String SUB_TYPE = "GoToR";
+
+ /**
+ * Default constructor.
+ */
+ public PDActionRemoteGoTo()
+ {
+ action = new COSDictionary();
+ setSubType( SUB_TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDActionRemoteGoTo( COSDictionary a )
+ {
+ super( a );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return action;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return action;
+ }
+
+ /**
+ * This will get the type of action that the actions dictionary describes.
+ * It must be GoToR for a remote go-to action.
+ *
+ * @return The S entry of the specific remote go-to action dictionary.
+ */
+ public String getS()
+ {
+ return action.getNameAsString( "S" );
+ }
+
+ /**
+ * This will set the type of action that the actions dictionary describes.
+ * It must be GoToR for a remote go-to action.
+ *
+ * @param s The remote go-to action.
+ */
+ public void setS( String s )
+ {
+ action.setName( "S", s );
+ }
+
+ /**
+ * This will get the file in which the destination is located.
+ *
+ * @return The F entry of the specific remote go-to action dictionary.
+ */
+ public PDFileSpecification getFile()
+ {
+ return PDFileSpecification.createFS( action.getDictionaryObject( "F" ) );
+ }
+
+ /**
+ * This will set the file in which the destination is located.
+ *
+ * @param fs The file specification.
+ */
+ public void setFile( PDFileSpecification fs )
+ {
+ action.setItem( "F", fs );
+ }
+
+ /**
+ * This will get the destination to jump to.
+ * If the value is an array defining an explicit destination,
+ * its first element must be a page number within the remote
+ * document rather than an indirect reference to a page object
+ * in the current document. The first page is numbered 0.
+ *
+ * @return The D entry of the specific remote go-to action dictionary.
+ */
+
+ // Array or String.
+ public COSBase getD()
+ {
+ return action.getDictionaryObject( "D" );
+ }
+
+ /**
+ * This will set the destination to jump to.
+ * If the value is an array defining an explicit destination,
+ * its first element must be a page number within the remote
+ * document rather than an indirect reference to a page object
+ * in the current document. The first page is numbered 0.
+ *
+ * @param d The destination.
+ */
+
+ // In case the value is an array.
+ public void setD( COSBase d )
+ {
+ action.setItem( "D", d );
+ }
+
+ /**
+ * This will specify whether to open the destination document in a new window.
+ * If this flag is false, the destination document will replace the current
+ * document in the same window. If this entry is absent, the viewer application
+ * should behave in accordance with the current user preference.
+ *
+ * @return A flag specifying whether to open the destination document in a new window.
+ */
+ public boolean shouldOpenInNewWindow()
+ {
+ return action.getBoolean( "NewWindow", true );
+ }
+
+ /**
+ * This will specify the destination document to open in a new window.
+ *
+ * @param value The flag value.
+ */
+ public void setOpenInNewWindow( boolean value )
+ {
+ action.setBoolean( "NewWindow", value );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionURI.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionURI.java
new file mode 100644
index 0000000..b98495d
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDActionURI.java
@@ -0,0 +1,183 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This represents a URI action that can be executed in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @author Panagiotis Toumasis (ptoumasis@mail.gr)
+ * @version $Revision: 1.2 $
+ */
+public class PDActionURI extends PDAction
+{
+ /**
+ * This type of action this object represents.
+ */
+ public static final String SUB_TYPE = "URI";
+
+ /**
+ * Default constructor.
+ */
+ public PDActionURI()
+ {
+ action = new COSDictionary();
+ setSubType( SUB_TYPE );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param a The action dictionary.
+ */
+ public PDActionURI( COSDictionary a )
+ {
+ super( a );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return action;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return action;
+ }
+
+ /**
+ * This will get the type of action that the actions dictionary describes.
+ * It must be URI for a URI action.
+ *
+ * @return The S entry of the specific URI action dictionary.
+ */
+ public String getS()
+ {
+ return action.getNameAsString( "S" );
+ }
+
+ /**
+ * This will set the type of action that the actions dictionary describes.
+ * It must be URI for a URI action.
+ *
+ * @param s The URI action.
+ */
+ public void setS( String s )
+ {
+ action.setName( "S", s );
+ }
+
+ /**
+ * This will get the uniform resource identifier to resolve, encoded in 7-bit ASCII.
+ *
+ * @return The URI entry of the specific URI action dictionary.
+ */
+ public String getURI()
+ {
+ return action.getString( "URI" );
+ }
+
+ /**
+ * This will set the uniform resource identifier to resolve, encoded in 7-bit ASCII.
+ *
+ * @param uri The uniform resource identifier.
+ */
+ public void setURI( String uri )
+ {
+ action.setString( "URI", uri );
+ }
+
+ /**
+ * This will specify whether to track the mouse position when the URI is resolved.
+ * Default value: false.
+ * This entry applies only to actions triggered by the user's clicking an annotation;
+ * it is ignored for actions associated with outline items or with a document's OpenAction entry.
+ *
+ * @return A flag specifying whether to track the mouse position when the URI is resolved.
+ */
+ public boolean shouldTrackMousePosition()
+ {
+ return action.getBoolean( "MousePosition", true );
+ }
+
+ /**
+ * This will specify whether to track the mouse position when the URI is resolved.
+ *
+ * @param value The flag value.
+ */
+ public void setTrackMousePosition( boolean value )
+ {
+ action.setBoolean( "MousePosition", value );
+ }
+
+ /**
+ * This will get the base URI to be used in resolving relative URI references.
+ * URI actions within the document may specify URIs in partial form, to be interpreted
+ * relative to this base address. If no base URI is specified, such partial URIs
+ * will be interpreted relative to the location of the document itself.
+ * The use of this entry is parallel to that of the body element &lt;BASE&gt;, as described
+ * in the HTML 4.01 Specification.
+ *
+ * @return The URI entry of the specific URI dictionary.
+ */
+ public String getBase()
+ {
+ return action.getString( "Base" );
+ }
+
+ /**
+ * This will set the base URI to be used in resolving relative URI references.
+ * URI actions within the document may specify URIs in partial form, to be interpreted
+ * relative to this base address. If no base URI is specified, such partial URIs
+ * will be interpreted relative to the location of the document itself.
+ * The use of this entry is parallel to that of the body element &lt;BASE&gt;, as described
+ * in the HTML 4.01 Specification.
+ *
+ * @param base The the base URI to be used.
+ */
+ public void setBase( String base )
+ {
+ action.setString( "Base", base );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java
new file mode 100644
index 0000000..7af4ff2
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.action.type;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * Launch paramaters for the windows OS.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDWindowsLaunchParams implements COSObjectable
+{
+ /**
+ * The open operation for the launch.
+ */
+ public static final String OPERATION_OPEN = "open";
+ /**
+ * The print operation for the lanuch.
+ */
+ public static final String OPERATION_PRINT = "print";
+
+ /**
+ * The params dictionary.
+ */
+ protected COSDictionary params;
+
+ /**
+ * Default constructor.
+ */
+ public PDWindowsLaunchParams()
+ {
+ params = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param p The params dictionary.
+ */
+ public PDWindowsLaunchParams( COSDictionary p )
+ {
+ params = p;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return params;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return params;
+ }
+
+ /**
+ * The file to launch.
+ *
+ * @return The executable/document to launch.
+ */
+ public String getFilename()
+ {
+ return params.getString( "F" );
+ }
+
+ /**
+ * Set the file to launch.
+ *
+ * @param file The executable/document to launch.
+ */
+ public void setFilename( String file )
+ {
+ params.setString( "F", file );
+ }
+
+ /**
+ * The dir to launch from.
+ *
+ * @return The dir of the executable/document to launch.
+ */
+ public String getDirectory()
+ {
+ return params.getString( "D" );
+ }
+
+ /**
+ * Set the dir to launch from.
+ *
+ * @param dir The dir of the executable/document to launch.
+ */
+ public void setDirectory( String dir )
+ {
+ params.setString( "D", dir );
+ }
+
+ /**
+ * Get the operation to perform on the file. This method will not return null,
+ * OPERATION_OPEN is the default.
+ *
+ * @return The operation to perform for the file.
+ * @see PDWindowsLaunchParams#OPERATION_OPEN
+ * @see PDWindowsLaunchParams#OPERATION_PRINT
+ */
+ public String getOperation()
+ {
+ return params.getString( "O", OPERATION_OPEN );
+ }
+
+ /**
+ * Set the operation to perform..
+ *
+ * @param op The operation to perform on the file.
+ */
+ public void setOperation( String op )
+ {
+ params.setString( "D", op );
+ }
+
+ /**
+ * A parameter to pass the executable.
+ *
+ * @return The parameter to pass the executable.
+ */
+ public String getExecuteParam()
+ {
+ return params.getString( "P" );
+ }
+
+ /**
+ * Set the parameter to pass the executable.
+ *
+ * @param param The parameter for the executable.
+ */
+ public void setExecuteParam( String param )
+ {
+ params.setString( "P", param );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/action/type/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/package.html
new file mode 100644
index 0000000..f0db5c3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/action/type/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains all of the available PDF action types.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java
new file mode 100644
index 0000000..4d245c0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java
@@ -0,0 +1,503 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.annotation;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.interactive.action.PDAdditionalActions;
+import org.pdfbox.util.BitFlagHelper;
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This class represents a PDF annotation.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.10 $
+ */
+public abstract class PDAnnotation implements COSObjectable
+{
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_INVISIBLE = 1 << 0;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_HIDDEN = 1 << 1;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_PRINTED = 1 << 2;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_NO_ZOOM = 1 << 3;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_NO_ROTATE = 1 << 4;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_NO_VIEW = 1 << 5;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_READ_ONLY = 1 << 6;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_LOCKED = 1 << 7;
+ /**
+ * An annotation flag.
+ */
+ public static final int FLAG_TOGGLE_NO_VIEW = 1 << 8;
+
+
+
+ private COSDictionary dictionary;
+
+ /**
+ * Create the correct annotation from the base COS object.
+ *
+ * @param base The COS object that is the annotation.
+ * @return The correctly typed annotation object.
+ * @throws IOException If there is an error while creating the annotation.
+ */
+ public static PDAnnotation createAnnotation( COSBase base ) throws IOException
+ {
+ PDAnnotation annot = null;
+ if( base instanceof COSDictionary )
+ {
+ COSDictionary annotDic = (COSDictionary)base;
+ String subtype = annotDic.getString( COSName.SUBTYPE );
+ if( subtype.equals( PDAnnotationRubberStamp.SUB_TYPE ) )
+ {
+ annot = new PDAnnotationRubberStamp(annotDic);
+ }
+ else
+ {
+ annot = new PDAnnotationUnknown( annotDic );
+ }
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown annotation type " + base );
+ }
+
+ return annot;
+ }
+
+ /**
+ * Constructor.
+ */
+ public PDAnnotation()
+ {
+ dictionary = new COSDictionary();
+ dictionary.setItem( COSName.TYPE, COSName.getPDFName( "Annot" ) );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dict The annotations dictionary.
+ */
+ public PDAnnotation( COSDictionary dict )
+ {
+ dictionary = dict;
+ }
+
+ /**
+ * returns the dictionary.
+ * @return the dictionary
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ /**
+ * The annotation rectangle, defining the location of the annotation
+ * on the page in default user space units. This is usually required and should
+ * not return null on valid PDF documents. But where this is a parent form field
+ * with children, such as radio button collections then the rectangle will be null.
+ *
+ * @return The Rect value of this annotation.
+ */
+ public PDRectangle getRectangle()
+ {
+ COSArray rectArray = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "Rect" ) );
+ PDRectangle rectangle = null;
+ if( rectArray != null )
+ {
+ rectangle = new PDRectangle( rectArray );
+ }
+ return rectangle;
+ }
+
+ /**
+ * This will set the rectangle for this annotation.
+ *
+ * @param rectangle The new rectangle values.
+ */
+ public void setRectangle( PDRectangle rectangle )
+ {
+ dictionary.setItem( COSName.getPDFName( "Rect" ), rectangle.getCOSArray() );
+ }
+
+ /**
+ * This will get the flags for this field.
+ *
+ * @return flags The set of flags.
+ */
+ public int getAnnotationFlags()
+ {
+ return getDictionary().getInt( "F", 0 );
+ }
+
+ /**
+ * This will set the flags for this field.
+ *
+ * @param flags The new flags.
+ */
+ public void setAnnotationFlags( int flags )
+ {
+ getDictionary().setInt( "F", flags );
+ }
+
+ /**
+ * Interface method for COSObjectable.
+ *
+ * @return This object as a standard COS object.
+ */
+ public COSBase getCOSObject()
+ {
+ return getDictionary();
+ }
+
+ /**
+ * This will get the name of the current appearance stream if any.
+ *
+ * @return The name of the appearance stream.
+ */
+ public String getAppearanceStream()
+ {
+ String retval = null;
+ COSName name = (COSName)getDictionary().getDictionaryObject( COSName.getPDFName( "AS" ) );
+ if( name != null )
+ {
+ retval = name.getName();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the annotations appearance stream name.
+ *
+ * @param as The name of the appearance stream.
+ */
+ public void setAppearanceStream( String as )
+ {
+ if( as == null )
+ {
+ getDictionary().removeItem( COSName.getPDFName( "AS" ) );
+ }
+ else
+ {
+ getDictionary().setItem( COSName.getPDFName( "AS" ), COSName.getPDFName( as ) );
+ }
+ }
+
+ /**
+ * This will get the appearance dictionary associated with this annotation.
+ * This may return null.
+ *
+ * @return This annotations appearance.
+ */
+ public PDAppearanceDictionary getAppearance()
+ {
+ PDAppearanceDictionary ap = null;
+ COSDictionary apDic = (COSDictionary)dictionary.getDictionaryObject( COSName.getPDFName( "AP" ) );
+ if( apDic != null )
+ {
+ ap = new PDAppearanceDictionary( apDic );
+ }
+ return ap;
+ }
+
+ /**
+ * This will set the appearance associated with this annotation.
+ *
+ * @param appearance The appearance dictionary for this annotation.
+ */
+ public void setAppearance( PDAppearanceDictionary appearance )
+ {
+ COSDictionary ap = null;
+ if( appearance != null )
+ {
+ ap = appearance.getDictionary();
+ }
+ dictionary.setItem( COSName.getPDFName( "AP" ), ap );
+ }
+
+ /**
+ * Get the invisible flag.
+ *
+ * @return The invisible flag.
+ */
+ public boolean isInvisible()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_INVISIBLE );
+ }
+
+ /**
+ * Set the invisible flag.
+ *
+ * @param invisible The new invisible flag.
+ */
+ public void setInvisible( boolean invisible )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_INVISIBLE, invisible );
+ }
+
+ /**
+ * Get the hidden flag.
+ *
+ * @return The hidden flag.
+ */
+ public boolean isHidden()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_HIDDEN );
+ }
+
+ /**
+ * Set the hidden flag.
+ *
+ * @param hidden The new hidden flag.
+ */
+ public void setHidden( boolean hidden )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_HIDDEN, hidden );
+ }
+
+ /**
+ * Get the printed flag.
+ *
+ * @return The printed flag.
+ */
+ public boolean isPrinted()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_PRINTED );
+ }
+
+ /**
+ * Set the printed flag.
+ *
+ * @param printed The new printed flag.
+ */
+ public void setPrinted( boolean printed )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_PRINTED, printed );
+ }
+
+ /**
+ * Get the noZoom flag.
+ *
+ * @return The noZoom flag.
+ */
+ public boolean isNoZoom()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_NO_ZOOM );
+ }
+
+ /**
+ * Set the noZoom flag.
+ *
+ * @param noZoom The new noZoom flag.
+ */
+ public void setNoZoom( boolean noZoom )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_NO_ZOOM, noZoom );
+ }
+
+ /**
+ * Get the noRotate flag.
+ *
+ * @return The noRotate flag.
+ */
+ public boolean isNoRotate()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_NO_ROTATE );
+ }
+
+ /**
+ * Set the noRotate flag.
+ *
+ * @param noRotate The new noRotate flag.
+ */
+ public void setNoRotate( boolean noRotate )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_NO_ROTATE, noRotate );
+ }
+
+ /**
+ * Get the noView flag.
+ *
+ * @return The noView flag.
+ */
+ public boolean isNoView()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_NO_VIEW );
+ }
+
+ /**
+ * Set the noView flag.
+ *
+ * @param noView The new noView flag.
+ */
+ public void setNoView( boolean noView )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_NO_VIEW, noView );
+ }
+
+ /**
+ * Get the readOnly flag.
+ *
+ * @return The readOnly flag.
+ */
+ public boolean isReadOnly()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_READ_ONLY );
+ }
+
+ /**
+ * Set the readOnly flag.
+ *
+ * @param readOnly The new readOnly flag.
+ */
+ public void setReadOnly( boolean readOnly )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_READ_ONLY, readOnly );
+ }
+
+ /**
+ * Get the locked flag.
+ *
+ * @return The locked flag.
+ */
+ public boolean isLocked()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_LOCKED );
+ }
+
+ /**
+ * Set the locked flag.
+ *
+ * @param locked The new locked flag.
+ */
+ public void setLocked( boolean locked )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_LOCKED, locked );
+ }
+
+ /**
+ * Get the toggleNoView flag.
+ *
+ * @return The toggleNoView flag.
+ */
+ public boolean isToggleNoView()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_TOGGLE_NO_VIEW );
+ }
+
+ /**
+ * Set the toggleNoView flag.
+ *
+ * @param toggleNoView The new toggleNoView flag.
+ */
+ public void setToggleNoView( boolean toggleNoView )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "F", FLAG_TOGGLE_NO_VIEW, toggleNoView );
+ }
+
+ /**
+ * Get the additional actions for this field. This will return null
+ * if there are no additional actions for this field.
+ *
+ * @return The actions of the field.
+ */
+ public PDAdditionalActions getActions()
+ {
+ COSDictionary aa = (COSDictionary)dictionary.getDictionaryObject( "AA" );
+ PDAdditionalActions retval = null;
+ if( aa != null )
+ {
+ retval = new PDAdditionalActions( aa );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the actions of the field.
+ *
+ * @param actions The field actions.
+ */
+ public void setActions( PDAdditionalActions actions )
+ {
+ dictionary.setItem( "AA", actions );
+ }
+
+ /**
+ * Get the "contents" of the field.
+ *
+ * @return the value of the contents
+ */
+ public String getContents()
+ {
+ return dictionary.getString(COSName.CONTENTS);
+ }
+
+ /**
+ * Set the "contents" of the field.
+ *
+ * @param value the value of the contents.
+ */
+ public void setContents( String value)
+ {
+ dictionary.setString(COSName.CONTENTS, value);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java
new file mode 100644
index 0000000..089b19f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.annotation;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is the class that represents a widget.
+ *
+ * @author Paul King
+ * @version $Revision: 1.1 $
+ */
+public class PDAnnotationRubberStamp extends PDAnnotation
+{
+
+ /*
+ * The various values of the rubber stamp as defined in
+ * the PDF 1.6 reference Table 8.28
+ */
+
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_APPROVED = "Approved";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_EXPERIMENTAL = "Experimental";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_NOT_APPROVED = "NotApproved";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_AS_IS = "AsIs";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_EXPIRED = "Expired";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_NOT_FOR_PUBLIC_RELEASE = "NotForPublicRelease";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_FOR_PUBLIC_RELEASE = "ForPublicRelease";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_DRAFT = "Draft";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_FOR_COMMENT = "ForComment";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_TOP_SECRET = "TopSecret";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_DEPARTMENTAL = "Departmental";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_CONFIDENTIAL = "Confidential";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_FINAL = "Final";
+ /**
+ * Constant for the name of a rubber stamp.
+ */
+ public static final String NAME_SOLD = "Sold";
+
+ /**
+ * The type of annotation.
+ */
+ public static final String SUB_TYPE = "Stamp";
+
+ /**
+ * Constructor.
+ */
+ public PDAnnotationRubberStamp()
+ {
+ super();
+ getDictionary().setItem( COSName.SUBTYPE, COSName.getPDFName( SUB_TYPE ) );
+ }
+
+ /**
+ * Creates a Rubber Stamp annotation from a COSDictionary, expected to be
+ * a correct object definition.
+ *
+ * @param field the PDF objet to represent as a field.
+ */
+ public PDAnnotationRubberStamp(COSDictionary field)
+ {
+ super( field );
+ }
+
+ /**
+ * This will set the name (and hence appearance, AP taking precedence)
+ * For this annotation. See the NAME_XXX constants for valid values.
+ *
+ * @param name The name of the rubber stamp.
+ */
+ public void setName( String name )
+ {
+ getDictionary().setName(COSName.NAME, name);
+ }
+
+ /**
+ * This will retrieve the name (and hence appearance, AP taking precedence)
+ * For this annotation. The default is DRAFT.
+ *
+ * @return The name of this rubber stamp, see the NAME_XXX constants.
+ */
+ public String getName()
+ {
+ return getDictionary().getNameAsString(COSName.NAME, NAME_DRAFT);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java
new file mode 100644
index 0000000..408f5fb
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.annotation;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is the class that represents an arbitary Unknown Annotation type.
+ *
+ * @author Paul King
+ * @version $Revision: 1.1 $
+ */
+public class PDAnnotationUnknown extends PDAnnotation
+{
+
+ /**
+ * Creates an arbitary annotation from a COSDictionary, expected to be
+ * a correct object definition for some sort of annotation.
+ *
+ * @param dic The dictionary which represents this Annotation.
+ */
+ public PDAnnotationUnknown(COSDictionary dic)
+ {
+ super( dic );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java
new file mode 100644
index 0000000..5faf983
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.annotation;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+/**
+ * This is the class that represents a widget.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDAnnotationWidget extends PDAnnotation
+{
+
+ /**
+ * Constructor.
+ */
+ public PDAnnotationWidget()
+ {
+ super();
+ getDictionary().setItem( COSName.SUBTYPE, COSName.getPDFName( "Widget" ) );
+ }
+
+
+ /**
+ * Creates a PDWidget from a COSDictionary, expected to be
+ * a correct object definition for a field in PDF.
+ *
+ * @param field the PDF objet to represent as a field.
+ */
+ public PDAnnotationWidget(COSDictionary field)
+ {
+ super( field );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java
new file mode 100644
index 0000000..da3a182
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java
@@ -0,0 +1,245 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.annotation;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.COSDictionaryMap;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This class represents a PDF /AP entry the appearance dictionary.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDAppearanceDictionary implements COSObjectable
+{
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ */
+ public PDAppearanceDictionary()
+ {
+ dictionary = new COSDictionary();
+ //the N entry is required.
+ dictionary.setItem( COSName.getPDFName( "N" ), new COSDictionary() );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dict The annotations dictionary.
+ */
+ public PDAppearanceDictionary( COSDictionary dict )
+ {
+ dictionary = dict;
+ }
+
+ /**
+ * returns the dictionary.
+ * @return the dictionary
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ /**
+ * returns the dictionary.
+ * @return the dictionary
+ */
+ public COSBase getCOSObject()
+ {
+ return dictionary;
+ }
+
+ /**
+ * This will return a list of appearances. In the case where there is
+ * only one appearance the map will contain one entry whose key is the string
+ * "default".
+ *
+ * @return A list of key(java.lang.String) value(PDAppearanceStream) pairs
+ */
+ public Map getNormalAppearance()
+ {
+ COSBase ap = dictionary.getDictionaryObject( COSName.getPDFName( "N" ) );
+ if( ap instanceof COSStream )
+ {
+ COSStream aux = (COSStream) ap;
+ ap = new COSDictionary();
+ ((COSDictionary)ap).setItem(COSName.getPDFName( "default" ), aux );
+ }
+ COSDictionary map = (COSDictionary)ap;
+ Map actuals = new HashMap();
+ Map retval = new COSDictionaryMap( actuals, map );
+ Iterator asNames = map.keyList().iterator();
+ while( asNames.hasNext() )
+ {
+ COSName asName = (COSName)asNames.next();
+ COSStream as = (COSStream)map.getDictionaryObject( asName );
+ actuals.put( asName.getName(), new PDAppearanceStream( as ) );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set a list of appearances. If you would like to set the single
+ * appearance then you should use the key "default", and when the PDF is written
+ * back to the filesystem then there will only be one stream.
+ *
+ * @param appearanceMap The updated map with the appearance.
+ */
+ public void setNormalAppearance( Map appearanceMap )
+ {
+ dictionary.setItem( COSName.getPDFName( "N" ), COSDictionaryMap.convert( appearanceMap ) );
+ }
+
+ /**
+ * This will set the normal appearance when there is only one appearance
+ * to be shown.
+ *
+ * @param ap The appearance stream to show.
+ */
+ public void setNormalAppearance( PDAppearanceStream ap )
+ {
+ dictionary.setItem( COSName.getPDFName( "N" ), ap.getStream() );
+ }
+
+ /**
+ * This will return a list of appearances. In the case where there is
+ * only one appearance the map will contain one entry whose key is the string
+ * "default". If there is no rollover appearance then the normal appearance
+ * will be returned. Which means that this method will never return null.
+ *
+ * @return A list of key(java.lang.String) value(PDAppearanceStream) pairs
+ */
+ public Map getRolloverAppearance()
+ {
+ Map retval = null;
+ COSBase ap = dictionary.getDictionaryObject( COSName.getPDFName( "R" ) );
+ if( ap == null )
+ {
+ retval = getNormalAppearance();
+ }
+ else
+ {
+ if( ap instanceof COSStream )
+ {
+ ap = new COSDictionary();
+ ((COSDictionary)ap).setItem(COSName.getPDFName( "default" ), ap );
+ }
+ COSDictionary map = (COSDictionary)ap;
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, map );
+ Iterator asNames = map.keyList().iterator();
+ while( asNames.hasNext() )
+ {
+ COSName asName = (COSName)asNames.next();
+ COSStream as = (COSStream)map.getDictionaryObject( asName );
+ actuals.put( asName.getName(), new PDAppearanceStream( as ) );
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set a list of appearances. If you would like to set the single
+ * appearance then you should use the key "default", and when the PDF is written
+ * back to the filesystem then there will only be one stream.
+ *
+ * @param appearanceMap The updated map with the appearance.
+ */
+ public void setRolloverAppearance( Map appearanceMap )
+ {
+ dictionary.setItem( COSName.getPDFName( "R" ), COSDictionaryMap.convert( appearanceMap ) );
+ }
+
+ /**
+ * This will return a list of appearances. In the case where there is
+ * only one appearance the map will contain one entry whose key is the string
+ * "default". If there is no rollover appearance then the normal appearance
+ * will be returned. Which means that this method will never return null.
+ *
+ * @return A list of key(java.lang.String) value(PDAppearanceStream) pairs
+ */
+ public Map getDownAppearance()
+ {
+ Map retval = null;
+ COSBase ap = dictionary.getDictionaryObject( COSName.getPDFName( "D" ) );
+ if( ap == null )
+ {
+ retval = getNormalAppearance();
+ }
+ else
+ {
+ if( ap instanceof COSStream )
+ {
+ ap = new COSDictionary();
+ ((COSDictionary)ap).setItem(COSName.getPDFName( "default" ), ap );
+ }
+ COSDictionary map = (COSDictionary)ap;
+ Map actuals = new HashMap();
+ retval = new COSDictionaryMap( actuals, map );
+ Iterator asNames = map.keyList().iterator();
+ while( asNames.hasNext() )
+ {
+ COSName asName = (COSName)asNames.next();
+ COSStream as = (COSStream)map.getDictionaryObject( asName );
+ actuals.put( asName.getName(), new PDAppearanceStream( as ) );
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set a list of appearances. If you would like to set the single
+ * appearance then you should use the key "default", and when the PDF is written
+ * back to the filesystem then there will only be one stream.
+ *
+ * @param appearanceMap The updated map with the appearance.
+ */
+ public void setDownAppearance( Map appearanceMap )
+ {
+ dictionary.setItem( COSName.getPDFName( "D" ), COSDictionaryMap.convert( appearanceMap ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java
new file mode 100644
index 0000000..ca3f4a4
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.annotation;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.pdmodel.PDResources;
+
+
+/**
+ * This class represents an appearance for an annotation.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDAppearanceStream implements COSObjectable
+{
+ private COSStream stream = null;
+
+
+ /**
+ * Constructor.
+ *
+ * @param s The cos stream for this appearance.
+ */
+ public PDAppearanceStream( COSStream s )
+ {
+ stream = s;
+ }
+
+ /**
+ * This will return the underlying stream.
+ *
+ * @return The wrapped stream.
+ */
+ public COSStream getStream()
+ {
+ return stream;
+ }
+
+ /**
+ * @see COSObjectable#getCOSObject()
+ */
+ public COSBase getCOSObject()
+ {
+ return stream;
+ }
+
+ /**
+ * Get the bounding box for this appearance. This may return null in which
+ * case the Rectangle from the annotation should be used.
+ *
+ * @return The bounding box for this appearance.
+ */
+ public PDRectangle getBoundingBox()
+ {
+ PDRectangle box = null;
+ COSArray bbox = (COSArray)stream.getDictionaryObject( COSName.getPDFName( "BBox" ) );
+ if( bbox != null )
+ {
+ box = new PDRectangle( bbox );
+ }
+ return box;
+ }
+
+ /**
+ * This will set the bounding box for this appearance stream.
+ *
+ * @param rectangle The new bounding box.
+ */
+ public void setBoundingBox( PDRectangle rectangle )
+ {
+ COSArray array = null;
+ if( rectangle != null )
+ {
+ array = rectangle.getCOSArray();
+ }
+ stream.setItem( COSName.getPDFName( "BBox" ), array );
+ }
+
+ /**
+ * This will get the resources for this appearance stream.
+ *
+ * @return The appearance stream resources.
+ */
+ public PDResources getResources()
+ {
+ PDResources retval = null;
+ COSDictionary dict = (COSDictionary)stream.getDictionaryObject( COSName.RESOURCES );
+ if( dict != null )
+ {
+ retval = new PDResources( dict );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the new resources.
+ *
+ * @param resources The new resources.
+ */
+ public void setResources( PDResources resources )
+ {
+ COSDictionary dict = null;
+ if( resources != null )
+ {
+ dict = resources.getCOSDictionary();
+ }
+ stream.setItem( COSName.RESOURCES, dict );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/annotation/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/package.html
new file mode 100644
index 0000000..4d9fdeb
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/annotation/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The annotation package contains classes that work with PDF annotation elements.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java b/src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
new file mode 100644
index 0000000..7ef1c1e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.digitalsignature;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents a digital signature that can be attached to a document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDSignature implements COSObjectable
+{
+ private COSDictionary sig;
+
+ /**
+ * Default constructor.
+ */
+ public PDSignature()
+ {
+ sig = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param s The signature dictionary.
+ */
+ public PDSignature( COSDictionary s )
+ {
+ sig = s;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return sig;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return sig;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/package.html
new file mode 100644
index 0000000..d9944e6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/digitalsignature/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The digitial signature library will manage signatures that are stored in the PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java
new file mode 100644
index 0000000..98a0345
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents a destination in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public abstract class PDDestination implements COSObjectable
+{
+
+ /**
+ * This will create a new destination depending on the type of COSBase
+ * that is passed in.
+ *
+ * @param base The base level object.
+ *
+ * @return A new destination.
+ *
+ * @throws IOException If the base cannot be converted to a Destination.
+ */
+ public static PDDestination create( COSBase base ) throws IOException
+ {
+ PDDestination retval = null;
+ if( base == null )
+ {
+ //this is ok, just return null.
+ }
+ else if( base instanceof COSArray && ((COSArray)base).size() > 0 )
+ {
+ COSArray array = (COSArray)base;
+ COSName type = (COSName)array.getObject( 1 );
+ String typeString = type.getName();
+ if( typeString.equals( PDPageFitDestination.TYPE ) ||
+ typeString.equals( PDPageFitDestination.TYPE_BOUNDED ))
+ {
+ retval = new PDPageFitDestination( array );
+ }
+ else if( typeString.equals( PDPageFitHeightDestination.TYPE ) ||
+ typeString.equals( PDPageFitHeightDestination.TYPE_BOUNDED ))
+ {
+ retval = new PDPageFitHeightDestination( array );
+ }
+ else if( typeString.equals( PDPageFitRectangleDestination.TYPE ) )
+ {
+ retval = new PDPageFitRectangleDestination( array );
+ }
+ else if( typeString.equals( PDPageFitWidthDestination.TYPE ) ||
+ typeString.equals( PDPageFitWidthDestination.TYPE_BOUNDED ))
+ {
+ retval = new PDPageFitWidthDestination( array );
+ }
+ else if( typeString.equals( PDPageXYZDestination.TYPE ) )
+ {
+ retval = new PDPageXYZDestination( array );
+ }
+ else
+ {
+ throw new IOException( "Unknown destination type:" + type );
+ }
+ }
+ else if( base instanceof COSString )
+ {
+ retval = new PDNamedDestination( (COSString)base );
+ }
+ else if( base instanceof COSName )
+ {
+ retval = new PDNamedDestination( (COSName)base );
+ }
+ else
+ {
+ throw new IOException( "Error: can't convert to Destination " + base );
+ }
+ return retval;
+ }
+
+ /**
+ * Return a string representation of this class.
+ *
+ * @return A human readable string.
+ */
+ public String toString()
+ {
+ return super.toString();
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java
new file mode 100644
index 0000000..11da8e7
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+/**
+ * This represents a destination to a page by referencing it with a name.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class PDNamedDestination extends PDDestination
+{
+ private COSBase namedDestination;
+
+ /**
+ * Constructor.
+ *
+ * @param dest The named destination.
+ */
+ public PDNamedDestination( COSString dest )
+ {
+ namedDestination = dest;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dest The named destination.
+ */
+ public PDNamedDestination( COSName dest )
+ {
+ namedDestination = dest;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public PDNamedDestination()
+ {
+ //default, so do nothing
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @param dest The named destination.
+ */
+ public PDNamedDestination( String dest )
+ {
+ namedDestination = new COSString( dest );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return namedDestination;
+ }
+
+ /**
+ * This will get the name of the destination.
+ *
+ * @return The name of the destination.
+ */
+ public String getNamedDestination()
+ {
+ String retval = null;
+ if( namedDestination instanceof COSString )
+ {
+ retval = ((COSString)namedDestination).getString();
+ }
+ else if( namedDestination instanceof COSName )
+ {
+ retval = ((COSName)namedDestination).getName();
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the named destination.
+ *
+ * @param dest The new named destination.
+ *
+ * @throws IOException If there is an error setting the named destination.
+ */
+ public void setNamedDestination( String dest ) throws IOException
+ {
+ if( namedDestination instanceof COSString )
+ {
+ COSString string = ((COSString)namedDestination);
+ string.reset();
+ string.append( dest.getBytes() );
+ }
+ else if( dest == null )
+ {
+ namedDestination = null;
+ }
+ else
+ {
+ namedDestination = new COSString( dest );
+ }
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java
new file mode 100644
index 0000000..74dbf9a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.PDPage;
+
+/**
+ * This represents a destination to a page, see subclasses for specific parameters.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public abstract class PDPageDestination extends PDDestination
+{
+ /**
+ * Storage for the page destination.
+ */
+ protected COSArray array;
+
+ /**
+ * Constructor to create empty page destination.
+ *
+ */
+ protected PDPageDestination()
+ {
+ array = new COSArray();
+ }
+
+ /**
+ * Constructor to create empty page destination.
+ *
+ * @param arr A page destination array.
+ */
+ protected PDPageDestination( COSArray arr )
+ {
+ array = arr;
+ }
+
+ /**
+ * This will get the page for this destination. A page destination
+ * can either reference a page or a page number(when doing a remote destination to
+ * another PDF). If this object is referencing by page number then this method will
+ * return null and getPageNumber should be used.
+ *
+ * @return The page for this destination.
+ */
+ public PDPage getPage()
+ {
+ PDPage retval = null;
+ if( array.size() > 0 )
+ {
+ COSBase page = array.getObject( 0 );
+ if( page instanceof COSDictionary )
+ {
+ retval = new PDPage( (COSDictionary)page );
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Set the page for this destination.
+ *
+ * @param page The page for the destination.
+ */
+ public void setPage( PDPage page )
+ {
+ array.set( 0, page );
+ }
+
+ /**
+ * This will get the page number for this destination. A page destination
+ * can either reference a page or a page number(when doing a remote destination to
+ * another PDF). If this object is referencing by page number then this method will
+ * return that number, otherwise -1 will be returned.
+ *
+ * @return The page number for this destination.
+ */
+ public int getPageNumber()
+ {
+ int retval = -1;
+ if( array.size() > 0 )
+ {
+ COSBase page = array.getObject( 0 );
+ if( page instanceof COSNumber )
+ {
+ retval = ((COSNumber)page).intValue();
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Set the page number for this destination.
+ *
+ * @param pageNumber The page for the destination.
+ */
+ public void setPageNumber( int pageNumber )
+ {
+ array.set( 0, pageNumber );
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return array;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSArray getCOSArray()
+ {
+ return array;
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java
new file mode 100644
index 0000000..8f46bbe
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import org.pdfbox.cos.COSArray;
+
+/**
+ * This represents a destination to a page and the page contents will be magnified to just
+ * fit on the screen.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDPageFitDestination extends PDPageDestination
+{
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE = "Fit";
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE_BOUNDED = "FitB";
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDPageFitDestination()
+ {
+ super();
+ array.growToSize(2);
+ array.setName( 1, TYPE );
+
+ }
+
+ /**
+ * Constructor from an existing destination array.
+ *
+ * @param arr The destination array.
+ */
+ public PDPageFitDestination( COSArray arr )
+ {
+ super( arr );
+ }
+
+ /**
+ * A flag indicating if this page destination should just fit bounding box of the PDF.
+ *
+ * @return true If the destination should fit just the bounding box.
+ */
+ public boolean fitBoundingBox()
+ {
+ return TYPE_BOUNDED.equals( array.getName( 1 ) );
+ }
+
+ /**
+ * Set if this page destination should just fit the bounding box. The default is false.
+ *
+ * @param fitBoundingBox A flag indicating if this should fit the bounding box.
+ */
+ public void setFitBoundingBox( boolean fitBoundingBox )
+ {
+ array.growToSize( 2 );
+ if( fitBoundingBox )
+ {
+ array.setName( 1, TYPE_BOUNDED );
+ }
+ else
+ {
+ array.setName( 1, TYPE );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java
new file mode 100644
index 0000000..64df612
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This represents a destination to a page at a x location and the height is magnified
+ * to just fit on the screen.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDPageFitHeightDestination extends PDPageDestination
+{
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE = "FitV";
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE_BOUNDED = "FitBV";
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDPageFitHeightDestination()
+ {
+ super();
+ array.growToSize(3);
+ array.setName( 1, TYPE );
+
+ }
+
+ /**
+ * Constructor from an existing destination array.
+ *
+ * @param arr The destination array.
+ */
+ public PDPageFitHeightDestination( COSArray arr )
+ {
+ super( arr );
+ }
+
+ /**
+ * Get the left x coordinate. A return value of -1 implies that the current x-coordinate
+ * will be used.
+ *
+ * @return The left x coordinate.
+ */
+ public int getLeft()
+ {
+ return array.getInt( 2 );
+ }
+
+ /**
+ * Set the left x-coordinate, a value of -1 implies that the current x-coordinate
+ * will be used.
+ * @param x The left x coordinate.
+ */
+ public void setLeft( int x )
+ {
+ array.growToSize( 3 );
+ if( x == -1 )
+ {
+ array.set( 2, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 2, x );
+ }
+ }
+
+ /**
+ * A flag indicating if this page destination should just fit bounding box of the PDF.
+ *
+ * @return true If the destination should fit just the bounding box.
+ */
+ public boolean fitBoundingBox()
+ {
+ return TYPE_BOUNDED.equals( array.getName( 1 ) );
+ }
+
+ /**
+ * Set if this page destination should just fit the bounding box. The default is false.
+ *
+ * @param fitBoundingBox A flag indicating if this should fit the bounding box.
+ */
+ public void setFitBoundingBox( boolean fitBoundingBox )
+ {
+ array.growToSize( 2 );
+ if( fitBoundingBox )
+ {
+ array.setName( 1, TYPE_BOUNDED );
+ }
+ else
+ {
+ array.setName( 1, TYPE );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java
new file mode 100644
index 0000000..00fc5f1
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java
@@ -0,0 +1,188 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This represents a destination to a page at a y location and the width is magnified
+ * to just fit on the screen.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDPageFitRectangleDestination extends PDPageDestination
+{
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE = "FitR";
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDPageFitRectangleDestination()
+ {
+ super();
+ array.growToSize(6);
+ array.setName( 1, TYPE );
+
+ }
+
+ /**
+ * Constructor from an existing destination array.
+ *
+ * @param arr The destination array.
+ */
+ public PDPageFitRectangleDestination( COSArray arr )
+ {
+ super( arr );
+ }
+
+ /**
+ * Get the left x coordinate. A return value of -1 implies that the current x-coordinate
+ * will be used.
+ *
+ * @return The left x coordinate.
+ */
+ public int getLeft()
+ {
+ return array.getInt( 2 );
+ }
+
+ /**
+ * Set the left x-coordinate, a value of -1 implies that the current x-coordinate
+ * will be used.
+ * @param x The left x coordinate.
+ */
+ public void setLeft( int x )
+ {
+ array.growToSize( 3 );
+ if( x == -1 )
+ {
+ array.set( 2, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 2, x );
+ }
+ }
+
+ /**
+ * Get the bottom y coordinate. A return value of -1 implies that the current y-coordinate
+ * will be used.
+ *
+ * @return The bottom y coordinate.
+ */
+ public int getBottom()
+ {
+ return array.getInt( 3 );
+ }
+
+ /**
+ * Set the bottom y-coordinate, a value of -1 implies that the current y-coordinate
+ * will be used.
+ * @param y The bottom y coordinate.
+ */
+ public void setBottom( int y )
+ {
+ array.growToSize( 6 );
+ if( y == -1 )
+ {
+ array.set( 3, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 3, y );
+ }
+ }
+
+ /**
+ * Get the right x coordinate. A return value of -1 implies that the current x-coordinate
+ * will be used.
+ *
+ * @return The right x coordinate.
+ */
+ public int getRight()
+ {
+ return array.getInt( 4 );
+ }
+
+ /**
+ * Set the right x-coordinate, a value of -1 implies that the current x-coordinate
+ * will be used.
+ * @param x The right x coordinate.
+ */
+ public void setRight( int x )
+ {
+ array.growToSize( 6 );
+ if( x == -1 )
+ {
+ array.set( 4, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 4, x );
+ }
+ }
+
+
+ /**
+ * Get the top y coordinate. A return value of -1 implies that the current y-coordinate
+ * will be used.
+ *
+ * @return The top y coordinate.
+ */
+ public int getTop()
+ {
+ return array.getInt( 5 );
+ }
+
+ /**
+ * Set the top y-coordinate, a value of -1 implies that the current y-coordinate
+ * will be used.
+ * @param y The top ycoordinate.
+ */
+ public void setTop( int y )
+ {
+ array.growToSize( 6 );
+ if( y == -1 )
+ {
+ array.set( 5, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 5, y );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java
new file mode 100644
index 0000000..ef7385f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This represents a destination to a page at a y location and the width is magnified
+ * to just fit on the screen.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDPageFitWidthDestination extends PDPageDestination
+{
+
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE = "FitH";
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE_BOUNDED = "FitBH";
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDPageFitWidthDestination()
+ {
+ super();
+ array.growToSize(3);
+ array.setName( 1, TYPE );
+
+ }
+
+ /**
+ * Constructor from an existing destination array.
+ *
+ * @param arr The destination array.
+ */
+ public PDPageFitWidthDestination( COSArray arr )
+ {
+ super( arr );
+ }
+
+
+ /**
+ * Get the top y coordinate. A return value of -1 implies that the current y-coordinate
+ * will be used.
+ *
+ * @return The top y coordinate.
+ */
+ public int getTop()
+ {
+ return array.getInt( 2 );
+ }
+
+ /**
+ * Set the top y-coordinate, a value of -1 implies that the current y-coordinate
+ * will be used.
+ * @param y The top ycoordinate.
+ */
+ public void setTop( int y )
+ {
+ array.growToSize( 3 );
+ if( y == -1 )
+ {
+ array.set( 2, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 2, y );
+ }
+ }
+
+ /**
+ * A flag indicating if this page destination should just fit bounding box of the PDF.
+ *
+ * @return true If the destination should fit just the bounding box.
+ */
+ public boolean fitBoundingBox()
+ {
+ return TYPE_BOUNDED.equals( array.getName( 1 ) );
+ }
+
+ /**
+ * Set if this page destination should just fit the bounding box. The default is false.
+ *
+ * @param fitBoundingBox A flag indicating if this should fit the bounding box.
+ */
+ public void setFitBoundingBox( boolean fitBoundingBox )
+ {
+ array.growToSize( 2 );
+ if( fitBoundingBox )
+ {
+ array.setName( 1, TYPE_BOUNDED );
+ }
+ else
+ {
+ array.setName( 1, TYPE );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java
new file mode 100644
index 0000000..543a33c
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java
@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.destination;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+
+/**
+ * This represents a destination to a page at an x,y coordinate with a zoom setting.
+ * The default x,y,z will be whatever is the current value in the viewer application and
+ * are not required.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDPageXYZDestination extends PDPageDestination
+{
+ /**
+ * The type of this destination.
+ */
+ protected static final String TYPE = "XYZ";
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDPageXYZDestination()
+ {
+ super();
+ array.growToSize(5);
+ array.setName( 1, TYPE );
+
+ }
+
+ /**
+ * Constructor from an existing destination array.
+ *
+ * @param arr The destination array.
+ */
+ public PDPageXYZDestination( COSArray arr )
+ {
+ super( arr );
+ }
+
+ /**
+ * Get the left x coordinate. A return value of -1 implies that the current x-coordinate
+ * will be used.
+ *
+ * @return The left x coordinate.
+ */
+ public int getLeft()
+ {
+ return array.getInt( 2 );
+ }
+
+ /**
+ * Set the left x-coordinate, a value of -1 implies that the current x-coordinate
+ * will be used.
+ * @param x The left x coordinate.
+ */
+ public void setLeft( int x )
+ {
+ array.growToSize( 3 );
+ if( x == -1 )
+ {
+ array.set( 2, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 2, x );
+ }
+ }
+
+ /**
+ * Get the top y coordinate. A return value of -1 implies that the current y-coordinate
+ * will be used.
+ *
+ * @return The top y coordinate.
+ */
+ public int getTop()
+ {
+ return array.getInt( 3 );
+ }
+
+ /**
+ * Set the top y-coordinate, a value of -1 implies that the current y-coordinate
+ * will be used.
+ * @param y The top ycoordinate.
+ */
+ public void setTop( int y )
+ {
+ array.growToSize( 4 );
+ if( y == -1 )
+ {
+ array.set( 3, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 3, y );
+ }
+ }
+
+ /**
+ * Get the zoom value. A return value of -1 implies that the current zoom
+ * will be used.
+ *
+ * @return The zoom value for the page.
+ */
+ public int getZoom()
+ {
+ return array.getInt( 4 );
+ }
+
+ /**
+ * Set the zoom value for the page, a value of -1 implies that the current zoom
+ * will be used.
+ * @param zoom The zoom value.
+ */
+ public void setZoom( int zoom )
+ {
+ array.growToSize( 5 );
+ if( zoom == -1 )
+ {
+ array.set( 4, (COSBase)null );
+ }
+ else
+ {
+ array.setInt( 4, zoom );
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html
new file mode 100644
index 0000000..2122b1d
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The destination package allows destinations into a pdf document to be specified.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java
new file mode 100644
index 0000000..157a36f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.outline;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This represents an outline in a pdf document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDDocumentOutline extends PDOutlineNode
+{
+
+ /**
+ * Default Constructor.
+ */
+ public PDDocumentOutline()
+ {
+ super();
+ node.setName( "Type", "Outlines" );
+ }
+
+ /**
+ * Constructor for an existing document outline.
+ *
+ * @param dic The storage dictionary.
+ */
+ public PDDocumentOutline( COSDictionary dic )
+ {
+ super( dic );
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java
new file mode 100644
index 0000000..648eb64
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java
@@ -0,0 +1,425 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.outline;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.exceptions.OutlineNotLocalException;
+import org.pdfbox.pdmodel.PDDestinationNameTreeNode;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentNameDictionary;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.pdfbox.pdmodel.interactive.action.type.PDAction;
+import org.pdfbox.pdmodel.interactive.action.type.PDActionGoTo;
+import org.pdfbox.pdmodel.interactive.action.PDActionFactory;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
+import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
+
+/**
+ * This represents an outline in a pdf document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDOutlineItem extends PDOutlineNode
+{
+
+ private static final int ITALIC_FLAG = 1;
+ private static final int BOLD_FLAG = 2;
+
+ /**
+ * Default Constructor.
+ */
+ public PDOutlineItem()
+ {
+ super();
+ }
+
+ /**
+ * Constructor for an existing outline item.
+ *
+ * @param dic The storage dictionary.
+ */
+ public PDOutlineItem( COSDictionary dic )
+ {
+ super( dic );
+ }
+
+ /**
+ * Insert a sibling after this node.
+ *
+ * @param item The item to insert.
+ */
+ public void insertSiblingAfter( PDOutlineItem item )
+ {
+ item.setParent( getParent() );
+ PDOutlineItem next = getNextSibling();
+ setNextSibling( item );
+ item.setPreviousSibling( this );
+ if( next != null )
+ {
+ item.setNextSibling( next );
+ next.setPreviousSibling( item );
+ }
+ updateParentOpenCount( 1 );
+ }
+
+ /**
+ * Return the previous sibling or null if there is no sibling.
+ *
+ * @return The previous sibling.
+ */
+ public PDOutlineItem getPreviousSibling()
+ {
+ PDOutlineItem last = null;
+ COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Prev" );
+ if( lastDic != null )
+ {
+ last = new PDOutlineItem( lastDic );
+ }
+ return last;
+ }
+
+ /**
+ * Set the previous sibling, this will be maintained by this class.
+ *
+ * @param outlineNode The new previous sibling.
+ */
+ protected void setPreviousSibling( PDOutlineNode outlineNode )
+ {
+ node.setItem( "Prev", outlineNode );
+ }
+
+ /**
+ * Return the next sibling or null if there is no next sibling.
+ *
+ * @return The next sibling.
+ */
+ public PDOutlineItem getNextSibling()
+ {
+ PDOutlineItem last = null;
+ COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Next" );
+ if( lastDic != null )
+ {
+ last = new PDOutlineItem( lastDic );
+ }
+ return last;
+ }
+
+ /**
+ * Set the next sibling, this will be maintained by this class.
+ *
+ * @param outlineNode The new next sibling.
+ */
+ protected void setNextSibling( PDOutlineNode outlineNode )
+ {
+ node.setItem( "Next", outlineNode );
+ }
+
+ /**
+ * Get the title of this node.
+ *
+ * @return The title of this node.
+ */
+ public String getTitle()
+ {
+ return node.getString( "Title" );
+ }
+
+ /**
+ * Set the title for this node.
+ *
+ * @param title The new title for this node.
+ */
+ public void setTitle( String title )
+ {
+ node.setString( "Title", title );
+ }
+
+ /**
+ * Get the page destination of this node.
+ *
+ * @return The page destination of this node.
+ * @throws IOException If there is an error creating the destination.
+ */
+ public PDDestination getDestination() throws IOException
+ {
+ return PDDestination.create( node.getDictionaryObject( "Dest" ) );
+ }
+
+ /**
+ * Set the page destination for this node.
+ *
+ * @param dest The new page destination for this node.
+ */
+ public void setDestination( PDDestination dest )
+ {
+ node.setItem( "Dest", dest );
+ }
+
+ /**
+ * A convenience method that will create an XYZ destination using only the defaults.
+ *
+ * @param page The page to refer to.
+ */
+ public void setDestination( PDPage page )
+ {
+ PDPageXYZDestination dest = null;
+ if( page != null )
+ {
+ dest = new PDPageXYZDestination();
+ dest.setPage( page );
+ }
+ setDestination( dest );
+ }
+
+ /**
+ * This method will attempt to find the page in this PDF document that this outline points to.
+ * If the outline does not point to anything then this method will return null. If the outline
+ * is an action that is not a GoTo action then this methods will throw the OutlineNotLocationException
+ *
+ * @param doc The document to get the page from.
+ *
+ * @return The page that this outline will go to when activated or null if it does not point to anything.
+ * @throws IOException If there is an error when trying to find the page.
+ */
+ public PDPage findDestinationPage( PDDocument doc ) throws IOException
+ {
+ PDPage page = null;
+ PDDestination rawDest = getDestination();
+ if( rawDest == null )
+ {
+ PDAction outlineAction = getAction();
+ if( outlineAction instanceof PDActionGoTo )
+ {
+ rawDest = ((PDActionGoTo)outlineAction).getDestination();
+ }
+ else if( outlineAction == null )
+ {
+ //if the outline action is null then this outline does not refer
+ //to anything and we will just return null.
+ }
+ else
+ {
+ throw new OutlineNotLocalException( "Error: Outline does not reference a local page." );
+ }
+ }
+
+ PDPageDestination pageDest = null;
+ if( rawDest instanceof PDNamedDestination )
+ {
+ //if we have a named destination we need to lookup the PDPageDestination
+ PDNamedDestination namedDest = (PDNamedDestination)rawDest;
+ PDDocumentNameDictionary namesDict = doc.getDocumentCatalog().getNames();
+ if( namesDict != null )
+ {
+ PDDestinationNameTreeNode destsTree = namesDict.getDests();
+ if( destsTree != null )
+ {
+ pageDest = (PDPageDestination)destsTree.getValue( namedDest.getNamedDestination() );
+ }
+ }
+ }
+ else if( rawDest instanceof PDPageDestination)
+ {
+ pageDest = (PDPageDestination) rawDest;
+ }
+ else if( rawDest == null )
+ {
+ //if the destination is null then we will simply return a null page.
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown destination type " + rawDest );
+ }
+
+ if( pageDest != null )
+ {
+ page = pageDest.getPage();
+ if( page == null )
+ {
+ int pageNumber = pageDest.getPageNumber();
+ if( pageNumber != -1 )
+ {
+ List allPages = doc.getDocumentCatalog().getAllPages();
+ page = (PDPage)allPages.get( pageNumber );
+ }
+ }
+ }
+
+ return page;
+ }
+
+ /**
+ * Get the action of this node.
+ *
+ * @return The action of this node.
+ */
+ public PDAction getAction()
+ {
+ return PDActionFactory.createAction( (COSDictionary)node.getDictionaryObject( "A" ) );
+ }
+
+ /**
+ * Set the action for this node.
+ *
+ * @param action The new action for this node.
+ */
+ public void setAction( PDAction action )
+ {
+ node.setItem( "A", action );
+ }
+
+ /**
+ * Get the structure element of this node.
+ *
+ * @return The structure element of this node.
+ */
+ public PDStructureElement getStructureElement()
+ {
+ PDStructureElement se = null;
+ COSDictionary dic = (COSDictionary)node.getDictionaryObject( "SE" );
+ if( dic != null )
+ {
+ se = new PDStructureElement( dic );
+ }
+ return se;
+ }
+
+ /**
+ * Set the structure element for this node.
+ *
+ * @param structureElement The new structure element for this node.
+ */
+ public void setAction( PDStructureElement structureElement )
+ {
+ node.setItem( "SE", structureElement );
+ }
+
+ /**
+ * Get the text color of this node. Default is black and this method
+ * will never return null.
+ *
+ * @return The structure element of this node.
+ */
+ public PDColorSpaceInstance getTextColor()
+ {
+ PDColorSpaceInstance retval = null;
+ COSArray csValues = (COSArray)node.getDictionaryObject( "C" );
+ if( csValues == null )
+ {
+ csValues = new COSArray();
+ csValues.growToSize( 3, new COSFloat( 0 ) );
+ node.setItem( "C", csValues );
+ }
+ retval = new PDColorSpaceInstance(csValues);
+ retval.setColorSpace( PDDeviceRGB.INSTANCE );
+ return retval;
+ }
+
+ /**
+ * Set the text color for this node.
+ *
+ * @param textColor The text color for this node.
+ */
+ public void setTextColor( PDColorSpaceInstance textColor )
+ {
+ node.setItem( "C", textColor.getCOSColorSpaceValue() );
+ }
+
+ /**
+ * A flag telling if the text should be italic.
+ *
+ * @return The italic flag.
+ */
+ public boolean isItalic()
+ {
+ return (node.getInt( "F", 0 ) & ITALIC_FLAG) == ITALIC_FLAG;
+ }
+
+ /**
+ * Set the italic property of the text.
+ *
+ * @param italic The new italic flag.
+ */
+ public void setItalic( boolean italic )
+ {
+ int f = node.getInt( "F", 0 );
+ if( italic )
+ {
+ f = f | ITALIC_FLAG;
+ }
+ else
+ {
+ f = f ^ ITALIC_FLAG;
+ }
+ node.setInt( "F", f );
+ }
+
+ /**
+ * A flag telling if the text should be bold.
+ *
+ * @return The bold flag.
+ */
+ public boolean isBold()
+ {
+ return (node.getInt( "F", 0 ) & BOLD_FLAG) == BOLD_FLAG;
+ }
+
+ /**
+ * Set the bold property of the text.
+ *
+ * @param bold The new bold flag.
+ */
+ public void setBold( boolean bold )
+ {
+ int f = node.getInt( "F", 0 );
+ if( bold )
+ {
+ f = f | BOLD_FLAG;
+ }
+ else
+ {
+ f = f ^ BOLD_FLAG;
+ }
+ node.setInt( "F", f );
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java
new file mode 100644
index 0000000..04cc45f
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java
@@ -0,0 +1,320 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.documentnavigation.outline;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This represents an node in an outline in a pdf document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDOutlineNode implements COSObjectable
+{
+ /**
+ * The dictionary for this node.
+ */
+ protected COSDictionary node;
+
+ /**
+ * Default Constructor.
+ */
+ public PDOutlineNode()
+ {
+ node = new COSDictionary();
+ }
+
+ /**
+ * Default Constructor.
+ *
+ * @param dict The dictionary storage.
+ */
+ public PDOutlineNode( COSDictionary dict)
+ {
+ node = dict;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return node;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSDictionary getCOSDictionary()
+ {
+ return node;
+ }
+
+ /**
+ * Get the parent of this object. This will either be a DocumentOutline or an OutlineItem.
+ *
+ * @return The parent of this object, or null if this is the document outline and there
+ * is no parent.
+ */
+ public PDOutlineNode getParent()
+ {
+ PDOutlineNode retval = null;
+ COSDictionary parent = (COSDictionary)node.getDictionaryObject( "Parent" );
+ if( parent != null )
+ {
+ if( parent.getDictionaryObject( "Parent") == null )
+ {
+ retval = new PDDocumentOutline( parent );
+ }
+ else
+ {
+ retval = new PDOutlineItem( parent );
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the parent of this object, this is maintained by these objects and should not
+ * be called by any clients of PDFBox code.
+ *
+ * @param parent The parent of this object.
+ */
+ protected void setParent( PDOutlineNode parent )
+ {
+ node.setItem( "Parent", parent );
+ }
+
+ /**
+ * append a child node to this node.
+ *
+ * @param outlineNode The node to add.
+ */
+ public void appendChild( PDOutlineItem outlineNode )
+ {
+ outlineNode.setParent( this );
+ if( getFirstChild() == null )
+ {
+ int currentOpenCount = getOpenCount();
+ setFirstChild( outlineNode );
+ //1 for the the item we are adding;
+ int numberOfOpenNodesWeAreAdding = 1;
+ if( outlineNode.isNodeOpen() )
+ {
+ numberOfOpenNodesWeAreAdding += outlineNode.getOpenCount();
+ }
+ if( isNodeOpen() )
+ {
+ setOpenCount( currentOpenCount + numberOfOpenNodesWeAreAdding );
+ }
+ else
+ {
+ setOpenCount( currentOpenCount - numberOfOpenNodesWeAreAdding );
+ }
+ updateParentOpenCount( numberOfOpenNodesWeAreAdding );
+ }
+ else
+ {
+ PDOutlineItem previousLastChild = getLastChild();
+ previousLastChild.insertSiblingAfter( outlineNode );
+ }
+ setLastChild( outlineNode );
+ }
+
+ /**
+ * Return the first child or null if there is no child.
+ *
+ * @return The first child.
+ */
+ public PDOutlineItem getFirstChild()
+ {
+ PDOutlineItem last = null;
+ COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "First" );
+ if( lastDic != null )
+ {
+ last = new PDOutlineItem( lastDic );
+ }
+ return last;
+ }
+
+ /**
+ * Set the first child, this will be maintained by this class.
+ *
+ * @param outlineNode The new first child.
+ */
+ protected void setFirstChild( PDOutlineNode outlineNode )
+ {
+ node.setItem( "First", outlineNode );
+ }
+
+ /**
+ * Return the last child or null if there is no child.
+ *
+ * @return The last child.
+ */
+ public PDOutlineItem getLastChild()
+ {
+ PDOutlineItem last = null;
+ COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Last" );
+ if( lastDic != null )
+ {
+ last = new PDOutlineItem( lastDic );
+ }
+ return last;
+ }
+
+ /**
+ * Set the last child, this will be maintained by this class.
+ *
+ * @param outlineNode The new last child.
+ */
+ protected void setLastChild( PDOutlineNode outlineNode )
+ {
+ node.setItem( "Last", outlineNode );
+ }
+
+ /**
+ * Get the number of open nodes. Or a negative number if this node
+ * is closed. See PDF Reference for more details. This value
+ * is updated as you append children and siblings.
+ *
+ * @return The Count attribute of the outline dictionary.
+ */
+ public int getOpenCount()
+ {
+ return node.getInt( "Count", 0 );
+ }
+
+ /**
+ * Set the open count. This number is automatically managed for you
+ * when you add items to the outline.
+ *
+ * @param openCount The new open cound.
+ */
+ protected void setOpenCount( int openCount )
+ {
+ node.setInt( "Count", openCount );
+ }
+
+ /**
+ * This will set this node to be open when it is shown in the viewer. By default, when
+ * a new node is created it will be closed.
+ * This will do nothing if the node is already open.
+ */
+ public void openNode()
+ {
+ //if the node is already open then do nothing.
+ if( !isNodeOpen() )
+ {
+ int openChildrenCount = 0;
+ PDOutlineItem currentChild = getFirstChild();
+ while( currentChild != null )
+ {
+ //first increase by one for the current child
+ openChildrenCount++;
+ //then increase by the number of open nodes the child has
+ if( currentChild.isNodeOpen() )
+ {
+ openChildrenCount += currentChild.getOpenCount();
+ }
+ currentChild = currentChild.getNextSibling();
+ }
+ setOpenCount( openChildrenCount );
+ updateParentOpenCount( openChildrenCount );
+ }
+ }
+
+ /**
+ * Close this node.
+ *
+ */
+ public void closeNode()
+ {
+ //if the node is already closed then do nothing.
+ if( isNodeOpen() )
+ {
+ int openCount = getOpenCount();
+ updateParentOpenCount( -openCount );
+ setOpenCount( -openCount );
+ }
+ }
+
+ /**
+ * Node is open if the open count is greater than zero.
+ * @return true if this node is open.
+ */
+ public boolean isNodeOpen()
+ {
+ return getOpenCount() > 0;
+ }
+
+ /**
+ * The count parameter needs to be updated when you add or remove elements to
+ * the outline. When you add an element at a lower level then you need to
+ * increase all of the parents.
+ *
+ * @param amount The amount to update by.
+ */
+ protected void updateParentOpenCount( int amount )
+ {
+ PDOutlineNode parent = getParent();
+ if( parent != null )
+ {
+ int currentCount = parent.getOpenCount();
+ //if the currentCount is negative or it is absent then
+ //we will treat it as negative. The default is to be negative.
+ boolean negative = currentCount < 0 ||
+ parent.getCOSDictionary().getDictionaryObject( "Count" ) == null;
+ currentCount = Math.abs( currentCount );
+ currentCount += amount;
+ if( negative )
+ {
+ currentCount = -currentCount;
+ }
+ parent.setOpenCount( currentCount );
+ //recursively call parent to update count, but the parents count is only
+ //updated if this is an open node
+ if( !negative )
+ {
+ parent.updateParentOpenCount( amount );
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html
new file mode 100644
index 0000000..4ec0032
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The outline package allows for a PDF outline(bookmarks) to be created.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/package.html
new file mode 100644
index 0000000..b210cf5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/documentnavigation/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+A package to allow access to document level navigation within a PDF document.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java
new file mode 100644
index 0000000..4fb576d
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java
@@ -0,0 +1,328 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.fdf.FDFDictionary;
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+import org.pdfbox.pdmodel.fdf.FDFCatalog;
+import org.pdfbox.pdmodel.fdf.FDFField;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class represents the acroform of a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+public class PDAcroForm
+{
+ private COSDictionary acroForm;
+ private PDDocument document;
+
+ private Map fieldCache;
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document that this form is part of.
+ */
+ public PDAcroForm( PDDocument doc )
+ {
+ document = doc;
+ acroForm = new COSDictionary();
+ COSArray fields = new COSArray();
+ acroForm.setItem( COSName.getPDFName( "Fields" ), fields );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document that this form is part of.
+ * @param form The existing acroForm.
+ */
+ public PDAcroForm( PDDocument doc, COSDictionary form )
+ {
+ document = doc;
+ acroForm = form;
+ }
+
+ /**
+ * This will get the document associated with this form.
+ *
+ * @return The PDF document.
+ */
+ public PDDocument getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * This will get the dictionary that this form wraps.
+ *
+ * @return The dictionary for this form.
+ */
+ public COSDictionary getDictionary()
+ {
+ return acroForm;
+ }
+
+ /**
+ * This method will import an entire FDF document into the PDF document
+ * that this acroform is part of.
+ *
+ * @param fdf The FDF document to import.
+ *
+ * @throws IOException If there is an error doing the import.
+ */
+ public void importFDF( FDFDocument fdf ) throws IOException
+ {
+ List fields = fdf.getCatalog().getFDF().getFields();
+ if( fields != null )
+ {
+ for( int i=0; i<fields.size(); i++ )
+ {
+ FDFField fdfField = (FDFField)fields.get( i );
+ PDField docField = getField( fdfField.getPartialFieldName() );
+ if( docField != null )
+ {
+ docField.importFDF( fdfField );
+ }
+ }
+ }
+ }
+
+ /**
+ * This will export all FDF form data.
+ *
+ * @return An FDF document used to export the document.
+ * @throws IOException If there is an error when exporting the document.
+ */
+ public FDFDocument exportFDF() throws IOException
+ {
+ FDFDocument fdf = new FDFDocument();
+ FDFCatalog catalog = fdf.getCatalog();
+ FDFDictionary fdfDict = new FDFDictionary();
+ catalog.setFDF( fdfDict );
+
+ List fdfFields = new ArrayList();
+ List fields = getFields();
+ Iterator fieldIter = fields.iterator();
+ while( fieldIter.hasNext() )
+ {
+ PDField docField = (PDField)fieldIter.next();
+ addFieldAndChildren( docField, fdfFields );
+ }
+ fdfDict.setID( document.getDocument().getDocumentID() );
+ if( fdfFields.size() > 0 )
+ {
+ fdfDict.setFields( fdfFields );
+ }
+ return fdf;
+ }
+
+ private void addFieldAndChildren( PDField docField, List fdfFields ) throws IOException
+ {
+ Object fieldValue = docField.getValue();
+ FDFField fdfField = new FDFField();
+ fdfField.setPartialFieldName( docField.getPartialName() );
+ fdfField.setValue( fieldValue );
+ List kids = docField.getKids();
+ List childFDFFields = new ArrayList();
+ if( kids != null )
+ {
+
+ for( int i=0; i<kids.size(); i++ )
+ {
+ addFieldAndChildren( (PDField)kids.get( i ), childFDFFields );
+ }
+ if( childFDFFields.size() > 0 )
+ {
+ fdfField.setKids( childFDFFields );
+ }
+ }
+ if( fieldValue != null || childFDFFields.size() > 0 )
+ {
+ fdfFields.add( fdfField );
+ }
+ }
+
+ /**
+ * This will return all of the fields in the document. The type
+ * will be a org.pdfbox.pdmodel.field.PDField.
+ *
+ * @return A list of all the fields.
+ * @throws IOException If there is an error while getting the list of fields.
+ */
+ public List getFields() throws IOException
+ {
+ COSArray fields =
+ (COSArray) acroForm.getDictionaryObject(
+ COSName.getPDFName("Fields"));
+
+ List retval = new ArrayList();
+ for (int i = 0; i < fields.size(); i++)
+ {
+ COSDictionary element = (COSDictionary) fields.getObject(i);
+ if (element != null)
+ {
+ PDField field = PDFieldFactory.createField( this, element );
+ if( field != null )
+ {
+ retval.add(field);
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will tell this form to cache the fields into a Map structure
+ * for fast access via the getField method. The default is false. You would
+ * want this to be false if you were changing the COSDictionary behind the scenes,
+ * otherwise setting this to true is acceptable.
+ *
+ * @param cache A boolean telling if we should cache the fields.
+ * @throws IOException If there is an error while caching the fields.
+ */
+ public void setCacheFields( boolean cache ) throws IOException
+ {
+ if( cache )
+ {
+ fieldCache = new HashMap();
+ List fields = getFields();
+ Iterator fieldIter = fields.iterator();
+ while( fieldIter.hasNext() )
+ {
+ PDField next = (PDField)fieldIter.next();
+ fieldCache.put( next.getFullyQualifiedName(), next );
+ }
+ }
+ else
+ {
+ fieldCache = null;
+ }
+ }
+
+ /**
+ * This will tell if this acro form is caching the fields.
+ *
+ * @return true if the fields are being cached.
+ */
+ public boolean isCachingFields()
+ {
+ return fieldCache != null;
+ }
+
+ /**
+ * This will get a field by name, possibly using the cache if setCache is true.
+ *
+ * @param name The name of the field to get.
+ *
+ * @return The field with that name of null if one was not found.
+ *
+ * @throws IOException If there is an error getting the field type.
+ */
+ public PDField getField( String name ) throws IOException
+ {
+ PDField retval = null;
+ if( fieldCache != null )
+ {
+ retval = (PDField)fieldCache.get( name );
+ }
+ else
+ {
+ COSArray fields =
+ (COSArray) acroForm.getDictionaryObject(
+ COSName.getPDFName("Fields"));
+
+ for (int i = 0; i < fields.size() && retval == null; i++)
+ {
+ COSDictionary element = (COSDictionary) fields.getObject(i);
+ if( element != null )
+ {
+ COSString fieldName =
+ (COSString)element.getDictionaryObject( COSName.getPDFName( "T" ) );
+ if( fieldName.getString().equals( name ) )
+ {
+ retval = PDFieldFactory.createField( this, element );
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the default resources for the acro form.
+ *
+ * @return The default resources.
+ */
+ public PDResources getDefaultResources()
+ {
+ PDResources retval = null;
+ COSDictionary dr = (COSDictionary)acroForm.getDictionaryObject( COSName.getPDFName( "DR" ) );
+ if( dr != null )
+ {
+ retval = new PDResources( dr );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the default resources for the acroform.
+ *
+ * @param dr The new default resources.
+ */
+ public void setDefaultResources( PDResources dr )
+ {
+ COSDictionary drDict = null;
+ if( dr != null )
+ {
+ drDict = dr.getCOSDictionary();
+ }
+ acroForm.setItem( COSName.getPDFName( "DR" ), drDict );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
new file mode 100644
index 0000000..ff18c86
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
@@ -0,0 +1,645 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdfparser.PDFStreamParser;
+import org.pdfbox.pdfwriter.ContentStreamWriter;
+
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDFontDescriptor;
+import org.pdfbox.pdmodel.font.PDSimpleFont;
+
+import org.pdfbox.pdmodel.interactive.action.PDAdditionalActions;
+import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * This one took me a while, but i'm proud to say that it handles
+ * the appearance of a textbox. This allows you to apply a value to
+ * a field in the document and handle the appearance so that the
+ * value is actually visible too.
+ * The problem was described by Ben Litchfield, the author of the
+ * example: org.pdfbox.examlpes.fdf.ImportFDF. So Ben, here is the
+ * solution.
+ *
+ * @author sug
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.17 $
+ */
+public class PDAppearance
+{
+ private PDVariableText parent;
+
+ private String value;
+ private COSString defaultAppearance;
+
+ private PDAcroForm acroForm;
+ private List widgets = new ArrayList();
+
+
+ /**
+ * Constructs a COSAppearnce from the given field.
+ *
+ * @param theAcroForm the acro form that this field is part of.
+ * @param field the field which you wish to control the appearance of
+ * @throws IOException If there is an error creating the appearance.
+ */
+ public PDAppearance( PDAcroForm theAcroForm, PDVariableText field ) throws IOException
+ {
+ acroForm = theAcroForm;
+ parent = field;
+
+ widgets = field.getKids();
+ if( widgets == null )
+ {
+ widgets = new ArrayList();
+ widgets.add( field.getWidget() );
+ }
+
+ defaultAppearance = getDefaultAppearance();
+
+
+ }
+
+ /**
+ * Returns the default apperance of a textbox. If the textbox
+ * does not have one, then it will be taken from the AcroForm.
+ * @return The DA element
+ */
+ private COSString getDefaultAppearance()
+ {
+
+ COSString dap = parent.getDefaultAppearance();
+ if (dap == null)
+ {
+ COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( "Kids" );
+ if( kids != null && kids.size() > 0 )
+ {
+ COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
+ dap = (COSString)firstKid.getDictionaryObject( "DA" );
+ }
+ if( dap == null )
+ {
+ dap = (COSString) acroForm.getDictionary().getDictionaryObject(COSName.getPDFName("DA"));
+ }
+ }
+ return dap;
+ }
+
+ private int getQ()
+ {
+ int q = parent.getQ();
+ if( parent.getDictionary().getDictionaryObject( "Q" ) == null )
+ {
+ COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( "Kids" );
+ if( kids != null && kids.size() > 0 )
+ {
+ COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
+ COSNumber qNum = (COSNumber)firstKid.getDictionaryObject( "Q" );
+ if( qNum != null )
+ {
+ q = qNum.intValue();
+ }
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Extracts the original appearance stream into a list of tokens.
+ *
+ * @return The tokens in the original appearance stream
+ */
+ private List getStreamTokens( PDAppearanceStream appearanceStream ) throws IOException
+ {
+ List tokens = null;
+ if( appearanceStream != null )
+ {
+ tokens = getStreamTokens( appearanceStream.getStream() );
+ }
+ return tokens;
+ }
+
+ private List getStreamTokens( COSString string ) throws IOException
+ {
+ PDFStreamParser parser;
+
+ List tokens = null;
+ if( string != null )
+ {
+ ByteArrayInputStream stream = new ByteArrayInputStream( string.getBytes() );
+ parser = new PDFStreamParser( stream, acroForm.getDocument().getDocument().getScratchFile() );
+ parser.parse();
+ tokens = parser.getTokens();
+ }
+ return tokens;
+ }
+
+ private List getStreamTokens( COSStream stream ) throws IOException
+ {
+ PDFStreamParser parser;
+
+ List tokens = null;
+ if( stream != null )
+ {
+ parser = new PDFStreamParser( stream );
+ parser.parse();
+ tokens = parser.getTokens();
+ }
+ return tokens;
+ }
+
+ /**
+ * Tests if the apperance stream already contains content.
+ *
+ * @return true if it contains any content
+ */
+ private boolean containsMarkedContent( List stream )
+ {
+ return stream.contains( PDFOperator.getOperator( "BMC" ) );
+ }
+
+ /**
+ * This is the public method for setting the appearance stream.
+ *
+ * @param apValue the String value which the apperance shoud represent
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public void setAppearanceValue(String apValue) throws IOException
+ {
+ // MulitLine check and set
+ if ( parent.isMultiline() && apValue.indexOf('\n') != -1 )
+ {
+ apValue = convertToMultiLine( apValue );
+ }
+
+ value = apValue;
+ Iterator widgetIter = widgets.iterator();
+ while( widgetIter.hasNext() )
+ {
+ Object next = widgetIter.next();
+ PDAnnotationWidget widget = null;
+ if( next instanceof PDField )
+ {
+ widget = ((PDField)next).getWidget();
+ }
+ else
+ {
+ widget = (PDAnnotationWidget)next;
+ }
+ PDAdditionalActions actions = widget.getActions();
+ if( actions != null &&
+ actions.getF() != null &&
+ widget.getDictionary().getDictionaryObject( "AP" ) ==null)
+ {
+ //do nothing because the field will be formatted by acrobat
+ //when it is opened. See FreedomExpressions.pdf for an example of this.
+ }
+ else
+ {
+
+ PDAppearanceDictionary appearance = widget.getAppearance();
+ if( appearance == null )
+ {
+ appearance = new PDAppearanceDictionary();
+ widget.setAppearance( appearance );
+ }
+
+ Map normalAppearance = appearance.getNormalAppearance();
+ PDAppearanceStream appearanceStream = (PDAppearanceStream)normalAppearance.get( "default" );
+ if( appearanceStream == null )
+ {
+ COSStream cosStream = new COSStream( acroForm.getDocument().getDocument().getScratchFile() );
+ appearanceStream = new PDAppearanceStream( cosStream );
+ appearanceStream.setBoundingBox( widget.getRectangle().createRetranslatedRectangle() );
+ appearance.setNormalAppearance( appearanceStream );
+ }
+
+ List tokens = getStreamTokens( appearanceStream );
+ List daTokens = getStreamTokens( getDefaultAppearance() );
+ PDFont pdFont = getFontAndUpdateResources( tokens, appearanceStream );
+
+ if (!containsMarkedContent( tokens ))
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+ //BJL 9/25/2004 Must prepend existing stream
+ //because it might have operators to draw things like
+ //rectangles and such
+ ContentStreamWriter writer = new ContentStreamWriter( output );
+ writer.writeTokens( tokens );
+
+ output.write( " /Tx BMC\n".getBytes() );
+ insertGeneratedAppearance( widget, output, pdFont, tokens, appearanceStream );
+ output.write( " EMC".getBytes() );
+ writeToStream( output.toByteArray(), appearanceStream );
+ }
+ else
+ {
+ if( tokens != null )
+ {
+ if( daTokens != null )
+ {
+ int bmcIndex = tokens.indexOf( PDFOperator.getOperator( "BMC" ));
+ int emcIndex = tokens.indexOf( PDFOperator.getOperator( "EMC" ));
+ if( bmcIndex != -1 && emcIndex != -1 &&
+ emcIndex == bmcIndex+1 )
+ {
+ //if the EMC immediately follows the BMC index then should
+ //insert the daTokens inbetween the two markers.
+ tokens.addAll( emcIndex, daTokens );
+ }
+ }
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ ContentStreamWriter writer = new ContentStreamWriter( output );
+ float fontSize = calculateFontSize( pdFont, appearanceStream.getBoundingBox(), tokens, null );
+ boolean foundString = false;
+ for( int i=0; i<tokens.size(); i++ )
+ {
+ if( tokens.get( i ) instanceof COSString )
+ {
+ foundString = true;
+ COSString drawnString =((COSString)tokens.get(i));
+ drawnString.reset();
+ drawnString.append( apValue.getBytes() );
+ }
+ }
+ int setFontIndex = tokens.indexOf( PDFOperator.getOperator( "Tf" ));
+ tokens.set( setFontIndex-1, new COSFloat( fontSize ) );
+ if( foundString )
+ {
+ writer.writeTokens( tokens );
+ }
+ else
+ {
+ int bmcIndex = tokens.indexOf( PDFOperator.getOperator( "BMC" ) );
+ int emcIndex = tokens.indexOf( PDFOperator.getOperator( "EMC" ) );
+
+ if( bmcIndex != -1 )
+ {
+ writer.writeTokens( tokens, 0, bmcIndex+1 );
+ }
+ else
+ {
+ writer.writeTokens( tokens );
+ }
+ output.write( "\n".getBytes() );
+ insertGeneratedAppearance( widget, output,
+ pdFont, tokens, appearanceStream );
+ if( emcIndex != -1 )
+ {
+ writer.writeTokens( tokens, emcIndex, tokens.size() );
+ }
+ }
+ writeToStream( output.toByteArray(), appearanceStream );
+ }
+ else
+ {
+ //hmm?
+ }
+ }
+ }
+ }
+ }
+
+ private void insertGeneratedAppearance( PDAnnotationWidget fieldWidget, OutputStream output,
+ PDFont pdFont, List tokens, PDAppearanceStream appearanceStream ) throws IOException
+ {
+ PrintWriter printWriter = new PrintWriter( output, true );
+ float fontSize = 0.0f;
+ PDRectangle boundingBox = null;
+ boundingBox = appearanceStream.getBoundingBox();
+ if( boundingBox == null )
+ {
+ boundingBox = fieldWidget.getRectangle().createRetranslatedRectangle();
+ }
+ printWriter.println( "BT" );
+ if( defaultAppearance != null )
+ {
+ String daString = defaultAppearance.getString();
+ PDFStreamParser daParser = new PDFStreamParser(new ByteArrayInputStream( daString.getBytes() ), null );
+ daParser.parse();
+ List daTokens = daParser.getTokens();
+ fontSize = calculateFontSize( pdFont, boundingBox, tokens, daTokens );
+ int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) );
+ if(fontIndex != -1 )
+ {
+ daTokens.set( fontIndex-1, new COSFloat( fontSize ) );
+ }
+ ContentStreamWriter daWriter = new ContentStreamWriter(output);
+ daWriter.writeTokens( daTokens );
+ }
+ printWriter.println( getTextPosition( boundingBox, pdFont, fontSize, tokens ) );
+ int q = getQ();
+ if( q == PDTextbox.QUADDING_LEFT )
+ {
+ //do nothing because left is default
+ }
+ else if( q == PDTextbox.QUADDING_CENTERED ||
+ q == PDTextbox.QUADDING_RIGHT )
+ {
+ float fieldWidth = boundingBox.getWidth();
+ float stringWidth = (pdFont.getStringWidth( value )/1000)*fontSize;
+ float adjustAmount = fieldWidth - stringWidth - 4;
+
+ if( q == PDTextbox.QUADDING_CENTERED )
+ {
+ adjustAmount = adjustAmount/2.0f;
+ }
+
+ printWriter.println( adjustAmount + " 0 Td" );
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown justification value:" + q );
+ }
+ printWriter.println("(" + value + ") Tj");
+ printWriter.println("ET" );
+ printWriter.flush();
+ }
+
+ private PDFont getFontAndUpdateResources( List tokens, PDAppearanceStream appearanceStream ) throws IOException
+ {
+
+ PDFont retval = null;
+ PDResources streamResources = appearanceStream.getResources();
+ PDResources formResources = acroForm.getDefaultResources();
+ if( formResources != null )
+ {
+ if( streamResources == null )
+ {
+ streamResources = new PDResources();
+ appearanceStream.setResources( streamResources );
+ }
+
+ COSString da = getDefaultAppearance();
+ if( da != null )
+ {
+ String data = da.getString();
+ PDFStreamParser streamParser = new PDFStreamParser(
+ new ByteArrayInputStream( data.getBytes() ), null );
+ streamParser.parse();
+ tokens = streamParser.getTokens();
+ }
+
+ int setFontIndex = tokens.indexOf( PDFOperator.getOperator( "Tf" ));
+ COSName cosFontName = (COSName)tokens.get( setFontIndex-2 );
+ String fontName = cosFontName.getName();
+ retval = (PDFont)streamResources.getFonts().get( fontName );
+ if( retval == null )
+ {
+ retval = (PDFont)formResources.getFonts().get( fontName );
+ streamResources.getFonts().put( fontName, retval );
+ }
+ }
+ return retval;
+ }
+
+ private String convertToMultiLine( String line )
+ {
+ int currIdx = 0;
+ int lastIdx = 0;
+ StringBuffer result = new StringBuffer(line.length() + 64);
+ while( (currIdx = line.indexOf('\n',lastIdx )) > -1 )
+ {
+ result.append(value.substring(lastIdx,currIdx));
+ result.append(" ) Tj\n0 -13 Td\n(");
+ lastIdx = currIdx + 1;
+ }
+ result.append(line.substring(lastIdx));
+ return result.toString();
+ }
+
+ /**
+ * Writes the stream to the actual stream in the COSStream.
+ *
+ * @throws IOException If there is an error writing to the stream
+ */
+ private void writeToStream( byte[] data, PDAppearanceStream appearanceStream ) throws IOException
+ {
+ OutputStream out = appearanceStream.getStream().createUnfilteredStream();
+ out.write( data );
+ out.flush();
+ }
+
+
+ /**
+ * w in an appearance stream represents the lineWidth.
+ * @return the linewidth
+ */
+ private float getLineWidth( List tokens )
+ {
+
+ float retval = 1;
+ if( tokens != null )
+ {
+ int btIndex = tokens.indexOf(PDFOperator.getOperator( "BT" ));
+ int wIndex = tokens.indexOf(PDFOperator.getOperator( "w" ));
+ //the w should only be used if it is before the first BT.
+ if( (wIndex > 0) && (wIndex < btIndex) )
+ {
+ retval = ((COSNumber)tokens.get(wIndex-1)).floatValue();
+ }
+ }
+ return retval;
+ }
+
+ private PDRectangle getSmallestDrawnRectangle( PDRectangle boundingBox, List tokens )
+ {
+ PDRectangle smallest = boundingBox;
+ for( int i=0; i<tokens.size(); i++ )
+ {
+ Object next = tokens.get( i );
+ if( next == PDFOperator.getOperator( "re" ) )
+ {
+ COSNumber x = (COSNumber)tokens.get( i-4 );
+ COSNumber y = (COSNumber)tokens.get( i-3 );
+ COSNumber width = (COSNumber)tokens.get( i-2 );
+ COSNumber height = (COSNumber)tokens.get( i-1 );
+ PDRectangle potentialSmallest = new PDRectangle();
+ potentialSmallest.setLowerLeftX( x.floatValue() );
+ potentialSmallest.setLowerLeftY( y.floatValue() );
+ potentialSmallest.setUpperRightX( x.floatValue() + width.floatValue() );
+ potentialSmallest.setUpperRightY( y.floatValue() + height.floatValue() );
+ if( smallest == null ||
+ smallest.getLowerLeftX() < potentialSmallest.getLowerLeftX() ||
+ smallest.getUpperRightY() > potentialSmallest.getUpperRightY() )
+ {
+ smallest = potentialSmallest;
+ }
+
+ }
+ }
+ return smallest;
+ }
+
+ /**
+ * My "not so great" method for calculating the fontsize.
+ * It does not work superb, but it handles ok.
+ * @return the calculated font-size
+ *
+ * @throws IOException If there is an error getting the font height.
+ */
+ private float calculateFontSize( PDFont pdFont, PDRectangle boundingBox, List tokens, List daTokens )
+ throws IOException
+ {
+ float fontSize = 0;
+ if( daTokens != null )
+ {
+ //daString looks like "BMC /Helv 3.4 Tf EMC"
+
+ int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) );
+ if(fontIndex != -1 )
+ {
+ fontSize = ((COSNumber)daTokens.get(fontIndex-1)).floatValue();
+ }
+ }
+ if( parent.doNotScroll() )
+ {
+ //if we don't scroll then we will shrink the font to fit into the text area.
+ float widthAtFontSize1 = pdFont.getStringWidth( value );
+ float availableWidth = boundingBox.getWidth();
+ float perfectFitFontSize = availableWidth / widthAtFontSize1;
+ }
+ else if( fontSize == 0 )
+ {
+ float lineWidth = getLineWidth( tokens );
+ float stringWidth = pdFont.getStringWidth( value );
+ float height = 0;
+ if( pdFont instanceof PDSimpleFont )
+ {
+ height = ((PDSimpleFont)pdFont).getFontDescriptor().getFontBoundingBox().getHeight();
+ }
+ else
+ {
+ //now much we can do, so lets assume font is square and use width
+ //as the height
+ height = pdFont.getAverageFontWidth();
+ }
+ height = height/1000f;
+
+ float availHeight = getAvailableHeight( boundingBox, lineWidth );
+ fontSize =(availHeight/height);
+ }
+ return fontSize;
+ }
+
+ /**
+ * Calculates where to start putting the text in the box.
+ * The positioning is not quite as accurate as when Acrobat
+ * places the elements, but it works though.
+ *
+ * @return the sting for representing the start position of the text
+ *
+ * @throws IOException If there is an error calculating the text position.
+ */
+ private String getTextPosition( PDRectangle boundingBox, PDFont pdFont, float fontSize, List tokens )
+ throws IOException
+ {
+ float lineWidth = getLineWidth( tokens );
+ float pos = 0.0f;
+ if(parent.isMultiline())
+ {
+ int rows = (int) (getAvailableHeight( boundingBox, lineWidth ) / ((int) fontSize));
+ pos = ((rows)*fontSize)-fontSize;
+ }
+ else
+ {
+ if( pdFont instanceof PDSimpleFont )
+ {
+ //BJL 9/25/2004
+ //This algorithm is a little bit of black magic. It does
+ //not appear to be documented anywhere. Through examining a few
+ //PDF documents and the value that Acrobat places in there I
+ //have determined that the below method of computing the position
+ //is correct for certain documents, but maybe not all. It does
+ //work f1040ez.pdf and Form_1.pdf
+ PDFontDescriptor fd = ((PDSimpleFont)pdFont).getFontDescriptor();
+ float bBoxHeight = boundingBox.getHeight();
+ float fontHeight = fd.getFontBoundingBox().getHeight() + 2 * fd.getDescent();
+ fontHeight = (fontHeight/1000) * fontSize;
+ pos = (bBoxHeight - fontHeight)/2;
+ }
+ else
+ {
+ throw new IOException( "Error: Don't know how to calculate the position for non-simple fonts" );
+ }
+ }
+ PDRectangle innerBox = getSmallestDrawnRectangle( boundingBox, tokens );
+ float xInset = 2+ 2*(boundingBox.getWidth() - innerBox.getWidth());
+ return Math.round(xInset) + " "+ pos + " Td";
+ }
+
+ /**
+ * calculates the available width of the box.
+ * @return the calculated available width of the box
+ */
+ private float getAvailableWidth( PDRectangle boundingBox, float lineWidth )
+ {
+ return boundingBox.getWidth() - 2 * lineWidth;
+ }
+
+ /**
+ * calculates the available height of the box.
+ * @return the calculated available height of the box
+ */
+ private float getAvailableHeight( PDRectangle boundingBox, float lineWidth )
+ {
+ return boundingBox.getHeight() - 2 * lineWidth;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java
new file mode 100644
index 0000000..2e175d0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class for handling the PDF field as a checkbox.
+ *
+ * @author sug
+ * @version $Revision: 1.10 $
+ */
+public class PDCheckbox extends PDChoiceButton
+{
+ private static final COSName KEY = COSName.getPDFName("AS");
+ private static final COSName OFF_VALUE = COSName.getPDFName("Off");
+
+ private COSName value;
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The checkbox field dictionary
+ */
+ public PDCheckbox( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ COSDictionary ap = (COSDictionary) field.getDictionaryObject(COSName.getPDFName("AP"));
+ if( ap != null )
+ {
+ COSBase n = ap.getDictionaryObject(COSName.getPDFName("N"));
+
+ if( n instanceof COSDictionary )
+ {
+ List li = ((COSDictionary)n).keyList();
+ for( int i=0; i<li.size(); i++ )
+ {
+ COSName name = (COSName)li.get( i );
+ if( !name.equals( OFF_VALUE ))
+ {
+ value = name;
+ }
+ }
+
+ }
+ }
+ else
+ {
+ value = (COSName)getDictionary().getDictionaryObject( "V" );
+ }
+ }
+
+ /**
+ * This will tell if this radio button is currently checked or not.
+ *
+ * @return true If the radio button is checked.
+ */
+ public boolean isChecked()
+ {
+ boolean retval = false;
+ String onValue = getOnValue();
+ COSName radioValue = (COSName)getDictionary().getDictionaryObject( KEY );
+ if( radioValue != null && value != null && radioValue.getName().equals( onValue ) )
+ {
+ retval = true;
+ }
+
+ return retval;
+ }
+
+ /**
+ * Checks the radiobutton.
+ */
+ public void check()
+ {
+ getDictionary().setItem(KEY, value);
+ }
+
+ /**
+ * Unchecks the radiobutton.
+ */
+ public void unCheck()
+ {
+ getDictionary().setItem(KEY, OFF_VALUE);
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
+ */
+ public void setValue(String newValue)
+ {
+ getDictionary().setName( "V", newValue );
+ if( newValue == null )
+ {
+ getDictionary().setItem( KEY, OFF_VALUE );
+ }
+ else
+ {
+ getDictionary().setName( KEY, newValue );
+ }
+ }
+
+ /**
+ * This will get the value of the radio button.
+ *
+ * @return The value of the radio button.
+ */
+ public String getOffValue()
+ {
+ return OFF_VALUE.getName();
+ }
+
+ /**
+ * This will get the value of the radio button.
+ *
+ * @return The value of the radio button.
+ */
+ public String getOnValue()
+ {
+ String retval = null;
+ COSDictionary ap = (COSDictionary) getDictionary().getDictionaryObject(COSName.getPDFName("AP"));
+ COSBase n = ap.getDictionaryObject(COSName.getPDFName("N"));
+
+ //N can be a COSDictionary or a COSStream
+ if( n instanceof COSDictionary )
+ {
+ Iterator li = ((COSDictionary)n).keyList().iterator();
+ while( li.hasNext() )
+ {
+ Object key = li.next();
+ if( !key.equals( OFF_VALUE) )
+ {
+ retval = ((COSName)key).getName();
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * getValue gets the fields value to as a string.
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error getting the value.
+ */
+ public String getValue() throws IOException
+ {
+ return getDictionary().getNameAsString( "V" );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java
new file mode 100644
index 0000000..5196d85
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This holds common functionality for check boxes and radio buttons.
+ *
+ * @author sug
+ * @version $Revision: 1.4 $
+ */
+public abstract class PDChoiceButton extends PDField
+{
+
+ /**
+ * @see PDField#PDField(PDAcroForm,org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field for this button.
+ */
+ public PDChoiceButton( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm, field);
+ }
+
+ /**
+ * This will get the option values "Opt" entry of the pdf button.
+ *
+ * @return A list of java.lang.String values.
+ */
+ public List getOptions()
+ {
+ List retval = null;
+ COSArray array = (COSArray)getDictionary().getDictionaryObject( COSName.getPDFName( "Opt" ) );
+ if( array != null )
+ {
+ List strings = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ strings.add( ((COSString)array.getObject( i )).getString() );
+ }
+ retval = new COSArrayList( strings, array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will will set the list of options for this button.
+ *
+ * @param options The list of options for the button.
+ */
+ public void setOptions( List options )
+ {
+ getDictionary().setItem(
+ COSName.getPDFName( "Opt" ),
+ COSArrayList.converterToCOSArray( options ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java
new file mode 100644
index 0000000..ff02cc6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSInteger;
+
+/**
+ * A class for handling the PDF field as a choicefield.
+ *
+ * @author sug
+ * @version $Revision: 1.6 $
+ */
+public class PDChoiceField extends PDVariableText
+{
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#COSField(org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field for this choice field.
+ */
+ public PDChoiceField( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm, field);
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#setValue(java.lang.String)
+ *
+ * @param optionValue The new value for this text field.
+ *
+ * @throws IOException If there is an error calculating the appearance stream or the value in not one
+ * of the existing options.
+ */
+ public void setValue(String optionValue) throws IOException
+ {
+ int indexSelected = -1;
+ COSArray options = (COSArray)getDictionary().getDictionaryObject( "Opt" );
+ if( options.size() == 0 )
+ {
+ throw new IOException( "Error: You cannot set a value for a choice field if there are no options." );
+ }
+ else
+ {
+ COSBase option = options.getObject( 0 );
+ if( option instanceof COSArray )
+ {
+ for( int i=0; i<options.size() && indexSelected == -1; i++ )
+ {
+ COSArray keyValuePair = (COSArray)options.get( i );
+ COSString key = (COSString)keyValuePair.getObject( 0 );
+ COSString value = (COSString)keyValuePair.getObject( 1 );
+ if( optionValue.equals( key.getString() ) || optionValue.equals( value.getString() ) )
+ {
+ //have the parent draw the appearance stream with the value
+ super.setValue( value.getString() );
+ //but then use the key as the V entry
+ getDictionary().setItem( COSName.getPDFName( "V" ), key );
+ indexSelected = i;
+ }
+ }
+ }
+ else
+ {
+ for( int i=0; i<options.size() && indexSelected == -1; i++ )
+ {
+ COSString value = (COSString)options.get( i );
+ if( optionValue.equals( value.getString() ) )
+ {
+ setValue( optionValue );
+ indexSelected = i;
+ }
+ }
+ }
+ }
+ if( indexSelected == -1 )
+ {
+ throw new IOException( "Error: '" + optionValue + "' was not an available option.");
+ }
+ else
+ {
+ COSArray indexArray = (COSArray)getDictionary().getDictionaryObject( "I" );
+ if( indexArray != null )
+ {
+ indexArray.clear();
+ indexArray.add( new COSInteger( indexSelected ) );
+ }
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java
new file mode 100644
index 0000000..c395794
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java
@@ -0,0 +1,610 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.PDTextStream;
+
+import org.pdfbox.pdmodel.fdf.FDFField;
+import org.pdfbox.util.BitFlagHelper;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is the superclass for a Field element in a PDF.
+ * Based on the COS object model from PDFBox.
+ *
+ * @author sug
+ * @version $Revision: 1.21 $
+ */
+public abstract class PDField implements COSObjectable
+{
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_READ_ONLY = 1;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_REQUIRED = 1 << 1;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_NO_EXPORT = 1 << 2;
+
+
+ private PDAcroForm acroForm;
+
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ *
+ * @param theAcroForm The form that this field is part of.
+ */
+ public PDField( PDAcroForm theAcroForm )
+ {
+ acroForm = theAcroForm;
+ dictionary = new COSDictionary();
+ //no required fields in base field class
+ }
+
+
+ /**
+ * Creates a COSField from a COSDictionary, expected to be
+ * a correct object definition for a field in PDF.
+ *
+ * @param theAcroForm The form that this field is part of.
+ * @param field the PDF objet to represent as a field.
+ */
+ public PDField(PDAcroForm theAcroForm, COSDictionary field)
+ {
+ acroForm = theAcroForm;
+ dictionary = field;
+ }
+
+ /**
+ * Returns the partial name of the field.
+ *
+ * @return the name of the field
+ */
+ public String getPartialName()
+ {
+ return getDictionary().getString( "T" );
+ }
+
+ /**
+ * This will set the partial name of the field.
+ *
+ * @param name The new name for the field.
+ */
+ public void setPartialName( String name )
+ {
+ getDictionary().setString( "T", name );
+ }
+
+ /**
+ * Returns the fully qualified name of the field, which is a concatenation of
+ * the names of all the parents fields.
+ *
+ * @return the name of the field
+ *
+ * @throws IOException If there is an error generating the fully qualified name.
+ */
+ public String getFullyQualifiedName() throws IOException
+ {
+ PDField parent = getParent();
+ String parentName = null;
+ if( parent != null )
+ {
+ parentName = parent.getFullyQualifiedName();
+ }
+ String finalName = getPartialName();
+ if( parentName != null )
+ {
+ finalName = parentName + "." + finalName;
+ }
+ return finalName;
+ }
+
+ /**
+ * Get the FT entry of the field. This is a read only field and is set depending
+ * on the actual type. The field type is an inheritable attribute. This method will
+ * return only the direct value on this object. Use the findFieldType for an upward
+ * recursive search.
+ *
+ * @return The Field type.
+ *
+ * @see PDField#findFieldType()
+ */
+ public String getFieldType()
+ {
+ return getDictionary().getNameAsString( "FT" );
+ }
+
+ /**
+ * Find the field type and optionally do a recursive upward search. Sometimes the fieldtype
+ * will be specified on the parent instead of the direct object. This will look at this
+ * object for the field type, if none is specified then it will look to the parent if there
+ * is a parent. If there is no parent and no field type has been found then this
+ * will return null.
+ *
+ * @return The field type or null if none was found.
+ */
+ public String findFieldType()
+ {
+ return findFieldType( getDictionary() );
+ }
+
+ private String findFieldType( COSDictionary dic )
+ {
+ String retval = dic.getNameAsString( "FT" );
+ if( retval == null )
+ {
+ COSDictionary parent = (COSDictionary)dic.getDictionaryObject( "Parent" );
+ if( parent != null )
+ {
+ retval = findFieldType( parent );
+ }
+ }
+ return retval;
+
+ }
+
+
+ /**
+ * setValue sets the fields value to a given string.
+ *
+ * @param value the string value
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public abstract void setValue(String value) throws IOException;
+
+ /**
+ * getValue gets the fields value to as a string.
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error getting the value.
+ */
+ public abstract String getValue() throws IOException;
+
+ /**
+ * sets the field to be read-only.
+ *
+ * @param readonly The new flag for readonly.
+ */
+ public void setReadonly(boolean readonly)
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_READ_ONLY, readonly );
+ }
+
+ /**
+ *
+ * @return true if the field is readonly
+ */
+ public boolean isReadonly()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_READ_ONLY );
+ }
+
+ /**
+ * sets the field to be required.
+ *
+ * @param required The new flag for required.
+ */
+ public void setRequired(boolean required)
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_REQUIRED, required );
+ }
+
+ /**
+ *
+ * @return true if the field is required
+ */
+ public boolean isRequired()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_REQUIRED );
+ }
+
+ /**
+ * sets the field to be not exported..
+ *
+ * @param noExport The new flag for noExport.
+ */
+ public void setNoExport(boolean noExport)
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_NO_EXPORT, noExport );
+ }
+
+ /**
+ *
+ * @return true if the field is not to be exported.
+ */
+ public boolean isNoExport()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_NO_EXPORT );
+ }
+
+ /**
+ * This will get the flags for this field.
+ *
+ * @return flags The set of flags.
+ */
+ public int getFieldFlags()
+ {
+ int retval = 0;
+ COSInteger ff = (COSInteger)getDictionary().getDictionaryObject( COSName.getPDFName( "Ff" ) );
+ if( ff != null )
+ {
+ retval = ff.intValue();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the flags for this field.
+ *
+ * @param flags The new flags.
+ */
+ public void setFieldFlags( int flags )
+ {
+ COSInteger ff = new COSInteger( flags );
+ getDictionary().setItem( COSName.getPDFName( "Ff" ), ff );
+ }
+
+ /**
+ * This will import a fdf field from a fdf document.
+ *
+ * @param fdfField The fdf field to import.
+ *
+ * @throws IOException If there is an error importing the data for this field.
+ */
+ public void importFDF( FDFField fdfField ) throws IOException
+ {
+ Object fieldValue = fdfField.getValue();
+ int fieldFlags = getFieldFlags();
+
+ if( fieldValue != null )
+ {
+ if( fieldValue instanceof String )
+ {
+ setValue( (String)fieldValue );
+ }
+ else if( fieldValue instanceof PDTextStream )
+ {
+ setValue( ((PDTextStream)fieldValue).getAsString() );
+ }
+ else
+ {
+ throw new IOException( "Unknown field type:" + fieldValue.getClass().getName() );
+ }
+ }
+ Integer ff = fdfField.getFieldFlags();
+ if( ff != null )
+ {
+ setFieldFlags( ff.intValue() );
+ }
+ else
+ {
+ //these are suppose to be ignored if the Ff is set.
+ Integer setFf = fdfField.getSetFieldFlags();
+
+ if( setFf != null )
+ {
+ int setFfInt = setFf.intValue();
+ fieldFlags = fieldFlags | setFfInt;
+ setFieldFlags( fieldFlags );
+ }
+
+ Integer clrFf = fdfField.getClearFieldFlags();
+ if( clrFf != null )
+ {
+ //we have to clear the bits of the document fields for every bit that is
+ //set in this field.
+ //
+ //Example:
+ //docFf = 1011
+ //clrFf = 1101
+ //clrFfValue = 0010;
+ //newValue = 1011 & 0010 which is 0010
+ int clrFfValue = clrFf.intValue();
+ clrFfValue ^= 0xFFFFFFFF;
+ fieldFlags = fieldFlags & clrFfValue;
+ setFieldFlags( fieldFlags );
+ }
+ }
+
+ PDAnnotationWidget widget = getWidget();
+ if( widget != null )
+ {
+ int annotFlags = widget.getAnnotationFlags();
+ Integer f = fdfField.getWidgetFieldFlags();
+ if( f != null && widget != null )
+ {
+ widget.setAnnotationFlags( f.intValue() );
+ }
+ else
+ {
+ //these are suppose to be ignored if the F is set.
+ Integer setF = fdfField.getSetWidgetFieldFlags();
+ if( setF != null )
+ {
+ annotFlags = annotFlags | setF.intValue();
+ widget.setAnnotationFlags( annotFlags );
+ }
+
+ Integer clrF = fdfField.getClearWidgetFieldFlags();
+ if( clrF != null )
+ {
+ //we have to clear the bits of the document fields for every bit that is
+ //set in this field.
+ //
+ //Example:
+ //docF = 1011
+ //clrF = 1101
+ //clrFValue = 0010;
+ //newValue = 1011 & 0010 which is 0010
+ int clrFValue = clrF.intValue();
+ clrFValue ^= 0xFFFFFFFFL;
+ annotFlags = annotFlags & clrFValue;
+ widget.setAnnotationFlags( annotFlags );
+ }
+ }
+ }
+ List fdfKids = fdfField.getKids();
+ List pdKids = getKids();
+ for( int i=0; fdfKids != null && i<fdfKids.size(); i++ )
+ {
+ FDFField fdfChild = (FDFField)fdfKids.get( i );
+ String fdfName = fdfChild.getPartialFieldName();
+ for( int j=0; j<pdKids.size(); j++ )
+ {
+ Object pdChildObj = pdKids.get( j );
+ if( pdChildObj instanceof PDField )
+ {
+ PDField pdChild = (PDField)pdChildObj;
+ if( fdfName != null && fdfName.equals( pdChild.getPartialName() ) )
+ {
+ pdChild.importFDF( fdfChild );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This will get the single associated widget that is part of this field. This
+ * occurs when the Widget is embedded in the fields dictionary. Sometimes there
+ * are multiple sub widgets associated with this field, in which case you want to
+ * use getKids(). If the kids entry is specified, then the first entry in that
+ * list will be returned.
+ *
+ * @return The widget that is associated with this field.
+ * @throws IOException If there is an error getting the widget object.
+ */
+ public PDAnnotationWidget getWidget() throws IOException
+ {
+ PDAnnotationWidget retval = null;
+ List kids = getKids();
+ if( kids == null )
+ {
+ retval = new PDAnnotationWidget( getDictionary() );
+ }
+ else if( kids.size() > 0 )
+ {
+ Object firstKid = kids.get( 0 );
+ if( firstKid instanceof PDAnnotationWidget )
+ {
+ retval = (PDAnnotationWidget)firstKid;
+ }
+ else
+ {
+ retval = ((PDField)firstKid).getWidget();
+ }
+ }
+ else
+ {
+ retval = null;
+ }
+ return retval;
+ }
+
+ /**
+ * Get the parent field to this field, or null if none exists.
+ *
+ * @return The parent field.
+ *
+ * @throws IOException If there is an error creating the parent field.
+ */
+ public PDField getParent() throws IOException
+ {
+ PDField parent = null;
+ COSDictionary parentDic = (COSDictionary)getDictionary().getDictionaryObject( "Parent" );
+ if( parentDic != null )
+ {
+ parent = PDFieldFactory.createField( getAcroForm(), parentDic );
+ }
+ return parent;
+ }
+
+ /**
+ * Set the parent of this field.
+ *
+ * @param parent The parent to this field.
+ */
+ public void setParent( PDField parent )
+ {
+ getDictionary().setItem( "Parent", parent );
+ }
+
+ /**
+ * This will get all the kids of this field. The values in the list
+ * will either be PDWidget or PDField. Normally they will be PDWidget objects
+ * unless this is a non-terminal field and they will be child PDField objects.
+ *
+ * @return A list of either PDWidget or PDField objects.
+ * @throws IOException If there is an error retrieving the kids.
+ */
+ public List getKids() throws IOException
+ {
+ List retval = null;
+ COSArray kids = (COSArray)getDictionary().getDictionaryObject(COSName.KIDS);
+ if( kids != null )
+ {
+ List kidsList = new ArrayList();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ COSDictionary kidDictionary = (COSDictionary)kids.getObject(i);
+ COSDictionary parent = (COSDictionary)kidDictionary.getDictionaryObject( "Parent" );
+ if( kidDictionary.getDictionaryObject( "FT" ) != null ||
+ (parent != null && parent.getDictionaryObject( "FT" ) != null ) )
+ {
+ kidsList.add( PDFieldFactory.createField( acroForm, kidDictionary ));
+ }
+ else if( "Widget".equals( kidDictionary.getNameAsString( "Subtype" ) ) )
+ {
+ kidsList.add( new PDAnnotationWidget( kidDictionary ) );
+ }
+ else
+ {
+ //
+ kidsList.add( PDFieldFactory.createField( acroForm, kidDictionary ));
+ }
+ }
+ retval = new COSArrayList( kidsList, kids );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the list of kids.
+ *
+ * @param kids The list of child widgets.
+ */
+ public void setKids( List kids )
+ {
+ COSArray kidsArray = COSArrayList.converterToCOSArray( kids );
+ getDictionary().setItem( COSName.KIDS, kidsArray );
+ }
+
+ /**
+ * This will return a string representation of this field.
+ *
+ * @return A string representation of this field.
+ */
+ public String toString()
+ {
+ return "" + getDictionary().getDictionaryObject( COSName.getPDFName( "V" ) );
+ }
+
+ /**
+ * This will get the acroform that this field is part of.
+ *
+ * @return The form this field is on.
+ */
+ public PDAcroForm getAcroForm()
+ {
+ return acroForm;
+ }
+
+ /**
+ * This will set the form this field is on.
+ *
+ * @param value The new form to use.
+ */
+ public void setAcroForm(PDAcroForm value)
+ {
+ acroForm = value;
+ }
+
+ /**
+ * This will get the dictionary associated with this field.
+ *
+ * @return The dictionary that this class wraps.
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return dictionary;
+ }
+
+ /**
+ * Get the additional actions for this field. This will return null
+ * if there are no additional actions for this field.
+ *
+ * @return The actions of the field.
+ */
+ public PDFormFieldAdditionalActions getActions()
+ {
+ COSDictionary aa = (COSDictionary)dictionary.getDictionaryObject( "AA" );
+ PDFormFieldAdditionalActions retval = null;
+ if( aa != null )
+ {
+ retval = new PDFormFieldAdditionalActions( aa );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the actions of the field.
+ *
+ * @param actions The field actions.
+ */
+ public void setActions( PDFormFieldAdditionalActions actions )
+ {
+ dictionary.setItem( "AA", actions );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java
new file mode 100644
index 0000000..c796714
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+
+import java.io.IOException;
+
+import java.util.List;
+
+/**
+ * This is the Factory for creating and returning the correct
+ * field elements.
+ *
+ * @author sug
+ * @version $Revision: 1.8 $
+ */
+public class PDFieldFactory
+{
+ private static final int RADIO_BITMASK = 32768;
+ private static final int PUSHBUTTON_BITMASK = 65536;
+ private static final int RADIOS_IN_UNISON_BITMASK = 33554432;
+
+ private static final String FIELD_TYPE_BTN = "Btn";
+ private static final String FIELD_TYPE_TX = "Tx";
+ private static final String FIELD_TYPE_CH = "Ch";
+ private static final String FIELD_TYPE_SIG = "Sig";
+
+ /**
+ * Utility class so no constructor.
+ */
+ private PDFieldFactory()
+ {
+ //do nothing.
+ }
+
+ /**
+ * This method creates a COSField subclass from the given field.
+ * The field is a PDF Dictionary object that must represent a
+ * field element. - othewise null is returned
+ *
+ * @param acroForm The form that the field will be part of.
+ * @param field The dictionary representing a field element
+ *
+ * @return a subclass to COSField according to the kind of field passed to createField
+ * @throws IOException If there is an error determining the field type.
+ */
+ public static PDField createField( PDAcroForm acroForm, COSDictionary field) throws IOException
+ {
+ PDField pdField = new PDUnknownField( acroForm, field );
+ if( isButton(pdField) )
+ {
+ int flags = pdField.getFieldFlags();
+ //BJL, I have found that the radio flag bit is not always set
+ //and that sometimes there is just a kids dictionary.
+ //so, if there is a kids dictionary then it must be a radio button
+ //group.
+ COSArray kids = (COSArray)field.getDictionaryObject( COSName.getPDFName( "Kids" ) );
+ if( kids != null || isRadio(flags) )
+ {
+ pdField = new PDRadioCollection( acroForm, field );
+ }
+ else if( isPushButton( flags ) )
+ {
+ pdField = new PDPushButton( acroForm, field );
+ }
+ else
+ {
+ pdField = new PDCheckbox( acroForm, field );
+ }
+
+ }
+ else if (isChoiceField(pdField))
+ {
+ pdField = new PDChoiceField( acroForm, field );
+ }
+ else if (isTextbox(pdField))
+ {
+ pdField = new PDTextbox( acroForm, field );
+ }
+ else if( isSignature( pdField ) )
+ {
+ pdField = new PDSignature( acroForm, field );
+ }
+ else
+ {
+ //do nothing and return an unknown field type.
+ }
+ return pdField;
+ }
+
+ /**
+ * This method determines if the given
+ * field is a radiobutton collection.
+ *
+ * @param flags The field flags.
+ *
+ * @return the result of the determination
+ */
+ private static boolean isRadio( int flags )
+ {
+ return (flags & RADIO_BITMASK) > 0;
+ }
+
+ /**
+ * This method determines if the given
+ * field is a pushbutton.
+ *
+ * @param flags The field flags.
+ *
+ * @return the result of the determination
+ */
+ private static boolean isPushButton( int flags )
+ {
+ return (flags & PUSHBUTTON_BITMASK) > 0;
+ }
+
+ /**
+ * This method determines if the given field is a choicefield
+ * Choicefields are either listboxes or comboboxes.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ */
+ private static boolean isChoiceField(PDField field) throws IOException
+ {
+ return FIELD_TYPE_CH.equals(field.findFieldType());
+ }
+
+ /**
+ * This method determines if the given field is a button.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ *
+ * @throws IOException If there is an error determining the field type.
+ */
+ private static boolean isButton(PDField field) throws IOException
+ {
+ String ft = field.findFieldType();
+ boolean retval = FIELD_TYPE_BTN.equals( ft );
+ List kids = field.getKids();
+ if( ft == null && kids != null && kids.size() > 0)
+ {
+ //sometimes if it is a button the type is only defined by one
+ //of the kids entries
+ Object obj = kids.get( 0 );
+ COSDictionary kidDict = null;
+ if( obj instanceof PDField )
+ {
+ kidDict = ((PDField)obj).getDictionary();
+ }
+ else if( obj instanceof PDAnnotationWidget )
+ {
+ kidDict = ((PDAnnotationWidget)obj).getDictionary();
+ }
+ else
+ {
+ throw new IOException( "Error:Unexpected type of kids field:" + obj );
+ }
+ retval = isButton( new PDUnknownField( field.getAcroForm(), kidDict ) );
+ }
+ return retval;
+ }
+
+ /**
+ * This method determines if the given field is a signature.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ */
+ private static boolean isSignature(PDField field) throws IOException
+ {
+ return FIELD_TYPE_SIG.equals(field.findFieldType());
+ }
+
+ /**
+ * This method determines if the given field is a Textbox.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ */
+ private static boolean isTextbox(PDField field) throws IOException
+ {
+ return FIELD_TYPE_TX.equals(field.findFieldType());
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java
new file mode 100644
index 0000000..e832921
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import java.io.IOException;
+
+/**
+ * A class for handling the PDF field as a PDPushButton.
+ *
+ * @author sug
+ * @version $Revision: 1.3 $
+ */
+public class PDPushButton extends PDField
+{
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#COSField(org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field for this push button.
+ */
+ public PDPushButton( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm, field);
+ }
+
+ /**
+ * @see as.interactive.pdf.form.cos.COSField#setValue(java.lang.String)
+ *
+ * @param value The new value for the field.
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public void setValue(String value) throws IOException
+ {
+ COSString fieldValue = new COSString(value);
+ getDictionary().setItem( COSName.getPDFName( "V" ), fieldValue );
+ getDictionary().setItem( COSName.getPDFName( "DV" ), fieldValue );
+ }
+
+ /**
+ * getValue gets the fields value to as a string.
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error getting the value.
+ */
+ public String getValue() throws IOException
+ {
+ return getDictionary().getString( "V" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java
new file mode 100644
index 0000000..27b48d6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.util.BitFlagHelper;
+
+/**
+ * A class for handling the PDF field as a Radio Collection.
+ * This class automatically keeps track of the child radio buttons
+ * in the collection.
+ *
+ * @see PDCheckbox
+ * @author sug
+ * @version $Revision: 1.12 $
+ */
+public class PDRadioCollection extends PDChoiceButton
+{
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_RADIOS_IN_UNISON = 1 << 25;
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field that makes up the radio collection.
+ */
+ public PDRadioCollection( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm,field);
+ }
+
+ /**
+ * From the PDF Spec <br/>
+ * If set, a group of radio buttons within a radio button field that
+ * use the same value for the on state will turn on and off in unison; that is if
+ * one is checked, they are all checked. If clear, the buttons are mutually exclusive
+ * (the same behavior as HTML radio buttons).
+ *
+ * @param radiosInUnison The new flag for radiosInUnison.
+ */
+ public void setRadiosInUnison(boolean radiosInUnison)
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_RADIOS_IN_UNISON, radiosInUnison );
+ }
+
+ /**
+ *
+ * @return true If the flag is set for radios in unison.
+ */
+ public boolean isRadiosInUnison()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_RADIOS_IN_UNISON );
+ }
+
+ /**
+ * This setValue method iterates the collection of radiobuttons
+ * and checks or unchecks each radiobutton according to the
+ * given value.
+ * If the value is not represented by any of the radiobuttons,
+ * then none will be checked.
+ *
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
+ */
+ public void setValue(String value) throws IOException
+ {
+ getDictionary().setString( "V", value );
+ List kids = getKids();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ PDCheckbox btn = (PDCheckbox)kids.get(i);
+ if( btn.getOnValue().equals(value) )
+ {
+ btn.check();
+ }
+ else
+ {
+ btn.unCheck();
+ }
+ }
+ }
+
+ /**
+ * getValue gets the fields value to as a string.
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error getting the value.
+ */
+ public String getValue()throws IOException
+ {
+ String retval = null;
+ List kids = getKids();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ PDCheckbox btn = (PDCheckbox)kids.get(i);
+ if( btn.isChecked() )
+ {
+ retval = btn.getOnValue();
+ }
+ }
+ if( retval == null )
+ {
+ retval = getDictionary().getNameAsString( "V" );
+ }
+ return retval;
+ }
+
+
+ /**
+ * This will return a list of PDField objects that are part of this radio collection.
+ *
+ * @see PDField#getWidget()
+ * @return A list of PDWidget objects.
+ * @throws IOException if there is an error while creating the children objects.
+ */
+ public List getKids() throws IOException
+ {
+ List retval = null;
+ COSArray kids = (COSArray)getDictionary().getDictionaryObject(COSName.KIDS);
+ if( kids != null )
+ {
+ List kidsList = new ArrayList();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ kidsList.add( PDFieldFactory.createField( getAcroForm(), (COSDictionary)kids.getObject(i) ) );
+ }
+ retval = new COSArrayList( kidsList, kids );
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java
new file mode 100644
index 0000000..66ed3e3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+
+import java.io.IOException;
+
+/**
+ * A class for handling the PDF field as a signature.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDSignature extends PDField
+{
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#COSField(org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The dictionary for the signature.
+ */
+ public PDSignature( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm,field);
+ }
+
+ /**
+ * @see as.interactive.pdf.form.cos.COSField#setValue(java.lang.String)
+ *
+ * @param value The new value for the field.
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public void setValue(String value) throws IOException
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * @see as.interactive.pdf.form.cos.COSField#setValue(java.lang.String)
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public String getValue() throws IOException
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * Return a string rep of this object.
+ *
+ * @return A string rep of this object.
+ */
+ public String toString()
+ {
+ return "PDSignature";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java
new file mode 100644
index 0000000..fd9550e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * A class for handling the PDF field as a textbox.
+ *
+ * @author sug
+ * @version $Revision: 1.9 $
+ */
+public class PDTextbox extends PDVariableText
+{
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroform.
+ */
+ public PDTextbox( PDAcroForm theAcroForm )
+ {
+ super( theAcroForm );
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field's dictionary.
+ */
+ public PDTextbox( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java
new file mode 100644
index 0000000..806b5f5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This class represents a form field with an unknown type.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDUnknownField extends PDField
+{
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm, COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field's dictionary.
+ */
+ public PDUnknownField( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(String)
+ */
+ public void setValue(String value) throws IOException
+ {
+ //do nothing
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#getValue()
+ */
+ public String getValue() throws IOException
+ {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java
new file mode 100644
index 0000000..7ed5008
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java
@@ -0,0 +1,324 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.util.BitFlagHelper;
+
+import java.io.IOException;
+
+/**
+ * A class for handling PDF fields that display text.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.6 $
+ */
+public abstract class PDVariableText extends PDField
+{
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_MULTILINE = 1 << 12;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_PASSWORD = 1 << 13;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_FILE_SELECT = 1 << 20;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_DO_NOT_SPELL_CHECK = 1 << 22;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_DO_NOT_SCROLL = 1 << 23;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_COMB = 1 << 24;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_RICH_TEXT = 1 << 25;
+
+
+ /**
+ * DA Default appearance.
+ */
+ private COSString da;
+
+ private PDAppearance appearance;
+
+
+ /**
+ * A Q value.
+ */
+ public static final int QUADDING_LEFT = 0;
+
+ /**
+ * A Q value.
+ */
+ public static final int QUADDING_CENTERED = 1;
+
+ /**
+ * A Q value.
+ */
+ public static final int QUADDING_RIGHT = 2;
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroform.
+ */
+ public PDVariableText( PDAcroForm theAcroForm )
+ {
+ super( theAcroForm );
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field's dictionary.
+ */
+ public PDVariableText( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ da = (COSString) field.getDictionaryObject(COSName.getPDFName("DA"));
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
+ *
+ * @param value The new value for this text field.
+ *
+ * @throws IOException If there is an error calculating the appearance stream.
+ */
+ public void setValue(String value) throws IOException
+ {
+ COSString fieldValue = new COSString(value);
+ getDictionary().setItem( COSName.getPDFName( "V" ), fieldValue );
+
+ //hmm, not sure what the case where the DV gets set to the field
+ //value, for now leave blank until we can come up with a case
+ //where it needs to be in there
+ //getDictionary().setItem( COSName.getPDFName( "DV" ), fieldValue );
+ if(appearance == null)
+ {
+ this.appearance = new PDAppearance( getAcroForm(), this );
+ }
+ appearance.setAppearanceValue(value);
+ }
+
+ /**
+ * getValue gets the fields value to as a string.
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error getting the value.
+ */
+ public String getValue() throws IOException
+ {
+ return getDictionary().getString( "V" );
+ }
+
+ /**
+ * @return true if the field is multiline
+ */
+ public boolean isMultiline()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_MULTILINE );
+ }
+
+ /**
+ * Set the multiline bit.
+ *
+ * @param multiline The value for the multiline.
+ */
+ public void setMultiline( boolean multiline )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_MULTILINE, multiline );
+ }
+
+ /**
+ * @return true if the field is a password field.
+ */
+ public boolean isPassword()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_PASSWORD );
+ }
+
+ /**
+ * Set the password bit.
+ *
+ * @param password The value for the password.
+ */
+ public void setPassword( boolean password )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_PASSWORD, password );
+ }
+
+ /**
+ * @return true if the field is a file select field.
+ */
+ public boolean isFileSelect()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_FILE_SELECT );
+ }
+
+ /**
+ * Set the file select bit.
+ *
+ * @param fileSelect The value for the fileSelect.
+ */
+ public void setFileSelect( boolean fileSelect )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_FILE_SELECT, fileSelect );
+ }
+
+ /**
+ * @return true if the field is not suppose to spell check.
+ */
+ public boolean doNotSpellCheck()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_DO_NOT_SPELL_CHECK );
+ }
+
+ /**
+ * Set the doNotSpellCheck bit.
+ *
+ * @param doNotSpellCheck The value for the doNotSpellCheck.
+ */
+ public void setDoNotSpellCheck( boolean doNotSpellCheck )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_DO_NOT_SPELL_CHECK, doNotSpellCheck );
+ }
+
+ /**
+ * @return true if the field is not suppose to scroll.
+ */
+ public boolean doNotScroll()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_DO_NOT_SCROLL );
+ }
+
+ /**
+ * Set the doNotScroll bit.
+ *
+ * @param doNotScroll The value for the doNotScroll.
+ */
+ public void setDoNotScroll( boolean doNotScroll )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_DO_NOT_SCROLL, doNotScroll );
+ }
+
+ /**
+ * @return true if the field is not suppose to comb the text display.
+ */
+ public boolean shouldComb()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_COMB );
+ }
+
+ /**
+ * Set the comb bit.
+ *
+ * @param comb The value for the comb.
+ */
+ public void setComb( boolean comb )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_COMB, comb );
+ }
+
+ /**
+ * @return true if the field is a rich text field.
+ */
+ public boolean isRichText()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_RICH_TEXT );
+ }
+
+ /**
+ * Set the richText bit.
+ *
+ * @param richText The value for the richText.
+ */
+ public void setRichText( boolean richText )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_RICH_TEXT, richText );
+ }
+
+ /**
+ * @return the DA element of the dictionary object
+ */
+ protected COSString getDefaultAppearance()
+ {
+ return da;
+ }
+
+ /**
+ * This will get the 'quadding' or justification of the text to be displayed.
+ * 0 - Left(default)<br/>
+ * 1 - Centered<br />
+ * 2 - Right<br />
+ * Please see the QUADDING_CONSTANTS.
+ *
+ * @return The justification of the text strings.
+ */
+ public int getQ()
+ {
+ int retval = 0;
+ COSNumber number = (COSNumber)getDictionary().getDictionaryObject( COSName.getPDFName( "Q" ) );
+ if( number != null )
+ {
+ retval = number.intValue();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the quadding/justification of the text. See QUADDING constants.
+ *
+ * @param q The new text justification.
+ */
+ public void setQ( int q )
+ {
+ getDictionary().setItem( COSName.getPDFName( "Q" ), new COSInteger( q ) );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/form/package.html
new file mode 100644
index 0000000..36c4b4b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The interactive package contains classes that deal with interactive annotations such as textfields and buttons.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java b/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java
new file mode 100644
index 0000000..5226099
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.pagenavigation;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.PDDocumentInformation;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This a single thread in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PDThread implements COSObjectable
+{
+
+
+ private COSDictionary thread;
+
+ /**
+ * Constructor that is used for a preexisting dictionary.
+ *
+ * @param t The underlying dictionary.
+ */
+ public PDThread( COSDictionary t )
+ {
+ thread = t;
+ }
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDThread()
+ {
+ thread = new COSDictionary();
+ thread.setName( "Type", "Thread" );
+ }
+
+ /**
+ * This will get the underlying dictionary that this object wraps.
+ *
+ * @return The underlying info dictionary.
+ */
+ public COSDictionary getDictionary()
+ {
+ return thread;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return thread;
+ }
+
+ /**
+ * Get info about the thread, or null if there is nothing.
+ *
+ * @return The thread information.
+ */
+ public PDDocumentInformation getThreadInfo()
+ {
+ PDDocumentInformation retval = null;
+ COSDictionary info = (COSDictionary)thread.getDictionaryObject( "I" );
+ if( info != null )
+ {
+ retval = new PDDocumentInformation( info );
+ }
+
+ return retval;
+ }
+
+ /**
+ * Set the thread info, can be null.
+ *
+ * @param info The info dictionary about this thread.
+ */
+ public void setThreadInfo( PDDocumentInformation info )
+ {
+ thread.setItem( "I", info );
+ }
+
+ /**
+ * Get the first bead in the thread, or null if it has not been set yet. This
+ * is a required field for this object.
+ *
+ * @return The first bead in the thread.
+ */
+ public PDThreadBead getFirstBead()
+ {
+ PDThreadBead retval = null;
+ COSDictionary bead = (COSDictionary)thread.getDictionaryObject( "F" );
+ if( bead != null )
+ {
+ retval = new PDThreadBead( bead );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will set the first bead in the thread. When this is set it will
+ * also set the thread property of the bead object.
+ *
+ * @param bead The first bead in the thread.
+ */
+ public void setFirstBead( PDThreadBead bead )
+ {
+ if( bead != null )
+ {
+ bead.setThread( this );
+ }
+ thread.setItem( "F", bead );
+ }
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java b/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java
new file mode 100644
index 0000000..547d4bc
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java
@@ -0,0 +1,234 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.pagenavigation;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.common.COSObjectable;
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+/**
+ * This a single bead in a thread in a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDThreadBead implements COSObjectable
+{
+
+
+ private COSDictionary bead;
+
+ /**
+ * Constructor that is used for a preexisting dictionary.
+ *
+ * @param b The underlying dictionary.
+ */
+ public PDThreadBead( COSDictionary b )
+ {
+ bead = b;
+ }
+
+ /**
+ * Default constructor.
+ *
+ */
+ public PDThreadBead()
+ {
+ bead = new COSDictionary();
+ bead.setName( "Type", "Bead" );
+ setNextBead( this );
+ setPreviousBead( this );
+ }
+
+ /**
+ * This will get the underlying dictionary that this object wraps.
+ *
+ * @return The underlying info dictionary.
+ */
+ public COSDictionary getDictionary()
+ {
+ return bead;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return bead;
+ }
+
+ /**
+ * This will get the thread that this bead is part of. This is only required
+ * for the first bead in a thread, so other beads 'may' return null.
+ *
+ * @return The thread that this bead is part of.
+ */
+ public PDThread getThread()
+ {
+ PDThread retval = null;
+ COSDictionary dic = (COSDictionary)bead.getDictionaryObject( "T" );
+ if( dic != null )
+ {
+ retval = new PDThread( dic );
+ }
+ return retval;
+ }
+
+ /**
+ * Set the thread that this bead is part of. This is only required for the
+ * first bead in a thread. Note: This property is set for you by the PDThread.setFirstBead() method.
+ *
+ * @param thread The thread that this bead is part of.
+ */
+ public void setThread( PDThread thread )
+ {
+ bead.setItem( "T", thread );
+ }
+
+ /**
+ * This will get the next bead. If this bead is the last bead in the list then this
+ * will return the first bead.
+ *
+ * @return The next bead in the list or the first bead if this is the last bead.
+ */
+ public PDThreadBead getNextBead()
+ {
+ return new PDThreadBead( (COSDictionary) bead.getDictionaryObject( "N" ) );
+ }
+
+ /**
+ * Set the next bead in the thread.
+ *
+ * @param next The next bead.
+ */
+ protected void setNextBead( PDThreadBead next )
+ {
+ bead.setItem( "N", next );
+ }
+
+ /**
+ * This will get the previous bead. If this bead is the first bead in the list then this
+ * will return the last bead.
+ *
+ * @return The previous bead in the list or the last bead if this is the first bead.
+ */
+ public PDThreadBead getPreviousBead()
+ {
+ return new PDThreadBead( (COSDictionary) bead.getDictionaryObject( "V" ) );
+ }
+
+ /**
+ * Set the previous bead in the thread.
+ *
+ * @param previous The previous bead.
+ */
+ protected void setPreviousBead( PDThreadBead previous )
+ {
+ bead.setItem( "V", previous );
+ }
+
+ /**
+ * Append a bead after this bead. This will correctly set the next/previous beads in the
+ * linked list.
+ *
+ * @param append The bead to insert.
+ */
+ public void appendBead( PDThreadBead append )
+ {
+ PDThreadBead nextBead = getNextBead();
+ nextBead.setPreviousBead( append );
+ append.setNextBead( nextBead );
+ setNextBead( append );
+ append.setPreviousBead( this );
+ }
+
+ /**
+ * Get the page that this bead is part of.
+ *
+ * @return The page that this bead is part of.
+ */
+ public PDPage getPage()
+ {
+ PDPage page = null;
+ COSDictionary dic = (COSDictionary)bead.getDictionaryObject( "P" );
+ if( dic != null )
+ {
+ page = new PDPage( dic );
+ }
+ return page;
+ }
+
+ /**
+ * Set the page that this bead is part of. This is a required property and must be
+ * set when creating a new bead. The PDPage object also has a list of beads in the natural
+ * reading order. It is recommended that you add this object to that list as well.
+ *
+ * @param page The page that this bead is on.
+ */
+ public void setPage( PDPage page )
+ {
+ bead.setItem( "P", page );
+ }
+
+ /**
+ * The rectangle on the page that this bead is part of.
+ *
+ * @return The part of the page that this bead covers.
+ */
+ public PDRectangle getRectangle()
+ {
+ PDRectangle rect = null;
+ COSArray array = (COSArray)bead.getDictionaryObject( COSName.R );
+ if( array != null )
+ {
+ rect = new PDRectangle( array );
+ }
+ return rect;
+ }
+
+ /**
+ * Set the rectangle on the page that this bead covers.
+ *
+ * @param rect The portion of the page that this bead covers.
+ */
+ public void setRectangle( PDRectangle rect )
+ {
+ bead.setItem( COSName.R, rect );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/package.html
new file mode 100644
index 0000000..5fb1b88
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/pagenavigation/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+A package to allow provide access to PDF page navigation functionality.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java b/src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java
new file mode 100644
index 0000000..d9958e6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java
@@ -0,0 +1,365 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.viewerpreferences;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This is the document viewing preferences.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class PDViewerPreferences implements COSObjectable
+{
+ /**
+ * From PDF Reference: "Neither document outline nor thumbnail images visible".
+ */
+ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_NONE = "UseNone";
+ /**
+ * From PDF Reference: "Document outline visible".
+ */
+ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_OUTLINES = "UseOutlines";
+ /**
+ * From PDF Reference: "Thumbnail images visible".
+ */
+ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_THUMBS = "UseThumbs";
+ /**
+ * From PDF Reference: "Optional content group panel visible".
+ */
+ public static final String NON_FULL_SCREEN_PAGE_MODE_USE_OPTIONAL_CONTENT = "UseOC";
+
+ /**
+ * Reading direction.
+ */
+ public static final String READING_DIRECTION_L2R = "L2R";
+ /**
+ * Reading direction.
+ */
+ public static final String READING_DIRECTION_R2L = "R2L";
+
+ /**
+ * Boundary constant.
+ */
+ public static final String BOUNDARY_MEDIA_BOX = "MediaBox";
+ /**
+ * Boundary constant.
+ */
+ public static final String BOUNDARY_CROP_BOX = "CropBox";
+ /**
+ * Boundary constant.
+ */
+ public static final String BOUNDARY_BLEED_BOX = "BleedBox";
+ /**
+ * Boundary constant.
+ */
+ public static final String BOUNDARY_TRIM_BOX = "TrimBox";
+ /**
+ * Boundary constant.
+ */
+ public static final String BOUNDARY_ART_BOX = "ArtBox";
+
+
+ private COSDictionary prefs;
+
+ /**
+ * Constructor that is used for a preexisting dictionary.
+ *
+ * @param dic The underlying dictionary.
+ */
+ public PDViewerPreferences( COSDictionary dic )
+ {
+ prefs = dic;
+ }
+
+ /**
+ * This will get the underlying dictionary that this object wraps.
+ *
+ * @return The underlying info dictionary.
+ */
+ public COSDictionary getDictionary()
+ {
+ return prefs;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return prefs;
+ }
+
+ /**
+ * Get the toolbar preference.
+ *
+ * @return the toolbar preference.
+ */
+ public boolean hideToolbar()
+ {
+ return prefs.getBoolean( "HideToolbar", false );
+ }
+
+ /**
+ * Set the toolbar preference.
+ *
+ * @param value Set the toolbar preference.
+ */
+ public void setHideToolbar( boolean value )
+ {
+ prefs.setBoolean( "HideToolbar", value );
+ }
+
+ /**
+ * Get the menubar preference.
+ *
+ * @return the menubar preference.
+ */
+ public boolean hideMenubar()
+ {
+ return prefs.getBoolean( "HideMenubar", false );
+ }
+
+ /**
+ * Set the menubar preference.
+ *
+ * @param value Set the menubar preference.
+ */
+ public void setHideMenubar( boolean value )
+ {
+ prefs.setBoolean( "HideMenubar", value );
+ }
+
+ /**
+ * Get the window UI preference.
+ *
+ * @return the window UI preference.
+ */
+ public boolean hideWindowUI()
+ {
+ return prefs.getBoolean( "HideWindowUI", false );
+ }
+
+ /**
+ * Set the window UI preference.
+ *
+ * @param value Set the window UI preference.
+ */
+ public void setHideWindowUI( boolean value )
+ {
+ prefs.setBoolean( "HideWindowUI", value );
+ }
+
+ /**
+ * Get the fit window preference.
+ *
+ * @return the fit window preference.
+ */
+ public boolean fitWindow()
+ {
+ return prefs.getBoolean( "FitWindow", false );
+ }
+
+ /**
+ * Set the fit window preference.
+ *
+ * @param value Set the fit window preference.
+ */
+ public void setFitWindow( boolean value )
+ {
+ prefs.setBoolean( "FitWindow", value );
+ }
+
+ /**
+ * Get the center window preference.
+ *
+ * @return the center window preference.
+ */
+ public boolean centerWindow()
+ {
+ return prefs.getBoolean( "CenterWindow", false );
+ }
+
+ /**
+ * Set the center window preference.
+ *
+ * @param value Set the center window preference.
+ */
+ public void setCenterWindow( boolean value )
+ {
+ prefs.setBoolean( "CenterWindow", value );
+ }
+
+ /**
+ * Get the display doc title preference.
+ *
+ * @return the display doc title preference.
+ */
+ public boolean displayDocTitle()
+ {
+ return prefs.getBoolean( "DisplayDocTitle", false );
+ }
+
+ /**
+ * Set the display doc title preference.
+ *
+ * @param value Set the display doc title preference.
+ */
+ public void setDisplayDocTitle( boolean value )
+ {
+ prefs.setBoolean( "DisplayDocTitle", value );
+ }
+
+ /**
+ * Get the non full screen page mode preference.
+ *
+ * @return the non full screen page mode preference.
+ */
+ public String getNonFullScreenPageMode()
+ {
+ return prefs.getNameAsString( "NonFullScreenPageMode", NON_FULL_SCREEN_PAGE_MODE_USE_NONE);
+ }
+
+ /**
+ * Set the non full screen page mode preference.
+ *
+ * @param value Set the non full screen page mode preference.
+ */
+ public void setNonFullScreenPageMode( String value )
+ {
+ prefs.setName( "NonFullScreenPageMode", value );
+ }
+
+ /**
+ * Get the reading direction preference.
+ *
+ * @return the reading direction preference.
+ */
+ public String getReadingDirection()
+ {
+ return prefs.getNameAsString( "Direction", READING_DIRECTION_L2R);
+ }
+
+ /**
+ * Set the reading direction preference.
+ *
+ * @param value Set the reading direction preference.
+ */
+ public void setReadingDirection( String value )
+ {
+ prefs.setName( "Direction", value );
+ }
+
+ /**
+ * Get the ViewArea preference. See BOUNDARY_XXX constants.
+ *
+ * @return the ViewArea preference.
+ */
+ public String getViewArea()
+ {
+ return prefs.getNameAsString( "ViewArea", BOUNDARY_CROP_BOX);
+ }
+
+ /**
+ * Set the ViewArea preference. See BOUNDARY_XXX constants.
+ *
+ * @param value Set the ViewArea preference.
+ */
+ public void setViewArea( String value )
+ {
+ prefs.setName( "ViewArea", value );
+ }
+
+ /**
+ * Get the ViewClip preference. See BOUNDARY_XXX constants.
+ *
+ * @return the ViewClip preference.
+ */
+ public String getViewClip()
+ {
+ return prefs.getNameAsString( "ViewClip", BOUNDARY_CROP_BOX);
+ }
+
+ /**
+ * Set the ViewClip preference. See BOUNDARY_XXX constants.
+ *
+ * @param value Set the ViewClip preference.
+ */
+ public void setViewClip( String value )
+ {
+ prefs.setName( "ViewClip", value );
+ }
+
+ /**
+ * Get the PrintArea preference. See BOUNDARY_XXX constants.
+ *
+ * @return the PrintArea preference.
+ */
+ public String getPrintArea()
+ {
+ return prefs.getNameAsString( "PrintArea", BOUNDARY_CROP_BOX);
+ }
+
+ /**
+ * Set the PrintArea preference. See BOUNDARY_XXX constants.
+ *
+ * @param value Set the PrintArea preference.
+ */
+ public void setPrintArea( String value )
+ {
+ prefs.setName( "PrintArea", value );
+ }
+
+ /**
+ * Get the PrintClip preference. See BOUNDARY_XXX constants.
+ *
+ * @return the PrintClip preference.
+ */
+ public String getPrintClip()
+ {
+ return prefs.getNameAsString( "PrintClip", BOUNDARY_CROP_BOX);
+ }
+
+ /**
+ * Set the PrintClip preference. See BOUNDARY_XXX constants.
+ *
+ * @param value Set the PrintClip preference.
+ */
+ public void setPrintClip( String value )
+ {
+ prefs.setName( "PrintClip", value );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/package.html
new file mode 100644
index 0000000..310d6b4
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/viewerpreferences/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+A package to allow access to document viewing preferences.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/package.html b/src/main/java/org/pdfbox/pdmodel/package.html
new file mode 100644
index 0000000..85bbe62
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The PDModel package represents a high level API for creating and manipulating PDF documents.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pdmodel/text/PDTextState.java b/src/main/java/org/pdfbox/pdmodel/text/PDTextState.java
new file mode 100644
index 0000000..45e9516
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/text/PDTextState.java
@@ -0,0 +1,286 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.text;
+
+import org.pdfbox.pdmodel.font.PDFont;
+
+/**
+ * This class will hold the current state of the text parameters when executing a
+ * content stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDTextState implements Cloneable
+{
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_FILL_TEXT = 0;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_STROKE_TEXT = 1;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_FILL_THEN_STROKE_TEXT = 2;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_NEITHER_FILL_NOR_STROKE_TEXT = 3;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_FILL_TEXT_AND_ADD_TO_PATH_FOR_CLIPPING = 4;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_STROKE_TEXT_AND_ADD_TO_PATH_FOR_CLIPPING = 5;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_FILL_THEN_STROKE_TEXT_AND_ADD_TO_PATH_FOR_CLIPPING = 6;
+ /**
+ * See PDF Reference 1.5 Table 5.3.
+ */
+ public static final int RENDERING_MODE_ADD_TEXT_TO_PATH_FOR_CLIPPING = 7;
+
+
+ //these are set default according to PDF Reference 1.5 section 5.2
+ private float characterSpacing = 0;
+ private float wordSpacing = 0;
+ private float horizontalScaling = 100;
+ private float leading = 0;
+ private PDFont font;
+ private float fontSize;
+ private int renderingMode = 0;
+ private float rise = 0;
+ private boolean knockout = true;
+
+ /**
+ * Get the value of the characterSpacing.
+ *
+ * @return The current characterSpacing.
+ */
+ public float getCharacterSpacing()
+ {
+ return characterSpacing;
+ }
+
+ /**
+ * Set the value of the characterSpacing.
+ *
+ * @param value The characterSpacing.
+ */
+ public void setCharacterSpacing(float value)
+ {
+ characterSpacing = value;
+ }
+
+ /**
+ * Get the value of the wordSpacing.
+ *
+ * @return The wordSpacing.
+ */
+ public float getWordSpacing()
+ {
+ return wordSpacing;
+ }
+
+ /**
+ * Set the value of the wordSpacing.
+ *
+ * @param value The wordSpacing.
+ */
+ public void setWordSpacing(float value)
+ {
+ wordSpacing = value;
+ }
+
+ /**
+ * Get the value of the horizontalScaling. The default is 100. This value
+ * is the percentage value 0-100 and not 0-1. So for mathematical operations
+ * you will probably need to divide by 100 first.
+ *
+ * @return The horizontalScaling.
+ */
+ public float getHorizontalScalingPercent()
+ {
+ return horizontalScaling;
+ }
+
+ /**
+ * Set the value of the horizontalScaling.
+ *
+ * @param value The horizontalScaling.
+ */
+ public void setHorizontalScalingPercent(float value)
+ {
+ horizontalScaling = value;
+ }
+
+ /**
+ * Get the value of the leading.
+ *
+ * @return The leading.
+ */
+ public float getLeading()
+ {
+ return leading;
+ }
+
+ /**
+ * Set the value of the leading.
+ *
+ * @param value The leading.
+ */
+ public void setLeading(float value)
+ {
+ leading = value;
+ }
+
+ /**
+ * Get the value of the font.
+ *
+ * @return The font.
+ */
+ public PDFont getFont()
+ {
+ return font;
+ }
+
+ /**
+ * Set the value of the font.
+ *
+ * @param value The font.
+ */
+ public void setFont(PDFont value)
+ {
+ font = value;
+ }
+
+ /**
+ * Get the value of the fontSize.
+ *
+ * @return The fontSize.
+ */
+ public float getFontSize()
+ {
+ return fontSize;
+ }
+
+ /**
+ * Set the value of the fontSize.
+ *
+ * @param value The fontSize.
+ */
+ public void setFontSize(float value)
+ {
+ fontSize = value;
+ }
+
+ /**
+ * Get the value of the renderingMode.
+ *
+ * @return The renderingMode.
+ */
+ public int getRenderingMode()
+ {
+ return renderingMode;
+ }
+
+ /**
+ * Set the value of the renderingMode.
+ *
+ * @param value The renderingMode.
+ */
+ public void setRenderingMode(int value)
+ {
+ renderingMode = value;
+ }
+
+ /**
+ * Get the value of the rise.
+ *
+ * @return The rise.
+ */
+ public float getRise()
+ {
+ return rise;
+ }
+
+ /**
+ * Set the value of the rise.
+ *
+ * @param value The rise.
+ */
+ public void setRise(float value)
+ {
+ rise = value;
+ }
+
+ /**
+ * Get the value of the knockout.
+ *
+ * @return The knockout.
+ */
+ public boolean getKnockoutFlag()
+ {
+ return knockout;
+ }
+
+ /**
+ * Set the value of the knockout.
+ *
+ * @param value The knockout.
+ */
+ public void setKnockoutFlag(boolean value)
+ {
+ knockout = value;
+ }
+
+ /**
+ * @see Object#clone()
+ */
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException ignore)
+ {
+ //ignore
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/text/package.html b/src/main/java/org/pdfbox/pdmodel/text/package.html
new file mode 100644
index 0000000..50bb7c5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/text/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The PDModel text package deals with text states, operations, and parameters within the PDF document.
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/persistence/util/COSHEXTable.java b/src/main/java/org/pdfbox/persistence/util/COSHEXTable.java
new file mode 100644
index 0000000..b483213
--- /dev/null
+++ b/src/main/java/org/pdfbox/persistence/util/COSHEXTable.java
@@ -0,0 +1,566 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.persistence.util;
+
+/**
+ * helper type for faster mapping of bytes to their hex equivalent.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.4 $
+ */
+public final class COSHEXTable
+{
+ /**
+ * ASCII byte values for the hex strings.
+ */
+ public static final byte[][] TABLE =
+ {
+ "00".getBytes(),
+ "01".getBytes(),
+ "02".getBytes(),
+ "03".getBytes(),
+ "04".getBytes(),
+ "05".getBytes(),
+ "06".getBytes(),
+ "07".getBytes(),
+ "08".getBytes(),
+ "09".getBytes(),
+ "0A".getBytes(),
+ "0B".getBytes(),
+ "0C".getBytes(),
+ "0D".getBytes(),
+ "0E".getBytes(),
+ "0F".getBytes(),
+ "10".getBytes(),
+ "11".getBytes(),
+ "12".getBytes(),
+ "13".getBytes(),
+ "14".getBytes(),
+ "15".getBytes(),
+ "16".getBytes(),
+ "17".getBytes(),
+ "18".getBytes(),
+ "19".getBytes(),
+ "1A".getBytes(),
+ "1B".getBytes(),
+ "1C".getBytes(),
+ "1D".getBytes(),
+ "1E".getBytes(),
+ "1F".getBytes(),
+ "20".getBytes(),
+ "21".getBytes(),
+ "22".getBytes(),
+ "23".getBytes(),
+ "24".getBytes(),
+ "25".getBytes(),
+ "26".getBytes(),
+ "27".getBytes(),
+ "28".getBytes(),
+ "29".getBytes(),
+ "2A".getBytes(),
+ "2B".getBytes(),
+ "2C".getBytes(),
+ "2D".getBytes(),
+ "2E".getBytes(),
+ "2F".getBytes(),
+ "30".getBytes(),
+ "31".getBytes(),
+ "32".getBytes(),
+ "33".getBytes(),
+ "34".getBytes(),
+ "35".getBytes(),
+ "36".getBytes(),
+ "37".getBytes(),
+ "38".getBytes(),
+ "39".getBytes(),
+ "3A".getBytes(),
+ "3B".getBytes(),
+ "3C".getBytes(),
+ "3D".getBytes(),
+ "3E".getBytes(),
+ "3F".getBytes(),
+ "40".getBytes(),
+ "41".getBytes(),
+ "42".getBytes(),
+ "43".getBytes(),
+ "44".getBytes(),
+ "45".getBytes(),
+ "46".getBytes(),
+ "47".getBytes(),
+ "48".getBytes(),
+ "49".getBytes(),
+ "4A".getBytes(),
+ "4B".getBytes(),
+ "4C".getBytes(),
+ "4D".getBytes(),
+ "4E".getBytes(),
+ "4F".getBytes(),
+ "50".getBytes(),
+ "51".getBytes(),
+ "52".getBytes(),
+ "53".getBytes(),
+ "54".getBytes(),
+ "55".getBytes(),
+ "56".getBytes(),
+ "57".getBytes(),
+ "58".getBytes(),
+ "59".getBytes(),
+ "5A".getBytes(),
+ "5B".getBytes(),
+ "5C".getBytes(),
+ "5D".getBytes(),
+ "5E".getBytes(),
+ "5F".getBytes(),
+ "60".getBytes(),
+ "61".getBytes(),
+ "62".getBytes(),
+ "63".getBytes(),
+ "64".getBytes(),
+ "65".getBytes(),
+ "66".getBytes(),
+ "67".getBytes(),
+ "68".getBytes(),
+ "69".getBytes(),
+ "6A".getBytes(),
+ "6B".getBytes(),
+ "6C".getBytes(),
+ "6D".getBytes(),
+ "6E".getBytes(),
+ "6F".getBytes(),
+ "70".getBytes(),
+ "71".getBytes(),
+ "72".getBytes(),
+ "73".getBytes(),
+ "74".getBytes(),
+ "75".getBytes(),
+ "76".getBytes(),
+ "77".getBytes(),
+ "78".getBytes(),
+ "79".getBytes(),
+ "7A".getBytes(),
+ "7B".getBytes(),
+ "7C".getBytes(),
+ "7D".getBytes(),
+ "7E".getBytes(),
+ "7F".getBytes(),
+ "80".getBytes(),
+ "81".getBytes(),
+ "82".getBytes(),
+ "83".getBytes(),
+ "84".getBytes(),
+ "85".getBytes(),
+ "86".getBytes(),
+ "87".getBytes(),
+ "88".getBytes(),
+ "89".getBytes(),
+ "8A".getBytes(),
+ "8B".getBytes(),
+ "8C".getBytes(),
+ "8D".getBytes(),
+ "8E".getBytes(),
+ "8F".getBytes(),
+ "90".getBytes(),
+ "91".getBytes(),
+ "92".getBytes(),
+ "93".getBytes(),
+ "94".getBytes(),
+ "95".getBytes(),
+ "96".getBytes(),
+ "97".getBytes(),
+ "98".getBytes(),
+ "99".getBytes(),
+ "9A".getBytes(),
+ "9B".getBytes(),
+ "9C".getBytes(),
+ "9D".getBytes(),
+ "9E".getBytes(),
+ "9F".getBytes(),
+ "A0".getBytes(),
+ "A1".getBytes(),
+ "A2".getBytes(),
+ "A3".getBytes(),
+ "A4".getBytes(),
+ "A5".getBytes(),
+ "A6".getBytes(),
+ "A7".getBytes(),
+ "A8".getBytes(),
+ "A9".getBytes(),
+ "AA".getBytes(),
+ "AB".getBytes(),
+ "AC".getBytes(),
+ "AD".getBytes(),
+ "AE".getBytes(),
+ "AF".getBytes(),
+ "B0".getBytes(),
+ "B1".getBytes(),
+ "B2".getBytes(),
+ "B3".getBytes(),
+ "B4".getBytes(),
+ "B5".getBytes(),
+ "B6".getBytes(),
+ "B7".getBytes(),
+ "B8".getBytes(),
+ "B9".getBytes(),
+ "BA".getBytes(),
+ "BB".getBytes(),
+ "BC".getBytes(),
+ "BD".getBytes(),
+ "BE".getBytes(),
+ "BF".getBytes(),
+ "C0".getBytes(),
+ "C1".getBytes(),
+ "C2".getBytes(),
+ "C3".getBytes(),
+ "C4".getBytes(),
+ "C5".getBytes(),
+ "C6".getBytes(),
+ "C7".getBytes(),
+ "C8".getBytes(),
+ "C9".getBytes(),
+ "CA".getBytes(),
+ "CB".getBytes(),
+ "CC".getBytes(),
+ "CD".getBytes(),
+ "CE".getBytes(),
+ "CF".getBytes(),
+ "D0".getBytes(),
+ "D1".getBytes(),
+ "D2".getBytes(),
+ "D3".getBytes(),
+ "D4".getBytes(),
+ "D5".getBytes(),
+ "D6".getBytes(),
+ "D7".getBytes(),
+ "D8".getBytes(),
+ "D9".getBytes(),
+ "DA".getBytes(),
+ "DB".getBytes(),
+ "DC".getBytes(),
+ "DD".getBytes(),
+ "DE".getBytes(),
+ "DF".getBytes(),
+ "E0".getBytes(),
+ "E1".getBytes(),
+ "E2".getBytes(),
+ "E3".getBytes(),
+ "E4".getBytes(),
+ "E5".getBytes(),
+ "E6".getBytes(),
+ "E7".getBytes(),
+ "E8".getBytes(),
+ "E9".getBytes(),
+ "EA".getBytes(),
+ "EB".getBytes(),
+ "EC".getBytes(),
+ "ED".getBytes(),
+ "EE".getBytes(),
+ "EF".getBytes(),
+ "F0".getBytes(),
+ "F1".getBytes(),
+ "F2".getBytes(),
+ "F3".getBytes(),
+ "F4".getBytes(),
+ "F5".getBytes(),
+ "F6".getBytes(),
+ "F7".getBytes(),
+ "F8".getBytes(),
+ "F9".getBytes(),
+ "FA".getBytes(),
+ "FB".getBytes(),
+ "FC".getBytes(),
+ "FD".getBytes(),
+ "FE".getBytes(),
+ "FF".getBytes()
+ };
+
+ /**
+ * ASCII byte values for the hex strings.
+ */
+ public static final String[] HEX_TABLE =
+ {
+ "00",
+ "01",
+ "02",
+ "03",
+ "04",
+ "05",
+ "06",
+ "07",
+ "08",
+ "09",
+ "0A",
+ "0B",
+ "0C",
+ "0D",
+ "0E",
+ "0F",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "17",
+ "18",
+ "19",
+ "1A",
+ "1B",
+ "1C",
+ "1D",
+ "1E",
+ "1F",
+ "20",
+ "21",
+ "22",
+ "23",
+ "24",
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "2A",
+ "2B",
+ "2C",
+ "2D",
+ "2E",
+ "2F",
+ "30",
+ "31",
+ "32",
+ "33",
+ "34",
+ "35",
+ "36",
+ "37",
+ "38",
+ "39",
+ "3A",
+ "3B",
+ "3C",
+ "3D",
+ "3E",
+ "3F",
+ "40",
+ "41",
+ "42",
+ "43",
+ "44",
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "4A",
+ "4B",
+ "4C",
+ "4D",
+ "4E",
+ "4F",
+ "50",
+ "51",
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "57",
+ "58",
+ "59",
+ "5A",
+ "5B",
+ "5C",
+ "5D",
+ "5E",
+ "5F",
+ "60",
+ "61",
+ "62",
+ "63",
+ "64",
+ "65",
+ "66",
+ "67",
+ "68",
+ "69",
+ "6A",
+ "6B",
+ "6C",
+ "6D",
+ "6E",
+ "6F",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "75",
+ "76",
+ "77",
+ "78",
+ "79",
+ "7A",
+ "7B",
+ "7C",
+ "7D",
+ "7E",
+ "7F",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85",
+ "86",
+ "87",
+ "88",
+ "89",
+ "8A",
+ "8B",
+ "8C",
+ "8D",
+ "8E",
+ "8F",
+ "90",
+ "91",
+ "92",
+ "93",
+ "94",
+ "95",
+ "96",
+ "97",
+ "98",
+ "99",
+ "9A",
+ "9B",
+ "9C",
+ "9D",
+ "9E",
+ "9F",
+ "A0",
+ "A1",
+ "A2",
+ "A3",
+ "A4",
+ "A5",
+ "A6",
+ "A7",
+ "A8",
+ "A9",
+ "AA",
+ "AB",
+ "AC",
+ "AD",
+ "AE",
+ "AF",
+ "B0",
+ "B1",
+ "B2",
+ "B3",
+ "B4",
+ "B5",
+ "B6",
+ "B7",
+ "B8",
+ "B9",
+ "BA",
+ "BB",
+ "BC",
+ "BD",
+ "BE",
+ "BF",
+ "C0",
+ "C1",
+ "C2",
+ "C3",
+ "C4",
+ "C5",
+ "C6",
+ "C7",
+ "C8",
+ "C9",
+ "CA",
+ "CB",
+ "CC",
+ "CD",
+ "CE",
+ "CF",
+ "D0",
+ "D1",
+ "D2",
+ "D3",
+ "D4",
+ "D5",
+ "D6",
+ "D7",
+ "D8",
+ "D9",
+ "DA",
+ "DB",
+ "DC",
+ "DD",
+ "DE",
+ "DF",
+ "E0",
+ "E1",
+ "E2",
+ "E3",
+ "E4",
+ "E5",
+ "E6",
+ "E7",
+ "E8",
+ "E9",
+ "EA",
+ "EB",
+ "EC",
+ "ED",
+ "EE",
+ "EF",
+ "F0",
+ "F1",
+ "F2",
+ "F3",
+ "F4",
+ "F5",
+ "F6",
+ "F7",
+ "F8",
+ "F9",
+ "FA",
+ "FB",
+ "FC",
+ "FD",
+ "FE",
+ "FF"
+ };
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/persistence/util/COSObjectKey.java b/src/main/java/org/pdfbox/persistence/util/COSObjectKey.java
new file mode 100644
index 0000000..4a4d778
--- /dev/null
+++ b/src/main/java/org/pdfbox/persistence/util/COSObjectKey.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.persistence.util;
+
+import org.pdfbox.cos.COSObject;
+
+/**
+ * object representing the physical reference to an indirect pdf object.
+ *
+ * @author Michael Traut
+ * @version $Revision: 1.4 $
+ */
+
+public class COSObjectKey
+{
+ private long number;
+ private long generation;
+
+ /**
+ * PDFObjectKey constructor comment.
+ *
+ * @param object The object that this key will represent.
+ */
+ public COSObjectKey(COSObject object)
+ {
+ this( object.getObjectNumber().longValue(), object.getGenerationNumber().longValue() );
+ }
+
+ /**
+ * PDFObjectKey constructor comment.
+ *
+ * @param num The object number.
+ * @param gen The object generation number.
+ */
+ public COSObjectKey(long num, long gen)
+ {
+ setNumber(num);
+ setGeneration(gen);
+ }
+
+ /**
+ * @see Object#equals( Object )
+ */
+ public boolean equals(Object obj)
+ {
+ return (obj instanceof COSObjectKey) &&
+ ((COSObjectKey)obj).getNumber() == getNumber() &&
+ ((COSObjectKey)obj).getGeneration() == getGeneration();
+ }
+
+ /**
+ * This will get the generation number.
+ *
+ * @return The objects generation number.
+ */
+ public long getGeneration()
+ {
+ return generation;
+ }
+ /**
+ * This will get the objects id.
+ *
+ * @return The object's id.
+ */
+ public long getNumber()
+ {
+ return number;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return (int)(number + generation);
+ }
+ /**
+ * This will set the objects generation number.
+ *
+ * @param newGeneration The objects generation number.
+ */
+ public void setGeneration(long newGeneration)
+ {
+ generation = newGeneration;
+ }
+ /**
+ * This will set the objects id.
+ *
+ * @param newNumber The objects number.
+ */
+ public void setNumber(long newNumber)
+ {
+ number = newNumber;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ return "" + getNumber() + " " + getGeneration() + " R";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/persistence/util/package.html b/src/main/java/org/pdfbox/persistence/util/package.html
new file mode 100644
index 0000000..fb8a0f8
--- /dev/null
+++ b/src/main/java/org/pdfbox/persistence/util/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+These are utilities used by the persistence layer.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/pfb/PfbParser.java b/src/main/java/org/pdfbox/pfb/PfbParser.java
new file mode 100644
index 0000000..ab069d6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pfb/PfbParser.java
@@ -0,0 +1,211 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+
+package org.pdfbox.pfb;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Parser for a pfb-file.
+ *
+ * @author <a href="mailto:m.g.n@gmx.de">Michael Niedermair</a>
+ * @version $Revision: 1.3 $
+ */
+public class PfbParser
+{
+ /**
+ * the pdf header length.
+ * (start-marker (1 byte), ascii-/binary-marker (1 byte), size (4 byte))
+ * 3*6 == 18
+ */
+ private static final int PFB_HEADER_LENGTH = 18;
+
+ /**
+ * the start marker.
+ */
+ private static final int START_MARKER = 0x80;
+
+ /**
+ * the ascii marker.
+ */
+ private static final int ASCII_MARKER = 0x01;
+
+ /**
+ * the binary marker.
+ */
+ private static final int BINARY_MARKER = 0x02;
+
+ /**
+ * The record types in the pfb-file.
+ */
+ private static final int[] PFB_RECORDS = {ASCII_MARKER, BINARY_MARKER,
+ ASCII_MARKER};
+
+ /**
+ * buffersize.
+ */
+ private static final int BUFFER_SIZE = 0xffff;
+
+ /**
+ * the parsed pfb-data.
+ */
+ private byte[] pfbdata;
+
+ /**
+ * the lengths of the records.
+ */
+ private int[] lengths;
+
+ // sample (pfb-file)
+ // 00000000 80 01 8b 15 00 00 25 21 50 53 2d 41 64 6f 62 65
+ // ......%!PS-Adobe
+
+
+ /**
+ * Create a new object.
+ * @param filename the file name
+ * @throws IOException if an IO-error occurs.
+ */
+ public PfbParser(final String filename) throws IOException
+ {
+ this( new BufferedInputStream(new FileInputStream(filename),BUFFER_SIZE) );
+ }
+
+ /**
+ * Create a new object.
+ * @param in The input.
+ * @throws IOException if an IO-error occurs.
+ */
+ public PfbParser(final InputStream in) throws IOException
+ {
+ byte[] pfb = readPfbInput(in);
+ parsePfb(pfb);
+ }
+
+ /**
+ * Parse the pfb-array.
+ * @param pfb The pfb-Array
+ * @throws IOException in an IO-error occurs.
+ */
+ private void parsePfb(final byte[] pfb) throws IOException
+ {
+
+ ByteArrayInputStream in = new ByteArrayInputStream(pfb);
+ pfbdata = new byte[pfb.length - PFB_HEADER_LENGTH];
+ lengths = new int[PFB_RECORDS.length];
+ int pointer = 0;
+ for (int records = 0; records < PFB_RECORDS.length; records++)
+ {
+ if (in.read() != START_MARKER)
+ {
+ throw new IOException("Start marker missing");
+ }
+
+ if (in.read() != PFB_RECORDS[records])
+ {
+ throw new IOException("Incorrect record type");
+ }
+
+ int size = in.read();
+ size += in.read() << 8;
+ size += in.read() << 16;
+ size += in.read() << 24;
+ lengths[records] = size;
+ int got = in.read(pfbdata, pointer, size);
+ if (got < 0)
+ {
+ throw new EOFException();
+ }
+ pointer += got;
+ }
+ }
+
+ /**
+ * Read the pdf input.
+ * @param in The input.
+ * @return Returns the pdf-array.
+ * @throws IOException if an IO-error occurs.
+ */
+ private byte[] readPfbInput(final InputStream in) throws IOException
+ {
+ // copy into an array
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] tmpbuf = new byte[BUFFER_SIZE];
+ int amountRead = -1;
+ while ((amountRead = in.read(tmpbuf)) != -1)
+ {
+ out.write(tmpbuf, 0, amountRead);
+ }
+ return out.toByteArray();
+ }
+
+ /**
+ * Returns the lengths.
+ * @return Returns the lengths.
+ */
+ public int[] getLengths()
+ {
+ return lengths;
+ }
+
+ /**
+ * Returns the pfbdata.
+ * @return Returns the pfbdata.
+ */
+ public byte[] getPfbdata()
+ {
+ return pfbdata;
+ }
+
+ /**
+ * Returns the pfb data as stream.
+ * @return Returns the pfb data as stream.
+ */
+ public InputStream getInputStream()
+ {
+ return new ByteArrayInputStream(pfbdata);
+ }
+
+ /**
+ * Returns the size of the pfb-data.
+ * @return Returns the size of the pfb-data.
+ */
+ public int size()
+ {
+ return pfbdata.length;
+ }
+}
diff --git a/src/main/java/org/pdfbox/pfb/package.html b/src/main/java/org/pdfbox/pfb/package.html
new file mode 100644
index 0000000..fcc23f0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pfb/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+Classes that are used to parse pfb files.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/searchengine/lucene/IndexFiles.java b/src/main/java/org/pdfbox/searchengine/lucene/IndexFiles.java
new file mode 100644
index 0000000..dd36dd9
--- /dev/null
+++ b/src/main/java/org/pdfbox/searchengine/lucene/IndexFiles.java
@@ -0,0 +1,308 @@
+package org.pdfbox.searchengine.lucene;
+
+/*
+ * This source was originally written as an example for the lucene project.
+ * It has been modified to use PDFBox as a lucene document creator.
+ * -Ben Litchfield
+ *
+ *====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache Lucene" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache Lucene", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+import org.apache.lucene.demo.HTMLDocument;
+
+import org.apache.lucene.document.Document;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+
+import java.util.Arrays;
+
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Date;
+
+
+/**
+ * This is a class that will index some files on a local filesystem. This code
+ * was modified from a demo that comes with the lucene search engine.
+ *
+ * @author Lucene team
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ *
+ * @version $Revision: 1.6 $
+ */
+public class IndexFiles
+{
+ private boolean deleting = false; // true during deletion pass
+ private IndexReader reader; // existing index
+ private IndexWriter writer; // new index being built
+ private TermEnum uidIter; // document id iterator
+
+ /**
+ * This is the main entry point for the indexer.
+ *
+ * @param argv The command line arguments.
+ */
+ public static void main(String[] argv)
+ {
+
+ String index = "index";
+ boolean create = false;
+ File root = null;
+
+ String usage = "org.pdfbox.searchengine.lucene.IndexFiles [-create] [-index <index>] <root_directory>";
+
+ if (argv.length == 0)
+ {
+ System.err.println("Usage: " + usage);
+ return;
+ }
+
+ for (int i = 0; i < argv.length; i++)
+ {
+ if (argv[i].equals("-index"))
+ { // parse -index option
+ index = argv[++i];
+ }
+ else if (argv[i].equals("-create"))
+ { // parse -create option
+ create = true;
+ }
+ else if (i != argv.length-1)
+ {
+ System.err.println("Usage: " + usage);
+ return;
+ }
+ else
+ {
+ System.out.println( "root=" +argv[i] );
+ root = new File(argv[i]);
+ }
+ }
+ IndexFiles indexer = new IndexFiles();
+ indexer.index( root, create, index );
+ }
+
+ /**
+ * This will index a directory.
+ *
+ * @param root The root directory to start indexing.
+ * @param create Should we create a new index?
+ * @param index The name of the index.
+ */
+ public void index( File root, boolean create, String index )
+ {
+
+ try
+ {
+ Date start = new Date();
+
+ writer = new IndexWriter(index, new StandardAnalyzer(), create);
+ writer.maxFieldLength = 1000000;
+
+ if (!create)
+ { // delete stale docs
+ deleting = true;
+ indexDocs(root, index, create);
+ }
+
+ indexDocs(root, index, create); // add new docs
+
+ System.out.println("Optimizing index...");
+ writer.optimize();
+ writer.close();
+
+ Date end = new Date();
+
+ System.out.print(end.getTime() - start.getTime());
+ System.out.println(" total milliseconds");
+
+ }
+ catch( Exception e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Walk directory hierarchy in uid order, while keeping uid iterator from
+ * existing index in sync. Mismatches indicate one of: (a) old documents to
+ * be deleted; (b) unchanged documents, to be left alone; or (c) new
+ * documents, to be indexed.
+ *
+ * @param file The directory to index.
+ * @param index The index to add the file to.
+ * @param create A flag telling if we should create the index.
+ *
+ * @throws Exception If there is any error indexing the directory.
+ */
+ private void indexDocs(File file, String index, boolean create) throws Exception
+ {
+ if (!create)
+ { // incrementally update
+
+ reader = IndexReader.open(index); // open existing index
+ uidIter = reader.terms(new Term("uid", "")); // init uid iterator
+
+ indexDocs(file);
+
+ if (deleting)
+ { // delete rest of stale docs
+ while (uidIter.term() != null && uidIter.term().field().equals( "uid" ) )
+ {
+ System.out.println("deleting " +
+ HTMLDocument.uid2url(uidIter.term().text()));
+ reader.delete(uidIter.term());
+ uidIter.next();
+ }
+ deleting = false;
+ }
+
+ uidIter.close(); // close uid iterator
+ reader.close(); // close existing index
+
+ }
+ else
+ {
+ indexDocs(file);
+ }
+ }
+
+
+ private void indexDocs(File file) throws Exception
+ {
+ if (file.isDirectory())
+ { // if a directory
+ String[] files = file.list(); // list its files
+ Arrays.sort(files); // sort the files
+ for (int i = 0; i < files.length; i++) // recursively index them
+ {
+ indexDocs(new File(file, files[i]));
+ }
+ }
+ else
+ {
+ if (uidIter != null)
+ {
+ String uid = HTMLDocument.uid(file); // construct uid for doc
+
+ while( uidIter.term() != null &&
+ uidIter.term().field().equals( "uid" ) &&
+ uidIter.term().text().compareTo(uid) < 0)
+ {
+ if (deleting)
+ { // delete stale docs
+ System.out.println("deleting " +
+ HTMLDocument.uid2url(uidIter.term().text()));
+ reader.delete(uidIter.term());
+ }
+ uidIter.next();
+ }
+ if( uidIter.term() != null &&
+ uidIter.term().field().equals( "uid" ) &&
+ uidIter.term().text().compareTo(uid) == 0)
+ {
+ System.out.println( "Next uid=" +uidIter );
+ uidIter.next(); // keep matching docs
+ }
+ }
+ else
+ {
+ try
+ {
+ addDocument( file );
+ }
+ catch( IOException e )
+ {
+ //catch exception and move onto the next document
+ System.out.println( e.getMessage() );
+ }
+ }
+ }
+ }
+
+ private void addDocument( File file ) throws IOException, InterruptedException
+ {
+ String path = file.getName().toUpperCase();
+ Document doc = null;
+ //Gee, this would be a great place for a command pattern
+ if( path.endsWith(".HTML") || // index .html files
+ path.endsWith(".HTM") || // index .htm files
+ path.endsWith(".TXT"))
+ {
+ System.out.println( "Indexing Text document: " + file );
+ doc = HTMLDocument.Document(file);
+ }
+ else if( path.endsWith( ".PDF" ) )
+ {
+ System.out.println( "Indexing PDF document: " + file );
+ doc = LucenePDFDocument.getDocument( file );
+ }
+ else
+ {
+ System.out.println( "Skipping " + file );
+ }
+
+ if( doc != null )
+ {
+ writer.addDocument(doc);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/searchengine/lucene/LucenePDFDocument.java b/src/main/java/org/pdfbox/searchengine/lucene/LucenePDFDocument.java
new file mode 100644
index 0000000..1bb8d0c
--- /dev/null
+++ b/src/main/java/org/pdfbox/searchengine/lucene/LucenePDFDocument.java
@@ -0,0 +1,387 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.searchengine.lucene;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import java.net.URL;
+import java.net.URLConnection;
+
+import java.util.Date;
+
+import org.apache.lucene.document.DateField;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDDocumentInformation;
+
+import org.pdfbox.exceptions.CryptographyException;
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.pdfbox.util.PDFTextStripper;
+
+/**
+ * This class is used to create a document for the lucene search engine.
+ * This should easily plug into the IndexHTML or IndexFiles that comes with
+ * the lucene project. This class will populate the following fields.
+ * <table>
+ * <tr>
+ * <td>Lucene Field Name</td>
+ * <td>Description</td>
+ * </tr>
+ * <tr>
+ * <td>path</td>
+ * <td>File system path if loaded from a file</td>
+ * </tr>
+ * <tr>
+ * <td>url</td>
+ * <td>URL to PDF document</td>
+ * </tr>
+ * <tr>
+ * <td>contents</td>
+ * <td>Entire contents of PDF document, indexed but not stored</td>
+ * </tr>
+ * <tr>
+ * <td>summary</td>
+ * <td>First 500 characters of content</td>
+ * </tr>
+ * <tr>
+ * <td>modified</td>
+ * <td>The modified date/time according to the url or path</td>
+ * </tr>
+ * <tr>
+ * <td>uid</td>
+ * <td>A unique identifier for the Lucene document.</td>
+ * </tr>
+ * <tr>
+ * <td>CreationDate</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * <tr>
+ * <td>Creator</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * <tr>
+ * <td>Keywords</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * <tr>
+ * <td>ModificationDate</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * <tr>
+ * <td>Producer</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * <tr>
+ * <td>Subject</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * <tr>
+ * <td>Trapped</td>
+ * <td>From PDF meta-data if available</td>
+ * </tr>
+ * </table>
+ *
+ * @author Ben Litchfield
+ * @version $Revision: 1.18 $
+ */
+public final class LucenePDFDocument
+{
+ private static final char FILE_SEPARATOR = System.getProperty("file.separator").charAt(0);
+
+
+ /**
+ * private constructor because there are only static methods.
+ */
+ private LucenePDFDocument()
+ {
+ //utility class should not be instantiated
+ }
+
+ /**
+ * This will get a lucene document from a PDF file.
+ *
+ * @param is The stream to read the PDF from.
+ *
+ * @return The lucene document.
+ *
+ * @throws IOException If there is an error parsing or indexing the document.
+ */
+ public static Document getDocument( InputStream is ) throws IOException
+ {
+ Document document = new Document();
+ addContent( document, is, "<inputstream>" );
+ return document;
+ }
+
+ /**
+ * This will get a lucene document from a PDF file.
+ *
+ * @param file The file to get the document for.
+ *
+ * @return The lucene document.
+ *
+ * @throws IOException If there is an error parsing or indexing the document.
+ */
+ public static Document getDocument( File file ) throws IOException
+ {
+ Document document = new Document();
+
+ // Add the url as a field named "url". Use an UnIndexed field, so
+ // that the url is just stored with the document, but is not searchable.
+ document.add( Field.UnIndexed("path", file.getPath() ) );
+ document.add(Field.UnIndexed("url", file.getPath().replace(FILE_SEPARATOR, '/')));
+
+ // Add the last modified date of the file a field named "modified". Use a
+ // Keyword field, so that it's searchable, but so that no attempt is made
+ // to tokenize the field into words.
+ document.add(Field.Keyword("modified", DateField.timeToString( file.lastModified() )));
+
+ String uid = file.getPath().replace(FILE_SEPARATOR, '\u0000') + "\u0000" +
+ DateField.timeToString(file.lastModified() );
+
+ // Add the uid as a field, so that index can be incrementally maintained.
+ // This field is not stored with document, it is indexed, but it is not
+ // tokenized prior to indexing.
+ document.add(new Field("uid", uid, false, true, false));
+
+ FileInputStream input = null;
+ try
+ {
+ input = new FileInputStream( file );
+ addContent( document, input, file.getPath() );
+ }
+ finally
+ {
+ if( input != null )
+ {
+ input.close();
+ }
+ }
+
+
+ // return the document
+
+ return document;
+ }
+
+ /**
+ * This will get a lucene document from a PDF file.
+ *
+ * @param url The file to get the document for.
+ *
+ * @return The lucene document.
+ *
+ * @throws IOException If there is an error parsing or indexing the document.
+ */
+ public static Document getDocument( URL url ) throws IOException
+ {
+ Document document = new Document();
+ URLConnection connection = url.openConnection();
+ connection.connect();
+ // Add the url as a field named "url". Use an UnIndexed field, so
+ // that the url is just stored with the document, but is not searchable.
+ document.add( Field.UnIndexed("url", url.toExternalForm() ) );
+
+ // Add the last modified date of the file a field named "modified". Use a
+ // Keyword field, so that it's searchable, but so that no attempt is made
+ // to tokenize the field into words.
+ document.add(Field.Keyword("modified", DateField.timeToString( connection.getLastModified())));
+
+ String uid = url.toExternalForm().replace(FILE_SEPARATOR, '\u0000') + "\u0000" +
+ DateField.timeToString( connection.getLastModified() );
+
+ // Add the uid as a field, so that index can be incrementally maintained.
+ // This field is not stored with document, it is indexed, but it is not
+ // tokenized prior to indexing.
+ document.add(new Field("uid", uid, false, true, false));
+
+ InputStream input = null;
+ try
+ {
+ input = connection.getInputStream();
+ addContent( document, input,url.toExternalForm() );
+ }
+ finally
+ {
+ if( input != null )
+ {
+ input.close();
+ }
+ }
+
+ // return the document
+ return document;
+ }
+
+ /**
+ * This will add the contents to the lucene document.
+ *
+ * @param document The document to add the contents to.
+ * @param is The stream to get the contents from.
+ * @param documentLocation The location of the document, used just for debug messages.
+ *
+ * @throws IOException If there is an error parsing the document.
+ */
+ private static void addContent( Document document, InputStream is, String documentLocation ) throws IOException
+ {
+ PDDocument pdfDocument = null;
+ try
+ {
+ pdfDocument = PDDocument.load( is );
+
+
+ if( pdfDocument.isEncrypted() )
+ {
+ //Just try using the default password and move on
+ pdfDocument.decrypt( "" );
+ }
+
+ //create a writer where to append the text content.
+ StringWriter writer = new StringWriter();
+ PDFTextStripper stripper = new PDFTextStripper();
+ stripper.writeText( pdfDocument, writer );
+
+ // Note: the buffer to string operation is costless;
+ // the char array value of the writer buffer and the content string
+ // is shared as long as the buffer content is not modified, which will
+ // not occur here.
+ String contents = writer.getBuffer().toString();
+
+ StringReader reader = new StringReader( contents );
+
+ // Add the tag-stripped contents as a Reader-valued Text field so it will
+ // get tokenized and indexed.
+ document.add( Field.Text( "contents", reader ) );
+
+ PDDocumentInformation info = pdfDocument.getDocumentInformation();
+ if( info.getAuthor() != null )
+ {
+ document.add(Field.Text( "Author", info.getAuthor() ) );
+ }
+ if( info.getCreationDate() != null )
+ {
+ Date date = info.getCreationDate().getTime();
+ //for some reason lucene cannot handle dates before the epoch
+ //and throws a nasty RuntimeException, so we will check and
+ //verify that this does not happen
+ if( date.getTime() >= 0 )
+ {
+ document.add(Field.Text("CreationDate", DateField.dateToString( date ) ) );
+ }
+ }
+ if( info.getCreator() != null )
+ {
+ document.add( Field.Text( "Creator", info.getCreator() ) );
+ }
+ if( info.getKeywords() != null )
+ {
+ document.add( Field.Text( "Keywords", info.getKeywords() ) );
+ }
+ if( info.getModificationDate() != null )
+ {
+ Date date = info.getModificationDate().getTime();
+ //for some reason lucene cannot handle dates before the epoch
+ //and throws a nasty RuntimeException, so we will check and
+ //verify that this does not happen
+ if( date.getTime() >= 0 )
+ {
+ document.add(Field.Text("ModificationDate", DateField.dateToString( date ) ) );
+ }
+ }
+ if( info.getProducer() != null )
+ {
+ document.add( Field.Text( "Producer", info.getProducer() ) );
+ }
+ if( info.getSubject() != null )
+ {
+ document.add( Field.Text( "Subject", info.getSubject() ) );
+ }
+ if( info.getTitle() != null )
+ {
+ document.add( Field.Text( "Title", info.getTitle() ) );
+ }
+ if( info.getTrapped() != null )
+ {
+ document.add( Field.Text( "Trapped", info.getTrapped() ) );
+ }
+
+ int summarySize = Math.min( contents.length(), 500 );
+ String summary = contents.substring( 0, summarySize );
+ // Add the summary as an UnIndexed field, so that it is stored and returned
+ // with hit documents for display.
+ document.add( Field.UnIndexed( "summary", summary ) );
+ }
+ catch( CryptographyException e )
+ {
+ throw new IOException( "Error decrypting document(" + documentLocation + "): " + e );
+ }
+ catch( InvalidPasswordException e )
+ {
+ //they didn't suppply a password and the default of "" was wrong.
+ throw new IOException( "Error: The document(" + documentLocation +
+ ") is encrypted and will not be indexed." );
+ }
+ finally
+ {
+ if( pdfDocument != null )
+ {
+ pdfDocument.close();
+ }
+ }
+ }
+
+ /**
+ * This will test creating a document.
+ *
+ * usage: java pdfparser.searchengine.lucene.LucenePDFDocument &lt;pdf-document&gt;
+ *
+ * @param args command line arguments.
+ *
+ * @throws IOException If there is an error.
+ */
+ public static void main( String[] args ) throws IOException
+ {
+ if( args.length != 1 )
+ {
+ System.err.println( "usage: java org.pdfbox.searchengine.lucene.LucenePDFDocument <pdf-document>" );
+ System.exit( 1 );
+ }
+ System.out.println( "Document=" + getDocument( new File( args[0] ) ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/searchengine/lucene/package.html b/src/main/java/org/pdfbox/searchengine/lucene/package.html
new file mode 100644
index 0000000..fbf3a38
--- /dev/null
+++ b/src/main/java/org/pdfbox/searchengine/lucene/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package holds classes that are used to integrate the PDFBox project with lucene.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/searchengine/package.html b/src/main/java/org/pdfbox/searchengine/package.html
new file mode 100644
index 0000000..1cb4629
--- /dev/null
+++ b/src/main/java/org/pdfbox/searchengine/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+Classes that are used to integrate PDFBox with a search engine are located here.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/ttf/CMAPEncodingEntry.java b/src/main/java/org/pdfbox/ttf/CMAPEncodingEntry.java
new file mode 100644
index 0000000..a740203
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/CMAPEncodingEntry.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * An encoding entry for a cmap.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class CMAPEncodingEntry
+{
+
+ private int platformId;
+ private int platformEncodingId;
+ private long subTableOffset;
+ private int[] glyphIdToCharacterCode;
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ platformId = data.readUnsignedShort();
+ platformEncodingId = data.readUnsignedShort();
+ subTableOffset = data.readUnsignedInt();
+ }
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initSubtable( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ data.seek( ttf.getCMAP().getOffset() + subTableOffset );
+ int subtableFormat = data.readUnsignedShort();
+ int length = data.readUnsignedShort();
+ int version = data.readUnsignedShort();
+ int numGlyphs = ttf.getMaximumProfile().getNumGlyphs();
+ if( subtableFormat == 0 )
+ {
+ byte[] glyphMapping = data.read( 256 );
+ glyphIdToCharacterCode = new int[256];
+ for( int i=0;i<glyphMapping.length; i++ )
+ {
+ glyphIdToCharacterCode[i]=(glyphMapping[i]+256)%256;
+ }
+ }
+ else if( subtableFormat == 2 )
+ {
+ int[] subHeaderKeys = new int[256];
+ for( int i=0; i<256; i++)
+ {
+ subHeaderKeys[i] = data.readUnsignedShort();
+ }
+ int firstCode = data.readUnsignedShort();
+ int entryCount = data.readUnsignedShort();
+ short idDelta = data.readSignedShort();
+ int idRangeOffset = data.readUnsignedShort();
+ //BJL
+ //HMM the TTF spec is not very clear about what is suppose to
+ //happen here. If you know please submit a patch or point
+ //me to some better documentation.
+ throw new IOException( "Not yet implemented:" + subtableFormat );
+ }
+ else if( subtableFormat == 4 )
+ {
+ int segCountX2 = data.readUnsignedShort();
+ int segCount = segCountX2/2;
+ int searchRange = data.readUnsignedShort();
+ int entrySelector = data.readUnsignedShort();
+ int rangeShift = data.readUnsignedShort();
+ int[] endCount = data.readUnsignedShortArray( segCount );
+ int reservedPad = data.readUnsignedShort();
+ int[] startCount = data.readUnsignedShortArray( segCount );
+ int[] idDelta = data.readUnsignedShortArray( segCount );
+ int[] idRangeOffset = data.readUnsignedShortArray( segCount );
+
+ //this is the final result
+ //key=glyphId, value is character codes
+ glyphIdToCharacterCode = new int[numGlyphs];
+
+ long currentPosition = data.getCurrentPosition();
+
+ for( int i=0; i<segCount; i++ )
+ {
+ int start = startCount[i];
+ int end = endCount[i];
+ int delta = idDelta[i];
+ int rangeOffset = idRangeOffset[i];
+ if( start != 65536 && end != 65536 )
+ {
+ for( int j=start; j<=end; j++ )
+ {
+ if( rangeOffset == 0 )
+ {
+ glyphIdToCharacterCode[ ((j+delta)%65536) ]=j;
+ }
+ else
+ {
+ long glyphOffset = currentPosition +
+ ((rangeOffset/2) + //idRangeOffset[i]/2
+ (j-start) + //(c - startCount[i])
+ (i-segCount))*2; //&idRangeOffset[i]);
+ data.seek( glyphOffset );
+ int glyphIndex = data.readUnsignedShort();
+ if( glyphIndex != 0 )
+ {
+ glyphIndex += delta;
+ glyphIndex = glyphIndex % 65536;
+ if( glyphIdToCharacterCode[glyphIndex] == 0 )
+ {
+ glyphIdToCharacterCode[glyphIndex] = j;
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ else if( subtableFormat == 6 )
+ {
+ int firstCode = data.readUnsignedShort();
+ int entryCount = data.readUnsignedShort();
+ glyphIdToCharacterCode = new int[numGlyphs];
+ int[] glyphIdArray = data.readUnsignedShortArray( entryCount );
+ for( int i=0; i<entryCount; i++)
+ {
+ glyphIdToCharacterCode[glyphIdArray[i]] = firstCode+i;
+ }
+ }
+ else
+ {
+ throw new IOException( "Unknown cmap format:" + subtableFormat );
+ }
+ }
+
+
+ /**
+ * @return Returns the glyphIdToCharacterCode.
+ */
+ public int[] getGlyphIdToCharacterCode()
+ {
+ return glyphIdToCharacterCode;
+ }
+ /**
+ * @param glyphIdToCharacterCodeValue The glyphIdToCharacterCode to set.
+ */
+ public void setGlyphIdToCharacterCode(int[] glyphIdToCharacterCodeValue)
+ {
+ this.glyphIdToCharacterCode = glyphIdToCharacterCodeValue;
+ }
+
+ /**
+ * @return Returns the platformEncodingId.
+ */
+ public int getPlatformEncodingId()
+ {
+ return platformEncodingId;
+ }
+ /**
+ * @param platformEncodingIdValue The platformEncodingId to set.
+ */
+ public void setPlatformEncodingId(int platformEncodingIdValue)
+ {
+ this.platformEncodingId = platformEncodingIdValue;
+ }
+ /**
+ * @return Returns the platformId.
+ */
+ public int getPlatformId()
+ {
+ return platformId;
+ }
+ /**
+ * @param platformIdValue The platformId to set.
+ */
+ public void setPlatformId(int platformIdValue)
+ {
+ this.platformId = platformIdValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/CMAPTable.java b/src/main/java/org/pdfbox/ttf/CMAPTable.java
new file mode 100644
index 0000000..92a74bc
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/CMAPTable.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class CMAPTable extends TTFTable
+{
+ /**
+ * A tag used to identify this table.
+ */
+ public static final String TAG = "cmap";
+
+ /**
+ * A constant for the platform.
+ */
+ public static final int PLATFORM_WINDOWS = 3;
+
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_SYMBOL = 0;
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_UNICODE = 1;
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_SHIFT_JIS = 2;
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_BIG5 = 3;
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_PRC = 4;
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_WANSUNG = 5;
+ /**
+ * An encoding constant.
+ */
+ public static final int ENCODING_JOHAB = 6;
+
+ private CMAPEncodingEntry[] cmaps;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ int version = data.readUnsignedShort();
+ int numberOfTables = data.readUnsignedShort();
+ cmaps = new CMAPEncodingEntry[ numberOfTables ];
+ for( int i=0; i< numberOfTables; i++ )
+ {
+ CMAPEncodingEntry cmap = new CMAPEncodingEntry();
+ cmap.initData( ttf, data );
+ cmaps[i]=cmap;
+ }
+ for( int i=0; i< numberOfTables; i++ )
+ {
+ cmaps[i].initSubtable( ttf, data );
+ }
+
+ }
+ /**
+ * @return Returns the cmaps.
+ */
+ public CMAPEncodingEntry[] getCmaps()
+ {
+ return cmaps;
+ }
+ /**
+ * @param cmapsValue The cmaps to set.
+ */
+ public void setCmaps(CMAPEncodingEntry[] cmapsValue)
+ {
+ this.cmaps = cmapsValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/DigitalSignatureTable.java b/src/main/java/org/pdfbox/ttf/DigitalSignatureTable.java
new file mode 100644
index 0000000..67ae170
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/DigitalSignatureTable.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class DigitalSignatureTable extends TTFTable
+{
+ /**
+ * Tag to identify this table.
+ */
+ public static final String TAG = "DSIG";
+}
diff --git a/src/main/java/org/pdfbox/ttf/GlyphData.java b/src/main/java/org/pdfbox/ttf/GlyphData.java
new file mode 100644
index 0000000..95aed28
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/GlyphData.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+import org.pdfbox.util.BoundingBox;
+
+/**
+ * A glyph data record in the glyf table.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class GlyphData
+{
+ private static final int FLAG_ON_CURVE = 1;
+ private static final int FLAG_SHORT_X = 1<<1;
+ private static final int FLAG_SHORT_Y = 1<<2;
+ private static final int FLAG_X_MAGIC = 1<<3;
+ private static final int FLAG_Y_MAGIC = 1<<4;
+
+ private BoundingBox boundingBox = new BoundingBox();
+ private short numberOfContours;
+ private int[] endPointsOfContours;
+ private byte[] instructions;
+ private int[] flags;
+ private short[] xCoordinates;
+ private short[] yCoordinates;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ numberOfContours = data.readSignedShort();
+ boundingBox.setLowerLeftX( data.readSignedShort() );
+ boundingBox.setLowerLeftY( data.readSignedShort() );
+ boundingBox.setUpperRightX( data.readSignedShort() );
+ boundingBox.setUpperRightY( data.readSignedShort() );
+ /**if( numberOfContours > 0 )
+ {
+ endPointsOfContours = new int[ numberOfContours ];
+ for( int i=0; i<numberOfContours; i++ )
+ {
+ endPointsOfContours[i] = data.readUnsignedShort();
+ }
+ int instructionLength = data.readUnsignedShort();
+ instructions = data.read( instructionLength );
+
+ //BJL It is possible to read some more information here but PDFBox
+ //does not need it at this time so just ignore it.
+
+ //not sure if the length of the flags is the number of contours??
+ //flags = new int[numberOfContours];
+ //first read the flags, and just so the TTF can save a couples bytes
+ //we need to check some bit masks to see if there are more bytes or not.
+ //int currentFlagIndex = 0;
+ //int currentFlag =
+
+
+ }*/
+ }
+
+ /**
+ * @return Returns the boundingBox.
+ */
+ public BoundingBox getBoundingBox()
+ {
+ return boundingBox;
+ }
+ /**
+ * @param boundingBoxValue The boundingBox to set.
+ */
+ public void setBoundingBox(BoundingBox boundingBoxValue)
+ {
+ this.boundingBox = boundingBoxValue;
+ }
+ /**
+ * @return Returns the numberOfContours.
+ */
+ public short getNumberOfContours()
+ {
+ return numberOfContours;
+ }
+ /**
+ * @param numberOfContoursValue The numberOfContours to set.
+ */
+ public void setNumberOfContours(short numberOfContoursValue)
+ {
+ this.numberOfContours = numberOfContoursValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/GlyphTable.java b/src/main/java/org/pdfbox/ttf/GlyphTable.java
new file mode 100644
index 0000000..bbde89a
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/GlyphTable.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class GlyphTable extends TTFTable
+{
+ /**
+ * Tag to identify this table.
+ */
+ public static final String TAG = "glyf";
+
+ private GlyphData[] glyphs;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ MaximumProfileTable maxp = ttf.getMaximumProfile();
+ IndexToLocationTable loc = ttf.getIndexToLocation();
+ PostScriptTable post = ttf.getPostScript();
+ long[] offsets = loc.getOffsets();
+ int numGlyphs = maxp.getNumGlyphs();
+ glyphs = new GlyphData[numGlyphs];
+ String[] glyphNames = post.getGlyphNames();
+ for( int i=0; i<numGlyphs-1; i++ )
+ {
+ GlyphData glyph = new GlyphData();
+ data.seek( getOffset() + offsets[i] );
+ glyph.initData( ttf, data );
+ glyphs[i] = glyph;
+ }
+ }
+ /**
+ * @return Returns the glyphs.
+ */
+ public GlyphData[] getGlyphs()
+ {
+ return glyphs;
+ }
+ /**
+ * @param glyphsValue The glyphs to set.
+ */
+ public void setGlyphs(GlyphData[] glyphsValue)
+ {
+ this.glyphs = glyphsValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/HeaderTable.java b/src/main/java/org/pdfbox/ttf/HeaderTable.java
new file mode 100644
index 0000000..b4a8369
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/HeaderTable.java
@@ -0,0 +1,332 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+import java.util.Calendar;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class HeaderTable extends TTFTable
+{
+ /**
+ * Tag to identify this table.
+ */
+ public static final String TAG = "head";
+
+ private float version;
+ private float fontRevision;
+ private long checkSumAdjustment;
+ private long magicNumber;
+ private int flags;
+ private int unitsPerEm;
+ private Calendar created;
+ private Calendar modified;
+ private short xMin;
+ private short yMin;
+ private short xMax;
+ private short yMax;
+ private int macStyle;
+ private int lowestRecPPEM;
+ private short fontDirectionHint;
+ private short indexToLocFormat;
+ private short glyphDataFormat;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ version = data.read32Fixed();
+ fontRevision = data.read32Fixed();
+ checkSumAdjustment = data.readUnsignedInt();
+ magicNumber = data.readUnsignedInt();
+ flags = data.readUnsignedShort();
+ unitsPerEm = data.readUnsignedShort();
+ created = data.readInternationalDate();
+ modified = data.readInternationalDate();
+ xMin = data.readSignedShort();
+ yMin = data.readSignedShort();
+ xMax = data.readSignedShort();
+ yMax = data.readSignedShort();
+ macStyle = data.readUnsignedShort();
+ lowestRecPPEM = data.readUnsignedShort();
+ fontDirectionHint = data.readSignedShort();
+ indexToLocFormat = data.readSignedShort();
+ glyphDataFormat = data.readSignedShort();
+ }
+ /**
+ * @return Returns the checkSumAdjustment.
+ */
+ public long getCheckSumAdjustment()
+ {
+ return checkSumAdjustment;
+ }
+ /**
+ * @param checkSumAdjustmentValue The checkSumAdjustment to set.
+ */
+ public void setCheckSumAdjustment(long checkSumAdjustmentValue)
+ {
+ this.checkSumAdjustment = checkSumAdjustmentValue;
+ }
+ /**
+ * @return Returns the created.
+ */
+ public Calendar getCreated()
+ {
+ return created;
+ }
+ /**
+ * @param createdValue The created to set.
+ */
+ public void setCreated(Calendar createdValue)
+ {
+ this.created = createdValue;
+ }
+ /**
+ * @return Returns the flags.
+ */
+ public int getFlags()
+ {
+ return flags;
+ }
+ /**
+ * @param flagsValue The flags to set.
+ */
+ public void setFlags(int flagsValue)
+ {
+ this.flags = flagsValue;
+ }
+ /**
+ * @return Returns the fontDirectionHint.
+ */
+ public short getFontDirectionHint()
+ {
+ return fontDirectionHint;
+ }
+ /**
+ * @param fontDirectionHintValue The fontDirectionHint to set.
+ */
+ public void setFontDirectionHint(short fontDirectionHintValue)
+ {
+ this.fontDirectionHint = fontDirectionHintValue;
+ }
+ /**
+ * @return Returns the fontRevision.
+ */
+ public float getFontRevision()
+ {
+ return fontRevision;
+ }
+ /**
+ * @param fontRevisionValue The fontRevision to set.
+ */
+ public void setFontRevision(float fontRevisionValue)
+ {
+ this.fontRevision = fontRevisionValue;
+ }
+ /**
+ * @return Returns the glyphDataFormat.
+ */
+ public short getGlyphDataFormat()
+ {
+ return glyphDataFormat;
+ }
+ /**
+ * @param glyphDataFormatValue The glyphDataFormat to set.
+ */
+ public void setGlyphDataFormat(short glyphDataFormatValue)
+ {
+ this.glyphDataFormat = glyphDataFormatValue;
+ }
+ /**
+ * @return Returns the indexToLocFormat.
+ */
+ public short getIndexToLocFormat()
+ {
+ return indexToLocFormat;
+ }
+ /**
+ * @param indexToLocFormatValue The indexToLocFormat to set.
+ */
+ public void setIndexToLocFormat(short indexToLocFormatValue)
+ {
+ this.indexToLocFormat = indexToLocFormatValue;
+ }
+ /**
+ * @return Returns the lowestRecPPEM.
+ */
+ public int getLowestRecPPEM()
+ {
+ return lowestRecPPEM;
+ }
+ /**
+ * @param lowestRecPPEMValue The lowestRecPPEM to set.
+ */
+ public void setLowestRecPPEM(int lowestRecPPEMValue)
+ {
+ this.lowestRecPPEM = lowestRecPPEMValue;
+ }
+ /**
+ * @return Returns the macStyle.
+ */
+ public int getMacStyle()
+ {
+ return macStyle;
+ }
+ /**
+ * @param macStyleValue The macStyle to set.
+ */
+ public void setMacStyle(int macStyleValue)
+ {
+ this.macStyle = macStyleValue;
+ }
+ /**
+ * @return Returns the magicNumber.
+ */
+ public long getMagicNumber()
+ {
+ return magicNumber;
+ }
+ /**
+ * @param magicNumberValue The magicNumber to set.
+ */
+ public void setMagicNumber(long magicNumberValue)
+ {
+ this.magicNumber = magicNumberValue;
+ }
+ /**
+ * @return Returns the modified.
+ */
+ public Calendar getModified()
+ {
+ return modified;
+ }
+ /**
+ * @param modifiedValue The modified to set.
+ */
+ public void setModified(Calendar modifiedValue)
+ {
+ this.modified = modifiedValue;
+ }
+ /**
+ * @return Returns the unitsPerEm.
+ */
+ public int getUnitsPerEm()
+ {
+ return unitsPerEm;
+ }
+ /**
+ * @param unitsPerEmValue The unitsPerEm to set.
+ */
+ public void setUnitsPerEm(int unitsPerEmValue)
+ {
+ this.unitsPerEm = unitsPerEmValue;
+ }
+ /**
+ * @return Returns the version.
+ */
+ public float getVersion()
+ {
+ return version;
+ }
+ /**
+ * @param versionValue The version to set.
+ */
+ public void setVersion(float versionValue)
+ {
+ this.version = versionValue;
+ }
+ /**
+ * @return Returns the xMax.
+ */
+ public short getXMax()
+ {
+ return xMax;
+ }
+ /**
+ * @param maxValue The xMax to set.
+ */
+ public void setXMax(short maxValue)
+ {
+ xMax = maxValue;
+ }
+ /**
+ * @return Returns the xMin.
+ */
+ public short getXMin()
+ {
+ return xMin;
+ }
+ /**
+ * @param minValue The xMin to set.
+ */
+ public void setXMin(short minValue)
+ {
+ xMin = minValue;
+ }
+ /**
+ * @return Returns the yMax.
+ */
+ public short getYMax()
+ {
+ return yMax;
+ }
+ /**
+ * @param maxValue The yMax to set.
+ */
+ public void setYMax(short maxValue)
+ {
+ yMax = maxValue;
+ }
+ /**
+ * @return Returns the yMin.
+ */
+ public short getYMin()
+ {
+ return yMin;
+ }
+ /**
+ * @param minValue The yMin to set.
+ */
+ public void setYMin(short minValue)
+ {
+ yMin = minValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/HorizontalHeaderTable.java b/src/main/java/org/pdfbox/ttf/HorizontalHeaderTable.java
new file mode 100644
index 0000000..43eff25
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/HorizontalHeaderTable.java
@@ -0,0 +1,332 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class HorizontalHeaderTable extends TTFTable
+{
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "hhea";
+
+ private float version;
+ private short ascender;
+ private short descender;
+ private short lineGap;
+ private int advanceWidthMax;
+ private short minLeftSideBearing;
+ private short minRightSideBearing;
+ private short xMaxExtent;
+ private short caretSlopeRise;
+ private short caretSlopeRun;
+ private short reserved1;
+ private short reserved2;
+ private short reserved3;
+ private short reserved4;
+ private short reserved5;
+ private short metricDataFormat;
+ private int numberOfHMetrics;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ version = data.read32Fixed();
+ ascender = data.readSignedShort();
+ descender = data.readSignedShort();
+ lineGap = data.readSignedShort();
+ advanceWidthMax = data.readUnsignedShort();
+ minLeftSideBearing = data.readSignedShort();
+ minRightSideBearing = data.readSignedShort();
+ xMaxExtent = data.readSignedShort();
+ caretSlopeRise = data.readSignedShort();
+ caretSlopeRun = data.readSignedShort();
+ reserved1 = data.readSignedShort();
+ reserved2 = data.readSignedShort();
+ reserved3 = data.readSignedShort();
+ reserved4 = data.readSignedShort();
+ reserved5 = data.readSignedShort();
+ metricDataFormat = data.readSignedShort();
+ numberOfHMetrics = data.readUnsignedShort();
+ }
+
+ /**
+ * @return Returns the advanceWidthMax.
+ */
+ public int getAdvanceWidthMax()
+ {
+ return advanceWidthMax;
+ }
+ /**
+ * @param advanceWidthMaxValue The advanceWidthMax to set.
+ */
+ public void setAdvanceWidthMax(int advanceWidthMaxValue)
+ {
+ this.advanceWidthMax = advanceWidthMaxValue;
+ }
+ /**
+ * @return Returns the ascender.
+ */
+ public short getAscender()
+ {
+ return ascender;
+ }
+ /**
+ * @param ascenderValue The ascender to set.
+ */
+ public void setAscender(short ascenderValue)
+ {
+ this.ascender = ascenderValue;
+ }
+ /**
+ * @return Returns the caretSlopeRise.
+ */
+ public short getCaretSlopeRise()
+ {
+ return caretSlopeRise;
+ }
+ /**
+ * @param caretSlopeRiseValue The caretSlopeRise to set.
+ */
+ public void setCaretSlopeRise(short caretSlopeRiseValue)
+ {
+ this.caretSlopeRise = caretSlopeRiseValue;
+ }
+ /**
+ * @return Returns the caretSlopeRun.
+ */
+ public short getCaretSlopeRun()
+ {
+ return caretSlopeRun;
+ }
+ /**
+ * @param caretSlopeRunValue The caretSlopeRun to set.
+ */
+ public void setCaretSlopeRun(short caretSlopeRunValue)
+ {
+ this.caretSlopeRun = caretSlopeRunValue;
+ }
+ /**
+ * @return Returns the descender.
+ */
+ public short getDescender()
+ {
+ return descender;
+ }
+ /**
+ * @param descenderValue The descender to set.
+ */
+ public void setDescender(short descenderValue)
+ {
+ this.descender = descenderValue;
+ }
+ /**
+ * @return Returns the lineGap.
+ */
+ public short getLineGap()
+ {
+ return lineGap;
+ }
+ /**
+ * @param lineGapValue The lineGap to set.
+ */
+ public void setLineGap(short lineGapValue)
+ {
+ this.lineGap = lineGapValue;
+ }
+ /**
+ * @return Returns the metricDataFormat.
+ */
+ public short getMetricDataFormat()
+ {
+ return metricDataFormat;
+ }
+ /**
+ * @param metricDataFormatValue The metricDataFormat to set.
+ */
+ public void setMetricDataFormat(short metricDataFormatValue)
+ {
+ this.metricDataFormat = metricDataFormatValue;
+ }
+ /**
+ * @return Returns the minLeftSideBearing.
+ */
+ public short getMinLeftSideBearing()
+ {
+ return minLeftSideBearing;
+ }
+ /**
+ * @param minLeftSideBearingValue The minLeftSideBearing to set.
+ */
+ public void setMinLeftSideBearing(short minLeftSideBearingValue)
+ {
+ this.minLeftSideBearing = minLeftSideBearingValue;
+ }
+ /**
+ * @return Returns the minRightSideBearing.
+ */
+ public short getMinRightSideBearing()
+ {
+ return minRightSideBearing;
+ }
+ /**
+ * @param minRightSideBearingValue The minRightSideBearing to set.
+ */
+ public void setMinRightSideBearing(short minRightSideBearingValue)
+ {
+ this.minRightSideBearing = minRightSideBearingValue;
+ }
+ /**
+ * @return Returns the numberOfHMetrics.
+ */
+ public int getNumberOfHMetrics()
+ {
+ return numberOfHMetrics;
+ }
+ /**
+ * @param numberOfHMetricsValue The numberOfHMetrics to set.
+ */
+ public void setNumberOfHMetrics(int numberOfHMetricsValue)
+ {
+ this.numberOfHMetrics = numberOfHMetricsValue;
+ }
+ /**
+ * @return Returns the reserved1.
+ */
+ public short getReserved1()
+ {
+ return reserved1;
+ }
+ /**
+ * @param reserved1Value The reserved1 to set.
+ */
+ public void setReserved1(short reserved1Value)
+ {
+ this.reserved1 = reserved1Value;
+ }
+ /**
+ * @return Returns the reserved2.
+ */
+ public short getReserved2()
+ {
+ return reserved2;
+ }
+ /**
+ * @param reserved2Value The reserved2 to set.
+ */
+ public void setReserved2(short reserved2Value)
+ {
+ this.reserved2 = reserved2Value;
+ }
+ /**
+ * @return Returns the reserved3.
+ */
+ public short getReserved3()
+ {
+ return reserved3;
+ }
+ /**
+ * @param reserved3Value The reserved3 to set.
+ */
+ public void setReserved3(short reserved3Value)
+ {
+ this.reserved3 = reserved3Value;
+ }
+ /**
+ * @return Returns the reserved4.
+ */
+ public short getReserved4()
+ {
+ return reserved4;
+ }
+ /**
+ * @param reserved4Value The reserved4 to set.
+ */
+ public void setReserved4(short reserved4Value)
+ {
+ this.reserved4 = reserved4Value;
+ }
+ /**
+ * @return Returns the reserved5.
+ */
+ public short getReserved5()
+ {
+ return reserved5;
+ }
+ /**
+ * @param reserved5Value The reserved5 to set.
+ */
+ public void setReserved5(short reserved5Value)
+ {
+ this.reserved5 = reserved5Value;
+ }
+ /**
+ * @return Returns the version.
+ */
+ public float getVersion()
+ {
+ return version;
+ }
+ /**
+ * @param versionValue The version to set.
+ */
+ public void setVersion(float versionValue)
+ {
+ this.version = versionValue;
+ }
+ /**
+ * @return Returns the xMaxExtent.
+ */
+ public short getXMaxExtent()
+ {
+ return xMaxExtent;
+ }
+ /**
+ * @param maxExtentValue The xMaxExtent to set.
+ */
+ public void setXMaxExtent(short maxExtentValue)
+ {
+ xMaxExtent = maxExtentValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/HorizontalMetricsTable.java b/src/main/java/org/pdfbox/ttf/HorizontalMetricsTable.java
new file mode 100644
index 0000000..4dc9081
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/HorizontalMetricsTable.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class HorizontalMetricsTable extends TTFTable
+{
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "hmtx";
+
+ private int[] advanceWidth;
+ private short[] leftSideBearing;
+ private short[] nonHorizontalLeftSideBearing;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ HorizontalHeaderTable hHeader = ttf.getHorizontalHeader();
+ MaximumProfileTable maxp = ttf.getMaximumProfile();
+ int numHMetrics = hHeader.getNumberOfHMetrics();
+ int numGlyphs = maxp.getNumGlyphs();
+
+ advanceWidth = new int[ numHMetrics ];
+ leftSideBearing = new short[ numHMetrics ];
+ for( int i=0; i<numHMetrics; i++ )
+ {
+ advanceWidth[i] = data.readUnsignedShort();
+ leftSideBearing[i] = data.readSignedShort();
+ }
+
+ int numberNonHorizontal = numGlyphs - numHMetrics;
+ nonHorizontalLeftSideBearing = new short[ numberNonHorizontal ];
+ for( int i=0; i<numberNonHorizontal; i++ )
+ {
+ nonHorizontalLeftSideBearing[i] = data.readSignedShort();
+ }
+ }
+ /**
+ * @return Returns the advanceWidth.
+ */
+ public int[] getAdvanceWidth()
+ {
+ return advanceWidth;
+ }
+ /**
+ * @param advanceWidthValue The advanceWidth to set.
+ */
+ public void setAdvanceWidth(int[] advanceWidthValue)
+ {
+ this.advanceWidth = advanceWidthValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/IndexToLocationTable.java b/src/main/java/org/pdfbox/ttf/IndexToLocationTable.java
new file mode 100644
index 0000000..a695c73
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/IndexToLocationTable.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class IndexToLocationTable extends TTFTable
+{
+ private static final short SHORT_OFFSETS = 0;
+ private static final short LONG_OFFSETS = 1;
+
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "loca";
+
+ private long[] offsets;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ HeaderTable head = ttf.getHeader();
+ MaximumProfileTable maxp = ttf.getMaximumProfile();
+ int numGlyphs = maxp.getNumGlyphs();
+ offsets = new long[ numGlyphs +1];
+ for( int i=0; i<numGlyphs+1; i++ )
+ {
+ if( head.getIndexToLocFormat() == SHORT_OFFSETS )
+ {
+ offsets[i] = data.readUnsignedShort() * 2;
+ }
+ else if( head.getIndexToLocFormat() == LONG_OFFSETS )
+ {
+ offsets[i] = data.readUnsignedInt();
+ }
+ else
+ {
+ throw new IOException( "Error:TTF.loca unknown offset format.");
+ }
+ }
+ }
+ /**
+ * @return Returns the offsets.
+ */
+ public long[] getOffsets()
+ {
+ return offsets;
+ }
+ /**
+ * @param offsetsValue The offsets to set.
+ */
+ public void setOffsets(long[] offsetsValue)
+ {
+ this.offsets = offsetsValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/MaximumProfileTable.java b/src/main/java/org/pdfbox/ttf/MaximumProfileTable.java
new file mode 100644
index 0000000..36cf443
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/MaximumProfileTable.java
@@ -0,0 +1,300 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class MaximumProfileTable extends TTFTable
+{
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "maxp";
+
+ private float version;
+ private int numGlyphs;
+ private int maxPoints;
+ private int maxContours;
+ private int maxCompositePoints;
+ private int maxCompositeContours;
+ private int maxZones;
+ private int maxTwilightPoints;
+ private int maxStorage;
+ private int maxFunctionDefs;
+ private int maxInstructionDefs;
+ private int maxStackElements;
+ private int maxSizeOfInstructions;
+ private int maxComponentElements;
+ private int maxComponentDepth;
+
+ /**
+ * @return Returns the maxComponentDepth.
+ */
+ public int getMaxComponentDepth()
+ {
+ return maxComponentDepth;
+ }
+ /**
+ * @param maxComponentDepthValue The maxComponentDepth to set.
+ */
+ public void setMaxComponentDepth(int maxComponentDepthValue)
+ {
+ this.maxComponentDepth = maxComponentDepthValue;
+ }
+ /**
+ * @return Returns the maxComponentElements.
+ */
+ public int getMaxComponentElements()
+ {
+ return maxComponentElements;
+ }
+ /**
+ * @param maxComponentElementsValue The maxComponentElements to set.
+ */
+ public void setMaxComponentElements(int maxComponentElementsValue)
+ {
+ this.maxComponentElements = maxComponentElementsValue;
+ }
+ /**
+ * @return Returns the maxCompositeContours.
+ */
+ public int getMaxCompositeContours()
+ {
+ return maxCompositeContours;
+ }
+ /**
+ * @param maxCompositeContoursValue The maxCompositeContours to set.
+ */
+ public void setMaxCompositeContours(int maxCompositeContoursValue)
+ {
+ this.maxCompositeContours = maxCompositeContoursValue;
+ }
+ /**
+ * @return Returns the maxCompositePoints.
+ */
+ public int getMaxCompositePoints()
+ {
+ return maxCompositePoints;
+ }
+ /**
+ * @param maxCompositePointsValue The maxCompositePoints to set.
+ */
+ public void setMaxCompositePoints(int maxCompositePointsValue)
+ {
+ this.maxCompositePoints = maxCompositePointsValue;
+ }
+ /**
+ * @return Returns the maxContours.
+ */
+ public int getMaxContours()
+ {
+ return maxContours;
+ }
+ /**
+ * @param maxContoursValue The maxContours to set.
+ */
+ public void setMaxContours(int maxContoursValue)
+ {
+ this.maxContours = maxContoursValue;
+ }
+ /**
+ * @return Returns the maxFunctionDefs.
+ */
+ public int getMaxFunctionDefs()
+ {
+ return maxFunctionDefs;
+ }
+ /**
+ * @param maxFunctionDefsValue The maxFunctionDefs to set.
+ */
+ public void setMaxFunctionDefs(int maxFunctionDefsValue)
+ {
+ this.maxFunctionDefs = maxFunctionDefsValue;
+ }
+ /**
+ * @return Returns the maxInstructionDefs.
+ */
+ public int getMaxInstructionDefs()
+ {
+ return maxInstructionDefs;
+ }
+ /**
+ * @param maxInstructionDefsValue The maxInstructionDefs to set.
+ */
+ public void setMaxInstructionDefs(int maxInstructionDefsValue)
+ {
+ this.maxInstructionDefs = maxInstructionDefsValue;
+ }
+ /**
+ * @return Returns the maxPoints.
+ */
+ public int getMaxPoints()
+ {
+ return maxPoints;
+ }
+ /**
+ * @param maxPointsValue The maxPoints to set.
+ */
+ public void setMaxPoints(int maxPointsValue)
+ {
+ this.maxPoints = maxPointsValue;
+ }
+ /**
+ * @return Returns the maxSizeOfInstructions.
+ */
+ public int getMaxSizeOfInstructions()
+ {
+ return maxSizeOfInstructions;
+ }
+ /**
+ * @param maxSizeOfInstructionsValue The maxSizeOfInstructions to set.
+ */
+ public void setMaxSizeOfInstructions(int maxSizeOfInstructionsValue)
+ {
+ this.maxSizeOfInstructions = maxSizeOfInstructionsValue;
+ }
+ /**
+ * @return Returns the maxStackElements.
+ */
+ public int getMaxStackElements()
+ {
+ return maxStackElements;
+ }
+ /**
+ * @param maxStackElementsValue The maxStackElements to set.
+ */
+ public void setMaxStackElements(int maxStackElementsValue)
+ {
+ this.maxStackElements = maxStackElementsValue;
+ }
+ /**
+ * @return Returns the maxStorage.
+ */
+ public int getMaxStorage()
+ {
+ return maxStorage;
+ }
+ /**
+ * @param maxStorageValue The maxStorage to set.
+ */
+ public void setMaxStorage(int maxStorageValue)
+ {
+ this.maxStorage = maxStorageValue;
+ }
+ /**
+ * @return Returns the maxTwilightPoints.
+ */
+ public int getMaxTwilightPoints()
+ {
+ return maxTwilightPoints;
+ }
+ /**
+ * @param maxTwilightPointsValue The maxTwilightPoints to set.
+ */
+ public void setMaxTwilightPoints(int maxTwilightPointsValue)
+ {
+ this.maxTwilightPoints = maxTwilightPointsValue;
+ }
+ /**
+ * @return Returns the maxZones.
+ */
+ public int getMaxZones()
+ {
+ return maxZones;
+ }
+ /**
+ * @param maxZonesValue The maxZones to set.
+ */
+ public void setMaxZones(int maxZonesValue)
+ {
+ this.maxZones = maxZonesValue;
+ }
+ /**
+ * @return Returns the numGlyphs.
+ */
+ public int getNumGlyphs()
+ {
+ return numGlyphs;
+ }
+ /**
+ * @param numGlyphsValue The numGlyphs to set.
+ */
+ public void setNumGlyphs(int numGlyphsValue)
+ {
+ this.numGlyphs = numGlyphsValue;
+ }
+ /**
+ * @return Returns the version.
+ */
+ public float getVersion()
+ {
+ return version;
+ }
+ /**
+ * @param versionValue The version to set.
+ */
+ public void setVersion(float versionValue)
+ {
+ this.version = versionValue;
+ }
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ version = data.read32Fixed();
+ numGlyphs = data.readUnsignedShort();
+ maxPoints = data.readUnsignedShort();
+ maxContours = data.readUnsignedShort();
+ maxCompositePoints = data.readUnsignedShort();
+ maxCompositeContours = data.readUnsignedShort();
+ maxZones = data.readUnsignedShort();
+ maxTwilightPoints = data.readUnsignedShort();
+ maxStorage = data.readUnsignedShort();
+ maxFunctionDefs = data.readUnsignedShort();
+ maxInstructionDefs = data.readUnsignedShort();
+ maxStackElements = data.readUnsignedShort();
+ maxSizeOfInstructions = data.readUnsignedShort();
+ maxComponentElements = data.readUnsignedShort();
+ maxComponentDepth = data.readUnsignedShort();
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/MemoryTTFDataStream.java b/src/main/java/org/pdfbox/ttf/MemoryTTFDataStream.java
new file mode 100644
index 0000000..17f4455
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/MemoryTTFDataStream.java
@@ -0,0 +1,231 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.pdfbox.pdmodel.common.PDMemoryStream;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * An interface into a data stream.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class MemoryTTFDataStream extends TTFDataStream
+{
+ private byte[] data = null;
+ private int currentPosition = 0;
+
+ /**
+ * Constructor from a stream.
+ * @param is The stream of read from.
+ * @throws IOException If an error occurs while reading from the stream.
+ */
+ public MemoryTTFDataStream( InputStream is ) throws IOException
+ {
+ try
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream( is.available() );
+ byte[] buffer = new byte[1024];
+ int amountRead = 0;
+ while( (amountRead = is.read( buffer ) ) != -1 )
+ {
+ output.write( buffer, 0, amountRead );
+ }
+ data = output.toByteArray();
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ }
+ }
+
+
+
+ /**
+ * Read an unsigned byte.
+ * @return An unsigned byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public long readLong() throws IOException
+ {
+ return ((long)(readSignedInt()) << 32) + (readSignedInt() & 0xFFFFFFFFL);
+ }
+
+ /**
+ * Read a signed integer.
+ *
+ * @return A signed integer.
+ * @throws IOException If there is a problem reading the file.
+ */
+ public int readSignedInt() throws IOException
+ {
+ int ch1 = read();
+ int ch2 = read();
+ int ch3 = read();
+ int ch4 = read();
+ if( (ch1 | ch2 | ch3 | ch4) < 0)
+ {
+ throw new EOFException();
+ }
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
+ }
+
+ /**
+ * Read an unsigned byte.
+ * @return An unsigned byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public int read() throws IOException
+ {
+ int retval = -1;
+ if( currentPosition < data.length )
+ {
+ retval = data[currentPosition];
+ }
+ currentPosition++;
+ return (retval+256)%256;
+ }
+
+ /**
+ * Read an unsigned short.
+ *
+ * @return An unsigned short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public int readUnsignedShort() throws IOException
+ {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ {
+ throw new EOFException();
+ }
+ return (ch1 << 8) + (ch2 << 0);
+ }
+
+ /**
+ * Read an signed short.
+ *
+ * @return An signed short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public short readSignedShort() throws IOException
+ {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ {
+ throw new EOFException();
+ }
+ return (short)((ch1 << 8) + (ch2 << 0));
+ }
+
+ /**
+ * Close the underlying resources.
+ *
+ * @throws IOException If there is an error closing the resources.
+ */
+ public void close() throws IOException
+ {
+ data = null;
+ }
+
+ /**
+ * Seek into the datasource.
+ *
+ * @param pos The position to seek to.
+ * @throws IOException If there is an error seeking to that position.
+ */
+ public void seek(long pos) throws IOException
+ {
+ currentPosition = (int)pos;
+ }
+
+ /**
+ * @see java.io.InputStream#read( byte[], int, int )
+ *
+ * @param b The buffer to write to.
+ * @param off The offset into the buffer.
+ * @param len The length into the buffer.
+ *
+ * @return The number of bytes read.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public int read(byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int amountRead = Math.min( len, data.length-currentPosition );
+ System.arraycopy(data,currentPosition,b, off, amountRead );
+ currentPosition+=amountRead;
+
+ return amountRead;
+ }
+
+ /**
+ * Get the current position in the stream.
+ * @return The current position in the stream.
+ * @throws IOException If an error occurs while reading the stream.
+ */
+ public long getCurrentPosition() throws IOException
+ {
+ return currentPosition;
+ }
+
+ /**
+ * Get a COSStream from this TTFDataStream
+ * This permit to pass the data read from an
+ * external source to the COSObjects to keep
+ * a certain persistence layer between specialized
+ * objects like the TTF package and the pdmodel package.
+ *
+ * Created by Pascal Allain
+ * Vertical7 Inc.
+ *
+ * @return COSStream describing this stream
+ */
+ public PDStream getPDStream()
+ {
+ return new PDMemoryStream( data );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ttf/NameRecord.java b/src/main/java/org/pdfbox/ttf/NameRecord.java
new file mode 100644
index 0000000..354fa89
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/NameRecord.java
@@ -0,0 +1,242 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A name record in the name table.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class NameRecord
+{
+ /**
+ * A constant for the platform.
+ */
+ public static final int PLATFORM_APPLE_UNICODE = 0;
+ /**
+ * A constant for the platform.
+ */
+ public static final int PLATFORM_MACINTOSH = 1;
+ /**
+ * A constant for the platform.
+ */
+ public static final int PLATFORM_ISO = 2;
+ /**
+ * A constant for the platform.
+ */
+ public static final int PLATFORM_WINDOWS = 3;
+
+ /**
+ * Platform specific encoding.
+ */
+ public static final int PLATFORM_ENCODING_WINDOWS_UNDEFINED = 0;
+ /**
+ * Platform specific encoding.
+ */
+ public static final int PLATFORM_ENCODING_WINDOWS_UNICODE = 1;
+
+ /**
+ * A name id.
+ */
+ public static final int NAME_COPYRIGHT = 0;
+ /**
+ * A name id.
+ */
+ public static final int NAME_FONT_FAMILY_NAME = 1;
+ /**
+ * A name id.
+ */
+ public static final int NAME_FONT_SUB_FAMILY_NAME = 2;
+ /**
+ * A name id.
+ */
+ public static final int NAME_UNIQUE_FONT_ID = 3;
+ /**
+ * A name id.
+ */
+ public static final int NAME_FULL_FONT_NAME = 4;
+ /**
+ * A name id.
+ */
+ public static final int NAME_VERSION = 5;
+ /**
+ * A name id.
+ */
+ public static final int NAME_POSTSCRIPT_NAME = 6;
+ /**
+ * A name id.
+ */
+ public static final int NAME_TRADEMARK = 7;
+
+
+
+ private int platformId;
+ private int platformEncodingId;
+ private int languageId;
+ private int nameId;
+ private int stringLength;
+ private int stringOffset;
+ private String string;
+
+ /**
+ * @return Returns the stringLength.
+ */
+ public int getStringLength()
+ {
+ return stringLength;
+ }
+ /**
+ * @param stringLengthValue The stringLength to set.
+ */
+ public void setStringLength(int stringLengthValue)
+ {
+ this.stringLength = stringLengthValue;
+ }
+ /**
+ * @return Returns the stringOffset.
+ */
+ public int getStringOffset()
+ {
+ return stringOffset;
+ }
+ /**
+ * @param stringOffsetValue The stringOffset to set.
+ */
+ public void setStringOffset(int stringOffsetValue)
+ {
+ this.stringOffset = stringOffsetValue;
+ }
+
+ /**
+ * @return Returns the languageId.
+ */
+ public int getLanguageId()
+ {
+ return languageId;
+ }
+ /**
+ * @param languageIdValue The languageId to set.
+ */
+ public void setLanguageId(int languageIdValue)
+ {
+ this.languageId = languageIdValue;
+ }
+ /**
+ * @return Returns the nameId.
+ */
+ public int getNameId()
+ {
+ return nameId;
+ }
+ /**
+ * @param nameIdValue The nameId to set.
+ */
+ public void setNameId(int nameIdValue)
+ {
+ this.nameId = nameIdValue;
+ }
+ /**
+ * @return Returns the platformEncodingId.
+ */
+ public int getPlatformEncodingId()
+ {
+ return platformEncodingId;
+ }
+ /**
+ * @param platformEncodingIdValue The platformEncodingId to set.
+ */
+ public void setPlatformEncodingId(int platformEncodingIdValue)
+ {
+ this.platformEncodingId = platformEncodingIdValue;
+ }
+ /**
+ * @return Returns the platformId.
+ */
+ public int getPlatformId()
+ {
+ return platformId;
+ }
+ /**
+ * @param platformIdValue The platformId to set.
+ */
+ public void setPlatformId(int platformIdValue)
+ {
+ this.platformId = platformIdValue;
+ }
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ platformId = data.readUnsignedShort();
+ platformEncodingId = data.readUnsignedShort();
+ languageId = data.readUnsignedShort();
+ nameId = data.readUnsignedShort();
+ stringLength = data.readUnsignedShort();
+ stringOffset = data.readUnsignedShort();
+ }
+
+ /**
+ * Return a string representation of this class.
+ *
+ * @return A string for this class.
+ */
+ public String toString()
+ {
+ return
+ "platform=" + platformId +
+ " pEncoding=" + platformEncodingId +
+ " language=" + languageId +
+ " name=" + nameId;
+ }
+ /**
+ * @return Returns the string.
+ */
+ public String getString()
+ {
+ return string;
+ }
+ /**
+ * @param stringValue The string to set.
+ */
+ public void setString(String stringValue)
+ {
+ this.string = stringValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/NamingTable.java b/src/main/java/org/pdfbox/ttf/NamingTable.java
new file mode 100644
index 0000000..5976e47
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/NamingTable.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class NamingTable extends TTFTable
+{
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "name";
+
+ private List nameRecords = new ArrayList();
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ int formatSelector = data.readUnsignedShort();
+ int numberOfNameRecords = data.readUnsignedShort();
+ int offsetToStartOfStringStorage = data.readUnsignedShort();
+ for( int i=0; i< numberOfNameRecords; i++ )
+ {
+ NameRecord nr = new NameRecord();
+ nr.initData( ttf, data );
+ nameRecords.add( nr );
+ }
+ for( int i=0; i<numberOfNameRecords; i++ )
+ {
+ NameRecord nr = (NameRecord)nameRecords.get( i );
+ data.seek( getOffset() + (2*3)+numberOfNameRecords*2*6+nr.getStringOffset() );
+ int platform = nr.getPlatformId();
+ int encoding = nr.getPlatformEncodingId();
+ String charset = "ISO-8859-1";
+ if( platform == 3 && encoding == 1 )
+ {
+ charset = "UTF-16";
+ }
+ else if( platform == 2 )
+ {
+ if( encoding == 0 )
+ {
+ charset = "US-ASCII";
+ }
+ else if( encoding == 1 )
+ {
+ //not sure is this is correct??
+ charset = "ISO-10646-1";
+ }
+ else if( encoding == 2 )
+ {
+ charset = "ISO-8859-1";
+ }
+ }
+ String string = data.readString( nr.getStringLength(), charset );
+ nr.setString( string );
+ }
+ }
+
+ /**
+ * This will get the name records for this naming table.
+ *
+ * @return A list of NameRecord objects.
+ */
+ public List getNameRecords()
+ {
+ return nameRecords;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/OS2WindowsMetricsTable.java b/src/main/java/org/pdfbox/ttf/OS2WindowsMetricsTable.java
new file mode 100644
index 0000000..f5c5a90
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/OS2WindowsMetricsTable.java
@@ -0,0 +1,694 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class OS2WindowsMetricsTable extends TTFTable
+{
+
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_THIN = 100;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_ULTRA_LIGHT = 200;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_LIGHT = 300;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_NORMAL = 400;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_MEDIUM = 500;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_SEMI_BOLD = 600;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_BOLD = 700;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_EXTRA_BOLD = 800;
+ /**
+ * Weight class constant.
+ */
+ public static final int WEIGHT_CLASS_BLACK = 900;
+
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_ULTRA_CONDENSED = 1;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_EXTRA_CONDENSED = 2;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_CONDENSED = 3;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_SEMI_CONDENSED = 4;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_MEDIUM = 5;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_SEMI_EXPANDED = 6;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_EXPANDED = 7;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_EXTRA_EXPANDED = 8;
+ /**
+ * Width class constant.
+ */
+ public static final int WIDTH_CLASS_ULTRA_EXPANDED = 9;
+
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_NO_CLASSIFICATION = 0;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_OLDSTYLE_SERIFS = 1;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_TRANSITIONAL_SERIFS = 2;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_MODERN_SERIFS = 3;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_CLAREDON_SERIFS = 4;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_SLAB_SERIFS = 5;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_FREEFORM_SERIFS = 7;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_SANS_SERIF = 8;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_ORNAMENTALS = 9;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_SCRIPTS = 10;
+ /**
+ * Family class constant.
+ */
+ public static final int FAMILY_CLASS_SYMBOLIC = 12;
+
+ /**
+ * @return Returns the achVendId.
+ */
+ public String getAchVendId()
+ {
+ return achVendId;
+ }
+ /**
+ * @param achVendIdValue The achVendId to set.
+ */
+ public void setAchVendId(String achVendIdValue)
+ {
+ this.achVendId = achVendIdValue;
+ }
+ /**
+ * @return Returns the averageCharWidth.
+ */
+ public short getAverageCharWidth()
+ {
+ return averageCharWidth;
+ }
+ /**
+ * @param averageCharWidthValue The averageCharWidth to set.
+ */
+ public void setAverageCharWidth(short averageCharWidthValue)
+ {
+ this.averageCharWidth = averageCharWidthValue;
+ }
+ /**
+ * @return Returns the codePageRange1.
+ */
+ public long getCodePageRange1()
+ {
+ return codePageRange1;
+ }
+ /**
+ * @param codePageRange1Value The codePageRange1 to set.
+ */
+ public void setCodePageRange1(long codePageRange1Value)
+ {
+ this.codePageRange1 = codePageRange1Value;
+ }
+ /**
+ * @return Returns the codePageRange2.
+ */
+ public long getCodePageRange2()
+ {
+ return codePageRange2;
+ }
+ /**
+ * @param codePageRange2Value The codePageRange2 to set.
+ */
+ public void setCodePageRange2(long codePageRange2Value)
+ {
+ this.codePageRange2 = codePageRange2Value;
+ }
+ /**
+ * @return Returns the familyClass.
+ */
+ public short getFamilyClass()
+ {
+ return familyClass;
+ }
+ /**
+ * @param familyClassValue The familyClass to set.
+ */
+ public void setFamilyClass(short familyClassValue)
+ {
+ this.familyClass = familyClassValue;
+ }
+ /**
+ * @return Returns the firstCharIndex.
+ */
+ public int getFirstCharIndex()
+ {
+ return firstCharIndex;
+ }
+ /**
+ * @param firstCharIndexValue The firstCharIndex to set.
+ */
+ public void setFirstCharIndex(int firstCharIndexValue)
+ {
+ this.firstCharIndex = firstCharIndexValue;
+ }
+ /**
+ * @return Returns the fsSelection.
+ */
+ public int getFsSelection()
+ {
+ return fsSelection;
+ }
+ /**
+ * @param fsSelectionValue The fsSelection to set.
+ */
+ public void setFsSelection(int fsSelectionValue)
+ {
+ this.fsSelection = fsSelectionValue;
+ }
+ /**
+ * @return Returns the fsType.
+ */
+ public short getFsType()
+ {
+ return fsType;
+ }
+ /**
+ * @param fsTypeValue The fsType to set.
+ */
+ public void setFsType(short fsTypeValue)
+ {
+ this.fsType = fsTypeValue;
+ }
+ /**
+ * @return Returns the lastCharIndex.
+ */
+ public int getLastCharIndex()
+ {
+ return lastCharIndex;
+ }
+ /**
+ * @param lastCharIndexValue The lastCharIndex to set.
+ */
+ public void setLastCharIndex(int lastCharIndexValue)
+ {
+ this.lastCharIndex = lastCharIndexValue;
+ }
+ /**
+ * @return Returns the panose.
+ */
+ public byte[] getPanose()
+ {
+ return panose;
+ }
+ /**
+ * @param panoseValue The panose to set.
+ */
+ public void setPanose(byte[] panoseValue)
+ {
+ this.panose = panoseValue;
+ }
+ /**
+ * @return Returns the strikeoutPosition.
+ */
+ public short getStrikeoutPosition()
+ {
+ return strikeoutPosition;
+ }
+ /**
+ * @param strikeoutPositionValue The strikeoutPosition to set.
+ */
+ public void setStrikeoutPosition(short strikeoutPositionValue)
+ {
+ this.strikeoutPosition = strikeoutPositionValue;
+ }
+ /**
+ * @return Returns the strikeoutSize.
+ */
+ public short getStrikeoutSize()
+ {
+ return strikeoutSize;
+ }
+ /**
+ * @param strikeoutSizeValue The strikeoutSize to set.
+ */
+ public void setStrikeoutSize(short strikeoutSizeValue)
+ {
+ this.strikeoutSize = strikeoutSizeValue;
+ }
+ /**
+ * @return Returns the subscriptXOffset.
+ */
+ public short getSubscriptXOffset()
+ {
+ return subscriptXOffset;
+ }
+ /**
+ * @param subscriptXOffsetValue The subscriptXOffset to set.
+ */
+ public void setSubscriptXOffset(short subscriptXOffsetValue)
+ {
+ this.subscriptXOffset = subscriptXOffsetValue;
+ }
+ /**
+ * @return Returns the subscriptXSize.
+ */
+ public short getSubscriptXSize()
+ {
+ return subscriptXSize;
+ }
+ /**
+ * @param subscriptXSizeValue The subscriptXSize to set.
+ */
+ public void setSubscriptXSize(short subscriptXSizeValue)
+ {
+ this.subscriptXSize = subscriptXSizeValue;
+ }
+ /**
+ * @return Returns the subscriptYOffset.
+ */
+ public short getSubscriptYOffset()
+ {
+ return subscriptYOffset;
+ }
+ /**
+ * @param subscriptYOffsetValue The subscriptYOffset to set.
+ */
+ public void setSubscriptYOffset(short subscriptYOffsetValue)
+ {
+ this.subscriptYOffset = subscriptYOffsetValue;
+ }
+ /**
+ * @return Returns the subscriptYSize.
+ */
+ public short getSubscriptYSize()
+ {
+ return subscriptYSize;
+ }
+ /**
+ * @param subscriptYSizeValue The subscriptYSize to set.
+ */
+ public void setSubscriptYSize(short subscriptYSizeValue)
+ {
+ this.subscriptYSize = subscriptYSizeValue;
+ }
+ /**
+ * @return Returns the superscriptXOffset.
+ */
+ public short getSuperscriptXOffset()
+ {
+ return superscriptXOffset;
+ }
+ /**
+ * @param superscriptXOffsetValue The superscriptXOffset to set.
+ */
+ public void setSuperscriptXOffset(short superscriptXOffsetValue)
+ {
+ this.superscriptXOffset = superscriptXOffsetValue;
+ }
+ /**
+ * @return Returns the superscriptXSize.
+ */
+ public short getSuperscriptXSize()
+ {
+ return superscriptXSize;
+ }
+ /**
+ * @param superscriptXSizeValue The superscriptXSize to set.
+ */
+ public void setSuperscriptXSize(short superscriptXSizeValue)
+ {
+ this.superscriptXSize = superscriptXSizeValue;
+ }
+ /**
+ * @return Returns the superscriptYOffset.
+ */
+ public short getSuperscriptYOffset()
+ {
+ return superscriptYOffset;
+ }
+ /**
+ * @param superscriptYOffsetValue The superscriptYOffset to set.
+ */
+ public void setSuperscriptYOffset(short superscriptYOffsetValue)
+ {
+ this.superscriptYOffset = superscriptYOffsetValue;
+ }
+ /**
+ * @return Returns the superscriptYSize.
+ */
+ public short getSuperscriptYSize()
+ {
+ return superscriptYSize;
+ }
+ /**
+ * @param superscriptYSizeValue The superscriptYSize to set.
+ */
+ public void setSuperscriptYSize(short superscriptYSizeValue)
+ {
+ this.superscriptYSize = superscriptYSizeValue;
+ }
+ /**
+ * @return Returns the typeLineGap.
+ */
+ public int getTypeLineGap()
+ {
+ return typeLineGap;
+ }
+ /**
+ * @param typeLineGapValue The typeLineGap to set.
+ */
+ public void setTypeLineGap(int typeLineGapValue)
+ {
+ this.typeLineGap = typeLineGapValue;
+ }
+ /**
+ * @return Returns the typoAscender.
+ */
+ public int getTypoAscender()
+ {
+ return typoAscender;
+ }
+ /**
+ * @param typoAscenderValue The typoAscender to set.
+ */
+ public void setTypoAscender(int typoAscenderValue)
+ {
+ this.typoAscender = typoAscenderValue;
+ }
+ /**
+ * @return Returns the typoDescender.
+ */
+ public int getTypoDescender()
+ {
+ return typoDescender;
+ }
+ /**
+ * @param typoDescenderValue The typoDescender to set.
+ */
+ public void setTypoDescender(int typoDescenderValue)
+ {
+ this.typoDescender = typoDescenderValue;
+ }
+ /**
+ * @return Returns the unicodeRange1.
+ */
+ public long getUnicodeRange1()
+ {
+ return unicodeRange1;
+ }
+ /**
+ * @param unicodeRange1Value The unicodeRange1 to set.
+ */
+ public void setUnicodeRange1(long unicodeRange1Value)
+ {
+ this.unicodeRange1 = unicodeRange1Value;
+ }
+ /**
+ * @return Returns the unicodeRange2.
+ */
+ public long getUnicodeRange2()
+ {
+ return unicodeRange2;
+ }
+ /**
+ * @param unicodeRange2Value The unicodeRange2 to set.
+ */
+ public void setUnicodeRange2(long unicodeRange2Value)
+ {
+ this.unicodeRange2 = unicodeRange2Value;
+ }
+ /**
+ * @return Returns the unicodeRange3.
+ */
+ public long getUnicodeRange3()
+ {
+ return unicodeRange3;
+ }
+ /**
+ * @param unicodeRange3Value The unicodeRange3 to set.
+ */
+ public void setUnicodeRange3(long unicodeRange3Value)
+ {
+ this.unicodeRange3 = unicodeRange3Value;
+ }
+ /**
+ * @return Returns the unicodeRange4.
+ */
+ public long getUnicodeRange4()
+ {
+ return unicodeRange4;
+ }
+ /**
+ * @param unicodeRange4Value The unicodeRange4 to set.
+ */
+ public void setUnicodeRange4(long unicodeRange4Value)
+ {
+ this.unicodeRange4 = unicodeRange4Value;
+ }
+ /**
+ * @return Returns the version.
+ */
+ public int getVersion()
+ {
+ return version;
+ }
+ /**
+ * @param versionValue The version to set.
+ */
+ public void setVersion(int versionValue)
+ {
+ this.version = versionValue;
+ }
+ /**
+ * @return Returns the weightClass.
+ */
+ public int getWeightClass()
+ {
+ return weightClass;
+ }
+ /**
+ * @param weightClassValue The weightClass to set.
+ */
+ public void setWeightClass(int weightClassValue)
+ {
+ this.weightClass = weightClassValue;
+ }
+ /**
+ * @return Returns the widthClass.
+ */
+ public int getWidthClass()
+ {
+ return widthClass;
+ }
+ /**
+ * @param widthClassValue The widthClass to set.
+ */
+ public void setWidthClass(int widthClassValue)
+ {
+ this.widthClass = widthClassValue;
+ }
+ /**
+ * @return Returns the winAscent.
+ */
+ public int getWinAscent()
+ {
+ return winAscent;
+ }
+ /**
+ * @param winAscentValue The winAscent to set.
+ */
+ public void setWinAscent(int winAscentValue)
+ {
+ this.winAscent = winAscentValue;
+ }
+ /**
+ * @return Returns the winDescent.
+ */
+ public int getWinDescent()
+ {
+ return winDescent;
+ }
+ /**
+ * @param winDescentValue The winDescent to set.
+ */
+ public void setWinDescent(int winDescentValue)
+ {
+ this.winDescent = winDescentValue;
+ }
+ private int version;
+ private short averageCharWidth;
+ private int weightClass;
+ private int widthClass;
+ private short fsType;
+ private short subscriptXSize;
+ private short subscriptYSize;
+ private short subscriptXOffset;
+ private short subscriptYOffset;
+ private short superscriptXSize;
+ private short superscriptYSize;
+ private short superscriptXOffset;
+ private short superscriptYOffset;
+ private short strikeoutSize;
+ private short strikeoutPosition;
+ private short familyClass;
+ private byte[] panose = new byte[10];
+ private long unicodeRange1;
+ private long unicodeRange2;
+ private long unicodeRange3;
+ private long unicodeRange4;
+ private String achVendId;
+ private int fsSelection;
+ private int firstCharIndex;
+ private int lastCharIndex;
+ private int typoAscender;
+ private int typoDescender;
+ private int typeLineGap;
+ private int winAscent;
+ private int winDescent;
+ private long codePageRange1 = -1;
+ private long codePageRange2 = -1;
+
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "OS/2";
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ version = data.readUnsignedShort();
+ averageCharWidth = data.readSignedShort();
+ weightClass = data.readUnsignedShort();
+ widthClass = data.readUnsignedShort();
+ fsType = data.readSignedShort();
+ subscriptXSize = data.readSignedShort();
+ subscriptYSize = data.readSignedShort();
+ subscriptXOffset = data.readSignedShort();
+ subscriptYOffset = data.readSignedShort();
+ superscriptXSize = data.readSignedShort();
+ superscriptYSize = data.readSignedShort();
+ superscriptXOffset = data.readSignedShort();
+ superscriptYOffset = data.readSignedShort();
+ strikeoutSize = data.readSignedShort();
+ strikeoutPosition = data.readSignedShort();
+ familyClass = data.readSignedShort();
+ panose = data.read( 10 );
+ unicodeRange1 = data.readUnsignedInt();
+ unicodeRange2 = data.readUnsignedInt();
+ unicodeRange3 = data.readUnsignedInt();
+ unicodeRange4 = data.readUnsignedInt();
+ achVendId = data.readString( 4 );
+ fsSelection = data.readUnsignedShort();
+ firstCharIndex = data.readUnsignedShort();
+ lastCharIndex = data.readUnsignedShort();
+ typoAscender = data.readSignedShort();
+ typoDescender = data.readSignedShort();
+ typeLineGap = data.readSignedShort();
+ winAscent = data.readUnsignedShort();
+ winDescent = data.readUnsignedShort();
+ if( version >= 1 )
+ {
+ codePageRange1 = data.readUnsignedInt();
+ codePageRange2 = data.readUnsignedInt();
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/PostScriptTable.java b/src/main/java/org/pdfbox/ttf/PostScriptTable.java
new file mode 100644
index 0000000..e7b7822
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/PostScriptTable.java
@@ -0,0 +1,304 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.encoding.MacRomanEncoding;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class PostScriptTable extends TTFTable
+{
+ private float formatType;
+ private float italicAngle;
+ private short underlinePosition;
+ private short underlineThickness;
+ private long isFixedPitch;
+ private long minMemType42;
+ private long maxMemType42;
+ private long mimMemType1;
+ private long maxMemType1;
+ private String[] glyphNames = null;
+
+ /**
+ * A tag that identifies this table type.
+ */
+ public static final String TAG = "post";
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ MaximumProfileTable maxp = ttf.getMaximumProfile();
+ formatType = data.read32Fixed();
+ italicAngle = data.read32Fixed();
+ underlinePosition = data.readSignedShort();
+ underlineThickness = data.readSignedShort();
+ isFixedPitch = data.readUnsignedInt();
+ minMemType42 = data.readUnsignedInt();
+ maxMemType42 = data.readUnsignedInt();
+ mimMemType1 = data.readUnsignedInt();
+ maxMemType1 = data.readUnsignedInt();
+ MacRomanEncoding encoding = new MacRomanEncoding();
+
+
+ if( formatType == 1.0f )
+ {
+ /*
+ * This TrueType font file contains exactly the 258 glyphs in the standard
+ * Macintosh TrueType.
+ */
+ glyphNames = new String[258];
+ for( int i=0; i<glyphNames.length; i++)
+ {
+ COSName name = encoding.getName( i );
+ if( name != null )
+ {
+ glyphNames[i] = name.getName();
+ }
+ }
+ }
+ else if( formatType == 2.0f )
+ {
+ int numGlyphs = data.readUnsignedShort();
+ int[] glyphNameIndex = new int[numGlyphs];
+ glyphNames = new String[ numGlyphs ];
+ int maxIndex = Integer.MIN_VALUE;
+ for( int i=0; i<numGlyphs; i++ )
+ {
+ int index = data.readUnsignedShort();
+ glyphNameIndex[i] = index;
+ maxIndex = Math.max( maxIndex, index );
+ }
+ String[] nameArray = null;
+ if( maxIndex >= 258 )
+ {
+ nameArray = new String[ maxIndex-258 +1 ];
+ for( int i=0; i<maxIndex-258+1; i++ )
+ {
+ int numberOfChars = data.read();
+ nameArray[i]=data.readString( numberOfChars );
+ }
+ }
+ for( int i=0; i<numGlyphs; i++ )
+ {
+ int index = glyphNameIndex[i];
+ if( index < 258 )
+ {
+ glyphNames[i] = encoding.getName( index ).getName();
+ }
+ else if( index >= 258 && index <= 32767 )
+ {
+ glyphNames[i] = nameArray[index-258];
+ }
+ else
+ {
+ throw new IOException( "Unknown glyph name index:" + index );
+ }
+ }
+ }
+ else if( formatType == 2.5f )
+ {
+ int[] glyphNameIndex = new int[maxp.getNumGlyphs()];
+ for( int i=0; i<glyphNameIndex.length; i++)
+ {
+ int offset = data.readSignedByte();
+ glyphNameIndex[i] = i+1+offset;
+ }
+ glyphNames = new String[glyphNameIndex.length];
+ for( int i=0; i<glyphNames.length; i++)
+ {
+ COSName name = encoding.getName( glyphNameIndex[i] );
+ if( name != null )
+ {
+ glyphNames[i] = name.getName();
+ }
+ }
+
+ }
+ else if( formatType == 3.0f )
+ {
+ //no postscript information is provided.
+ }
+ }
+ /**
+ * @return Returns the formatType.
+ */
+ public float getFormatType()
+ {
+ return formatType;
+ }
+ /**
+ * @param formatTypeValue The formatType to set.
+ */
+ public void setFormatType(float formatTypeValue)
+ {
+ this.formatType = formatTypeValue;
+ }
+ /**
+ * @return Returns the isFixedPitch.
+ */
+ public long getIsFixedPitch()
+ {
+ return isFixedPitch;
+ }
+ /**
+ * @param isFixedPitchValue The isFixedPitch to set.
+ */
+ public void setIsFixedPitch(long isFixedPitchValue)
+ {
+ this.isFixedPitch = isFixedPitchValue;
+ }
+ /**
+ * @return Returns the italicAngle.
+ */
+ public float getItalicAngle()
+ {
+ return italicAngle;
+ }
+ /**
+ * @param italicAngleValue The italicAngle to set.
+ */
+ public void setItalicAngle(float italicAngleValue)
+ {
+ this.italicAngle = italicAngleValue;
+ }
+ /**
+ * @return Returns the maxMemType1.
+ */
+ public long getMaxMemType1()
+ {
+ return maxMemType1;
+ }
+ /**
+ * @param maxMemType1Value The maxMemType1 to set.
+ */
+ public void setMaxMemType1(long maxMemType1Value)
+ {
+ this.maxMemType1 = maxMemType1Value;
+ }
+ /**
+ * @return Returns the maxMemType42.
+ */
+ public long getMaxMemType42()
+ {
+ return maxMemType42;
+ }
+ /**
+ * @param maxMemType42Value The maxMemType42 to set.
+ */
+ public void setMaxMemType42(long maxMemType42Value)
+ {
+ this.maxMemType42 = maxMemType42Value;
+ }
+ /**
+ * @return Returns the mimMemType1.
+ */
+ public long getMimMemType1()
+ {
+ return mimMemType1;
+ }
+ /**
+ * @param mimMemType1Value The mimMemType1 to set.
+ */
+ public void setMimMemType1(long mimMemType1Value)
+ {
+ this.mimMemType1 = mimMemType1Value;
+ }
+ /**
+ * @return Returns the minMemType42.
+ */
+ public long getMinMemType42()
+ {
+ return minMemType42;
+ }
+ /**
+ * @param minMemType42Value The minMemType42 to set.
+ */
+ public void setMinMemType42(long minMemType42Value)
+ {
+ this.minMemType42 = minMemType42Value;
+ }
+ /**
+ * @return Returns the underlinePosition.
+ */
+ public short getUnderlinePosition()
+ {
+ return underlinePosition;
+ }
+ /**
+ * @param underlinePositionValue The underlinePosition to set.
+ */
+ public void setUnderlinePosition(short underlinePositionValue)
+ {
+ this.underlinePosition = underlinePositionValue;
+ }
+ /**
+ * @return Returns the underlineThickness.
+ */
+ public short getUnderlineThickness()
+ {
+ return underlineThickness;
+ }
+ /**
+ * @param underlineThicknessValue The underlineThickness to set.
+ */
+ public void setUnderlineThickness(short underlineThicknessValue)
+ {
+ this.underlineThickness = underlineThicknessValue;
+ }
+ /**
+ * @return Returns the glyphNames.
+ */
+ public String[] getGlyphNames()
+ {
+ return glyphNames;
+ }
+ /**
+ * @param glyphNamesValue The glyphNames to set.
+ */
+ public void setGlyphNames(String[] glyphNamesValue)
+ {
+ this.glyphNames = glyphNamesValue;
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/RAFDataStream.java b/src/main/java/org/pdfbox/ttf/RAFDataStream.java
new file mode 100644
index 0000000..14ebfd9
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/RAFDataStream.java
@@ -0,0 +1,193 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * An implementation of the TTFDataStream that goes against a RAF.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.4 $
+ */
+public class RAFDataStream extends TTFDataStream
+{
+ private RandomAccessFile raf = null;
+ /**
+ * Constructor.
+ *
+ * @param name The raf file.
+ * @param mode The mode to open the RAF.
+ *
+ * @throws FileNotFoundException If there is a problem creating the RAF.
+ *
+ * @see RandomAccessFile#RandomAccessFile( String, String )
+ */
+ public RAFDataStream(String name, String mode) throws FileNotFoundException
+ {
+ raf = new RandomAccessFile( name, mode );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param file The raf file.
+ * @param mode The mode to open the RAF.
+ *
+ * @throws FileNotFoundException If there is a problem creating the RAF.
+ *
+ * @see RandomAccessFile#RandomAccessFile( File, String )
+ */
+ public RAFDataStream(File file, String mode) throws FileNotFoundException
+ {
+ raf = new RandomAccessFile( file, mode );
+ }
+
+ /**
+ * Read an signed short.
+ *
+ * @return An signed short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public short readSignedShort() throws IOException
+ {
+ return raf.readShort();
+ }
+
+ /**
+ * Get the current position in the stream.
+ * @return The current position in the stream.
+ * @throws IOException If an error occurs while reading the stream.
+ */
+ public long getCurrentPosition() throws IOException
+ {
+ return raf.getFilePointer();
+ }
+
+ /**
+ * Get a COSStream from this TTFDataStream
+ * This permit to pass the data read from an
+ * external source to the COSObjects to keep
+ * a certain persistence layer between specialized
+ * objects like the TTF package and the pdmodel package.
+ *
+ * Created by Pascal Allain
+ * Vertical7 Inc.
+ *
+ * @return COSStream describing this stream (unfiletred)
+ */
+ public PDStream getPDStream()
+ {
+ COSStream retval = null;
+
+ retval = new COSStream(raf);
+
+ return new PDStream( retval );
+ }
+
+ /**
+ * Close the underlying resources.
+ *
+ * @throws IOException If there is an error closing the resources.
+ */
+ public void close() throws IOException
+ {
+ raf.close();
+ raf = null;
+ }
+
+ /**
+ * Read an unsigned byte.
+ * @return An unsigned byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public int read() throws IOException
+ {
+ return raf.read();
+ }
+
+ /**
+ * Read an unsigned short.
+ *
+ * @return An unsigned short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public int readUnsignedShort() throws IOException
+ {
+ return raf.readUnsignedShort();
+ }
+
+ /**
+ * Read an unsigned byte.
+ * @return An unsigned byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public long readLong() throws IOException
+ {
+ return raf.readLong();
+ }
+
+ /**
+ * Seek into the datasource.
+ *
+ * @param pos The position to seek to.
+ * @throws IOException If there is an error seeking to that position.
+ */
+ public void seek(long pos) throws IOException
+ {
+ raf.seek( pos );
+ }
+
+ /**
+ * @see java.io.InputStream#read( byte[], int, int )
+ *
+ * @param b The buffer to write to.
+ * @param off The offset into the buffer.
+ * @param len The length into the buffer.
+ *
+ * @return The number of bytes read.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public int read(byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ return raf.read(b,off,len);
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/TTFDataStream.java b/src/main/java/org/pdfbox/ttf/TTFDataStream.java
new file mode 100644
index 0000000..c4cf19d
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/TTFDataStream.java
@@ -0,0 +1,253 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * An interface into a data stream.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.5 $
+ */
+public abstract class TTFDataStream
+{
+
+ /**
+ * Read a 16.16 fixed value, where the first 16 bits are the decimal and the last
+ * 16 bits are the fraction.
+ * @return A 32 bit value.
+ * @throws IOException If there is an error reading the data.
+ */
+ public float read32Fixed() throws IOException
+ {
+ float retval = 0;
+ retval = readSignedShort();
+ retval += (readUnsignedShort()/65536);
+ return retval;
+ }
+
+ /**
+ * Read a fixed length ascii string.
+ * @param length The length of the string to read.
+ * @return A string of the desired length.
+ * @throws IOException If there is an error reading the data.
+ */
+ public String readString( int length ) throws IOException
+ {
+ return readString( length, "ISO-8859-1" );
+ }
+
+ /**
+ * Read a fixed length ascii string.
+ * @param length The length of the string to read in bytes.
+ * @param charset The expected character set of the string.
+ * @return A string of the desired length.
+ * @throws IOException If there is an error reading the data.
+ */
+ public String readString( int length, String charset ) throws IOException
+ {
+ byte[] buffer = read( length );
+ return new String(buffer, charset);
+ }
+
+ /**
+ * Read an unsigned byte.
+ * @return An unsigned byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public abstract int read() throws IOException;
+
+ /**
+ * Read an unsigned byte.
+ * @return An unsigned byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public abstract long readLong() throws IOException;
+
+
+ /**
+ * Read a signed byte.
+ * @return A signed byte.
+ * @throws IOException If there is an error reading the data.
+ */
+ public int readSignedByte() throws IOException
+ {
+ int signedByte = read();
+ return signedByte < 127 ? signedByte : signedByte-256;
+ }
+
+ /**
+ * Read an unsigned integer.
+ * @return An unsiged integer.
+ * @throws IOException If there is an error reading the data.
+ */
+ public long readUnsignedInt() throws IOException
+ {
+ long byte1 = read();
+ long byte2 = read();
+ long byte3 = read();
+ long byte4 = read();
+ if( byte4 < 0 )
+ {
+ throw new EOFException();
+ }
+ return (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + (byte4 << 0);
+ }
+
+ /**
+ * Read an unsigned short.
+ *
+ * @return An unsigned short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public abstract int readUnsignedShort() throws IOException;
+
+ /**
+ * Read an unsigned short array.
+ *
+ * @param length The length of the array to read.
+ * @return An unsigned short array.
+ * @throws IOException If there is an error reading the data.
+ */
+ public int[] readUnsignedShortArray( int length ) throws IOException
+ {
+ int[] array = new int[ length ];
+ for( int i=0; i<length; i++ )
+ {
+ array[i] = readUnsignedShort();
+ }
+ return array;
+ }
+
+ /**
+ * Read an signed short.
+ *
+ * @return An signed short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public abstract short readSignedShort() throws IOException;
+
+ /**
+ * Read an eight byte international date.
+ *
+ * @return An signed short.
+ * @throws IOException If there is an error reading the data.
+ */
+ public Calendar readInternationalDate() throws IOException
+ {
+ long secondsSince1904 = readLong();
+ GregorianCalendar cal = new GregorianCalendar( 1904, 0, 1 );
+ long millisFor1904 = cal.getTimeInMillis();
+ millisFor1904 += (secondsSince1904*1000);
+ cal.setTimeInMillis( millisFor1904 );
+ return cal;
+ }
+
+ /**
+ * Close the underlying resources.
+ *
+ * @throws IOException If there is an error closing the resources.
+ */
+ public abstract void close() throws IOException;
+
+ /**
+ * Seek into the datasource.
+ *
+ * @param pos The position to seek to.
+ * @throws IOException If there is an error seeking to that position.
+ */
+ public abstract void seek(long pos) throws IOException;
+
+ /**
+ * Read a specific number of bytes from the stream.
+ * @param numberOfBytes The number of bytes to read.
+ * @return The byte buffer.
+ * @throws IOException If there is an error while reading.
+ */
+ public byte[] read( int numberOfBytes ) throws IOException
+ {
+ byte[] data = new byte[ numberOfBytes ];
+ int amountRead = 0;
+ int totalAmountRead = 0;
+ while( (amountRead = read( data, totalAmountRead, numberOfBytes-totalAmountRead ) ) != -1 &&
+ totalAmountRead < numberOfBytes )
+ {
+ totalAmountRead += amountRead;
+ //read at most numberOfBytes bytes from the stream.
+ }
+ return data;
+ }
+
+ /**
+ * @see java.io.InputStream#read( byte[], int, int )
+ *
+ * @param b The buffer to write to.
+ * @param off The offset into the buffer.
+ * @param len The length into the buffer.
+ *
+ * @return The number of bytes read.
+ *
+ * @throws IOException If there is an error reading from the stream.
+ */
+ public abstract int read(byte[] b,
+ int off,
+ int len)
+ throws IOException;
+
+ /**
+ * Get the current position in the stream.
+ * @return The current position in the stream.
+ * @throws IOException If an error occurs while reading the stream.
+ */
+ public abstract long getCurrentPosition() throws IOException;
+
+ /**
+ * Get a COSStream from this TTFDataStream
+ * This permit to pass the data read from an
+ * external source to the COSObjects to keep
+ * a certain persistence layer between specialized
+ * objects like the TTF package and the pdmodel package.
+ *
+ * Created by Pascal Allain
+ * Vertical7 Inc.
+ *
+ * @return COSStream describing this stream
+ */
+ public abstract PDStream getPDStream();
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ttf/TTFParser.java b/src/main/java/org/pdfbox/ttf/TTFParser.java
new file mode 100644
index 0000000..903badb
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/TTFParser.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A true type font file parser.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class TTFParser
+{
+ /**
+ * A simple command line program to test parsing of a TTF file. <br/>
+ * usage: java org.pdfbox.ttf.TTFParser &lt;ttf-file&gt;
+ *
+ * @param args The command line arguments.
+ *
+ * @throws IOException If there is an error while parsing the font file.
+ */
+ public static void main( String[] args ) throws IOException
+ {
+ if( args.length != 1 )
+ {
+ System.err.println( "usage: java org.pdfbox.ttf.TTFParser <ttf-file>" );
+ System.exit( -1 );
+ }
+ TTFParser parser = new TTFParser();
+ TrueTypeFont font = parser.parseTTF( args[0] );
+ System.out.println( "Font:" + font );
+ }
+
+ /**
+ * Parse a file and get a true type font.
+ * @param ttfFile The TTF file.
+ * @return A true type font.
+ * @throws IOException If there is an error parsing the true type font.
+ */
+ public TrueTypeFont parseTTF( String ttfFile ) throws IOException
+ {
+ RAFDataStream raf = new RAFDataStream( ttfFile, "r" );
+ return parseTTF( raf );
+ }
+
+ /**
+ * Parse a file and get a true type font.
+ * @param ttfFile The TTF file.
+ * @return A true type font.
+ * @throws IOException If there is an error parsing the true type font.
+ */
+ public TrueTypeFont parseTTF( File ttfFile ) throws IOException
+ {
+ RAFDataStream raf = new RAFDataStream( ttfFile, "r" );
+ return parseTTF( raf );
+ }
+
+ /**
+ * Parse a file and get a true type font.
+ * @param raf The TTF file.
+ * @return A true type font.
+ * @throws IOException If there is an error parsing the true type font.
+ */
+ public TrueTypeFont parseTTF( TTFDataStream raf ) throws IOException
+ {
+ TrueTypeFont font = new TrueTypeFont( raf );
+ font.setVersion( raf.read32Fixed() );
+ int numberOfTables = raf.readUnsignedShort();
+ int searchRange = raf.readUnsignedShort();
+ int entrySelector = raf.readUnsignedShort();
+ int rangeShift = raf.readUnsignedShort();
+ for( int i=0; i<numberOfTables; i++ )
+ {
+ TTFTable table = readTableDirectory( raf );
+ font.addTable( table );
+ }
+ List initialized = new ArrayList();
+ //need to initialize a couple tables in a certain order
+ HeaderTable head = font.getHeader();
+ raf.seek( head.getOffset() );
+ head.initData( font, raf );
+ initialized.add( head );
+
+
+ HorizontalHeaderTable hh = font.getHorizontalHeader();
+ raf.seek( hh.getOffset() );
+ hh.initData( font, raf );
+ initialized.add( hh );
+
+ MaximumProfileTable maxp = font.getMaximumProfile();
+ raf.seek( maxp.getOffset() );
+ maxp.initData( font, raf );
+ initialized.add( maxp );
+
+ PostScriptTable post = font.getPostScript();
+ raf.seek( post.getOffset() );
+ post.initData( font, raf );
+ initialized.add( post );
+
+ IndexToLocationTable loc = font.getIndexToLocation();
+ raf.seek( loc.getOffset() );
+ loc.initData( font, raf );
+ initialized.add( loc );
+
+ Iterator iter = font.getTables().iterator();
+ while( iter.hasNext() )
+ {
+ TTFTable table = (TTFTable)iter.next();
+ if( !initialized.contains( table ) )
+ {
+ raf.seek( table.getOffset() );
+ table.initData( font, raf );
+ }
+ }
+ return font;
+ }
+
+ private TTFTable readTableDirectory( TTFDataStream raf ) throws IOException
+ {
+ TTFTable retval = null;
+ String tag = raf.readString( 4 );
+ if( tag.equals( CMAPTable.TAG ) )
+ {
+ retval = new CMAPTable();
+ }
+ else if( tag.equals( GlyphTable.TAG ) )
+ {
+ retval = new GlyphTable();
+ }
+ else if( tag.equals( HeaderTable.TAG ) )
+ {
+ retval = new HeaderTable();
+ }
+ else if( tag.equals( HorizontalHeaderTable.TAG ) )
+ {
+ retval = new HorizontalHeaderTable();
+ }
+ else if( tag.equals( HorizontalMetricsTable.TAG ) )
+ {
+ retval = new HorizontalMetricsTable();
+ }
+ else if( tag.equals( IndexToLocationTable.TAG ) )
+ {
+ retval = new IndexToLocationTable();
+ }
+ else if( tag.equals( MaximumProfileTable.TAG ) )
+ {
+ retval = new MaximumProfileTable();
+ }
+ else if( tag.equals( NamingTable.TAG ) )
+ {
+ retval = new NamingTable();
+ }
+ else if( tag.equals( OS2WindowsMetricsTable.TAG ) )
+ {
+ retval = new OS2WindowsMetricsTable();
+ }
+ else if( tag.equals( PostScriptTable.TAG ) )
+ {
+ retval = new PostScriptTable();
+ }
+ else if( tag.equals( GlyphTable.TAG ) )
+ {
+ retval = new GlyphTable();
+ }
+ else if( tag.equals( GlyphTable.TAG ) )
+ {
+ retval = new GlyphTable();
+ }
+ else if( tag.equals( DigitalSignatureTable.TAG ) )
+ {
+ retval = new DigitalSignatureTable();
+ }
+ else
+ {
+ //unknown table type but read it anyway.
+ retval = new TTFTable();
+ }
+ retval.setTag( tag );
+ retval.setCheckSum( raf.readUnsignedInt() );
+ retval.setOffset( raf.readUnsignedInt() );
+ retval.setLength( raf.readUnsignedInt() );
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/ttf/TTFTable.java b/src/main/java/org/pdfbox/ttf/TTFTable.java
new file mode 100644
index 0000000..fdc03ea
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/TTFTable.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A table in a true type font.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.1 $
+ */
+public class TTFTable
+{
+ private String tag;
+ private long checkSum;
+ private long offset;
+ private long length;
+
+ /**
+ * @return Returns the checkSum.
+ */
+ public long getCheckSum()
+ {
+ return checkSum;
+ }
+ /**
+ * @param checkSumValue The checkSum to set.
+ */
+ public void setCheckSum(long checkSumValue)
+ {
+ this.checkSum = checkSumValue;
+ }
+ /**
+ * @return Returns the length.
+ */
+ public long getLength()
+ {
+ return length;
+ }
+ /**
+ * @param lengthValue The length to set.
+ */
+ public void setLength(long lengthValue)
+ {
+ this.length = lengthValue;
+ }
+ /**
+ * @return Returns the offset.
+ */
+ public long getOffset()
+ {
+ return offset;
+ }
+ /**
+ * @param offsetValue The offset to set.
+ */
+ public void setOffset(long offsetValue)
+ {
+ this.offset = offsetValue;
+ }
+ /**
+ * @return Returns the tag.
+ */
+ public String getTag()
+ {
+ return tag;
+ }
+ /**
+ * @param tagValue The tag to set.
+ */
+ public void setTag(String tagValue)
+ {
+ this.tag = tagValue;
+ }
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException
+ {
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/TrueTypeFont.java b/src/main/java/org/pdfbox/ttf/TrueTypeFont.java
new file mode 100644
index 0000000..bbe6392
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/TrueTypeFont.java
@@ -0,0 +1,221 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.ttf;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import java.io.IOException;
+
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * A class to hold true type font information.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.4 $
+ */
+public class TrueTypeFont
+{
+ private float version;
+
+ private Map tables = new HashMap();
+
+ private TTFDataStream data;
+
+ /**
+ * Constructor. Clients should use the TTFParser to create a new TrueTypeFont object.
+ *
+ * @param fontData The font data.
+ */
+ TrueTypeFont( TTFDataStream fontData )
+ {
+ data = fontData;
+ }
+
+ /**
+ * Close the underlying resources.
+ *
+ * @throws IOException If there is an error closing the resources.
+ */
+ public void close() throws IOException
+ {
+ data.close();
+ }
+
+ /**
+ * @return Returns the version.
+ */
+ public float getVersion()
+ {
+ return version;
+ }
+ /**
+ * @param versionValue The version to set.
+ */
+ public void setVersion(float versionValue)
+ {
+ version = versionValue;
+ }
+
+ /**
+ * Add a table definition.
+ *
+ * @param table The table to add.
+ */
+ public void addTable( TTFTable table )
+ {
+ tables.put( table.getTag(), table );
+ }
+
+ /**
+ * Get all of the tables.
+ *
+ * @return All of the tables.
+ */
+ public Collection getTables()
+ {
+ return tables.values();
+ }
+
+ /**
+ * This will get the naming table for the true type font.
+ *
+ * @return The naming table.
+ */
+ public NamingTable getNaming()
+ {
+ return (NamingTable)tables.get( NamingTable.TAG );
+ }
+
+ /**
+ * Get the postscript table for this TTF.
+ *
+ * @return The postscript table.
+ */
+ public PostScriptTable getPostScript()
+ {
+ return (PostScriptTable)tables.get( PostScriptTable.TAG );
+ }
+
+ /**
+ * Get the OS/2 table for this TTF.
+ *
+ * @return The OS/2 table.
+ */
+ public OS2WindowsMetricsTable getOS2Windows()
+ {
+ return (OS2WindowsMetricsTable)tables.get( OS2WindowsMetricsTable.TAG );
+ }
+
+ /**
+ * Get the maxp table for this TTF.
+ *
+ * @return The maxp table.
+ */
+ public MaximumProfileTable getMaximumProfile()
+ {
+ return (MaximumProfileTable)tables.get( MaximumProfileTable.TAG );
+ }
+
+ /**
+ * Get the head table for this TTF.
+ *
+ * @return The head table.
+ */
+ public HeaderTable getHeader()
+ {
+ return (HeaderTable)tables.get( HeaderTable.TAG );
+ }
+
+ /**
+ * Get the hhea table for this TTF.
+ *
+ * @return The hhea table.
+ */
+ public HorizontalHeaderTable getHorizontalHeader()
+ {
+ return (HorizontalHeaderTable)tables.get( HorizontalHeaderTable.TAG );
+ }
+
+ /**
+ * Get the hmtx table for this TTF.
+ *
+ * @return The hmtx table.
+ */
+ public HorizontalMetricsTable getHorizontalMetrics()
+ {
+ return (HorizontalMetricsTable)tables.get( HorizontalMetricsTable.TAG );
+ }
+
+ /**
+ * Get the loca table for this TTF.
+ *
+ * @return The loca table.
+ */
+ public IndexToLocationTable getIndexToLocation()
+ {
+ return (IndexToLocationTable)tables.get( IndexToLocationTable.TAG );
+ }
+
+ /**
+ * Get the glyf table for this TTF.
+ *
+ * @return The glyf table.
+ */
+ public GlyphTable getGlyph()
+ {
+ return (GlyphTable)tables.get( GlyphTable.TAG );
+ }
+
+ /**
+ * Get the cmap table for this TTF.
+ *
+ * @return The cmap table.
+ */
+ public CMAPTable getCMAP()
+ {
+ return (CMAPTable)tables.get( CMAPTable.TAG );
+ }
+
+ /**
+ * This permit to get the COSStream of the True Type Font
+ * program representing the stream used to build this
+ * object (normally from the TTFParser object).
+ *
+ * @return COSStream True type font program stream
+ */
+ public PDStream getPDStream()
+ {
+ return data.getPDStream();
+ }
+}
diff --git a/src/main/java/org/pdfbox/ttf/package.html b/src/main/java/org/pdfbox/ttf/package.html
new file mode 100644
index 0000000..55d2af5
--- /dev/null
+++ b/src/main/java/org/pdfbox/ttf/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains classes to parse a TTF file.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/util/BitFlagHelper.java b/src/main/java/org/pdfbox/util/BitFlagHelper.java
new file mode 100644
index 0000000..058f733
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/BitFlagHelper.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This class will be used for bit flag operations.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class BitFlagHelper
+{
+ private BitFlagHelper()
+ {
+ //helper class should not be constructed
+ }
+
+ /**
+ * Sets the given boolean value at bitPos in the flags.
+ *
+ * @param dic The dictionary to set the value into.
+ * @param field The name of the field to set the value into.
+ * @param bitFlag the bit position to set the value in.
+ * @param value the value the bit position should have.
+ */
+ public static void setFlag( COSDictionary dic, String field, int bitFlag, boolean value )
+ {
+ int currentFlags = dic.getInt( field, 0 );
+ if( value )
+ {
+ currentFlags = currentFlags | bitFlag;
+ }
+ else
+ {
+ currentFlags = currentFlags &= ~bitFlag;
+ }
+ dic.setInt( field, currentFlags );
+ }
+
+ /**
+ * Gets the boolean value from the flags at the given bit
+ * position.
+ *
+ * @param dic The dictionary to get the field from.
+ * @param field The name of the field to get the flag from.
+ * @param bitFlag the bitPosition to get the value from.
+ *
+ * @return true if the number at bitPos is '1'
+ */
+ public static boolean getFlag(COSDictionary dic, String field, int bitFlag)
+ {
+ int ff = dic.getInt( field, 0 );
+ return (ff & bitFlag) == bitFlag;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/BoundingBox.java b/src/main/java/org/pdfbox/util/BoundingBox.java
new file mode 100644
index 0000000..aaea354
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/BoundingBox.java
@@ -0,0 +1,188 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.awt.Point;
+
+/**
+ * This is an implementation of a bounding box. This was originally written for the
+ * AMF parser.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class BoundingBox
+{
+ private float lowerLeftX;
+ private float lowerLeftY;
+ private float upperRightX;
+ private float upperRightY;
+
+ /**
+ * Getter for property lowerLeftX.
+ *
+ * @return Value of property lowerLeftX.
+ */
+ public float getLowerLeftX()
+ {
+ return lowerLeftX;
+ }
+
+ /**
+ * Setter for property lowerLeftX.
+ *
+ * @param lowerLeftXValue New value of property lowerLeftX.
+ */
+ public void setLowerLeftX(float lowerLeftXValue)
+ {
+ this.lowerLeftX = lowerLeftXValue;
+ }
+
+ /**
+ * Getter for property lowerLeftY.
+ *
+ * @return Value of property lowerLeftY.
+ */
+ public float getLowerLeftY()
+ {
+ return lowerLeftY;
+ }
+
+ /**
+ * Setter for property lowerLeftY.
+ *
+ * @param lowerLeftYValue New value of property lowerLeftY.
+ */
+ public void setLowerLeftY(float lowerLeftYValue)
+ {
+ this.lowerLeftY = lowerLeftYValue;
+ }
+
+ /**
+ * Getter for property upperRightX.
+ *
+ * @return Value of property upperRightX.
+ */
+ public float getUpperRightX()
+ {
+ return upperRightX;
+ }
+
+ /**
+ * Setter for property upperRightX.
+ *
+ * @param upperRightXValue New value of property upperRightX.
+ */
+ public void setUpperRightX(float upperRightXValue)
+ {
+ this.upperRightX = upperRightXValue;
+ }
+
+ /**
+ * Getter for property upperRightY.
+ *
+ * @return Value of property upperRightY.
+ */
+ public float getUpperRightY()
+ {
+ return upperRightY;
+ }
+
+ /**
+ * Setter for property upperRightY.
+ *
+ * @param upperRightYValue New value of property upperRightY.
+ */
+ public void setUpperRightY(float upperRightYValue)
+ {
+ this.upperRightY = upperRightYValue;
+ }
+
+ /**
+ * This will get the width of this rectangle as calculated by
+ * upperRightX - lowerLeftX.
+ *
+ * @return The width of this rectangle.
+ */
+ public float getWidth()
+ {
+ return getUpperRightX() - getLowerLeftX();
+ }
+
+ /**
+ * This will get the height of this rectangle as calculated by
+ * upperRightY - lowerLeftY.
+ *
+ * @return The height of this rectangle.
+ */
+ public float getHeight()
+ {
+ return getUpperRightY() - getLowerLeftY();
+ }
+
+ /**
+ * Checks if a point is inside this rectangle.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ *
+ * @return true If the point is on the edge or inside the rectangle bounds.
+ */
+ public boolean contains( float x, float y )
+ {
+ return x >= lowerLeftX && x <= upperRightX &&
+ y >= lowerLeftY && y <= upperRightY;
+ }
+
+ /**
+ * Checks if a point is inside this rectangle.
+ *
+ * @param point The point to check
+ *
+ * @return true If the point is on the edge or inside the rectangle bounds.
+ */
+ public boolean contains( Point point )
+ {
+ return contains( (float)point.getX(), (float)point.getY() );
+ }
+
+ /**
+ * This will return a string representation of this rectangle.
+ *
+ * @return This object as a string.
+ */
+ public String toString()
+ {
+ return "[" + getLowerLeftX() + "," + getLowerLeftY() + "," +
+ getUpperRightX() + "," + getUpperRightY() +"]";
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/DateConverter.java b/src/main/java/org/pdfbox/util/DateConverter.java
new file mode 100644
index 0000000..43d3566
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/DateConverter.java
@@ -0,0 +1,281 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import java.io.IOException;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+import org.pdfbox.cos.COSString;
+
+/**
+ * This class is used to convert dates to strings and back using the PDF
+ * date standards. Date are described in PDFReference1.4 section 3.8.2
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.9 $
+ */
+public class DateConverter
+{
+ private static final SimpleDateFormat PDF_DATE_FORMAT = new SimpleDateFormat( "yyyyMMddHHmmss" );
+
+ //The Date format is supposed to be the PDF_DATE_FORMAT, but not all PDF documents
+ //will use that date, so I have added a couple other potential formats
+ //to try if the original one does not work.
+ private static final SimpleDateFormat[] POTENTIAL_FORMATS = new SimpleDateFormat[] {
+ new SimpleDateFormat("EEEE, dd MMM yyyy hh:mm:ss a"),
+ new SimpleDateFormat("EEEE, MMM dd, yyyy hh:mm:ss a")};
+
+ private static final SimpleDateFormat ISO_8601_DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" );
+
+ private DateConverter()
+ {
+ //utility class should not be constructed.
+ }
+
+ /**
+ * This will convert the calendar to a string.
+ *
+ * @param date The date to convert to a string.
+ *
+ * @return The date as a String to be used in a PDF document.
+ */
+ public static String toString( Calendar date )
+ {
+ String retval = null;
+ if( date != null )
+ {
+ StringBuffer buffer = new StringBuffer();
+ TimeZone zone = date.getTimeZone();
+ long offsetInMinutes = zone.getOffset( date.getTimeInMillis() )/1000/60;
+ long hours = Math.abs( offsetInMinutes/60 );
+ long minutes = Math.abs( offsetInMinutes%60 );
+ buffer.append( "D:" );
+ buffer.append( PDF_DATE_FORMAT.format( date.getTime() ) );
+ if( offsetInMinutes == 0 )
+ {
+ buffer.append( "Z" );
+ }
+ else if( offsetInMinutes < 0 )
+ {
+ buffer.append( "-" );
+ }
+ else
+ {
+ buffer.append( "+" );
+ }
+ if( hours < 10 )
+ {
+ buffer.append( "0" );
+ }
+ buffer.append( hours );
+ buffer.append( "'" );
+ if( minutes < 10 )
+ {
+ buffer.append( "0" );
+ }
+ buffer.append( minutes );
+ buffer.append( "'" );
+ retval = buffer.toString();
+
+ }
+ return retval;
+ }
+
+ /**
+ * This will convert a string to a calendar.
+ *
+ * @param date The string representation of the calendar.
+ *
+ * @return The calendar that this string represents.
+ *
+ * @throws IOException If the date string is not in the correct format.
+ */
+ public static Calendar toCalendar( COSString date ) throws IOException
+ {
+ Calendar retval = null;
+ if( date != null )
+ {
+ retval = toCalendar( date.getString() );
+ }
+
+ return retval;
+ }
+
+ /**
+ * This will convert a string to a calendar.
+ *
+ * @param date The string representation of the calendar.
+ *
+ * @return The calendar that this string represents.
+ *
+ * @throws IOException If the date string is not in the correct format.
+ */
+ public static Calendar toCalendar( String date ) throws IOException
+ {
+ Calendar retval = null;
+ if( date != null )
+ {
+ //these are the default values
+ int year = 0;
+ int month = 1;
+ int day = 1;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ //first string off the prefix if it exists
+ try
+ {
+ SimpleTimeZone zone = null;
+ if( date.startsWith( "D:" ) )
+ {
+ date = date.substring( 2, date.length() );
+ }
+ if( date.length() < 4 )
+ {
+ throw new IOException( "Error: Invalid date format '" + date + "'" );
+ }
+ year = Integer.parseInt( date.substring( 0, 4 ) );
+ if( date.length() >= 6 )
+ {
+ month = Integer.parseInt( date.substring( 4, 6 ) );
+ }
+ if( date.length() >= 8 )
+ {
+ day = Integer.parseInt( date.substring( 6, 8 ) );
+ }
+ if( date.length() >= 10 )
+ {
+ hour = Integer.parseInt( date.substring( 8, 10 ) );
+ }
+ if( date.length() >= 12 )
+ {
+ minute = Integer.parseInt( date.substring( 10, 12 ) );
+ }
+ if( date.length() >= 14 )
+ {
+ second = Integer.parseInt( date.substring( 12, 14 ) );
+ }
+ retval = new GregorianCalendar( year, month-1, day, hour, minute, second );
+ if( date.length() >= 15 )
+ {
+ char sign = date.charAt( 14 );
+ if( sign == 'Z' )
+ {
+ zone = new SimpleTimeZone(0,"Unknown");
+ }
+ else
+ {
+ int hours = 0;
+ int minutes = 0;
+ if( date.length() >= 17 )
+ {
+ hours = Integer.parseInt( date.substring( 15, 17 ) );
+ }
+ if( date.length() > 20 )
+ {
+ minutes = Integer.parseInt( date.substring( 18, 20 ) );
+ }
+ zone = new SimpleTimeZone( hours*60*60*1000 + minutes*60*1000, "Unknown" );
+ }
+ retval.setTimeZone( zone );
+ }
+ }
+ catch( NumberFormatException e )
+ {
+ for( int i=0; retval == null && i<POTENTIAL_FORMATS.length; i++ )
+ {
+ try
+ {
+ Date utilDate = POTENTIAL_FORMATS[i].parse( date );
+ retval = new GregorianCalendar();
+ retval.setTime( utilDate );
+ }
+ catch( ParseException pe )
+ {
+ //ignore and move to next potential format
+ }
+ }
+ if( retval == null )
+ {
+ //we didn't find a valid date format so throw an exception
+ throw new IOException( "Error converting date:" + date );
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Convert the date to iso 8601 string format.
+ *
+ * @param cal The date to convert.
+ * @return The date represented as an ISO 8601 string.
+ */
+ public static String toISO8601( Calendar cal )
+ {
+ StringBuffer retval = new StringBuffer();
+ retval.append( ISO_8601_DATE_FORMAT.format( cal.getTime() ) );
+ int timeZone = cal.get( Calendar.ZONE_OFFSET );
+ if( timeZone < 0 )
+ {
+ retval.append( "-" );
+ }
+ else
+ {
+ retval.append( "+" );
+ }
+ timeZone = Math.abs( timeZone );
+ //milliseconds/1000 = seconds = seconds / 60 = minutes = minutes/60 = hours
+ int hours = timeZone/1000/60/60;
+ int minutes = (timeZone - (hours*1000*60*60))/1000/1000;
+ if( hours < 10 )
+ {
+ retval.append( "0" );
+ }
+ retval.append( Integer.toString( hours ) );
+ retval.append( ":" );
+ if( minutes < 10 )
+ {
+ retval.append( "0" );
+ }
+ retval.append( Integer.toString( minutes ) );
+
+ return retval.toString();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/DefaultFileFilter.java b/src/main/java/org/pdfbox/util/DefaultFileFilter.java
new file mode 100644
index 0000000..e4baec0
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/DefaultFileFilter.java
@@ -0,0 +1,285 @@
+/*
+ * @(#)DefaultFileFilter.java 1.12 01/12/03
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+package org.pdfbox.util;
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.Enumeration;
+//import javax.swing.*;
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * A convenience implementation of FileFilter that filters out
+ * all files except for those type extensions that it knows about.
+ *
+ * Extensions are of the type ".foo", which is typically found on
+ * Windows and Unix boxes, but not on Macinthosh. Case is ignored.
+ *
+ * Example - create a new filter that filerts out all files
+ * but gif and jpg image files:
+ *
+ * JFileChooser chooser = new JFileChooser();
+ * DefaultFileFilter filter = new DefaultFileFilter(
+ * new String{"gif", "jpg"}, "JPEG & GIF Images")
+ * chooser.addChoosableFileFilter(filter);
+ * chooser.showOpenDialog(this);
+ *
+ * @version $Revision: 1.2 $
+ * @author Jeff Dinkins
+ */
+public class DefaultFileFilter extends FileFilter
+{
+
+ private static final String TYPE_UNKNOWN = "Type Unknown";
+ private static final String HIDDEN_FILE = "Hidden File";
+
+ private Hashtable filters = null;
+ private String description = null;
+ private String fullDescription = null;
+ private boolean useExtensionsInDescription = true;
+
+ /**
+ * Creates a file filter. If no filters are added, then all
+ * files are accepted.
+ *
+ * @see #addExtension
+ */
+ public DefaultFileFilter()
+ {
+ this.filters = new Hashtable();
+ }
+
+ /**
+ * Creates a file filter that accepts files with the given extension.
+ * Example: new DefaultFileFilter("jpg");
+ *
+ * @see #addExtension
+ */
+ public DefaultFileFilter(String extension)
+ {
+ this(extension,null);
+ }
+
+ /**
+ * Creates a file filter that accepts the given file type.
+ * Example: new DefaultFileFilter("jpg", "JPEG Image Images");
+ *
+ * Note that the "." before the extension is not needed. If
+ * provided, it will be ignored.
+ *
+ * @see #addExtension
+ */
+ public DefaultFileFilter(String extension, String desc)
+ {
+ this();
+ if(extension!=null)
+ {
+ addExtension(extension);
+ }
+ if(desc!=null)
+ {
+ setDescription(desc);
+ }
+ }
+
+ /**
+ * Creates a file filter from the given string array.
+ * Example: new DefaultFileFilter(String {"gif", "jpg"});
+ *
+ * Note that the "." before the extension is not needed adn
+ * will be ignored.
+ *
+ * @see #addExtension
+ */
+ public DefaultFileFilter(String[] filterArray)
+ {
+ this(filterArray, null);
+ }
+
+ /**
+ * Creates a file filter from the given string array and description.
+ * Example: new DefaultFileFilter(String {"gif", "jpg"}, "Gif and JPG Images");
+ *
+ * Note that the "." before the extension is not needed and will be ignored.
+ *
+ * @see #addExtension
+ */
+ public DefaultFileFilter(String[] filterArray, String desc)
+ {
+ this();
+ for (int i = 0; i < filterArray.length; i++)
+ {
+ // add filters one by one
+ addExtension(filterArray[i]);
+ }
+ if(desc!=null)
+ {
+ setDescription(desc);
+ }
+ }
+
+ /**
+ * Files that begin with "." are ignored.
+ *
+ * @param f The file to accept.
+ * @return true if this file should be shown in the directory pane, false if it shouldn't.
+ * @see #getExtension
+ * @see FileFilter#accepts
+ */
+ public boolean accept(File f)
+ {
+ if(f != null)
+ {
+ if(f.isDirectory())
+ {
+ return true;
+ }
+ String extension = getExtension(f);
+ if(extension != null && filters.get(getExtension(f)) != null)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the extension portion of the file's name .
+ *
+ * @param f The file to get the extension of.
+ * @return The extension of a file.
+ * @see #getExtension
+ * @see FileFilter#accept
+ */
+ public String getExtension(File f)
+ {
+ if(f != null)
+ {
+ String filename = f.getName();
+ int i = filename.lastIndexOf('.');
+ if(i>0 && i<filename.length()-1)
+ {
+ return filename.substring(i+1).toLowerCase();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a filetype "dot" extension to filter against.
+ *
+ * For example: the following code will create a filter that filters
+ * out all files except those that end in ".jpg" and ".tif":
+ *
+ * DefaultFileFilter filter = new DefaultFileFilter();
+ * filter.addExtension("jpg");
+ * filter.addExtension("tif");
+ *
+ * Note that the "." before the extension is not needed and will be ignored.
+ *
+ * @param extension The new extension to add.
+ */
+ public void addExtension(String extension)
+ {
+ if(filters == null)
+ {
+ filters = new Hashtable(5);
+ }
+ filters.put(extension.toLowerCase(), this);
+ fullDescription = null;
+ }
+
+
+ /**
+ * Returns the human readable description of this filter. For
+ * example: "JPEG and GIF Image Files (*.jpg, *.gif)"
+ *
+ * @see setDescription
+ * @see setExtensionListInDescription
+ * @see isExtensionListInDescription
+ * @see FileFilter#getDescription
+ *
+ * @return The human readable description of this filter.
+ */
+ public String getDescription()
+ {
+ if(fullDescription == null)
+ {
+ if(description == null || isExtensionListInDescription())
+ {
+ fullDescription = description==null ? "(" : description + " (";
+ // build the description from the extension list
+ Enumeration extensions = filters.keys();
+ if(extensions != null)
+ {
+ fullDescription += "." + (String) extensions.nextElement();
+ while (extensions.hasMoreElements())
+ {
+ fullDescription += ", ." + (String) extensions.nextElement();
+ }
+ }
+ fullDescription += ")";
+ }
+ else
+ {
+ fullDescription = description;
+ }
+ }
+ return fullDescription;
+ }
+
+ /**
+ * Sets the human readable description of this filter. For
+ * example: filter.setDescription("Gif and JPG Images");
+ *
+ * @param desc the new description for the file.
+ * @see setDescription
+ * @see setExtensionListInDescription
+ * @see isExtensionListInDescription
+ */
+ public void setDescription(String desc)
+ {
+ description = desc;
+ fullDescription = null;
+ }
+
+ /**
+ * Determines whether the extension list (.jpg, .gif, etc) should
+ * show up in the human readable description.
+ *
+ * Only relevent if a description was provided in the constructor
+ * or using setDescription();
+ *
+ *
+ * @param b Tell if the extionsion shoud show up in human readable description.
+ * @see getDescription
+ * @see setDescription
+ * @see isExtensionListInDescription
+ */
+ public void setExtensionListInDescription(boolean b)
+ {
+ useExtensionsInDescription = b;
+ fullDescription = null;
+ }
+
+ /**
+ * @return whether the extension list (.jpg, .gif, etc) should
+ * show up in the human readable description.
+ *
+ * Only relevent if a description was provided in the constructor
+ * or using setDescription();
+ *
+ *
+ * @see getDescription
+ * @see setDescription
+ * @see setExtensionListInDescription
+ */
+ public boolean isExtensionListInDescription()
+ {
+ return useExtensionsInDescription;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/ErrorLogger.java b/src/main/java/org/pdfbox/util/ErrorLogger.java
new file mode 100644
index 0000000..b2f380c
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/ErrorLogger.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+/**
+ * This class deals with some logging that is not handled by the log4j replacement.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class ErrorLogger
+{
+ /**
+ * Utility class, should not be instantiated.
+ *
+ */
+ private ErrorLogger()
+ {
+ }
+
+ /**
+ * Log an error message. This is only used for log4j replacement and
+ * should never be used when writing code.
+ *
+ * @param errorMessage The error message.
+ */
+ public static void log( String errorMessage )
+ {
+ System.err.println( errorMessage );
+ }
+
+ /**
+ * Log an error message. This is only used for log4j replacement and
+ * should never be used when writing code.
+ *
+ * @param errorMessage The error message.
+ * @param t The exception.
+ */
+ public static void log( String errorMessage, Throwable t )
+ {
+ System.err.println( errorMessage );
+ t.printStackTrace();
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/ImageParameters.java b/src/main/java/org/pdfbox/util/ImageParameters.java
new file mode 100644
index 0000000..79f09b0
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/ImageParameters.java
@@ -0,0 +1,234 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * This contains all of the image parameters for in inlined image.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class ImageParameters
+{
+ private COSDictionary dictionary;
+
+ /**
+ * Constructor.
+ */
+ public ImageParameters()
+ {
+ dictionary = new COSDictionary();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param params The image parameters.
+ */
+ public ImageParameters( COSDictionary params )
+ {
+ dictionary = params;
+ }
+
+ /**
+ * This will get the dictionary that stores the image parameters.
+ *
+ * @return The COS dictionary that stores the image parameters.
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ private COSBase getCOSObject( String abbreviatedName, String name )
+ {
+ COSBase retval = dictionary.getDictionaryObject( COSName.getPDFName( abbreviatedName ) );
+ if( retval == null )
+ {
+ retval = dictionary.getDictionaryObject( COSName.getPDFName( name ) );
+ }
+ return retval;
+ }
+
+ private int getNumberOrNegativeOne( String abbreviatedName, String name )
+ {
+ int retval = -1;
+ COSNumber number = (COSNumber)getCOSObject( abbreviatedName, name );
+ if( number != null )
+ {
+ retval = number.intValue();
+ }
+ return retval;
+ }
+
+ /**
+ * The bits per component of this image. This will return -1 if one has not
+ * been set.
+ *
+ * @return The number of bits per component.
+ */
+ public int getBitsPerComponent()
+ {
+ return getNumberOrNegativeOne( "BPC", "BitsPerComponent" );
+ }
+
+ /**
+ * Set the number of bits per component.
+ *
+ * @param bpc The number of bits per component.
+ */
+ public void setBitsPerComponent( int bpc )
+ {
+ dictionary.setItem( COSName.getPDFName( "BPC" ), new COSInteger( bpc ) );
+ }
+
+
+ /**
+ * This will get the color space or null if none exists.
+ *
+ * @return The color space for this image.
+ *
+ * @throws IOException If there is an error getting the colorspace.
+ */
+ public PDColorSpace getColorSpace() throws IOException
+ {
+ COSBase cs = getCOSObject( "CS", "ColorSpace" );
+ PDColorSpace retval = null;
+ if( cs != null )
+ {
+ retval = PDColorSpaceFactory.createColorSpace( cs );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the color space for this image.
+ *
+ * @param cs The color space for this image.
+ */
+ public void setColorSpace( PDColorSpace cs )
+ {
+ COSBase base = null;
+ if( cs != null )
+ {
+ base = cs.getCOSObject();
+ }
+ dictionary.setItem( COSName.getPDFName( "CS" ), base );
+ }
+
+ /**
+ * The height of this image. This will return -1 if one has not
+ * been set.
+ *
+ * @return The height.
+ */
+ public int getHeight()
+ {
+ return getNumberOrNegativeOne( "H", "Height" );
+ }
+
+ /**
+ * Set the height of the image.
+ *
+ * @param h The height of the image.
+ */
+ public void setHeight( int h )
+ {
+ dictionary.setItem( COSName.getPDFName( "H" ), new COSInteger( h ) );
+ }
+
+ /**
+ * The width of this image. This will return -1 if one has not
+ * been set.
+ *
+ * @return The width.
+ */
+ public int getWidth()
+ {
+ return getNumberOrNegativeOne( "W", "Width" );
+ }
+
+ /**
+ * Set the width of the image.
+ *
+ * @param w The width of the image.
+ */
+ public void setWidth( int w )
+ {
+ dictionary.setItem( COSName.getPDFName( "W" ), new COSInteger( w ) );
+ }
+
+ /**
+ * This will get the list of filters that are associated with this stream. Or
+ * null if there are none.
+ * @return A list of all encoding filters to apply to this stream.
+ */
+ public List getFilters()
+ {
+ List retval = null;
+ COSBase filters = dictionary.getDictionaryObject( new String[] {"Filter", "F"} );
+ if( filters instanceof COSName )
+ {
+ COSName name = (COSName)filters;
+ retval = new COSArrayList( name.getName(), name, dictionary, "Filter" );
+ }
+ else if( filters instanceof COSArray )
+ {
+ retval = COSArrayList.convertCOSNameCOSArrayToList( (COSArray)filters );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the filters that are part of this stream.
+ *
+ * @param filters The filters that are part of this stream.
+ */
+ public void setFilters( List filters )
+ {
+ COSBase obj = COSArrayList.convertStringListToCOSNameCOSArray( filters );
+ dictionary.setItem( "Filter", obj );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/Matrix.java b/src/main/java/org/pdfbox/util/Matrix.java
new file mode 100644
index 0000000..ff14887
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/Matrix.java
@@ -0,0 +1,350 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.awt.geom.AffineTransform;
+
+/**
+ * This class will be used for matrix manipulation.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.13 $
+ */
+public class Matrix implements Cloneable
+{
+ private float[] single =
+ {
+ 1,0,0,
+ 0,1,0,
+ 0,0,1
+ };
+
+ /**
+ * Constructor.
+ */
+ public Matrix()
+ {
+ //default constructor
+ }
+
+ /**
+ * Create an affine transform from this matrix's values.
+ *
+ * @return An affine transform with this matrix's values.
+ */
+ public AffineTransform createAffineTransform()
+ {
+ AffineTransform retval = new AffineTransform(
+ single[0], single[1],
+ single[3], single[4],
+ single[6], single[7] );
+ return retval;
+ }
+
+ /**
+ * Set the values of the matrix from the AffineTransform.
+ *
+ * @param af The transform to get the values from.
+ */
+ public void setFromAffineTransform( AffineTransform af )
+ {
+ single[0] = (float)af.getScaleX();
+ single[1] = (float)af.getShearY();
+ single[3] = (float)af.getShearX();
+ single[4] = (float)af.getScaleY();
+ single[6] = (float)af.getTranslateX();
+ single[7] = (float)af.getTranslateY();
+ }
+
+ /**
+ * This will get a matrix value at some point.
+ *
+ * @param row The row to get the value from.
+ * @param column The column to get the value from.
+ *
+ * @return The value at the row/column position.
+ */
+ public float getValue( int row, int column )
+ {
+ return single[row*3+column];
+ }
+
+ /**
+ * This will set a value at a position.
+ *
+ * @param row The row to set the value at.
+ * @param column the column to set the value at.
+ * @param value The value to set at the position.
+ */
+ public void setValue( int row, int column, float value )
+ {
+ single[row*3+column] = value;
+ }
+
+ /**
+ * Return a single dimension array of all values in the matrix.
+ *
+ * @return The values ot this matrix.
+ */
+ public float[][] getValues()
+ {
+ float[][] retval = new float[3][3];
+ retval[0][0] = single[0];
+ retval[0][1] = single[1];
+ retval[0][2] = single[2];
+ retval[1][0] = single[3];
+ retval[1][1] = single[4];
+ retval[1][2] = single[5];
+ retval[2][0] = single[6];
+ retval[2][1] = single[7];
+ retval[2][2] = single[8];
+ return retval;
+ }
+
+ /**
+ * Return a single dimension array of all values in the matrix.
+ *
+ * @return The values ot this matrix.
+ */
+ public double[][] getValuesAsDouble()
+ {
+ double[][] retval = new double[3][3];
+ retval[0][0] = single[0];
+ retval[0][1] = single[1];
+ retval[0][2] = single[2];
+ retval[1][0] = single[3];
+ retval[1][1] = single[4];
+ retval[1][2] = single[5];
+ retval[2][0] = single[6];
+ retval[2][1] = single[7];
+ retval[2][2] = single[8];
+ return retval;
+ }
+
+ /**
+ * This will take the current matrix and multipy it with a matrix that is passed in.
+ *
+ * @param b The matrix to multiply by.
+ *
+ * @return The result of the two multiplied matrices.
+ */
+ public Matrix multiply( Matrix b )
+ {
+ Matrix result = new Matrix();
+
+ float[] bMatrix = b.single;
+ float[] resultMatrix = result.single;
+ resultMatrix[0] = single[0] * bMatrix[0] + single[1] * bMatrix[3] + single[2] * bMatrix[6];
+ resultMatrix[1] = single[0] * bMatrix[1] + single[1] * bMatrix[4] + single[2] * bMatrix[7];
+ resultMatrix[2] = single[0] * bMatrix[2] + single[1] * bMatrix[5] + single[2] * bMatrix[8];
+ resultMatrix[3] = single[3] * bMatrix[0] + single[4] * bMatrix[3] + single[5] * bMatrix[6];
+ resultMatrix[4] = single[3] * bMatrix[1] + single[4] * bMatrix[4] + single[5] * bMatrix[7];
+ resultMatrix[5] = single[3] * bMatrix[2] + single[4] * bMatrix[5] + single[5] * bMatrix[8];
+ resultMatrix[6] = single[6] * bMatrix[0] + single[7] * bMatrix[3] + single[8] * bMatrix[6];
+ resultMatrix[7] = single[6] * bMatrix[1] + single[7] * bMatrix[4] + single[8] * bMatrix[7];
+ resultMatrix[8] = single[6] * bMatrix[2] + single[7] * bMatrix[5] + single[8] * bMatrix[8];
+
+ return result;
+ }
+
+ /**
+ * Create a new matrix with just the scaling operators.
+ *
+ * @return A new matrix with just the scaling operators.
+ */
+ public Matrix extractScaling()
+ {
+ Matrix retval = new Matrix();
+
+ retval.single[0] = this.single[0];
+ retval.single[4] = this.single[4];
+
+ return retval;
+ }
+
+ /**
+ * Convenience method to create a scaled instance.
+ *
+ * @param x The xscale operator.
+ * @param y The yscale operator.
+ * @return A new matrix with just the x/y scaling
+ */
+ public static Matrix getScaleInstance( float x, float y)
+ {
+ Matrix retval = new Matrix();
+
+ retval.single[0] = x;
+ retval.single[4] = y;
+
+ return retval;
+ }
+
+ /**
+ * Create a new matrix with just the translating operators.
+ *
+ * @return A new matrix with just the translating operators.
+ */
+ public Matrix extractTranslating()
+ {
+ Matrix retval = new Matrix();
+
+ retval.single[6] = this.single[6];
+ retval.single[7] = this.single[7];
+
+ return retval;
+ }
+
+ /**
+ * Convenience method to create a translating instance.
+ *
+ * @param x The x translating operator.
+ * @param y The y translating operator.
+ * @return A new matrix with just the x/y translating.
+ */
+ public static Matrix getTranslatingInstance( float x, float y)
+ {
+ Matrix retval = new Matrix();
+
+ retval.single[6] = x;
+ retval.single[7] = y;
+
+ return retval;
+ }
+
+ /**
+ * Clones this object.
+ * @return cloned matrix as an object.
+ */
+ public Object clone()
+ {
+ Matrix clone = new Matrix();
+ System.arraycopy( single, 0, clone.single, 0, 9 );
+ return clone;
+ }
+
+ /**
+ * This will copy the text matrix data.
+ *
+ * @return a matrix that matches this one.
+ */
+ public Matrix copy()
+ {
+ return (Matrix) clone();
+ }
+
+ /**
+ * This will return a string representation of the matrix.
+ *
+ * @return The matrix as a string.
+ */
+ public String toString()
+ {
+ StringBuffer result = new StringBuffer( "" );
+ result.append( "[[" );
+ result.append( single[0] + "," );
+ result.append( single[1] + "," );
+ result.append( single[2] + "][");
+ result.append( single[3] + "," );
+ result.append( single[4] + "," );
+ result.append( single[5] + "][");
+ result.append( single[6] + "," );
+ result.append( single[7] + "," );
+ result.append( single[8] + "]]");
+
+ return result.toString();
+ }
+
+ /**
+ * Get the xscaling factor of this matrix.
+ * @return The x-scale.
+ */
+ public float getXScale()
+ {
+ float xScale = single[0];
+
+ /**
+ * BM: if the trm is rotated, the calculation is a little more complicated
+ *
+ * The rotation matrix multiplied with the scaling matrix is:
+ * ( x 0 0) ( cos sin 0) ( x*cos x*sin 0)
+ * ( 0 y 0) * (-sin cos 0) = (-y*sin y*cos 0)
+ * ( 0 0 1) ( 0 0 1) ( 0 0 1)
+ *
+ * So, if you want to deduce x from the matrix you take
+ * M(0,0) = x*cos and M(0,1) = x*sin and use the theorem of Pythagoras
+ *
+ * sqrt(M(0,0)^2+M(0,1)^2) =
+ * sqrt(x2*cos2+x2*sin2) =
+ * sqrt(x2*(cos2+sin2)) = <- here is the trick cos2+sin2 is one
+ * sqrt(x2) =
+ * abs(x)
+ */
+ if( !(single[1]==0.0f && single[3]==0.0f) )
+ {
+ xScale = (float)Math.sqrt(Math.pow(single[0], 2)+
+ Math.pow(single[1], 2));
+ }
+ return xScale;
+ }
+
+ /**
+ * Get the y scaling factor of this matrix.
+ * @return The y-scale factor.
+ */
+ public float getYScale()
+ {
+ float yScale = single[4];
+ if( !(single[1]==0.0f && single[3]==0.0f) )
+ {
+ yScale = (float)Math.sqrt(Math.pow(single[3], 2)+
+ Math.pow(single[4], 2));
+ }
+ return yScale;
+ }
+
+ /**
+ * Get the x position in the matrix.
+ * @return The x-position.
+ */
+ public float getXPosition()
+ {
+ return single[6];
+ }
+
+ /**
+ * Get the y position.
+ * @return The y position.
+ */
+ public float getYPosition()
+ {
+ return single[7];
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/PDFHighlighter.java b/src/main/java/org/pdfbox/util/PDFHighlighter.java
new file mode 100644
index 0000000..6c27225
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/PDFHighlighter.java
@@ -0,0 +1,213 @@
+package org.pdfbox.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+
+/**
+ * Highlighting of words in a PDF document with an XML file.
+ *
+ * @author slagraulet (slagraulet@cardiweb.com)
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ *
+ * @see <a href="http://partners.adobe.com/public/developer/en/pdf/HighlightFileFormat.pdf">
+ * Adobe Highlight File Format</a>
+ */
+public class PDFHighlighter extends PDFTextStripper
+{
+ private Writer highlighterOutput = null;
+ //private Color highlightColor = Color.YELLOW;
+
+ private String[] searchedWords;
+ private ByteArrayOutputStream textOS = null;
+ private Writer textWriter = null;
+
+ /**
+ * Default constructor.
+ *
+ * @throws IOException If there is an error constructing this class.
+ */
+ public PDFHighlighter() throws IOException
+ {
+ super();
+ super.setLineSeparator( "" );
+ super.setPageSeparator( "" );
+ super.setWordSeparator( "" );
+ super.setShouldSeparateByBeads( false );
+ super.setSuppressDuplicateOverlappingText( false );
+ }
+
+ /**
+ * Generate an XML highlight string based on the PDF.
+ *
+ * @param pdDocument The PDF to find words in.
+ * @param highlightWord The word to search for.
+ * @param xmlOutput The resulting output xml file.
+ *
+ * @throws IOException If there is an error reading from the PDF, or writing to the XML.
+ */
+ public void generateXMLHighlight(PDDocument pdDocument, String highlightWord, Writer xmlOutput ) throws IOException
+ {
+ generateXMLHighlight( pdDocument, new String[] { highlightWord }, xmlOutput );
+ }
+
+ /**
+ * Generate an XML highlight string based on the PDF.
+ *
+ * @param pdDocument The PDF to find words in.
+ * @param sWords The words to search for.
+ * @param xmlOutput The resulting output xml file.
+ *
+ * @throws IOException If there is an error reading from the PDF, or writing to the XML.
+ */
+ public void generateXMLHighlight(PDDocument pdDocument, String[] sWords, Writer xmlOutput ) throws IOException
+ {
+ highlighterOutput = xmlOutput;
+ searchedWords = sWords;
+ highlighterOutput.write("<XML>\n<Body units=characters " +
+ //color and mode are not implemented by the highlight spec
+ //so don't include them for now
+ //" color=#" + getHighlightColorAsString() +
+ //" mode=active " + */
+ " version=2>\n<Highlight>\n");
+ textOS = new ByteArrayOutputStream();
+ textWriter = new OutputStreamWriter( textOS, "UTF-16" );
+ writeText(pdDocument, textWriter);
+ highlighterOutput.write("</Highlight>\n</Body>\n</XML>");
+ highlighterOutput.flush();
+ }
+
+ /**
+ * @see PDFTextStripper#endPage( PDPage )
+ */
+ protected void endPage( PDPage pdPage ) throws IOException
+ {
+ textWriter.flush();
+
+ String page = new String( textOS.toByteArray(), "UTF-16" );
+ textOS.reset();
+ //page = page.replaceAll( "\n", "" );
+ //page = page.replaceAll( "\r", "" );
+ //page = CCRStringUtil.stripChar(page, '\n');
+ //page = CCRStringUtil.stripChar(page, '\r');
+
+ // Traitement des listes à puces (caractères spéciaux)
+ if (page.indexOf("a") != -1)
+ {
+ page = page.replaceAll("a[0-9]{1,3}", ".");
+ }
+
+ for (int i = 0; i < searchedWords.length; i++)
+ {
+ Pattern pattern = Pattern.compile(searchedWords[i], Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pattern.matcher(page);
+ while( matcher.find() )
+ {
+ int begin = matcher.start();
+ int end = matcher.end();
+ highlighterOutput.write(" <loc " +
+ "pg=" + (getCurrentPageNo()-1)
+ + " pos=" + begin
+ + " len="+ (end - begin)
+ + ">\n");
+ }
+ }
+ }
+
+ /**
+ * Command line application.
+ *
+ * @param args The command line arguments to the application.
+ *
+ * @throws IOException If there is an error generating the highlight file.
+ */
+ public static void main(String[] args) throws IOException
+ {
+ PDFHighlighter xmlExtractor = new PDFHighlighter();
+ PDDocument doc = null;
+ try
+ {
+ if( args.length < 2 )
+ {
+ usage();
+ }
+ String[] highlightStrings = new String[ args.length - 1];
+ System.arraycopy( args, 1, highlightStrings, 0, highlightStrings.length );
+ doc = PDDocument.load( args[0] );
+
+ xmlExtractor.generateXMLHighlight(
+ doc,
+ highlightStrings,
+ new OutputStreamWriter( System.out ) );
+ }
+ finally
+ {
+ if( doc != null )
+ {
+ doc.close();
+ }
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println( "usage: java " + PDFHighlighter.class.getName() + " <pdf file> word1 word2 word3 ..." );
+ System.exit( 1 );
+ }
+
+
+ /**
+ * Get the color to highlight the strings with. Default is Color.YELLOW.
+ *
+ * @return The color to highlight strings with.
+ */
+ /*public Color getHighlightColor()
+ {
+ return highlightColor;
+ }**/
+
+ /**
+ * Get the color to highlight the strings with. Default is Color.YELLOW.
+ *
+ * @param color The color to highlight strings with.
+ */
+ /*public void setHighlightColor(Color color)
+ {
+ this.highlightColor = color;
+ }**/
+
+ /**
+ * Set the highlight color using HTML like rgb string. The string must be 6 characters long.
+ *
+ * @param color The color to use for highlighting. Should be in the format of "FF0000".
+ */
+ /*public void setHighlightColor( String color )
+ {
+ highlightColor = Color.decode( color );
+ }**/
+
+ /**
+ * Get the highlight color as an HTML like string. This will return a string of six characters.
+ *
+ * @return The current highlight color. For example FF0000
+ */
+ /*public String getHighlightColorAsString()
+ {
+ //BJL: kudos to anyone that has a cleaner way of doing this!
+ String red = Integer.toHexString( highlightColor.getRed() );
+ String green = Integer.toHexString( highlightColor.getGreen() );
+ String blue = Integer.toHexString( highlightColor.getBlue() );
+
+ return (red.length() < 2 ? "0" + red : red) +
+ (green.length() < 2 ? "0" + green : green) +
+ (blue.length() < 2 ? "0" + blue : blue);
+ }**/
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/PDFOperator.java b/src/main/java/org/pdfbox/util/PDFOperator.java
new file mode 100644
index 0000000..76d9887
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/PDFOperator.java
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class represents an Operator in the content stream.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.13 $
+ */
+public class PDFOperator
+{
+ private String theOperator;
+ private byte[] imageData;
+ private ImageParameters imageParameters;
+
+ private static Map operators = Collections.synchronizedMap( new HashMap() );
+
+ /**
+ * Constructor.
+ *
+ * @param aOperator The operator that this object will represent.
+ */
+ private PDFOperator( String aOperator )
+ {
+ theOperator = aOperator;
+ if( aOperator.startsWith( "/" ) )
+ {
+ throw new RuntimeException( "Operators are not allowed to start with / '" + aOperator + "'" );
+ }
+ }
+
+ /**
+ * This is used to create/cache operators in the system.
+ *
+ * @param operator The operator for the system.
+ *
+ * @return The operator that matches the operator keyword.
+ */
+ public static PDFOperator getOperator( String operator )
+ {
+ PDFOperator operation = null;
+ if( operator.equals( "ID" ) || operator.equals( "BI" ) )
+ {
+ //we can't cache the ID operators.
+ operation = new PDFOperator( operator );
+ }
+ else
+ {
+ operation = (PDFOperator)operators.get( operator );
+ if( operation == null )
+ {
+ operation = new PDFOperator( operator );
+ operators.put( operator, operation );
+ }
+ }
+
+ return operation;
+ }
+
+ /**
+ * This will get the operation that this operator represents.
+ *
+ * @return The string representation of the operation.
+ */
+ public String getOperation()
+ {
+ return theOperator;
+ }
+
+ /**
+ * This will print a string rep of this class.
+ *
+ * @return A string rep of this class.
+ */
+ public String toString()
+ {
+ return "PDFOperator{" + theOperator + "}";
+ }
+
+ /**
+ * This is the special case for the ID operator where there are just random
+ * bytes inlined the stream.
+ *
+ * @return Value of property imageData.
+ */
+ public byte[] getImageData()
+ {
+ return this.imageData;
+ }
+
+ /**
+ * This will set the image data, this is only used for the ID operator.
+ *
+ * @param imageDataArray New value of property imageData.
+ */
+ public void setImageData(byte[] imageDataArray)
+ {
+ imageData = imageDataArray;
+ }
+
+ /**
+ * This will get the image parameters, this is only valid for BI operators.
+ *
+ * @return The image parameters.
+ */
+ public ImageParameters getImageParameters()
+ {
+ return imageParameters;
+ }
+
+ /**
+ * This will set the image parameters, this is only valid for BI operators.
+ *
+ * @param params The image parameters.
+ */
+ public void setImageParameters( ImageParameters params)
+ {
+ imageParameters = params;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/PDFStreamEngine.java b/src/main/java/org/pdfbox/util/PDFStreamEngine.java
new file mode 100644
index 0000000..1e05f8a
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/PDFStreamEngine.java
@@ -0,0 +1,622 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Stack;
+
+import org.pdfbox.cos.COSObject;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.exceptions.WrappedIOException;
+
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.font.PDFont;
+
+import org.pdfbox.pdmodel.graphics.PDGraphicsState;
+
+import org.pdfbox.util.operator.OperatorProcessor;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class will run through a PDF content stream and execute certain operations
+ * and provide a callback interface for clients that want to do things with the stream.
+ * See the PDFTextStripper class for an example of how to use this class.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.29 $
+ */
+public class PDFStreamEngine
+{
+ private static Logger log = Logger.getLogger(PDFStreamEngine.class);
+
+ static protected final byte[] SPACE_BYTES = { (byte)32 };
+
+ private PDGraphicsState graphicsState = null;
+
+ protected Matrix textMatrix = null;
+ protected Matrix textLineMatrix = null;
+ protected Stack graphicsStack = new Stack();
+ //private PDResources resources = null;
+
+ protected Map operators = new HashMap();
+
+ protected Map fontToAverageWidths = new HashMap();
+
+ protected Stack streamResourcesStack = new Stack();
+
+ protected PDPage page;
+
+ /**
+ * This is a simple internal class used by the Stream engine to handle the
+ * resources stack.
+ */
+ protected static class StreamResources
+ {
+ protected Map fonts;
+ protected Map colorSpaces;
+ protected Map xobjects;
+ protected Map graphicsStates;
+ protected PDResources resources;
+ }
+
+ /**
+ * Constructor.
+ */
+ public PDFStreamEngine()
+ {
+ //default constructor
+ }
+
+ /**
+ * Constructor with engine properties. The property keys are all
+ * PDF operators, the values are class names used to execute those
+ * operators.
+ *
+ * @param properties The engine properties.
+ *
+ * @throws IOException If there is an error setting the engine properties.
+ */
+ public PDFStreamEngine( Properties properties ) throws IOException
+ {
+ try
+ {
+ Iterator keys = properties.keySet().iterator();
+ while( keys.hasNext() )
+ {
+ String operator = (String)keys.next();
+ String operatorClass = properties.getProperty( operator );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Operator Class: " + operator + "=" + operatorClass );
+ }
+ OperatorProcessor op = (OperatorProcessor)Class.forName( operatorClass ).newInstance();
+ op.setContext( this );
+ operators.put( operator, op );
+ }
+ }
+ catch( Exception e )
+ {
+ throw new WrappedIOException( e );
+ }
+ }
+
+ /**
+ * This will process the contents of the stream.
+ *
+ * @param aPage The page.
+ * @param resources The location to retrieve resources.
+ * @param cosStream the Stream to execute.
+ *
+ *
+ * @throws IOException if there is an error accessing the stream.
+ */
+ public void processStream( PDPage aPage, PDResources resources, COSStream cosStream ) throws IOException
+ {
+ graphicsState = new PDGraphicsState();
+ textMatrix = null;
+ textLineMatrix = null;
+ graphicsStack.clear();
+ streamResourcesStack.clear();
+ fontToAverageWidths.clear();
+
+ processSubStream( aPage, resources, cosStream );
+ }
+
+ /**
+ * Process a sub stream of the current stream.
+ *
+ * @param aPage The page used for drawing.
+ * @param resources The resources used when processing the stream.
+ * @param cosStream The stream to process.
+ *
+ * @throws IOException If there is an exception while processing the stream.
+ */
+ public void processSubStream( PDPage aPage, PDResources resources, COSStream cosStream ) throws IOException
+ {
+ page = aPage;
+ if( resources != null )
+ {
+ StreamResources sr = new StreamResources();
+ sr.fonts = resources.getFonts();
+ sr.colorSpaces = resources.getColorSpaces();
+ sr.xobjects = resources.getXObjects();
+ sr.graphicsStates = resources.getGraphicsStates();
+ sr.resources = resources;
+ streamResourcesStack.push(sr);
+ }
+ try
+ {
+ List arguments = new ArrayList();
+ long startTokens = System.currentTimeMillis();
+ List tokens = cosStream.getStreamTokens();
+ long stopTokens = System.currentTimeMillis();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Getting tokens time=" + (stopTokens-startTokens) );
+ }
+ if( tokens != null )
+ {
+ Iterator iter = tokens.iterator();
+ while( iter.hasNext() )
+ {
+ Object next = iter.next();
+ if( next instanceof COSObject )
+ {
+ arguments.add( ((COSObject)next).getObject() );
+ }
+ else if( next instanceof PDFOperator )
+ {
+ processOperator( (PDFOperator)next, arguments );
+ arguments = new ArrayList();
+ }
+ else
+ {
+ arguments.add( next );
+ }
+ }
+ }
+ }
+ finally
+ {
+ if( resources != null )
+ {
+ streamResourcesStack.pop();
+ }
+ }
+
+ }
+
+ /**
+ * A method provided as an event interface to allow a subclass to perform
+ * some specific functionality when a character needs to be displayed.
+ *
+ * @param text The character to be displayed.
+ */
+ protected void showCharacter( TextPosition text )
+ {
+ //subclasses can override to provide specific functionality.
+ }
+
+ /**
+ * You should override this method if you want to perform an action when a
+ * string is being shown.
+ *
+ * @param string The string to display.
+ *
+ * @throws IOException If there is an error showing the string
+ */
+ public void showString( byte[] string ) throws IOException
+ {
+ float spaceWidth = 0;
+ float spacing = 0;
+ StringBuffer stringResult = new StringBuffer(string.length);
+
+ float characterDisplacement = 0;
+ float spaceDisplacement = 0;
+ float fontSize = graphicsState.getTextState().getFontSize();
+ float horizontalScaling = graphicsState.getTextState().getHorizontalScalingPercent()/100f;
+ float rise = graphicsState.getTextState().getRise();
+ final float wordSpacing = graphicsState.getTextState().getWordSpacing();
+ final float characterSpacing = graphicsState.getTextState().getCharacterSpacing();
+ float wordSpacingDisplacement = 0;
+
+ PDFont font = graphicsState.getTextState().getFont();
+
+ //This will typically be 1000 but in the case of a type3 font
+ //this might be a different number
+ float glyphSpaceToTextSpaceFactor = 1f/font.getFontMatrix().getValue( 0, 0 );
+ Float averageWidth = (Float)fontToAverageWidths.get( font );
+ if( averageWidth == null )
+ {
+ averageWidth = new Float( font.getAverageFontWidth() );
+ fontToAverageWidths.put( font, averageWidth );
+ }
+
+ Matrix initialMatrix = new Matrix();
+ initialMatrix.setValue(0,0,1);
+ initialMatrix.setValue(0,1,0);
+ initialMatrix.setValue(0,2,0);
+ initialMatrix.setValue(1,0,0);
+ initialMatrix.setValue(1,1,1);
+ initialMatrix.setValue(1,2,0);
+ initialMatrix.setValue(2,0,0);
+ initialMatrix.setValue(2,1,rise);
+ initialMatrix.setValue(2,2,1);
+
+
+ //this
+ int codeLength = 1;
+ Matrix ctm = graphicsState.getCurrentTransformationMatrix();
+
+ //lets see what the space displacement should be
+ spaceDisplacement = (font.getFontWidth( SPACE_BYTES, 0, 1 )/glyphSpaceToTextSpaceFactor);
+ if( spaceDisplacement == 0 )
+ {
+ spaceDisplacement = (averageWidth.floatValue()/glyphSpaceToTextSpaceFactor);
+ //The average space width appears to be higher than necessary
+ //so lets make it a little bit smaller.
+ spaceDisplacement *= .80f;
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Font: Space From Average=" + spaceDisplacement );
+ }
+ }
+ int pageRotation = page.findRotation();
+ Matrix trm = initialMatrix.multiply( textMatrix ).multiply( ctm );
+ float x = trm.getValue(2,0);
+ float y = trm.getValue(2,1);
+ if( pageRotation == 0 )
+ {
+ trm.setValue( 2,1, -y + page.findMediaBox().getHeight() );
+ }
+ else if( pageRotation == 90 )
+ {
+ trm.setValue( 2,0, y );
+ trm.setValue( 2,1, x );
+ }
+ else if( pageRotation == 270 )
+ {
+ trm.setValue( 2,0, -y + page.findMediaBox().getHeight() );
+ trm.setValue( 2,1, x );
+ }
+ for( int i=0; i<string.length; i+=codeLength )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "initialMatrix=" + initialMatrix );
+ log.debug( "textMatrix=" + textMatrix );
+ log.debug( "initialMatrix.multiply( textMatrix )=" + initialMatrix.multiply( textMatrix ) );
+ log.debug( "ctm=" + ctm );
+ log.debug( "trm=" + initialMatrix.multiply( textMatrix ).multiply( ctm ) );
+ }
+ codeLength = 1;
+
+ String c = font.encode( string, i, codeLength );
+
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Character Code=" + string[i] + "='" + c + "'" );
+ }
+ if( c == null && i+1<string.length)
+ {
+ //maybe a multibyte encoding
+ codeLength++;
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Multibyte Character Code=" + string[i] + string[i+1] );
+ }
+ c = font.encode( string, i, codeLength );
+ }
+ stringResult.append( c );
+
+ //todo, handle horizontal displacement
+ characterDisplacement += (font.getFontWidth( string, i, codeLength )/glyphSpaceToTextSpaceFactor);
+
+
+ // PDF Spec - 5.5.2 Word Spacing
+ //
+ // Word spacing works the same was as character spacing, but applies
+ // only to the space character, code 32.
+ //
+ // Note: Word spacing is applied to every occurrence of the single-byte
+ // character code 32 in a string. This can occur when using a simple
+ // font or a composite font that defines code 32 as a single-byte code.
+ // It does not apply to occurrences of the byte value 32 in multiple-byte
+ // codes.
+ //
+ // RDD - My interpretation of this is that only character code 32's that
+ // encode to spaces should have word spacing applied. Cases have been
+ // observed where a font has a space character with a character code
+ // other than 32, and where word spacing (Tw) was used. In these cases,
+ // applying word spacing to either the non-32 space or to the character
+ // code 32 non-space resulted in errors consistent with this interpretation.
+ //
+
+ boolean withCS = false;
+ if( (string[i] == 0x20) && c.equals( " " ) )
+ {
+ spacing += wordSpacing + characterSpacing;
+ withCS = true;
+ }
+ else
+ {
+ spacing += characterSpacing;
+ }
+
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Checking code '" + c + "' font=" + graphicsState.getTextState().getFont() +
+ " Tc=" + characterSpacing +
+ " Tw=" + wordSpacing +
+ " fontSize=" + fontSize +
+ " horizontalScaling=" + horizontalScaling +
+ " totalDisp=" + characterDisplacement +
+ " spacing=" + spacing + "(" + withCS + ")" );
+ }
+ // We want to update the textMatrix using the width, in text space units.
+ //
+
+ }
+
+ //The adjustment will always be zero. The adjustment as shown in the
+ //TJ operator will be handled separately.
+ float adjustment=0;
+ //todo, need to compute the horizontal displacement
+ float ty = 0;
+ float tx = ((characterDisplacement-adjustment/glyphSpaceToTextSpaceFactor)*fontSize + spacing)
+ *horizontalScaling;
+
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "disp=" + characterDisplacement + " adj=" + adjustment +
+ " fSize=" + fontSize + " tx=" + tx );
+ }
+
+ float xScale = trm.getXScale();
+ float yScale = trm.getYScale();
+ float xPos = trm.getXPosition();
+ float yPos = trm.getYPosition();
+ spaceWidth = spaceDisplacement * xScale * fontSize;
+ wordSpacingDisplacement = wordSpacing*xScale * fontSize;
+ Matrix td = new Matrix();
+ td.setValue( 2, 0, tx );
+ td.setValue( 2, 1, ty );
+
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "TRM=" + trm );
+ log.debug( "TextMatrix before " + textMatrix );
+ }
+ float xPosBefore = textMatrix.getXPosition();
+ float yPosBefore = textMatrix.getYPosition();
+ textMatrix = td.multiply( textMatrix );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "TextMatrix after " + textMatrix );
+ }
+ float totalStringDisplacement = 0;
+ if( pageRotation == 0 )
+ {
+ totalStringDisplacement = (textMatrix.getXPosition() - xPosBefore);
+ }
+ else if( pageRotation == 90 )
+ {
+ totalStringDisplacement = (textMatrix.getYPosition() - yPosBefore);
+ }
+ else if( pageRotation == 270 )
+ {
+ totalStringDisplacement = (yPosBefore - textMatrix.getYPosition());
+ }
+ showCharacter(
+ new TextPosition(
+ xPos,
+ yPos,
+ xScale,
+ yScale,
+ totalStringDisplacement,
+ spaceWidth,
+ stringResult.toString(),
+ graphicsState.getTextState().getFont(),
+ graphicsState.getTextState().getFontSize(),
+ wordSpacingDisplacement ));
+ }
+
+ /**
+ * This is used to handle an operation.
+ *
+ * @param operation The operation to perform.
+ * @param arguments The list of arguments.
+ *
+ * @throws IOException If there is an error processing the operation.
+ */
+ public void processOperator( String operation, List arguments ) throws IOException
+ {
+ PDFOperator oper = PDFOperator.getOperator( operation );
+ processOperator( oper, arguments );
+ }
+
+ /**
+ * This is used to handle an operation.
+ *
+ * @param operator The operation to perform.
+ * @param arguments The list of arguments.
+ *
+ * @throws IOException If there is an error processing the operation.
+ */
+ protected void processOperator( PDFOperator operator, List arguments ) throws IOException
+ {
+ String operation = operator.getOperation();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "processOperator( '" + operation + "' )" );
+ }
+ OperatorProcessor processor = (OperatorProcessor)operators.get( operation );
+ if( processor != null )
+ {
+ processor.process( operator, arguments );
+ }
+ }
+
+ /**
+ * @return Returns the colorSpaces.
+ */
+ public Map getColorSpaces()
+ {
+ return ((StreamResources) streamResourcesStack.peek()).colorSpaces;
+ }
+
+ /**
+ * @return Returns the colorSpaces.
+ */
+ public Map getXObjects()
+ {
+ return ((StreamResources) streamResourcesStack.peek()).xobjects;
+ }
+
+ /**
+ * @param value The colorSpaces to set.
+ */
+ public void setColorSpaces(Map value)
+ {
+ ((StreamResources) streamResourcesStack.peek()).colorSpaces = value;
+ }
+ /**
+ * @return Returns the fonts.
+ */
+ public Map getFonts()
+ {
+ return ((StreamResources) streamResourcesStack.peek()).fonts;
+ }
+ /**
+ * @param value The fonts to set.
+ */
+ public void setFonts(Map value)
+ {
+ ((StreamResources) streamResourcesStack.peek()).fonts = value;
+ }
+ /**
+ * @return Returns the graphicsStack.
+ */
+ public Stack getGraphicsStack()
+ {
+ return graphicsStack;
+ }
+ /**
+ * @param value The graphicsStack to set.
+ */
+ public void setGraphicsStack(Stack value)
+ {
+ graphicsStack = value;
+ }
+ /**
+ * @return Returns the graphicsState.
+ */
+ public PDGraphicsState getGraphicsState()
+ {
+ return graphicsState;
+ }
+ /**
+ * @param value The graphicsState to set.
+ */
+ public void setGraphicsState(PDGraphicsState value)
+ {
+ graphicsState = value;
+ }
+ /**
+ * @return Returns the graphicsStates.
+ */
+ public Map getGraphicsStates()
+ {
+ return ((StreamResources) streamResourcesStack.peek()).graphicsStates;
+ }
+ /**
+ * @param value The graphicsStates to set.
+ */
+ public void setGraphicsStates(Map value)
+ {
+ ((StreamResources) streamResourcesStack.peek()).graphicsStates = value;
+ }
+ /**
+ * @return Returns the textLineMatrix.
+ */
+ public Matrix getTextLineMatrix()
+ {
+ return textLineMatrix;
+ }
+ /**
+ * @param value The textLineMatrix to set.
+ */
+ public void setTextLineMatrix(Matrix value)
+ {
+ textLineMatrix = value;
+ }
+ /**
+ * @return Returns the textMatrix.
+ */
+ public Matrix getTextMatrix()
+ {
+ return textMatrix;
+ }
+ /**
+ * @param value The textMatrix to set.
+ */
+ public void setTextMatrix(Matrix value)
+ {
+ textMatrix = value;
+ }
+ /**
+ * @return Returns the resources.
+ */
+ public PDResources getResources()
+ {
+ return ((StreamResources) streamResourcesStack.peek()).resources;
+ }
+
+ /**
+ * Get the current page that is being processed.
+ *
+ * @return The page being processed.
+ */
+ public PDPage getCurrentPage()
+ {
+ return page;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/PDFText2HTML.java b/src/main/java/org/pdfbox/util/PDFText2HTML.java
new file mode 100644
index 0000000..0409eaa
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/PDFText2HTML.java
@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Wrap stripped text in simple HTML, trying to form HTML paragraphs.
+ * Paragraphs broken by pages, columns, or figures are not mended.
+ *
+ *
+ * @author jjb - http://www.johnjbarton.com
+ * @version $Revision: 1.1 $
+ *
+ */
+public class PDFText2HTML extends PDFTextStripper
+{
+ private static Logger log = Logger.getLogger(PDFText2HTML.class);
+ private static final int INITIAL_PDF_TO_HTML_BYTES = 8192;
+
+ private TextPosition beginTitle;
+ private TextPosition afterEndTitle;
+ private String titleGuess;
+ private boolean suppressParagraphs;
+ private boolean onFirstPage = true;
+
+ /**
+ * Constructor.
+ *
+ * @throws IOException If there is an error during initialization.
+ */
+ public PDFText2HTML() throws IOException
+ {
+ titleGuess = "";
+ beginTitle = null;
+ afterEndTitle = null;
+ suppressParagraphs = false;
+ }
+
+ /**
+ * Write the header to the output document.
+ *
+ * @throws IOException If there is a problem writing out the header to the document.
+ */
+ protected void writeHeader() throws IOException
+ {
+ StringBuffer buf = new StringBuffer(INITIAL_PDF_TO_HTML_BYTES);
+ buf.append("<html><head>");
+ buf.append("<title>");
+ buf.append(getTitleGuess());
+ buf.append("</title>");
+ buf.append("</head>");
+ buf.append("<body>\n");
+ getOutput().write(buf.toString());
+ }
+
+ /**
+ * The guess to the document title.
+ *
+ * @return A string that is the title of this document.
+ */
+ protected String getTitleGuess()
+ {
+ return titleGuess;
+ }
+
+ /**
+ * @see PDFTextStripper#flushText
+ */
+ protected void flushText() throws IOException
+ {
+ Iterator textIter = getCharactersByArticle().iterator();
+
+ if (onFirstPage)
+ {
+ guessTitle(textIter);
+ writeHeader();
+ onFirstPage = false;
+ }
+ super.flushText();
+ }
+
+ /**
+ * @see PDFTextStripper#endDocument( PDDocument )
+ */
+ public void endDocument(PDDocument pdf) throws IOException
+ {
+ output.write("</body></html>");
+ }
+
+ /**
+ * This method will attempt to guess the title of the document.
+ *
+ * @param textIter The characters on the first page.
+ * @return The text position that is guessed to be the title.
+ */
+ protected TextPosition guessTitle(Iterator textIter)
+ {
+ float lastFontSize = -1.0f;
+ int stringsInFont = 0;
+ StringBuffer titleText = new StringBuffer();
+ while (textIter.hasNext())
+ {
+ Iterator textByArticle = ((List)textIter.next()).iterator();
+ while( textByArticle.hasNext() )
+ {
+ TextPosition position = (TextPosition) textByArticle.next();
+ float currentFontSize = position.getFontSize();
+ if (currentFontSize != lastFontSize)
+ {
+ if (beginTitle != null)
+ { // font change in candidate title.
+ if (stringsInFont == 0)
+ {
+ beginTitle = null; // false alarm
+ titleText.setLength(0);
+ }
+ else
+ {
+ // had a significant font with some words: call it a title
+ titleGuess = titleText.toString();
+ log.debug("Title candidate =" + titleGuess);
+ afterEndTitle = position;
+ return beginTitle;
+ }
+ }
+ else
+ { // font change and begin == null
+ if (currentFontSize > 13.0f)
+ { // most body text is 12pt max I guess
+ beginTitle = position;
+ }
+ }
+
+ lastFontSize = currentFontSize;
+ stringsInFont = 0;
+ }
+ stringsInFont++;
+ if (beginTitle != null)
+ {
+ titleText.append(position.getCharacter()+" ");
+ }
+ }
+ }
+ return beginTitle; // null
+ }
+
+ /**
+ * Write out the paragraph separator.
+ *
+ * @throws IOException If there is an error writing to the stream.
+ */
+ protected void startParagraph() throws IOException
+ {
+ if (! suppressParagraphs)
+ {
+ getOutput().write("<p>");
+ }
+ }
+ /**
+ * Write out the paragraph separator.
+ *
+ * @throws IOException If there is an error writing to the stream.
+ */
+ protected void endParagraph() throws IOException
+ {
+ if (! suppressParagraphs)
+ {
+ getOutput().write("</p>");
+ }
+ }
+
+ /**
+ * @see PDFTextStripper#writeCharacters( TextPosition )
+ */
+ protected void writeCharacters(TextPosition position ) throws IOException
+ {
+ if (position == beginTitle)
+ {
+ output.write("<H1>");
+ suppressParagraphs = true;
+ }
+ if (position == afterEndTitle)
+ {
+ output.write("</H1>"); // end title and start first paragraph
+ suppressParagraphs = false;
+ }
+
+ String chars = position.getCharacter();
+
+ for (int i = 0; i < chars.length(); i++)
+ {
+ char c = chars.charAt(i);
+ if ((c < 32) || (c > 126))
+ {
+ int charAsInt = c;
+ output.write("&#" + charAsInt + ";");
+ }
+ else
+ {
+ switch (c)
+ {
+ case 34:
+ output.write("&quot;");
+ break;
+ case 38:
+ output.write("&amp;");
+ break;
+ case 60:
+ output.write("&lt;");
+ break;
+ case 62:
+ output.write("&gt;");
+ break;
+ default:
+ output.write(c);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return Returns the suppressParagraphs.
+ */
+ public boolean isSuppressParagraphs()
+ {
+ return suppressParagraphs;
+ }
+ /**
+ * @param shouldSuppressParagraphs The suppressParagraphs to set.
+ */
+ public void setSuppressParagraphs(boolean shouldSuppressParagraphs)
+ {
+ this.suppressParagraphs = shouldSuppressParagraphs;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/PDFTextStripper.java b/src/main/java/org/pdfbox/util/PDFTextStripper.java
new file mode 100644
index 0000000..56e80cc
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/PDFTextStripper.java
@@ -0,0 +1,1033 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSStream;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+import org.pdfbox.pdmodel.common.PDStream;
+
+import org.pdfbox.pdmodel.encryption.PDEncryptionDictionary;
+import org.pdfbox.pdmodel.encryption.PDStandardEncryption;
+import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
+import org.pdfbox.pdmodel.interactive.pagenavigation.PDThreadBead;
+
+import org.pdfbox.exceptions.CryptographyException;
+import org.pdfbox.exceptions.InvalidPasswordException;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * This class will take a pdf document and strip out all of the text and ignore the
+ * formatting and such.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.62 $
+ */
+public class PDFTextStripper extends PDFStreamEngine
+{
+ private static Logger log = Logger.getLogger(PDFTextStripper.class);
+
+ private int currentPageNo = 0;
+ private int startPage = 1;
+ private int endPage = Integer.MAX_VALUE;
+ private PDOutlineItem startBookmark = null;
+ private int startBookmarkPageNumber = -1;
+ private PDOutlineItem endBookmark = null;
+ private int endBookmarkPageNumber = -1;
+ private PDDocument document;
+ private boolean suppressDuplicateOverlappingText = true;
+ private boolean shouldSeparateByBeads = true;
+ private boolean sortByPosition = false;
+
+ private List pageArticles = null;
+ /**
+ * The charactersByArticle is used to extract text by article divisions. For example
+ * a PDF that has two columns like a newspaper, we want to extract the first column and
+ * then the second column. In this example the PDF would have 2 beads(or articles), one for
+ * each column. The size of the charactersByArticle would be 5, because not all text on the
+ * screen will fall into one of the articles. The five divisions are shown below
+ *
+ * Text before first article
+ * first article text
+ * text between first article and second article
+ * second article text
+ * text after second article
+ *
+ * Most PDFs won't have any beads, so charactersByArticle will contain a single entry.
+ */
+ protected Vector charactersByArticle = new Vector();
+
+ private Map characterListMapping = new HashMap();
+
+ private String lineSeparator = System.getProperty("line.separator");
+ private String pageSeparator = System.getProperty("line.separator");
+ private String wordSeparator = " ";
+
+ /**
+ * The stream to write the output to.
+ */
+ protected Writer output;
+
+ /**
+ * Instantiate a new PDFTextStripper object. This object will load properties from
+ * Resources/PDFTextStripper.properties.
+ * @throws IOException If there is an error loading the properties.
+ */
+ public PDFTextStripper() throws IOException
+ {
+ super( ResourceLoader.loadProperties( "Resources/PDFTextStripper.properties" ) );
+ }
+
+ /**
+ * This will return the text of a document. See writeText. <br />
+ * NOTE: The document must not be encrypted when coming into this method.
+ *
+ * @param doc The document to get the text from.
+ *
+ * @return The text of the PDF document.
+ *
+ * @throws IOException if the doc state is invalid or it is encrypted.
+ */
+ public String getText( PDDocument doc ) throws IOException
+ {
+ StringWriter outputStream = new StringWriter();
+ writeText( doc, outputStream );
+ return outputStream.toString();
+ }
+
+ /**
+ * @deprecated
+ * @see PDFTextStripper#getText( PDDocument )
+ * @param doc The document to extract the text from.
+ * @return The document text.
+ * @throws IOException If there is an error extracting the text.
+ */
+ public String getText( COSDocument doc ) throws IOException
+ {
+ return getText( new PDDocument( doc ) );
+ }
+
+ /**
+ * @deprecated
+ * @see PDFTextStripper#writeText( PDDocument, Writer )
+ * @param doc The document to extract the text.
+ * @param outputStream The stream to write the text to.
+ * @throws IOException If there is an error extracting the text.
+ */
+ public void writeText( COSDocument doc, Writer outputStream ) throws IOException
+ {
+ writeText( new PDDocument( doc ), outputStream );
+ }
+
+ /**
+ * This will take a PDDocument and write the text of that document to the print writer.
+ *
+ * @param doc The document to get the data from.
+ * @param outputStream The location to put the text.
+ *
+ * @throws IOException If the doc is in an invalid state.
+ */
+ public void writeText( PDDocument doc, Writer outputStream ) throws IOException
+ {
+
+ PDEncryptionDictionary encDictionary = doc.getEncryptionDictionary();
+
+ //only care about standard encryption and if it was decrypted with the
+ //user password
+ if( encDictionary instanceof PDStandardEncryption &&
+ !doc.wasDecryptedWithOwnerPassword() )
+ {
+ PDStandardEncryption stdEncryption = (PDStandardEncryption)encDictionary;
+ if( !stdEncryption.canExtractContent() )
+ {
+ throw new IOException( "You do not have permission to extract text" );
+ }
+ }
+ currentPageNo = 0;
+ document = doc;
+ output = outputStream;
+ startDocument(document);
+
+ if( document.isEncrypted() )
+ {
+ // We are expecting non-encrypted documents here, but it is common
+ // for users to pass in a document that is encrypted with an empty
+ // password (such a document appears to not be encrypted by
+ // someone viewing the document, thus the confusion). We will
+ // attempt to decrypt with the empty password to handle this case.
+ //
+ log.debug("Document is encrypted, decrypting with empty password");
+ try
+ {
+ document.decrypt("");
+ }
+ catch (CryptographyException e)
+ {
+ throw new IOException("Error decrypting document, details: " + e.getMessage());
+ }
+ catch (InvalidPasswordException e)
+ {
+ throw new IOException("Error: document is encrypted");
+ }
+ }
+
+ processPages( document.getDocumentCatalog().getAllPages() );
+ endDocument(document);
+ }
+
+ /**
+ * This will process all of the pages and the text that is in them.
+ *
+ * @param pages The pages object in the document.
+ *
+ * @throws IOException If there is an error parsing the text.
+ */
+ protected void processPages( List pages ) throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "processPages( " + pages + " )" );
+ }
+
+ if( startBookmark != null )
+ {
+ startBookmarkPageNumber = getPageNumber( startBookmark, pages );
+ }
+
+ if( endBookmark != null )
+ {
+ endBookmarkPageNumber = getPageNumber( endBookmark, pages );
+ }
+
+ if( startBookmarkPageNumber == -1 && startBookmark != null &&
+ endBookmarkPageNumber == -1 && endBookmark != null &&
+ startBookmark.getCOSObject() == endBookmark.getCOSObject() )
+ {
+ //this is a special case where both the start and end bookmark
+ //are the same but point to nothing. In this case
+ //we will not extract any text.
+ startBookmarkPageNumber = 0;
+ endBookmarkPageNumber = 0;
+ }
+
+
+ Iterator pageIter = pages.iterator();
+ while( pageIter.hasNext() )
+ {
+ PDPage nextPage = (PDPage)pageIter.next();
+ PDStream contentStream = nextPage.getContents();
+ if( contentStream != null )
+ {
+ COSStream contents = contentStream.getStream();
+ processPage( nextPage, contents );
+ }
+ }
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "processPages() end" );
+ }
+ }
+
+ private int getPageNumber( PDOutlineItem bookmark, List allPages ) throws IOException
+ {
+ int pageNumber = -1;
+ PDPage page = bookmark.findDestinationPage( document );
+ if( page != null )
+ {
+ pageNumber = allPages.indexOf( page )+1;//use one based indexing
+ }
+ return pageNumber;
+ }
+
+ /**
+ * This method is available for subclasses of this class. It will be called before processing
+ * of the document start.
+ *
+ * @param pdf The PDF document that is being processed.
+ * @throws IOException If an IO error occurs.
+ */
+ protected void startDocument(PDDocument pdf) throws IOException
+ {
+ // no default implementation, but available for subclasses
+ }
+
+ /**
+ * This method is available for subclasses of this class. It will be called after processing
+ * of the document finishes.
+ *
+ * @param pdf The PDF document that is being processed.
+ * @throws IOException If an IO error occurs.
+ */
+ protected void endDocument(PDDocument pdf ) throws IOException
+ {
+ // no default implementation, but available for subclasses
+ }
+
+ /**
+ * This will process the contents of a page.
+ *
+ * @param page The page to process.
+ * @param content The contents of the page.
+ *
+ * @throws IOException If there is an error processing the page.
+ */
+ protected void processPage( PDPage page, COSStream content ) throws IOException
+ {
+ long start = System.currentTimeMillis();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "processPage( " + page + ", " + content + " )" );
+ }
+ currentPageNo++;
+ if( currentPageNo >= startPage && currentPageNo <= endPage &&
+ (startBookmarkPageNumber == -1 || currentPageNo >= startBookmarkPageNumber ) &&
+ (endBookmarkPageNumber == -1 || currentPageNo <= endBookmarkPageNumber ))
+ {
+ startPage( page );
+ pageArticles = page.getThreadBeads();
+ int numberOfArticleSections = 1 + pageArticles.size() * 2;
+ if( !shouldSeparateByBeads )
+ {
+ numberOfArticleSections = 1;
+ }
+ int originalSize = charactersByArticle.size();
+ charactersByArticle.setSize( numberOfArticleSections );
+ for( int i=0; i<numberOfArticleSections; i++ )
+ {
+ if( numberOfArticleSections < originalSize )
+ {
+ ((List)charactersByArticle.get( i )).clear();
+ }
+ else
+ {
+ charactersByArticle.set( i, new ArrayList() );
+ }
+ }
+
+ characterListMapping.clear();
+ long startProcess = System.currentTimeMillis();
+ processStream( page, page.findResources(), content );
+ long stopProcess = System.currentTimeMillis();
+ long startFlush = System.currentTimeMillis();
+ flushText();
+ long stopFlush = System.currentTimeMillis();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "processStream time=" + (stopProcess-startProcess) );
+ log.debug( "flushText time=" + (stopFlush-startFlush) );
+ }
+ endPage( page );
+ }
+ long stop = System.currentTimeMillis();
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "processPage() end time=" + (stop-start) );
+ }
+
+ }
+
+ /**
+ * Start a new paragraph. Default implementation is to do nothing. Subclasses
+ * may provide additional information.
+ *
+ * @throws IOException If there is any error writing to the stream.
+ */
+ protected void startParagraph() throws IOException
+ {
+ //default is to do nothing.
+ }
+
+ /**
+ * End a paragraph. Default implementation is to do nothing. Subclasses
+ * may provide additional information.
+ *
+ * @throws IOException If there is any error writing to the stream.
+ */
+ protected void endParagraph() throws IOException
+ {
+ //default is to do nothing
+ }
+
+ /**
+ * Start a new page. Default implementation is to do nothing. Subclasses
+ * may provide additional information.
+ *
+ * @param page The page we are about to process.
+ *
+ * @throws IOException If there is any error writing to the stream.
+ */
+ protected void startPage( PDPage page ) throws IOException
+ {
+ //default is to do nothing.
+ }
+
+ /**
+ * End a page. Default implementation is to do nothing. Subclasses
+ * may provide additional information.
+ *
+ * @param page The page we are about to process.
+ *
+ * @throws IOException If there is any error writing to the stream.
+ */
+ protected void endPage( PDPage page ) throws IOException
+ {
+ //default is to do nothing
+ }
+
+ /**
+ * This will print the text to the output stream.
+ *
+ * @throws IOException If there is an error writing the text.
+ */
+ protected void flushText() throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "flushText() start" );
+ }
+ float currentY = -1;
+ float lastBaselineFontSize = -1;
+ if( log.isDebugEnabled() )
+ {
+ log.debug("<Starting text object list>");
+ }
+ float endOfLastTextX = -1;
+ float startOfNextWordX = -1;
+ float lastWordSpacing = -1;
+ TextPosition lastProcessedCharacter = null;
+
+ for( int i=0; i<charactersByArticle.size(); i++)
+ {
+ startParagraph();
+ List textList = (List)charactersByArticle.get( i );
+ if( sortByPosition )
+ {
+ TextPositionComparator comparator = new TextPositionComparator( getCurrentPage() );
+ Collections.sort( textList, comparator );
+ }
+ Iterator textIter = textList.iterator();
+ while( textIter.hasNext() )
+ {
+ TextPosition position = (TextPosition)textIter.next();
+ String characterValue = position.getCharacter();
+
+ //wordSpacing = position.getWordSpacing();
+ float wordSpacing = 0;
+
+ if( wordSpacing == 0 )
+ {
+ //try to get width of a space character
+ wordSpacing = position.getWidthOfSpace();
+ //if still zero fall back to getting the width of the current
+ //character
+ if( wordSpacing == 0 )
+ {
+ wordSpacing = position.getWidth();
+ }
+ }
+
+
+ // RDD - We add a conservative approximation for space determination.
+ // basically if there is a blank area between two characters that is
+ //equal to some percentage of the word spacing then that will be the
+ //start of the next word
+ if( lastWordSpacing <= 0 )
+ {
+ startOfNextWordX = endOfLastTextX + (wordSpacing* 0.50f);
+ }
+ else
+ {
+ startOfNextWordX = endOfLastTextX + (((wordSpacing+lastWordSpacing)/2f)* 0.50f);
+ }
+
+ lastWordSpacing = wordSpacing;
+
+ // RDD - We will suppress text that is very close to the current line
+ // and which overwrites previously rendered text on this line.
+ // This is done specifically to handle a reasonably common situation
+ // where an application (MS Word, in the case of my examples) renders
+ // text four times at small (1 point) offsets in order to accomplish
+ // bold printing. You would not want to do this step if you were
+ // going to render the TextPosition objects graphically.
+ //
+ /*if ((endOfLastTextX != -1 && position.getX() < endOfLastTextX) &&
+ (currentY != -1 && Math.abs(position.getY() - currentY) < 1))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Suppressing text overwrite" +
+ " x: " + position.getX() +
+ " endOfLastTextX: " + endOfLastTextX +
+ " string: " + position.getCharacter());
+ }
+ continue;
+ }*/
+
+ // RDD - Here we determine whether this text object is on the current
+ // line. We use the lastBaselineFontSize to handle the superscript
+ // case, and the size of the current font to handle the subscript case.
+ // Text must overlap with the last rendered baseline text by at least
+ // a small amount in order to be considered as being on the same line.
+ //
+ int verticalScaling = 1;
+ if( lastBaselineFontSize < 0 || position.getFontSize() < 0 )
+ {
+ verticalScaling = -1;
+ }
+ if (currentY != -1 &&
+ ((position.getY() < (currentY - (lastBaselineFontSize * 0.9f * verticalScaling))) ||
+ (position.getY() > (currentY + (position.getFontSize() * 0.9f * verticalScaling)))))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("<newline currentY=" + currentY + ", y=" + position.getY() +
+ " fs=" + position.getFontSize()+ " lb fs=" + lastBaselineFontSize + ">");
+ }
+ output.write(lineSeparator);
+ endOfLastTextX = -1;
+ startOfNextWordX = -1;
+ currentY = -1;
+ lastBaselineFontSize = -1;
+ }
+
+ if (startOfNextWordX != -1 && startOfNextWordX < position.getX() &&
+ lastProcessedCharacter != null &&
+ //only bother adding a space if the last character was not a space
+ lastProcessedCharacter.getCharacter() != null &&
+ !lastProcessedCharacter.getCharacter().endsWith( " " ) )
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("<space startOfNextWordX=" + startOfNextWordX + ", x=" + position.getX() + ">");
+ }
+ output.write( wordSeparator );
+ }
+
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("flushText" +
+ " x=" + position.getX() +
+ " y=" + position.getY() +
+ " xScale=" + position.getXScale() +
+ " yScale=" + position.getYScale() +
+ " width=" + position.getWidth() +
+ " currentY=" + currentY +
+ " endOfLastTextX=" + endOfLastTextX +
+ " startOfNextWordX=" + startOfNextWordX +
+ " fontSize=" + position.getFontSize() +
+ " wordSpacing=" + wordSpacing +
+ " string=\"" + characterValue + "\"");
+ }
+
+ if (currentY == -1)
+ {
+ currentY = position.getY();
+ }
+
+ if (currentY == position.getY())
+ {
+ lastBaselineFontSize = position.getFontSize();
+ }
+
+ // RDD - endX is what PDF considers to be the x coordinate of the
+ // end position of the text. We use it in computing our metrics below.
+ //
+ endOfLastTextX = position.getX() + position.getWidth();
+
+
+ if (characterValue != null)
+ {
+ output.write(characterValue);
+ }
+ else
+ {
+ log.debug( "Position.getString() is null so not writing anything" );
+ }
+ lastProcessedCharacter = position;
+ }
+ endParagraph();
+ }
+
+
+ // RDD - newline at end of flush - required for end of page (so that the top
+ // of the next page starts on its own line.
+ //
+ if( log.isDebugEnabled() )
+ {
+ log.debug("<newline endOfFlush=\"true\">");
+ }
+ output.write(pageSeparator);
+
+ output.flush();
+ }
+
+ /**
+ * Write the string to the output stream.
+ *
+ * @param text The text to write to the stream.
+ * @throws IOException If there is an error when writing the text.
+ */
+ protected void writeCharacters( TextPosition text ) throws IOException
+ {
+ output.write( text.getCharacter() );
+ }
+
+ /**
+ * This will determine of two floating point numbers are within a specified variance.
+ *
+ * @param first The first number to compare to.
+ * @param second The second number to compare to.
+ * @param variance The allowed variance.
+ */
+ private boolean within( float first, float second, float variance )
+ {
+ return second > first - variance && second < first + variance;
+ }
+
+ /**
+ * This will show add a character to the list of characters to be printed to
+ * the text file.
+ *
+ * @param text The description of the character to display.
+ */
+ protected void showCharacter( TextPosition text )
+ {
+ boolean showCharacter = true;
+ if( suppressDuplicateOverlappingText )
+ {
+ showCharacter = false;
+ String textCharacter = text.getCharacter();
+ float textX = text.getX();
+ float textY = text.getY();
+ List sameTextCharacters = (List)characterListMapping.get( textCharacter );
+ if( sameTextCharacters == null )
+ {
+ sameTextCharacters = new ArrayList();
+ characterListMapping.put( textCharacter, sameTextCharacters );
+ }
+
+ // RDD - Here we compute the value that represents the end of the rendered
+ // text. This value is used to determine whether subsequent text rendered
+ // on the same line overwrites the current text.
+ //
+ // We subtract any positive padding to handle cases where extreme amounts
+ // of padding are applied, then backed off (not sure why this is done, but there
+ // are cases where the padding is on the order of 10x the character width, and
+ // the TJ just backs up to compensate after each character). Also, we subtract
+ // an amount to allow for kerning (a percentage of the width of the last
+ // character).
+ //
+ boolean suppressCharacter = false;
+ float tolerance = (text.getWidth()/textCharacter.length())/3.0f;
+ for( int i=0; i<sameTextCharacters.size() && textCharacter != null; i++ )
+ {
+ TextPosition character = (TextPosition)sameTextCharacters.get( i );
+ String charCharacter = character.getCharacter();
+ float charX = character.getX();
+ float charY = character.getY();
+ //only want to suppress
+
+ if( charCharacter != null &&
+ //charCharacter.equals( textCharacter ) &&
+ within( charX, textX, tolerance ) &&
+ within( charY,
+ textY,
+ tolerance ) )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug("suppressText" +
+ " x=" + charX +
+ " y=" + charY +
+ " width=" + character.getWidth() +
+ " fontSize=" + character.getFontSize() +
+ " string=\"" + charCharacter + "\"");
+ }
+ suppressCharacter = true;
+ }
+ }
+ if( !suppressCharacter )
+ {
+ sameTextCharacters.add( text );
+ showCharacter = true;
+ }
+ }
+
+ if( showCharacter )
+ {
+ //if we are showing the character then we need to determine which
+ //article it belongs to.
+ int foundArticleDivisionIndex = -1;
+ int notFoundButFirstLeftAndAboveArticleDivisionIndex = -1;
+ int notFoundButFirstLeftArticleDivisionIndex = -1;
+ int notFoundButFirstAboveArticleDivisionIndex = -1;
+ float x = text.getX();
+ float y = text.getY();
+ if( shouldSeparateByBeads )
+ {
+ for( int i=0; i<pageArticles.size() && foundArticleDivisionIndex == -1; i++ )
+ {
+ PDThreadBead bead = (PDThreadBead)pageArticles.get( i );
+ if( bead != null )
+ {
+ PDRectangle rect = bead.getRectangle();
+ if( rect.contains( x, y ) )
+ {
+ foundArticleDivisionIndex = i*2+1;
+ }
+ else if( (x < rect.getLowerLeftX() ||
+ y < rect.getUpperRightY()) &&
+ notFoundButFirstLeftAndAboveArticleDivisionIndex == -1)
+ {
+ notFoundButFirstLeftAndAboveArticleDivisionIndex = i*2;
+ }
+ else if( x < rect.getLowerLeftX() &&
+ notFoundButFirstLeftArticleDivisionIndex == -1)
+ {
+ notFoundButFirstLeftArticleDivisionIndex = i*2;
+ }
+ else if( y < rect.getUpperRightY() &&
+ notFoundButFirstAboveArticleDivisionIndex == -1)
+ {
+ notFoundButFirstAboveArticleDivisionIndex = i*2;
+ }
+ }
+ else
+ {
+ foundArticleDivisionIndex = 0;
+ }
+ }
+ }
+ else
+ {
+ foundArticleDivisionIndex = 0;
+ }
+ int articleDivisionIndex = -1;
+ if( foundArticleDivisionIndex != -1 )
+ {
+ articleDivisionIndex = foundArticleDivisionIndex;
+ }
+ else if( notFoundButFirstLeftAndAboveArticleDivisionIndex != -1 )
+ {
+ articleDivisionIndex = notFoundButFirstLeftAndAboveArticleDivisionIndex;
+ }
+ else if( notFoundButFirstLeftArticleDivisionIndex != -1 )
+ {
+ articleDivisionIndex = notFoundButFirstLeftArticleDivisionIndex;
+ }
+ else if( notFoundButFirstAboveArticleDivisionIndex != -1 )
+ {
+ articleDivisionIndex = notFoundButFirstAboveArticleDivisionIndex;
+ }
+ else
+ {
+ articleDivisionIndex = charactersByArticle.size()-1;
+ }
+ List textList = (List) charactersByArticle.get( articleDivisionIndex );
+ textList.add( text );
+ }
+ }
+
+ /**
+ * This is the page that the text extraction will start on. The pages start
+ * at page 1. For example in a 5 page PDF document, if the start page is 1
+ * then all pages will be extracted. If the start page is 4 then pages 4 and 5
+ * will be extracted. The default value is 1.
+ *
+ * @return Value of property startPage.
+ */
+ public int getStartPage()
+ {
+ return startPage;
+ }
+
+ /**
+ * This will set the first page to be extracted by this class.
+ *
+ * @param startPageValue New value of property startPage.
+ */
+ public void setStartPage(int startPageValue)
+ {
+ startPage = startPageValue;
+ }
+
+ /**
+ * This will get the last page that will be extracted. This is inclusive,
+ * for example if a 5 page PDF an endPage value of 5 would extract the
+ * entire document, an end page of 2 would extract pages 1 and 2. This defaults
+ * to Integer.MAX_VALUE such that all pages of the pdf will be extracted.
+ *
+ * @return Value of property endPage.
+ */
+ public int getEndPage()
+ {
+ return endPage;
+ }
+
+ /**
+ * This will set the last page to be extracted by this class.
+ *
+ * @param endPageValue New value of property endPage.
+ */
+ public void setEndPage(int endPageValue)
+ {
+ endPage = endPageValue;
+ }
+
+ /**
+ * Set the desired line separator for output text. The line.separator
+ * system property is used if the line separator preference is not set
+ * explicitly using this method.
+ *
+ * @param separator The desired line separator string.
+ */
+ public void setLineSeparator(String separator)
+ {
+ lineSeparator = separator;
+ }
+
+ /**
+ * This will get the line separator.
+ *
+ * @return The desired line separator string.
+ */
+ public String getLineSeparator()
+ {
+ return lineSeparator;
+ }
+
+ /**
+ * Set the desired page separator for output text. The line.separator
+ * system property is used if the page separator preference is not set
+ * explicitly using this method.
+ *
+ * @param separator The desired page separator string.
+ */
+ public void setPageSeparator(String separator)
+ {
+ pageSeparator = separator;
+ }
+
+ /**
+ * This will get the word separator.
+ *
+ * @return The desired word separator string.
+ */
+ public String getWordSeparator()
+ {
+ return wordSeparator;
+ }
+
+ /**
+ * Set the desired word separator for output text. The PDFBox text extraction
+ * algorithm will output a space character if there is enough space between
+ * two words. By default a space character is used. If you need and accurate
+ * count of characters that are found in a PDF document then you might want to
+ * set the word separator to the empty string.
+ *
+ * @param separator The desired page separator string.
+ */
+ public void setWordSeparator(String separator)
+ {
+ wordSeparator = separator;
+ }
+
+ /**
+ * This will get the page separator.
+ *
+ * @return The page separator string.
+ */
+ public String getPageSeparator()
+ {
+ return pageSeparator;
+ }
+ /**
+ * @return Returns the suppressDuplicateOverlappingText.
+ */
+ public boolean shouldSuppressDuplicateOverlappingText()
+ {
+ return suppressDuplicateOverlappingText;
+ }
+
+ /**
+ * Get the current page number that is being processed.
+ *
+ * @return A 1 based number representing the current page.
+ */
+ protected int getCurrentPageNo()
+ {
+ return currentPageNo;
+ }
+
+ /**
+ * The output stream that is being written to.
+ *
+ * @return The stream that output is being written to.
+ */
+ protected Writer getOutput()
+ {
+ return output;
+ }
+
+ /**
+ * Character strings are grouped by articles. It is quite common that there
+ * will only be a single article. This returns a List that contains List objects,
+ * the inner lists will contain TextPosition objects.
+ *
+ * @return A double List of TextPositions for all text strings on the page.
+ */
+ protected List getCharactersByArticle()
+ {
+ return charactersByArticle;
+ }
+
+ /**
+ * By default the text stripper will attempt to remove text that overlapps each other.
+ * Word paints the same character several times in order to make it look bold. By setting
+ * this to false all text will be extracted, which means that certain sections will be
+ * duplicated, but better performance will be noticed.
+ *
+ * @param suppressDuplicateOverlappingTextValue The suppressDuplicateOverlappingText to set.
+ */
+ public void setSuppressDuplicateOverlappingText(
+ boolean suppressDuplicateOverlappingTextValue)
+ {
+ this.suppressDuplicateOverlappingText = suppressDuplicateOverlappingTextValue;
+ }
+
+ /**
+ * This will tell if the text stripper should separate by beads.
+ *
+ * @return If the text will be grouped by beads.
+ */
+ public boolean shouldSeparateByBeads()
+ {
+ return shouldSeparateByBeads;
+ }
+
+ /**
+ * Set if the text stripper should group the text output by a list of beads. The default value is true!
+ *
+ * @param aShouldSeparateByBeads The new grouping of beads.
+ */
+ public void setShouldSeparateByBeads(boolean aShouldSeparateByBeads)
+ {
+ this.shouldSeparateByBeads = aShouldSeparateByBeads;
+ }
+
+ /**
+ * Get the bookmark where text extraction should end, inclusive. Default is null.
+ *
+ * @return The ending bookmark.
+ */
+ public PDOutlineItem getEndBookmark()
+ {
+ return endBookmark;
+ }
+
+ /**
+ * Set the bookmark where the text extraction should stop.
+ *
+ * @param aEndBookmark The ending bookmark.
+ */
+ public void setEndBookmark(PDOutlineItem aEndBookmark)
+ {
+ endBookmark = aEndBookmark;
+ }
+
+ /**
+ * Get the bookmark where text extraction should start, inclusive. Default is null.
+ *
+ * @return The starting bookmark.
+ */
+ public PDOutlineItem getStartBookmark()
+ {
+ return startBookmark;
+ }
+
+ /**
+ * Set the bookmark where text extraction should start, inclusive.
+ *
+ * @param aStartBookmark The starting bookmark.
+ */
+ public void setStartBookmark(PDOutlineItem aStartBookmark)
+ {
+ startBookmark = aStartBookmark;
+ }
+
+ /**
+ * This will tell if the text stripper should sort the text tokens
+ * before writing to the stream.
+ *
+ * @return true If the text tokens will be sorted before being written.
+ */
+ public boolean shouldSortByPosition()
+ {
+ return sortByPosition;
+ }
+
+ /**
+ * The order of the text tokens in a PDF file may not be in the same
+ * as they appear visually on the screen. For example, a PDF writer may
+ * write out all text by font, so all bold or larger text, then make a second
+ * pass and write out the normal text.<br/>
+ * The default is to <b>not</b> sort by position.<br/>
+ * <br/>
+ * A PDF writer could choose to write each character in a different order. By
+ * default PDFBox does <b>not</b> sort the text tokens before processing them due to
+ * performance reasons.
+ *
+ * @param newSortByPosition Tell PDFBox to sort the text positions.
+ */
+ public void setSortByPosition(boolean newSortByPosition)
+ {
+ sortByPosition = newSortByPosition;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/PDFTextStripperByArea.java b/src/main/java/org/pdfbox/util/PDFTextStripperByArea.java
new file mode 100644
index 0000000..91c76a5
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/PDFTextStripperByArea.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * This will extract text from a specified region in the PDF.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.3 $
+ */
+public class PDFTextStripperByArea extends PDFTextStripper
+{
+ private List regions = new ArrayList();
+ private Map regionArea = new HashMap();
+ private Map regionCharacterList = new HashMap();
+ private Map regionText = new HashMap();
+
+ /**
+ * Constructor.
+ * @throws IOException If there is an error loading properties.
+ */
+ public PDFTextStripperByArea() throws IOException
+ {
+ super();
+ }
+
+ /**
+ * Add a new region to group text by.
+ *
+ * @param regionName The name of the region.
+ * @param rect The rectangle area to retrieve the text from.
+ */
+ public void addRegion( String regionName, Rectangle rect )
+ {
+ regions.add( regionName );
+ regionArea.put( regionName, rect );
+ }
+
+ /**
+ * Get the list of regions that have been setup.
+ *
+ * @return A list of java.lang.String objects to identify the region names.
+ */
+ public List getRegions()
+ {
+ return regions;
+ }
+
+ /**
+ * Get the text for the region, this should be called after extractRegions().
+ *
+ * @param regionName The name of the region to get the text from.
+ * @return The text that was identified in that region.
+ */
+ public String getTextForRegion( String regionName )
+ {
+ StringWriter text = (StringWriter)regionText.get( regionName );
+ return text.toString();
+ }
+
+ /**
+ * Process the page to extract the region text.
+ *
+ * @param page The page to extract the regions from.
+ * @throws IOException If there is an error while extracting text.
+ */
+ public void extractRegions( PDPage page ) throws IOException
+ {
+ Iterator regionIter = regions.iterator();
+ while( regionIter.hasNext() )
+ {
+ //reset the stored text for the region so this class
+ //can be reused.
+ String regionName = (String)regionIter.next();
+ Vector regionCharactersByArticle = new Vector();
+ regionCharactersByArticle.add( new ArrayList() );
+ regionCharacterList.put( regionName, regionCharactersByArticle );
+ regionText.put( regionName, new StringWriter() );
+ }
+
+ PDStream contentStream = page.getContents();
+ if( contentStream != null )
+ {
+ COSStream contents = contentStream.getStream();
+ processPage( page, contents );
+ }
+ }
+
+ /**
+ * @see PDFTextStripper#showCharacter(TextPosition)
+ */
+ protected void showCharacter( TextPosition text )
+ {
+ Iterator regionIter = regionArea.keySet().iterator();
+ while( regionIter.hasNext() )
+ {
+ String region = (String)regionIter.next();
+ Rectangle rect = (Rectangle)regionArea.get( region );
+ if( rect.contains( text.getX(), text.getY() ) )
+ {
+ charactersByArticle = (Vector)regionCharacterList.get( region );
+ super.showCharacter( text );
+ }
+ }
+ }
+
+ /**
+ * This will print the text to the output stream.
+ *
+ * @throws IOException If there is an error writing the text.
+ */
+ protected void flushText() throws IOException
+ {
+ Iterator regionIter = regionArea.keySet().iterator();
+ while( regionIter.hasNext() )
+ {
+ String region = (String)regionIter.next();
+ charactersByArticle = (Vector)regionCharacterList.get( region );
+ output = (StringWriter)regionText.get( region );
+ super.flushText();
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/ResourceLoader.java b/src/main/java/org/pdfbox/util/ResourceLoader.java
new file mode 100644
index 0000000..0f80d09
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/ResourceLoader.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ */
+package org.pdfbox.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class will handle loading resource files(AFM/CMAP).
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.6 $
+ */
+public class ResourceLoader
+{
+ private static Logger log = Logger.getLogger( ResourceLoader.class );
+
+ /**
+ * private constructor for utility class.
+ */
+ private ResourceLoader()
+ {
+ //private utility class
+ }
+
+ /**
+ * This will attempt to load the resource given the resource name.
+ *
+ * @param resourceName The resource to try and load.
+ *
+ * @return The resource as a stream or null if it could not be found.
+ *
+ * @throws IOException If there is an error while attempting to load the resource.
+ */
+ public static InputStream loadResource( String resourceName ) throws IOException
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "loadResource( " + resourceName + ")" );
+ }
+
+ ClassLoader loader = ResourceLoader.class.getClassLoader();
+
+ InputStream is = null;
+
+ if( loader != null )
+ {
+ is = loader.getResourceAsStream( resourceName );
+ }
+
+ //see sourceforge bug 863053, this is a fix for a user that
+ //needed to have PDFBox loaded by the bootstrap classloader
+ if( is == null )
+ {
+ loader = ClassLoader.getSystemClassLoader();
+ if( loader != null )
+ {
+ is = loader.getResourceAsStream( resourceName );
+ }
+ }
+
+ if( is == null )
+ {
+ File f = new File( resourceName );
+ if( f.exists() )
+ {
+ is = new FileInputStream( f );
+ }
+ }
+
+ return is;
+ }
+
+ /**
+ * This will attempt to load the resource given the resource name.
+ *
+ * @param resourceName The resource to try and load.
+ *
+ * @return The resource as a stream or null if it could not be found.
+ *
+ * @throws IOException If there is an error loading the properties.
+ */
+ public static Properties loadProperties( String resourceName ) throws IOException
+ {
+ Properties properties = null;
+ InputStream is = null;
+ try
+ {
+ is = loadResource( resourceName );
+ if( is != null )
+ {
+ properties = new Properties();
+ properties.load( is );
+ }
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * This will attempt to load the resource given the resource name.
+ *
+ * @param resourceName The resource to try and load.
+ * @param defaults A stream of default properties.
+ *
+ * @return The resource as a stream or null if it could not be found.
+ *
+ * @throws IOException If there is an error loading the properties.
+ */
+ public static Properties loadProperties( String resourceName, Properties defaults ) throws IOException
+ {
+ InputStream is = null;
+ try
+ {
+ is = loadResource( resourceName );
+ if( is != null )
+ {
+ defaults.load( is );
+ }
+ }
+ finally
+ {
+ if( is != null )
+ {
+ is.close();
+ }
+ }
+ return defaults;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/SimpleConfigurator.java b/src/main/java/org/pdfbox/util/SimpleConfigurator.java
new file mode 100644
index 0000000..df94029
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/SimpleConfigurator.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ */
+package org.pdfbox.util;
+
+import java.net.URL;
+
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+
+/**
+ * Log4J configurator.
+ *
+ * @author Robert Dickinson (bob@brutesquadlabs.com)
+ * @version $Revision: 1.2 $
+ */
+public class SimpleConfigurator implements Configurator
+{
+ /**
+ * Constructor.
+ */
+ public SimpleConfigurator()
+ {
+ }
+
+ /**
+ * Interpret a resource pointed to by a URL and set up log4J accordingly.
+ * The configuration is done relative to the <code>heirarchy</code> parameter.
+ *
+ * @param url The URL to parse
+ * @param repository The heirarchy to operate upon
+ */
+ public void doConfigure(URL url, LoggerRepository repository)
+ {
+ BasicConfigurator.configure();
+ Logger.getRootLogger().setLevel(Level.DEBUG);
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/Splitter.java b/src/main/java/org/pdfbox/util/Splitter.java
new file mode 100644
index 0000000..c03a989
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/Splitter.java
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Split a document into several other documents.
+ *
+ * @author Mario Ivankovits (mario@ops.co.at)
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.6 $
+ */
+public class Splitter
+{
+
+ /**
+ * The source PDF document.
+ */
+ protected PDDocument pdfDocument;
+
+ /**
+ * The current PDF document that contains the splitted page.
+ */
+ protected PDDocument currentDocument = null;
+
+ private int splitAtPage=1;
+ private List newDocuments = null;
+
+ /**
+ * The current page number that we are processing, zero based.
+ */
+ protected int pageNumber = 0;
+
+ /**
+ * This will take a document and split into several other documents.
+ *
+ * @param document The document to split.
+ *
+ * @return A list of all the split documents.
+ *
+ * @throws IOException If there is an IOError
+ */
+ public List split( PDDocument document ) throws IOException
+ {
+ newDocuments = new ArrayList();
+ pdfDocument = document;
+
+ List pages = pdfDocument.getDocumentCatalog().getAllPages();
+ processPages(pages);
+ return newDocuments;
+ }
+
+ /**
+ * This will tell the splitting algorithm where to split the pages. The default
+ * is 1, so every page will become a new document. If it was to then each document would
+ * contain 2 pages. So it the source document had 5 pages it would split into
+ * 3 new documents, 2 documents containing 2 pages and 1 document containing one
+ * page.
+ *
+ * @param split The number of pages each split document should contain.
+ */
+ public void setSplitAtPage( int split )
+ {
+ if( split <= 0 )
+ {
+ throw new RuntimeException( "Error split must be at least one page." );
+ }
+ splitAtPage = split;
+ }
+
+ /**
+ * This will return how many pages each split document will contain.
+ *
+ * @return The split parameter.
+ */
+ public int getSplitAtPage()
+ {
+ return splitAtPage;
+ }
+
+ /**
+ * Interface method to handle the start of the page processing.
+ *
+ * @param pages The list of pages from the source document.
+ *
+ * @throws IOException If an IO error occurs.
+ */
+ protected void processPages(List pages) throws IOException
+ {
+ Iterator iter = pages.iterator();
+ while( iter.hasNext() )
+ {
+ PDPage page = (PDPage)iter.next();
+ processNextPage( page );
+ }
+ }
+
+ /**
+ * Interface method, you can control where a document gets split by implementing
+ * this method. By default a split occurs at every page. If you wanted to split
+ * based on some complex logic then you could override this method. For example.
+ * <code>
+ * protected void createNewDocumentIfNecessary()
+ * {
+ * if( isPrime( pageNumber ) )
+ * {
+ * super.createNewDocumentIfNecessary();
+ * }
+ * }
+ * </code>
+ *
+ * @throws IOException If there is an error creating the new document.
+ */
+ protected void createNewDocumentIfNecessary() throws IOException
+ {
+ if (isNewDocNecessary())
+ {
+ createNewDocument();
+ }
+ }
+
+ /**
+ * Check if it is necessary to create a new document.
+ *
+ * @return true If a new document should be created.
+ */
+ protected boolean isNewDocNecessary()
+ {
+ return pageNumber % splitAtPage == 0 || currentDocument == null;
+ }
+
+ /**
+ * Create a new document to write the splitted contents to.
+ *
+ * @throws IOException If there is an problem creating the new document.
+ */
+ protected void createNewDocument() throws IOException
+ {
+ currentDocument = new PDDocument();
+ currentDocument.setDocumentInformation(pdfDocument.getDocumentInformation());
+ currentDocument.getDocumentCatalog().setViewerPreferences(
+ pdfDocument.getDocumentCatalog().getViewerPreferences());
+ newDocuments.add(currentDocument);
+ }
+
+
+
+ /**
+ * Interface to start processing a new page.
+ *
+ * @param page The page that is about to get processed.
+ *
+ * @throws IOException If there is an error creating the new document.
+ */
+ protected void processNextPage( PDPage page ) throws IOException
+ {
+ createNewDocumentIfNecessary();
+ PDPage imported = currentDocument.importPage( page );
+ imported.setCropBox( page.findCropBox() );
+ imported.setMediaBox( page.findMediaBox() );
+ imported.setResources( page.findResources() );
+ imported.setRotation( page.findRotation() );
+ pageNumber++;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/TextPosition.java b/src/main/java/org/pdfbox/util/TextPosition.java
new file mode 100644
index 0000000..f44ca56
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/TextPosition.java
@@ -0,0 +1,203 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import org.pdfbox.pdmodel.font.PDFont;
+
+/**
+ * This represents a character and a position on the screen of those characters.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.9 $
+ */
+public class TextPosition
+{
+ private float x;
+ private float y;
+ private float xScale;
+ private float yScale;
+ private float width;
+ private float widthOfSpace;
+ private String c;
+ private PDFont font;
+ private float fontSize;
+ private float wordSpacing;
+
+ /**
+ * Constructor.
+ *
+ * @param xPos The x coordinate of the character.
+ * @param yPos The y coordinate of the character.
+ * @param xScl The x scaling of the character.
+ * @param yScl The y scaling of the character.
+ * @param widthValue The width of the character.
+ * @param spaceWidth The width of the space character.
+ * @param string The character to be displayed.
+ * @param currentFont The current for for this text position.
+ * @param fontSizeValue The new font size.
+ * @param ws The word spacing parameter
+ */
+ public TextPosition(
+ float xPos,
+ float yPos,
+ float xScl,
+ float yScl,
+ float widthValue,
+ float spaceWidth,
+ String string,
+ PDFont currentFont,
+ float fontSizeValue,
+ float ws
+ )
+ {
+ this.x = xPos;
+ this.y = yPos;
+ this.xScale = xScl;
+ this.yScale = yScl;
+ this.width = widthValue;
+ this.widthOfSpace = spaceWidth;
+ this.c = string;
+ this.font = currentFont;
+ this.fontSize = fontSizeValue;
+ this.wordSpacing = ws;
+ }
+
+ /**
+ * This will the character that will be displayed on the screen.
+ *
+ * @return The character on the screen.
+ */
+ public String getCharacter()
+ {
+ return c;
+ }
+
+ /**
+ * This will get the x position of the character.
+ *
+ * @return The x coordinate of the character.
+ */
+ public float getX()
+ {
+ return x;
+ }
+
+ /**
+ * This will get the y position of the character.
+ *
+ * @return The y coordinate of the character.
+ */
+ public float getY()
+ {
+ return y;
+ }
+
+ /**
+ * This will get with width of this character.
+ *
+ * @return The width of this character.
+ */
+ public float getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * This will get the font size that this object is
+ * suppose to be drawn at.
+ *
+ * @return The font size.
+ */
+ public float getFontSize()
+ {
+ return fontSize;
+ }
+
+ /**
+ * This will get the font for the text being drawn.
+ *
+ * @return The font size.
+ */
+ public PDFont getFont()
+ {
+ return font;
+ }
+
+ /**
+ * This will get the current word spacing.
+ *
+ * @return The current word spacing.
+ */
+ public float getWordSpacing()
+ {
+ return wordSpacing;
+ }
+
+ /**
+ * This will get the width of a space character. This is useful for some
+ * algorithms such as the text stripper, that need to know the width of a
+ * space character.
+ *
+ * @return The width of a space character.
+ */
+ public float getWidthOfSpace()
+ {
+ return widthOfSpace;
+ }
+ /**
+ * @return Returns the xScale.
+ */
+ public float getXScale()
+ {
+ return xScale;
+ }
+ /**
+ * @param scale The xScale to set.
+ */
+ public void setXScale(float scale)
+ {
+ xScale = scale;
+ }
+ /**
+ * @return Returns the yScale.
+ */
+ public float getYScale()
+ {
+ return yScale;
+ }
+ /**
+ * @param scale The yScale to set.
+ */
+ public void setYScale(float scale)
+ {
+ yScale = scale;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/TextPositionComparator.java b/src/main/java/org/pdfbox/util/TextPositionComparator.java
new file mode 100644
index 0000000..fab6a6e
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/TextPositionComparator.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.util.Comparator;
+
+import org.pdfbox.pdmodel.PDPage;
+
+/**
+ * This class is a comparator for TextPosition operators.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class TextPositionComparator implements Comparator
+{
+ private PDPage thePage = null;
+
+ /**
+ * Constuctor, comparison of TextPosition depends on the rotation
+ * of the page.
+ * @param page The page that the text position is on.
+ */
+ public TextPositionComparator( PDPage page )
+ {
+ thePage = page;
+ }
+
+ /**
+ * @see Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(Object o1, Object o2)
+ {
+ int retval = 0;
+ TextPosition pos1 = (TextPosition)o1;
+ TextPosition pos2 = (TextPosition)o2;
+ int rotation = thePage.findRotation();
+ float x1 = 0;
+ float x2 = 0;
+ float y1 = 0;
+ float y2 = 0;
+ if( rotation == 0 )
+ {
+ x1 = pos1.getX();
+ x2 = pos2.getX();
+ y1 = pos1.getY();
+ y2 = pos2.getY();
+ }
+ else if( rotation == 90 )
+ {
+ x1 = pos1.getY();
+ x2 = pos2.getX();
+ y1 = pos1.getX();
+ y2 = pos2.getY();
+ }
+ else if( rotation == 180 )
+ {
+ x1 = -pos1.getX();
+ x2 = -pos2.getX();
+ y1 = -pos1.getY();
+ y2 = -pos2.getY();
+ }
+ else if( rotation == 270 )
+ {
+ x1 = -pos1.getY();
+ x2 = -pos2.getY();
+ y1 = -pos1.getX();
+ y2 = -pos2.getX();
+ }
+
+ if( y1 < y2 )
+ {
+ retval = -1;
+ }
+ else if( y1 > y2 )
+ {
+ return 1;
+ }
+ else
+ {
+ if( x1 < x2 )
+ {
+ retval = -1;
+ }
+ else if( x1 > x2 )
+ {
+ retval = 1;
+ }
+ else
+ {
+ retval = 0;
+ }
+ }
+
+ return retval;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/util/XMLUtil.java b/src/main/java/org/pdfbox/util/XMLUtil.java
new file mode 100644
index 0000000..1798aa2
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/XMLUtil.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * This class with handle some simple XML operations.
+ *
+ * @author blitchfield
+ * @version $Revision: 1.2 $
+ */
+public class XMLUtil
+{
+ /**
+ * Utility class, should not be instantiated.
+ *
+ */
+ private XMLUtil()
+ {
+ }
+
+ /**
+ * This will parse an XML stream and create a DOM document.
+ *
+ * @param is The stream to get the XML from.
+ * @return The DOM document.
+ * @throws IOException It there is an error creating the dom.
+ */
+ public static Document parse( InputStream is ) throws IOException
+ {
+ try
+ {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ return builder.parse( is );
+ }
+ catch( Exception e )
+ {
+ IOException thrown = new IOException( e.getMessage() );
+ throw thrown;
+ }
+ }
+
+ /**
+ * This will get the text value of an element.
+ *
+ * @param node The node to get the text value for.
+ * @return The text of the node.
+ */
+ public static String getNodeValue( Element node )
+ {
+ String retval = "";
+ NodeList children = node.getChildNodes();
+ for( int i=0; i<children.getLength(); i++ )
+ {
+ Node next = children.item( i );
+ if( next instanceof Text )
+ {
+ retval = next.getNodeValue();
+ }
+ }
+ return retval;
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/BeginText.java b/src/main/java/org/pdfbox/util/operator/BeginText.java
new file mode 100644
index 0000000..2b6af60
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/BeginText.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+* the long sequence of conditions in processOperator is remplaced
+* by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+public class BeginText extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(BeginText.class);
+
+ /**
+ * process : BT : Begin text object.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug("<BT> " + this.toString()+ " from " + context.toString());
+ }
+ context.setTextMatrix( new Matrix());
+ context.setTextLineMatrix( new Matrix() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/Concatenate.java b/src/main/java/org/pdfbox/util/operator/Concatenate.java
new file mode 100644
index 0000000..7fc986c
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/Concatenate.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.pdfbox.cos.COSNumber;
+import org.apache.log4j.Logger;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+* the long sequence of conditions in processOperator is remplaced by
+* this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+
+public class Concatenate extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(Concatenate.class);
+
+ /**
+ * process : cm : Concatenate matrix to current transformation matrix.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+
+ //concatenate matrix to current transformation matrix
+ COSNumber a = (COSNumber) arguments.get(0);
+ COSNumber b = (COSNumber) arguments.get(1);
+ COSNumber c = (COSNumber) arguments.get(2);
+ COSNumber d = (COSNumber) arguments.get(3);
+ COSNumber e = (COSNumber) arguments.get(4);
+ COSNumber f = (COSNumber) arguments.get(5);
+
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug("<cm " +
+ "a=\"" + a.floatValue() + "\" " +
+ "b=\"" + b.floatValue() + "\" " +
+ "c=\"" + c.floatValue() + "\" " +
+ "d=\"" + d.floatValue() + "\" " +
+ "e=\"" + e.floatValue() + "\" " +
+ "f=\"" + f.floatValue() + "\" > " +
+ this.toString() + " from " + context.toString());
+ }
+
+ Matrix newMatrix = new Matrix();
+ newMatrix.setValue(0, 0, a.floatValue());
+ newMatrix.setValue(0, 1, b.floatValue());
+ newMatrix.setValue(1, 0, c.floatValue());
+ newMatrix.setValue(1, 1, d.floatValue());
+ newMatrix.setValue(2, 0, e.floatValue());
+ newMatrix.setValue(2, 1, f.floatValue());
+
+ // wprinz: BUG Fix:
+ // In PDF, matrices have to be multiplied from left to right
+ // (the new matrix is left, the old one(s) are right)
+
+ Matrix old_ctm = context.getGraphicsState().getCurrentTransformationMatrix();
+ Matrix matrix_to_concat = newMatrix;
+ Matrix new_ctm = matrix_to_concat.multiply(old_ctm);
+ context.getGraphicsState().setCurrentTransformationMatrix(
+ new_ctm );
+
+ // :wprinz
+
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/EndText.java b/src/main/java/org/pdfbox/util/operator/EndText.java
new file mode 100644
index 0000000..2794ccf
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/EndText.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.util.PDFOperator;
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the
+* long sequence of conditions in processOperator is remplaced by
+* this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+public class EndText extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(EndText.class);
+
+ /**
+ * process : ET : End text object.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug("<ET> "+ this.toString());
+ }
+ context.setTextMatrix( null);
+ context.setTextLineMatrix( null);
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/GRestore.java b/src/main/java/org/pdfbox/util/operator/GRestore.java
new file mode 100644
index 0000000..c07b8d8
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/GRestore.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import org.apache.log4j.Logger;
+import java.util.List;
+
+import org.pdfbox.pdmodel.graphics.PDGraphicsState;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of
+ * conditions in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+public class GRestore extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(GRestore.class);
+
+
+ /**
+ * process : Q : Restore graphics state.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug("<Q> - restore state" + this.toString());
+ }
+ context.setGraphicsState( (PDGraphicsState)context.getGraphicsStack().pop() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/GSave.java b/src/main/java/org/pdfbox/util/operator/GSave.java
new file mode 100644
index 0000000..6a754b6
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/GSave.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of
+ * conditions in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+
+public class GSave extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(GSave.class);
+
+ /**
+ * process : q : Save graphics state.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug("<q> - save state " + this.toString());
+ }
+ context.getGraphicsStack().push( context.getGraphicsState().clone() );
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/Invoke.java b/src/main/java/org/pdfbox/util/operator/Invoke.java
new file mode 100644
index 0000000..2f8f789
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/Invoke.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.PDResources;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObject;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Map;
+
+/**
+ * Invoke named XObject.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author Mario Ivankovits
+ *
+ * @version $Revision: 1.6 $
+ */
+public class Invoke extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(Invoke.class);
+
+ private Set inProcess = new TreeSet();
+
+ /**
+ * process : Do - Invoke a named xobject.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ *
+ * @throws IOException If there is an error processing this operator.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSName name = (COSName) arguments.get( 0 );
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Do name=\"" + name.getName() + "\">");
+ }
+
+ // wprinz: allow recursive processing so that nested xobject forms are correctly dealt with
+// if (inProcess.contains(name))
+// {
+// // avoid recursive loop
+// return;
+// }
+ // /wprinz
+
+ inProcess.add(name);
+
+ try
+ {
+ //PDResources res = context.getResources();
+
+ Map xobjects = context.getXObjects();
+ PDXObject xobject = (PDXObject) xobjects.get(name.getName());
+
+ if(xobject instanceof PDXObjectForm)
+ {
+ PDXObjectForm form = (PDXObjectForm)xobject;
+ COSStream invoke = (COSStream)form.getCOSObject();
+ PDResources pdResources = form.getResources();
+ PDPage page = context.getCurrentPage();
+ if(pdResources == null)
+ {
+ pdResources = page.findResources();
+ }
+
+ getContext().processSubStream( page, pdResources, invoke );
+ }
+ }
+ finally
+ {
+ inProcess.remove(name);
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/MoveAndShow.java b/src/main/java/org/pdfbox/util/operator/MoveAndShow.java
new file mode 100644
index 0000000..ca3a3f9
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/MoveAndShow.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+* the long sequence of conditions in processOperator is remplaced by
+* this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.4 $
+ */
+public class MoveAndShow extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(MoveAndShow.class);
+
+ /**
+ * ' Move to next line and show text.
+ * @param arguments List
+ * @param operator The operator that is being executed.
+ * @throws IOException If there is an error processing the operator.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ // Move to start of next text line, and show text
+ //
+ if( LOG.isDebugEnabled())
+ {
+ COSString string = (COSString)arguments.get( 0 );
+ LOG.debug("<' string=\"" + string.getString() + "\">");
+ }
+
+ context.processOperator("T*", null);
+ context.processOperator("Tj", arguments);
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/MoveText.java b/src/main/java/org/pdfbox/util/operator/MoveText.java
new file mode 100644
index 0000000..10360bb
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/MoveText.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+public class MoveText extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(MoveText.class);
+
+
+ /**
+ * process : Td : Move text position.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Td x=\"" + x.floatValue() + "\" y=\"" + y.floatValue() + "\">");
+ }
+ Matrix td = new Matrix();
+ td.setValue( 2, 0, x.floatValue() );//.* textMatrix.getValue(0,0) );
+ td.setValue( 2, 1, y.floatValue() );//* textMatrix.getValue(1,1) );
+ //log.debug( "textLineMatrix before " + textLineMatrix );
+ context.setTextLineMatrix( td.multiply( context.getTextLineMatrix() ) ); //textLineMatrix.multiply( td );
+ //log.debug( "textLineMatrix after " + textLineMatrix );
+ context.setTextMatrix( context.getTextLineMatrix().copy() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/MoveTextSetLeading.java b/src/main/java/org/pdfbox/util/operator/MoveTextSetLeading.java
new file mode 100644
index 0000000..e68a1d1
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/MoveTextSetLeading.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.4 $
+ */
+public class MoveTextSetLeading extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(MoveTextSetLeading.class);
+
+ /**
+ * process : TD Move text position and set leading.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ *
+ * @throws IOException If there is an error during processing.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ //move text position and set leading
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ if (LOG.isDebugEnabled())
+ {
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ LOG.debug("<TD x=\"" + x.floatValue() + "\" y=\"" + y.floatValue() + "\">");
+ }
+
+ ArrayList args = new ArrayList();
+ args.add(new COSFloat(-1*y.floatValue()));
+ context.processOperator("TL", args);
+ context.processOperator("Td", arguments);
+
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/NextLine.java b/src/main/java/org/pdfbox/util/operator/NextLine.java
new file mode 100644
index 0000000..daa35eb
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/NextLine.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of
+ * conditions in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.4 $
+ */
+public class NextLine extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(NextLine.class);
+
+ /**
+ * process : T* Move to start of next text line.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ *
+ * @throws IOException If there is an error during processing.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<T* graphicsState.getTextState().getLeading()=\"" +
+ context.getGraphicsState().getTextState().getLeading() + "\">");
+ }
+ //move to start of next text line
+ ArrayList args = new ArrayList();
+ args.add(new COSFloat(0.0f));
+ // this must be -leading instead of just leading as written in the
+ // specification (p.369) the acrobat reader seems to implement it the same way
+ args.add(new COSFloat(-1*context.getGraphicsState().getTextState().getLeading()));
+ // use Td instead of repeating code
+ context.processOperator("Td", args);
+
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/OperatorProcessor.java b/src/main/java/org/pdfbox/util/operator/OperatorProcessor.java
new file mode 100644
index 0000000..27c21b9
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/OperatorProcessor.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.PDFStreamEngine;
+import java.util.List;
+import java.io.IOException;
+
+/**
+ *
+ * <p>Titre : OperatorProcessor</p>
+ * <p>Description : This class is the strategy abstract class
+ * in the strategy GOF pattern. After instancated, you must ever call
+* the setContext method to initiamise OPeratorProcessor</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+public abstract class OperatorProcessor
+{
+
+ /**
+ * The stream engine processing context.
+ */
+ protected PDFStreamEngine context = null;
+
+ /**
+ * Constructor.
+ *
+ */
+ protected OperatorProcessor()
+ {
+ }
+
+ /**
+ * Get the context for processing.
+ *
+ * @return The processing context.
+ */
+ protected PDFStreamEngine getContext()
+ {
+ return context;
+ }
+
+ /**
+ * Set the processing context.
+ *
+ * @param ctx The context for processing.
+ */
+ public void setContext(PDFStreamEngine ctx)
+ {
+ context = ctx;
+ }
+
+ /**
+ * process the operator.
+ * @param operator The operator that is being processed.
+ * @param arguments arguments needed by this operator.
+ *
+ * @throws IOException If there is an error processing the operator.
+ */
+ public abstract void process(PDFOperator operator, List arguments) throws IOException;
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetCharSpacing.java b/src/main/java/org/pdfbox/util/operator/SetCharSpacing.java
new file mode 100644
index 0000000..6813dce
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetCharSpacing.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ *
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.4 $
+ */
+public class SetCharSpacing extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(SetCharSpacing.class);
+
+ /**
+ * process : Tc Set character spacing.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ //set character spacing
+ if( arguments.size() > 0 )
+ {
+ //There are some documents which are incorrectly structured, and have
+ //a wrong number of arguments to this, so we will assume the last argument
+ //in the list
+ Object charSpacing = arguments.get( arguments.size()-1 );
+ if( charSpacing instanceof COSNumber )
+ {
+ COSNumber characterSpacing = (COSNumber)charSpacing;
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Tc characterSpacing=\"" + characterSpacing.floatValue() + "\" />");
+ }
+ context.getGraphicsState().getTextState().setCharacterSpacing( characterSpacing.floatValue() );
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetGraphicsStateParameters.java b/src/main/java/org/pdfbox/util/operator/SetGraphicsStateParameters.java
new file mode 100644
index 0000000..e72e05d
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetGraphicsStateParameters.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.graphics.PDExtendedGraphicsState;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+
+public class SetGraphicsStateParameters extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetTextFont.class);
+
+ /**
+ * gs Set parameters from graphics state parameter dictionary.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ //set parameters from graphics state parameter dictionary
+ COSName graphicsName = (COSName)arguments.get( 0 );
+
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<gs name=\"" + graphicsName.getName() + "\" />" );
+ }
+ PDExtendedGraphicsState gs = (PDExtendedGraphicsState)context.getGraphicsStates().get( graphicsName.getName() );
+ gs.copyIntoGraphicsState( context.getGraphicsState() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetHorizontalTextScaling.java b/src/main/java/org/pdfbox/util/operator/SetHorizontalTextScaling.java
new file mode 100644
index 0000000..bfb9ebe
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetHorizontalTextScaling.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+
+public class SetHorizontalTextScaling extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetTextFont.class);
+
+ /**
+ * Tz Set horizontal text scaling.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSNumber scaling = (COSNumber)arguments.get(0);
+ context.getGraphicsState().getTextState().setHorizontalScalingPercent( scaling.floatValue() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetLineWidth.java b/src/main/java/org/pdfbox/util/operator/SetLineWidth.java
new file mode 100644
index 0000000..de0576d
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetLineWidth.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+
+public class SetLineWidth extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetTextFont.class);
+
+ /**
+ * w Set line width.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSNumber width = (COSNumber)arguments.get( 0 );
+ context.getGraphicsState().setLineWidth( width.doubleValue() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetMatrix.java b/src/main/java/org/pdfbox/util/operator/SetMatrix.java
new file mode 100644
index 0000000..5d5f1f5
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetMatrix.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of conditions
+ * in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+
+public class SetMatrix extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(SetMatrix.class);
+
+ /**
+ * Tm Set text matrix and text line matrix.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ //Set text matrix and text line matrix
+ COSNumber a = (COSNumber)arguments.get( 0 );
+ COSNumber b = (COSNumber)arguments.get( 1 );
+ COSNumber c = (COSNumber)arguments.get( 2 );
+ COSNumber d = (COSNumber)arguments.get( 3 );
+ COSNumber e = (COSNumber)arguments.get( 4 );
+ COSNumber f = (COSNumber)arguments.get( 5 );
+
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Tm " +
+ "a=\"" + a.floatValue() + "\" " +
+ "b=\"" + b.floatValue() + "\" " +
+ "c=\"" + c.floatValue() + "\" " +
+ "d=\"" + d.floatValue() + "\" " +
+ "e=\"" + e.floatValue() + "\" " +
+ "f=\"" + f.floatValue() + "\" >");
+ }
+
+ Matrix textMatrix = new Matrix();
+ textMatrix.setValue( 0, 0, a.floatValue() );
+ textMatrix.setValue( 0, 1, b.floatValue() );
+ textMatrix.setValue( 1, 0, c.floatValue() );
+ textMatrix.setValue( 1, 1, d.floatValue() );
+ textMatrix.setValue( 2, 0, e.floatValue() );
+ textMatrix.setValue( 2, 1, f.floatValue() );
+ context.setTextMatrix( textMatrix );
+ context.setTextLineMatrix( textMatrix.copy() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetMoveAndShow.java b/src/main/java/org/pdfbox/util/operator/SetMoveAndShow.java
new file mode 100644
index 0000000..b49206d
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetMoveAndShow.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of conditions
+ * in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.5 $
+ */
+
+public class SetMoveAndShow extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(SetMoveAndShow.class);
+
+ /**
+ * " Set word and character spacing, move to next line, and show text.
+ * @param operator The operator that is being executed.
+ * @param arguments List.
+ * @throws IOException If there is an error processing the operator.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ //Set word and character spacing, move to next line, and show text
+ //
+ if (LOG.isDebugEnabled())
+ {
+ COSNumber wordSpacing = (COSNumber)arguments.get( 0 );
+ COSNumber characterSpacing = (COSNumber)arguments.get( 1 );
+ COSString string = (COSString)arguments.get( 2 );
+ LOG.debug("<\" wordSpacing=\"" + wordSpacing +
+ "\", characterSpacing=\"" + characterSpacing +
+ "\", string=\"" + string.getString() + "\">");
+ }
+
+ context.processOperator("Tw", arguments.subList(0,1));
+ context.processOperator("Tc", arguments.subList(1,2));
+ context.processOperator("'", arguments.subList(2,3));
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetNonStrokingCMYKColor.java b/src/main/java/org/pdfbox/util/operator/SetNonStrokingCMYKColor.java
new file mode 100644
index 0000000..941fcd6
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetNonStrokingCMYKColor.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Set the non stroking color space.</p>
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class SetNonStrokingCMYKColor extends OperatorProcessor
+{
+ /**
+ * cs Set color space for non stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PDColorSpace cs = PDDeviceCMYK.INSTANCE;
+ PDColorSpaceInstance colorInstance = context.getGraphicsState().getNonStrokingColorSpace();
+ colorInstance.setColorSpace( cs );
+ float[] values = new float[4];
+ for( int i=0; i<arguments.size(); i++ )
+ {
+ values[i] = ((COSNumber)arguments.get( i )).floatValue();
+ }
+ colorInstance.setColorSpaceValue( values );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetNonStrokingColorSpace.java b/src/main/java/org/pdfbox/util/operator/SetNonStrokingColorSpace.java
new file mode 100644
index 0000000..c75d1a4
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetNonStrokingColorSpace.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Set the non stroking color space.</p>
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class SetNonStrokingColorSpace extends OperatorProcessor
+{
+ private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
+
+ /**
+ * cs Set color space for non stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+// (PDF 1.1) Set color space for stroking operations
+ COSName name = (COSName)arguments.get( 0 );
+ PDColorSpace cs = (PDColorSpace)context.getColorSpaces().get( name.getName() );
+ if( cs == null )
+ {
+ cs = PDColorSpaceFactory.createColorSpace( name );
+ }
+ PDColorSpaceInstance colorInstance = context.getGraphicsState().getNonStrokingColorSpace();
+ colorInstance.setColorSpace( cs );
+ int numComponents = cs.getNumberOfComponents();
+ float[] values = EMPTY_FLOAT_ARRAY;
+ if( numComponents >= 0 )
+ {
+ values = new float[numComponents];
+ for( int i=0; i<numComponents; i++ )
+ {
+ values[i] = 0f;
+ }
+ if( cs instanceof PDDeviceCMYK )
+ {
+ values[3] = 1f;
+ }
+ }
+ colorInstance.setColorSpaceValue( values );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetNonStrokingRGBColor.java b/src/main/java/org/pdfbox/util/operator/SetNonStrokingRGBColor.java
new file mode 100644
index 0000000..ca4e8b9
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetNonStrokingRGBColor.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Set the non stroking color space.</p>
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.2 $
+ */
+public class SetNonStrokingRGBColor extends OperatorProcessor
+{
+ /**
+ * rg Set color space for non stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PDColorSpace cs = PDDeviceRGB.INSTANCE;
+ PDColorSpaceInstance colorInstance = context.getGraphicsState().getNonStrokingColorSpace();
+ colorInstance.setColorSpace( cs );
+ float[] values = new float[3];
+ for( int i=0; i<arguments.size(); i++ )
+ {
+ values[i] = ((COSNumber)arguments.get( i )).floatValue();
+ }
+ colorInstance.setColorSpaceValue( values );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetStrokingCMYKColor.java b/src/main/java/org/pdfbox/util/operator/SetStrokingCMYKColor.java
new file mode 100644
index 0000000..5a03d83
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetStrokingCMYKColor.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class SetStrokingCMYKColor extends OperatorProcessor
+{
+ /**
+ * CS Set color space for stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PDColorSpace cs = PDDeviceCMYK.INSTANCE;
+ PDColorSpaceInstance colorInstance = context.getGraphicsState().getStrokingColorSpace();
+ colorInstance.setColorSpace( cs );
+ float[] values = new float[4];
+ for( int i=0; i<arguments.size(); i++ )
+ {
+ values[i] = ((COSNumber)arguments.get( i )).floatValue();
+ }
+ colorInstance.setColorSpaceValue( values );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetStrokingColorSpace.java b/src/main/java/org/pdfbox/util/operator/SetStrokingColorSpace.java
new file mode 100644
index 0000000..66d5bbc
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetStrokingColorSpace.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+
+public class SetStrokingColorSpace extends OperatorProcessor
+{
+ private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
+
+ /**
+ * CS Set color space for stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ //(PDF 1.1) Set color space for stroking operations
+ COSName name = (COSName)arguments.get( 0 );
+ PDColorSpace cs = (PDColorSpace)context.getColorSpaces().get( name.getName() );
+ if( cs == null )
+ {
+ cs = PDColorSpaceFactory.createColorSpace( name );
+ }
+ PDColorSpaceInstance colorInstance = context.getGraphicsState().getStrokingColorSpace();
+ colorInstance.setColorSpace( cs );
+ int numComponents = cs.getNumberOfComponents();
+ float[] values = EMPTY_FLOAT_ARRAY;
+ if( numComponents >= 0 )
+ {
+ values = new float[numComponents];
+ for( int i=0; i<numComponents; i++ )
+ {
+ values[i] = 0f;
+ }
+ if( cs instanceof PDDeviceCMYK )
+ {
+ values[3] = 1f;
+ }
+ }
+ colorInstance.setColorSpaceValue( values );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetStrokingRGBColor.java b/src/main/java/org/pdfbox/util/operator/SetStrokingRGBColor.java
new file mode 100644
index 0000000..1b66445
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetStrokingRGBColor.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class SetStrokingRGBColor extends OperatorProcessor
+{
+ /**
+ * RG Set color space for stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PDColorSpace cs = PDDeviceRGB.INSTANCE;
+ PDColorSpaceInstance colorInstance = context.getGraphicsState().getStrokingColorSpace();
+ colorInstance.setColorSpace( cs );
+ float[] values = new float[3];
+ for( int i=0; i<arguments.size(); i++ )
+ {
+ values[i] = ((COSNumber)arguments.get( i )).floatValue();
+ }
+ colorInstance.setColorSpaceValue( values );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetTextFont.java b/src/main/java/org/pdfbox/util/operator/SetTextFont.java
new file mode 100644
index 0000000..3e54943
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetTextFont.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.4 $
+ */
+
+public class SetTextFont extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetTextFont.class);
+
+ /**
+ * Tf selectfont Set text font and size.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ //there are some documents that are incorrectly structured and
+ //arguments are in the wrong spot, so we will silently ignore them
+ //if there are no arguments
+ if( arguments.size() >= 2 )
+ {
+ //set font and size
+ COSName fontName = (COSName)arguments.get( 0 );
+ float fontSize = ((COSNumber)arguments.get( 1 ) ).floatValue();
+ context.getGraphicsState().getTextState().setFontSize( fontSize );
+
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Tf font=\"" + fontName.getName() + "\" size=\"" +
+ context.getGraphicsState().getTextState().getFontSize() + "\">");
+ }
+
+ //old way
+ //graphicsState.getTextState().getFont() = (COSObject)stream.getDictionaryObject( fontName );
+ //if( graphicsState.getTextState().getFont() == null )
+ //{
+ // graphicsState.getTextState().getFont() = (COSObject)graphicsState.getTextState().getFont()
+ // Dictionary.getItem( fontName );
+ //}
+ context.getGraphicsState().getTextState().setFont( (PDFont)context.getFonts().get( fontName.getName() ) );
+ if( context.getGraphicsState().getTextState().getFont() == null )
+ {
+ throw new IOException( "Error: Could not find font(" + fontName + ") in map=" + context.getFonts() );
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetTextLeading.java b/src/main/java/org/pdfbox/util/operator/SetTextLeading.java
new file mode 100644
index 0000000..928172b
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetTextLeading.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+* the long sequence of conditions in processOperator is remplaced
+* by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+
+public class SetTextLeading extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(SetTextLeading.class);
+
+ /**
+ * TL Set text leading.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ COSNumber leading = (COSNumber)arguments.get( 0 );
+ context.getGraphicsState().getTextState().setLeading( leading.floatValue() );
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<TL leading=\"" + leading.floatValue() + "\" >");
+ }
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetTextRenderingMode.java b/src/main/java/org/pdfbox/util/operator/SetTextRenderingMode.java
new file mode 100644
index 0000000..19da1e2
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetTextRenderingMode.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+
+public class SetTextRenderingMode extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetTextFont.class);
+
+ /**
+ * Tr Set text rendering mode.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSNumber mode = (COSNumber)arguments.get( 0 );
+ context.getGraphicsState().getTextState().setRenderingMode( mode.intValue() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetTextRise.java b/src/main/java/org/pdfbox/util/operator/SetTextRise.java
new file mode 100644
index 0000000..0d1e884
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetTextRise.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator is remplaced by
+ * this strategy pattern.</p>
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+
+public class SetTextRise extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetTextFont.class);
+
+ /**
+ * Ts Set text rise.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSNumber rise = (COSNumber)arguments.get(0);
+ context.getGraphicsState().getTextState().setRise( rise.floatValue() );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/SetWordSpacing.java b/src/main/java/org/pdfbox/util/operator/SetWordSpacing.java
new file mode 100644
index 0000000..3a1e176
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/SetWordSpacing.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of
+ * conditions in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+
+public class SetWordSpacing extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(SetWordSpacing.class);
+
+ /**
+ * Tw Set word spacing.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ //set word spacing
+ COSNumber wordSpacing = (COSNumber)arguments.get( 0 );
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Tw wordSpacing=\"" + wordSpacing.floatValue() + "\" />");
+ }
+ context.getGraphicsState().getTextState().setWordSpacing( wordSpacing.floatValue() );
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/ShowText.java b/src/main/java/org/pdfbox/util/operator/ShowText.java
new file mode 100644
index 0000000..1889259
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/ShowText.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.util.PDFOperator;
+
+import java.io.IOException;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class :
+ * the long sequence of conditions in processOperator
+ * is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.3 $
+ */
+
+public class ShowText extends OperatorProcessor
+{
+
+ private static final Logger LOG = Logger.getLogger(ShowText.class);
+
+ /**
+ * Tj show Show text.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ *
+ * @throws IOException If there is an error processing this operator.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSString string = (COSString)arguments.get( 0 );
+ context.showString( string.getBytes() );
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("<Tj string=\"" + string.getString() + "\">");
+ }
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/ShowTextGlyph.java b/src/main/java/org/pdfbox/util/operator/ShowTextGlyph.java
new file mode 100644
index 0000000..cf22e11
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/ShowTextGlyph.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator;
+
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSNumber;
+import java.io.IOException;
+import org.pdfbox.cos.COSString;
+
+/**
+ * <p>Titre : PDFEngine Modification.</p>
+ * <p>Description : Structal modification of the PDFEngine class : the long sequence of
+ * conditions in processOperator is remplaced by this strategy pattern</p>
+ * <p>Copyright : Copyright (c) 2004</p>
+ * <p>Société : DBGS</p>
+ * @author Huault : huault@free.fr
+ * @version $Revision: 1.5 $
+ */
+
+public class ShowTextGlyph extends OperatorProcessor
+{
+ private static final Logger LOG = Logger.getLogger(ShowTextGlyph.class);
+
+ /**
+ * TJ Show text, allowing individual glyph positioning.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If there is an error processing this operator.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSArray array = (COSArray)arguments.get( 0 );
+ float adjustment=0;
+ for( int i=0; i<array.size(); i++ )
+ {
+ COSBase next = array.get( i );
+ if( next instanceof COSNumber )
+ {
+ adjustment = ((COSNumber)next).floatValue();
+
+ Matrix adjMatrix = new Matrix();
+ adjustment=(-adjustment/1000)*context.getGraphicsState().getTextState().getFontSize() *
+ (context.getGraphicsState().getTextState().getHorizontalScalingPercent()/100);
+ adjMatrix.setValue( 2, 0, adjustment );
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug( "TJ adjustment=" + adjustment + " textMatrix=" + context.getTextMatrix() );
+ }
+ context.setTextMatrix( adjMatrix.multiply( context.getTextMatrix() ) );
+ if( LOG.isDebugEnabled() )
+ {
+ LOG.debug( "textMatrix after=" + context.getTextMatrix() );
+ }
+ }
+ else if( next instanceof COSString )
+ {
+ context.showString( ((COSString)next).getBytes() );
+ }
+ else
+ {
+ throw new IOException( "Unknown type in array for TJ operation:" + next );
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/util/operator/package.html b/src/main/java/org/pdfbox/util/operator/package.html
new file mode 100644
index 0000000..edb8444
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains implementations of all of the PDF operators.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java
new file mode 100644
index 0000000..7d2d49f
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.geom.Rectangle2D;
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class AppendRectangleToPath extends OperatorProcessor
+{
+
+
+ /**
+ * process : re : append rectangle to path.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+ COSNumber w = (COSNumber)arguments.get( 2 );
+ COSNumber h = (COSNumber)arguments.get( 3 );
+ Rectangle2D rect = new Rectangle2D.Double(
+ x.doubleValue(),
+ drawer.fixY( x.doubleValue(), y.doubleValue())-h.doubleValue(),
+ w.doubleValue()+1,
+ h.doubleValue()+1);
+ drawer.getLinePath().reset();
+
+ drawer.getLinePath().append( rect, false );
+ //graphics.drawRect((int)x.doubleValue(), (int)(pageSize.getHeight() - y.doubleValue()),
+ // (int)w.doubleValue(),(int)h.doubleValue() );
+ //System.out.println( "<re x=\"" + x.getValue() + "\" y=\"" + y.getValue() + "\" width=\"" +
+ // width.getValue() + "\" height=\"" + height.getValue() + "\" >" );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/BeginInlineImage.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/BeginInlineImage.java
new file mode 100644
index 0000000..9031345
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/BeginInlineImage.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.List;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.graphics.xobject.PDInlinedImage;
+import org.pdfbox.util.ImageParameters;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class BeginInlineImage extends OperatorProcessor
+{
+
+
+ /**
+ * process : BI : begin inline image.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If there is an error displaying the inline image.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PageDrawer drawer = (PageDrawer)context;
+ Graphics2D graphics = drawer.getGraphics();
+ //begin inline image object
+ ImageParameters params = operator.getImageParameters();
+ PDInlinedImage image = new PDInlinedImage();
+ image.setImageParameters( params );
+ image.setImageData( operator.getImageData() );
+ BufferedImage awtImage = image.createImage();
+
+ Matrix ctm = drawer.getGraphicsState().getCurrentTransformationMatrix();
+
+ int width = awtImage.getWidth();
+ int height = awtImage.getHeight();
+
+
+ AffineTransform at = new AffineTransform(
+ ctm.getValue(0,0)/width,
+ ctm.getValue(0,1),
+ ctm.getValue(1,0),
+ ctm.getValue(1,1)/height,
+ ctm.getValue(2,0),
+ ctm.getValue(2,1)
+ );
+ //at.setToRotation((double)page.getRotation());
+
+
+ // The transformation should be done
+ // 1 - Translation
+ // 2 - Rotation
+ // 3 - Scale or Skew
+ //AffineTransform at = new AffineTransform();
+
+ // Translation
+ //at = new AffineTransform();
+ //at.setToTranslation((double)ctm.getValue(0,0),
+ // (double)ctm.getValue(0,1));
+
+ // Rotation
+ //AffineTransform toAdd = new AffineTransform();
+ //toAdd.setToRotation(1.5705);
+ //toAdd.setToRotation(ctm.getValue(2,0)*(Math.PI/180));
+ //at.concatenate(toAdd);
+
+ // Scale / Skew?
+ //toAdd.setToScale(width, height);
+ //at.concatenate(toAdd);
+ //at.setToScale( width, height );
+ graphics.drawImage( awtImage, at, null );
+ //graphics.drawImage( awtImage,0,0, width,height,null);
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/ClosePath.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/ClosePath.java
new file mode 100644
index 0000000..418af6d
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/ClosePath.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class ClosePath extends OperatorProcessor
+{
+
+
+ /**
+ * process : h : Close path.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+ drawer.getLinePath().closePath();
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveTo.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveTo.java
new file mode 100644
index 0000000..c056b91
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveTo.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class CurveTo extends OperatorProcessor
+{
+
+
+ /**
+ * process : c : Append curved segment to path.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+
+ COSNumber x1 = (COSNumber)arguments.get( 0 );
+ COSNumber y1 = (COSNumber)arguments.get( 1 );
+ COSNumber x2 = (COSNumber)arguments.get( 2 );
+ COSNumber y2 = (COSNumber)arguments.get( 3 );
+ COSNumber x3 = (COSNumber)arguments.get( 4 );
+ COSNumber y3 = (COSNumber)arguments.get( 5 );
+ float x1f = x1.floatValue();
+ float y1f = (float)drawer.fixY( x1f, y1.floatValue() );
+ float x2f = x2.floatValue();
+ float y2f = (float)drawer.fixY( x2f, y2.floatValue() );
+ float x3f = x3.floatValue();
+ float y3f = (float)drawer.fixY( x3f, y3.floatValue() );
+ drawer.getLinePath().curveTo(x1f,y1f,x2f,y2f,x3f,y3f);
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java
new file mode 100644
index 0000000..670cdb3
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class CurveToReplicateFinalPoint extends OperatorProcessor
+{
+
+
+ /**
+ * process : y : Append curved segment to path (final point replicated).
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+
+ COSNumber x1 = (COSNumber)arguments.get( 0 );
+ COSNumber y1 = (COSNumber)arguments.get( 1 );
+ COSNumber x3 = (COSNumber)arguments.get( 2 );
+ COSNumber y3 = (COSNumber)arguments.get( 3 );
+ float x1f = x1.floatValue();
+ float y1f = (float)drawer.fixY( x1f, y1.floatValue() );
+ float x3f = x3.floatValue();
+ float y3f = (float)drawer.fixY( x3f, y3.floatValue() );
+ drawer.getLinePath().curveTo(x1f,y1f,x3f,y3f,x3f,y3f);
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java
new file mode 100644
index 0000000..ff354d0
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class CurveToReplicateInitialPoint extends OperatorProcessor
+{
+
+
+ /**
+ * process : v : Append curved segment to path (initial point replicated).
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+
+ COSNumber x2 = (COSNumber)arguments.get( 0 );
+ COSNumber y2 = (COSNumber)arguments.get( 1 );
+ COSNumber x3 = (COSNumber)arguments.get( 2 );
+ COSNumber y3 = (COSNumber)arguments.get( 3 );
+ float x2f = x2.floatValue();
+ float y2f = (float)drawer.fixY( x2f, y2.floatValue() );
+ float x3f = x3.floatValue();
+ float y3f = (float)drawer.fixY( x3f, y3.floatValue() );
+
+ GeneralPath path = drawer.getLinePath();
+ Point2D currentPoint = path.getCurrentPoint();
+ float currentX = (float)currentPoint.getX();
+ float currentY = (float)currentPoint.getY();
+ drawer.getLinePath().curveTo(currentX,currentY,x2f,y2f,x3f,y3f);
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java
new file mode 100644
index 0000000..da9e834
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+import java.util.List;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class FillEvenOddRule extends OperatorProcessor
+{
+
+
+ /**
+ * process : f* : fill path using even odd rule.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+// NOTE:changes here should probably also be made to FillNonZeroRule
+ PageDrawer drawer = (PageDrawer)context;
+ Graphics2D graphics = drawer.getGraphics();
+ //linePath.closePath();
+ graphics.setColor( drawer.getNonStrokingColor() );
+ drawer.getLinePath().setWindingRule( GeneralPath.WIND_EVEN_ODD );
+ graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
+ //else
+ //{
+ graphics.fill( drawer.getLinePath() );
+ //}
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java
new file mode 100644
index 0000000..56efa33
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+import java.util.List;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class FillNonZeroRule extends OperatorProcessor
+{
+
+
+ /**
+ * process : F/f : fill path using non zero winding rule.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ //NOTE:changes here should probably also be made to FillEvenOddRule
+ PageDrawer drawer = (PageDrawer)context;
+ Graphics2D graphics = drawer.getGraphics();
+ //linePath.closePath();
+ graphics.setColor( drawer.getNonStrokingColor() );
+ drawer.getLinePath().setWindingRule( GeneralPath.WIND_NON_ZERO );
+ graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
+ //else
+ //{
+ graphics.fill( drawer.getLinePath() );
+ //}
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/Invoke.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/Invoke.java
new file mode 100644
index 0000000..3d99b74
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/Invoke.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObject;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class Invoke extends OperatorProcessor
+{
+ private static Logger log = Logger.getLogger( Invoke.class );
+
+ /**
+ * process : re : append rectangle to path.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If there is an error invoking the sub object.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PageDrawer drawer = (PageDrawer)context;
+ PDPage page = drawer.getPage();
+ Dimension pageSize = drawer.getPageSize();
+ Graphics2D graphics = drawer.getGraphics();
+ COSName objectName = (COSName)arguments.get( 0 );
+ Map xobjects = drawer.getResources().getXObjects();
+ PDXObject xobject = (PDXObject)xobjects.get( objectName.getName() );
+ if( xobject instanceof PDXObjectImage )
+ {
+ PDXObjectImage image = (PDXObjectImage)xobject;
+ try
+ {
+ BufferedImage awtImage = image.getRGBImage();
+ Matrix ctm = drawer.getGraphicsState().getCurrentTransformationMatrix();
+
+ int width = awtImage.getWidth();
+ int height = awtImage.getHeight();
+
+ double rotationInRadians =(page.findRotation() * Math.PI)/180;
+
+
+ AffineTransform rotation = new AffineTransform();
+ rotation.setToRotation( rotationInRadians );
+ AffineTransform rotationInverse = rotation.createInverse();
+ Matrix rotationInverseMatrix = new Matrix();
+ rotationInverseMatrix.setFromAffineTransform( rotationInverse );
+ Matrix rotationMatrix = new Matrix();
+ rotationMatrix.setFromAffineTransform( rotation );
+
+ Matrix unrotatedCTM = ctm.multiply( rotationInverseMatrix );
+
+ Matrix scalingParams = unrotatedCTM.extractScaling();
+ Matrix scalingMatrix = Matrix.getScaleInstance(1f/width,1f/height);
+ scalingParams = scalingParams.multiply( scalingMatrix );
+
+ Matrix translationParams = unrotatedCTM.extractTranslating();
+ Matrix translationMatrix = null;
+ int pageRotation = page.findRotation();
+ if( pageRotation == 0 )
+ {
+ translationParams.setValue(2,1, -translationParams.getValue( 2,1 ));
+ translationMatrix = Matrix.getTranslatingInstance(
+ 0, (float)pageSize.getHeight()-height*scalingParams.getYScale() );
+ }
+ else if( pageRotation == 90 )
+ {
+ translationMatrix = Matrix.getTranslatingInstance( 0, (float)pageSize.getHeight() );
+ }
+ else
+ {
+ //TODO need to figure out other cases
+ }
+ translationParams = translationParams.multiply( translationMatrix );
+
+ AffineTransform at = new AffineTransform(
+ scalingParams.getValue( 0,0), 0,
+ 0, scalingParams.getValue( 1, 1),
+ translationParams.getValue(2,0), translationParams.getValue( 2,1 )
+ );
+
+
+
+
+ //at.setToTranslation( pageSize.getHeight()-ctm.getValue(2,0),ctm.getValue(2,1) );
+ //at.setToScale( ctm.getValue(0,0)/width, ctm.getValue(1,1)/height);
+ //at.setToRotation( (page.findRotation() * Math.PI)/180 );
+
+
+
+ //AffineTransform rotation = new AffineTransform();
+ //rotation.rotate( (90*Math.PI)/180);
+
+ /*
+
+ // The transformation should be done
+ // 1 - Translation
+ // 2 - Rotation
+ // 3 - Scale or Skew
+ AffineTransform at = new AffineTransform();
+
+ // Translation
+ at = new AffineTransform();
+ //at.setToTranslation((double)ctm.getValue(0,0),
+ // (double)ctm.getValue(0,1));
+
+ // Rotation
+ //AffineTransform toAdd = new AffineTransform();
+ toAdd.setToRotation(1.5705);
+ toAdd.setToRotation(ctm.getValue(2,0)*(Math.PI/180));
+ at.concatenate(toAdd);
+ */
+
+ // Scale / Skew?
+ //toAdd.setToScale(1, 1);
+ //at.concatenate(toAdd);
+
+ graphics.drawImage( awtImage, at, null );
+ }
+ catch( Exception e )
+ {
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ log.warn( "Unknown xobject type:" + xobject );
+ }
+
+
+ //invoke named object.
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/LineTo.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/LineTo.java
new file mode 100644
index 0000000..a70099b
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/LineTo.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class LineTo extends OperatorProcessor
+{
+
+
+ /**
+ * process : l : Append straight line segment to path.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+
+ //append straight line segment from the current point to the point.
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+
+ drawer.getLinePath().lineTo( x.floatValue(), (float)drawer.fixY( x.doubleValue(), y.doubleValue()) );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/MoveTo.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/MoveTo.java
new file mode 100644
index 0000000..e1c8f1e
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/MoveTo.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.awt.geom.GeneralPath;
+import java.util.List;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class MoveTo extends OperatorProcessor
+{
+
+
+ /**
+ * process : m : Begin new subpath.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ */
+ public void process(PDFOperator operator, List arguments)
+ {
+ PageDrawer drawer = (PageDrawer)context;
+
+ COSNumber x = (COSNumber)arguments.get( 0 );
+ COSNumber y = (COSNumber)arguments.get( 1 );
+
+ drawer.getLineSubPaths().add( drawer.getLinePath() );
+ GeneralPath newPath = new GeneralPath();
+ newPath.moveTo( x.floatValue(), (float)drawer.fixY( x.doubleValue(), y.doubleValue()) );
+ drawer.setLinePath( newPath );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetLineWidth.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetLineWidth.java
new file mode 100644
index 0000000..4217451
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetLineWidth.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+
+import java.awt.BasicStroke;
+import java.io.IOException;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetLineWidth extends org.pdfbox.util.operator.SetLineWidth
+{
+
+ /**
+ * w Set line width.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ super.process( operator, arguments );
+ float lineWidth = (float)context.getGraphicsState().getLineWidth();
+ if( lineWidth == 0 )
+ {
+ lineWidth = 1;
+ }
+ ((PageDrawer)context).getGraphics().setStroke( new BasicStroke( lineWidth ) );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingCMYKColor.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingCMYKColor.java
new file mode 100644
index 0000000..d1c37fe
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingCMYKColor.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetNonStrokingCMYKColor extends org.pdfbox.util.operator.SetNonStrokingCMYKColor
+{
+ /**
+ * k Set color space for non stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ super.process( operator, arguments );
+ PageDrawer drawer = (PageDrawer)context;
+ PDColorSpaceInstance colorInstance = drawer.getGraphicsState().getNonStrokingColorSpace();
+ Color color = colorInstance.createColor();
+ drawer.setNonStrokingColor( color );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingColorSpace.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingColorSpace.java
new file mode 100644
index 0000000..e5abae8
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingColorSpace.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.util.PDFOperator;
+
+
+import java.awt.Color;
+import java.io.IOException;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetNonStrokingColorSpace extends org.pdfbox.util.operator.SetNonStrokingColorSpace
+{
+ /**
+ * cs Set color space for non stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ try
+ {
+ super.process( operator, arguments );
+ PageDrawer drawer = (PageDrawer)context;
+ PDColorSpaceInstance colorInstance = drawer.getGraphicsState().getNonStrokingColorSpace();
+ Color color = colorInstance.createColor();
+ drawer.setNonStrokingColor( color );
+ }
+ catch( IOException e )
+ {
+ //ignore for now and continue drawing
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingRGBColor.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingRGBColor.java
new file mode 100644
index 0000000..f816d1f
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetNonStrokingRGBColor.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetNonStrokingRGBColor extends org.pdfbox.util.operator.SetNonStrokingRGBColor
+{
+ /**
+ * rg Set color space for non stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ super.process( operator, arguments );
+ PageDrawer drawer = (PageDrawer)context;
+ COSNumber r = (COSNumber)arguments.get( 0 );
+ COSNumber g = (COSNumber)arguments.get( 1 );
+ COSNumber b = (COSNumber)arguments.get( 2 );
+ drawer.setNonStrokingColor( new Color( r.floatValue(), g.floatValue(), b.floatValue() ) );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingCMYKColor.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingCMYKColor.java
new file mode 100644
index 0000000..81fcef3
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingCMYKColor.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetStrokingCMYKColor extends org.pdfbox.util.operator.SetStrokingCMYKColor
+{
+ /**
+ * CS Set color space for stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ super.process( operator, arguments );
+ PageDrawer drawer = (PageDrawer)context;
+ PDColorSpaceInstance colorInstance = drawer.getGraphicsState().getNonStrokingColorSpace();
+ Color color = colorInstance.createColor();
+ drawer.setStrokingColor( color );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingColorSpace.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingColorSpace.java
new file mode 100644
index 0000000..22c9c5c
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingColorSpace.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
+import org.pdfbox.util.PDFOperator;
+
+import java.awt.Color;
+import java.io.IOException;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetStrokingColorSpace extends org.pdfbox.util.operator.SetNonStrokingColorSpace
+{
+ /**
+ * CS Set color space for stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ super.process( operator, arguments );
+ try
+ {
+ PageDrawer drawer = (PageDrawer)context;
+ PDColorSpaceInstance colorInstance = drawer.getGraphicsState().getNonStrokingColorSpace();
+ Color color = colorInstance.createColor();
+ drawer.setStrokingColor( color );
+ }
+ catch( IOException e )
+ {
+ //ignore for now and continue drawing
+ }
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingRGBColor.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingRGBColor.java
new file mode 100644
index 0000000..8d7099e
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/SetStrokingRGBColor.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class SetStrokingRGBColor extends org.pdfbox.util.operator.SetStrokingRGBColor
+{
+ /**
+ * RG Set color space for stroking operations.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ super.process( operator, arguments );
+ PageDrawer drawer = (PageDrawer)context;
+ COSNumber r = (COSNumber)arguments.get( 0 );
+ COSNumber g = (COSNumber)arguments.get( 1 );
+ COSNumber b = (COSNumber)arguments.get( 2 );
+ drawer.setStrokingColor( new Color( r.floatValue(), g.floatValue(), b.floatValue() ) );
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/StrokePath.java b/src/main/java/org/pdfbox/util/operator/pagedrawer/StrokePath.java
new file mode 100644
index 0000000..31a489c
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/StrokePath.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.util.operator.pagedrawer;
+
+import java.util.List;
+import org.pdfbox.pdfviewer.PageDrawer;
+import org.pdfbox.util.PDFOperator;
+
+import java.awt.Graphics2D;
+import java.awt.geom.GeneralPath;
+import java.io.IOException;
+
+/**
+ * Implementation of content stream operator for page drawer.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.1 $
+ */
+public class StrokePath extends org.pdfbox.util.operator.SetLineWidth
+{
+
+ /**
+ * S stroke the path.
+ * @param operator The operator that is being executed.
+ * @param arguments List
+ *
+ * @throws IOException If an error occurs while processing the font.
+ */
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ PageDrawer drawer = (PageDrawer)context;
+ Graphics2D graphics = ((PageDrawer)context).getGraphics();
+ graphics.setColor( drawer.getStrokingColor() );
+ List subPaths = drawer.getLineSubPaths();
+ for( int i=0; i<subPaths.size(); i++ )
+ {
+ GeneralPath subPath = (GeneralPath)subPaths.get( i );
+ graphics.draw( subPath );
+ }
+ subPaths.clear();
+ GeneralPath path = drawer.getLinePath();
+ graphics.draw( path );
+ path.reset();
+ }
+}
diff --git a/src/main/java/org/pdfbox/util/operator/pagedrawer/package.html b/src/main/java/org/pdfbox/util/operator/pagedrawer/package.html
new file mode 100644
index 0000000..edb8444
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/operator/pagedrawer/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains implementations of all of the PDF operators.
+</body>
+</html>
diff --git a/src/main/java/org/pdfbox/util/package.html b/src/main/java/org/pdfbox/util/package.html
new file mode 100644
index 0000000..f8948fb
--- /dev/null
+++ b/src/main/java/org/pdfbox/util/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package contains utility classes that are used by the PDFBox project.
+</body>
+</html>