From 5fa4cb1f2798b068a7c8808a687d9df166a78a80 Mon Sep 17 00:00:00 2001 From: "vincent@cubedesigners.com" Date: Tue, 16 Aug 2011 15:05:50 +0000 Subject: [PATCH] --- fluidbook/tools/fwstk/.classpath | 2 +- fluidbook/tools/fwstk/build.xml | 74 + fluidbook/tools/fwstk/manifest.mf | 3 + .../tools/fwstk/nbproject/build-impl.xml | 1058 +++++++++ .../tools/fwstk/nbproject/genfiles.properties | 8 + .../fwstk/nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 6 + .../tools/fwstk/nbproject/project.properties | 87 + fluidbook/tools/fwstk/nbproject/project.xml | 16 + .../pdfbox/resources/FontMapping.properties | 48 + .../PDFBox_External_Fonts.properties | 19 + .../PDFMarkedContentExtractor.properties | 98 + .../resources/PDFTextStripper.properties | 97 + .../pdfbox/resources/PageDrawer.properties | 92 + .../pdfbox/resources/additional_glyphlist.txt | 160 ++ .../apache/pdfbox/resources/pdfbox.properties | 15 + .../fwstk/src/apache/commons/logging/Log.java | 246 +++ .../logging/LogConfigurationException.java | 98 + .../apache/commons/logging/LogFactory.java | 1843 ++++++++++++++++ .../src/apache/commons/logging/LogSource.java | 262 +++ .../commons/logging/impl/AvalonLogger.java | 292 +++ .../logging/impl/Jdk13LumberjackLogger.java | 335 +++ .../commons/logging/impl/Jdk14Logger.java | 304 +++ .../commons/logging/impl/Log4JLogger.java | 342 +++ .../commons/logging/impl/LogFactoryImpl.java | 1500 +++++++++++++ .../commons/logging/impl/LogKitLogger.java | 294 +++ .../apache/commons/logging/impl/NoOpLog.java | 107 + .../logging/impl/ServletContextCleaner.java | 138 ++ .../commons/logging/impl/SimpleLog.java | 721 +++++++ .../commons/logging/impl/WeakHashtable.java | 478 +++++ .../apache/commons/logging/impl/package.html | 22 + .../src/apache/commons/logging/package.html | 255 +++ .../src/apache/fontbox/afm/AFMParser.java | 1062 ++++++++++ .../src/apache/fontbox/afm/CharMetric.java | 285 +++ .../src/apache/fontbox/afm/Composite.java | 75 + .../src/apache/fontbox/afm/CompositePart.java | 79 + .../src/apache/fontbox/afm/FontMetric.java | 911 ++++++++ .../src/apache/fontbox/afm/KernPair.java | 96 + .../src/apache/fontbox/afm/Ligature.java | 62 + .../src/apache/fontbox/afm/TrackKern.java | 113 + .../fwstk/src/apache/fontbox/afm/package.html | 28 + .../src/apache/fontbox/cff/AFMFormatter.java | 168 ++ .../src/apache/fontbox/cff/CFFDataInput.java | 94 + .../fwstk/src/apache/fontbox/cff/CFFFont.java | 444 ++++ .../src/apache/fontbox/cff/CFFFontROS.java | 189 ++ .../src/apache/fontbox/cff/CFFOperator.java | 261 +++ .../src/apache/fontbox/cff/CFFParser.java | 1198 +++++++++++ .../apache/fontbox/cff/CFFStandardString.java | 435 ++++ .../apache/fontbox/cff/CIDKeyedFDSelect.java | 38 + .../apache/fontbox/cff/CharStringCommand.java | 307 +++ .../fontbox/cff/CharStringConverter.java | 457 ++++ .../apache/fontbox/cff/CharStringHandler.java | 75 + .../fontbox/cff/CharStringRenderer.java | 344 +++ .../src/apache/fontbox/cff/DataInput.java | 178 ++ .../src/apache/fontbox/cff/DataOutput.java | 118 ++ .../src/apache/fontbox/cff/IndexData.java | 107 + .../fontbox/cff/Type1CharStringFormatter.java | 102 + .../fontbox/cff/Type1CharStringParser.java | 104 + .../fontbox/cff/Type1FontFormatter.java | 249 +++ .../src/apache/fontbox/cff/Type1FontUtil.java | 164 ++ .../fontbox/cff/Type2CharStringParser.java | 192 ++ .../fontbox/cff/charset/CFFCharset.java | 149 ++ .../fontbox/cff/charset/CFFExpertCharset.java | 211 ++ .../cff/charset/CFFExpertSubsetCharset.java | 133 ++ .../cff/charset/CFFISOAdobeCharset.java | 275 +++ .../apache/fontbox/cff/charset/package.html | 25 + .../fontbox/cff/encoding/CFFEncoding.java | 150 ++ .../cff/encoding/CFFExpertEncoding.java | 302 +++ .../cff/encoding/CFFStandardEncoding.java | 302 +++ .../apache/fontbox/cff/encoding/package.html | 25 + .../fwstk/src/apache/fontbox/cff/package.html | 25 + .../src/apache/fontbox/cmap/CIDRange.java | 65 + .../fwstk/src/apache/fontbox/cmap/CMap.java | 453 ++++ .../src/apache/fontbox/cmap/CMapParser.java | 645 ++++++ .../apache/fontbox/cmap/CodespaceRange.java | 115 + .../src/apache/fontbox/cmap/package.html | 25 + .../src/apache/fontbox/encoding/Encoding.java | 148 ++ .../fontbox/encoding/MacRomanEncoding.java | 240 +++ .../src/apache/fontbox/encoding/package.html | 25 + .../src/apache/fontbox/pfb/PfbParser.java | 197 ++ .../fwstk/src/apache/fontbox/pfb/package.html | 25 + .../apache/fontbox/ttf/AbstractTTFParser.java | 270 +++ .../fontbox/ttf/CIDFontType2Parser.java | 30 + .../apache/fontbox/ttf/CMAPEncodingEntry.java | 574 +++++ .../src/apache/fontbox/ttf/CMAPTable.java | 108 + .../fontbox/ttf/DigitalSignatureTable.java | 31 + .../src/apache/fontbox/ttf/GlyphData.java | 111 + .../src/apache/fontbox/ttf/GlyphTable.java | 72 + .../src/apache/fontbox/ttf/HeaderTable.java | 318 +++ .../fontbox/ttf/HorizontalHeaderTable.java | 318 +++ .../fontbox/ttf/HorizontalMetricsTable.java | 81 + .../fontbox/ttf/IndexToLocationTable.java | 82 + .../fontbox/ttf/MaximumProfileTable.java | 286 +++ .../fontbox/ttf/MemoryTTFDataStream.java | 208 ++ .../src/apache/fontbox/ttf/NameRecord.java | 228 ++ .../src/apache/fontbox/ttf/NamingTable.java | 98 + .../fontbox/ttf/OS2WindowsMetricsTable.java | 680 ++++++ .../apache/fontbox/ttf/PostScriptTable.java | 294 +++ .../src/apache/fontbox/ttf/RAFDataStream.java | 168 ++ .../src/apache/fontbox/ttf/TTFDataStream.java | 253 +++ .../src/apache/fontbox/ttf/TTFParser.java | 69 + .../src/apache/fontbox/ttf/TTFTable.java | 101 + .../src/apache/fontbox/ttf/TrueTypeFont.java | 208 ++ .../fwstk/src/apache/fontbox/ttf/package.html | 25 + .../src/apache/fontbox/util/BoundingBox.java | 174 ++ .../apache/fontbox/util/ResourceLoader.java | 150 ++ .../src/apache/fontbox/util/package.html | 25 + .../apache/jempbox/impl/DateConverter.java | 260 +++ .../src/apache/jempbox/impl/XMLUtil.java | 435 ++++ .../src/apache/jempbox/impl/package.html | 25 + .../src/apache/jempbox/xmp/Elementable.java | 35 + .../src/apache/jempbox/xmp/ResourceEvent.java | 322 +++ .../src/apache/jempbox/xmp/ResourceRef.java | 257 +++ .../src/apache/jempbox/xmp/Thumbnail.java | 154 ++ .../src/apache/jempbox/xmp/XMPMetadata.java | 776 +++++++ .../src/apache/jempbox/xmp/XMPSchema.java | 1084 ++++++++++ .../apache/jempbox/xmp/XMPSchemaBasic.java | 374 ++++ .../jempbox/xmp/XMPSchemaBasicJobTicket.java | 53 + .../jempbox/xmp/XMPSchemaDublinCore.java | 531 +++++ .../jempbox/xmp/XMPSchemaDynamicMedia.java | 53 + .../jempbox/xmp/XMPSchemaIptc4xmpCore.java | 314 +++ .../jempbox/xmp/XMPSchemaMediaManagement.java | 283 +++ .../src/apache/jempbox/xmp/XMPSchemaPDF.java | 114 + .../jempbox/xmp/XMPSchemaPagedText.java | 53 + .../jempbox/xmp/XMPSchemaPhotoshop.java | 358 ++++ .../xmp/XMPSchemaRightsManagement.java | 220 ++ .../fwstk/src/apache/jempbox/xmp/package.html | 25 + .../jempbox/xmp/pdfa/XMPMetadataPDFA.java | 191 ++ .../jempbox/xmp/pdfa/XMPSchemaPDFAField.java | 58 + .../jempbox/xmp/pdfa/XMPSchemaPDFAId.java | 119 ++ .../xmp/pdfa/XMPSchemaPDFAProperty.java | 57 + .../jempbox/xmp/pdfa/XMPSchemaPDFASchema.java | 98 + .../jempbox/xmp/pdfa/XMPSchemaPDFAType.java | 57 + .../src/apache/jempbox/xmp/pdfa/package.html | 25 + .../src/apache/pdfbox/ConvertColorspace.java | 470 ++++ .../fwstk/src/apache/pdfbox/Decrypt.java | 190 ++ .../fwstk/src/apache/pdfbox/Encrypt.java | 230 ++ .../fwstk/src/apache/pdfbox/ExportFDF.java | 141 ++ .../fwstk/src/apache/pdfbox/ExportXFDF.java | 141 ++ .../src/apache/pdfbox/ExtractImages.java | 208 ++ .../fwstk/src/apache/pdfbox/ExtractText.java | 311 +++ .../fwstk/src/apache/pdfbox/ImportFDF.java | 142 ++ .../fwstk/src/apache/pdfbox/ImportXFDF.java | 143 ++ .../fwstk/src/apache/pdfbox/Overlay.java | 504 +++++ .../tools/fwstk/src/apache/pdfbox/PDFBox.java | 87 + .../fwstk/src/apache/pdfbox/PDFDebugger.java | 435 ++++ .../fwstk/src/apache/pdfbox/PDFMerger.java | 82 + .../fwstk/src/apache/pdfbox/PDFReader.java | 359 ++++ .../fwstk/src/apache/pdfbox/PDFSplit.java | 219 ++ .../fwstk/src/apache/pdfbox/PDFToImage.java | 308 +++ .../src/apache/pdfbox/PdfDecompressor.java | 89 + .../fwstk/src/apache/pdfbox/PrintPDF.java | 152 ++ .../fwstk/src/apache/pdfbox/TextToPDF.java | 268 +++ .../fwstk/src/apache/pdfbox/Version.java | 87 + .../src/apache/pdfbox/WriteDecodedDoc.java | 137 ++ .../fwstk/src/apache/pdfbox/cos/COSArray.java | 556 +++++ .../fwstk/src/apache/pdfbox/cos/COSBase.java | 110 + .../src/apache/pdfbox/cos/COSBoolean.java | 147 ++ .../src/apache/pdfbox/cos/COSDictionary.java | 1431 +++++++++++++ .../pdfbox/cos/COSDictionaryLateBinding.java | 61 + .../src/apache/pdfbox/cos/COSDocument.java | 626 ++++++ .../fwstk/src/apache/pdfbox/cos/COSFloat.java | 169 ++ .../src/apache/pdfbox/cos/COSInteger.java | 235 ++ .../fwstk/src/apache/pdfbox/cos/COSName.java | 1382 ++++++++++++ .../fwstk/src/apache/pdfbox/cos/COSNull.java | 74 + .../src/apache/pdfbox/cos/COSNumber.java | 102 + .../src/apache/pdfbox/cos/COSObject.java | 208 ++ .../src/apache/pdfbox/cos/COSStream.java | 445 ++++ .../src/apache/pdfbox/cos/COSString.java | 459 ++++ .../src/apache/pdfbox/cos/COSUnread.java | 100 + .../src/apache/pdfbox/cos/ICOSVisitor.java | 118 ++ .../fwstk/src/apache/pdfbox/cos/package.html | 28 + .../apache/pdfbox/encoding/AFMEncoding.java | 61 + .../pdfbox/encoding/DictionaryEncoding.java | 98 + .../src/apache/pdfbox/encoding/Encoding.java | 339 +++ .../pdfbox/encoding/EncodingManager.java | 70 + .../pdfbox/encoding/MacRomanEncoding.java | 261 +++ .../pdfbox/encoding/PdfDocEncoding.java | 283 +++ .../pdfbox/encoding/StandardEncoding.java | 203 ++ .../apache/pdfbox/encoding/Type1Encoding.java | 43 + .../pdfbox/encoding/WinAnsiEncoding.java | 270 +++ .../encoding/conversion/CJKConverter.java | 113 + .../encoding/conversion/CJKEncoding.java | 200 ++ .../encoding/conversion/CMapSubstitution.java | 84 + .../conversion/EncodingConversionManager.java | 72 + .../conversion/EncodingConverter.java | 49 + .../pdfbox/encoding/conversion/package.html | 25 + .../src/apache/pdfbox/encoding/package.html | 25 + .../src/apache/pdfbox/encryption/ARCFour.java | 168 ++ .../pdfbox/encryption/DocumentEncryption.java | 409 ++++ .../pdfbox/encryption/PDFEncryption.java | 589 ++++++ .../src/apache/pdfbox/encryption/package.html | 25 + .../pdfbox/examples/AbstractExample.java | 111 + .../pdfbox/examples/fdf/PrintFields.java | 149 ++ .../apache/pdfbox/examples/fdf/SetField.java | 114 + .../apache/pdfbox/examples/fdf/package.html | 25 + .../src/apache/pdfbox/examples/package.html | 25 + .../examples/pdmodel/AddImageToPDF.java | 134 ++ .../examples/pdmodel/AddJavascript.java | 82 + .../pdmodel/AddMessageToEachPage.java | 151 ++ .../pdmodel/AddMetadataFromDocInfo.java | 115 + .../pdfbox/examples/pdmodel/Annotation.java | 262 +++ .../examples/pdmodel/CreateBlankPDF.java | 94 + .../examples/pdmodel/CreateBookmarks.java | 105 + .../examples/pdmodel/CreateLandscapePDF.java | 135 ++ .../examples/pdmodel/EmbeddedFiles.java | 155 ++ .../examples/pdmodel/ExtractMetadata.java | 189 ++ .../pdmodel/GoToSecondBookmarkOnOpen.java | 104 + .../pdfbox/examples/pdmodel/HelloWorld.java | 123 ++ .../examples/pdmodel/HelloWorldTTF.java | 120 ++ .../pdmodel/HelloWorldType1AfmPfb.java | 120 ++ .../pdfbox/examples/pdmodel/ImageToPDF.java | 132 ++ .../examples/pdmodel/PrintBookmarks.java | 128 ++ .../pdmodel/PrintDocumentMetaData.java | 151 ++ .../pdfbox/examples/pdmodel/PrintURLs.java | 141 ++ .../examples/pdmodel/RemoveFirstPage.java | 84 + .../examples/pdmodel/ReplaceString.java | 172 ++ .../pdfbox/examples/pdmodel/ReplaceURLs.java | 113 + .../pdfbox/examples/pdmodel/RubberStamp.java | 99 + .../pdmodel/RubberStampWithImage.java | 197 ++ .../examples/pdmodel/ShowColorBoxes.java | 121 ++ .../examples/pdmodel/UsingTextMatrix.java | 182 ++ .../pdfbox/examples/pdmodel/package.html | 25 + .../pdfbox/examples/persistence/CopyDoc.java | 126 ++ .../pdfbox/examples/persistence/package.html | 25 + .../examples/signature/ShowSignature.java | 160 ++ .../pdfbox/examples/signature/package.html | 25 + .../examples/util/ExtractTextByArea.java | 105 + .../examples/util/PrintImageLocations.java | 172 ++ .../examples/util/PrintTextLocations.java | 129 ++ .../pdfbox/examples/util/RemoveAllText.java | 122 ++ .../apache/pdfbox/examples/util/package.html | 25 + .../exceptions/COSVisitorException.java | 39 + .../exceptions/CryptographyException.java | 68 + .../exceptions/InvalidPasswordException.java | 37 + .../exceptions/OutlineNotLocalException.java | 41 + .../pdfbox/exceptions/SignatureException.java | 95 + .../pdfbox/exceptions/WrappedException.java | 61 + .../pdfbox/exceptions/WrappedIOException.java | 50 + .../src/apache/pdfbox/exceptions/package.html | 25 + .../apache/pdfbox/filter/ASCII85Filter.java | 79 + .../apache/pdfbox/filter/ASCIIHexFilter.java | 230 ++ .../pdfbox/filter/CCITTFaxDecodeFilter.java | 134 ++ .../src/apache/pdfbox/filter/CryptFilter.java | 68 + .../src/apache/pdfbox/filter/DCTFilter.java | 58 + .../src/apache/pdfbox/filter/Filter.java | 58 + .../apache/pdfbox/filter/FilterManager.java | 127 ++ .../src/apache/pdfbox/filter/FlateFilter.java | 354 ++++ .../apache/pdfbox/filter/IdentityFilter.java | 64 + .../src/apache/pdfbox/filter/JBIG2Filter.java | 90 + .../src/apache/pdfbox/filter/JPXFilter.java | 76 + .../apache/pdfbox/filter/LZWDictionary.java | 201 ++ .../src/apache/pdfbox/filter/LZWFilter.java | 211 ++ .../src/apache/pdfbox/filter/LZWNode.java | 101 + .../pdfbox/filter/RunLengthDecodeFilter.java | 106 + .../apache/pdfbox/filter/TIFFFaxDecoder.java | 1446 +++++++++++++ .../src/apache/pdfbox/filter/package.html | 25 + .../apache/pdfbox/io/ASCII85InputStream.java | 255 +++ .../apache/pdfbox/io/ASCII85OutputStream.java | 290 +++ .../io/ByteArrayPushBackInputStream.java | 390 ++++ .../pdfbox/io/FastByteArrayOutputStream.java | 48 + .../src/apache/pdfbox/io/NBitInputStream.java | 110 + .../apache/pdfbox/io/NBitOutputStream.java | 102 + .../apache/pdfbox/io/PushBackInputStream.java | 201 ++ .../src/apache/pdfbox/io/RandomAccess.java | 94 + .../apache/pdfbox/io/RandomAccessBuffer.java | 155 ++ .../apache/pdfbox/io/RandomAccessFile.java | 101 + .../io/RandomAccessFileInputStream.java | 117 + .../io/RandomAccessFileOutputStream.java | 141 ++ .../fwstk/src/apache/pdfbox/io/package.html | 25 + .../fwstk/src/apache/pdfbox/package.html | 25 + .../apache/pdfbox/pdfparser/BaseParser.java | 1402 ++++++++++++ .../pdfbox/pdfparser/ConformingPDFParser.java | 696 ++++++ .../pdfparser/PDFObjectStreamParser.java | 134 ++ .../apache/pdfbox/pdfparser/PDFParser.java | 892 ++++++++ .../pdfbox/pdfparser/PDFStreamParser.java | 484 +++++ .../pdfbox/pdfparser/PDFXrefStreamParser.java | 167 ++ .../pdfparser/VisualSignatureParser.java | 244 +++ .../pdfbox/pdfparser/XrefTrailerResolver.java | 210 ++ .../src/apache/pdfbox/pdfparser/package.html | 25 + .../apache/pdfbox/pdfviewer/ArrayEntry.java | 69 + .../src/apache/pdfbox/pdfviewer/MapEntry.java | 91 + .../apache/pdfbox/pdfviewer/PDFPagePanel.java | 103 + .../pdfbox/pdfviewer/PDFTreeCellRenderer.java | 130 ++ .../apache/pdfbox/pdfviewer/PDFTreeModel.java | 319 +++ .../apache/pdfbox/pdfviewer/PageDrawer.java | 547 +++++ .../apache/pdfbox/pdfviewer/PageWrapper.java | 104 + .../pdfbox/pdfviewer/ReaderBottomPanel.java | 72 + .../src/apache/pdfbox/pdfviewer/package.html | 25 + .../pdfwriter/COSFilterInputStream.java | 119 ++ .../pdfwriter/COSStandardOutputStream.java | 224 ++ .../apache/pdfbox/pdfwriter/COSWriter.java | 1362 ++++++++++++ .../pdfbox/pdfwriter/COSWriterXRefEntry.java | 163 ++ .../pdfbox/pdfwriter/ContentStreamWriter.java | 180 ++ .../src/apache/pdfbox/pdfwriter/package.html | 25 + .../pdfbox/pdmodel/ConformingPDDocument.java | 115 + .../pdmodel/PDDestinationNameTreeNode.java | 78 + .../src/apache/pdfbox/pdmodel/PDDocument.java | 1378 ++++++++++++ .../pdfbox/pdmodel/PDDocumentCatalog.java | 630 ++++++ .../pdfbox/pdmodel/PDDocumentInformation.java | 316 +++ .../pdmodel/PDDocumentNameDictionary.java | 180 ++ .../pdmodel/PDEmbeddedFilesNameTreeNode.java | 68 + .../pdmodel/PDJavascriptNameTreeNode.java | 83 + .../src/apache/pdfbox/pdmodel/PDPage.java | 850 ++++++++ .../src/apache/pdfbox/pdmodel/PDPageNode.java | 467 ++++ .../src/apache/pdfbox/pdmodel/PDPageable.java | 190 ++ .../apache/pdfbox/pdmodel/PDResources.java | 322 +++ .../pdfbox/pdmodel/common/COSArrayList.java | 676 ++++++ .../pdmodel/common/COSDictionaryMap.java | 260 +++ .../pdfbox/pdmodel/common/COSObjectable.java | 35 + .../pdfbox/pdmodel/common/COSStreamArray.java | 313 +++ .../pdmodel/common/DualCOSObjectable.java | 42 + .../pdmodel/common/PDDestinationOrAction.java | 30 + .../pdmodel/common/PDDictionaryWrapper.java | 92 + .../pdfbox/pdmodel/common/PDMatrix.java | 115 + .../pdfbox/pdmodel/common/PDMemoryStream.java | 270 +++ .../pdfbox/pdmodel/common/PDMetadata.java | 102 + .../pdfbox/pdmodel/common/PDNameTreeNode.java | 333 +++ .../pdmodel/common/PDNamedTextStream.java | 123 ++ .../pdmodel/common/PDNumberTreeNode.java | 325 +++ .../pdfbox/pdmodel/common/PDObjectStream.java | 138 ++ .../pdmodel/common/PDPageLabelRange.java | 192 ++ .../pdfbox/pdmodel/common/PDPageLabels.java | 405 ++++ .../apache/pdfbox/pdmodel/common/PDRange.java | 132 ++ .../pdfbox/pdmodel/common/PDRectangle.java | 281 +++ .../pdfbox/pdmodel/common/PDStream.java | 550 +++++ .../pdfbox/pdmodel/common/PDTextStream.java | 166 ++ .../common/PDTypedDictionaryWrapper.java | 66 + .../pdfbox/pdmodel/common/XrefEntry.java | 43 + .../PDComplexFileSpecification.java | 312 +++ .../filespecification/PDEmbeddedFile.java | 297 +++ .../PDFileSpecification.java | 81 + .../PDSimpleFileSpecification.java | 81 + .../common/filespecification/package.html | 25 + .../pdmodel/common/function/PDFunction.java | 335 +++ .../common/function/PDFunctionType0.java | 360 ++++ .../common/function/PDFunctionType2.java | 134 ++ .../common/function/PDFunctionType3.java | 168 ++ .../common/function/PDFunctionType4.java | 61 + .../pdmodel/common/function/package.html | 25 + .../apache/pdfbox/pdmodel/common/package.html | 25 + .../logicalstructure/PDAttributeObject.java | 248 +++ .../PDDefaultAttributeObject.java | 133 ++ .../logicalstructure/PDMarkInfo.java | 135 ++ .../PDMarkedContentReference.java | 125 ++ .../logicalstructure/PDObjectReference.java | 124 ++ .../logicalstructure/PDStructureElement.java | 755 +++++++ .../logicalstructure/PDStructureNode.java | 428 ++++ .../logicalstructure/PDStructureTreeRoot.java | 136 ++ .../PDUserAttributeObject.java | 135 ++ .../logicalstructure/PDUserProperty.java | 191 ++ .../logicalstructure/Revisions.java | 141 ++ .../logicalstructure/package.html | 26 + .../markedcontent/PDMarkedContent.java | 203 ++ .../markedcontent/package.html | 26 + .../prepress/PDBoxStyle.java | 208 ++ .../documentinterchange/prepress/package.html | 25 + .../taggedpdf/PDArtifactMarkedContent.java | 147 ++ .../PDExportFormatAttributeObject.java | 266 +++ .../taggedpdf/PDFourColours.java | 186 ++ .../taggedpdf/PDLayoutAttributeObject.java | 1715 +++++++++++++++ .../taggedpdf/PDListAttributeObject.java | 140 ++ .../PDPrintFieldAttributeObject.java | 178 ++ .../taggedpdf/PDStandardAttributeObject.java | 449 ++++ .../taggedpdf/PDTableAttributeObject.java | 222 ++ .../taggedpdf/StandardStructureTypes.java | 320 +++ .../taggedpdf/package.html | 26 + .../pdmodel/edit/PDPageContentStream.java | 1355 ++++++++++++ .../apache/pdfbox/pdmodel/edit/package.html | 25 + .../pdmodel/encryption/AccessPermission.java | 422 ++++ .../BadSecurityHandlerException.java | 58 + .../encryption/DecryptionMaterial.java | 30 + .../encryption/PDCryptFilterDictionary.java | 115 + .../encryption/PDEncryptionDictionary.java | 433 ++++ .../encryption/PDEncryptionManager.java | 119 ++ .../encryption/PDStandardEncryption.java | 403 ++++ .../pdmodel/encryption/ProtectionPolicy.java | 65 + .../PublicKeyDecryptionMaterial.java | 153 ++ .../encryption/PublicKeyProtectionPolicy.java | 148 ++ .../encryption/PublicKeyRecipient.java | 75 + .../encryption/PublicKeySecurityHandler.java | 407 ++++ .../pdmodel/encryption/SecurityHandler.java | 530 +++++ .../encryption/SecurityHandlersManager.java | 224 ++ .../StandardDecryptionMaterial.java | 67 + .../encryption/StandardProtectionPolicy.java | 122 ++ .../encryption/StandardSecurityHandler.java | 900 ++++++++ .../pdfbox/pdmodel/encryption/package.html | 25 + .../pdfbox/pdmodel/fdf/FDFAnnotation.java | 608 ++++++ .../pdmodel/fdf/FDFAnnotationCaret.java | 69 + .../pdmodel/fdf/FDFAnnotationCircle.java | 69 + .../fdf/FDFAnnotationFileAttachment.java | 69 + .../pdmodel/fdf/FDFAnnotationFreeText.java | 69 + .../pdmodel/fdf/FDFAnnotationHighlight.java | 69 + .../pdfbox/pdmodel/fdf/FDFAnnotationInk.java | 69 + .../pdfbox/pdmodel/fdf/FDFAnnotationLine.java | 69 + .../pdmodel/fdf/FDFAnnotationPolygon.java | 69 + .../pdmodel/fdf/FDFAnnotationPolyline.java | 69 + .../pdmodel/fdf/FDFAnnotationSound.java | 69 + .../pdmodel/fdf/FDFAnnotationSquare.java | 69 + .../pdmodel/fdf/FDFAnnotationSquiggly.java | 69 + .../pdmodel/fdf/FDFAnnotationStamp.java | 69 + .../pdmodel/fdf/FDFAnnotationStrikeOut.java | 69 + .../pdfbox/pdmodel/fdf/FDFAnnotationText.java | 69 + .../pdmodel/fdf/FDFAnnotationUnderline.java | 69 + .../apache/pdfbox/pdmodel/fdf/FDFCatalog.java | 181 ++ .../pdfbox/pdmodel/fdf/FDFDictionary.java | 480 +++++ .../pdfbox/pdmodel/fdf/FDFDocument.java | 363 ++++ .../apache/pdfbox/pdmodel/fdf/FDFField.java | 749 +++++++ .../apache/pdfbox/pdmodel/fdf/FDFIconFit.java | 213 ++ .../pdfbox/pdmodel/fdf/FDFJavaScript.java | 157 ++ .../pdmodel/fdf/FDFNamedPageReference.java | 117 + .../pdfbox/pdmodel/fdf/FDFOptionElement.java | 115 + .../apache/pdfbox/pdmodel/fdf/FDFPage.java | 134 ++ .../pdfbox/pdmodel/fdf/FDFPageInfo.java | 71 + .../pdfbox/pdmodel/fdf/FDFTemplate.java | 153 ++ .../apache/pdfbox/pdmodel/fdf/package.html | 25 + .../pdfbox/pdmodel/font/FontManager.java | 305 +++ .../apache/pdfbox/pdmodel/font/PDCIDFont.java | 357 ++++ .../pdmodel/font/PDCIDFontType0Font.java | 83 + .../pdmodel/font/PDCIDFontType2Font.java | 94 + .../apache/pdfbox/pdmodel/font/PDFont.java | 843 ++++++++ .../pdfbox/pdmodel/font/PDFontDescriptor.java | 531 +++++ .../pdmodel/font/PDFontDescriptorAFM.java | 432 ++++ .../font/PDFontDescriptorDictionary.java | 605 ++++++ .../pdfbox/pdmodel/font/PDFontFactory.java | 142 ++ .../pdfbox/pdmodel/font/PDMMType1Font.java | 48 + .../pdfbox/pdmodel/font/PDSimpleFont.java | 450 ++++ .../pdfbox/pdmodel/font/PDTrueTypeFont.java | 485 +++++ .../pdfbox/pdmodel/font/PDType0Font.java | 190 ++ .../pdmodel/font/PDType1AfmPfbFont.java | 246 +++ .../pdfbox/pdmodel/font/PDType1CFont.java | 608 ++++++ .../pdfbox/pdmodel/font/PDType1Font.java | 432 ++++ .../pdfbox/pdmodel/font/PDType3Font.java | 129 ++ .../pdmodel/font/Type3StreamParser.java | 594 ++++++ .../apache/pdfbox/pdmodel/font/package.html | 25 + .../graphics/PDExtendedGraphicsState.java | 564 +++++ .../pdmodel/graphics/PDFontSetting.java | 119 ++ .../pdmodel/graphics/PDGraphicsState.java | 507 +++++ .../pdmodel/graphics/PDLineDashPattern.java | 178 ++ .../pdfbox/pdmodel/graphics/PDShading.java | 245 +++ .../graphics/color/ColorSpaceCMYK.java | 150 ++ .../graphics/color/ColorSpaceCalRGB.java | 142 ++ .../pdmodel/graphics/color/PDCalGray.java | 226 ++ .../pdmodel/graphics/color/PDCalRGB.java | 281 +++ .../pdmodel/graphics/color/PDColorSpace.java | 120 ++ .../graphics/color/PDColorSpaceFactory.java | 252 +++ .../pdmodel/graphics/color/PDColorState.java | 288 +++ .../pdmodel/graphics/color/PDDeviceCMYK.java | 110 + .../pdmodel/graphics/color/PDDeviceGray.java | 96 + .../pdmodel/graphics/color/PDDeviceN.java | 288 +++ .../graphics/color/PDDeviceNAttributes.java | 110 + .../pdmodel/graphics/color/PDDeviceRGB.java | 114 + .../pdmodel/graphics/color/PDGamma.java | 137 ++ .../pdmodel/graphics/color/PDICCBased.java | 349 +++ .../pdmodel/graphics/color/PDIndexed.java | 288 +++ .../pdfbox/pdmodel/graphics/color/PDLab.java | 286 +++ .../pdmodel/graphics/color/PDPattern.java | 108 + .../pdmodel/graphics/color/PDSeparation.java | 241 +++ .../pdmodel/graphics/color/PDTristimulus.java | 141 ++ .../pdmodel/graphics/color/package.html | 25 + .../PDOptionalContentGroup.java | 93 + .../PDOptionalContentProperties.java | 366 ++++ .../pdfbox/pdmodel/graphics/package.html | 25 + .../pdmodel/graphics/predictor/Average.java | 65 + .../pdmodel/graphics/predictor/None.java | 88 + .../pdmodel/graphics/predictor/Optimum.java | 137 ++ .../pdmodel/graphics/predictor/Paeth.java | 106 + .../predictor/PredictorAlgorithm.java | 322 +++ .../pdmodel/graphics/predictor/Sub.java | 72 + .../pdfbox/pdmodel/graphics/predictor/Up.java | 86 + .../pdmodel/graphics/predictor/package.html | 26 + .../graphics/xobject/CompositeImage.java | 104 + .../pdmodel/graphics/xobject/PDCcitt.java | 706 +++++++ .../graphics/xobject/PDInlinedImage.java | 203 ++ .../pdmodel/graphics/xobject/PDJpeg.java | 375 ++++ .../pdmodel/graphics/xobject/PDPixelMap.java | 366 ++++ .../pdmodel/graphics/xobject/PDXObject.java | 198 ++ .../graphics/xobject/PDXObjectForm.java | 189 ++ .../graphics/xobject/PDXObjectImage.java | 370 ++++ .../pdmodel/graphics/xobject/package.html | 25 + .../interactive/action/PDActionFactory.java | 82 + .../action/PDAdditionalActions.java | 92 + .../action/PDAnnotationAdditionalActions.java | 366 ++++ .../PDDocumentCatalogAdditionalActions.java | 224 ++ .../action/PDFormFieldAdditionalActions.java | 202 ++ .../action/PDPageAdditionalActions.java | 136 ++ .../pdmodel/interactive/action/package.html | 25 + .../interactive/action/type/PDAction.java | 174 ++ .../interactive/action/type/PDActionGoTo.java | 78 + .../action/type/PDActionJavaScript.java | 88 + .../action/type/PDActionLaunch.java | 234 ++ .../action/type/PDActionRemoteGoTo.java | 177 ++ .../interactive/action/type/PDActionURI.java | 173 ++ .../action/type/PDURIDictionary.java | 100 + .../action/type/PDWindowsLaunchParams.java | 166 ++ .../interactive/action/type/package.html | 25 + .../interactive/annotation/PDAnnotation.java | 644 ++++++ .../PDAnnotationFileAttachment.java | 118 ++ .../annotation/PDAnnotationLine.java | 493 +++++ .../annotation/PDAnnotationLink.java | 252 +++ .../annotation/PDAnnotationMarkup.java | 343 +++ .../annotation/PDAnnotationPopup.java | 114 + .../annotation/PDAnnotationRubberStamp.java | 140 ++ .../annotation/PDAnnotationSquareCircle.java | 225 ++ .../annotation/PDAnnotationText.java | 184 ++ .../annotation/PDAnnotationTextMarkup.java | 137 ++ .../annotation/PDAnnotationUnknown.java | 40 + .../annotation/PDAnnotationWidget.java | 241 +++ ...PDAppearanceCharacteristicsDictionary.java | 243 +++ .../annotation/PDAppearanceDictionary.java | 231 ++ .../annotation/PDAppearanceStream.java | 132 ++ .../annotation/PDBorderEffectDictionary.java | 132 ++ .../annotation/PDBorderStyleDictionary.java | 183 ++ .../annotation/PDExternalDataDictionary.java | 100 + .../interactive/annotation/package.html | 25 + .../digitalsignature/PDSignature.java | 330 +++ .../digitalsignature/SignatureInterface.java | 41 + .../digitalsignature/SignatureOptions.java | 101 + .../interactive/digitalsignature/package.html | 25 + .../destination/PDDestination.java | 111 + .../destination/PDNamedDestination.java | 128 ++ .../destination/PDPageDestination.java | 178 ++ .../destination/PDPageFitDestination.java | 88 + .../PDPageFitHeightDestination.java | 118 ++ .../PDPageFitRectangleDestination.java | 174 ++ .../PDPageFitWidthDestination.java | 120 ++ .../destination/PDPageXYZDestination.java | 145 ++ .../destination/package.html | 25 + .../outline/PDDocumentOutline.java | 48 + .../outline/PDOutlineItem.java | 418 ++++ .../outline/PDOutlineNode.java | 312 +++ .../documentnavigation/outline/package.html | 25 + .../documentnavigation/package.html | 25 + .../pdmodel/interactive/form/PDAcroForm.java | 385 ++++ .../interactive/form/PDAppearance.java | 641 ++++++ .../pdmodel/interactive/form/PDCheckbox.java | 168 ++ .../interactive/form/PDChoiceButton.java | 81 + .../interactive/form/PDChoiceField.java | 113 + .../pdmodel/interactive/form/PDField.java | 649 ++++++ .../interactive/form/PDFieldFactory.java | 204 ++ .../interactive/form/PDPushButton.java | 70 + .../interactive/form/PDRadioCollection.java | 165 ++ .../pdmodel/interactive/form/PDSignature.java | 76 + .../interactive/form/PDSignatureField.java | 165 ++ .../pdmodel/interactive/form/PDTextbox.java | 50 + .../interactive/form/PDUnknownField.java | 58 + .../interactive/form/PDVariableText.java | 310 +++ .../pdmodel/interactive/form/PDXFA.java | 49 + .../pdmodel/interactive/form/package.html | 25 + .../measurement/PDMeasureDictionary.java | 107 + .../measurement/PDNumberFormatDictionary.java | 333 +++ .../PDRectlinearMeasureDictionary.java | 344 +++ .../measurement/PDViewportDictionary.java | 159 ++ .../interactive/measurement/package.html | 25 + .../interactive/pagenavigation/PDThread.java | 138 ++ .../pagenavigation/PDThreadBead.java | 220 ++ .../interactive/pagenavigation/package.html | 25 + .../PDViewerPreferences.java | 351 +++ .../viewerpreferences/package.html | 25 + .../pdmodel/markedcontent/PDPropertyList.java | 93 + .../src/apache/pdfbox/pdmodel/package.html | 25 + .../pdfbox/pdmodel/text/PDTextState.java | 272 +++ .../apache/pdfbox/pdmodel/text/package.html | 25 + .../pdfbox/persistence/util/COSHEXTable.java | 557 +++++ .../pdfbox/persistence/util/COSObjectKey.java | 145 ++ .../pdfbox/persistence/util/package.html | 25 + .../src/apache/pdfbox/util/BitFlagHelper.java | 104 + .../src/apache/pdfbox/util/DateConverter.java | 359 ++++ .../src/apache/pdfbox/util/ErrorLogger.java | 58 + .../pdfbox/util/ExtensionFileFilter.java | 75 + .../src/apache/pdfbox/util/ICU4JImpl.java | 152 ++ .../apache/pdfbox/util/ImageParameters.java | 235 ++ .../src/apache/pdfbox/util/LayerUtility.java | 320 +++ .../fwstk/src/apache/pdfbox/util/MapUtil.java | 51 + .../fwstk/src/apache/pdfbox/util/Matrix.java | 415 ++++ .../apache/pdfbox/util/PDFCloneUtility.java | 244 +++ .../apache/pdfbox/util/PDFHighlighter.java | 230 ++ .../apache/pdfbox/util/PDFImageWriter.java | 256 +++ .../util/PDFMarkedContentExtractor.java | 269 +++ .../apache/pdfbox/util/PDFMergerUtility.java | 442 ++++ .../src/apache/pdfbox/util/PDFOperator.java | 139 ++ .../apache/pdfbox/util/PDFStreamEngine.java | 713 +++++++ .../src/apache/pdfbox/util/PDFText2HTML.java | 235 ++ .../apache/pdfbox/util/PDFTextStripper.java | 1882 +++++++++++++++++ .../pdfbox/util/PDFTextStripperByArea.java | 193 ++ .../src/apache/pdfbox/util/PageExtractor.java | 120 ++ .../apache/pdfbox/util/PositionWrapper.java | 138 ++ .../apache/pdfbox/util/ResourceLoader.java | 156 ++ .../src/apache/pdfbox/util/Splitter.java | 188 ++ .../src/apache/pdfbox/util/StringUtil.java | 39 + .../src/apache/pdfbox/util/TextNormalize.java | 180 ++ .../src/apache/pdfbox/util/TextPosition.java | 747 +++++++ .../pdfbox/util/TextPositionComparator.java | 90 + .../fwstk/src/apache/pdfbox/util/XMLUtil.java | 89 + .../operator/BeginMarkedContentSequence.java | 56 + ...inMarkedContentSequenceWithProperties.java | 61 + .../pdfbox/util/operator/BeginText.java | 44 + .../util/operator/CloseAndStrokePath.java | 47 + .../pdfbox/util/operator/Concatenate.java | 67 + .../operator/EndMarkedContentSequence.java | 46 + .../apache/pdfbox/util/operator/EndText.java | 42 + .../apache/pdfbox/util/operator/GRestore.java | 41 + .../apache/pdfbox/util/operator/GSave.java | 43 + .../apache/pdfbox/util/operator/Invoke.java | 77 + .../pdfbox/util/operator/MoveAndShow.java | 47 + .../apache/pdfbox/util/operator/MoveText.java | 49 + .../util/operator/MoveTextSetLeading.java | 54 + .../apache/pdfbox/util/operator/NextLine.java | 53 + .../util/operator/OperatorProcessor.java | 73 + .../pdfbox/util/operator/SetCharSpacing.java | 53 + .../operator/SetGraphicsStateParameters.java | 52 + .../operator/SetHorizontalTextScaling.java | 49 + .../pdfbox/util/operator/SetLineCapStyle.java | 48 + .../util/operator/SetLineDashPattern.java | 52 + .../util/operator/SetLineJoinStyle.java | 48 + .../util/operator/SetLineMiterLimit.java | 49 + .../pdfbox/util/operator/SetLineWidth.java | 49 + .../pdfbox/util/operator/SetMatrix.java | 59 + .../pdfbox/util/operator/SetMoveAndShow.java | 47 + .../operator/SetNonStrokingCMYKColor.java | 55 + .../operator/SetNonStrokingCalRGBColor.java | 51 + .../util/operator/SetNonStrokingColor.java | 106 + .../operator/SetNonStrokingColorSpace.java | 69 + .../util/operator/SetNonStrokingDeviceN.java | 105 + .../operator/SetNonStrokingGrayColor.java | 59 + .../operator/SetNonStrokingICCBasedColor.java | 53 + .../util/operator/SetNonStrokingRGBColor.java | 55 + .../operator/SetNonStrokingSeparation.java | 56 + .../util/operator/SetStrokingCMYKColor.java | 55 + .../util/operator/SetStrokingCalRGBColor.java | 53 + .../util/operator/SetStrokingColor.java | 106 + .../util/operator/SetStrokingColorSpace.java | 72 + .../util/operator/SetStrokingDeviceN.java | 105 + .../util/operator/SetStrokingGrayColor.java | 59 + .../operator/SetStrokingICCBasedColor.java | 53 + .../util/operator/SetStrokingRGBColor.java | 55 + .../util/operator/SetStrokingSeparation.java | 56 + .../pdfbox/util/operator/SetTextFont.java | 62 + .../pdfbox/util/operator/SetTextLeading.java | 43 + .../util/operator/SetTextRenderingMode.java | 49 + .../pdfbox/util/operator/SetTextRise.java | 49 + .../pdfbox/util/operator/SetWordSpacing.java | 44 + .../apache/pdfbox/util/operator/ShowText.java | 48 + .../pdfbox/util/operator/ShowTextGlyph.java | 71 + .../apache/pdfbox/util/operator/package.html | 25 + .../pagedrawer/AppendRectangleToPath.java | 77 + .../operator/pagedrawer/BeginInlineImage.java | 106 + .../operator/pagedrawer/ClipEvenOddRule.java | 64 + .../operator/pagedrawer/ClipNonZeroRule.java | 64 + .../CloseFillEvenOddAndStrokePath.java | 49 + .../CloseFillNonZeroAndStrokePath.java | 50 + .../util/operator/pagedrawer/ClosePath.java | 62 + .../util/operator/pagedrawer/CurveTo.java | 61 + .../CurveToReplicateFinalPoint.java | 58 + .../CurveToReplicateInitialPoint.java | 61 + .../util/operator/pagedrawer/EndPath.java | 46 + .../pagedrawer/FillEvenOddAndStrokePath.java | 54 + .../operator/pagedrawer/FillEvenOddRule.java | 64 + .../pagedrawer/FillNonZeroAndStrokePath.java | 54 + .../operator/pagedrawer/FillNonZeroRule.java | 65 + .../util/operator/pagedrawer/Invoke.java | 136 ++ .../util/operator/pagedrawer/LineTo.java | 54 + .../util/operator/pagedrawer/MoveTo.java | 66 + .../util/operator/pagedrawer/SHFill.java | 66 + .../operator/pagedrawer/SetLineCapStyle.java | 61 + .../pagedrawer/SetLineDashPattern.java | 80 + .../operator/pagedrawer/SetLineJoinStyle.java | 61 + .../pagedrawer/SetLineMiterLimit.java | 61 + .../operator/pagedrawer/SetLineWidth.java | 64 + .../util/operator/pagedrawer/StrokePath.java | 85 + .../util/operator/pagedrawer/package.html | 25 + .../fwstk/src/apache/pdfbox/util/package.html | 25 + 672 files changed, 136112 insertions(+), 1 deletion(-) create mode 100644 fluidbook/tools/fwstk/build.xml create mode 100644 fluidbook/tools/fwstk/manifest.mf create mode 100644 fluidbook/tools/fwstk/nbproject/build-impl.xml create mode 100644 fluidbook/tools/fwstk/nbproject/genfiles.properties create mode 100644 fluidbook/tools/fwstk/nbproject/private/config.properties create mode 100644 fluidbook/tools/fwstk/nbproject/private/private.properties create mode 100644 fluidbook/tools/fwstk/nbproject/project.properties create mode 100644 fluidbook/tools/fwstk/nbproject/project.xml create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/FontMapping.properties create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFBox_External_Fonts.properties create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFMarkedContentExtractor.properties create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFTextStripper.properties create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PageDrawer.properties create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/additional_glyphlist.txt create mode 100644 fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/pdfbox.properties create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/Log.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/LogConfigurationException.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/LogFactory.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/LogSource.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/AvalonLogger.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk13LumberjackLogger.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk14Logger.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/Log4JLogger.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogFactoryImpl.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogKitLogger.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/NoOpLog.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/ServletContextCleaner.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/SimpleLog.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/WeakHashtable.java create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/impl/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/commons/logging/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/AFMParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/CharMetric.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/Composite.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/CompositePart.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/FontMetric.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/KernPair.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/Ligature.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/TrackKern.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/afm/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/AFMFormatter.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFDataInput.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFontROS.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFOperator.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFStandardString.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CIDKeyedFDSelect.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringCommand.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringConverter.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringHandler.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringRenderer.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/DataInput.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/DataOutput.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/IndexData.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringFormatter.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontFormatter.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontUtil.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/Type2CharStringParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFCharset.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertCharset.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFISOAdobeCharset.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFExpertEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFStandardEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cff/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cmap/CIDRange.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMap.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMapParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cmap/CodespaceRange.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/cmap/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/encoding/Encoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/encoding/MacRomanEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/encoding/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/pfb/PfbParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/pfb/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/AbstractTTFParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/CIDFontType2Parser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/CMAPEncodingEntry.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/CMAPTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/DigitalSignatureTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/GlyphData.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/GlyphTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/HeaderTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/HorizontalHeaderTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/HorizontalMetricsTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/IndexToLocationTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/MaximumProfileTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/MemoryTTFDataStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/NameRecord.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/NamingTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/OS2WindowsMetricsTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/PostScriptTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/RAFDataStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/TTFDataStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/TTFParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/TTFTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/TrueTypeFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/ttf/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/util/BoundingBox.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/util/ResourceLoader.java create mode 100644 fluidbook/tools/fwstk/src/apache/fontbox/util/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/impl/DateConverter.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/impl/XMLUtil.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/impl/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/Elementable.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceEvent.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceRef.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/Thumbnail.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPMetadata.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchema.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasic.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasicJobTicket.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDublinCore.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDynamicMedia.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaIptc4xmpCore.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaMediaManagement.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPagedText.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPhotoshop.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaRightsManagement.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPMetadataPDFA.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAField.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAId.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAProperty.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFASchema.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAType.java create mode 100644 fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ConvertColorspace.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/Decrypt.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/Encrypt.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ExportFDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ExportXFDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ExtractImages.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ExtractText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ImportFDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/ImportXFDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/Overlay.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PDFBox.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PDFDebugger.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PDFMerger.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PDFReader.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PDFSplit.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PDFToImage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PdfDecompressor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/PrintPDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/TextToPDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/Version.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/WriteDecodedDoc.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSArray.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBase.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBoolean.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionaryLateBinding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDocument.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSFloat.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSInteger.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSName.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNull.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNumber.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSString.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSUnread.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/ICOSVisitor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/cos/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/AFMEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/DictionaryEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/Encoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/EncodingManager.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/MacRomanEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/PdfDocEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/StandardEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/Type1Encoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/WinAnsiEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKConverter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKEncoding.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CMapSubstitution.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConversionManager.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConverter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encoding/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encryption/ARCFour.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encryption/DocumentEncryption.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encryption/PDFEncryption.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/encryption/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/AbstractExample.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/PrintFields.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/SetField.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddImageToPDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddJavascript.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/Annotation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBlankPDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBookmarks.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateLandscapePDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ExtractMetadata.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/GoToSecondBookmarkOnOpen.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorld.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldTTF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ImageToPDF.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintBookmarks.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintDocumentMetaData.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintURLs.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RemoveFirstPage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceString.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceURLs.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStamp.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/UsingTextMatrix.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/CopyDoc.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/ShowSignature.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/ExtractTextByArea.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintImageLocations.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintTextLocations.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/RemoveAllText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/COSVisitorException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/CryptographyException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/InvalidPasswordException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/OutlineNotLocalException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/SignatureException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedIOException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCII85Filter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCIIHexFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/CCITTFaxDecodeFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/CryptFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/DCTFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/Filter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/FilterManager.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/FlateFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/IdentityFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/JBIG2Filter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/JPXFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/RunLengthDecodeFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/TIFFFaxDecoder.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/filter/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/ASCII85InputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/ASCII85OutputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/ByteArrayPushBackInputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/FastByteArrayOutputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitInputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitOutputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/PushBackInputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccess.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessBuffer.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFile.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileInputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileOutputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/io/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/BaseParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/ConformingPDFParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFObjectStreamParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFStreamParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFXrefStreamParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/VisualSignatureParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/XrefTrailerResolver.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ArrayEntry.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/MapEntry.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFPagePanel.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeCellRenderer.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeModel.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PageDrawer.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PageWrapper.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ReaderBottomPanel.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSFilterInputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSStandardOutputStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/ContentStreamWriter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/ConformingPDDocument.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDestinationNameTreeNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocument.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentCatalog.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentInformation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentNameDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDJavascriptNameTreeNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDPage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDPageNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDPageable.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDResources.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSArrayList.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSDictionaryMap.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSObjectable.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSStreamArray.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/DualCOSObjectable.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDestinationOrAction.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDictionaryWrapper.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMatrix.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMemoryStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMetadata.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNameTreeNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNamedTextStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDObjectStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabelRange.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabels.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRange.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRectangle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTextStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTypedDictionaryWrapper.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/XrefEntry.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunction.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType0.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType2.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType3.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType4.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDDefaultAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkedContentReference.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDObjectReference.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureTreeRoot.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserProperty.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/Revisions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/PDMarkedContent.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDArtifactMarkedContent.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDExportFormatAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDFourColours.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDLayoutAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDListAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDPrintFieldAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDStandardAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDTableAttributeObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/StandardStructureTypes.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/PDPageContentStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/AccessPermission.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/BadSecurityHandlerException.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/DecryptionMaterial.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDCryptFilterDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDEncryptionManager.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDStandardEncryption.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/ProtectionPolicy.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyDecryptionMaterial.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyProtectionPolicy.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyRecipient.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/SecurityHandler.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/SecurityHandlersManager.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardDecryptionMaterial.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardProtectionPolicy.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCaret.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCircle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFileAttachment.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFreeText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationHighlight.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationInk.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationLine.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolygon.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolyline.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSound.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquare.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquiggly.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStamp.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStrikeOut.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationUnderline.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFCatalog.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFDocument.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFField.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFIconFit.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFJavaScript.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFNamedPageReference.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFOptionElement.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFPage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFPageInfo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFTemplate.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/FontManager.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType0Font.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontFactory.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDMMType1Font.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDSimpleFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType0Font.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1AfmPfbFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1CFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1Font.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType3Font.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/Type3StreamParser.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDFontSetting.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDGraphicsState.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDLineDashPattern.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDShading.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCMYK.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCalRGB.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalGray.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorState.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceGray.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceN.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceNAttributes.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDGamma.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDICCBased.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDIndexed.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDLab.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDPattern.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDTristimulus.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Average.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/None.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Optimum.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Paeth.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Sub.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Up.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/CompositeImage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDCcitt.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDJpeg.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObject.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDActionFactory.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDAction.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionGoTo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionURI.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDURIDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationFileAttachment.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLine.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLink.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationMarkup.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationPopup.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationSquareCircle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationTextMarkup.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceCharacteristicsDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderEffectDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderStyleDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDExternalDataDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureInterface.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDCheckbox.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDChoiceButton.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDChoiceField.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDField.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDPushButton.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDRadioCollection.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignature.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignatureField.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDTextbox.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDUnknownField.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDXFA.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDMeasureDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDNumberFormatDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDRectlinearMeasureDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDViewportDictionary.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/PDTextState.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSHEXTable.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSObjectKey.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/BitFlagHelper.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/DateConverter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/ErrorLogger.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/ExtensionFileFilter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/ICU4JImpl.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/ImageParameters.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/LayerUtility.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/MapUtil.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/Matrix.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFCloneUtility.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFHighlighter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFImageWriter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMarkedContentExtractor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMergerUtility.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFOperator.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFStreamEngine.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFText2HTML.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripper.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripperByArea.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PageExtractor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/PositionWrapper.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/ResourceLoader.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/Splitter.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/StringUtil.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/TextNormalize.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPosition.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPositionComparator.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/XMLUtil.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginMarkedContentSequence.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginMarkedContentSequenceWithProperties.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/CloseAndStrokePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Concatenate.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndMarkedContentSequence.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GRestore.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GSave.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Invoke.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveAndShow.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveTextSetLeading.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/NextLine.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/OperatorProcessor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetCharSpacing.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetGraphicsStateParameters.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetHorizontalTextScaling.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineCapStyle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineDashPattern.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineJoinStyle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineMiterLimit.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineWidth.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMatrix.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMoveAndShow.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingCMYKColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingCalRGBColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingColorSpace.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingDeviceN.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingGrayColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingICCBasedColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingRGBColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingSeparation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingCMYKColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingCalRGBColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingColorSpace.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingDeviceN.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingGrayColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingICCBasedColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingRGBColor.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingSeparation.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextFont.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextLeading.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRenderingMode.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRise.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetWordSpacing.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowText.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowTextGlyph.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/BeginInlineImage.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipEvenOddRule.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipNonZeroRule.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillEvenOddAndStrokePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillNonZeroAndStrokePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClosePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveTo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/EndPath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddAndStrokePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroAndStrokePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/Invoke.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/LineTo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/MoveTo.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SHFill.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineCapStyle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineDashPattern.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineJoinStyle.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineMiterLimit.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineWidth.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/StrokePath.java create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/package.html create mode 100644 fluidbook/tools/fwstk/src/apache/pdfbox/util/package.html diff --git a/fluidbook/tools/fwstk/.classpath b/fluidbook/tools/fwstk/.classpath index 9c30ed735..c5b7acfb4 100644 --- a/fluidbook/tools/fwstk/.classpath +++ b/fluidbook/tools/fwstk/.classpath @@ -4,6 +4,6 @@ - + diff --git a/fluidbook/tools/fwstk/build.xml b/fluidbook/tools/fwstk/build.xml new file mode 100644 index 000000000..3afd22578 --- /dev/null +++ b/fluidbook/tools/fwstk/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project fwstk. + + + diff --git a/fluidbook/tools/fwstk/manifest.mf b/fluidbook/tools/fwstk/manifest.mf new file mode 100644 index 000000000..1574df4a2 --- /dev/null +++ b/fluidbook/tools/fwstk/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/fluidbook/tools/fwstk/nbproject/build-impl.xml b/fluidbook/tools/fwstk/nbproject/build-impl.xml new file mode 100644 index 000000000..d8914e78e --- /dev/null +++ b/fluidbook/tools/fwstk/nbproject/build-impl.xml @@ -0,0 +1,1058 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.src.dir + Must set src.project_resources.dir + Must set src.Java.dir + Must set src.resources.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + Must select one file in the IDE or set profile.class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fluidbook/tools/fwstk/nbproject/genfiles.properties b/fluidbook/tools/fwstk/nbproject/genfiles.properties new file mode 100644 index 000000000..678bc9550 --- /dev/null +++ b/fluidbook/tools/fwstk/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=e2012a3a +build.xml.script.CRC32=ef618d2d +build.xml.stylesheet.CRC32=28e38971@1.44.1.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=e2012a3a +nbproject/build-impl.xml.script.CRC32=7f407dc3 +nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45 diff --git a/fluidbook/tools/fwstk/nbproject/private/config.properties b/fluidbook/tools/fwstk/nbproject/private/config.properties new file mode 100644 index 000000000..e69de29bb diff --git a/fluidbook/tools/fwstk/nbproject/private/private.properties b/fluidbook/tools/fwstk/nbproject/private/private.properties new file mode 100644 index 000000000..2635b604a --- /dev/null +++ b/fluidbook/tools/fwstk/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\Vincent\\.netbeans\\7.0\\build.properties diff --git a/fluidbook/tools/fwstk/nbproject/project.properties b/fluidbook/tools/fwstk/nbproject/project.properties new file mode 100644 index 000000000..4a865c05a --- /dev/null +++ b/fluidbook/tools/fwstk/nbproject/project.properties @@ -0,0 +1,87 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=fwstk +application.vendor=Vincent +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/fwstk.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.Java-resources=../../../../Java/resources +file.reference.Works-Java=../../../../Java +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=com.fluidbook.fwstk.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.Java.dir=${file.reference.Works-Java} +src.project_resources.dir=project_resources +src.resources.dir=${file.reference.Java-resources} +src.src.dir=src diff --git a/fluidbook/tools/fwstk/nbproject/project.xml b/fluidbook/tools/fwstk/nbproject/project.xml new file mode 100644 index 000000000..557702c4e --- /dev/null +++ b/fluidbook/tools/fwstk/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + fwstk + + + + + + + + + + diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/FontMapping.properties b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/FontMapping.properties new file mode 100644 index 000000000..029e34ef5 --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/FontMapping.properties @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This file contains well known font substitutions. +# If a needed font isn't embedded in the pdf and not available in the running environment +# it will be substituted with an other suitable font, if available +# +# These are mappings for a java 1.4.x environment. +# Obviously not all fonts in the environment are found, so that we have to work with the few fonts java delivers +# +Arial=Lucida Sans +Arial,Bold=Lucida Sans,Bold +Arial,Italic=Lucida Sans,Italic +Arial,Bold,Italic=Lucida Sans,Bold,Italic +TimesNewRoman=Lucida Bright +TimesNewRoman,Bold=Lucida Bright,Bold +TimesNewRoman,Italic=Lucida Bright,Italic +TimesNewRoman,Bold,Italic=Lucida Bright,Bold,Italic +Courier=Lucida Sans Typewriter +Courier,Bold=Lucida Sans Typewriter,Bold +Courier,Italic=Lucida Sans Typewriter,Italic +Courier,Bold,Italic=Lucida Sans Typewriter,Bold,Italic +# +# These are some well known mappings for some frequently used fonts +# +Helvetica=Arial +Helvetica,Bold=Arial,Bold +Helvetica,Italic=Arial,Italic +Helvetica,Bold,Italic=Arial,Bold,Italic +SymbolMT=StandardSymbolsL +ArialMT=Arial +ArialMT,Bold=Arial,Bold +ArialMT,Italic=Arial,Italic +ArialMT,Bold,Italic=Arial,Bold,Italic +TimesNewRomanPSMT=Verdana +TimesNewRomanPSMT,Bold=Verdana,Bold diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFBox_External_Fonts.properties b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFBox_External_Fonts.properties new file mode 100644 index 000000000..b0dd63c85 --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFBox_External_Fonts.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +Arial-BoldItalicMT=org/apache/pdfbox/resources/ttf/Arial-BoldItalicMT.ttf +Arial-BoldMT=org/apache/pdfbox/resources/ttf/Arial-BoldMT.ttf +ArialMT=org/apache/pdfbox/resources/ttf/ArialMT.ttf +UNKNOWN_FONT=org/apache/pdfbox/resources/ttf/ArialMT.ttf diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFMarkedContentExtractor.properties b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFMarkedContentExtractor.properties new file mode 100644 index 000000000..f85c0bcc8 --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFMarkedContentExtractor.properties @@ -0,0 +1,98 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This table is maps PDF stream operators to concrete OperatorProcessor +# subclasses that are used by the PDFStreamEngine class to interpret the +# PDF document. The classes configured here allow the PDFTextStripper +# subclass of PDFStreamEngine to extract text content of the document. + +BT = org.apache.pdfbox.util.operator.BeginText +cm = org.apache.pdfbox.util.operator.Concatenate +Do = org.apache.pdfbox.util.operator.Invoke +ET = org.apache.pdfbox.util.operator.EndText +gs = org.apache.pdfbox.util.operator.SetGraphicsStateParameters +q = org.apache.pdfbox.util.operator.GSave +Q = org.apache.pdfbox.util.operator.GRestore +T* = org.apache.pdfbox.util.operator.NextLine +Tc = org.apache.pdfbox.util.operator.SetCharSpacing +Td = org.apache.pdfbox.util.operator.MoveText +TD = org.apache.pdfbox.util.operator.MoveTextSetLeading +Tf = org.apache.pdfbox.util.operator.SetTextFont +Tj = org.apache.pdfbox.util.operator.ShowText +TJ = org.apache.pdfbox.util.operator.ShowTextGlyph +TL = org.apache.pdfbox.util.operator.SetTextLeading +Tm = org.apache.pdfbox.util.operator.SetMatrix +Tr = org.apache.pdfbox.util.operator.SetTextRenderingMode +Ts = org.apache.pdfbox.util.operator.SetTextRise +Tw = org.apache.pdfbox.util.operator.SetWordSpacing +Tz = org.apache.pdfbox.util.operator.SetHorizontalTextScaling +w = org.apache.pdfbox.util.operator.SetLineWidth +\' = org.apache.pdfbox.util.operator.MoveAndShow +\" = org.apache.pdfbox.util.operator.SetMoveAndShow + +BDC = org.apache.pdfbox.util.operator.BeginMarkedContentSequenceWithProperties +BMC = org.apache.pdfbox.util.operator.BeginMarkedContentSequence +EMC = org.apache.pdfbox.util.operator.EndMarkedContentSequence + +# The following operators are not relevant to text extraction, +# so we can silently ignore them. + +b +B +b* +B* +BI +BX +c +CS +cs +d +d0 +d1 +DP +El +EX +f +F +f* +G +g +h +i +ID +j +J +K +k +l +m +M +MP +n +re +RG +rg +ri +s +S +SC +sc +SCN +scn +sh +v +W +W* +y \ No newline at end of file diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFTextStripper.properties b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFTextStripper.properties new file mode 100644 index 000000000..9f1b0e54d --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PDFTextStripper.properties @@ -0,0 +1,97 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This table is maps PDF stream operators to concrete OperatorProcessor +# subclasses that are used by the PDFStreamEngine class to interpret the +# PDF document. The classes configured here allow the PDFTextStripper +# subclass of PDFStreamEngine to extract text content of the document. + +BT = org.apache.pdfbox.util.operator.BeginText +cm = org.apache.pdfbox.util.operator.Concatenate +Do = org.apache.pdfbox.util.operator.Invoke +ET = org.apache.pdfbox.util.operator.EndText +gs = org.apache.pdfbox.util.operator.SetGraphicsStateParameters +q = org.apache.pdfbox.util.operator.GSave +Q = org.apache.pdfbox.util.operator.GRestore +T* = org.apache.pdfbox.util.operator.NextLine +Tc = org.apache.pdfbox.util.operator.SetCharSpacing +Td = org.apache.pdfbox.util.operator.MoveText +TD = org.apache.pdfbox.util.operator.MoveTextSetLeading +Tf = org.apache.pdfbox.util.operator.SetTextFont +Tj = org.apache.pdfbox.util.operator.ShowText +TJ = org.apache.pdfbox.util.operator.ShowTextGlyph +TL = org.apache.pdfbox.util.operator.SetTextLeading +Tm = org.apache.pdfbox.util.operator.SetMatrix +Tr = org.apache.pdfbox.util.operator.SetTextRenderingMode +Ts = org.apache.pdfbox.util.operator.SetTextRise +Tw = org.apache.pdfbox.util.operator.SetWordSpacing +Tz = org.apache.pdfbox.util.operator.SetHorizontalTextScaling +w = org.apache.pdfbox.util.operator.SetLineWidth +\' = org.apache.pdfbox.util.operator.MoveAndShow +\" = org.apache.pdfbox.util.operator.SetMoveAndShow + +# The following operators are not relevant to text extraction, +# so we can silently ignore them. + +b +B +b* +B* +BDC +BI +BMC +BX +c +CS +cs +d +d0 +d1 +DP +El +EMC +EX +f +F +f* +G +g +h +i +ID +j +J +K +k +l +m +M +MP +n +re +RG +rg +ri +s +S +SC +sc +SCN +scn +sh +v +W +W* +y diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PageDrawer.properties b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PageDrawer.properties new file mode 100644 index 000000000..4238e42fe --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/PageDrawer.properties @@ -0,0 +1,92 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# this Table is a correspondance Map of the PDF stream operators with concretes class of the +# OperatorProcessor abstract class for the stategy pattern used in the +# org.apache.pdfbox.util.PDFStreamEngine class. +# To change the behaviour of the system, remplace the class name by a new class name. +b=org.apache.pdfbox.util.operator.pagedrawer.CloseFillNonZeroAndStrokePath +B=org.apache.pdfbox.util.operator.pagedrawer.FillNonZeroAndStrokePath +b*=org.apache.pdfbox.util.operator.pagedrawer.CloseFillEvenOddAndStrokePath +B*=org.apache.pdfbox.util.operator.pagedrawer.FillEvenOddAndStrokePath +#BDC org.apache.pdfbox.util.operator.NotImplemented ##Begin Marked Content -- section 10.5 +BI=org.apache.pdfbox.util.operator.pagedrawer.BeginInlineImage +#BMC org.apache.pdfbox.util.operator.NotImplemented ##Begin Marked Content -- section 10.5 +BT=org.apache.pdfbox.util.operator.BeginText +#BX org.apache.pdfbox.util.operator.NotImplemented +c=org.apache.pdfbox.util.operator.pagedrawer.CurveTo +cm=org.apache.pdfbox.util.operator.Concatenate +CS=org.apache.pdfbox.util.operator.SetStrokingColorSpace +cs=org.apache.pdfbox.util.operator.SetNonStrokingColorSpace +d=org.apache.pdfbox.util.operator.pagedrawer.SetLineDashPattern +#d0 org.apache.pdfbox.util.operator.NotImplemented +#d1 org.apache.pdfbox.util.operator.NotImplemented +Do=org.apache.pdfbox.util.operator.pagedrawer.Invoke +#DP org.apache.pdfbox.util.operator.NotImplemented ##Marked Content Point-- section 10.5 +#El org.apache.pdfbox.util.operator.NotImplemented +#EMC org.apache.pdfbox.util.operator.NotImplemented ##End Marked Content -- section 10.5 +ET=org.apache.pdfbox.util.operator.EndText +#EX org.apache.pdfbox.util.operator.NotImplemented +f=org.apache.pdfbox.util.operator.pagedrawer.FillNonZeroRule +F=org.apache.pdfbox.util.operator.pagedrawer.FillNonZeroRule +f*=org.apache.pdfbox.util.operator.pagedrawer.FillEvenOddRule +G=org.apache.pdfbox.util.operator.SetStrokingGrayColor +g=org.apache.pdfbox.util.operator.SetNonStrokingGrayColor +gs=org.apache.pdfbox.util.operator.SetGraphicsStateParameters +h=org.apache.pdfbox.util.operator.pagedrawer.ClosePath +#i org.apache.pdfbox.util.operator.NotImplemented +#ID org.apache.pdfbox.util.operator.NotImplemented +j=org.apache.pdfbox.util.operator.pagedrawer.SetLineJoinStyle +J=org.apache.pdfbox.util.operator.pagedrawer.SetLineCapStyle +K=org.apache.pdfbox.util.operator.SetStrokingCMYKColor +k=org.apache.pdfbox.util.operator.SetNonStrokingCMYKColor +l=org.apache.pdfbox.util.operator.pagedrawer.LineTo +m=org.apache.pdfbox.util.operator.pagedrawer.MoveTo +M=org.apache.pdfbox.util.operator.pagedrawer.SetLineMiterLimit +#MP org.apache.pdfbox.util.operator.NotImplemented ##Marked Content Point-- section 10.5 +n=org.apache.pdfbox.util.operator.pagedrawer.EndPath +q=org.apache.pdfbox.util.operator.GSave +Q=org.apache.pdfbox.util.operator.GRestore +re=org.apache.pdfbox.util.operator.pagedrawer.AppendRectangleToPath +RG=org.apache.pdfbox.util.operator.SetStrokingRGBColor +rg=org.apache.pdfbox.util.operator.SetNonStrokingRGBColor +#ri org.apache.pdfbox.util.operator.NotImplemented +s=org.apache.pdfbox.util.operator.CloseAndStrokePath +S=org.apache.pdfbox.util.operator.pagedrawer.StrokePath +SC=org.apache.pdfbox.util.operator.SetStrokingColor +sc=org.apache.pdfbox.util.operator.SetNonStrokingColor +SCN=org.apache.pdfbox.util.operator.SetStrokingColor +scn=org.apache.pdfbox.util.operator.SetNonStrokingColor +sh=org.apache.pdfbox.util.operator.pagedrawer.SHFill +T*=org.apache.pdfbox.util.operator.NextLine +Tc=org.apache.pdfbox.util.operator.SetCharSpacing +Td=org.apache.pdfbox.util.operator.MoveText +TD=org.apache.pdfbox.util.operator.MoveTextSetLeading +Tf=org.apache.pdfbox.util.operator.SetTextFont +Tj=org.apache.pdfbox.util.operator.ShowText +TJ=org.apache.pdfbox.util.operator.ShowTextGlyph +TL=org.apache.pdfbox.util.operator.SetTextLeading +Tm=org.apache.pdfbox.util.operator.SetMatrix +Tr=org.apache.pdfbox.util.operator.SetTextRenderingMode +Ts=org.apache.pdfbox.util.operator.SetTextRise +Tw=org.apache.pdfbox.util.operator.SetWordSpacing +Tz=org.apache.pdfbox.util.operator.SetHorizontalTextScaling +v=org.apache.pdfbox.util.operator.pagedrawer.CurveToReplicateInitialPoint +w=org.apache.pdfbox.util.operator.pagedrawer.SetLineWidth +W org.apache.pdfbox.util.operator.pagedrawer.ClipNonZeroRule +W* org.apache.pdfbox.util.operator.pagedrawer.ClipEvenOddRule +y=org.apache.pdfbox.util.operator.pagedrawer.CurveToReplicateFinalPoint +\'=org.apache.pdfbox.util.operator.MoveAndShow +\"=org.apache.pdfbox.util.operator.SetMoveAndShow diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/additional_glyphlist.txt b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/additional_glyphlist.txt new file mode 100644 index 000000000..602e4d4de --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/additional_glyphlist.txt @@ -0,0 +1,160 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Format: Semicolon-delimited fields: +# (1) glyph name +# (2) Unicode scalar value +# +# These mappings are missing in the original copy of the adobe glyphlist.txt +# +angbracketleft;3008 +angbracketright;3009 +circlecopyrt;00A9 +controlNULL;0000 +# +# TeX-related mappings using named values +# +angbracketleftbig;2329 +angbracketleftBig;2329 +angbracketleftbigg;2329 +angbracketleftBigg;2329 +angbracketrightBig;232A +angbracketrightbig;232A +angbracketrightBigg;232A +angbracketrightbigg;232A +arrowhookleft;21AA +arrowhookright;21A9 +arrowlefttophalf;21BC +arrowleftbothalf;21BD +arrownortheast;2197 +arrownorthwest;2196 +arrowrighttophalf;21C0 +arrowrightbothalf;21C1 +arrowsoutheast;2198 +arrowsouthwest;2199 +backslashbig;2216 +backslashBig;2216 +backslashBigg;2216 +backslashbigg;2216 +bardbl;2016 +bracehtipdownleft;FE37 +bracehtipdownright;FE37 +bracehtipupleft;FE38 +bracehtipupright;FE38 +braceleftBig;007B +braceleftbig;007B +braceleftbigg;007B +braceleftBigg;007B +bracerightBig;007D +bracerightbig;007D +bracerightbigg;007D +bracerightBigg;007D +bracketleftbig;005B +bracketleftBig;005B +bracketleftbigg;005B +bracketleftBigg;005B +bracketrightBig;005D +bracketrightbig;005D +bracketrightbigg;005D +bracketrightBigg;005D +ceilingleftbig;2308 +ceilingleftBig;2308 +ceilingleftBigg;2308 +ceilingleftbigg;2308 +ceilingrightbig;2309 +ceilingrightBig;2309 +ceilingrightbigg;2309 +ceilingrightBigg;2309 +circledotdisplay;2299 +circledottext;2299 +circlemultiplydisplay;2297 +circlemultiplytext;2297 +circleplusdisplay;2295 +circleplustext;2295 +contintegraldisplay;222E +contintegraltext;222E +coproductdisplay;2210 +coproducttext;2210 +floorleftBig;230A +floorleftbig;230A +floorleftbigg;230A +floorleftBigg;230A +floorrightbig;230B +floorrightBig;230B +floorrightBigg;230B +floorrightbigg;230B +hatwide;0302 +hatwider;0302 +hatwidest;0302 +intercal;1D40 +integraldisplay;222B +integraltext;222B +intersectiondisplay;22C2 +intersectiontext;22C2 +logicalanddisplay;2227 +logicalandtext;2227 +logicalordisplay;2228 +logicalortext;2228 +parenleftBig;0028 +parenleftbig;0028 +parenleftBigg;0028 +parenleftbigg;0028 +parenrightBig;0029 +parenrightbig;0029 +parenrightBigg;0029 +parenrightbigg;0029 +prime;2032 +productdisplay;220F +producttext;220F +radicalbig;221A +radicalBig;221A +radicalBigg;221A +radicalbigg;221A +radicalbt;221A +radicaltp;221A +radicalvertex;221A +slashbig;002F +slashBig;002F +slashBigg;002F +slashbigg;002F +summationdisplay;2211 +summationtext;2211 +tildewide;02DC +tildewider;02DC +tildewidest;02DC +uniondisplay;22C3 +unionmultidisplay;228E +unionmultitext;228E +unionsqdisplay;2294 +unionsqtext;2294 +uniontext;22C3 +vextenddouble;2225 +vextendsingle;2223 +# +# TeX-related mappings using hexadecimal or decimal values +# +x1b;FB00 +x1c;FB01 +x1d;FB02 +x1e;FB03 +x8a;0141 +xff;00DF +a27;FB00 +a28;FB01 +a29;FB02 +a30;FB03 +a138;0141 +a255;00DF \ No newline at end of file diff --git a/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/pdfbox.properties b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/pdfbox.properties new file mode 100644 index 000000000..e78a6ad6c --- /dev/null +++ b/fluidbook/tools/fwstk/project_resources/org/apache/pdfbox/resources/pdfbox.properties @@ -0,0 +1,15 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +pdfbox.version=${project.version} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/Log.java b/fluidbook/tools/fwstk/src/apache/commons/logging/Log.java new file mode 100644 index 000000000..bf80bad2e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/Log.java @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging; + +/** + *

A simple logging interface abstracting logging APIs. In order to be + * instantiated successfully by {@link LogFactory}, classes that implement + * this interface must have a constructor that takes a single String + * parameter representing the "name" of this Log.

+ * + *

The six logging levels used by Log are (in order): + *

    + *
  1. trace (the least serious)
  2. + *
  3. debug
  4. + *
  5. info
  6. + *
  7. warn
  8. + *
  9. error
  10. + *
  11. fatal (the most serious)
  12. + *
+ * The mapping of these log levels to the concepts used by the underlying + * logging system is implementation dependent. + * The implemention should ensure, though, that this ordering behaves + * as expected.

+ * + *

Performance is often a logging concern. + * By examining the appropriate property, + * a component can avoid expensive operations (producing information + * to be logged).

+ * + *

For example, + *

+ *    if (log.isDebugEnabled()) {
+ *        ... do something expensive ...
+ *        log.debug(theResult);
+ *    }
+ * 
+ *

+ * + *

Configuration of the underlying logging system will generally be done + * external to the Logging APIs, through whatever mechanism is supported by + * that system.

+ * + * @author Scott Sanders + * @author Rod Waldhoff + * @version $Id: Log.java 424107 2006-07-20 23:15:42Z skitching $ + */ +public interface Log { + + + // ----------------------------------------------------- Logging Properties + + + /** + *

Is debug logging currently enabled?

+ * + *

Call this method to prevent having to perform expensive operations + * (for example, String concatenation) + * when the log level is more than debug.

+ * + * @return true if debug is enabled in the underlying logger. + */ + public boolean isDebugEnabled(); + + + /** + *

Is error logging currently enabled?

+ * + *

Call this method to prevent having to perform expensive operations + * (for example, String concatenation) + * when the log level is more than error.

+ * + * @return true if error is enabled in the underlying logger. + */ + public boolean isErrorEnabled(); + + + /** + *

Is fatal logging currently enabled?

+ * + *

Call this method to prevent having to perform expensive operations + * (for example, String concatenation) + * when the log level is more than fatal.

+ * + * @return true if fatal is enabled in the underlying logger. + */ + public boolean isFatalEnabled(); + + + /** + *

Is info logging currently enabled?

+ * + *

Call this method to prevent having to perform expensive operations + * (for example, String concatenation) + * when the log level is more than info.

+ * + * @return true if info is enabled in the underlying logger. + */ + public boolean isInfoEnabled(); + + + /** + *

Is trace logging currently enabled?

+ * + *

Call this method to prevent having to perform expensive operations + * (for example, String concatenation) + * when the log level is more than trace.

+ * + * @return true if trace is enabled in the underlying logger. + */ + public boolean isTraceEnabled(); + + + /** + *

Is warn logging currently enabled?

+ * + *

Call this method to prevent having to perform expensive operations + * (for example, String concatenation) + * when the log level is more than warn.

+ * + * @return true if warn is enabled in the underlying logger. + */ + public boolean isWarnEnabled(); + + + // -------------------------------------------------------- Logging Methods + + + /** + *

Log a message with trace log level.

+ * + * @param message log this message + */ + public void trace(Object message); + + + /** + *

Log an error with trace log level.

+ * + * @param message log this message + * @param t log this cause + */ + public void trace(Object message, Throwable t); + + + /** + *

Log a message with debug log level.

+ * + * @param message log this message + */ + public void debug(Object message); + + + /** + *

Log an error with debug log level.

+ * + * @param message log this message + * @param t log this cause + */ + public void debug(Object message, Throwable t); + + + /** + *

Log a message with info log level.

+ * + * @param message log this message + */ + public void info(Object message); + + + /** + *

Log an error with info log level.

+ * + * @param message log this message + * @param t log this cause + */ + public void info(Object message, Throwable t); + + + /** + *

Log a message with warn log level.

+ * + * @param message log this message + */ + public void warn(Object message); + + + /** + *

Log an error with warn log level.

+ * + * @param message log this message + * @param t log this cause + */ + public void warn(Object message, Throwable t); + + + /** + *

Log a message with error log level.

+ * + * @param message log this message + */ + public void error(Object message); + + + /** + *

Log an error with error log level.

+ * + * @param message log this message + * @param t log this cause + */ + public void error(Object message, Throwable t); + + + /** + *

Log a message with fatal log level.

+ * + * @param message log this message + */ + public void fatal(Object message); + + + /** + *

Log an error with fatal log level.

+ * + * @param message log this message + * @param t log this cause + */ + public void fatal(Object message, Throwable t); + + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/LogConfigurationException.java b/fluidbook/tools/fwstk/src/apache/commons/logging/LogConfigurationException.java new file mode 100644 index 000000000..0598a4c33 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/LogConfigurationException.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.logging; + + +/** + *

An exception that is thrown only if a suitable LogFactory + * or Log instance cannot be created by the corresponding + * factory methods.

+ * + * @author Craig R. McClanahan + * @version $Revision: 424107 $ $Date: 2006-07-21 01:15:42 +0200 (ven., 21 juil. 2006) $ + */ + +public class LogConfigurationException extends RuntimeException { + + + /** + * Construct a new exception with null as its detail message. + */ + public LogConfigurationException() { + + super(); + + } + + + /** + * Construct a new exception with the specified detail message. + * + * @param message The detail message + */ + public LogConfigurationException(String message) { + + super(message); + + } + + + /** + * Construct a new exception with the specified cause and a derived + * detail message. + * + * @param cause The underlying cause + */ + public LogConfigurationException(Throwable cause) { + + this((cause == null) ? null : cause.toString(), cause); + + } + + + /** + * Construct a new exception with the specified detail message and cause. + * + * @param message The detail message + * @param cause The underlying cause + */ + public LogConfigurationException(String message, Throwable cause) { + + super(message + " (Caused by " + cause + ")"); + this.cause = cause; // Two-argument version requires JDK 1.4 or later + + } + + + /** + * The underlying cause of this exception. + */ + protected Throwable cause = null; + + + /** + * Return the underlying cause of this exception (if any). + */ + public Throwable getCause() { + + return (this.cause); + + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/LogFactory.java b/fluidbook/tools/fwstk/src/apache/commons/logging/LogFactory.java new file mode 100644 index 000000000..b573a440e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/LogFactory.java @@ -0,0 +1,1843 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.logging; + + +import java.io.BufferedReader; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLConnection; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; + + +/** + *

Factory for creating {@link Log} instances, with discovery and + * configuration features similar to that employed by standard Java APIs + * such as JAXP.

+ * + *

IMPLEMENTATION NOTE - This implementation is heavily + * based on the SAXParserFactory and DocumentBuilderFactory implementations + * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.

+ * + * @author Craig R. McClanahan + * @author Costin Manolache + * @author Richard A. Sitze + * @version $Revision: 697387 $ $Date: 2008-09-20 18:33:51 +0200 (sam., 20 sept. 2008) $ + */ + +public abstract class LogFactory { + // Implementation note re AccessController usage + // + // It is important to keep code invoked via an AccessController to small + // auditable blocks. Such code must carefully evaluate all user input + // (parameters, system properties, config file contents, etc). As an + // example, a Log implementation should not write to its logfile + // with an AccessController anywhere in the call stack, otherwise an + // insecure application could configure the log implementation to write + // to a protected file using the privileges granted to JCL rather than + // to the calling application. + // + // Under no circumstance should a non-private method return data that is + // retrieved via an AccessController. That would allow an insecure app + // to invoke that method and obtain data that it is not permitted to have. + // + // Invoking user-supplied code with an AccessController set is not a major + // issue (eg invoking the constructor of the class specified by + // HASHTABLE_IMPLEMENTATION_PROPERTY). That class will be in a different + // trust domain, and therefore must have permissions to do whatever it + // is trying to do regardless of the permissions granted to JCL. There is + // a slight issue in that untrusted code may point that environment var + // to another trusted library, in which case the code runs if both that + // lib and JCL have the necessary permissions even when the untrusted + // caller does not. That's a pretty hard route to exploit though. + + + // ----------------------------------------------------- Manifest Constants + + /** + * The name (priority) of the key in the config file used to + * specify the priority of that particular config file. The associated value + * is a floating-point number; higher values take priority over lower values. + */ + public static final String PRIORITY_KEY = "priority"; + + /** + * The name (use_tccl) of the key in the config file used + * to specify whether logging classes should be loaded via the thread + * context class loader (TCCL), or not. By default, the TCCL is used. + */ + public static final String TCCL_KEY = "use_tccl"; + + /** + * The name (org.apache.commons.logging.LogFactory) of the property + * used to identify the LogFactory implementation + * class name. This can be used as a system property, or as an entry in a + * configuration properties file. + */ + public static final String FACTORY_PROPERTY = + "org.apache.commons.logging.LogFactory"; + + /** + * The fully qualified class name of the fallback LogFactory + * implementation class to use, if no other can be found. + */ + public static final String FACTORY_DEFAULT = + "org.apache.commons.logging.impl.LogFactoryImpl"; + + /** + * The name (commons-logging.properties) of the properties file to search for. + */ + public static final String FACTORY_PROPERTIES = + "commons-logging.properties"; + + /** + * JDK1.3+ + * 'Service Provider' specification. + * + */ + protected static final String SERVICE_ID = + "META-INF/services/org.apache.commons.logging.LogFactory"; + + /** + * The name (org.apache.commons.logging.diagnostics.dest) + * of the property used to enable internal commons-logging + * diagnostic output, in order to get information on what logging + * implementations are being discovered, what classloaders they + * are loaded through, etc. + *

+ * If a system property of this name is set then the value is + * assumed to be the name of a file. The special strings + * STDOUT or STDERR (case-sensitive) indicate output to + * System.out and System.err respectively. + *

+ * Diagnostic logging should be used only to debug problematic + * configurations and should not be set in normal production use. + */ + public static final String DIAGNOSTICS_DEST_PROPERTY = + "org.apache.commons.logging.diagnostics.dest"; + + /** + * When null (the usual case), no diagnostic output will be + * generated by LogFactory or LogFactoryImpl. When non-null, + * interesting events will be written to the specified object. + */ + private static PrintStream diagnosticsStream = null; + + /** + * A string that gets prefixed to every message output by the + * logDiagnostic method, so that users can clearly see which + * LogFactory class is generating the output. + */ + private static String diagnosticPrefix; + + /** + *

Setting this system property + * (org.apache.commons.logging.LogFactory.HashtableImpl) + * value allows the Hashtable used to store + * classloaders to be substituted by an alternative implementation. + *

+ *

+ * Note: LogFactory will print: + *

+     * [ERROR] LogFactory: Load of custom hashtable failed
+     * 
+ * to system error and then continue using a standard Hashtable. + *

+ *

+ * Usage: Set this property when Java is invoked + * and LogFactory will attempt to load a new instance + * of the given implementation class. + * For example, running the following ant scriplet: + *

+     *  <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}">
+     *     ...
+     *     <sysproperty 
+     *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
+     *        value="org.apache.commons.logging.AltHashtable"/>
+     *  </java>
+     * 
+ * will mean that LogFactory will load an instance of + * org.apache.commons.logging.AltHashtable. + *

+ *

+ * A typical use case is to allow a custom + * Hashtable implementation using weak references to be substituted. + * This will allow classloaders to be garbage collected without + * the need to release them (on 1.3+ JVMs only, of course ;) + *

+ */ + public static final String HASHTABLE_IMPLEMENTATION_PROPERTY = + "org.apache.commons.logging.LogFactory.HashtableImpl"; + /** Name used to load the weak hashtable implementation by names */ + private static final String WEAK_HASHTABLE_CLASSNAME = + "org.apache.commons.logging.impl.WeakHashtable"; + + /** + * A reference to the classloader that loaded this class. This is the + * same as LogFactory.class.getClassLoader(). However computing this + * value isn't quite as simple as that, as we potentially need to use + * AccessControllers etc. It's more efficient to compute it once and + * cache it here. + */ + private static ClassLoader thisClassLoader; + + // ----------------------------------------------------------- Constructors + + + /** + * Protected constructor that is not available for public use. + */ + protected LogFactory() { + } + + // --------------------------------------------------------- Public Methods + + + /** + * Return the configuration attribute with the specified name (if any), + * or null if there is no such attribute. + * + * @param name Name of the attribute to return + */ + public abstract Object getAttribute(String name); + + + /** + * Return an array containing the names of all currently defined + * configuration attributes. If there are no such attributes, a zero + * length array is returned. + */ + public abstract String[] getAttributeNames(); + + + /** + * Convenience method to derive a name from the specified class and + * call getInstance(String) with it. + * + * @param clazz Class for which a suitable Log name will be derived + * + * @exception LogConfigurationException if a suitable Log + * instance cannot be returned + */ + public abstract Log getInstance(Class clazz) + throws LogConfigurationException; + + + /** + *

Construct (if necessary) and return a Log instance, + * using the factory's current set of configuration attributes.

+ * + *

NOTE - Depending upon the implementation of + * the LogFactory you are using, the Log + * instance you are returned may or may not be local to the current + * application, and may or may not be returned again on a subsequent + * call with the same name argument.

+ * + * @param name Logical name of the Log instance to be + * returned (the meaning of this name is only known to the underlying + * logging implementation that is being wrapped) + * + * @exception LogConfigurationException if a suitable Log + * instance cannot be returned + */ + public abstract Log getInstance(String name) + throws LogConfigurationException; + + + /** + * Release any internal references to previously created {@link Log} + * instances returned by this factory. This is useful in environments + * like servlet containers, which implement application reloading by + * throwing away a ClassLoader. Dangling references to objects in that + * class loader would prevent garbage collection. + */ + public abstract void release(); + + + /** + * Remove any configuration attribute associated with the specified name. + * If there is no such attribute, no action is taken. + * + * @param name Name of the attribute to remove + */ + public abstract void removeAttribute(String name); + + + /** + * Set the configuration attribute with the specified name. Calling + * this with a null value is equivalent to calling + * removeAttribute(name). + * + * @param name Name of the attribute to set + * @param value Value of the attribute to set, or null + * to remove any setting for this attribute + */ + public abstract void setAttribute(String name, Object value); + + + // ------------------------------------------------------- Static Variables + + + /** + * The previously constructed LogFactory instances, keyed by + * the ClassLoader with which it was created. + */ + protected static Hashtable factories = null; + + /** + * Prevously constructed LogFactory instance as in the + * factories map, but for the case where + * getClassLoader returns null. + * This can happen when: + *
    + *
  • using JDK1.1 and the calling code is loaded via the system + * classloader (very common)
  • + *
  • using JDK1.2+ and the calling code is loaded via the boot + * classloader (only likely for embedded systems work).
  • + *
+ * Note that factories is a Hashtable (not a HashMap), + * and hashtables don't allow null as a key. + */ + protected static LogFactory nullClassLoaderFactory = null; + + /** + * Create the hashtable which will be used to store a map of + * (context-classloader -> logfactory-object). Version 1.2+ of Java + * supports "weak references", allowing a custom Hashtable class + * to be used which uses only weak references to its keys. Using weak + * references can fix memory leaks on webapp unload in some cases (though + * not all). Version 1.1 of Java does not support weak references, so we + * must dynamically determine which we are using. And just for fun, this + * code also supports the ability for a system property to specify an + * arbitrary Hashtable implementation name. + *

+ * Note that the correct way to ensure no memory leaks occur is to ensure + * that LogFactory.release(contextClassLoader) is called whenever a + * webapp is undeployed. + */ + private static final Hashtable createFactoryStore() { + Hashtable result = null; + String storeImplementationClass; + try { + storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null); + } catch(SecurityException ex) { + // Permissions don't allow this to be accessed. Default to the "modern" + // weak hashtable implementation if it is available. + storeImplementationClass = null; + } + + if (storeImplementationClass == null) { + storeImplementationClass = WEAK_HASHTABLE_CLASSNAME; + } + try { + Class implementationClass = Class.forName(storeImplementationClass); + result = (Hashtable) implementationClass.newInstance(); + + } catch (Throwable t) { + // ignore + if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) { + // if the user's trying to set up a custom implementation, give a clue + if (isDiagnosticsEnabled()) { + // use internal logging to issue the warning + logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed"); + } else { + // we *really* want this output, even if diagnostics weren't + // explicitly enabled by the user. + System.err.println("[ERROR] LogFactory: Load of custom hashtable failed"); + } + } + } + if (result == null) { + result = new Hashtable(); + } + return result; + } + + + // --------------------------------------------------------- Static Methods + + /** Utility method to safely trim a string. */ + private static String trim(String src) { + if (src == null) { + return null; + } + return src.trim(); + } + + /** + *

Construct (if necessary) and return a LogFactory + * instance, using the following ordered lookup procedure to determine + * the name of the implementation class to be loaded.

+ *
    + *
  • The org.apache.commons.logging.LogFactory system + * property.
  • + *
  • The JDK 1.3 Service Discovery mechanism
  • + *
  • Use the properties file commons-logging.properties + * file, if found in the class path of this class. The configuration + * file is in standard java.util.Properties format and + * contains the fully qualified name of the implementation class + * with the key being the system property defined above.
  • + *
  • Fall back to a default implementation class + * (org.apache.commons.logging.impl.LogFactoryImpl).
  • + *
+ * + *

NOTE - If the properties file method of identifying the + * LogFactory implementation class is utilized, all of the + * properties defined in this file will be set as configuration attributes + * on the corresponding LogFactory instance.

+ * + *

NOTE - In a multithreaded environment it is possible + * that two different instances will be returned for the same + * classloader environment. + *

+ * + * @exception LogConfigurationException if the implementation class is not + * available or cannot be instantiated. + */ + public static LogFactory getFactory() throws LogConfigurationException { + // Identify the class loader we will be using + ClassLoader contextClassLoader = getContextClassLoaderInternal(); + + if (contextClassLoader == null) { + // This is an odd enough situation to report about. This + // output will be a nuisance on JDK1.1, as the system + // classloader is null in that environment. + if (isDiagnosticsEnabled()) { + logDiagnostic("Context classloader is null."); + } + } + + // Return any previously registered factory for this class loader + LogFactory factory = getCachedFactory(contextClassLoader); + if (factory != null) { + return factory; + } + + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] LogFactory implementation requested for the first time for context classloader " + + objectId(contextClassLoader)); + logHierarchy("[LOOKUP] ", contextClassLoader); + } + + // Load properties file. + // + // If the properties file exists, then its contents are used as + // "attributes" on the LogFactory implementation class. One particular + // property may also control which LogFactory concrete subclass is + // used, but only if other discovery mechanisms fail.. + // + // As the properties file (if it exists) will be used one way or + // another in the end we may as well look for it first. + + Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES); + + // Determine whether we will be using the thread context class loader to + // load logging classes or not by checking the loaded properties file (if any). + ClassLoader baseClassLoader = contextClassLoader; + if (props != null) { + String useTCCLStr = props.getProperty(TCCL_KEY); + if (useTCCLStr != null) { + // The Boolean.valueOf(useTCCLStr).booleanValue() formulation + // is required for Java 1.2 compatability. + if (Boolean.valueOf(useTCCLStr).booleanValue() == false) { + // Don't use current context classloader when locating any + // LogFactory or Log classes, just use the class that loaded + // this abstract class. When this class is deployed in a shared + // classpath of a container, it means webapps cannot deploy their + // own logging implementations. It also means that it is up to the + // implementation whether to load library-specific config files + // from the TCCL or not. + baseClassLoader = thisClassLoader; + } + } + } + + // Determine which concrete LogFactory subclass to use. + // First, try a global system property + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY + + "] to define the LogFactory subclass to use..."); + } + + try { + String factoryClass = getSystemProperty(FACTORY_PROPERTY, null); + if (factoryClass != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass + + "' as specified by system property " + FACTORY_PROPERTY); + } + + factory = newFactory(factoryClass, baseClassLoader, contextClassLoader); + } else { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] No system property [" + FACTORY_PROPERTY + + "] defined."); + } + } + } catch (SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] A security exception occurred while trying to create an" + + " instance of the custom factory class" + + ": [" + trim(e.getMessage()) + + "]. Trying alternative implementations..."); + } + ; // ignore + } catch(RuntimeException e) { + // This is not consistent with the behaviour when a bad LogFactory class is + // specified in a services file. + // + // One possible exception that can occur here is a ClassCastException when + // the specified class wasn't castable to this LogFactory type. + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] An exception occurred while trying to create an" + + " instance of the custom factory class" + + ": [" + trim(e.getMessage()) + + "] as specified by a system property."); + } + throw e; + } + + + // Second, try to find a service by using the JDK1.3 class + // discovery mechanism, which involves putting a file with the name + // of an interface class in the META-INF/services directory, where the + // contents of the file is a single line specifying a concrete class + // that implements the desired interface. + + if (factory == null) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID + + "] to define the LogFactory subclass to use..."); + } + try { + InputStream is = getResourceAsStream(contextClassLoader, + SERVICE_ID); + + if( is != null ) { + // This code is needed by EBCDIC and other strange systems. + // It's a fix for bugs reported in xerces + BufferedReader rd; + try { + rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + rd = new BufferedReader(new InputStreamReader(is)); + } + + String factoryClassName = rd.readLine(); + rd.close(); + + if (factoryClassName != null && + ! "".equals(factoryClassName)) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Creating an instance of LogFactory class " + factoryClassName + + " as specified by file '" + SERVICE_ID + + "' which was present in the path of the context" + + " classloader."); + } + factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader ); + } + } else { + // is == null + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] No resource file with name '" + SERVICE_ID + + "' found."); + } + } + } catch( Exception ex ) { + // note: if the specified LogFactory class wasn't compatible with LogFactory + // for some reason, a ClassCastException will be caught here, and attempts will + // continue to find a compatible class. + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] A security exception occurred while trying to create an" + + " instance of the custom factory class" + + ": [" + trim(ex.getMessage()) + + "]. Trying alternative implementations..."); + } + ; // ignore + } + } + + + // Third try looking into the properties file read earlier (if found) + + if (factory == null) { + if (props != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Looking in properties file for entry with key '" + + FACTORY_PROPERTY + + "' to define the LogFactory subclass to use..."); + } + String factoryClass = props.getProperty(FACTORY_PROPERTY); + if (factoryClass != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Properties file specifies LogFactory subclass '" + + factoryClass + "'"); + } + factory = newFactory(factoryClass, baseClassLoader, contextClassLoader); + + // TODO: think about whether we need to handle exceptions from newFactory + } else { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Properties file has no entry specifying LogFactory subclass."); + } + } + } else { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] No properties file available to determine" + + " LogFactory subclass from.."); + } + } + } + + + // Fourth, try the fallback implementation class + + if (factory == null) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT + + "' via the same classloader that loaded this LogFactory" + + " class (ie not looking in the context classloader)."); + } + + // Note: unlike the above code which can try to load custom LogFactory + // implementations via the TCCL, we don't try to load the default LogFactory + // implementation via the context classloader because: + // * that can cause problems (see comments in newFactory method) + // * no-one should be customising the code of the default class + // Yes, we do give up the ability for the child to ship a newer + // version of the LogFactoryImpl class and have it used dynamically + // by an old LogFactory class in the parent, but that isn't + // necessarily a good idea anyway. + factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader); + } + + if (factory != null) { + /** + * Always cache using context class loader. + */ + cacheFactory(contextClassLoader, factory); + + if( props!=null ) { + Enumeration names = props.propertyNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + String value = props.getProperty(name); + factory.setAttribute(name, value); + } + } + } + + return factory; + } + + + /** + * Convenience method to return a named logger, without the application + * having to care about factories. + * + * @param clazz Class from which a log name will be derived + * + * @exception LogConfigurationException if a suitable Log + * instance cannot be returned + */ + public static Log getLog(Class clazz) + throws LogConfigurationException { + + return (getFactory().getInstance(clazz)); + + } + + + /** + * Convenience method to return a named logger, without the application + * having to care about factories. + * + * @param name Logical name of the Log instance to be + * returned (the meaning of this name is only known to the underlying + * logging implementation that is being wrapped) + * + * @exception LogConfigurationException if a suitable Log + * instance cannot be returned + */ + public static Log getLog(String name) + throws LogConfigurationException { + + return (getFactory().getInstance(name)); + + } + + + /** + * Release any internal references to previously created {@link LogFactory} + * instances that have been associated with the specified class loader + * (if any), after calling the instance method release() on + * each of them. + * + * @param classLoader ClassLoader for which to release the LogFactory + */ + public static void release(ClassLoader classLoader) { + + if (isDiagnosticsEnabled()) { + logDiagnostic("Releasing factory for classloader " + objectId(classLoader)); + } + synchronized (factories) { + if (classLoader == null) { + if (nullClassLoaderFactory != null) { + nullClassLoaderFactory.release(); + nullClassLoaderFactory = null; + } + } else { + LogFactory factory = (LogFactory) factories.get(classLoader); + if (factory != null) { + factory.release(); + factories.remove(classLoader); + } + } + } + + } + + + /** + * Release any internal references to previously created {@link LogFactory} + * instances, after calling the instance method release() on + * each of them. This is useful in environments like servlet containers, + * which implement application reloading by throwing away a ClassLoader. + * Dangling references to objects in that class loader would prevent + * garbage collection. + */ + public static void releaseAll() { + + if (isDiagnosticsEnabled()) { + logDiagnostic("Releasing factory for all classloaders."); + } + synchronized (factories) { + Enumeration elements = factories.elements(); + while (elements.hasMoreElements()) { + LogFactory element = (LogFactory) elements.nextElement(); + element.release(); + } + factories.clear(); + + if (nullClassLoaderFactory != null) { + nullClassLoaderFactory.release(); + nullClassLoaderFactory = null; + } + } + + } + + + // ------------------------------------------------------ Protected Methods + + /** + * Safely get access to the classloader for the specified class. + *

+ * Theoretically, calling getClassLoader can throw a security exception, + * and so should be done under an AccessController in order to provide + * maximum flexibility. However in practice people don't appear to use + * security policies that forbid getClassLoader calls. So for the moment + * all code is written to call this method rather than Class.getClassLoader, + * so that we could put AccessController stuff in this method without any + * disruption later if we need to. + *

+ * Even when using an AccessController, however, this method can still + * throw SecurityException. Commons-logging basically relies on the + * ability to access classloaders, ie a policy that forbids all + * classloader access will also prevent commons-logging from working: + * currently this method will throw an exception preventing the entire app + * from starting up. Maybe it would be good to detect this situation and + * just disable all commons-logging? Not high priority though - as stated + * above, security policies that prevent classloader access aren't common. + *

+ * Note that returning an object fetched via an AccessController would + * technically be a security flaw anyway; untrusted code that has access + * to a trusted JCL library could use it to fetch the classloader for + * a class even when forbidden to do so directly. + * + * @since 1.1 + */ + protected static ClassLoader getClassLoader(Class clazz) { + try { + return clazz.getClassLoader(); + } catch(SecurityException ex) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Unable to get classloader for class '" + clazz + + "' due to security restrictions - " + ex.getMessage()); + } + throw ex; + } + } + + /** + * Returns the current context classloader. + *

+ * In versions prior to 1.1, this method did not use an AccessController. + * In version 1.1, an AccessController wrapper was incorrectly added to + * this method, causing a minor security flaw. + *

+ * In version 1.1.1 this change was reverted; this method no longer uses + * an AccessController. User code wishing to obtain the context classloader + * must invoke this method via AccessController.doPrivileged if it needs + * support for that. + * + * @return the context classloader associated with the current thread, + * or null if security doesn't allow it. + * + * @throws LogConfigurationException if there was some weird error while + * attempting to get the context classloader. + * + * @throws SecurityException if the current java security policy doesn't + * allow this class to access the context classloader. + */ + protected static ClassLoader getContextClassLoader() + throws LogConfigurationException { + + return directGetContextClassLoader(); + } + + /** + * Calls LogFactory.directGetContextClassLoader under the control of an + * AccessController class. This means that java code running under a + * security manager that forbids access to ClassLoaders will still work + * if this class is given appropriate privileges, even when the caller + * doesn't have such privileges. Without using an AccessController, the + * the entire call stack must have the privilege before the call is + * allowed. + * + * @return the context classloader associated with the current thread, + * or null if security doesn't allow it. + * + * @throws LogConfigurationException if there was some weird error while + * attempting to get the context classloader. + * + * @throws SecurityException if the current java security policy doesn't + * allow this class to access the context classloader. + */ + private static ClassLoader getContextClassLoaderInternal() + throws LogConfigurationException { + return (ClassLoader)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return directGetContextClassLoader(); + } + }); + } + + /** + * Return the thread context class loader if available; otherwise return + * null. + *

+ * Most/all code should call getContextClassLoaderInternal rather than + * calling this method directly. + *

+ * The thread context class loader is available for JDK 1.2 + * or later, if certain security conditions are met. + *

+ * Note that no internal logging is done within this method because + * this method is called every time LogFactory.getLogger() is called, + * and we don't want too much output generated here. + * + * @exception LogConfigurationException if a suitable class loader + * cannot be identified. + * + * @exception SecurityException if the java security policy forbids + * access to the context classloader from one of the classes in the + * current call stack. + * @since 1.1 + */ + protected static ClassLoader directGetContextClassLoader() + throws LogConfigurationException + { + ClassLoader classLoader = null; + + try { + // Are we running on a JDK 1.2 or later system? + Method method = Thread.class.getMethod("getContextClassLoader", + (Class[]) null); + + // Get the thread context class loader (if there is one) + try { + classLoader = (ClassLoader)method.invoke(Thread.currentThread(), + (Object[]) null); + } catch (IllegalAccessException e) { + throw new LogConfigurationException + ("Unexpected IllegalAccessException", e); + } catch (InvocationTargetException e) { + /** + * InvocationTargetException is thrown by 'invoke' when + * the method being invoked (getContextClassLoader) throws + * an exception. + * + * getContextClassLoader() throws SecurityException when + * the context class loader isn't an ancestor of the + * calling class's class loader, or if security + * permissions are restricted. + * + * In the first case (not related), we want to ignore and + * keep going. We cannot help but also ignore the second + * with the logic below, but other calls elsewhere (to + * obtain a class loader) will trigger this exception where + * we can make a distinction. + */ + if (e.getTargetException() instanceof SecurityException) { + ; // ignore + } else { + // Capture 'e.getTargetException()' exception for details + // alternate: log 'e.getTargetException()', and pass back 'e'. + throw new LogConfigurationException + ("Unexpected InvocationTargetException", e.getTargetException()); + } + } + } catch (NoSuchMethodException e) { + // Assume we are running on JDK 1.1 + classLoader = getClassLoader(LogFactory.class); + + // We deliberately don't log a message here to outputStream; + // this message would be output for every call to LogFactory.getLog() + // when running on JDK1.1 + // + // if (outputStream != null) { + // outputStream.println( + // "Method Thread.getContextClassLoader does not exist;" + // + " assuming this is JDK 1.1, and that the context" + // + " classloader is the same as the class that loaded" + // + " the concrete LogFactory class."); + // } + + } + + // Return the selected class loader + return classLoader; + } + + /** + * Check cached factories (keyed by contextClassLoader) + * + * @param contextClassLoader is the context classloader associated + * with the current thread. This allows separate LogFactory objects + * per component within a container, provided each component has + * a distinct context classloader set. This parameter may be null + * in JDK1.1, and in embedded systems where jcl-using code is + * placed in the bootclasspath. + * + * @return the factory associated with the specified classloader if + * one has previously been created, or null if this is the first time + * we have seen this particular classloader. + */ + private static LogFactory getCachedFactory(ClassLoader contextClassLoader) + { + LogFactory factory = null; + + if (contextClassLoader == null) { + // We have to handle this specially, as factories is a Hashtable + // and those don't accept null as a key value. + // + // nb: nullClassLoaderFactory might be null. That's ok. + factory = nullClassLoaderFactory; + } else { + factory = (LogFactory) factories.get(contextClassLoader); + } + + return factory; + } + + /** + * Remember this factory, so later calls to LogFactory.getCachedFactory + * can return the previously created object (together with all its + * cached Log objects). + * + * @param classLoader should be the current context classloader. Note that + * this can be null under some circumstances; this is ok. + * + * @param factory should be the factory to cache. This should never be null. + */ + private static void cacheFactory(ClassLoader classLoader, LogFactory factory) + { + // Ideally we would assert(factory != null) here. However reporting + // errors from within a logging implementation is a little tricky! + + if (factory != null) { + if (classLoader == null) { + nullClassLoaderFactory = factory; + } else { + factories.put(classLoader, factory); + } + } + } + + /** + * Return a new instance of the specified LogFactory + * implementation class, loaded by the specified class loader. + * If that fails, try the class loader used to load this + * (abstract) LogFactory. + *

+ *

ClassLoader conflicts

+ * Note that there can be problems if the specified ClassLoader is not the + * same as the classloader that loaded this class, ie when loading a + * concrete LogFactory subclass via a context classloader. + *

+ * The problem is the same one that can occur when loading a concrete Log + * subclass via a context classloader. + *

+ * The problem occurs when code running in the context classloader calls + * class X which was loaded via a parent classloader, and class X then calls + * LogFactory.getFactory (either directly or via LogFactory.getLog). Because + * class X was loaded via the parent, it binds to LogFactory loaded via + * the parent. When the code in this method finds some LogFactoryYYYY + * class in the child (context) classloader, and there also happens to be a + * LogFactory class defined in the child classloader, then LogFactoryYYYY + * will be bound to LogFactory@childloader. It cannot be cast to + * LogFactory@parentloader, ie this method cannot return the object as + * the desired type. Note that it doesn't matter if the LogFactory class + * in the child classloader is identical to the LogFactory class in the + * parent classloader, they are not compatible. + *

+ * The solution taken here is to simply print out an error message when + * this occurs then throw an exception. The deployer of the application + * must ensure they remove all occurrences of the LogFactory class from + * the child classloader in order to resolve the issue. Note that they + * do not have to move the custom LogFactory subclass; that is ok as + * long as the only LogFactory class it can find to bind to is in the + * parent classloader. + *

+ * @param factoryClass Fully qualified name of the LogFactory + * implementation class + * @param classLoader ClassLoader from which to load this class + * @param contextClassLoader is the context that this new factory will + * manage logging for. + * + * @exception LogConfigurationException if a suitable instance + * cannot be created + * @since 1.1 + */ + protected static LogFactory newFactory(final String factoryClass, + final ClassLoader classLoader, + final ClassLoader contextClassLoader) + throws LogConfigurationException + { + // Note that any unchecked exceptions thrown by the createFactory + // method will propagate out of this method; in particular a + // ClassCastException can be thrown. + Object result = AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return createFactory(factoryClass, classLoader); + } + }); + + if (result instanceof LogConfigurationException) { + LogConfigurationException ex = (LogConfigurationException) result; + if (isDiagnosticsEnabled()) { + logDiagnostic( + "An error occurred while loading the factory class:" + + ex.getMessage()); + } + throw ex; + } + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Created object " + objectId(result) + + " to manage classloader " + objectId(contextClassLoader)); + } + return (LogFactory)result; + } + + /** + * Method provided for backwards compatibility; see newFactory version that + * takes 3 parameters. + *

+ * This method would only ever be called in some rather odd situation. + * Note that this method is static, so overriding in a subclass doesn't + * have any effect unless this method is called from a method in that + * subclass. However this method only makes sense to use from the + * getFactory method, and as that is almost always invoked via + * LogFactory.getFactory, any custom definition in a subclass would be + * pointless. Only a class with a custom getFactory method, then invoked + * directly via CustomFactoryImpl.getFactory or similar would ever call + * this. Anyway, it's here just in case, though the "managed class loader" + * value output to the diagnostics will not report the correct value. + */ + protected static LogFactory newFactory(final String factoryClass, + final ClassLoader classLoader) { + return newFactory(factoryClass, classLoader, null); + } + + /** + * Implements the operations described in the javadoc for newFactory. + * + * @param factoryClass + * + * @param classLoader used to load the specified factory class. This is + * expected to be either the TCCL or the classloader which loaded this + * class. Note that the classloader which loaded this class might be + * "null" (ie the bootloader) for embedded systems. + * + * @return either a LogFactory object or a LogConfigurationException object. + * @since 1.1 + */ + protected static Object createFactory(String factoryClass, ClassLoader classLoader) { + + // This will be used to diagnose bad configurations + // and allow a useful message to be sent to the user + Class logFactoryClass = null; + try { + if (classLoader != null) { + try { + // First the given class loader param (thread class loader) + + // Warning: must typecast here & allow exception + // to be generated/caught & recast properly. + logFactoryClass = classLoader.loadClass(factoryClass); + if (LogFactory.class.isAssignableFrom(logFactoryClass)) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Loaded class " + logFactoryClass.getName() + + " from classloader " + objectId(classLoader)); + } + } else { + // + // This indicates a problem with the ClassLoader tree. + // An incompatible ClassLoader was used to load the + // implementation. + // As the same classes + // must be available in multiple class loaders, + // it is very likely that multiple JCL jars are present. + // The most likely fix for this + // problem is to remove the extra JCL jars from the + // ClassLoader hierarchy. + // + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Factory class " + logFactoryClass.getName() + + " loaded from classloader " + objectId(logFactoryClass.getClassLoader()) + + " does not extend '" + LogFactory.class.getName() + + "' as loaded by this classloader."); + logHierarchy("[BAD CL TREE] ", classLoader); + } + } + + return (LogFactory) logFactoryClass.newInstance(); + + } catch (ClassNotFoundException ex) { + if (classLoader == thisClassLoader) { + // Nothing more to try, onwards. + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Unable to locate any class called '" + factoryClass + + "' via classloader " + objectId(classLoader)); + } + throw ex; + } + // ignore exception, continue + } catch (NoClassDefFoundError e) { + if (classLoader == thisClassLoader) { + // Nothing more to try, onwards. + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Class '" + factoryClass + "' cannot be loaded" + + " via classloader " + objectId(classLoader) + + " - it depends on some other class that cannot" + + " be found."); + } + throw e; + } + // ignore exception, continue + } catch(ClassCastException e) { + if (classLoader == thisClassLoader) { + // There's no point in falling through to the code below that + // tries again with thisClassLoader, because we've just tried + // loading with that loader (not the TCCL). Just throw an + // appropriate exception here. + + final boolean implementsLogFactory = implementsLogFactory(logFactoryClass); + + // + // Construct a good message: users may not actual expect that a custom implementation + // has been specified. Several well known containers use this mechanism to adapt JCL + // to their native logging system. + // + String msg = + "The application has specified that a custom LogFactory implementation should be used but " + + "Class '" + factoryClass + "' cannot be converted to '" + + LogFactory.class.getName() + "'. "; + if (implementsLogFactory) { + msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " + + "Background can be found in http://commons.apache.org/logging/tech.html. " + + "If you have not explicitly specified a custom LogFactory then it is likely that " + + "the container has set one without your knowledge. " + + "In this case, consider using the commons-logging-adapters.jar file or " + + "specifying the standard LogFactory from the command line. "; + } else { + msg = msg + "Please check the custom implementation. "; + } + msg = msg + "Help can be found @http://commons.apache.org/logging/troubleshooting.html."; + + if (isDiagnosticsEnabled()) { + logDiagnostic(msg); + } + + ClassCastException ex = new ClassCastException(msg); + throw ex; + } + + // Ignore exception, continue. Presumably the classloader was the + // TCCL; the code below will try to load the class via thisClassLoader. + // This will handle the case where the original calling class is in + // a shared classpath but the TCCL has a copy of LogFactory and the + // specified LogFactory implementation; we will fall back to using the + // LogFactory implementation from the same classloader as this class. + // + // Issue: this doesn't handle the reverse case, where this LogFactory + // is in the webapp, and the specified LogFactory implementation is + // in a shared classpath. In that case: + // (a) the class really does implement LogFactory (bad log msg above) + // (b) the fallback code will result in exactly the same problem. + } + } + + /* At this point, either classLoader == null, OR + * classLoader was unable to load factoryClass. + * + * In either case, we call Class.forName, which is equivalent + * to LogFactory.class.getClassLoader().load(name), ie we ignore + * the classloader parameter the caller passed, and fall back + * to trying the classloader associated with this class. See the + * javadoc for the newFactory method for more info on the + * consequences of this. + * + * Notes: + * * LogFactory.class.getClassLoader() may return 'null' + * if LogFactory is loaded by the bootstrap classloader. + */ + // Warning: must typecast here & allow exception + // to be generated/caught & recast properly. + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Unable to load factory class via classloader " + + objectId(classLoader) + + " - trying the classloader associated with this LogFactory."); + } + logFactoryClass = Class.forName(factoryClass); + return (LogFactory) logFactoryClass.newInstance(); + } catch (Exception e) { + // Check to see if we've got a bad configuration + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to create LogFactory instance."); + } + if (logFactoryClass != null + && !LogFactory.class.isAssignableFrom(logFactoryClass)) { + + return new LogConfigurationException( + "The chosen LogFactory implementation does not extend LogFactory." + + " Please check your configuration.", + e); + } + return new LogConfigurationException(e); + } + } + + /** + * Determines whether the given class actually implements LogFactory. + * Diagnostic information is also logged. + *

+ * Usage: to diagnose whether a classloader conflict is the cause + * of incompatibility. The test used is whether the class is assignable from + * the LogFactory class loaded by the class's classloader. + * @param logFactoryClass Class which may implement LogFactory + * @return true if the logFactoryClass does extend + * LogFactory when that class is loaded via the same + * classloader that loaded the logFactoryClass. + */ + private static boolean implementsLogFactory(Class logFactoryClass) { + boolean implementsLogFactory = false; + if (logFactoryClass != null) { + try { + ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader(); + if (logFactoryClassLoader == null) { + logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader"); + } else { + logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader); + Class factoryFromCustomLoader + = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader); + implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass); + if (implementsLogFactory) { + logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName() + + " implements LogFactory but was loaded by an incompatible classloader."); + } else { + logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName() + + " does not implement LogFactory."); + } + } + } catch (SecurityException e) { + // + // The application is running within a hostile security environment. + // This will make it very hard to diagnose issues with JCL. + // Consider running less securely whilst debugging this issue. + // + logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " + + "the compatibility was caused by a classloader conflict: " + + e.getMessage()); + } catch (LinkageError e) { + // + // This should be an unusual circumstance. + // LinkageError's usually indicate that a dependent class has incompatibly changed. + // Another possibility may be an exception thrown by an initializer. + // Time for a clean rebuild? + // + logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " + + "the compatibility was caused by a classloader conflict: " + + e.getMessage()); + } catch (ClassNotFoundException e) { + // + // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation. + // The custom implementation is not viable until this is corrected. + // Ensure that the JCL jar and the custom class are available from the same classloader. + // Running with diagnostics on should give information about the classloaders used + // to load the custom factory. + // + logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the " + + "custom LogFactory implementation. Is the custom factory in the right classloader?"); + } + } + return implementsLogFactory; + } + + /** + * Applets may run in an environment where accessing resources of a loader is + * a secure operation, but where the commons-logging library has explicitly + * been granted permission for that operation. In this case, we need to + * run the operation using an AccessController. + */ + private static InputStream getResourceAsStream(final ClassLoader loader, + final String name) + { + return (InputStream)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + if (loader != null) { + return loader.getResourceAsStream(name); + } else { + return ClassLoader.getSystemResourceAsStream(name); + } + } + }); + } + + /** + * Given a filename, return an enumeration of URLs pointing to + * all the occurrences of that filename in the classpath. + *

+ * This is just like ClassLoader.getResources except that the + * operation is done under an AccessController so that this method will + * succeed when this jarfile is privileged but the caller is not. + * This method must therefore remain private to avoid security issues. + *

+ * If no instances are found, an Enumeration is returned whose + * hasMoreElements method returns false (ie an "empty" enumeration). + * If resources could not be listed for some reason, null is returned. + */ + private static Enumeration getResources(final ClassLoader loader, + final String name) + { + PrivilegedAction action = + new PrivilegedAction() { + public Object run() { + try { + if (loader != null) { + return loader.getResources(name); + } else { + return ClassLoader.getSystemResources(name); + } + } catch(IOException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Exception while trying to find configuration file " + + name + ":" + e.getMessage()); + } + return null; + } catch(NoSuchMethodError e) { + // we must be running on a 1.1 JVM which doesn't support + // ClassLoader.getSystemResources; just return null in + // this case. + return null; + } + } + }; + Object result = AccessController.doPrivileged(action); + return (Enumeration) result; + } + + /** + * Given a URL that refers to a .properties file, load that file. + * This is done under an AccessController so that this method will + * succeed when this jarfile is privileged but the caller is not. + * This method must therefore remain private to avoid security issues. + *

+ * Null is returned if the URL cannot be opened. + */ + private static Properties getProperties(final URL url) { + PrivilegedAction action = + new PrivilegedAction() { + public Object run() { + InputStream stream = null; + try { + // We must ensure that useCaches is set to false, as the + // default behaviour of java is to cache file handles, and + // this "locks" files, preventing hot-redeploy on windows. + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + stream = connection.getInputStream(); + if (stream != null) { + Properties props = new Properties(); + props.load(stream); + stream.close(); + stream = null; + return props; + } + } catch(IOException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to read URL " + url); + } + } finally { + if (stream != null) { + try { + stream.close(); + } catch(Throwable t) { + // ignore exception; this should not happen + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to close stream for URL " + url); + } + } + } + } + + return null; + } + }; + return (Properties) AccessController.doPrivileged(action); + } + + /** + * Locate a user-provided configuration file. + *

+ * The classpath of the specified classLoader (usually the context classloader) + * is searched for properties files of the specified name. If none is found, + * null is returned. If more than one is found, then the file with the greatest + * value for its PRIORITY property is returned. If multiple files have the + * same PRIORITY value then the first in the classpath is returned. + *

+ * This differs from the 1.0.x releases; those always use the first one found. + * However as the priority is a new field, this change is backwards compatible. + *

+ * The purpose of the priority field is to allow a webserver administrator to + * override logging settings in all webapps by placing a commons-logging.properties + * file in a shared classpath location with a priority > 0; this overrides any + * commons-logging.properties files without priorities which are in the + * webapps. Webapps can also use explicit priorities to override a configuration + * file in the shared classpath if needed. + */ + private static final Properties getConfigurationFile( + ClassLoader classLoader, String fileName) { + + Properties props = null; + double priority = 0.0; + URL propsUrl = null; + try { + Enumeration urls = getResources(classLoader, fileName); + + if (urls == null) { + return null; + } + + while (urls.hasMoreElements()) { + URL url = (URL) urls.nextElement(); + + Properties newProps = getProperties(url); + if (newProps != null) { + if (props == null) { + propsUrl = url; + props = newProps; + String priorityStr = props.getProperty(PRIORITY_KEY); + priority = 0.0; + if (priorityStr != null) { + priority = Double.parseDouble(priorityStr); + } + + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Properties file found at '" + url + "'" + + " with priority " + priority); + } + } else { + String newPriorityStr = newProps.getProperty(PRIORITY_KEY); + double newPriority = 0.0; + if (newPriorityStr != null) { + newPriority = Double.parseDouble(newPriorityStr); + } + + if (newPriority > priority) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Properties file at '" + url + "'" + + " with priority " + newPriority + + " overrides file at '" + propsUrl + "'" + + " with priority " + priority); + } + + propsUrl = url; + props = newProps; + priority = newPriority; + } else { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[LOOKUP] Properties file at '" + url + "'" + + " with priority " + newPriority + + " does not override file at '" + propsUrl + "'" + + " with priority " + priority); + } + } + } + + } + } + } catch (SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("SecurityException thrown while trying to find/read config files."); + } + } + + if (isDiagnosticsEnabled()) { + if (props == null) { + logDiagnostic( + "[LOOKUP] No properties file of name '" + fileName + + "' found."); + } else { + logDiagnostic( + "[LOOKUP] Properties file of name '" + fileName + + "' found at '" + propsUrl + '"'); + } + } + + return props; + } + + /** + * Read the specified system property, using an AccessController so that + * the property can be read if JCL has been granted the appropriate + * security rights even if the calling code has not. + *

+ * Take care not to expose the value returned by this method to the + * calling application in any way; otherwise the calling app can use that + * info to access data that should not be available to it. + */ + private static String getSystemProperty(final String key, final String def) + throws SecurityException { + return (String) AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return System.getProperty(key, def); + } + }); + } + + /** + * Determines whether the user wants internal diagnostic output. If so, + * returns an appropriate writer object. Users can enable diagnostic + * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to + * a filename, or the special values STDOUT or STDERR. + */ + private static void initDiagnostics() { + String dest; + try { + dest = getSystemProperty(DIAGNOSTICS_DEST_PROPERTY, null); + if (dest == null) { + return; + } + } catch(SecurityException ex) { + // We must be running in some very secure environment. + // We just have to assume output is not wanted.. + return; + } + + if (dest.equals("STDOUT")) { + diagnosticsStream = System.out; + } else if (dest.equals("STDERR")) { + diagnosticsStream = System.err; + } else { + try { + // open the file in append mode + FileOutputStream fos = new FileOutputStream(dest, true); + diagnosticsStream = new PrintStream(fos); + } catch(IOException ex) { + // We should report this to the user - but how? + return; + } + } + + // In order to avoid confusion where multiple instances of JCL are + // being used via different classloaders within the same app, we + // ensure each logged message has a prefix of form + // [LogFactory from classloader OID] + // + // Note that this prefix should be kept consistent with that + // in LogFactoryImpl. However here we don't need to output info + // about the actual *instance* of LogFactory, as all methods that + // output diagnostics from this class are static. + String classLoaderName; + try { + ClassLoader classLoader = thisClassLoader; + if (thisClassLoader == null) { + classLoaderName = "BOOTLOADER"; + } else { + classLoaderName = objectId(classLoader); + } + } catch(SecurityException e) { + classLoaderName = "UNKNOWN"; + } + diagnosticPrefix = "[LogFactory from " + classLoaderName + "] "; + } + + /** + * Indicates true if the user has enabled internal logging. + *

+ * By the way, sorry for the incorrect grammar, but calling this method + * areDiagnosticsEnabled just isn't java beans style. + * + * @return true if calls to logDiagnostic will have any effect. + * @since 1.1 + */ + protected static boolean isDiagnosticsEnabled() { + return diagnosticsStream != null; + } + + /** + * Write the specified message to the internal logging destination. + *

+ * Note that this method is private; concrete subclasses of this class + * should not call it because the diagnosticPrefix string this + * method puts in front of all its messages is LogFactory@...., + * while subclasses should put SomeSubClass@... + *

+ * Subclasses should instead compute their own prefix, then call + * logRawDiagnostic. Note that calling isDiagnosticsEnabled is + * fine for subclasses. + *

+ * Note that it is safe to call this method before initDiagnostics + * is called; any output will just be ignored (as isDiagnosticsEnabled + * will return false). + * + * @param msg is the diagnostic message to be output. + */ + private static final void logDiagnostic(String msg) { + if (diagnosticsStream != null) { + diagnosticsStream.print(diagnosticPrefix); + diagnosticsStream.println(msg); + diagnosticsStream.flush(); + } + } + + /** + * Write the specified message to the internal logging destination. + * + * @param msg is the diagnostic message to be output. + * @since 1.1 + */ + protected static final void logRawDiagnostic(String msg) { + if (diagnosticsStream != null) { + diagnosticsStream.println(msg); + diagnosticsStream.flush(); + } + } + + /** + * Generate useful diagnostics regarding the classloader tree for + * the specified class. + *

+ * As an example, if the specified class was loaded via a webapp's + * classloader, then you may get the following output: + *

+     * Class com.acme.Foo was loaded via classloader 11111
+     * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT 
+     * 
+ *

+ * This method returns immediately if isDiagnosticsEnabled() + * returns false. + * + * @param clazz is the class whose classloader + tree are to be + * output. + */ + private static void logClassLoaderEnvironment(Class clazz) { + if (!isDiagnosticsEnabled()) { + return; + } + + try { + // Deliberately use System.getProperty here instead of getSystemProperty; if + // the overall security policy for the calling application forbids access to + // these variables then we do not want to output them to the diagnostic stream. + logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir")); + logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path")); + } catch(SecurityException ex) { + logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths."); + } + + String className = clazz.getName(); + ClassLoader classLoader; + + try { + classLoader = getClassLoader(clazz); + } catch(SecurityException ex) { + // not much useful diagnostics we can print here! + logDiagnostic( + "[ENV] Security forbids determining the classloader for " + className); + return; + } + + logDiagnostic( + "[ENV] Class " + className + " was loaded via classloader " + + objectId(classLoader)); + logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader); + } + + /** + * Logs diagnostic messages about the given classloader + * and it's hierarchy. The prefix is prepended to the message + * and is intended to make it easier to understand the logs. + * @param prefix + * @param classLoader + */ + private static void logHierarchy(String prefix, ClassLoader classLoader) { + if (!isDiagnosticsEnabled()) { + return; + } + ClassLoader systemClassLoader; + if (classLoader != null) { + final String classLoaderString = classLoader.toString(); + logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'"); + } + + try { + systemClassLoader = ClassLoader.getSystemClassLoader(); + } catch(SecurityException ex) { + logDiagnostic( + prefix + "Security forbids determining the system classloader."); + return; + } + if (classLoader != null) { + StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:"); + for(;;) { + buf.append(objectId(classLoader)); + if (classLoader == systemClassLoader) { + buf.append(" (SYSTEM) "); + } + + try { + classLoader = classLoader.getParent(); + } catch(SecurityException ex) { + buf.append(" --> SECRET"); + break; + } + + buf.append(" --> "); + if (classLoader == null) { + buf.append("BOOT"); + break; + } + } + logDiagnostic(buf.toString()); + } + } + + /** + * Returns a string that uniquely identifies the specified object, including + * its class. + *

+ * The returned string is of form "classname@hashcode", ie is the same as + * the return value of the Object.toString() method, but works even when + * the specified object's class has overidden the toString method. + * + * @param o may be null. + * @return a string of form classname@hashcode, or "null" if param o is null. + * @since 1.1 + */ + public static String objectId(Object o) { + if (o == null) { + return "null"; + } else { + return o.getClass().getName() + "@" + System.identityHashCode(o); + } + } + + // ---------------------------------------------------------------------- + // Static initialiser block to perform initialisation at class load time. + // + // We can't do this in the class constructor, as there are many + // static methods on this class that can be called before any + // LogFactory instances are created, and they depend upon this + // stuff having been set up. + // + // Note that this block must come after any variable declarations used + // by any methods called from this block, as we want any static initialiser + // associated with the variable to run first. If static initialisers for + // variables run after this code, then (a) their value might be needed + // by methods called from here, and (b) they might *override* any value + // computed here! + // + // So the wisest thing to do is just to place this code at the very end + // of the class file. + // ---------------------------------------------------------------------- + + static { + // note: it's safe to call methods before initDiagnostics (though + // diagnostic output gets discarded). + thisClassLoader = getClassLoader(LogFactory.class); + initDiagnostics(); + logClassLoaderEnvironment(LogFactory.class); + factories = createFactoryStore(); + if (isDiagnosticsEnabled()) { + logDiagnostic("BOOTSTRAP COMPLETED"); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/LogSource.java b/fluidbook/tools/fwstk/src/apache/commons/logging/LogSource.java new file mode 100644 index 000000000..9cb5f1184 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/LogSource.java @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.logging; + + +import java.lang.reflect.Constructor; +import java.util.Hashtable; + +import org.apache.commons.logging.impl.NoOpLog; + + +/** + *

Factory for creating {@link Log} instances. Applications should call + * the makeNewLogInstance() method to instantiate new instances + * of the configured {@link Log} implementation class.

+ * + *

By default, calling getInstance() will use the following + * algorithm:

+ *
    + *
  • If Log4J is available, return an instance of + * org.apache.commons.logging.impl.Log4JLogger.
  • + *
  • If JDK 1.4 or later is available, return an instance of + * org.apache.commons.logging.impl.Jdk14Logger.
  • + *
  • Otherwise, return an instance of + * org.apache.commons.logging.impl.NoOpLog.
  • + *
+ * + *

You can change the default behavior in one of two ways:

+ *
    + *
  • On the startup command line, set the system property + * org.apache.commons.logging.log to the name of the + * org.apache.commons.logging.Log implementation class + * you want to use.
  • + *
  • At runtime, call LogSource.setLogImplementation().
  • + *
+ * + * @deprecated Use {@link LogFactory} instead - The default factory + * implementation performs exactly the same algorithm as this class did + * + * @author Rod Waldhoff + * @version $Id: LogSource.java 424107 2006-07-20 23:15:42Z skitching $ + */ +public class LogSource { + + // ------------------------------------------------------- Class Attributes + + static protected Hashtable logs = new Hashtable(); + + /** Is log4j available (in the current classpath) */ + static protected boolean log4jIsAvailable = false; + + /** Is JDK 1.4 logging available */ + static protected boolean jdk14IsAvailable = false; + + /** Constructor for current log class */ + static protected Constructor logImplctor = null; + + + // ----------------------------------------------------- Class Initializers + + static { + + // Is Log4J Available? + try { + if (null != Class.forName("org.apache.log4j.Logger")) { + log4jIsAvailable = true; + } else { + log4jIsAvailable = false; + } + } catch (Throwable t) { + log4jIsAvailable = false; + } + + // Is JDK 1.4 Logging Available? + try { + if ((null != Class.forName("java.util.logging.Logger")) && + (null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger"))) { + jdk14IsAvailable = true; + } else { + jdk14IsAvailable = false; + } + } catch (Throwable t) { + jdk14IsAvailable = false; + } + + // Set the default Log implementation + String name = null; + try { + name = System.getProperty("org.apache.commons.logging.log"); + if (name == null) { + name = System.getProperty("org.apache.commons.logging.Log"); + } + } catch (Throwable t) { + } + if (name != null) { + try { + setLogImplementation(name); + } catch (Throwable t) { + try { + setLogImplementation + ("org.apache.commons.logging.impl.NoOpLog"); + } catch (Throwable u) { + ; + } + } + } else { + try { + if (log4jIsAvailable) { + setLogImplementation + ("org.apache.commons.logging.impl.Log4JLogger"); + } else if (jdk14IsAvailable) { + setLogImplementation + ("org.apache.commons.logging.impl.Jdk14Logger"); + } else { + setLogImplementation + ("org.apache.commons.logging.impl.NoOpLog"); + } + } catch (Throwable t) { + try { + setLogImplementation + ("org.apache.commons.logging.impl.NoOpLog"); + } catch (Throwable u) { + ; + } + } + } + + } + + + // ------------------------------------------------------------ Constructor + + + /** Don't allow others to create instances */ + private LogSource() { + } + + + // ---------------------------------------------------------- Class Methods + + + /** + * Set the log implementation/log implementation factory + * by the name of the class. The given class + * must implement {@link Log}, and provide a constructor that + * takes a single {@link String} argument (containing the name + * of the log). + */ + static public void setLogImplementation(String classname) throws + LinkageError, ExceptionInInitializerError, + NoSuchMethodException, SecurityException, + ClassNotFoundException { + try { + Class logclass = Class.forName(classname); + Class[] argtypes = new Class[1]; + argtypes[0] = "".getClass(); + logImplctor = logclass.getConstructor(argtypes); + } catch (Throwable t) { + logImplctor = null; + } + } + + + /** + * Set the log implementation/log implementation factory + * by class. The given class must implement {@link Log}, + * and provide a constructor that takes a single {@link String} + * argument (containing the name of the log). + */ + static public void setLogImplementation(Class logclass) throws + LinkageError, ExceptionInInitializerError, + NoSuchMethodException, SecurityException { + Class[] argtypes = new Class[1]; + argtypes[0] = "".getClass(); + logImplctor = logclass.getConstructor(argtypes); + } + + + /** Get a Log instance by class name */ + static public Log getInstance(String name) { + Log log = (Log) (logs.get(name)); + if (null == log) { + log = makeNewLogInstance(name); + logs.put(name, log); + } + return log; + } + + + /** Get a Log instance by class */ + static public Log getInstance(Class clazz) { + return getInstance(clazz.getName()); + } + + + /** + * Create a new {@link Log} implementation, based + * on the given name. + *

+ * The specific {@link Log} implementation returned + * is determined by the value of the + * org.apache.commons.logging.log property. + * The value of org.apache.commons.logging.log may be set to + * the fully specified name of a class that implements + * the {@link Log} interface. This class must also + * have a public constructor that takes a single + * {@link String} argument (containing the name + * of the {@link Log} to be constructed. + *

+ * When org.apache.commons.logging.log is not set, + * or when no corresponding class can be found, + * this method will return a Log4JLogger + * if the log4j Logger class is + * available in the {@link LogSource}'s classpath, or a + * Jdk14Logger if we are on a JDK 1.4 or later system, or + * NoOpLog if neither of the above conditions is true. + * + * @param name the log name (or category) + */ + static public Log makeNewLogInstance(String name) { + + Log log = null; + try { + Object[] args = new Object[1]; + args[0] = name; + log = (Log) (logImplctor.newInstance(args)); + } catch (Throwable t) { + log = null; + } + if (null == log) { + log = new NoOpLog(name); + } + return log; + + } + + + /** + * Returns a {@link String} array containing the names of + * all logs known to me. + */ + static public String[] getLogNames() { + return (String[]) (logs.keySet().toArray(new String[logs.size()])); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/AvalonLogger.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/AvalonLogger.java new file mode 100644 index 000000000..bcf699788 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/AvalonLogger.java @@ -0,0 +1,292 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.logging.impl; + +import org.apache.avalon.framework.logger.Logger; +import org.apache.commons.logging.Log; + +/** + *

Implementation of commons-logging Log interface that delegates all + * logging calls to the Avalon logging abstraction: the Logger interface. + *

+ *

+ * There are two ways in which this class can be used: + *

+ *
    + *
  • the instance can be constructed with an Avalon logger + * (by calling {@link #AvalonLogger(Logger)}). In this case, it acts + * as a simple thin wrapping implementation over the logger. This is + * particularly useful when using a property setter. + *
  • + *
  • the {@link #setDefaultLogger} class property can be called which + * sets the ancesteral Avalon logger for this class. Any AvalonLogger + * instances created through the LogFactory mechanisms will output + * to child loggers of this Logger. + *
  • + *
+ *

+ * Note: AvalonLogger does not implement Serializable + * because the constructors available for it make this impossible to achieve in all + * circumstances; there is no way to "reconnect" to an underlying Logger object on + * deserialization if one was just passed in to the constructor of the original + * object. This class was marked Serializable in the 1.0.4 release of + * commons-logging, but this never actually worked (a NullPointerException would + * be thrown as soon as the deserialized object was used), so removing this marker + * is not considered to be an incompatible change. + *

+ * @author Neeme Praks + * @version $Revision: 424107 $ $Date: 2006-07-21 01:15:42 +0200 (ven., 21 juil. 2006) $ + */ +public class AvalonLogger implements Log { + + /** Ancesteral avalon logger */ + private static Logger defaultLogger = null; + /** Avalon logger used to perform log */ + private transient Logger logger = null; + + /** + * Constructs an AvalonLogger that outputs to the given + * Logger instance. + * @param logger the avalon logger implementation to delegate to + */ + public AvalonLogger(Logger logger) { + this.logger = logger; + } + + /** + * Constructs an AvalonLogger that will log to a child + * of the Logger set by calling {@link #setDefaultLogger}. + * @param name the name of the avalon logger implementation to delegate to + */ + public AvalonLogger(String name) { + if (defaultLogger == null) + throw new NullPointerException("default logger has to be specified if this constructor is used!"); + this.logger = defaultLogger.getChildLogger(name); + } + + /** + * Gets the Avalon logger implementation used to perform logging. + * @return avalon logger implementation + */ + public Logger getLogger() { + return logger; + } + + /** + * Sets the ancesteral Avalon logger from which the delegating loggers + * will descend. + * @param logger the default avalon logger, + * in case there is no logger instance supplied in constructor + */ + public static void setDefaultLogger(Logger logger) { + defaultLogger = logger; + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.debug. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + public void debug(Object message, Throwable t) { + if (getLogger().isDebugEnabled()) getLogger().debug(String.valueOf(message), t); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.debug. + * + * @param message to log. + * @see org.apache.commons.logging.Log#debug(Object) + */ + public void debug(Object message) { + if (getLogger().isDebugEnabled()) getLogger().debug(String.valueOf(message)); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.error. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + public void error(Object message, Throwable t) { + if (getLogger().isErrorEnabled()) getLogger().error(String.valueOf(message), t); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.error. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + public void error(Object message) { + if (getLogger().isErrorEnabled()) getLogger().error(String.valueOf(message)); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.fatalError. + * + * @param message to log. + * @param t log this cause. + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + public void fatal(Object message, Throwable t) { + if (getLogger().isFatalErrorEnabled()) getLogger().fatalError(String.valueOf(message), t); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.fatalError. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + public void fatal(Object message) { + if (getLogger().isFatalErrorEnabled()) getLogger().fatalError(String.valueOf(message)); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.info. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + public void info(Object message, Throwable t) { + if (getLogger().isInfoEnabled()) getLogger().info(String.valueOf(message), t); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.info. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + public void info(Object message) { + if (getLogger().isInfoEnabled()) getLogger().info(String.valueOf(message)); + } + + /** + * Is logging to + * org.apache.avalon.framework.logger.Logger.debug enabled? + * @see org.apache.commons.logging.Log#isDebugEnabled() + */ + public boolean isDebugEnabled() { + return getLogger().isDebugEnabled(); + } + + /** + * Is logging to + * org.apache.avalon.framework.logger.Logger.error enabled? + * @see org.apache.commons.logging.Log#isErrorEnabled() + */ + public boolean isErrorEnabled() { + return getLogger().isErrorEnabled(); + } + + /** + * Is logging to + * org.apache.avalon.framework.logger.Logger.fatalError enabled? + * @see org.apache.commons.logging.Log#isFatalEnabled() + */ + public boolean isFatalEnabled() { + return getLogger().isFatalErrorEnabled(); + } + + /** + * Is logging to + * org.apache.avalon.framework.logger.Logger.info enabled? + * @see org.apache.commons.logging.Log#isInfoEnabled() + */ + public boolean isInfoEnabled() { + return getLogger().isInfoEnabled(); + } + + /** + * Is logging to + * org.apache.avalon.framework.logger.Logger.debug enabled? + * @see org.apache.commons.logging.Log#isTraceEnabled() + */ + public boolean isTraceEnabled() { + return getLogger().isDebugEnabled(); + } + + /** + * Is logging to + * org.apache.avalon.framework.logger.Logger.warn enabled? + * @see org.apache.commons.logging.Log#isWarnEnabled() + */ + public boolean isWarnEnabled() { + return getLogger().isWarnEnabled(); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.debug. + * + * @param message to log. + * @param t log this cause. + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + public void trace(Object message, Throwable t) { + if (getLogger().isDebugEnabled()) getLogger().debug(String.valueOf(message), t); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.debug. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + public void trace(Object message) { + if (getLogger().isDebugEnabled()) getLogger().debug(String.valueOf(message)); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.warn. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + public void warn(Object message, Throwable t) { + if (getLogger().isWarnEnabled()) getLogger().warn(String.valueOf(message), t); + } + + /** + * Logs a message with + * org.apache.avalon.framework.logger.Logger.warn. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + public void warn(Object message) { + if (getLogger().isWarnEnabled()) getLogger().warn(String.valueOf(message)); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk13LumberjackLogger.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk13LumberjackLogger.java new file mode 100644 index 000000000..c64b86846 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk13LumberjackLogger.java @@ -0,0 +1,335 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + + +import java.io.Serializable; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.LogRecord; +import java.util.StringTokenizer; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.apache.commons.logging.Log; + + +/** + *

Implementation of the org.apache.commons.logging.Log + * interface that wraps the standard JDK logging mechanisms that are + * available in SourceForge's Lumberjack for JDKs prior to 1.4.

+ * + * @author Scott Sanders + * @author Berin Loritsch + * @author Peter Donald + * @author Vince Eagen + * @version $Revision: 424107 $ $Date: 2006-07-21 01:15:42 +0200 (ven., 21 juil. 2006) $ + * @since 1.1 + */ + +public class Jdk13LumberjackLogger implements Log, Serializable { + + + // ----------------------------------------------------- Instance Variables + + + /** + * The underlying Logger implementation we are using. + */ + protected transient Logger logger = null; + protected String name = null; + private String sourceClassName = "unknown"; + private String sourceMethodName = "unknown"; + private boolean classAndMethodFound = false; + + + /** + * This member variable simply ensures that any attempt to initialise + * this class in a pre-1.4 JVM will result in an ExceptionInInitializerError. + * It must not be private, as an optimising compiler could detect that it + * is not used and optimise it away. + */ + protected static final Level dummyLevel = Level.FINE; + + // ----------------------------------------------------------- Constructors + + + /** + * Construct a named instance of this Logger. + * + * @param name Name of the logger to be constructed + */ + public Jdk13LumberjackLogger(String name) { + + this.name = name; + logger = getLogger(); + + } + + + // --------------------------------------------------------- Public Methods + + + private void log( Level level, String msg, Throwable ex ) { + if( getLogger().isLoggable(level) ) { + LogRecord record = new LogRecord(level, msg); + if( !classAndMethodFound ) { + getClassAndMethod(); + } + record.setSourceClassName(sourceClassName); + record.setSourceMethodName(sourceMethodName); + if( ex != null ) { + record.setThrown(ex); + } + getLogger().log(record); + } + } + + /** + *

Gets the class and method by looking at the stack trace for the + * first entry that is not this class.

+ */ + private void getClassAndMethod() { + try { + Throwable throwable = new Throwable(); + throwable.fillInStackTrace(); + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter( stringWriter ); + throwable.printStackTrace( printWriter ); + String traceString = stringWriter.getBuffer().toString(); + StringTokenizer tokenizer = + new StringTokenizer( traceString, "\n" ); + tokenizer.nextToken(); + String line = tokenizer.nextToken(); + while ( line.indexOf( this.getClass().getName() ) == -1 ) { + line = tokenizer.nextToken(); + } + while ( line.indexOf( this.getClass().getName() ) >= 0 ) { + line = tokenizer.nextToken(); + } + int start = line.indexOf( "at " ) + 3; + int end = line.indexOf( '(' ); + String temp = line.substring( start, end ); + int lastPeriod = temp.lastIndexOf( '.' ); + sourceClassName = temp.substring( 0, lastPeriod ); + sourceMethodName = temp.substring( lastPeriod + 1 ); + } catch ( Exception ex ) { + // ignore - leave class and methodname unknown + } + classAndMethodFound = true; + } + + /** + * Logs a message with java.util.logging.Level.FINE. + * + * @param message to log + * @see org.apache.commons.logging.Log#debug(Object) + */ + public void debug(Object message) { + log(Level.FINE, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.FINE. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + public void debug(Object message, Throwable exception) { + log(Level.FINE, String.valueOf(message), exception); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + public void error(Object message) { + log(Level.SEVERE, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + public void error(Object message, Throwable exception) { + log(Level.SEVERE, String.valueOf(message), exception); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + public void fatal(Object message) { + log(Level.SEVERE, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + public void fatal(Object message, Throwable exception) { + log(Level.SEVERE, String.valueOf(message), exception); + } + + + /** + * Return the native Logger instance we are using. + */ + public Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger(name); + } + return (logger); + } + + + /** + * Logs a message with java.util.logging.Level.INFO. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + public void info(Object message) { + log(Level.INFO, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.INFO. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + public void info(Object message, Throwable exception) { + log(Level.INFO, String.valueOf(message), exception); + } + + + /** + * Is debug logging currently enabled? + */ + public boolean isDebugEnabled() { + return (getLogger().isLoggable(Level.FINE)); + } + + + /** + * Is error logging currently enabled? + */ + public boolean isErrorEnabled() { + return (getLogger().isLoggable(Level.SEVERE)); + } + + + /** + * Is fatal logging currently enabled? + */ + public boolean isFatalEnabled() { + return (getLogger().isLoggable(Level.SEVERE)); + } + + + /** + * Is info logging currently enabled? + */ + public boolean isInfoEnabled() { + return (getLogger().isLoggable(Level.INFO)); + } + + + /** + * Is trace logging currently enabled? + */ + public boolean isTraceEnabled() { + return (getLogger().isLoggable(Level.FINEST)); + } + + + /** + * Is warn logging currently enabled? + */ + public boolean isWarnEnabled() { + return (getLogger().isLoggable(Level.WARNING)); + } + + + /** + * Logs a message with java.util.logging.Level.FINEST. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + public void trace(Object message) { + log(Level.FINEST, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.FINEST. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + public void trace(Object message, Throwable exception) { + log(Level.FINEST, String.valueOf(message), exception); + } + + + /** + * Logs a message with java.util.logging.Level.WARNING. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + public void warn(Object message) { + log(Level.WARNING, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.WARNING. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + public void warn(Object message, Throwable exception) { + log(Level.WARNING, String.valueOf(message), exception); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk14Logger.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk14Logger.java new file mode 100644 index 000000000..0de81f43c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Jdk14Logger.java @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + + +import java.io.Serializable; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.logging.Log; + + +/** + *

Implementation of the org.apache.commons.logging.Log + * interface that wraps the standard JDK logging mechanisms that were + * introduced in the Merlin release (JDK 1.4).

+ * + * @author Scott Sanders + * @author Berin Loritsch + * @author Peter Donald + * @version $Revision: 424107 $ $Date: 2006-07-21 01:15:42 +0200 (ven., 21 juil. 2006) $ + */ + +public class Jdk14Logger implements Log, Serializable { + + /** + * This member variable simply ensures that any attempt to initialise + * this class in a pre-1.4 JVM will result in an ExceptionInInitializerError. + * It must not be private, as an optimising compiler could detect that it + * is not used and optimise it away. + */ + protected static final Level dummyLevel = Level.FINE; + + // ----------------------------------------------------------- Constructors + + + /** + * Construct a named instance of this Logger. + * + * @param name Name of the logger to be constructed + */ + public Jdk14Logger(String name) { + + this.name = name; + logger = getLogger(); + + } + + + // ----------------------------------------------------- Instance Variables + + + /** + * The underlying Logger implementation we are using. + */ + protected transient Logger logger = null; + + + /** + * The name of the logger we are wrapping. + */ + protected String name = null; + + + // --------------------------------------------------------- Public Methods + + private void log( Level level, String msg, Throwable ex ) { + + Logger logger = getLogger(); + if (logger.isLoggable(level)) { + // Hack (?) to get the stack trace. + Throwable dummyException=new Throwable(); + StackTraceElement locations[]=dummyException.getStackTrace(); + // Caller will be the third element + String cname="unknown"; + String method="unknown"; + if( locations!=null && locations.length >2 ) { + StackTraceElement caller=locations[2]; + cname=caller.getClassName(); + method=caller.getMethodName(); + } + if( ex==null ) { + logger.logp( level, cname, method, msg ); + } else { + logger.logp( level, cname, method, msg, ex ); + } + } + + } + + /** + * Logs a message with java.util.logging.Level.FINE. + * + * @param message to log + * @see org.apache.commons.logging.Log#debug(Object) + */ + public void debug(Object message) { + log(Level.FINE, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.FINE. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + public void debug(Object message, Throwable exception) { + log(Level.FINE, String.valueOf(message), exception); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + public void error(Object message) { + log(Level.SEVERE, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + public void error(Object message, Throwable exception) { + log(Level.SEVERE, String.valueOf(message), exception); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + public void fatal(Object message) { + log(Level.SEVERE, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.SEVERE. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + public void fatal(Object message, Throwable exception) { + log(Level.SEVERE, String.valueOf(message), exception); + } + + + /** + * Return the native Logger instance we are using. + */ + public Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger(name); + } + return (logger); + } + + + /** + * Logs a message with java.util.logging.Level.INFO. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + public void info(Object message) { + log(Level.INFO, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.INFO. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + public void info(Object message, Throwable exception) { + log(Level.INFO, String.valueOf(message), exception); + } + + + /** + * Is debug logging currently enabled? + */ + public boolean isDebugEnabled() { + return (getLogger().isLoggable(Level.FINE)); + } + + + /** + * Is error logging currently enabled? + */ + public boolean isErrorEnabled() { + return (getLogger().isLoggable(Level.SEVERE)); + } + + + /** + * Is fatal logging currently enabled? + */ + public boolean isFatalEnabled() { + return (getLogger().isLoggable(Level.SEVERE)); + } + + + /** + * Is info logging currently enabled? + */ + public boolean isInfoEnabled() { + return (getLogger().isLoggable(Level.INFO)); + } + + + /** + * Is trace logging currently enabled? + */ + public boolean isTraceEnabled() { + return (getLogger().isLoggable(Level.FINEST)); + } + + + /** + * Is warn logging currently enabled? + */ + public boolean isWarnEnabled() { + return (getLogger().isLoggable(Level.WARNING)); + } + + + /** + * Logs a message with java.util.logging.Level.FINEST. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + public void trace(Object message) { + log(Level.FINEST, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.FINEST. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + public void trace(Object message, Throwable exception) { + log(Level.FINEST, String.valueOf(message), exception); + } + + + /** + * Logs a message with java.util.logging.Level.WARNING. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + public void warn(Object message) { + log(Level.WARNING, String.valueOf(message), null); + } + + + /** + * Logs a message with java.util.logging.Level.WARNING. + * + * @param message to log + * @param exception log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + public void warn(Object message, Throwable exception) { + log(Level.WARNING, String.valueOf(message), exception); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Log4JLogger.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Log4JLogger.java new file mode 100644 index 000000000..d609c0c4a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/Log4JLogger.java @@ -0,0 +1,342 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + +import java.io.Serializable; +import org.apache.commons.logging.Log; +import org.apache.log4j.Logger; +import org.apache.log4j.Priority; +import org.apache.log4j.Level; + +/** + * Implementation of {@link Log} that maps directly to a + * Logger for log4J version 1.2. + *

+ * Initial configuration of the corresponding Logger instances should be done + * in the usual manner, as outlined in the Log4J documentation. + *

+ * The reason this logger is distinct from the 1.3 logger is that in version 1.2 + * of Log4J: + *

    + *
  • class Logger takes Priority parameters not Level parameters. + *
  • class Level extends Priority + *
+ * Log4J1.3 is expected to change Level so it no longer extends Priority, which is + * a non-binary-compatible change. The class generated by compiling this code against + * log4j 1.2 will therefore not run against log4j 1.3. + * + * @author Scott Sanders + * @author Rod Waldhoff + * @author Robert Burrell Donkin + * @version $Id: Log4JLogger.java 479747 2006-11-27 20:15:01Z dennisl $ + */ + +public class Log4JLogger implements Log, Serializable { + + // ------------------------------------------------------------- Attributes + + /** The fully qualified name of the Log4JLogger class. */ + private static final String FQCN = Log4JLogger.class.getName(); + + /** Log to this logger */ + private transient Logger logger = null; + + /** Logger name */ + private String name = null; + + private static Priority traceLevel; + + // ------------------------------------------------------------ + // Static Initializer. + // + // Note that this must come after the static variable declarations + // otherwise initialiser expressions associated with those variables + // will override any settings done here. + // + // Verify that log4j is available, and that it is version 1.2. + // If an ExceptionInInitializerError is generated, then LogFactoryImpl + // will treat that as meaning that the appropriate underlying logging + // library is just not present - if discovery is in progress then + // discovery will continue. + // ------------------------------------------------------------ + + static { + if (!Priority.class.isAssignableFrom(Level.class)) { + // nope, this is log4j 1.3, so force an ExceptionInInitializerError + throw new InstantiationError("Log4J 1.2 not available"); + } + + // Releases of log4j1.2 >= 1.2.12 have Priority.TRACE available, earlier + // versions do not. If TRACE is not available, then we have to map + // calls to Log.trace(...) onto the DEBUG level. + + try { + traceLevel = (Priority) Level.class.getDeclaredField("TRACE").get(null); + } catch(Exception ex) { + // ok, trace not available + traceLevel = Priority.DEBUG; + } + } + + + // ------------------------------------------------------------ Constructor + + public Log4JLogger() { + } + + + /** + * Base constructor. + */ + public Log4JLogger(String name) { + this.name = name; + this.logger = getLogger(); + } + + /** + * For use with a log4j factory. + */ + public Log4JLogger(Logger logger ) { + if (logger == null) { + throw new IllegalArgumentException( + "Warning - null logger in constructor; possible log4j misconfiguration."); + } + this.name = logger.getName(); + this.logger=logger; + } + + + // --------------------------------------------------------- + // Implementation + // + // Note that in the methods below the Priority class is used to define + // levels even though the Level class is supported in 1.2. This is done + // so that at compile time the call definitely resolves to a call to + // a method that takes a Priority rather than one that takes a Level. + // + // The Category class (and hence its subclass Logger) in version 1.2 only + // has methods that take Priority objects. The Category class (and hence + // Logger class) in version 1.3 has methods that take both Priority and + // Level objects. This means that if we use Level here, and compile + // against log4j 1.3 then calls would be bound to the versions of + // methods taking Level objects and then would fail to run against + // version 1.2 of log4j. + // --------------------------------------------------------- + + + /** + * Logs a message with org.apache.log4j.Priority.TRACE. + * When using a log4j version that does not support the TRACE + * level, the message will be logged at the DEBUG level. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + public void trace(Object message) { + getLogger().log(FQCN, traceLevel, message, null ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.TRACE. + * When using a log4j version that does not support the TRACE + * level, the message will be logged at the DEBUG level. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + public void trace(Object message, Throwable t) { + getLogger().log(FQCN, traceLevel, message, t ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.DEBUG. + * + * @param message to log + * @see org.apache.commons.logging.Log#debug(Object) + */ + public void debug(Object message) { + getLogger().log(FQCN, Priority.DEBUG, message, null ); + } + + /** + * Logs a message with org.apache.log4j.Priority.DEBUG. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + public void debug(Object message, Throwable t) { + getLogger().log(FQCN, Priority.DEBUG, message, t ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.INFO. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + public void info(Object message) { + getLogger().log(FQCN, Priority.INFO, message, null ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.INFO. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + public void info(Object message, Throwable t) { + getLogger().log(FQCN, Priority.INFO, message, t ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.WARN. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + public void warn(Object message) { + getLogger().log(FQCN, Priority.WARN, message, null ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.WARN. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + public void warn(Object message, Throwable t) { + getLogger().log(FQCN, Priority.WARN, message, t ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.ERROR. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + public void error(Object message) { + getLogger().log(FQCN, Priority.ERROR, message, null ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.ERROR. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + public void error(Object message, Throwable t) { + getLogger().log(FQCN, Priority.ERROR, message, t ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.FATAL. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + public void fatal(Object message) { + getLogger().log(FQCN, Priority.FATAL, message, null ); + } + + + /** + * Logs a message with org.apache.log4j.Priority.FATAL. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + public void fatal(Object message, Throwable t) { + getLogger().log(FQCN, Priority.FATAL, message, t ); + } + + + /** + * Return the native Logger instance we are using. + */ + public Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger(name); + } + return (this.logger); + } + + + /** + * Check whether the Log4j Logger used is enabled for DEBUG priority. + */ + public boolean isDebugEnabled() { + return getLogger().isDebugEnabled(); + } + + + /** + * Check whether the Log4j Logger used is enabled for ERROR priority. + */ + public boolean isErrorEnabled() { + return getLogger().isEnabledFor(Priority.ERROR); + } + + + /** + * Check whether the Log4j Logger used is enabled for FATAL priority. + */ + public boolean isFatalEnabled() { + return getLogger().isEnabledFor(Priority.FATAL); + } + + + /** + * Check whether the Log4j Logger used is enabled for INFO priority. + */ + public boolean isInfoEnabled() { + return getLogger().isInfoEnabled(); + } + + + /** + * Check whether the Log4j Logger used is enabled for TRACE priority. + * When using a log4j version that does not support the TRACE level, this call + * will report whether DEBUG is enabled or not. + */ + public boolean isTraceEnabled() { + return getLogger().isEnabledFor(traceLevel); + } + + /** + * Check whether the Log4j Logger used is enabled for WARN priority. + */ + public boolean isWarnEnabled() { + return getLogger().isEnabledFor(Priority.WARN); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogFactoryImpl.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogFactoryImpl.java new file mode 100644 index 000000000..b4fa8db97 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogFactoryImpl.java @@ -0,0 +1,1500 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.logging.impl; + + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogConfigurationException; +import org.apache.commons.logging.LogFactory; + + +/** + *

Concrete subclass of {@link LogFactory} that implements the + * following algorithm to dynamically select a logging implementation + * class to instantiate a wrapper for.

+ *
    + *
  • Use a factory configuration attribute named + * org.apache.commons.logging.Log to identify the + * requested implementation class.
  • + *
  • Use the org.apache.commons.logging.Log system property + * to identify the requested implementation class.
  • + *
  • If Log4J is available, return an instance of + * org.apache.commons.logging.impl.Log4JLogger.
  • + *
  • If JDK 1.4 or later is available, return an instance of + * org.apache.commons.logging.impl.Jdk14Logger.
  • + *
  • Otherwise, return an instance of + * org.apache.commons.logging.impl.SimpleLog.
  • + *
+ * + *

If the selected {@link Log} implementation class has a + * setLogFactory() method that accepts a {@link LogFactory} + * parameter, this method will be called on each newly created instance + * to identify the associated factory. This makes factory configuration + * attributes available to the Log instance, if it so desires.

+ * + *

This factory will remember previously created Log instances + * for the same name, and will return them on repeated requests to the + * getInstance() method.

+ * + * @author Rod Waldhoff + * @author Craig R. McClanahan + * @author Richard A. Sitze + * @author Brian Stansberry + * @version $Revision: 581090 $ $Date: 2007-10-02 00:01:06 +0200 (mar., 02 oct. 2007) $ + */ + +public class LogFactoryImpl extends LogFactory { + + + /** Log4JLogger class name */ + private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger"; + /** Jdk14Logger class name */ + private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger"; + /** Jdk13LumberjackLogger class name */ + private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger"; + /** SimpleLog class name */ + private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog"; + + private static final String PKG_IMPL="org.apache.commons.logging.impl."; + private static final int PKG_LEN = PKG_IMPL.length(); + + // ----------------------------------------------------------- Constructors + + + + /** + * Public no-arguments constructor required by the lookup mechanism. + */ + public LogFactoryImpl() { + super(); + initDiagnostics(); // method on this object + if (isDiagnosticsEnabled()) { + logDiagnostic("Instance created."); + } + } + + + // ----------------------------------------------------- Manifest Constants + + + /** + * The name (org.apache.commons.logging.Log) of the system + * property identifying our {@link Log} implementation class. + */ + public static final String LOG_PROPERTY = + "org.apache.commons.logging.Log"; + + + /** + * The deprecated system property used for backwards compatibility with + * old versions of JCL. + */ + protected static final String LOG_PROPERTY_OLD = + "org.apache.commons.logging.log"; + + /** + * The name (org.apache.commons.logging.Log.allowFlawedContext) + * of the system property which can be set true/false to + * determine system behaviour when a bad context-classloader is encountered. + * When set to false, a LogConfigurationException is thrown if + * LogFactoryImpl is loaded via a child classloader of the TCCL (this + * should never happen in sane systems). + * + * Default behaviour: true (tolerates bad context classloaders) + * + * See also method setAttribute. + */ + public static final String ALLOW_FLAWED_CONTEXT_PROPERTY = + "org.apache.commons.logging.Log.allowFlawedContext"; + + /** + * The name (org.apache.commons.logging.Log.allowFlawedDiscovery) + * of the system property which can be set true/false to + * determine system behaviour when a bad logging adapter class is + * encountered during logging discovery. When set to false, an + * exception will be thrown and the app will fail to start. When set + * to true, discovery will continue (though the user might end up + * with a different logging implementation than they expected). + * + * Default behaviour: true (tolerates bad logging adapters) + * + * See also method setAttribute. + */ + public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY = + "org.apache.commons.logging.Log.allowFlawedDiscovery"; + + /** + * The name (org.apache.commons.logging.Log.allowFlawedHierarchy) + * of the system property which can be set true/false to + * determine system behaviour when a logging adapter class is + * encountered which has bound to the wrong Log class implementation. + * When set to false, an exception will be thrown and the app will fail + * to start. When set to true, discovery will continue (though the user + * might end up with a different logging implementation than they expected). + * + * Default behaviour: true (tolerates bad Log class hierarchy) + * + * See also method setAttribute. + */ + public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY = + "org.apache.commons.logging.Log.allowFlawedHierarchy"; + + + /** + * The names of classes that will be tried (in order) as logging + * adapters. Each class is expected to implement the Log interface, + * and to throw NoClassDefFound or ExceptionInInitializerError when + * loaded if the underlying logging library is not available. Any + * other error indicates that the underlying logging library is available + * but broken/unusable for some reason. + */ + private static final String[] classesToDiscover = { + LOGGING_IMPL_LOG4J_LOGGER, + "org.apache.commons.logging.impl.Jdk14Logger", + "org.apache.commons.logging.impl.Jdk13LumberjackLogger", + "org.apache.commons.logging.impl.SimpleLog" + }; + + + // ----------------------------------------------------- Instance Variables + + /** + * Determines whether logging classes should be loaded using the thread-context + * classloader, or via the classloader that loaded this LogFactoryImpl class. + */ + private boolean useTCCL = true; + + /** + * The string prefixed to every message output by the logDiagnostic method. + */ + private String diagnosticPrefix; + + + /** + * Configuration attributes. + */ + protected Hashtable attributes = new Hashtable(); + + + /** + * The {@link org.apache.commons.logging.Log} instances that have + * already been created, keyed by logger name. + */ + protected Hashtable instances = new Hashtable(); + + + /** + * Name of the class implementing the Log interface. + */ + private String logClassName; + + + /** + * The one-argument constructor of the + * {@link org.apache.commons.logging.Log} + * implementation class that will be used to create new instances. + * This value is initialized by getLogConstructor(), + * and then returned repeatedly. + */ + protected Constructor logConstructor = null; + + + /** + * The signature of the Constructor to be used. + */ + protected Class logConstructorSignature[] = + { java.lang.String.class }; + + + /** + * The one-argument setLogFactory method of the selected + * {@link org.apache.commons.logging.Log} method, if it exists. + */ + protected Method logMethod = null; + + + /** + * The signature of the setLogFactory method to be used. + */ + protected Class logMethodSignature[] = + { LogFactory.class }; + + /** + * See getBaseClassLoader and initConfiguration. + */ + private boolean allowFlawedContext; + + /** + * See handleFlawedDiscovery and initConfiguration. + */ + private boolean allowFlawedDiscovery; + + /** + * See handleFlawedHierarchy and initConfiguration. + */ + private boolean allowFlawedHierarchy; + + // --------------------------------------------------------- Public Methods + + + /** + * Return the configuration attribute with the specified name (if any), + * or null if there is no such attribute. + * + * @param name Name of the attribute to return + */ + public Object getAttribute(String name) { + + return (attributes.get(name)); + + } + + + /** + * Return an array containing the names of all currently defined + * configuration attributes. If there are no such attributes, a zero + * length array is returned. + */ + public String[] getAttributeNames() { + + Vector names = new Vector(); + Enumeration keys = attributes.keys(); + while (keys.hasMoreElements()) { + names.addElement((String) keys.nextElement()); + } + String results[] = new String[names.size()]; + for (int i = 0; i < results.length; i++) { + results[i] = (String) names.elementAt(i); + } + return (results); + + } + + + /** + * Convenience method to derive a name from the specified class and + * call getInstance(String) with it. + * + * @param clazz Class for which a suitable Log name will be derived + * + * @exception LogConfigurationException if a suitable Log + * instance cannot be returned + */ + public Log getInstance(Class clazz) throws LogConfigurationException { + + return (getInstance(clazz.getName())); + + } + + + /** + *

Construct (if necessary) and return a Log instance, + * using the factory's current set of configuration attributes.

+ * + *

NOTE - Depending upon the implementation of + * the LogFactory you are using, the Log + * instance you are returned may or may not be local to the current + * application, and may or may not be returned again on a subsequent + * call with the same name argument.

+ * + * @param name Logical name of the Log instance to be + * returned (the meaning of this name is only known to the underlying + * logging implementation that is being wrapped) + * + * @exception LogConfigurationException if a suitable Log + * instance cannot be returned + */ + public Log getInstance(String name) throws LogConfigurationException { + + Log instance = (Log) instances.get(name); + if (instance == null) { + instance = newInstance(name); + instances.put(name, instance); + } + return (instance); + + } + + + /** + * Release any internal references to previously created + * {@link org.apache.commons.logging.Log} + * instances returned by this factory. This is useful in environments + * like servlet containers, which implement application reloading by + * throwing away a ClassLoader. Dangling references to objects in that + * class loader would prevent garbage collection. + */ + public void release() { + + logDiagnostic("Releasing all known loggers"); + instances.clear(); + } + + + /** + * Remove any configuration attribute associated with the specified name. + * If there is no such attribute, no action is taken. + * + * @param name Name of the attribute to remove + */ + public void removeAttribute(String name) { + + attributes.remove(name); + + } + + + /** + * Set the configuration attribute with the specified name. Calling + * this with a null value is equivalent to calling + * removeAttribute(name). + *

+ * This method can be used to set logging configuration programmatically + * rather than via system properties. It can also be used in code running + * within a container (such as a webapp) to configure behaviour on a + * per-component level instead of globally as system properties would do. + * To use this method instead of a system property, call + *

+     * LogFactory.getFactory().setAttribute(...)
+     * 
+ * This must be done before the first Log object is created; configuration + * changes after that point will be ignored. + *

+ * This method is also called automatically if LogFactory detects a + * commons-logging.properties file; every entry in that file is set + * automatically as an attribute here. + * + * @param name Name of the attribute to set + * @param value Value of the attribute to set, or null + * to remove any setting for this attribute + */ + public void setAttribute(String name, Object value) { + + if (logConstructor != null) { + logDiagnostic("setAttribute: call too late; configuration already performed."); + } + + if (value == null) { + attributes.remove(name); + } else { + attributes.put(name, value); + } + + if (name.equals(TCCL_KEY)) { + useTCCL = Boolean.valueOf(value.toString()).booleanValue(); + } + + } + + + // ------------------------------------------------------ + // Static Methods + // + // These methods only defined as workarounds for a java 1.2 bug; + // theoretically none of these are needed. + // ------------------------------------------------------ + + /** + * Gets the context classloader. + * This method is a workaround for a java 1.2 compiler bug. + * @since 1.1 + */ + protected static ClassLoader getContextClassLoader() throws LogConfigurationException { + return LogFactory.getContextClassLoader(); + } + + + /** + * Workaround for bug in Java1.2; in theory this method is not needed. + * See LogFactory.isDiagnosticsEnabled. + */ + protected static boolean isDiagnosticsEnabled() { + return LogFactory.isDiagnosticsEnabled(); + } + + + /** + * Workaround for bug in Java1.2; in theory this method is not needed. + * See LogFactory.getClassLoader. + * @since 1.1 + */ + protected static ClassLoader getClassLoader(Class clazz) { + return LogFactory.getClassLoader(clazz); + } + + + // ------------------------------------------------------ Protected Methods + + /** + * Calculate and cache a string that uniquely identifies this instance, + * including which classloader the object was loaded from. + *

+ * This string will later be prefixed to each "internal logging" message + * emitted, so that users can clearly see any unexpected behaviour. + *

+ * Note that this method does not detect whether internal logging is + * enabled or not, nor where to output stuff if it is; that is all + * handled by the parent LogFactory class. This method just computes + * its own unique prefix for log messages. + */ + private void initDiagnostics() { + // It would be nice to include an identifier of the context classloader + // that this LogFactoryImpl object is responsible for. However that + // isn't possible as that information isn't available. It is possible + // to figure this out by looking at the logging from LogFactory to + // see the context & impl ids from when this object was instantiated, + // in order to link the impl id output as this object's prefix back to + // the context it is intended to manage. + // Note that this prefix should be kept consistent with that + // in LogFactory. + Class clazz = this.getClass(); + ClassLoader classLoader = getClassLoader(clazz); + String classLoaderName; + try { + if (classLoader == null) { + classLoaderName = "BOOTLOADER"; + } else { + classLoaderName = objectId(classLoader); + } + } catch(SecurityException e) { + classLoaderName = "UNKNOWN"; + } + diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] "; + } + + + /** + * Output a diagnostic message to a user-specified destination (if the + * user has enabled diagnostic logging). + * + * @param msg diagnostic message + * @since 1.1 + */ + protected void logDiagnostic(String msg) { + if (isDiagnosticsEnabled()) { + logRawDiagnostic(diagnosticPrefix + msg); + } + } + + /** + * Return the fully qualified Java classname of the {@link Log} + * implementation we will be using. + * + * @deprecated Never invoked by this class; subclasses should not assume + * it will be. + */ + protected String getLogClassName() { + + if (logClassName == null) { + discoverLogImplementation(getClass().getName()); + } + + return logClassName; + } + + + /** + *

Return the Constructor that can be called to instantiate + * new {@link org.apache.commons.logging.Log} instances.

+ * + *

IMPLEMENTATION NOTE - Race conditions caused by + * calling this method from more than one thread are ignored, because + * the same Constructor instance will ultimately be derived + * in all circumstances.

+ * + * @exception LogConfigurationException if a suitable constructor + * cannot be returned + * + * @deprecated Never invoked by this class; subclasses should not assume + * it will be. + */ + protected Constructor getLogConstructor() + throws LogConfigurationException { + + // Return the previously identified Constructor (if any) + if (logConstructor == null) { + discoverLogImplementation(getClass().getName()); + } + + return logConstructor; + } + + + /** + * Is JDK 1.3 with Lumberjack logging available? + * + * @deprecated Never invoked by this class; subclasses should not assume + * it will be. + */ + protected boolean isJdk13LumberjackAvailable() { + return isLogLibraryAvailable( + "Jdk13Lumberjack", + "org.apache.commons.logging.impl.Jdk13LumberjackLogger"); + } + + + /** + *

Return true if JDK 1.4 or later logging + * is available. Also checks that the Throwable class + * supports getStackTrace(), which is required by + * Jdk14Logger.

+ * + * @deprecated Never invoked by this class; subclasses should not assume + * it will be. + */ + protected boolean isJdk14Available() { + return isLogLibraryAvailable( + "Jdk14", + "org.apache.commons.logging.impl.Jdk14Logger"); + } + + + /** + * Is a Log4J implementation available? + * + * @deprecated Never invoked by this class; subclasses should not assume + * it will be. + */ + protected boolean isLog4JAvailable() { + return isLogLibraryAvailable( + "Log4J", + LOGGING_IMPL_LOG4J_LOGGER); + } + + + /** + * Create and return a new {@link org.apache.commons.logging.Log} + * instance for the specified name. + * + * @param name Name of the new logger + * + * @exception LogConfigurationException if a new instance cannot + * be created + */ + protected Log newInstance(String name) throws LogConfigurationException { + + Log instance = null; + try { + if (logConstructor == null) { + instance = discoverLogImplementation(name); + } + else { + Object params[] = { name }; + instance = (Log) logConstructor.newInstance(params); + } + + if (logMethod != null) { + Object params[] = { this }; + logMethod.invoke(instance, params); + } + + return (instance); + + } catch (LogConfigurationException lce) { + + // this type of exception means there was a problem in discovery + // and we've already output diagnostics about the issue, etc.; + // just pass it on + throw (LogConfigurationException) lce; + + } catch (InvocationTargetException e) { + // A problem occurred invoking the Constructor or Method + // previously discovered + Throwable c = e.getTargetException(); + if (c != null) { + throw new LogConfigurationException(c); + } else { + throw new LogConfigurationException(e); + } + } catch (Throwable t) { + // A problem occurred invoking the Constructor or Method + // previously discovered + throw new LogConfigurationException(t); + } + } + + + // ------------------------------------------------------ Private Methods + + /** + * Calls LogFactory.directGetContextClassLoader under the control of an + * AccessController class. This means that java code running under a + * security manager that forbids access to ClassLoaders will still work + * if this class is given appropriate privileges, even when the caller + * doesn't have such privileges. Without using an AccessController, the + * the entire call stack must have the privilege before the call is + * allowed. + * + * @return the context classloader associated with the current thread, + * or null if security doesn't allow it. + * + * @throws LogConfigurationException if there was some weird error while + * attempting to get the context classloader. + * + * @throws SecurityException if the current java security policy doesn't + * allow this class to access the context classloader. + */ + private static ClassLoader getContextClassLoaderInternal() + throws LogConfigurationException { + return (ClassLoader)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return LogFactory.directGetContextClassLoader(); + } + }); + } + + /** + * Read the specified system property, using an AccessController so that + * the property can be read if JCL has been granted the appropriate + * security rights even if the calling code has not. + *

+ * Take care not to expose the value returned by this method to the + * calling application in any way; otherwise the calling app can use that + * info to access data that should not be available to it. + */ + private static String getSystemProperty(final String key, final String def) + throws SecurityException { + return (String) AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return System.getProperty(key, def); + } + }); + } + + /** + * Fetch the parent classloader of a specified classloader. + *

+ * If a SecurityException occurs, null is returned. + *

+ * Note that this method is non-static merely so logDiagnostic is available. + */ + private ClassLoader getParentClassLoader(final ClassLoader cl) { + try { + return (ClassLoader)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return cl.getParent(); + } + }); + } catch(SecurityException ex) { + logDiagnostic("[SECURITY] Unable to obtain parent classloader"); + return null; + } + + } + + /** + * Utility method to check whether a particular logging library is + * present and available for use. Note that this does not + * affect the future behaviour of this class. + */ + private boolean isLogLibraryAvailable(String name, String classname) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Checking for '" + name + "'."); + } + try { + Log log = createLogFromClass( + classname, + this.getClass().getName(), // dummy category + false); + + if (log == null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Did not find '" + name + "'."); + } + return false; + } else { + if (isDiagnosticsEnabled()) { + logDiagnostic("Found '" + name + "'."); + } + return true; + } + } catch(LogConfigurationException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Logging system '" + name + "' is available but not useable."); + } + return false; + } + } + + /** + * Attempt to find an attribute (see method setAttribute) or a + * system property with the provided name and return its value. + *

+ * The attributes associated with this object are checked before + * system properties in case someone has explicitly called setAttribute, + * or a configuration property has been set in a commons-logging.properties + * file. + * + * @return the value associated with the property, or null. + */ + private String getConfigurationValue(String property) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Trying to get configuration for item " + property); + } + + Object valueObj = getAttribute(property); + if (valueObj != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property); + } + return valueObj.toString(); + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] No LogFactory attribute found for " + property); + } + + try { + // warning: minor security hole here, in that we potentially read a system + // property that the caller cannot, then output it in readable form as a + // diagnostic message. However it's only ever JCL-specific properties + // involved here, so the harm is truly trivial. + String value = getSystemProperty(property, null); + if (value != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Found system property [" + value + "] for " + property); + } + return value; + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] No system property found for property " + property); + } + } catch (SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Security prevented reading system property " + property); + } + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] No configuration defined for item " + property); + } + + return null; + } + + /** + * Get the setting for the user-configurable behaviour specified by key. + * If nothing has explicitly been set, then return dflt. + */ + private boolean getBooleanConfiguration(String key, boolean dflt) { + String val = getConfigurationValue(key); + if (val == null) + return dflt; + return Boolean.valueOf(val).booleanValue(); + } + + /** + * Initialize a number of variables that control the behaviour of this + * class and that can be tweaked by the user. This is done when the first + * logger is created, not in the constructor of this class, because we + * need to give the user a chance to call method setAttribute in order to + * configure this object. + */ + private void initConfiguration() { + allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true); + allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true); + allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true); + } + + + /** + * Attempts to create a Log instance for the given category name. + * Follows the discovery process described in the class javadoc. + * + * @param logCategory the name of the log category + * + * @throws LogConfigurationException if an error in discovery occurs, + * or if no adapter at all can be instantiated + */ + private Log discoverLogImplementation(String logCategory) + throws LogConfigurationException + { + if (isDiagnosticsEnabled()) { + logDiagnostic("Discovering a Log implementation..."); + } + + initConfiguration(); + + Log result = null; + + // See if the user specified the Log implementation to use + String specifiedLogClassName = findUserSpecifiedLogClassName(); + + if (specifiedLogClassName != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Attempting to load user-specified log class '" + + specifiedLogClassName + "'..."); + } + + result = createLogFromClass(specifiedLogClassName, + logCategory, + true); + if (result == null) { + StringBuffer messageBuffer = new StringBuffer("User-specified log class '"); + messageBuffer.append(specifiedLogClassName); + messageBuffer.append("' cannot be found or is not useable."); + + // Mistyping or misspelling names is a common fault. + // Construct a good error message, if we can + if (specifiedLogClassName != null) { + informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER); + informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER); + informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER); + informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER); + } + throw new LogConfigurationException(messageBuffer.toString()); + } + + return result; + } + + // No user specified log; try to discover what's on the classpath + // + // Note that we deliberately loop here over classesToDiscover and + // expect method createLogFromClass to loop over the possible source + // classloaders. The effect is: + // for each discoverable log adapter + // for each possible classloader + // see if it works + // + // It appears reasonable at first glance to do the opposite: + // for each possible classloader + // for each discoverable log adapter + // see if it works + // + // The latter certainly has advantages for user-installable logging + // libraries such as log4j; in a webapp for example this code should + // first check whether the user has provided any of the possible + // logging libraries before looking in the parent classloader. + // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4, + // and SimpleLog will always work in any JVM. So the loop would never + // ever look for logging libraries in the parent classpath. Yet many + // users would expect that putting log4j there would cause it to be + // detected (and this is the historical JCL behaviour). So we go with + // the first approach. A user that has bundled a specific logging lib + // in a webapp should use a commons-logging.properties file or a + // service file in META-INF to force use of that logging lib anyway, + // rather than relying on discovery. + + if (isDiagnosticsEnabled()) { + logDiagnostic( + "No user-specified Log implementation; performing discovery" + + " using the standard supported logging implementations..."); + } + for(int i=0; (iStringBuffer the message should be appended to, + * not null + * @param name the (trimmed) name to be test against the candidate, not null + * @param candidate the candidate name (not null) + */ + private void informUponSimilarName(final StringBuffer messageBuffer, final String name, + final String candidate) { + if (name.equals(candidate)) { + // Don't suggest a name that is exactly the same as the one the + // user tried... + return; + } + + // If the user provides a name that is in the right package, and gets + // the first 5 characters of the adapter class right (ignoring case), + // then suggest the candidate adapter class name. + if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) { + messageBuffer.append(" Did you mean '"); + messageBuffer.append(candidate); + messageBuffer.append("'?"); + } + } + + + /** + * Checks system properties and the attribute map for + * a Log implementation specified by the user under the + * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}. + * + * @return classname specified by the user, or null + */ + private String findUserSpecifiedLogClassName() + { + if (isDiagnosticsEnabled()) { + logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'"); + } + String specifiedClass = (String) getAttribute(LOG_PROPERTY); + + if (specifiedClass == null) { // @deprecated + if (isDiagnosticsEnabled()) { + logDiagnostic("Trying to get log class from attribute '" + + LOG_PROPERTY_OLD + "'"); + } + specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD); + } + + if (specifiedClass == null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Trying to get log class from system property '" + + LOG_PROPERTY + "'"); + } + try { + specifiedClass = getSystemProperty(LOG_PROPERTY, null); + } catch (SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("No access allowed to system property '" + + LOG_PROPERTY + "' - " + e.getMessage()); + } + } + } + + if (specifiedClass == null) { // @deprecated + if (isDiagnosticsEnabled()) { + logDiagnostic("Trying to get log class from system property '" + + LOG_PROPERTY_OLD + "'"); + } + try { + specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null); + } catch (SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("No access allowed to system property '" + + LOG_PROPERTY_OLD + "' - " + e.getMessage()); + } + } + } + + // Remove any whitespace; it's never valid in a classname so its + // presence just means a user mistake. As we know what they meant, + // we may as well strip the spaces. + if (specifiedClass != null) { + specifiedClass = specifiedClass.trim(); + } + + return specifiedClass; + } + + + /** + * Attempts to load the given class, find a suitable constructor, + * and instantiate an instance of Log. + * + * @param logAdapterClassName classname of the Log implementation + * + * @param logCategory argument to pass to the Log implementation's + * constructor + * + * @param affectState true if this object's state should + * be affected by this method call, false otherwise. + * + * @return an instance of the given class, or null if the logging + * library associated with the specified adapter is not available. + * + * @throws LogConfigurationException if there was a serious error with + * configuration and the handleFlawedDiscovery method decided this + * problem was fatal. + */ + private Log createLogFromClass(String logAdapterClassName, + String logCategory, + boolean affectState) + throws LogConfigurationException { + + if (isDiagnosticsEnabled()) { + logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'"); + } + + Object[] params = { logCategory }; + Log logAdapter = null; + Constructor constructor = null; + + Class logAdapterClass = null; + ClassLoader currentCL = getBaseClassLoader(); + + for(;;) { + // Loop through the classloader hierarchy trying to find + // a viable classloader. + logDiagnostic( + "Trying to load '" + + logAdapterClassName + + "' from classloader " + + objectId(currentCL)); + try { + if (isDiagnosticsEnabled()) { + // Show the location of the first occurrence of the .class file + // in the classpath. This is the location that ClassLoader.loadClass + // will load the class from -- unless the classloader is doing + // something weird. + URL url; + String resourceName = logAdapterClassName.replace('.', '/') + ".class"; + if (currentCL != null) { + url = currentCL.getResource(resourceName ); + } else { + url = ClassLoader.getSystemResource(resourceName + ".class"); + } + + if (url == null) { + logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found."); + } else { + logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'"); + } + } + + Class c = null; + try { + c = Class.forName(logAdapterClassName, true, currentCL); + } catch (ClassNotFoundException originalClassNotFoundException) { + // The current classloader was unable to find the log adapter + // in this or any ancestor classloader. There's no point in + // trying higher up in the hierarchy in this case.. + String msg = "" + originalClassNotFoundException.getMessage(); + logDiagnostic( + "The log adapter '" + + logAdapterClassName + + "' is not available via classloader " + + objectId(currentCL) + + ": " + + msg.trim()); + try { + // Try the class classloader. + // This may work in cases where the TCCL + // does not contain the code executed or JCL. + // This behaviour indicates that the application + // classloading strategy is not consistent with the + // Java 1.2 classloading guidelines but JCL can + // and so should handle this case. + c = Class.forName(logAdapterClassName); + } catch (ClassNotFoundException secondaryClassNotFoundException) { + // no point continuing: this adapter isn't available + msg = "" + secondaryClassNotFoundException.getMessage(); + logDiagnostic( + "The log adapter '" + + logAdapterClassName + + "' is not available via the LogFactoryImpl class classloader: " + + msg.trim()); + break; + } + } + + constructor = c.getConstructor(logConstructorSignature); + Object o = constructor.newInstance(params); + + // Note that we do this test after trying to create an instance + // [rather than testing Log.class.isAssignableFrom(c)] so that + // we don't complain about Log hierarchy problems when the + // adapter couldn't be instantiated anyway. + if (o instanceof Log) { + logAdapterClass = c; + logAdapter = (Log) o; + break; + } + + // Oops, we have a potential problem here. An adapter class + // has been found and its underlying lib is present too, but + // there are multiple Log interface classes available making it + // impossible to cast to the type the caller wanted. We + // certainly can't use this logger, but we need to know whether + // to keep on discovering or terminate now. + // + // The handleFlawedHierarchy method will throw + // LogConfigurationException if it regards this problem as + // fatal, and just return if not. + handleFlawedHierarchy(currentCL, c); + } catch (NoClassDefFoundError e) { + // We were able to load the adapter but it had references to + // other classes that could not be found. This simply means that + // the underlying logger library is not present in this or any + // ancestor classloader. There's no point in trying higher up + // in the hierarchy in this case.. + String msg = "" + e.getMessage(); + logDiagnostic( + "The log adapter '" + + logAdapterClassName + + "' is missing dependencies when loaded via classloader " + + objectId(currentCL) + + ": " + + msg.trim()); + break; + } catch (ExceptionInInitializerError e) { + // A static initializer block or the initializer code associated + // with a static variable on the log adapter class has thrown + // an exception. + // + // We treat this as meaning the adapter's underlying logging + // library could not be found. + String msg = "" + e.getMessage(); + logDiagnostic( + "The log adapter '" + + logAdapterClassName + + "' is unable to initialize itself when loaded via classloader " + + objectId(currentCL) + + ": " + + msg.trim()); + break; + } catch(LogConfigurationException e) { + // call to handleFlawedHierarchy above must have thrown + // a LogConfigurationException, so just throw it on + throw e; + } catch(Throwable t) { + // handleFlawedDiscovery will determine whether this is a fatal + // problem or not. If it is fatal, then a LogConfigurationException + // will be thrown. + handleFlawedDiscovery(logAdapterClassName, currentCL, t); + } + + if (currentCL == null) { + break; + } + + // try the parent classloader + // currentCL = currentCL.getParent(); + currentCL = getParentClassLoader(currentCL); + } + + if ((logAdapter != null) && affectState) { + // We've succeeded, so set instance fields + this.logClassName = logAdapterClassName; + this.logConstructor = constructor; + + // Identify the setLogFactory method (if there is one) + try { + this.logMethod = logAdapterClass.getMethod("setLogFactory", + logMethodSignature); + logDiagnostic("Found method setLogFactory(LogFactory) in '" + + logAdapterClassName + "'"); + } catch (Throwable t) { + this.logMethod = null; + logDiagnostic( + "[INFO] '" + logAdapterClassName + + "' from classloader " + objectId(currentCL) + + " does not declare optional method " + + "setLogFactory(LogFactory)"); + } + + logDiagnostic( + "Log adapter '" + logAdapterClassName + + "' from classloader " + objectId(logAdapterClass.getClassLoader()) + + " has been selected for use."); + } + + return logAdapter; + } + + + /** + * Return the classloader from which we should try to load the logging + * adapter classes. + *

+ * This method usually returns the context classloader. However if it + * is discovered that the classloader which loaded this class is a child + * of the context classloader and the allowFlawedContext option + * has been set then the classloader which loaded this class is returned + * instead. + *

+ * The only time when the classloader which loaded this class is a + * descendant (rather than the same as or an ancestor of the context + * classloader) is when an app has created custom classloaders but + * failed to correctly set the context classloader. This is a bug in + * the calling application; however we provide the option for JCL to + * simply generate a warning rather than fail outright. + * + */ + private ClassLoader getBaseClassLoader() throws LogConfigurationException { + ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class); + + if (useTCCL == false) { + return thisClassLoader; + } + + ClassLoader contextClassLoader = getContextClassLoaderInternal(); + + ClassLoader baseClassLoader = getLowestClassLoader( + contextClassLoader, thisClassLoader); + + if (baseClassLoader == null) { + // The two classloaders are not part of a parent child relationship. + // In some classloading setups (e.g. JBoss with its + // UnifiedLoaderRepository) this can still work, so if user hasn't + // forbidden it, just return the contextClassLoader. + if (allowFlawedContext) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "[WARNING] the context classloader is not part of a" + + " parent-child relationship with the classloader that" + + " loaded LogFactoryImpl."); + } + // If contextClassLoader were null, getLowestClassLoader() would + // have returned thisClassLoader. The fact we are here means + // contextClassLoader is not null, so we can just return it. + return contextClassLoader; + } + else { + throw new LogConfigurationException( + "Bad classloader hierarchy; LogFactoryImpl was loaded via" + + " a classloader that is not related to the current context" + + " classloader."); + } + } + + if (baseClassLoader != contextClassLoader) { + // We really should just use the contextClassLoader as the starting + // point for scanning for log adapter classes. However it is expected + // that there are a number of broken systems out there which create + // custom classloaders but fail to set the context classloader so + // we handle those flawed systems anyway. + if (allowFlawedContext) { + if (isDiagnosticsEnabled()) { + logDiagnostic( + "Warning: the context classloader is an ancestor of the" + + " classloader that loaded LogFactoryImpl; it should be" + + " the same or a descendant. The application using" + + " commons-logging should ensure the context classloader" + + " is used correctly."); + } + } else { + throw new LogConfigurationException( + "Bad classloader hierarchy; LogFactoryImpl was loaded via" + + " a classloader that is not related to the current context" + + " classloader."); + } + } + + return baseClassLoader; + } + + /** + * Given two related classloaders, return the one which is a child of + * the other. + *

+ * @param c1 is a classloader (including the null classloader) + * @param c2 is a classloader (including the null classloader) + * + * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor, + * and null if neither is an ancestor of the other. + */ + private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) { + // TODO: use AccessController when dealing with classloaders here + + if (c1 == null) + return c2; + + if (c2 == null) + return c1; + + ClassLoader current; + + // scan c1's ancestors to find c2 + current = c1; + while (current != null) { + if (current == c2) + return c1; + current = current.getParent(); + } + + // scan c2's ancestors to find c1 + current = c2; + while (current != null) { + if (current == c1) + return c2; + current = current.getParent(); + } + + return null; + } + + /** + * Generates an internal diagnostic logging of the discovery failure and + * then throws a LogConfigurationException that wraps + * the passed Throwable. + * + * @param logAdapterClassName is the class name of the Log implementation + * that could not be instantiated. Cannot be null. + * + * @param classLoader is the classloader that we were trying to load the + * logAdapterClassName from when the exception occurred. + * + * @param discoveryFlaw is the Throwable created by the classloader + * + * @throws LogConfigurationException ALWAYS + */ + private void handleFlawedDiscovery(String logAdapterClassName, + ClassLoader classLoader, + Throwable discoveryFlaw) { + + if (isDiagnosticsEnabled()) { + logDiagnostic("Could not instantiate Log '" + + logAdapterClassName + "' -- " + + discoveryFlaw.getClass().getName() + ": " + + discoveryFlaw.getLocalizedMessage()); + + if (discoveryFlaw instanceof InvocationTargetException ) { + // Ok, the lib is there but while trying to create a real underlying + // logger something failed in the underlying lib; display info about + // that if possible. + InvocationTargetException ite = (InvocationTargetException)discoveryFlaw; + Throwable cause = ite.getTargetException(); + if (cause != null) { + logDiagnostic("... InvocationTargetException: " + + cause.getClass().getName() + ": " + + cause.getLocalizedMessage()); + + if (cause instanceof ExceptionInInitializerError) { + ExceptionInInitializerError eiie = (ExceptionInInitializerError)cause; + Throwable cause2 = eiie.getException(); + if (cause2 != null) { + logDiagnostic("... ExceptionInInitializerError: " + + cause2.getClass().getName() + ": " + + cause2.getLocalizedMessage()); + } + } + } + } + } + + if (!allowFlawedDiscovery) { + throw new LogConfigurationException(discoveryFlaw); + } + } + + + /** + * Report a problem loading the log adapter, then either return + * (if the situation is considered recoverable) or throw a + * LogConfigurationException. + *

+ * There are two possible reasons why we successfully loaded the + * specified log adapter class then failed to cast it to a Log object: + *

    + *
  1. the specific class just doesn't implement the Log interface + * (user screwed up), or + *
  2. the specified class has bound to a Log class loaded by some other + * classloader; Log@classloaderX cannot be cast to Log@classloaderY. + *
+ *

+ * Here we try to figure out which case has occurred so we can give the + * user some reasonable feedback. + * + * @param badClassLoader is the classloader we loaded the problem class from, + * ie it is equivalent to badClass.getClassLoader(). + * + * @param badClass is a Class object with the desired name, but which + * does not implement Log correctly. + * + * @throws LogConfigurationException when the situation + * should not be recovered from. + */ + private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass) + throws LogConfigurationException { + + boolean implementsLog = false; + String logInterfaceName = Log.class.getName(); + Class interfaces[] = badClass.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (logInterfaceName.equals(interfaces[i].getName())) { + implementsLog = true; + break; + } + } + + if (implementsLog) { + // the class does implement an interface called Log, but + // it is in the wrong classloader + if (isDiagnosticsEnabled()) { + try { + ClassLoader logInterfaceClassLoader = getClassLoader(Log.class); + logDiagnostic( + "Class '" + badClass.getName() + + "' was found in classloader " + + objectId(badClassLoader) + + ". It is bound to a Log interface which is not" + + " the one loaded from classloader " + + objectId(logInterfaceClassLoader)); + } catch (Throwable t) { + logDiagnostic( + "Error while trying to output diagnostics about" + + " bad class '" + badClass + "'"); + } + } + + if (!allowFlawedHierarchy) { + StringBuffer msg = new StringBuffer(); + msg.append("Terminating logging for this context "); + msg.append("due to bad log hierarchy. "); + msg.append("You have more than one version of '"); + msg.append(Log.class.getName()); + msg.append("' visible."); + if (isDiagnosticsEnabled()) { + logDiagnostic(msg.toString()); + } + throw new LogConfigurationException(msg.toString()); + } + + if (isDiagnosticsEnabled()) { + StringBuffer msg = new StringBuffer(); + msg.append("Warning: bad log hierarchy. "); + msg.append("You have more than one version of '"); + msg.append(Log.class.getName()); + msg.append("' visible."); + logDiagnostic(msg.toString()); + } + } else { + // this is just a bad adapter class + if (!allowFlawedDiscovery) { + StringBuffer msg = new StringBuffer(); + msg.append("Terminating logging for this context. "); + msg.append("Log class '"); + msg.append(badClass.getName()); + msg.append("' does not implement the Log interface."); + if (isDiagnosticsEnabled()) { + logDiagnostic(msg.toString()); + } + + throw new LogConfigurationException(msg.toString()); + } + + if (isDiagnosticsEnabled()) { + StringBuffer msg = new StringBuffer(); + msg.append("[WARNING] Log class '"); + msg.append(badClass.getName()); + msg.append("' does not implement the Log interface."); + logDiagnostic(msg.toString()); + } + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogKitLogger.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogKitLogger.java new file mode 100644 index 000000000..5e6cf4b7c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/LogKitLogger.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + +import java.io.Serializable; +import org.apache.log.Logger; +import org.apache.log.Hierarchy; +import org.apache.commons.logging.Log; + +/** + *

Implementation of org.apache.commons.logging.Log + * that wraps the avalon-logkit + * logging system. Configuration of LogKit is left to the user. + *

+ * + *

LogKit accepts only String messages. + * Therefore, this implementation converts object messages into strings + * by called their toString() method before logging them.

+ * + * @author Scott Sanders + * @author Robert Burrell Donkin + * @version $Id: LogKitLogger.java 424107 2006-07-20 23:15:42Z skitching $ + */ + +public class LogKitLogger implements Log, Serializable { + + + // ------------------------------------------------------------- Attributes + + + /** Logging goes to this LogKit logger */ + protected transient Logger logger = null; + + /** Name of this logger */ + protected String name = null; + + + // ------------------------------------------------------------ Constructor + + + /** + * Construct LogKitLogger which wraps the LogKit + * logger with given name. + * + * @param name log name + */ + public LogKitLogger(String name) { + this.name = name; + this.logger = getLogger(); + } + + + // --------------------------------------------------------- Public Methods + + + /** + *

Return the underlying Logger we are using.

+ */ + public Logger getLogger() { + + if (logger == null) { + logger = Hierarchy.getDefaultHierarchy().getLoggerFor(name); + } + return (logger); + + } + + + // ----------------------------------------------------- Log Implementation + + + /** + * Logs a message with org.apache.log.Priority.DEBUG. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + public void trace(Object message) { + debug(message); + } + + + /** + * Logs a message with org.apache.log.Priority.DEBUG. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + public void trace(Object message, Throwable t) { + debug(message, t); + } + + + /** + * Logs a message with org.apache.log.Priority.DEBUG. + * + * @param message to log + * @see org.apache.commons.logging.Log#debug(Object) + */ + public void debug(Object message) { + if (message != null) { + getLogger().debug(String.valueOf(message)); + } + } + + + /** + * Logs a message with org.apache.log.Priority.DEBUG. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + public void debug(Object message, Throwable t) { + if (message != null) { + getLogger().debug(String.valueOf(message), t); + } + } + + + /** + * Logs a message with org.apache.log.Priority.INFO. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + public void info(Object message) { + if (message != null) { + getLogger().info(String.valueOf(message)); + } + } + + + /** + * Logs a message with org.apache.log.Priority.INFO. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + public void info(Object message, Throwable t) { + if (message != null) { + getLogger().info(String.valueOf(message), t); + } + } + + + /** + * Logs a message with org.apache.log.Priority.WARN. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + public void warn(Object message) { + if (message != null) { + getLogger().warn(String.valueOf(message)); + } + } + + + /** + * Logs a message with org.apache.log.Priority.WARN. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + public void warn(Object message, Throwable t) { + if (message != null) { + getLogger().warn(String.valueOf(message), t); + } + } + + + /** + * Logs a message with org.apache.log.Priority.ERROR. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + public void error(Object message) { + if (message != null) { + getLogger().error(String.valueOf(message)); + } + } + + + /** + * Logs a message with org.apache.log.Priority.ERROR. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + public void error(Object message, Throwable t) { + if (message != null) { + getLogger().error(String.valueOf(message), t); + } + } + + + /** + * Logs a message with org.apache.log.Priority.FATAL_ERROR. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + public void fatal(Object message) { + if (message != null) { + getLogger().fatalError(String.valueOf(message)); + } + } + + + /** + * Logs a message with org.apache.log.Priority.FATAL_ERROR. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + public void fatal(Object message, Throwable t) { + if (message != null) { + getLogger().fatalError(String.valueOf(message), t); + } + } + + + /** + * Checks whether the LogKit logger will log messages of priority DEBUG. + */ + public boolean isDebugEnabled() { + return getLogger().isDebugEnabled(); + } + + + /** + * Checks whether the LogKit logger will log messages of priority ERROR. + */ + public boolean isErrorEnabled() { + return getLogger().isErrorEnabled(); + } + + + /** + * Checks whether the LogKit logger will log messages of priority FATAL_ERROR. + */ + public boolean isFatalEnabled() { + return getLogger().isFatalErrorEnabled(); + } + + + /** + * Checks whether the LogKit logger will log messages of priority INFO. + */ + public boolean isInfoEnabled() { + return getLogger().isInfoEnabled(); + } + + + /** + * Checks whether the LogKit logger will log messages of priority DEBUG. + */ + public boolean isTraceEnabled() { + return getLogger().isDebugEnabled(); + } + + + /** + * Checks whether the LogKit logger will log messages of priority WARN. + */ + public boolean isWarnEnabled() { + return getLogger().isWarnEnabled(); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/NoOpLog.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/NoOpLog.java new file mode 100644 index 000000000..a66bd1037 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/NoOpLog.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + + +import java.io.Serializable; +import org.apache.commons.logging.Log; + + +/** + *

Trivial implementation of Log that throws away all messages. No + * configurable system properties are supported.

+ * + * @author Scott Sanders + * @author Rod Waldhoff + * @version $Id: NoOpLog.java 424107 2006-07-20 23:15:42Z skitching $ + */ +public class NoOpLog implements Log, Serializable { + + /** Convenience constructor */ + public NoOpLog() { } + /** Base constructor */ + public NoOpLog(String name) { } + /** Do nothing */ + public void trace(Object message) { } + /** Do nothing */ + public void trace(Object message, Throwable t) { } + /** Do nothing */ + public void debug(Object message) { } + /** Do nothing */ + public void debug(Object message, Throwable t) { } + /** Do nothing */ + public void info(Object message) { } + /** Do nothing */ + public void info(Object message, Throwable t) { } + /** Do nothing */ + public void warn(Object message) { } + /** Do nothing */ + public void warn(Object message, Throwable t) { } + /** Do nothing */ + public void error(Object message) { } + /** Do nothing */ + public void error(Object message, Throwable t) { } + /** Do nothing */ + public void fatal(Object message) { } + /** Do nothing */ + public void fatal(Object message, Throwable t) { } + + /** + * Debug is never enabled. + * + * @return false + */ + public final boolean isDebugEnabled() { return false; } + + /** + * Error is never enabled. + * + * @return false + */ + public final boolean isErrorEnabled() { return false; } + + /** + * Fatal is never enabled. + * + * @return false + */ + public final boolean isFatalEnabled() { return false; } + + /** + * Info is never enabled. + * + * @return false + */ + public final boolean isInfoEnabled() { return false; } + + /** + * Trace is never enabled. + * + * @return false + */ + public final boolean isTraceEnabled() { return false; } + + /** + * Warn is never enabled. + * + * @return false + */ + public final boolean isWarnEnabled() { return false; } + +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/ServletContextCleaner.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/ServletContextCleaner.java new file mode 100644 index 000000000..046bf7e29 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/ServletContextCleaner.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.apache.commons.logging.LogFactory; + + +/** + * This class is capable of receiving notifications about the undeployment of + * a webapp, and responds by ensuring that commons-logging releases all + * memory associated with the undeployed webapp. + *

+ * In general, the WeakHashtable support added in commons-logging release 1.1 + * ensures that logging classes do not hold references that prevent an + * undeployed webapp's memory from being garbage-collected even when multiple + * copies of commons-logging are deployed via multiple classloaders (a + * situation that earlier versions had problems with). However there are + * some rare cases where the WeakHashtable approach does not work; in these + * situations specifying this class as a listener for the web application will + * ensure that all references held by commons-logging are fully released. + *

+ * To use this class, configure the webapp deployment descriptor to call + * this class on webapp undeploy; the contextDestroyed method will tell + * every accessable LogFactory class that the entry in its map for the + * current webapp's context classloader should be cleared. + * + * @since 1.1 + */ + +public class ServletContextCleaner implements ServletContextListener { + + private Class[] RELEASE_SIGNATURE = {ClassLoader.class}; + + /** + * Invoked when a webapp is undeployed, this tells the LogFactory + * class to release any logging information related to the current + * contextClassloader. + */ + public void contextDestroyed(ServletContextEvent sce) { + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + + Object[] params = new Object[1]; + params[0] = tccl; + + // Walk up the tree of classloaders, finding all the available + // LogFactory classes and releasing any objects associated with + // the tccl (ie the webapp). + // + // When there is only one LogFactory in the classpath, and it + // is within the webapp being undeployed then there is no problem; + // garbage collection works fine. + // + // When there are multiple LogFactory classes in the classpath but + // parent-first classloading is used everywhere, this loop is really + // short. The first instance of LogFactory found will + // be the highest in the classpath, and then no more will be found. + // This is ok, as with this setup this will be the only LogFactory + // holding any data associated with the tccl being released. + // + // When there are multiple LogFactory classes in the classpath and + // child-first classloading is used in any classloader, then multiple + // LogFactory instances may hold info about this TCCL; whenever the + // webapp makes a call into a class loaded via an ancestor classloader + // and that class calls LogFactory the tccl gets registered in + // the LogFactory instance that is visible from the ancestor + // classloader. However the concrete logging library it points + // to is expected to have been loaded via the TCCL, so the + // underlying logging lib is only initialised/configured once. + // These references from ancestor LogFactory classes down to + // TCCL classloaders are held via weak references and so should + // be released but there are circumstances where they may not. + // Walking up the classloader ancestry ladder releasing + // the current tccl at each level tree, though, will definitely + // clear any problem references. + ClassLoader loader = tccl; + while (loader != null) { + // Load via the current loader. Note that if the class is not accessable + // via this loader, but is accessable via some ancestor then that class + // will be returned. + try { + Class logFactoryClass = loader.loadClass("org.apache.commons.logging.LogFactory"); + Method releaseMethod = logFactoryClass.getMethod("release", RELEASE_SIGNATURE); + releaseMethod.invoke(null, params); + loader = logFactoryClass.getClassLoader().getParent(); + } catch(ClassNotFoundException ex) { + // Neither the current classloader nor any of its ancestors could find + // the LogFactory class, so we can stop now. + loader = null; + } catch(NoSuchMethodException ex) { + // This is not expected; every version of JCL has this method + System.err.println("LogFactory instance found which does not support release method!"); + loader = null; + } catch(IllegalAccessException ex) { + // This is not expected; every ancestor class should be accessable + System.err.println("LogFactory instance found which is not accessable!"); + loader = null; + } catch(InvocationTargetException ex) { + // This is not expected + System.err.println("LogFactory instance release method failed!"); + loader = null; + } + } + + // Just to be sure, invoke release on the LogFactory that is visible from + // this ServletContextCleaner class too. This should already have been caught + // by the above loop but just in case... + LogFactory.release(tccl); + } + + /** + * Invoked when a webapp is deployed. Nothing needs to be done here. + */ + public void contextInitialized(ServletContextEvent sce) { + // do nothing + } +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/SimpleLog.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/SimpleLog.java new file mode 100644 index 000000000..fe1a7c59d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/SimpleLog.java @@ -0,0 +1,721 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + +import java.io.InputStream; +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogConfigurationException; + +/** + *

Simple implementation of Log that sends all enabled log messages, + * for all defined loggers, to System.err. The following system properties + * are supported to configure the behavior of this logger:

+ *
    + *
  • org.apache.commons.logging.simplelog.defaultlog - + * Default logging detail level for all instances of SimpleLog. + * Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). + * If not specified, defaults to "info".
  • + *
  • org.apache.commons.logging.simplelog.log.xxxxx - + * Logging detail level for a SimpleLog instance named "xxxxx". + * Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). + * If not specified, the default logging detail level is used.
  • + *
  • org.apache.commons.logging.simplelog.showlogname - + * Set to true if you want the Log instance name to be + * included in output messages. Defaults to false.
  • + *
  • org.apache.commons.logging.simplelog.showShortLogname - + * Set to true if you want the last component of the name to be + * included in output messages. Defaults to true.
  • + *
  • org.apache.commons.logging.simplelog.showdatetime - + * Set to true if you want the current date and time + * to be included in output messages. Default is false.
  • + *
  • org.apache.commons.logging.simplelog.dateTimeFormat - + * The date and time format to be used in the output messages. + * The pattern describing the date and time format is the same that is + * used in java.text.SimpleDateFormat. If the format is not + * specified or is invalid, the default format is used. + * The default format is yyyy/MM/dd HH:mm:ss:SSS zzz.
  • + *
+ * + *

In addition to looking for system properties with the names specified + * above, this implementation also checks for a class loader resource named + * "simplelog.properties", and includes any matching definitions + * from this resource (if it exists).

+ * + * @author Scott Sanders + * @author Rod Waldhoff + * @author Robert Burrell Donkin + * + * @version $Id: SimpleLog.java 581090 2007-10-01 22:01:06Z dennisl $ + */ +public class SimpleLog implements Log, Serializable { + + + // ------------------------------------------------------- Class Attributes + + /** All system properties used by SimpleLog start with this */ + static protected final String systemPrefix = + "org.apache.commons.logging.simplelog."; + + /** Properties loaded from simplelog.properties */ + static protected final Properties simpleLogProps = new Properties(); + + /** The default format to use when formating dates */ + static protected final String DEFAULT_DATE_TIME_FORMAT = + "yyyy/MM/dd HH:mm:ss:SSS zzz"; + + /** Include the instance name in the log message? */ + static protected boolean showLogName = false; + /** Include the short name ( last component ) of the logger in the log + * message. Defaults to true - otherwise we'll be lost in a flood of + * messages without knowing who sends them. + */ + static protected boolean showShortName = true; + /** Include the current time in the log message */ + static protected boolean showDateTime = false; + /** The date and time format to use in the log message */ + static protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT; + + /** + * Used to format times. + *

+ * Any code that accesses this object should first obtain a lock on it, + * ie use synchronized(dateFormatter); this requirement was introduced + * in 1.1.1 to fix an existing thread safety bug (SimpleDateFormat.format + * is not thread-safe). + */ + static protected DateFormat dateFormatter = null; + + // ---------------------------------------------------- Log Level Constants + + + /** "Trace" level logging. */ + public static final int LOG_LEVEL_TRACE = 1; + /** "Debug" level logging. */ + public static final int LOG_LEVEL_DEBUG = 2; + /** "Info" level logging. */ + public static final int LOG_LEVEL_INFO = 3; + /** "Warn" level logging. */ + public static final int LOG_LEVEL_WARN = 4; + /** "Error" level logging. */ + public static final int LOG_LEVEL_ERROR = 5; + /** "Fatal" level logging. */ + public static final int LOG_LEVEL_FATAL = 6; + + /** Enable all logging levels */ + public static final int LOG_LEVEL_ALL = (LOG_LEVEL_TRACE - 1); + + /** Enable no logging levels */ + public static final int LOG_LEVEL_OFF = (LOG_LEVEL_FATAL + 1); + + // ------------------------------------------------------------ Initializer + + private static String getStringProperty(String name) { + String prop = null; + try { + prop = System.getProperty(name); + } catch (SecurityException e) { + ; // Ignore + } + return (prop == null) ? simpleLogProps.getProperty(name) : prop; + } + + private static String getStringProperty(String name, String dephault) { + String prop = getStringProperty(name); + return (prop == null) ? dephault : prop; + } + + private static boolean getBooleanProperty(String name, boolean dephault) { + String prop = getStringProperty(name); + return (prop == null) ? dephault : "true".equalsIgnoreCase(prop); + } + + // Initialize class attributes. + // Load properties file, if found. + // Override with system properties. + static { + // Add props from the resource simplelog.properties + InputStream in = getResourceAsStream("simplelog.properties"); + if(null != in) { + try { + simpleLogProps.load(in); + in.close(); + } catch(java.io.IOException e) { + // ignored + } + } + + showLogName = getBooleanProperty( systemPrefix + "showlogname", showLogName); + showShortName = getBooleanProperty( systemPrefix + "showShortLogname", showShortName); + showDateTime = getBooleanProperty( systemPrefix + "showdatetime", showDateTime); + + if(showDateTime) { + dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat", + dateTimeFormat); + try { + dateFormatter = new SimpleDateFormat(dateTimeFormat); + } catch(IllegalArgumentException e) { + // If the format pattern is invalid - use the default format + dateTimeFormat = DEFAULT_DATE_TIME_FORMAT; + dateFormatter = new SimpleDateFormat(dateTimeFormat); + } + } + } + + // ------------------------------------------------------------- Attributes + + /** The name of this simple log instance */ + protected String logName = null; + /** The current log level */ + protected int currentLogLevel; + /** The short name of this simple log instance */ + private String shortLogName = null; + + + // ------------------------------------------------------------ Constructor + + /** + * Construct a simple log with given name. + * + * @param name log name + */ + public SimpleLog(String name) { + + logName = name; + + // Set initial log level + // Used to be: set default log level to ERROR + // IMHO it should be lower, but at least info ( costin ). + setLevel(SimpleLog.LOG_LEVEL_INFO); + + // Set log level from properties + String lvl = getStringProperty(systemPrefix + "log." + logName); + int i = String.valueOf(name).lastIndexOf("."); + while(null == lvl && i > -1) { + name = name.substring(0,i); + lvl = getStringProperty(systemPrefix + "log." + name); + i = String.valueOf(name).lastIndexOf("."); + } + + if(null == lvl) { + lvl = getStringProperty(systemPrefix + "defaultlog"); + } + + if("all".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_ALL); + } else if("trace".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_TRACE); + } else if("debug".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_DEBUG); + } else if("info".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_INFO); + } else if("warn".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_WARN); + } else if("error".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_ERROR); + } else if("fatal".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_FATAL); + } else if("off".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_OFF); + } + + } + + + // -------------------------------------------------------- Properties + + /** + *

Set logging level.

+ * + * @param currentLogLevel new logging level + */ + public void setLevel(int currentLogLevel) { + + this.currentLogLevel = currentLogLevel; + + } + + + /** + *

Get logging level.

+ */ + public int getLevel() { + + return currentLogLevel; + } + + + // -------------------------------------------------------- Logging Methods + + + /** + *

Do the actual logging. + * This method assembles the message + * and then calls write() to cause it to be written.

+ * + * @param type One of the LOG_LEVEL_XXX constants defining the log level + * @param message The message itself (typically a String) + * @param t The exception whose stack trace should be logged + */ + protected void log(int type, Object message, Throwable t) { + // Use a string buffer for better performance + StringBuffer buf = new StringBuffer(); + + // Append date-time if so configured + if(showDateTime) { + Date now = new Date(); + String dateText; + synchronized(dateFormatter) { + dateText = dateFormatter.format(now); + } + buf.append(dateText); + buf.append(" "); + } + + // Append a readable representation of the log level + switch(type) { + case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break; + case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break; + case SimpleLog.LOG_LEVEL_INFO: buf.append("[INFO] "); break; + case SimpleLog.LOG_LEVEL_WARN: buf.append("[WARN] "); break; + case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break; + case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break; + } + + // Append the name of the log instance if so configured + if( showShortName) { + if( shortLogName==null ) { + // Cut all but the last component of the name for both styles + shortLogName = logName.substring(logName.lastIndexOf(".") + 1); + shortLogName = + shortLogName.substring(shortLogName.lastIndexOf("/") + 1); + } + buf.append(String.valueOf(shortLogName)).append(" - "); + } else if(showLogName) { + buf.append(String.valueOf(logName)).append(" - "); + } + + // Append the message + buf.append(String.valueOf(message)); + + // Append stack trace if not null + if(t != null) { + buf.append(" <"); + buf.append(t.toString()); + buf.append(">"); + + java.io.StringWriter sw= new java.io.StringWriter(1024); + java.io.PrintWriter pw= new java.io.PrintWriter(sw); + t.printStackTrace(pw); + pw.close(); + buf.append(sw.toString()); + } + + // Print to the appropriate destination + write(buf); + + } + + + /** + *

Write the content of the message accumulated in the specified + * StringBuffer to the appropriate output destination. The + * default implementation writes to System.err.

+ * + * @param buffer A StringBuffer containing the accumulated + * text to be logged + */ + protected void write(StringBuffer buffer) { + + System.err.println(buffer.toString()); + + } + + + /** + * Is the given log level currently enabled? + * + * @param logLevel is this level enabled? + */ + protected boolean isLevelEnabled(int logLevel) { + // log level are numerically ordered so can use simple numeric + // comparison + return (logLevel >= currentLogLevel); + } + + + // -------------------------------------------------------- Log Implementation + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG. + * + * @param message to log + * @see org.apache.commons.logging.Log#debug(Object) + */ + public final void debug(Object message) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { + log(SimpleLog.LOG_LEVEL_DEBUG, message, null); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + public final void debug(Object message, Throwable t) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { + log(SimpleLog.LOG_LEVEL_DEBUG, message, t); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + public final void trace(Object message) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { + log(SimpleLog.LOG_LEVEL_TRACE, message, null); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + public final void trace(Object message, Throwable t) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { + log(SimpleLog.LOG_LEVEL_TRACE, message, t); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + public final void info(Object message) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { + log(SimpleLog.LOG_LEVEL_INFO,message,null); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + public final void info(Object message, Throwable t) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { + log(SimpleLog.LOG_LEVEL_INFO, message, t); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + public final void warn(Object message) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { + log(SimpleLog.LOG_LEVEL_WARN, message, null); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + public final void warn(Object message, Throwable t) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { + log(SimpleLog.LOG_LEVEL_WARN, message, t); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + public final void error(Object message) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { + log(SimpleLog.LOG_LEVEL_ERROR, message, null); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + public final void error(Object message, Throwable t) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { + log(SimpleLog.LOG_LEVEL_ERROR, message, t); + } + } + + + /** + * Log a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + public final void fatal(Object message) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { + log(SimpleLog.LOG_LEVEL_FATAL, message, null); + } + } + + + /** + * Logs a message with + * org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + public final void fatal(Object message, Throwable t) { + + if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { + log(SimpleLog.LOG_LEVEL_FATAL, message, t); + } + } + + + /** + *

Are debug messages currently enabled?

+ * + *

This allows expensive operations such as String + * concatenation to be avoided when the message will be ignored by the + * logger.

+ */ + public final boolean isDebugEnabled() { + + return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG); + } + + + /** + *

Are error messages currently enabled?

+ * + *

This allows expensive operations such as String + * concatenation to be avoided when the message will be ignored by the + * logger.

+ */ + public final boolean isErrorEnabled() { + + return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR); + } + + + /** + *

Are fatal messages currently enabled?

+ * + *

This allows expensive operations such as String + * concatenation to be avoided when the message will be ignored by the + * logger.

+ */ + public final boolean isFatalEnabled() { + + return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL); + } + + + /** + *

Are info messages currently enabled?

+ * + *

This allows expensive operations such as String + * concatenation to be avoided when the message will be ignored by the + * logger.

+ */ + public final boolean isInfoEnabled() { + + return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO); + } + + + /** + *

Are trace messages currently enabled?

+ * + *

This allows expensive operations such as String + * concatenation to be avoided when the message will be ignored by the + * logger.

+ */ + public final boolean isTraceEnabled() { + + return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE); + } + + + /** + *

Are warn messages currently enabled?

+ * + *

This allows expensive operations such as String + * concatenation to be avoided when the message will be ignored by the + * logger.

+ */ + public final boolean isWarnEnabled() { + + return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN); + } + + + /** + * Return the thread context class loader if available. + * Otherwise return null. + * + * The thread context class loader is available for JDK 1.2 + * or later, if certain security conditions are met. + * + * @exception LogConfigurationException if a suitable class loader + * cannot be identified. + */ + private static ClassLoader getContextClassLoader() + { + ClassLoader classLoader = null; + + if (classLoader == null) { + try { + // Are we running on a JDK 1.2 or later system? + Method method = Thread.class.getMethod("getContextClassLoader", + (Class[]) null); + + // Get the thread context class loader (if there is one) + try { + classLoader = (ClassLoader)method.invoke(Thread.currentThread(), + (Class[]) null); + } catch (IllegalAccessException e) { + ; // ignore + } catch (InvocationTargetException e) { + /** + * InvocationTargetException is thrown by 'invoke' when + * the method being invoked (getContextClassLoader) throws + * an exception. + * + * getContextClassLoader() throws SecurityException when + * the context class loader isn't an ancestor of the + * calling class's class loader, or if security + * permissions are restricted. + * + * In the first case (not related), we want to ignore and + * keep going. We cannot help but also ignore the second + * with the logic below, but other calls elsewhere (to + * obtain a class loader) will trigger this exception where + * we can make a distinction. + */ + if (e.getTargetException() instanceof SecurityException) { + ; // ignore + } else { + // Capture 'e.getTargetException()' exception for details + // alternate: log 'e.getTargetException()', and pass back 'e'. + throw new LogConfigurationException + ("Unexpected InvocationTargetException", e.getTargetException()); + } + } + } catch (NoSuchMethodException e) { + // Assume we are running on JDK 1.1 + ; // ignore + } + } + + if (classLoader == null) { + classLoader = SimpleLog.class.getClassLoader(); + } + + // Return the selected class loader + return classLoader; + } + + private static InputStream getResourceAsStream(final String name) + { + return (InputStream)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + ClassLoader threadCL = getContextClassLoader(); + + if (threadCL != null) { + return threadCL.getResourceAsStream(name); + } else { + return ClassLoader.getSystemResourceAsStream(name); + } + } + }); + } +} + diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/WeakHashtable.java b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/WeakHashtable.java new file mode 100644 index 000000000..3748cebf9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/WeakHashtable.java @@ -0,0 +1,478 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.logging.impl; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.*; + +/** + *

Implementation of Hashtable that uses WeakReference's + * to hold its keys thus allowing them to be reclaimed by the garbage collector. + * The associated values are retained using strong references.

+ * + *

This class follows the symantics of Hashtable as closely as + * possible. It therefore does not accept null values or keys.

+ * + *

Note: + * This is not intended to be a general purpose hash table replacement. + * This implementation is also tuned towards a particular purpose: for use as a replacement + * for Hashtable in LogFactory. This application requires + * good liveliness for get and put. Various tradeoffs + * have been made with this in mind. + *

+ *

+ * Usage: typical use case is as a drop-in replacement + * for the Hashtable used in LogFactory for J2EE enviroments + * running 1.3+ JVMs. Use of this class in most cases (see below) will + * allow classloaders to be collected by the garbage collector without the need + * to call {@link org.apache.commons.logging.LogFactory#release(ClassLoader) LogFactory.release(ClassLoader)}. + *

+ * + *

org.apache.commons.logging.LogFactory checks whether this class + * can be supported by the current JVM, and if so then uses it to store + * references to the LogFactory implementationd it loads + * (rather than using a standard Hashtable instance). + * Having this class used instead of Hashtable solves + * certain issues related to dynamic reloading of applications in J2EE-style + * environments. However this class requires java 1.3 or later (due to its use + * of java.lang.ref.WeakReference and associates). + * And by the way, this extends Hashtable rather than HashMap + * for backwards compatibility reasons. See the documentation + * for method LogFactory.createFactoryStore for more details.

+ * + *

The reason all this is necessary is due to a issue which + * arises during hot deploy in a J2EE-like containers. + * Each component running in the container owns one or more classloaders; when + * the component loads a LogFactory instance via the component classloader + * a reference to it gets stored in the static LogFactory.factories member, + * keyed by the component's classloader so different components don't + * stomp on each other. When the component is later unloaded, the container + * sets the component's classloader to null with the intent that all the + * component's classes get garbage-collected. However there's still a + * reference to the component's classloader from a key in the "global" + * LogFactory's factories member! If LogFactory.release() + * is called whenever component is unloaded, the classloaders will be correctly + * garbage collected; this should be done by any container that + * bundles commons-logging by default. However, holding the classloader + * references weakly ensures that the classloader will be garbage collected + * without the container performing this step.

+ * + *

+ * Limitations: + * There is still one (unusual) scenario in which a component will not + * be correctly unloaded without an explicit release. Though weak references + * are used for its keys, it is necessary to use strong references for its values. + *

+ * + *

If the abstract class LogFactory is + * loaded by the container classloader but a subclass of + * LogFactory [LogFactory1] is loaded by the component's + * classloader and an instance stored in the static map associated with the + * base LogFactory class, then there is a strong reference from the LogFactory + * class to the LogFactory1 instance (as normal) and a strong reference from + * the LogFactory1 instance to the component classloader via + * getClass().getClassLoader(). This chain of references will prevent + * collection of the child classloader.

+ * + *

+ * Such a situation occurs when the commons-logging.jar is + * loaded by a parent classloader (e.g. a server level classloader in a + * servlet container) and a custom LogFactory implementation is + * loaded by a child classloader (e.g. a web app classloader).

+ * + *

To avoid this scenario, ensure + * that any custom LogFactory subclass is loaded by the same classloader as + * the base LogFactory. Creating custom LogFactory subclasses is, + * however, rare. The standard LogFactoryImpl class should be sufficient + * for most or all users.

+ * + * + * @author Brian Stansberry + * + * @since 1.1 + */ +public final class WeakHashtable extends Hashtable { + + /** + * The maximum number of times put() or remove() can be called before + * the map will be purged of all cleared entries. + */ + private static final int MAX_CHANGES_BEFORE_PURGE = 100; + + /** + * The maximum number of times put() or remove() can be called before + * the map will be purged of one cleared entry. + */ + private static final int PARTIAL_PURGE_COUNT = 10; + + /* ReferenceQueue we check for gc'd keys */ + private ReferenceQueue queue = new ReferenceQueue(); + /* Counter used to control how often we purge gc'd entries */ + private int changeCount = 0; + + /** + * Constructs a WeakHashtable with the Hashtable default + * capacity and load factor. + */ + public WeakHashtable() {} + + + /** + *@see Hashtable + */ + public boolean containsKey(Object key) { + // purge should not be required + Referenced referenced = new Referenced(key); + return super.containsKey(referenced); + } + + /** + *@see Hashtable + */ + public Enumeration elements() { + purge(); + return super.elements(); + } + + /** + *@see Hashtable + */ + public Set entrySet() { + purge(); + Set referencedEntries = super.entrySet(); + Set unreferencedEntries = new HashSet(); + for (Iterator it=referencedEntries.iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + Referenced referencedKey = (Referenced) entry.getKey(); + Object key = referencedKey.getValue(); + Object value = entry.getValue(); + if (key != null) { + Entry dereferencedEntry = new Entry(key, value); + unreferencedEntries.add(dereferencedEntry); + } + } + return unreferencedEntries; + } + + /** + *@see Hashtable + */ + public Object get(Object key) { + // for performance reasons, no purge + Referenced referenceKey = new Referenced(key); + return super.get(referenceKey); + } + + /** + *@see Hashtable + */ + public Enumeration keys() { + purge(); + final Enumeration enumer = super.keys(); + return new Enumeration() { + public boolean hasMoreElements() { + return enumer.hasMoreElements(); + } + public Object nextElement() { + Referenced nextReference = (Referenced) enumer.nextElement(); + return nextReference.getValue(); + } + }; + } + + + /** + *@see Hashtable + */ + public Set keySet() { + purge(); + Set referencedKeys = super.keySet(); + Set unreferencedKeys = new HashSet(); + for (Iterator it=referencedKeys.iterator(); it.hasNext();) { + Referenced referenceKey = (Referenced) it.next(); + Object keyValue = referenceKey.getValue(); + if (keyValue != null) { + unreferencedKeys.add(keyValue); + } + } + return unreferencedKeys; + } + + /** + *@see Hashtable + */ + public Object put(Object key, Object value) { + // check for nulls, ensuring symantics match superclass + if (key == null) { + throw new NullPointerException("Null keys are not allowed"); + } + if (value == null) { + throw new NullPointerException("Null values are not allowed"); + } + + // for performance reasons, only purge every + // MAX_CHANGES_BEFORE_PURGE times + if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) { + purge(); + changeCount = 0; + } + // do a partial purge more often + else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) { + purgeOne(); + } + + Referenced keyRef = new Referenced(key, queue); + return super.put(keyRef, value); + } + + /** + *@see Hashtable + */ + public void putAll(Map t) { + if (t != null) { + Set entrySet = t.entrySet(); + for (Iterator it=entrySet.iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + put(entry.getKey(), entry.getValue()); + } + } + } + + /** + *@see Hashtable + */ + public Collection values() { + purge(); + return super.values(); + } + + /** + *@see Hashtable + */ + public Object remove(Object key) { + // for performance reasons, only purge every + // MAX_CHANGES_BEFORE_PURGE times + if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) { + purge(); + changeCount = 0; + } + // do a partial purge more often + else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) { + purgeOne(); + } + return super.remove(new Referenced(key)); + } + + /** + *@see Hashtable + */ + public boolean isEmpty() { + purge(); + return super.isEmpty(); + } + + /** + *@see Hashtable + */ + public int size() { + purge(); + return super.size(); + } + + /** + *@see Hashtable + */ + public String toString() { + purge(); + return super.toString(); + } + + /** + * @see Hashtable + */ + protected void rehash() { + // purge here to save the effort of rehashing dead entries + purge(); + super.rehash(); + } + + /** + * Purges all entries whose wrapped keys + * have been garbage collected. + */ + private void purge() { + synchronized (queue) { + WeakKey key; + while ((key = (WeakKey) queue.poll()) != null) { + super.remove(key.getReferenced()); + } + } + } + + /** + * Purges one entry whose wrapped key + * has been garbage collected. + */ + private void purgeOne() { + + synchronized (queue) { + WeakKey key = (WeakKey) queue.poll(); + if (key != null) { + super.remove(key.getReferenced()); + } + } + } + + /** Entry implementation */ + private final static class Entry implements Map.Entry { + + private final Object key; + private final Object value; + + private Entry(Object key, Object value) { + this.key = key; + this.value = value; + } + + public boolean equals(Object o) { + boolean result = false; + if (o != null && o instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) o; + result = (getKey()==null ? + entry.getKey() == null : + getKey().equals(entry.getKey())) + && + (getValue()==null ? + entry.getValue() == null : + getValue().equals(entry.getValue())); + } + return result; + } + + public int hashCode() { + + return (getKey()==null ? 0 : getKey().hashCode()) ^ + (getValue()==null ? 0 : getValue().hashCode()); + } + + public Object setValue(Object value) { + throw new UnsupportedOperationException("Entry.setValue is not supported."); + } + + public Object getValue() { + return value; + } + + public Object getKey() { + return key; + } + } + + + /** Wrapper giving correct symantics for equals and hashcode */ + private final static class Referenced { + + private final WeakReference reference; + private final int hashCode; + + /** + * + * @throws NullPointerException if referant is null + */ + private Referenced(Object referant) { + reference = new WeakReference(referant); + // Calc a permanent hashCode so calls to Hashtable.remove() + // work if the WeakReference has been cleared + hashCode = referant.hashCode(); + } + + /** + * + * @throws NullPointerException if key is null + */ + private Referenced(Object key, ReferenceQueue queue) { + reference = new WeakKey(key, queue, this); + // Calc a permanent hashCode so calls to Hashtable.remove() + // work if the WeakReference has been cleared + hashCode = key.hashCode(); + + } + + public int hashCode() { + return hashCode; + } + + private Object getValue() { + return reference.get(); + } + + public boolean equals(Object o) { + boolean result = false; + if (o instanceof Referenced) { + Referenced otherKey = (Referenced) o; + Object thisKeyValue = getValue(); + Object otherKeyValue = otherKey.getValue(); + if (thisKeyValue == null) { + result = (otherKeyValue == null); + + // Since our hashcode was calculated from the original + // non-null referant, the above check breaks the + // hashcode/equals contract, as two cleared Referenced + // objects could test equal but have different hashcodes. + // We can reduce (not eliminate) the chance of this + // happening by comparing hashcodes. + if (result == true) { + result = (this.hashCode() == otherKey.hashCode()); + } + // In any case, as our c'tor does not allow null referants + // and Hashtable does not do equality checks between + // existing keys, normal hashtable operations should never + // result in an equals comparison between null referants + } + else + { + result = thisKeyValue.equals(otherKeyValue); + } + } + return result; + } + } + + /** + * WeakReference subclass that holds a hard reference to an + * associated value and also makes accessible + * the Referenced object holding it. + */ + private final static class WeakKey extends WeakReference { + + private final Referenced referenced; + + private WeakKey(Object key, + ReferenceQueue queue, + Referenced referenced) { + super(key, queue); + this.referenced = referenced; + } + + private Referenced getReferenced() { + return referenced; + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/impl/package.html b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/package.html new file mode 100644 index 000000000..0d370715c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/impl/package.html @@ -0,0 +1,22 @@ + + + +

Concrete implementations of commons-logging wrapper APIs.

+ diff --git a/fluidbook/tools/fwstk/src/apache/commons/logging/package.html b/fluidbook/tools/fwstk/src/apache/commons/logging/package.html new file mode 100644 index 000000000..6be8fd402 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/commons/logging/package.html @@ -0,0 +1,255 @@ + + + +

Simple wrapper API around multiple logging APIs.

+ + +

Overview

+ +

This package provides an API for logging in server-based applications that +can be used around a variety of different logging implementations, including +prebuilt support for the following:

+
    +
  • Log4J (version 1.2 or later) + from Apache's Logging project. Each named Log + instance is connected to a corresponding Log4J Logger.
  • +
  • + JDK Logging API, included in JDK 1.4 or later systems. Each named + Log instance is connected to a corresponding + java.util.logging.Logger instance.
  • +
  • LogKit from Apache's + Avalon project. Each named Log instance is + connected to a corresponding LogKit Logger.
  • +
  • NoOpLog implementation that simply swallows + all log output, for all named Log instances.
  • +
  • SimpleLog implementation that writes all + log output, for all named Log instances, to + System.err.
  • +
+ + +

Quick Start Guide

+ +

For those impatient to just get on with it, the following example +illustrates the typical declaration and use of a logger that is named (by +convention) after the calling class: + +

+    import org.apache.commons.logging.Log;
+    import org.apache.commons.logging.LogFactory;
+
+    public class Foo {
+
+        private Log log = LogFactory.getLog(Foo.class);
+
+        public void foo() {
+            ...
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("About to do something to object " + name);
+                }
+                name.bar();
+            } catch (IllegalStateException e) {
+                log.error("Something bad happened to " + name, e);
+            }
+            ...
+        }
+
+ +

Unless you configure things differently, all log output will be written +to System.err. Therefore, you really will want to review the remainder of +this page in order to understand how to configure logging for your +application.

+ + +

Configuring the Commons Logging Package

+ + +

Choosing a LogFactory Implementation

+ +

From an application perspective, the first requirement is to retrieve an +object reference to the LogFactory instance that will be used +to create Log instances for this +application. This is normally accomplished by calling the static +getFactory() method. This method implements the following +discovery algorithm to select the name of the LogFactory +implementation class this application wants to use:

+
    +
  • Check for a system property named + org.apache.commons.logging.LogFactory.
  • +
  • Use the JDK 1.3 JAR Services Discovery mechanism (see + + http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html for + more information) to look for a resource named + META-INF/services/org.apache.commons.logging.LogFactory + whose first line is assumed to contain the desired class name.
  • +
  • Look for a properties file named commons-logging.properties + visible in the application class path, with a property named + org.apache.commons.logging.LogFactory defining the + desired implementation class name.
  • +
  • Fall back to a default implementation, which is described + further below.
  • +
+ +

If a commons-logging.properties file is found, all of the +properties defined there are also used to set configuration attributes on +the instantiated LogFactory instance.

+ +

Once an implementation class name is selected, the corresponding class is +loaded from the current Thread context class loader (if there is one), or +from the class loader that loaded the LogFactory class itself +otherwise. This allows a copy of commons-logging.jar to be +shared in a multiple class loader environment (such as a servlet container), +but still allow each web application to provide its own LogFactory +implementation, if it so desires. An instance of this class will then be +created, and cached per class loader. + + +

The Default LogFactory Implementation

+ +

The Logging Package APIs include a default LogFactory +implementation class ( +org.apache.commons.logging.impl.LogFactoryImpl) that is selected if no +other implementation class name can be discovered. Its primary purpose is +to create (as necessary) and return Log instances +in response to calls to the getInstance() method. The default +implementation uses the following rules:

+
    +
  • At most one Log instance of the same name will be created. + Subsequent getInstance() calls to the same + LogFactory instance, with the same name or Class + parameter, will return the same Log instance.
  • +
  • When a new Log instance must be created, the default + LogFactory implementation uses the following discovery + process: +
      +
    • Look for a configuration attribute of this factory named + org.apache.commons.logging.Log (for backwards + compatibility to pre-1.0 versions of this API, an attribute + org.apache.commons.logging.log is also consulted).
    • +
    • Look for a system property named + org.apache.commons.logging.Log (for backwards + compatibility to pre-1.0 versions of this API, a system property + org.apache.commons.logging.log is also consulted).
    • +
    • If the Log4J logging system is available in the application + class path, use the corresponding wrapper class + (Log4JLogger).
    • +
    • If the application is executing on a JDK 1.4 system, use + the corresponding wrapper class + (Jdk14Logger).
    • +
    • Fall back to the default simple logging implementation + (SimpleLog).
    • +
  • +
  • Load the class of the specified name from the thread context class + loader (if any), or from the class loader that loaded the + LogFactory class otherwise.
  • +
  • Instantiate an instance of the selected Log + implementation class, passing the specified name as the single + argument to its constructor.
  • +
+ +

See the SimpleLog JavaDocs for detailed +configuration information for this default implementation.

+ + +

Configuring the Underlying Logging System

+ +

The basic principle is that the user is totally responsible for the +configuration of the underlying logging system. +Commons-logging should not change the existing configuration.

+ +

Each individual Log implementation may +support its own configuration properties. These will be documented in the +class descriptions for the corresponding implementation class.

+ +

Finally, some Log implementations (such as the one for Log4J) +require an external configuration file for the entire logging environment. +This file should be prepared in a manner that is specific to the actual logging +technology being used.

+ + +

Using the Logging Package APIs

+ +

Use of the Logging Package APIs, from the perspective of an application +component, consists of the following steps:

+
    +
  1. Acquire a reference to an instance of + org.apache.commons.logging.Log, by calling the + factory method + + LogFactory.getInstance(String name). Your application can contain + references to multiple loggers that are used for different + purposes. A typical scenario for a server application is to have each + major component of the server use its own Log instance.
  2. +
  3. Cause messages to be logged (if the corresponding detail level is enabled) + by calling appropriate methods (trace(), debug(), + info(), warn(), error, and + fatal()).
  4. +
+ +

For convenience, LogFactory also offers a static method +getLog() that combines the typical two-step pattern:

+
+  Log log = LogFactory.getFactory().getInstance(Foo.class);
+
+

into a single method call:

+
+  Log log = LogFactory.getLog(Foo.class);
+
+ +

For example, you might use the following technique to initialize and +use a Log instance in an application component:

+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MyComponent {
+
+  protected Log log =
+    LogFactory.getLog(MyComponent.class);
+
+  // Called once at startup time
+  public void start() {
+    ...
+    log.info("MyComponent started");
+    ...
+  }
+
+  // Called once at shutdown time
+  public void stop() {
+    ...
+    log.info("MyComponent stopped");
+    ...
+  }
+
+  // Called repeatedly to process a particular argument value
+  // which you want logged if debugging is enabled
+  public void process(String value) {
+    ...
+    // Do the string concatenation only if logging is enabled
+    if (log.isDebugEnabled())
+      log.debug("MyComponent processing " + value);
+    ...
+  }
+
+}
+
+ + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/afm/AFMParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/AFMParser.java new file mode 100644 index 000000000..cb0e52f81 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/AFMParser.java @@ -0,0 +1,1062 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +import java.io.InputStream; +import java.io.IOException; + +import java.util.StringTokenizer; + +import org.apache.fontbox.util.BoundingBox; + +/** + * This class is used to parse AFM(Adobe Font Metrics) documents. + * + * @see AFM Documentation + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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= 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 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/fluidbook/tools/fwstk/src/apache/fontbox/afm/CharMetric.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/CharMetric.java new file mode 100644 index 000000000..096eaa8aa --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/CharMetric.java @@ -0,0 +1,285 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.fontbox.util.BoundingBox; + +/** + * This class represents a single character metric. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/afm/Composite.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/Composite.java new file mode 100644 index 000000000..eb178330a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/Composite.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents composite character data. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/afm/CompositePart.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/CompositePart.java new file mode 100644 index 000000000..c9a4ea724 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/CompositePart.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +/** + * This class represents a part of composite character data. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/afm/FontMetric.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/FontMetric.java new file mode 100644 index 000000000..ca0d75c5c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/FontMetric.java @@ -0,0 +1,911 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +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.apache.fontbox.util.BoundingBox; + +/** + * This is the outermost AFM type. This can be created by the afmparser with a valid + * AFM document. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.3 $ + */ +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 = 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 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 getCharacterHeight( String name ) throws IOException + { + float result = 0; + CharMetric metric = charMetricsMap.get( name ); + if( metric == null ) + { + result=0; + //don't throw an exception right away. + //throw new IOException( "Unknown AFM(" + getFullName() + ") characer '" + name + "'" ); + } + else + { + if( metric.getWy() == 0 ) + { + result = metric.getBoundingBox().getHeight(); + } + else + { + result = metric.getWy(); + } + } + 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 = 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 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(List kernPairsList) + { + kernPairs = kernPairsList; + } + + /** Getter for property kernPairs0. + * @return Value of property kernPairs0. + */ + public 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(List kernPairs0List) + { + kernPairs0 = kernPairs0List; + } + + /** Getter for property kernPairs1. + * @return Value of property kernPairs1. + */ + public 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(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/fluidbook/tools/fwstk/src/apache/fontbox/afm/KernPair.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/KernPair.java new file mode 100644 index 000000000..38103bdec --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/KernPair.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +/** + * This represents some kern pair data. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/afm/Ligature.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/Ligature.java new file mode 100644 index 000000000..3fbcad31f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/Ligature.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +/** + * This class represents a ligature, which is an entry of the CharMetrics. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/afm/TrackKern.java b/fluidbook/tools/fwstk/src/apache/fontbox/afm/TrackKern.java new file mode 100644 index 000000000..26672a4f3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/TrackKern.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.afm; + +/** + * This class represents a piece of track kerning data. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/afm/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/afm/package.html new file mode 100644 index 000000000..223f7ccc0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/afm/package.html @@ -0,0 +1,28 @@ + + + + + + + +This package holds classes used to parse AFM(Adobe Font Metrics) files. +
+More information about AFM files can be found at +http://partners.adobe.com/asn/developer/type/ + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/AFMFormatter.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/AFMFormatter.java new file mode 100644 index 000000000..c681923c0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/AFMFormatter.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.fontbox.cff.encoding.CFFEncoding; + +/** + * This class creates all needed AFM font metric data from a CFFFont ready to be read from a AFMPaser. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class AFMFormatter +{ + + private AFMFormatter() + { + } + + /** + * Create font metric data for the given CFFFont. + * @param font the CFFFont + * @return the created font metric data + * @throws IOException if an error occurs during reading + */ + public static byte[] format(CFFFont font) throws IOException + { + DataOutput output = new DataOutput(); + printFont(font, output); + return output.getBytes(); + } + + private static void printFont(CFFFont font, DataOutput output) + throws IOException + { + printFontMetrics(font, output); + } + + @SuppressWarnings(value = { "unchecked" }) + private static void printFontMetrics(CFFFont font, DataOutput output) + throws IOException + { + List metrics = renderFont(font); + output.println("StartFontMetrics 2.0"); + output.println("FontName " + font.getName()); + output.println("FullName " + font.getProperty("FullName")); + output.println("FamilyName " + font.getProperty("FamilyName")); + output.println("Weight " + font.getProperty("Weight")); + CFFEncoding encoding = font.getEncoding(); + if (encoding.isFontSpecific()) + { + output.println("EncodingScheme FontSpecific"); + } + Rectangle2D bounds = getBounds(metrics); + output.println("FontBBox " + (int)bounds.getX() + " " + (int)bounds.getY() + + " " + (int)bounds.getMaxX() + " " + (int)bounds.getMaxY()); + printDirectionMetrics(font, output); + printCharMetrics(font, metrics, output); + output.println("EndFontMetrics"); + } + + private static void printDirectionMetrics(CFFFont font, DataOutput output) + throws IOException + { + output.println("UnderlinePosition " + + font.getProperty("UnderlinePosition")); + output.println("UnderlineThickness " + + font.getProperty("UnderlineThickness")); + output.println("ItalicAngle " + font.getProperty("ItalicAngle")); + output.println("IsFixedPitch " + font.getProperty("isFixedPitch")); + } + + private static void printCharMetrics(CFFFont font, List metrics, DataOutput output) + throws IOException + { + output.println("StartCharMetrics " + metrics.size()); + Collections.sort(metrics); + for (CharMetric metric : metrics) + { + output.print("C " + metric.code + " ;"); + output.print(" "); + output.print("WX " + metric.width + " ;"); + output.print(" "); + output.print("N " + metric.name + " ;"); + output.print(" "); + output.print("B " + (int) metric.bounds.getX() + " " + + (int) metric.bounds.getY() + " " + + (int) metric.bounds.getMaxX() + " " + + (int) metric.bounds.getMaxY() + " ;"); + output.println(); + } + output.println("EndCharMetrics"); + } + + private static List renderFont(CFFFont font) throws IOException + { + List metrics = new ArrayList(); + CharStringRenderer renderer = font.createRenderer(); + Collection mappings = font.getMappings(); + for (CFFFont.Mapping mapping : mappings) + { + CharMetric metric = new CharMetric(); + metric.code = mapping.getCode(); + metric.name = mapping.getName(); + renderer.render(mapping.toType1Sequence()); + metric.width = renderer.getWidth(); + metric.bounds = renderer.getBounds(); + metrics.add(metric); + } + return metrics; + } + + private static Rectangle2D getBounds(List metrics) + { + Rectangle2D bounds = null; + for(CharMetric metric : metrics) + { + if(bounds == null) + { + bounds = new Rectangle2D.Double(); + bounds.setFrame(metric.bounds); + } + else + { + Rectangle2D.union(bounds, metric.bounds, bounds); + } + } + return bounds; + } + + /** + * This class represents the metric of one single character. + * + */ + private static class CharMetric implements Comparable + { + private int code; + private String name; + private int width; + private Rectangle2D bounds; + + public int compareTo(CharMetric that) + { + return code - that.code; + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFDataInput.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFDataInput.java new file mode 100644 index 000000000..358c221a8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFDataInput.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; + +/** + * This is specialized DataInput. It's used to parse a CFFFont. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFDataInput extends DataInput +{ + + /** + * Constructor. + * @param buffer the buffer to be read + */ + public CFFDataInput(byte[] buffer) + { + super(buffer); + } + + /** + * Read one single Card8 value from the buffer. + * @return the card8 value + * @throws IOException if an error occurs during reading + */ + public int readCard8() throws IOException + { + return readUnsignedByte(); + } + + /** + * Read one single Card16 value from the buffer. + * @return the card16 value + * @throws IOException if an error occurs during reading + */ + public int readCard16() throws IOException + { + return readUnsignedShort(); + } + + /** + * Read the offset from the buffer. + * @param offSize the given offsize + * @return the offset + * @throws IOException if an error occurs during reading + */ + public int readOffset(int offSize) throws IOException + { + int value = 0; + for (int i = 0; i < offSize; i++) + { + value = value << 8 | readUnsignedByte(); + } + return value; + } + + /** + * Read the offsize from the buffer. + * @return the offsize + * @throws IOException if an error occurs during reading + */ + public int readOffSize() throws IOException + { + return readUnsignedByte(); + } + + /** + * Read a SID from the buffer. + * @return the SID + * @throws IOException if an error occurs during reading + */ + public int readSID() throws IOException + { + return readUnsignedShort(); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFont.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFont.java new file mode 100644 index 000000000..0434328fd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFont.java @@ -0,0 +1,444 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.fontbox.cff.IndexData; +import org.apache.fontbox.cff.charset.CFFCharset; +import org.apache.fontbox.cff.encoding.CFFEncoding; + +/** + * This class represents a CFF/Type2 Font. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFFont +{ + + private String fontname = null; + private Map topDict = new LinkedHashMap(); + private Map privateDict = new LinkedHashMap(); + private CFFEncoding fontEncoding = null; + private CFFCharset fontCharset = null; + private Map charStringsDict = new LinkedHashMap(); + private IndexData globalSubrIndex = null; + private IndexData localSubrIndex = null; + + /** + * The name of the font. + * @return the name of the font + */ + public String getName() + { + return fontname; + } + + /** + * Sets the name of the font. + * @param name the name of the font + */ + public void setName(String name) + { + fontname = name; + } + + /** + * Returns the value for the given name from the dictionary. + * @param name the name of the value + * @return the value of the name if available + */ + public Object getProperty(String name) + { + Object topDictValue = topDict.get(name); + if (topDictValue != null) + { + return topDictValue; + } + Object privateDictValue = privateDict.get(name); + if (privateDictValue != null) + { + return privateDictValue; + } + return null; + } + + /** + * Adds the given key/value pair to the top dictionary. + * @param name the given key + * @param value the given value + */ + public void addValueToTopDict(String name, Object value) + { + if (value != null) + { + topDict.put(name, value); + } + } + /** + * Returns the top dictionary. + * @return the dictionary + */ + public Map getTopDict() + { + return topDict; + } + + /** + * Adds the given key/value pair to the private dictionary. + * @param name the given key + * @param value the given value + */ + public void addValueToPrivateDict(String name, Object value) + { + if (value != null) + { + privateDict.put(name, value); + } + } + /** + * Returns the private dictionary. + * @return the dictionary + */ + public Map getPrivateDict() + { + return privateDict; + } + + /** + * Get the mapping (code/SID/charname/bytes) for this font. + * @return mappings for codes < 256 and for codes > = 256 + */ + public Collection getMappings() + { + List mappings = new ArrayList(); + Set mappedNames = new HashSet(); + for (CFFEncoding.Entry entry : fontEncoding.getEntries()) + { + String charName = fontCharset.getName(entry.getSID()); + // Predefined encoding + if (charName == null) + { + continue; + } + byte[] bytes = charStringsDict.get(charName); + if (bytes == null) + { + continue; + } + Mapping mapping = new Mapping(); + mapping.setCode(entry.getCode()); + mapping.setSID(entry.getSID()); + mapping.setName(charName); + mapping.setBytes(bytes); + mappings.add(mapping); + mappedNames.add(charName); + } + if (fontEncoding instanceof CFFParser.EmbeddedEncoding) + { + CFFParser.EmbeddedEncoding embeddedEncoding = (CFFParser.EmbeddedEncoding)fontEncoding; + + for (CFFParser.EmbeddedEncoding.Supplement supplement : embeddedEncoding.getSupplements()) + { + String charName = fontCharset.getName(supplement.getGlyph()); + if (charName == null) + { + continue; + } + byte[] bytes = charStringsDict.get(charName); + if (bytes == null) + { + continue; + } + Mapping mapping = new Mapping(); + mapping.setCode(supplement.getCode()); + mapping.setSID(supplement.getGlyph()); + mapping.setName(charName); + mapping.setBytes(bytes); + mappings.add(mapping); + mappedNames.add(charName); + } + } + // XXX + int code = 256; + for (CFFCharset.Entry entry : fontCharset.getEntries()) + { + String name = entry.getName(); + if (mappedNames.contains(name)) + { + continue; + } + byte[] bytes = this.charStringsDict.get(name); + if (bytes == null) + { + continue; + } + Mapping mapping = new Mapping(); + mapping.setCode(code++); + mapping.setSID(entry.getSID()); + mapping.setName(name); + mapping.setBytes(bytes); + + mappings.add(mapping); + + mappedNames.add(name); + } + return mappings; + } + + /** + * Return the Width value of the given Glyph identifier + * + * @param SID + * @return -1 if the SID is missing from the Font. + * @throws IOException + */ + public int getWidth(int SID) throws IOException { + int nominalWidth = privateDict.containsKey("nominalWidthX") ? ((Number)privateDict.get("nominalWidthX")).intValue() : 0; + int defaultWidth = privateDict.containsKey("defaultWidthX") ? ((Number)privateDict.get("defaultWidthX")).intValue() : 1000 ; + for (Mapping m : getMappings() ){ + if (m.getSID() == SID) { + + CharStringRenderer csr = null; + if (((Number)getProperty("CharstringType")).intValue() == 2 ) { + List lSeq = m.toType2Sequence(); + csr = new CharStringRenderer(false); + csr.render(lSeq); + } else { + List lSeq = m.toType1Sequence(); + csr = new CharStringRenderer(); + csr.render(lSeq); + } + + // ---- If the CharString has a Width nominalWidthX must be added, + // otherwise it is the default width. + return csr.getWidth() != 0 ? csr.getWidth() + nominalWidth : defaultWidth; + } + } + + // ---- Width not found, return the default width + return defaultWidth; + } + + /** + * Returns the CFFEncoding of the font. + * @return the encoding + */ + public CFFEncoding getEncoding() + { + return fontEncoding; + } + + /** + * Sets the CFFEncoding of the font. + * @param encoding the given CFFEncoding + */ + public void setEncoding(CFFEncoding encoding) + { + fontEncoding = encoding; + } + + /** + * Returns the CFFCharset of the font. + * @return the charset + */ + public CFFCharset getCharset() + { + return fontCharset; + } + + /** + * Sets the CFFCharset of the font. + * @param charset the given CFFCharset + */ + public void setCharset(CFFCharset charset) + { + fontCharset = charset; + } + + /** + * Returns the character strings dictionary. + * @return the dictionary + */ + public Map getCharStringsDict() + { + return charStringsDict; + } + + /** + * Creates a CharStringConverter for this font. + * @return the new CharStringConverter + */ + public CharStringConverter createConverter() + { + Number defaultWidthX = (Number) getProperty("defaultWidthX"); + Number nominalWidthX = (Number) getProperty("nominalWidthX"); + return new CharStringConverter(defaultWidthX.intValue(), nominalWidthX + .intValue(), getGlobalSubrIndex(), getLocalSubrIndex()); + } + + /** + * Creates a CharStringRenderer for this font. + * @return the new CharStringRenderer + */ + public CharStringRenderer createRenderer() + { + return new CharStringRenderer(); + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getClass().getName() + "[name=" + fontname + ", topDict=" + topDict + + ", privateDict=" + privateDict + ", encoding=" + fontEncoding + + ", charset=" + fontCharset + ", charStringsDict=" + + charStringsDict + "]"; + } + + + /** + * Sets the global subroutine index data. + * @param globalSubrIndex the IndexData object containing the global subroutines + */ + public void setGlobalSubrIndex(IndexData globalSubrIndex) { + this.globalSubrIndex = globalSubrIndex; + } + + /** + * Returns the global subroutine index data. + * @return the dictionary + */ + public IndexData getGlobalSubrIndex() { + return globalSubrIndex; + } + + /** + * Returns the local subroutine index data. + * @return the dictionary + */ + public IndexData getLocalSubrIndex() { + return localSubrIndex; + } + + /** + * Sets the local subroutine index data. + * @param localSubrIndex the IndexData object containing the local subroutines + */ + public void setLocalSubrIndex(IndexData localSubrIndex) { + this.localSubrIndex = localSubrIndex; + } + + /** + * This class is used for the font mapping. + * + */ + public class Mapping + { + private int mappedCode; + private int mappedSID; + private String mappedName; + private byte[] mappedBytes; + + /** + * Converts the mapping into a Type1-sequence. + * @return the Type1-sequence + * @throws IOException if an error occurs during reading + */ + public List toType1Sequence() throws IOException + { + CharStringConverter converter = createConverter(); + return converter.convert(toType2Sequence()); + } + + /** + * Converts the mapping into a Type2-sequence. + * @return the Type2-sequence + * @throws IOException if an error occurs during reading + */ + public List toType2Sequence() throws IOException + { + Type2CharStringParser parser = new Type2CharStringParser(); + return parser.parse(getBytes()); + } + + /** + * Gets the value for the code. + * @return the code + */ + public int getCode() + { + return mappedCode; + } + + private void setCode(int code) + { + mappedCode = code; + } + + /** + * Gets the value for the SID. + * @return the SID + */ + public int getSID() + { + return mappedSID; + } + + private void setSID(int sid) + { + this.mappedSID = sid; + } + + /** + * Gets the value for the name. + * @return the name + */ + public String getName() + { + return mappedName; + } + + private void setName(String name) + { + this.mappedName = name; + } + + /** + * Gets the value for the bytes. + * @return the bytes + */ + public byte[] getBytes() + { + return mappedBytes; + } + + private void setBytes(byte[] bytes) + { + this.mappedBytes = bytes; + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFontROS.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFontROS.java new file mode 100644 index 000000000..0f3e376b6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFFontROS.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class CFFFontROS extends CFFFont { + private String registry; + private String ordering; + private int supplement; + + private List> fontDictionaries = new LinkedList>(); + private List> privateDictionaries = new LinkedList>(); + private CIDKeyedFDSelect fdSelect = null; + + /** + * Returns the registry value. + * @return the registry + */ + public String getRegistry() { + return registry; + } + + /** + * Sets the registry value. + * + * @param registry the registry to set + */ + public void setRegistry(String registry) { + this.registry = registry; + } + + /** + * Returns the ordering value. + * + * @return the ordering + */ + public String getOrdering() { + return ordering; + } + + /** + * Sets the ordering value. + * + * @param ordering the ordering to set + */ + public void setOrdering(String ordering) { + this.ordering = ordering; + } + + /** + * Returns the supplement value. + * + * @return the supplement + */ + public int getSupplement() { + return supplement; + } + + /** + * Sets the supplement value. + * + * @param supplement the supplement to set + */ + public void setSupplement(int supplement) { + this.supplement = supplement; + } + + /** + * Returns the font dictionaries. + * + * @return the fontDict + */ + public List> getFontDict() { + return fontDictionaries; + } + + /** + * Sets the font dictionaries. + * + * @param fontDict the fontDict to set + */ + public void setFontDict(List> fontDict) { + this.fontDictionaries = fontDict; + } + + /** + * Returns the private dictionary. + * + * @return the privDict + */ + public List> getPrivDict() { + return privateDictionaries; + } + + /** + * Sets the private dictionary. + * + * @param privDict the privDict to set + */ + public void setPrivDict(List> privDict) { + this.privateDictionaries = privDict; + } + + /** + * Returns the fdSelect value. + * + * @return the fdSelect + */ + public CIDKeyedFDSelect getFdSelect() { + return fdSelect; + } + + /** + * Sets the fdSelect value. + * + * @param fdSelect the fdSelect to set + */ + public void setFdSelect(CIDKeyedFDSelect fdSelect) { + this.fdSelect = fdSelect; + } + + /** + * Returns the Width value of the given Glyph identifier + * + * @param SID + * @return -1 if the SID is missing from the Font. + * @throws IOException + */ + public int getWidth(int SID) throws IOException { + // ---- search the right FDArray index in the FDSelect according to the Character identifier + // this index will be used to access the private dictionary which contains useful values + // to compute width. + int fdArrayIndex = this.fdSelect.getFd(SID); + if (fdArrayIndex == -1 && SID == 0 ) { // --- notdef char + return super.getWidth(SID); + } else if (fdArrayIndex == -1) { + return 1000; + } + + Map fontDict = this.fontDictionaries.get(fdArrayIndex); + Map privDict = this.privateDictionaries.get(fdArrayIndex); + + int nominalWidth = privDict.containsKey("nominalWidthX") ? ((Number)privDict.get("nominalWidthX")).intValue() : 0; + int defaultWidth = privDict.containsKey("defaultWidthX") ? ((Number)privDict.get("defaultWidthX")).intValue() : 1000 ; + + for (Mapping m : getMappings() ){ + if (m.getSID() == SID) { + + CharStringRenderer csr = null; + Number charStringType = (Number)getProperty("CharstringType"); + if ( charStringType.intValue() == 2 ) { + List lSeq = m.toType2Sequence(); + csr = new CharStringRenderer(false); + csr.render(lSeq); + } else { + List lSeq = m.toType1Sequence(); + csr = new CharStringRenderer(); + csr.render(lSeq); + } + + // ---- If the CharString has a Width nominalWidthX must be added, + // otherwise it is the default width. + return csr.getWidth() != 0 ? csr.getWidth() + nominalWidth : defaultWidth; + } + } + + // ---- Width not found, return the default width + return defaultWidth; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFOperator.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFOperator.java new file mode 100644 index 000000000..97663266d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFOperator.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * This class represents a CFF operator. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class CFFOperator +{ + + private Key operatorKey = null; + private String operatorName = null; + + private CFFOperator(Key key, String name) + { + setKey(key); + setName(name); + } + + /** + * The key of the operator. + * @return the key + */ + public Key getKey() + { + return operatorKey; + } + + private void setKey(Key key) + { + operatorKey = key; + } + + /** + * The name of the operator. + * @return the name + */ + public String getName() + { + return operatorName; + } + + private void setName(String name) + { + operatorName = name; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getName(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return getKey().hashCode(); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof CFFOperator) + { + CFFOperator that = (CFFOperator) object; + return getKey().equals(that.getKey()); + } + return false; + } + + private static void register(Key key, String name) + { + CFFOperator operator = new CFFOperator(key, name); + keyMap.put(key, operator); + nameMap.put(name, operator); + } + + /** + * Returns the operator corresponding to the given key. + * @param key the given key + * @return the corresponding operator + */ + public static CFFOperator getOperator(Key key) + { + return keyMap.get(key); + } + + /** + * Returns the operator corresponding to the given name. + * @param key the given name + * @return the corresponding operator + */ + public static CFFOperator getOperator(String name) + { + return nameMap.get(name); + } + + /** + * This class is a holder for a key value. It consists of one or two bytes. + * @author Villu Ruusmann + */ + public static class Key + { + private int[] value = null; + + /** + * Constructor. + * @param b0 the one byte value + */ + public Key(int b0) + { + this(new int[] { b0 }); + } + + /** + * Constructor. + * @param b0 the first byte of a two byte value + * @param b1 the second byte of a two byte value + */ + public Key(int b0, int b1) + { + this(new int[] { b0, b1 }); + } + + private Key(int[] value) + { + setValue(value); + } + + /** + * Returns the value of the key. + * @return the value + */ + public int[] getValue() + { + return value; + } + + private void setValue(int[] value) + { + this.value = value; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return Arrays.toString(getValue()); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return Arrays.hashCode(getValue()); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof Key) + { + Key that = (Key) object; + return Arrays.equals(getValue(), that.getValue()); + } + return false; + } + } + + private static Map keyMap = new LinkedHashMap(); + private static Map nameMap = new LinkedHashMap(); + + static + { + // Top DICT + register(new Key(0), "version"); + register(new Key(1), "Notice"); + register(new Key(12, 0), "Copyright"); + register(new Key(2), "FullName"); + register(new Key(3), "FamilyName"); + register(new Key(4), "Weight"); + register(new Key(12, 1), "isFixedPitch"); + register(new Key(12, 2), "ItalicAngle"); + register(new Key(12, 3), "UnderlinePosition"); + register(new Key(12, 4), "UnderlineThickness"); + register(new Key(12, 5), "PaintType"); + register(new Key(12, 6), "CharstringType"); + register(new Key(12, 7), "FontMatrix"); + register(new Key(13), "UniqueID"); + register(new Key(5), "FontBBox"); + register(new Key(12, 8), "StrokeWidth"); + register(new Key(14), "XUID"); + register(new Key(15), "charset"); + register(new Key(16), "Encoding"); + register(new Key(17), "CharStrings"); + register(new Key(18), "Private"); + register(new Key(12, 20), "SyntheticBase"); + register(new Key(12, 21), "PostScript"); + register(new Key(12, 22), "BaseFontName"); + register(new Key(12, 23), "BaseFontBlend"); + register(new Key(12, 30), "ROS"); + register(new Key(12, 31), "CIDFontVersion"); + register(new Key(12, 32), "CIDFontRevision"); + register(new Key(12, 33), "CIDFontType"); + register(new Key(12, 34), "CIDCount"); + register(new Key(12, 35), "UIDBase"); + register(new Key(12, 36), "FDArray"); + register(new Key(12, 37), "FDSelect"); + register(new Key(12, 38), "FontName"); + + // Private DICT + register(new Key(6), "BlueValues"); + register(new Key(7), "OtherBlues"); + register(new Key(8), "FamilyBlues"); + register(new Key(9), "FamilyOtherBlues"); + register(new Key(12, 9), "BlueScale"); + register(new Key(12, 10), "BlueShift"); + register(new Key(12, 11), "BlueFuzz"); + register(new Key(10), "StdHW"); + register(new Key(11), "StdVW"); + register(new Key(12, 12), "StemSnapH"); + register(new Key(12, 13), "StemSnapV"); + register(new Key(12, 14), "ForceBold"); + register(new Key(12, 15), "LanguageGroup"); + register(new Key(12, 16), "ExpansionFactor"); + register(new Key(12, 17), "initialRandomSeed"); + register(new Key(19), "Subrs"); + register(new Key(20), "defaultWidthX"); + register(new Key(21), "nominalWidthX"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFParser.java new file mode 100644 index 000000000..9dee2be1a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFParser.java @@ -0,0 +1,1198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.fontbox.cff.CFFFont.Mapping; +import org.apache.fontbox.cff.CFFOperator; +import org.apache.fontbox.cff.charset.CFFCharset; +import org.apache.fontbox.cff.charset.CFFExpertCharset; +import org.apache.fontbox.cff.charset.CFFExpertSubsetCharset; +import org.apache.fontbox.cff.charset.CFFISOAdobeCharset; +import org.apache.fontbox.cff.encoding.CFFEncoding; +import org.apache.fontbox.cff.encoding.CFFExpertEncoding; +import org.apache.fontbox.cff.encoding.CFFStandardEncoding; + +/** + * This class represents a parser for a CFF font. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class CFFParser +{ + + private CFFDataInput input = null; + private Header header = null; + private IndexData nameIndex = null; + private IndexData topDictIndex = null; + private IndexData stringIndex = null; + + /** + * Parsing CFF Font using a byte array as input. + * @param bytes the given byte array + * @return the parsed CFF fonts + * @throws IOException If there is an error reading from the stream + */ + public List parse(byte[] bytes) throws IOException + { + input = new CFFDataInput(bytes); + header = readHeader(input); + nameIndex = readIndexData(input); + topDictIndex = readIndexData(input); + stringIndex = readIndexData(input); + IndexData globalSubrIndex = readIndexData(input); + + List fonts = new ArrayList(); + for (int i = 0; i < nameIndex.getCount(); i++) + { + CFFFont font = parseFont(i); + font.setGlobalSubrIndex(globalSubrIndex); + fonts.add(font); + } + return fonts; + } + + private static Header readHeader(CFFDataInput input) throws IOException + { + Header header = new Header(); + header.major = input.readCard8(); + header.minor = input.readCard8(); + header.hdrSize = input.readCard8(); + header.offSize = input.readOffSize(); + return header; + } + + private static IndexData readIndexData(CFFDataInput input) + throws IOException + { + int count = input.readCard16(); + IndexData index = new IndexData(count); + if (count == 0) + { + return index; + } + int offSize = input.readOffSize(); + for (int i = 0; i <= count; i++) + { + index.setOffset(i, input.readOffset(offSize)); + } + int dataSize = index.getOffset(count) - index.getOffset(0); + index.initData(dataSize); + for (int i = 0; i < dataSize; i++) + { + index.setData(i, input.readCard8()); + } + return index; + } + + private static DictData readDictData(CFFDataInput input) throws IOException + { + DictData dict = new DictData(); + dict.entries = new ArrayList(); + while (input.hasRemaining()) + { + DictData.Entry entry = readEntry(input); + dict.entries.add(entry); + } + return dict; + } + + private static DictData.Entry readEntry(CFFDataInput input) + throws IOException + { + DictData.Entry entry = new DictData.Entry(); + while (true) + { + int b0 = input.readUnsignedByte(); + + if (b0 >= 0 && b0 <= 21) + { + entry.operator = readOperator(input, b0); + break; + } + else if (b0 == 28 || b0 == 29) + { + entry.operands.add(readIntegerNumber(input, b0)); + } + else if (b0 == 30) + { + entry.operands.add(readRealNumber(input, b0)); + } + else if (b0 >= 32 && b0 <= 254) + { + entry.operands.add(readIntegerNumber(input, b0)); + } + else + { + throw new IllegalArgumentException(); + } + } + return entry; + } + + private static CFFOperator readOperator(CFFDataInput input, int b0) + throws IOException + { + CFFOperator.Key key = readOperatorKey(input, b0); + return CFFOperator.getOperator(key); + } + + private static CFFOperator.Key readOperatorKey(CFFDataInput input, int b0) + throws IOException + { + if (b0 == 12) + { + int b1 = input.readUnsignedByte(); + return new CFFOperator.Key(b0, b1); + } + return new CFFOperator.Key(b0); + } + + private static Integer readIntegerNumber(CFFDataInput input, int b0) + throws IOException + { + if (b0 == 28) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + return Integer.valueOf((short) (b1 << 8 | b2)); + } + else if (b0 == 29) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + int b3 = input.readUnsignedByte(); + int b4 = input.readUnsignedByte(); + return Integer.valueOf(b1 << 24 | b2 << 16 | b3 << 8 | b4); + } + else if (b0 >= 32 && b0 <= 246) + { + return Integer.valueOf(b0 - 139); + } + else if (b0 >= 247 && b0 <= 250) + { + int b1 = input.readUnsignedByte(); + return Integer.valueOf((b0 - 247) * 256 + b1 + 108); + } + else if (b0 >= 251 && b0 <= 254) + { + int b1 = input.readUnsignedByte(); + return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108); + } + else + { + throw new IllegalArgumentException(); + } + } + + private static Double readRealNumber(CFFDataInput input, int b0) + throws IOException + { + StringBuffer sb = new StringBuffer(); + boolean done = false; + while (!done) + { + int b = input.readUnsignedByte(); + int[] nibbles = { b / 16, b % 16 }; + for (int nibble : nibbles) + { + switch (nibble) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + sb.append(nibble); + break; + case 0xa: + sb.append("."); + break; + case 0xb: + sb.append("E"); + break; + case 0xc: + sb.append("E-"); + break; + case 0xd: + break; + case 0xe: + sb.append("-"); + break; + case 0xf: + done = true; + break; + default: + throw new IllegalArgumentException(); + } + } + } + return Double.valueOf(sb.toString()); + } + + private CFFFont parseFont(int index) throws IOException + { + CFFFont font = null; + DataInput nameInput = new DataInput(nameIndex.getBytes(index)); + String name = nameInput.getString(); + + CFFDataInput topDictInput = new CFFDataInput(topDictIndex.getBytes(index)); + DictData topDict = readDictData(topDictInput); + DictData.Entry syntheticBaseEntry = topDict.getEntry("SyntheticBase"); + if (syntheticBaseEntry != null) + { + throw new IOException("Synthetic Fonts are not supported"); + } + + DictData.Entry rosEntry = topDict.getEntry("ROS"); + if (rosEntry != null) { + font = new CFFFontROS(); + ((CFFFontROS)font).setRegistry(readString(rosEntry.getNumber(0).intValue())); + ((CFFFontROS)font).setOrdering(readString(rosEntry.getNumber(1).intValue())); + ((CFFFontROS)font).setSupplement(rosEntry.getNumber(2).intValue()); + } + + if (font == null) { + // -- No specific behavior for this font + font = new CFFFont(); + } + + font.setName(name); + + font.addValueToTopDict("version", getString(topDict,"version")); + font.addValueToTopDict("Notice", getString(topDict,"Notice")); + font.addValueToTopDict("Copyright", getString(topDict,"Copyright")); + font.addValueToTopDict("FullName", getString(topDict,"FullName")); + font.addValueToTopDict("FamilyName", getString(topDict,"FamilyName")); + font.addValueToTopDict("Weight", getString(topDict,"Weight")); + font.addValueToTopDict("isFixedPitch", getBoolean(topDict, "isFixedPitch", false)); + font.addValueToTopDict("ItalicAngle", getNumber(topDict, "ItalicAngle", 0)); + font.addValueToTopDict("UnderlinePosition", getNumber(topDict, "UnderlinePosition", -100)); + font.addValueToTopDict("UnderlineThickness", getNumber(topDict, "UnderlineThickness", 50)); + font.addValueToTopDict("PaintType", getNumber(topDict, "PaintType", 0)); + font.addValueToTopDict("CharstringType", getNumber(topDict, "CharstringType", 2)); + font.addValueToTopDict("FontMatrix", getArray(topDict, "FontMatrix", Arrays + . asList(Double.valueOf(0.001), Double.valueOf(0), + Double.valueOf(0), Double.valueOf(0.001), Double + .valueOf(0), Double.valueOf(0)))); + font.addValueToTopDict("UniqueID", getNumber(topDict, "UniqueID", null)); + font.addValueToTopDict("FontBBox", getArray(topDict, "FontBBox", Arrays + . asList(Integer.valueOf(0), Integer.valueOf(0), + Integer.valueOf(0), Integer.valueOf(0)))); + font.addValueToTopDict("StrokeWidth", getNumber(topDict, "StrokeWidth", 0)); + font.addValueToTopDict("XUID", getArray(topDict, "XUID", null)); + + DictData.Entry charStringsEntry = topDict.getEntry("CharStrings"); + int charStringsOffset = charStringsEntry.getNumber(0).intValue(); + input.setPosition(charStringsOffset); + IndexData charStringsIndex = readIndexData(input); + DictData.Entry charsetEntry = topDict.getEntry("charset"); + CFFCharset charset; + int charsetId = charsetEntry != null ? charsetEntry.getNumber(0) + .intValue() : 0; + if (charsetId == 0) + { + charset = CFFISOAdobeCharset.getInstance(); + } + else if (charsetId == 1) + { + charset = CFFExpertCharset.getInstance(); + } + else if (charsetId == 2) + { + charset = CFFExpertSubsetCharset.getInstance(); + } + else + { + input.setPosition(charsetId); + charset = readCharset(input, charStringsIndex.getCount()); + } + font.setCharset(charset); + font.getCharStringsDict().put(".notdef", charStringsIndex.getBytes(0)); + int[] gids = new int[charStringsIndex.getCount()]; + List glyphEntries = charset.getEntries(); + for (int i = 1; i < charStringsIndex.getCount(); i++) + { + CFFCharset.Entry glyphEntry = glyphEntries.get(i - 1); + gids[i - 1] = glyphEntry.getSID(); + font.getCharStringsDict().put(glyphEntry.getName(), charStringsIndex.getBytes(i)); + } + DictData.Entry encodingEntry = topDict.getEntry("Encoding"); + CFFEncoding encoding; + int encodingId = encodingEntry != null ? encodingEntry.getNumber(0).intValue() : 0; + if (encodingId == 0 || rosEntry != null) // --- ROS uses StandardEncoding + { + encoding = CFFStandardEncoding.getInstance(); + } + else if (encodingId == 1) + { + encoding = CFFExpertEncoding.getInstance(); + } + else + { + input.setPosition(encodingId); + encoding = readEncoding(input, gids); + } + font.setEncoding(encoding); + + + if (rosEntry != null) { + + // ---- It is a CIDKeyed Font, The Private Dictionary isn't in the Top Dict But in the Font Dict + // ---- Font Dict can be accessed by the FDArray and FDSelect + DictData.Entry fdArrayEntry = topDict.getEntry("FDArray"); + if (fdArrayEntry == null) { + throw new IOException("FDArray is missing for a CIDKeyed Font."); + } + + int fontDictOffset = fdArrayEntry.getNumber(0).intValue(); + input.setPosition(fontDictOffset); + IndexData fdIndex = readIndexData(input); + + List> privateDictionaries = new LinkedList>(); + List> fontDictionaries = new LinkedList>(); + CFFFontROS fontRos = (CFFFontROS)font; + + for (int i = 0; i < fdIndex.getCount(); ++i) { + byte[] b = fdIndex.getBytes(i); + CFFDataInput fontDictInput = new CFFDataInput(b); + DictData fontDictData = readDictData(fontDictInput); + + Map fontDictMap = new LinkedHashMap(); + fontDictMap.put("FontName", getString(fontDictData, "FontName")); + fontDictMap.put("FontType", getNumber(fontDictData, "FontType", 0)); + fontDictMap.put("FontBBox", getDelta(fontDictData, "FontBBox", null)); + fontDictMap.put("FontMatrix", getDelta(fontDictData, "FontMatrix", null)); + // TODO OD-4 : Add here other keys + fontDictionaries.add(fontDictMap); + + DictData.Entry privateEntry = fontDictData.getEntry("Private"); + // --- Font DICT is invalid without "Private" entry + if ( privateEntry == null) { + throw new IOException("Missing Private Dictionary"); + } + + int privateOffset = privateEntry.getNumber(1).intValue(); + input.setPosition(privateOffset); + int privateSize = privateEntry.getNumber(0).intValue(); + CFFDataInput privateDictData = new CFFDataInput(input.readBytes(privateSize)); + DictData privateDict = readDictData(privateDictData); + + Map privDict = new LinkedHashMap(); + privDict.put("BlueValues", getDelta(privateDict, "BlueValues", null)); + privDict.put("OtherBlues", getDelta(privateDict, "OtherBlues", null)); + privDict.put("FamilyBlues", getDelta(privateDict, "FamilyBlues", null)); + privDict.put("FamilyOtherBlues", getDelta(privateDict, "FamilyOtherBlues", null)); + privDict.put("BlueScale", getNumber(privateDict, "BlueScale", Double.valueOf(0.039625))); + privDict.put("BlueShift", getNumber(privateDict, "BlueShift", Integer.valueOf(7))); + privDict.put("BlueFuzz", getNumber(privateDict, "BlueFuzz", Integer.valueOf(1))); + privDict.put("StdHW", getNumber(privateDict, "StdHW", null)); + privDict.put("StdVW", getNumber(privateDict, "StdVW", null)); + privDict.put("StemSnapH", getDelta(privateDict, "StemSnapH", null)); + privDict.put("StemSnapV", getDelta(privateDict, "StemSnapV", null)); + privDict.put("ForceBold", getBoolean(privateDict, "ForceBold", false)); + privDict.put("LanguageGroup", getNumber(privateDict, "LanguageGroup", Integer.valueOf(0))); + privDict.put("ExpansionFactor", getNumber(privateDict, "ExpansionFactor", Double.valueOf(0.06))); + privDict.put("initialRandomSeed", getNumber(privateDict, "initialRandomSeed", Integer.valueOf(0))); + privDict.put("defaultWidthX", getNumber(privateDict, "defaultWidthX", Integer.valueOf(0))); + privDict.put("nominalWidthX", getNumber(privateDict, "nominalWidthX", Integer.valueOf(0))); + + int localSubrOffset = (Integer)getNumber(privateDict, "Subrs", Integer.valueOf(0)); + if (localSubrOffset == 0) + { + font.setLocalSubrIndex(new IndexData(0)); + } + else + { + input.setPosition(privateOffset + localSubrOffset); + font.setLocalSubrIndex(readIndexData(input)); + } + + privateDictionaries.add(privDict); + } + + fontRos.setFontDict(fontDictionaries); + fontRos.setPrivDict(privateDictionaries); + + DictData.Entry fdSelectEntry = topDict.getEntry("FDSelect"); + int fdSelectPos = fdSelectEntry.getNumber(0).intValue(); + input.setPosition(fdSelectPos); + CIDKeyedFDSelect fdSelect = readFDSelect(input, charStringsIndex.getCount(), fontRos); + + font.addValueToPrivateDict("defaultWidthX", Integer.valueOf(1000)); + font.addValueToPrivateDict("nominalWidthX", Integer.valueOf(0)); + + fontRos.setFdSelect(fdSelect); + + } else { + DictData.Entry privateEntry = topDict.getEntry("Private"); + int privateOffset = privateEntry.getNumber(1).intValue(); + input.setPosition(privateOffset); + int privateSize = privateEntry.getNumber(0).intValue(); + CFFDataInput privateDictData = new CFFDataInput(input.readBytes(privateSize)); + DictData privateDict = readDictData(privateDictData); + font.addValueToPrivateDict("BlueValues", getDelta(privateDict, "BlueValues", null)); + font.addValueToPrivateDict("OtherBlues", getDelta(privateDict, "OtherBlues", null)); + font.addValueToPrivateDict("FamilyBlues", getDelta(privateDict, "FamilyBlues", null)); + font.addValueToPrivateDict("FamilyOtherBlues", getDelta(privateDict, "FamilyOtherBlues", null)); + font.addValueToPrivateDict("BlueScale", getNumber(privateDict, "BlueScale", Double.valueOf(0.039625))); + font.addValueToPrivateDict("BlueShift", getNumber(privateDict, "BlueShift", Integer.valueOf(7))); + font.addValueToPrivateDict("BlueFuzz", getNumber(privateDict, "BlueFuzz", Integer.valueOf(1))); + font.addValueToPrivateDict("StdHW", getNumber(privateDict, "StdHW", null)); + font.addValueToPrivateDict("StdVW", getNumber(privateDict, "StdVW", null)); + font.addValueToPrivateDict("StemSnapH", getDelta(privateDict, "StemSnapH", null)); + font.addValueToPrivateDict("StemSnapV", getDelta(privateDict, "StemSnapV", null)); + font.addValueToPrivateDict("ForceBold", getBoolean(privateDict, "ForceBold", false)); + font.addValueToPrivateDict("LanguageGroup", getNumber(privateDict, "LanguageGroup", Integer.valueOf(0))); + font.addValueToPrivateDict("ExpansionFactor", getNumber(privateDict, "ExpansionFactor", Double.valueOf(0.06))); + font.addValueToPrivateDict("initialRandomSeed", getNumber(privateDict, "initialRandomSeed", Integer.valueOf(0))); + font.addValueToPrivateDict("defaultWidthX", getNumber(privateDict, "defaultWidthX", Integer.valueOf(0))); + font.addValueToPrivateDict("nominalWidthX", getNumber(privateDict, "nominalWidthX", Integer.valueOf(0))); + + int localSubrOffset = (Integer)getNumber(privateDict, "Subrs", Integer.valueOf(0)); + if (localSubrOffset == 0) + { + font.setLocalSubrIndex(new IndexData(0)); + } + else + { + input.setPosition(privateOffset + localSubrOffset); + font.setLocalSubrIndex(readIndexData(input)); + } + } + + return font; + } + + private String readString(int index) throws IOException + { + if (index >= 0 && index <= 390) + { + return CFFStandardString.getName(index); + } + if (index - 391 <= stringIndex.getCount()) { + DataInput dataInput = new DataInput(stringIndex.getBytes(index - 391)); + return dataInput.getString(); + } + else { + return CFFStandardString.getName(0); + } + } + + private String getString(DictData dict, String name) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return (entry != null ? readString(entry.getNumber(0).intValue()) : null); + } + + private Boolean getBoolean(DictData dict, String name, boolean defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getBoolean(0) : defaultValue; + } + + private Number getNumber(DictData dict, String name, Number defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getNumber(0) : defaultValue; + } + + // TODO Where is the difference to getDelta?? + private List getArray(DictData dict, String name, List defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getArray() : defaultValue; + } + + // TODO Where is the difference to getArray?? + private List getDelta(DictData dict, String name, List defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getArray() : defaultValue; + } + + private CFFEncoding readEncoding(CFFDataInput dataInput, int[] gids) + throws IOException + { + int format = dataInput.readCard8(); + int baseFormat = format & 0x7f; + + if (baseFormat == 0) + { + return readFormat0Encoding(dataInput, format, gids); + } + else if (baseFormat == 1) + { + return readFormat1Encoding(dataInput, format, gids); + } + else + { + throw new IllegalArgumentException(); + } + } + + private Format0Encoding readFormat0Encoding(CFFDataInput dataInput, int format, + int[] gids) throws IOException + { + Format0Encoding encoding = new Format0Encoding(); + encoding.format = format; + encoding.nCodes = dataInput.readCard8(); + encoding.code = new int[encoding.nCodes]; + for (int i = 0; i < encoding.code.length; i++) + { + encoding.code[i] = dataInput.readCard8(); + encoding.register(encoding.code[i], gids[i]); + } + if ((format & 0x80) != 0) + { + readSupplement(dataInput, encoding); + } + return encoding; + } + + private Format1Encoding readFormat1Encoding(CFFDataInput dataInput, int format, + int[] gids) throws IOException + { + Format1Encoding encoding = new Format1Encoding(); + encoding.format = format; + encoding.nRanges = dataInput.readCard8(); + int count = 0; + encoding.range = new Format1Encoding.Range1[encoding.nRanges]; + for (int i = 0; i < encoding.range.length; i++) + { + Format1Encoding.Range1 range = new Format1Encoding.Range1(); + range.first = dataInput.readCard8(); + range.nLeft = dataInput.readCard8(); + encoding.range[i] = range; + for (int j = 0; j < 1 + range.nLeft; j++) + { + encoding.register(range.first + j, gids[count + j]); + } + count += 1 + range.nLeft; + } + if ((format & 0x80) != 0) + { + readSupplement(dataInput, encoding); + } + return encoding; + } + + private void readSupplement(CFFDataInput dataInput, EmbeddedEncoding encoding) + throws IOException + { + encoding.nSups = dataInput.readCard8(); + encoding.supplement = new EmbeddedEncoding.Supplement[encoding.nSups]; + for (int i = 0; i < encoding.supplement.length; i++) + { + EmbeddedEncoding.Supplement supplement = new EmbeddedEncoding.Supplement(); + supplement.code = dataInput.readCard8(); + supplement.glyph = dataInput.readSID(); + encoding.supplement[i] = supplement; + } + } + + /** + * Read the FDSelect Data according to the format. + * @param dataInput + * @param nGlyphs + * @param ros + * @return + * @throws IOException + */ + private CIDKeyedFDSelect readFDSelect(CFFDataInput dataInput, int nGlyphs, CFFFontROS ros) + throws IOException + { + int format = dataInput.readCard8(); + if (format == 0) + { + return readFormat0FDSelect(dataInput, format, nGlyphs, ros); + } + else if (format == 3) + { + return readFormat3FDSelect(dataInput, format, nGlyphs, ros); + } + else + { + throw new IllegalArgumentException(); + } + } + + /** + * Read the Format 0 of the FDSelect data structure. + * @param dataInput + * @param format + * @param nGlyphs + * @param ros + * @return + * @throws IOException + */ + private Format0FDSelect readFormat0FDSelect(CFFDataInput dataInput, int format, int nGlyphs, CFFFontROS ros) throws IOException + { + Format0FDSelect fdselect = new Format0FDSelect(ros); + fdselect.format = format; + fdselect.fds = new int[nGlyphs]; + for (int i = 0; i < fdselect.fds.length; i++) + { + fdselect.fds[i] = dataInput.readCard8(); + + } + return fdselect; + } + + /** + * Read the Format 3 of the FDSelect data structure. + * + * @param dataInput + * @param format + * @param nGlyphs + * @param ros + * @return + * @throws IOException + */ + private Format3FDSelect readFormat3FDSelect(CFFDataInput dataInput, int format, int nGlyphs, CFFFontROS ros) throws IOException + { + Format3FDSelect fdselect = new Format3FDSelect(ros); + fdselect.format = format; + fdselect.nbRanges = dataInput.readCard16(); + + fdselect.range3 = new Range3[fdselect.nbRanges]; + for (int i = 0; i < fdselect.nbRanges; i++) + { + Range3 r3 = new Range3(); + r3.first = dataInput.readCard16(); + r3.fd = dataInput.readCard8(); + fdselect.range3[i] = r3; + + } + + fdselect.sentinel = dataInput.readCard16(); + return fdselect; + } + + /** + * Container of a Format 3 FDSelect data (see "The Compact Font Format Specification" chapter "FDSelect" ) + */ + private static class Format3FDSelect extends CIDKeyedFDSelect { + private int format; + private int nbRanges; + private Range3[] range3; + private int sentinel; + + private Format3FDSelect(CFFFontROS _owner) { + super(_owner); + } + + /* (non-Javadoc) + * @see org.apache.fontbox.cff.CIDKeyedFDSelect#getFd(int) + */ + @Override + public int getFd(int glyph) { + for (int i = 0 ; i < nbRanges; ++i) { + if (range3[i].first >= glyph) { + if (i + 1 < nbRanges ) { + if (range3[i+1].first > glyph ) { + return range3[i].fd; + } else { + // go to next range + } + } else { + // last range reach, the sentinel must be greater than glyph + if (sentinel > glyph) { + return range3[i].fd; + } else { + return -1; + } + } + } + } + return 0; + } + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + " nbRanges=" + nbRanges + ", range3=" + + Arrays.toString(range3) + " sentinel=" + sentinel + "]"; + } + } + + /** + * Structure of a Range3 element + */ + private static class Range3 { + private int first; + private int fd; + + @Override + public String toString() { + return getClass().getName() + "[first=" + first + ", fd=" + fd + "]"; + } + } + + /** + * Container of a Format 0 FDSelect data (see "The Compact Font Format Specification" chapter "FDSelect" ) + */ + private static class Format0FDSelect extends CIDKeyedFDSelect { + private int format; + private int[] fds; + + private Format0FDSelect(CFFFontROS _owner) { + super(_owner); + } + + /* (non-Javadoc) + * @see org.apache.fontbox.cff.CIDKeyedFDSelect#getFd(int) + */ + @Override + public int getFd(int glyph) { + // ---- search the position of the given glyph + for (Mapping mapping: this.owner.getMappings()) { + if (mapping.getSID() == glyph) { + int index = 0; + Map charString = this.owner.getCharStringsDict(); + Set keys = charString.keySet(); + for (String str : keys) { + if (mapping.getName().equals(str)) { + return fds[index]; + } + ++index; + } + } + } + + return -1; + } + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", fds=" + + Arrays.toString(fds) + "]"; + } + } + + private CFFCharset readCharset(CFFDataInput dataInput, int nGlyphs) + throws IOException + { + int format = dataInput.readCard8(); + if (format == 0) + { + return readFormat0Charset(dataInput, format, nGlyphs); + } + else if (format == 1) + { + return readFormat1Charset(dataInput, format, nGlyphs); + } + else if (format == 2) + { + return readFormat2Charset(dataInput, format, nGlyphs); + } + else + { + throw new IllegalArgumentException(); + } + } + + private Format0Charset readFormat0Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException + { + Format0Charset charset = new Format0Charset(); + charset.format = format; + charset.glyph = new int[nGlyphs - 1]; + for (int i = 0; i < charset.glyph.length; i++) + { + charset.glyph[i] = dataInput.readSID(); + charset.register(charset.glyph[i], readString(charset.glyph[i])); + } + return charset; + } + + private Format1Charset readFormat1Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException + { + Format1Charset charset = new Format1Charset(); + charset.format = format; + List ranges = new ArrayList(); + for (int i = 0; i < nGlyphs - 1;) + { + Format1Charset.Range1 range = new Format1Charset.Range1(); + range.first = dataInput.readSID(); + range.nLeft = dataInput.readCard8(); + ranges.add(range); + for (int j = 0; j < 1 + range.nLeft; j++) + { + charset.register(range.first + j, readString(range.first + j)); + } + i += 1 + range.nLeft; + } + charset.range = ranges.toArray(new Format1Charset.Range1[0]); + return charset; + } + + private Format2Charset readFormat2Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException + { + Format2Charset charset = new Format2Charset(); + charset.format = format; + charset.range = new Format2Charset.Range2[0]; + for (int i = 0; i < nGlyphs - 1;) + { + Format2Charset.Range2[] newRange = new Format2Charset.Range2[charset.range.length + 1]; + System.arraycopy(charset.range, 0, newRange, 0, + charset.range.length); + charset.range = newRange; + Format2Charset.Range2 range = new Format2Charset.Range2(); + range.first = dataInput.readSID(); + range.nLeft = dataInput.readCard16(); + charset.range[charset.range.length - 1] = range; + for (int j = 0; j < 1 + range.nLeft; j++) + { + charset.register(range.first + j, readString(range.first + j)); + } + i += 1 + range.nLeft; + } + return charset; + } + + /** + * Inner class holding the header of a CFF font. + */ + private static class Header + { + private int major; + private int minor; + private int hdrSize; + private int offSize; + + @Override + public String toString() + { + return getClass().getName() + "[major=" + major + ", minor=" + + minor + ", hdrSize=" + hdrSize + ", offSize=" + offSize + + "]"; + } + } + + /** + * Inner class holding the DictData of a CFF font. + */ + private static class DictData + { + + private List entries = null; + + public Entry getEntry(CFFOperator.Key key) + { + return getEntry(CFFOperator.getOperator(key)); + } + + public Entry getEntry(String name) + { + return getEntry(CFFOperator.getOperator(name)); + } + + private Entry getEntry(CFFOperator operator) + { + for (Entry entry : entries) + { + // Check for null entry before comparing the Font + if (entry != null && entry.operator != null && + entry.operator.equals(operator)) + { + return entry; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getClass().getName() + "[entries=" + entries + "]"; + } + + /** + * Inner class holding an operand of a CFF font. + */ + private static class Entry + { + private List operands = new ArrayList(); + private CFFOperator operator = null; + + public Number getNumber(int index) + { + return operands.get(index); + } + + public Boolean getBoolean(int index) + { + Number operand = operands.get(index); + if (operand instanceof Integer) + { + switch (operand.intValue()) + { + case 0: + return Boolean.FALSE; + case 1: + return Boolean.TRUE; + default: + break; + } + } + throw new IllegalArgumentException(); + } + + // TODO unused?? + public Integer getSID(int index) + { + Number operand = operands.get(index); + if (operand instanceof Integer) + { + return (Integer) operand; + } + throw new IllegalArgumentException(); + } + + // TODO Where is the difference to getDelta?? + public List getArray() + { + return operands; + } + + // TODO Where is the difference to getArray?? + public List getDelta() + { + return operands; + } + + @Override + public String toString() + { + return getClass().getName() + "[operands=" + operands + + ", operator=" + operator + "]"; + } + } + } + + /** + * Inner class representing an embedded CFF encoding. + */ + abstract static class EmbeddedEncoding extends CFFEncoding + { + + private int nSups; + private Supplement[] supplement; + + @Override + public boolean isFontSpecific() + { + return true; + } + + List getSupplements() + { + if(supplement == null){ + return Collections.emptyList(); + } + return Arrays.asList(supplement); + } + + /** + * Inner class representing a supplement for an encoding. + */ + static class Supplement + { + private int code; + private int glyph; + + int getCode(){ + return code; + } + + int getGlyph(){ + return glyph; + } + + @Override + public String toString() + { + return getClass().getName() + "[code=" + code + ", glyph=" + + glyph + "]"; + } + } + } + + /** + * Inner class representing a Format0 encoding. + */ + private static class Format0Encoding extends EmbeddedEncoding + { + private int format; + private int nCodes; + private int[] code; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", nCodes=" + + nCodes + ", code=" + Arrays.toString(code) + + ", supplement=" + Arrays.toString(super.supplement) + "]"; + } + } + + /** + * Inner class representing a Format1 encoding. + */ + private static class Format1Encoding extends EmbeddedEncoding + { + private int format; + private int nRanges; + private Range1[] range; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", nRanges=" + + nRanges + ", range=" + Arrays.toString(range) + + ", supplement=" + Arrays.toString(super.supplement) + "]"; + } + + /** + * Inner class representing a range of an encoding. + */ + private static class Range1 + { + private int first; + private int nLeft; + + @Override + public String toString() + { + return getClass().getName() + "[first=" + first + ", nLeft=" + + nLeft + "]"; + } + } + } + + /** + * Inner class representing an embedded CFF charset. + */ + abstract static class EmbeddedCharset extends CFFCharset + { + @Override + public boolean isFontSpecific() + { + return true; + } + } + + /** + * Inner class representing a Format0 charset. + */ + private static class Format0Charset extends EmbeddedCharset + { + private int format; + private int[] glyph; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", glyph=" + + Arrays.toString(glyph) + "]"; + } + } + + /** + * Inner class representing a Format1 charset. + */ + private static class Format1Charset extends EmbeddedCharset + { + private int format; + private Range1[] range; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", range=" + + Arrays.toString(range) + "]"; + } + + /** + * Inner class representing a range of a charset. + */ + private static class Range1 + { + private int first; + private int nLeft; + + @Override + public String toString() + { + return getClass().getName() + "[first=" + first + ", nLeft=" + + nLeft + "]"; + } + } + } + + /** + * Inner class representing a Format2 charset. + */ + private static class Format2Charset extends EmbeddedCharset + { + private int format; + private Range2[] range; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", range=" + + Arrays.toString(range) + "]"; + } + + /** + * Inner class representing a range of a charset. + */ + private static class Range2 + { + private int first; + private int nLeft; + + @Override + public String toString() + { + return getClass().getName() + "[first=" + first + ", nLeft=" + + nLeft + "]"; + } + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFStandardString.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFStandardString.java new file mode 100644 index 000000000..6ed4bc4a5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CFFStandardString.java @@ -0,0 +1,435 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +/** + * This class represents a standard SID to String mapping. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class CFFStandardString +{ + + private CFFStandardString() + { + } + + /** + * This will return the string mapped to the given SID. + * @param sid the given SID + * @return the mapped string + */ + public static String getName(int sid) + { + return SID2STR[sid]; + } + + private static final String[] SID2STR = + { + ".notdef", + "space", + "exclam", + "quotedbl", + "numbersign", + "dollar", + "percent", + "ampersand", + "quoteright", + "parenleft", + "parenright", + "asterisk", + "plus", + "comma", + "hyphen", + "period", + "slash", + "zero", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "colon", + "semicolon", + "less", + "equal", + "greater", + "question", + "at", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "bracketleft", + "backslash", + "bracketright", + "asciicircum", + "underscore", + "quoteleft", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "braceleft", + "bar", + "braceright", + "asciitilde", + "exclamdown", + "cent", + "sterling", + "fraction", + "yen", + "florin", + "section", + "currency", + "quotesingle", + "quotedblleft", + "guillemotleft", + "guilsinglleft", + "guilsinglright", + "fi", + "fl", + "endash", + "dagger", + "daggerdbl", + "periodcentered", + "paragraph", + "bullet", + "quotesinglbase", + "quotedblbase", + "quotedblright", + "guillemotright", + "ellipsis", + "perthousand", + "questiondown", + "grave", + "acute", + "circumflex", + "tilde", + "macron", + "breve", + "dotaccent", + "dieresis", + "ring", + "cedilla", + "hungarumlaut", + "ogonek", + "caron", + "emdash", + "AE", + "ordfeminine", + "Lslash", + "Oslash", + "OE", + "ordmasculine", + "ae", + "dotlessi", + "lslash", + "oslash", + "oe", + "germandbls", + "onesuperior", + "logicalnot", + "mu", + "trademark", + "Eth", + "onehalf", + "plusminus", + "Thorn", + "onequarter", + "divide", + "brokenbar", + "degree", + "thorn", + "threequarters", + "twosuperior", + "registered", + "minus", + "eth", + "multiply", + "threesuperior", + "copyright", + "Aacute", + "Acircumflex", + "Adieresis", + "Agrave", + "Aring", + "Atilde", + "Ccedilla", + "Eacute", + "Ecircumflex", + "Edieresis", + "Egrave", + "Iacute", + "Icircumflex", + "Idieresis", + "Igrave", + "Ntilde", + "Oacute", + "Ocircumflex", + "Odieresis", + "Ograve", + "Otilde", + "Scaron", + "Uacute", + "Ucircumflex", + "Udieresis", + "Ugrave", + "Yacute", + "Ydieresis", + "Zcaron", + "aacute", + "acircumflex", + "adieresis", + "agrave", + "aring", + "atilde", + "ccedilla", + "eacute", + "ecircumflex", + "edieresis", + "egrave", + "iacute", + "icircumflex", + "idieresis", + "igrave", + "ntilde", + "oacute", + "ocircumflex", + "odieresis", + "ograve", + "otilde", + "scaron", + "uacute", + "ucircumflex", + "udieresis", + "ugrave", + "yacute", + "ydieresis", + "zcaron", + "exclamsmall", + "Hungarumlautsmall", + "dollaroldstyle", + "dollarsuperior", + "ampersandsmall", + "Acutesmall", + "parenleftsuperior", + "parenrightsuperior", + "twodotenleader", + "onedotenleader", + "zerooldstyle", + "oneoldstyle", + "twooldstyle", + "threeoldstyle", + "fouroldstyle", + "fiveoldstyle", + "sixoldstyle", + "sevenoldstyle", + "eightoldstyle", + "nineoldstyle", + "commasuperior", + "threequartersemdash", + "periodsuperior", + "questionsmall", + "asuperior", + "bsuperior", + "centsuperior", + "dsuperior", + "esuperior", + "isuperior", + "lsuperior", + "msuperior", + "nsuperior", + "osuperior", + "rsuperior", + "ssuperior", + "tsuperior", + "ff", + "ffi", + "ffl", + "parenleftinferior", + "parenrightinferior", + "Circumflexsmall", + "hyphensuperior", + "Gravesmall", + "Asmall", + "Bsmall", + "Csmall", + "Dsmall", + "Esmall", + "Fsmall", + "Gsmall", + "Hsmall", + "Ismall", + "Jsmall", + "Ksmall", + "Lsmall", + "Msmall", + "Nsmall", + "Osmall", + "Psmall", + "Qsmall", + "Rsmall", + "Ssmall", + "Tsmall", + "Usmall", + "Vsmall", + "Wsmall", + "Xsmall", + "Ysmall", + "Zsmall", + "colonmonetary", + "onefitted", + "rupiah", + "Tildesmall", + "exclamdownsmall", + "centoldstyle", + "Lslashsmall", + "Scaronsmall", + "Zcaronsmall", + "Dieresissmall", + "Brevesmall", + "Caronsmall", + "Dotaccentsmall", + "Macronsmall", + "figuredash", + "hypheninferior", + "Ogoneksmall", + "Ringsmall", + "Cedillasmall", + "questiondownsmall", + "oneeighth", + "threeeighths", + "fiveeighths", + "seveneighths", + "onethird", + "twothirds", + "zerosuperior", + "foursuperior", + "fivesuperior", + "sixsuperior", + "sevensuperior", + "eightsuperior", + "ninesuperior", + "zeroinferior", + "oneinferior", + "twoinferior", + "threeinferior", + "fourinferior", + "fiveinferior", + "sixinferior", + "seveninferior", + "eightinferior", + "nineinferior", + "centinferior", + "dollarinferior", + "periodinferior", + "commainferior", + "Agravesmall", + "Aacutesmall", + "Acircumflexsmall", + "Atildesmall", + "Adieresissmall", + "Aringsmall", + "AEsmall", + "Ccedillasmall", + "Egravesmall", + "Eacutesmall", + "Ecircumflexsmall", + "Edieresissmall", + "Igravesmall", + "Iacutesmall", + "Icircumflexsmall", + "Idieresissmall", + "Ethsmall", + "Ntildesmall", + "Ogravesmall", + "Oacutesmall", + "Ocircumflexsmall", + "Otildesmall", + "Odieresissmall", + "OEsmall", + "Oslashsmall", + "Ugravesmall", + "Uacutesmall", + "Ucircumflexsmall", + "Udieresissmall", + "Yacutesmall", + "Thornsmall", + "Ydieresissmall", + "001.000", + "001.001", + "001.002", + "001.003", + "Black", + "Bold", + "Book", + "Light", + "Medium", + "Regular", + "Roman", + "Semibold", + }; +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CIDKeyedFDSelect.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CIDKeyedFDSelect.java new file mode 100644 index 000000000..ccbb92adc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CIDKeyedFDSelect.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.fontbox.cff; + +public abstract class CIDKeyedFDSelect { + + protected CFFFontROS owner = null; + + /** + * Constructor. + * @param _owner the owner of the FDSelect data. + */ + public CIDKeyedFDSelect(CFFFontROS _owner) { + this.owner = _owner; + } + /** + * Returns the Font DICT index for the given glyph identifier + * + * @param glyph + * @return -1 if the glyph isn't define, otherwise the FD index value + */ + public abstract int getFd(int glyph); +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringCommand.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringCommand.java new file mode 100644 index 000000000..3431ab8c2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringCommand.java @@ -0,0 +1,307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * This class represents a CharStringCommand. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CharStringCommand +{ + + private Key commandKey = null; + + /** + * Constructor with one value. + * + * @param b0 value + */ + public CharStringCommand(int b0) + { + setKey(new Key(b0)); + } + + /** + * Constructor with two values. + * + * @param b0 value1 + * @param b1 value2 + */ + public CharStringCommand(int b0, int b1) + { + setKey(new Key(b0, b1)); + } + + /** + * Constructor with an array as values. + * + * @param values array of values + */ + public CharStringCommand(int[] values) + { + setKey(new Key(values)); + } + + /** + * The key of the CharStringCommand. + * @return the key + */ + public Key getKey() + { + return commandKey; + } + + private void setKey(Key key) + { + commandKey = key; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getKey().toString(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return getKey().hashCode(); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof CharStringCommand) + { + CharStringCommand that = (CharStringCommand) object; + return getKey().equals(that.getKey()); + } + return false; + } + + /** + * A static class to hold one or more int values as key. + */ + public static class Key + { + + private int[] keyValues = null; + + /** + * Constructor with one value. + * + * @param b0 value + */ + public Key(int b0) + { + setValue(new int[] { b0 }); + } + + /** + * Constructor with two values. + * + * @param b0 value1 + * @param b1 value2 + */ + public Key(int b0, int b1) + { + setValue(new int[] { b0, b1 }); + } + + /** + * Constructor with an array as values. + * + * @param values array of values + */ + public Key(int[] values) + { + setValue(values); + } + + /** + * Array the with the values. + * + * @return array with the values + */ + public int[] getValue() + { + return keyValues; + } + + private void setValue(int[] value) + { + keyValues = value; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return Arrays.toString(getValue()); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + if (keyValues[0] == 12) + { + if (keyValues.length > 1) + { + return keyValues[0] ^ keyValues[1]; + } + } + return keyValues[0]; + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof Key) + { + Key that = (Key) object; + if (keyValues[0] == 12 && that.keyValues[0] == 12) + { + if (keyValues.length > 1 && that.keyValues.length > 1) + { + return keyValues[1] == that.keyValues[1]; + } + + return keyValues.length == that.keyValues.length; + } + return keyValues[0] == that.keyValues[0]; + } + return false; + } + } + + /** + * A map with the Type1 vocabulary. + */ + public static final Map TYPE1_VOCABULARY; + + static + { + Map map = new LinkedHashMap(); + map.put(new Key(1), "hstem"); + map.put(new Key(3), "vstem"); + map.put(new Key(4), "vmoveto"); + map.put(new Key(5), "rlineto"); + map.put(new Key(6), "hlineto"); + map.put(new Key(7), "vlineto"); + map.put(new Key(8), "rrcurveto"); + map.put(new Key(9), "closepath"); + map.put(new Key(10), "callsubr"); + map.put(new Key(11), "return"); + map.put(new Key(12), "escape"); + map.put(new Key(12, 0), "dotsection"); + map.put(new Key(12, 1), "vstem3"); + map.put(new Key(12, 2), "hstem3"); + map.put(new Key(12, 6), "seac"); + map.put(new Key(12, 7), "sbw"); + map.put(new Key(12, 12), "div"); + map.put(new Key(12, 16), "callothersubr"); + map.put(new Key(12, 17), "pop"); + map.put(new Key(12, 33), "setcurrentpoint"); + map.put(new Key(13), "hsbw"); + map.put(new Key(14), "endchar"); + map.put(new Key(21), "rmoveto"); + map.put(new Key(22), "hmoveto"); + map.put(new Key(30), "vhcurveto"); + map.put(new Key(31), "hvcurveto"); + + TYPE1_VOCABULARY = Collections.unmodifiableMap(map); + } + + /** + * A map with the Type2 vocabulary. + */ + public static final Map TYPE2_VOCABULARY; + + static + { + Map map = new LinkedHashMap(); + map.put(new Key(1), "hstem"); + map.put(new Key(3), "vstem"); + map.put(new Key(4), "vmoveto"); + map.put(new Key(5), "rlineto"); + map.put(new Key(6), "hlineto"); + map.put(new Key(7), "vlineto"); + map.put(new Key(8), "rrcurveto"); + map.put(new Key(10), "callsubr"); + map.put(new Key(11), "return"); + map.put(new Key(12), "escape"); + map.put(new Key(12, 3), "and"); + map.put(new Key(12, 4), "or"); + map.put(new Key(12, 5), "not"); + map.put(new Key(12, 9), "abs"); + map.put(new Key(12, 10), "add"); + map.put(new Key(12, 11), "sub"); + map.put(new Key(12, 12), "div"); + map.put(new Key(12, 14), "neg"); + map.put(new Key(12, 15), "eq"); + map.put(new Key(12, 18), "drop"); + map.put(new Key(12, 20), "put"); + map.put(new Key(12, 21), "get"); + map.put(new Key(12, 22), "ifelse"); + map.put(new Key(12, 23), "random"); + map.put(new Key(12, 24), "mul"); + map.put(new Key(12, 26), "sqrt"); + map.put(new Key(12, 27), "dup"); + map.put(new Key(12, 28), "exch"); + map.put(new Key(12, 29), "index"); + map.put(new Key(12, 30), "roll"); + map.put(new Key(12, 34), "hflex"); + map.put(new Key(12, 35), "flex"); + map.put(new Key(12, 36), "hflex1"); + map.put(new Key(12, 37), "flex1"); + map.put(new Key(14), "endchar"); + map.put(new Key(18), "hstemhm"); + map.put(new Key(19), "hintmask"); + map.put(new Key(20), "cntrmask"); + map.put(new Key(21), "rmoveto"); + map.put(new Key(22), "hmoveto"); + map.put(new Key(23), "vstemhm"); + map.put(new Key(24), "rcurveline"); + map.put(new Key(25), "rlinecurve"); + map.put(new Key(26), "vvcurveto"); + map.put(new Key(27), "hhcurveto"); + map.put(new Key(28), "shortint"); + map.put(new Key(29), "callgsubr"); + map.put(new Key(30), "vhcurveto"); + map.put(new Key(31), "hvcurveto"); + + TYPE2_VOCABULARY = Collections.unmodifiableMap(map); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringConverter.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringConverter.java new file mode 100644 index 000000000..b30f5d1b1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringConverter.java @@ -0,0 +1,457 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A class to translate Type2 CharString command sequence to Type1 CharString command sequence. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CharStringConverter extends CharStringHandler +{ + + private int defaultWidthX = 0; + private int nominalWidthX = 0; + private List sequence = null; + private int pathCount = 0; + private IndexData globalSubrIndex = null; + private IndexData localSubrIndex = null; + + /** + * Constructor. + * + * @param defaultWidth default width + * @param nominalWidth nominal width + */ + public CharStringConverter(int defaultWidth, int nominalWidth, IndexData fontGlobalSubrIndex, IndexData fontLocalSubrIndex) + { + defaultWidthX = defaultWidth; + nominalWidthX = nominalWidth; + globalSubrIndex = fontGlobalSubrIndex; + localSubrIndex = fontLocalSubrIndex; + } + + /** + * Converts a sequence of Type1/Type2 commands into a sequence of CharStringCommands. + * @param commandSequence the type1/type2 sequence + * @return the CHarStringCommandSequence + */ + public List convert(List commandSequence) + { + sequence = new ArrayList(); + pathCount = 0; + handleSequence(commandSequence); + return sequence; + } + + /** + * {@inheritDoc} + */ + @Override + public List handleCommand(List numbers, CharStringCommand command) + { + + if (CharStringCommand.TYPE1_VOCABULARY.containsKey(command.getKey())) + { + return handleType1Command(numbers, command); + } + else + { + return handleType2Command(numbers, command); + } + } + + private List handleType1Command(List numbers, + CharStringCommand command) + { + String name = CharStringCommand.TYPE1_VOCABULARY.get(command.getKey()); + + if ("hstem".equals(name)) + { + numbers = clearStack(numbers, numbers.size() % 2 != 0); + expandStemHints(numbers, true); + } + else if ("vstem".equals(name)) + { + numbers = clearStack(numbers, numbers.size() % 2 != 0); + expandStemHints(numbers, false); + } + else if ("vmoveto".equals(name)) + { + numbers = clearStack(numbers, numbers.size() > 1); + markPath(); + addCommand(numbers, command); + } + else if ("rlineto".equals(name)) + { + addCommandList(split(numbers, 2), command); + } + else if ("hlineto".equals(name)) + { + drawAlternatingLine(numbers, true); + } + else if ("vlineto".equals(name)) + { + drawAlternatingLine(numbers, false); + } + else if ("rrcurveto".equals(name)) + { + addCommandList(split(numbers, 6), command); + } + else if ("endchar".equals(name)) + { + numbers = clearStack(numbers, numbers.size() > 0); + closePath(); + addCommand(numbers, command); + } + else if ("rmoveto".equals(name)) + { + numbers = clearStack(numbers, numbers.size() > 2); + markPath(); + addCommand(numbers, command); + } + else if ("hmoveto".equals(name)) + { + numbers = clearStack(numbers, numbers.size() > 1); + markPath(); + addCommand(numbers, command); + } + else if ("vhcurveto".equals(name)) + { + drawAlternatingCurve(numbers, false); + } + else if ("hvcurveto".equals(name)) + { + drawAlternatingCurve(numbers, true); + } + else if ("callsubr".equals(name)) + { + //get subrbias + int bias = 0; + int nSubrs = localSubrIndex.getCount(); + + if (nSubrs < 1240) + { + bias = 107; + } + else if (nSubrs < 33900) + { + bias = 1131; + } + else + { + bias = 32768; + } + + List result = null; + int subrNumber = bias+numbers.get(numbers.size()-1); + if (subrNumber < localSubrIndex.getCount()) + { + Type2CharStringParser parser = new Type2CharStringParser(); + byte[] bytes = localSubrIndex.getBytes(subrNumber); + List parsed = null; + try { + parsed = parser.parse(bytes); + parsed.addAll(0,numbers.subList(0, numbers.size()-1)); + result = handleSequence(parsed); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + return result; + } + else if ("return".equals(name)) + { + return numbers; + } + else + { + addCommand(numbers, command); + } + return null; + } + + @SuppressWarnings(value = { "unchecked" }) + private List handleType2Command(List numbers, + CharStringCommand command) + { + String name = CharStringCommand.TYPE2_VOCABULARY.get(command.getKey()); + if ("hflex".equals(name)) + { + List first = Arrays.asList(numbers.get(0), Integer.valueOf(0), + numbers.get(1), numbers.get(2), numbers.get(3), Integer.valueOf(0)); + List second = Arrays.asList(numbers.get(4), Integer.valueOf(0), + numbers.get(5), Integer.valueOf(-numbers.get(2).intValue()), + numbers.get(6), Integer.valueOf(0)); + addCommandList(Arrays.asList(first, second), new CharStringCommand(8)); + } + else if ("flex".equals(name)) + { + List first = numbers.subList(0, 6); + List second = numbers.subList(6, 12); + addCommandList(Arrays.asList(first, second), new CharStringCommand(8)); + } + else if ("hflex1".equals(name)) + { + List first = Arrays.asList(numbers.get(0), numbers.get(1), + numbers.get(2), numbers.get(3), numbers.get(4), Integer.valueOf(0)); + List second = Arrays.asList(numbers.get(5), Integer.valueOf(0), + numbers.get(6), numbers.get(7), numbers.get(8), Integer.valueOf(0)); + addCommandList(Arrays.asList(first, second), new CharStringCommand(8)); + } + else if ("flex1".equals(name)) + { + int dx = 0; + int dy = 0; + for(int i = 0; i < 5; i++){ + dx += numbers.get(i * 2).intValue(); + dy += numbers.get(i * 2 + 1).intValue(); + } + List first = numbers.subList(0, 6); + List second = Arrays.asList(numbers.get(6), numbers.get(7), numbers.get(8), + numbers.get(9), (Math.abs(dx) > Math.abs(dy) ? numbers.get(10) : Integer.valueOf(-dx)), + (Math.abs(dx) > Math.abs(dy) ? Integer.valueOf(-dy) : numbers.get(10))); + addCommandList(Arrays.asList(first, second), new CharStringCommand(8)); + } + else if ("hstemhm".equals(name)) + { + numbers = clearStack(numbers, numbers.size() % 2 != 0); + expandStemHints(numbers, true); + } + else if ("hintmask".equals(name) || "cntrmask".equals(name)) + { + numbers = clearStack(numbers, numbers.size() % 2 != 0); + if (numbers.size() > 0) + { + expandStemHints(numbers, false); + } + } + else if ("vstemhm".equals(name)) + { + numbers = clearStack(numbers, numbers.size() % 2 != 0); + expandStemHints(numbers, false); + } + else if ("rcurveline".equals(name)) + { + addCommandList(split(numbers.subList(0, numbers.size() - 2), 6), + new CharStringCommand(8)); + addCommand(numbers.subList(numbers.size() - 2, numbers.size()), + new CharStringCommand(5)); + } + else if ("rlinecurve".equals(name)) + { + addCommandList(split(numbers.subList(0, numbers.size() - 6), 2), + new CharStringCommand(5)); + addCommand(numbers.subList(numbers.size() - 6, numbers.size()), + new CharStringCommand(8)); + } + else if ("vvcurveto".equals(name)) + { + drawCurve(numbers, false); + } + else if ("hhcurveto".equals(name)) + { + drawCurve(numbers, true); + } + else if ("callgsubr".equals(name)) + { + //get subrbias + int bias = 0; + int nSubrs = globalSubrIndex.getCount(); + + if (nSubrs < 1240) + { + bias = 107; + } + else if (nSubrs < 33900) + { + bias = 1131; + } + else + { + bias = 32768; + } + List result = null; + int subrNumber = bias+numbers.get(numbers.size()-1); + if (subrNumber < nSubrs) + { + Type2CharStringParser parser = new Type2CharStringParser(); + byte[] bytes = globalSubrIndex.getBytes(subrNumber); + List parsed = null; + try { + parsed = parser.parse(bytes); + parsed.addAll(0,numbers.subList(0, numbers.size()-1)); + result = handleSequence(parsed); + } catch (IOException e) + { + e.printStackTrace(); + } + } + return result; + } + else + { + addCommand(numbers, command); + } + return null; + } + + private List clearStack(List numbers, boolean flag) + { + + if (sequence.size() == 0) + { + if (flag) + { + addCommand(Arrays.asList(Integer.valueOf(0), Integer + .valueOf(numbers.get(0).intValue() + nominalWidthX)), + new CharStringCommand(13)); + + numbers = numbers.subList(1, numbers.size()); + } + else + { + addCommand(Arrays.asList(Integer.valueOf(0), Integer + .valueOf(defaultWidthX)), new CharStringCommand(13)); + } + } + + return numbers; + } + + private void expandStemHints(List numbers, boolean horizontal) + { + // TODO + } + + private void markPath() + { + if (pathCount > 0) + { + closePath(); + } + pathCount++; + } + + private void closePath() + { + CharStringCommand command = pathCount > 0 ? (CharStringCommand) sequence + .get(sequence.size() - 1) + : null; + + CharStringCommand closepathCommand = new CharStringCommand(9); + if (command != null && !closepathCommand.equals(command)) + { + addCommand(Collections. emptyList(), closepathCommand); + } + } + + private void drawAlternatingLine(List numbers, boolean horizontal) + { + while (numbers.size() > 0) + { + addCommand(numbers.subList(0, 1), new CharStringCommand( + horizontal ? 6 : 7)); + numbers = numbers.subList(1, numbers.size()); + horizontal = !horizontal; + } + } + + private void drawAlternatingCurve(List numbers, boolean horizontal) + { + while (numbers.size() > 0) + { + boolean last = numbers.size() == 5; + if (horizontal) + { + addCommand(Arrays.asList(numbers.get(0), Integer.valueOf(0), + numbers.get(1), numbers.get(2), last ? numbers.get(4) + : Integer.valueOf(0), numbers.get(3)), + new CharStringCommand(8)); + } + else + { + addCommand(Arrays.asList(Integer.valueOf(0), numbers.get(0), + numbers.get(1), numbers.get(2), numbers.get(3), + last ? numbers.get(4) : Integer.valueOf(0)), + new CharStringCommand(8)); + } + numbers = numbers.subList(last ? 5 : 4, numbers.size()); + horizontal = !horizontal; + } + } + + private void drawCurve(List numbers, boolean horizontal) + { + while (numbers.size() > 0) + { + boolean first = numbers.size() % 4 == 1; + + if (horizontal) + { + addCommand(Arrays.asList(numbers.get(first ? 1 : 0), + first ? numbers.get(0) : Integer.valueOf(0), numbers + .get(first ? 2 : 1), + numbers.get(first ? 3 : 2), numbers.get(first ? 4 : 3), + Integer.valueOf(0)), new CharStringCommand(8)); + } + else + { + addCommand(Arrays.asList(first ? numbers.get(0) : Integer + .valueOf(0), numbers.get(first ? 1 : 0), numbers + .get(first ? 2 : 1), numbers.get(first ? 3 : 2), + Integer.valueOf(0), numbers.get(first ? 4 : 3)), + new CharStringCommand(8)); + } + numbers = numbers.subList(first ? 5 : 4, numbers.size()); + } + } + + private void addCommandList(List> numbers, + CharStringCommand command) + { + for (int i = 0; i < numbers.size(); i++) + { + addCommand(numbers.get(i), command); + } + } + + private void addCommand(List numbers, CharStringCommand command) + { + sequence.addAll(numbers); + sequence.add(command); + } + + private static List> split(List list, int size) + { + List> result = new ArrayList>(); + for (int i = 0; i < list.size() / size; i++) + { + result.add(list.subList(i * size, (i + 1) * size)); + } + return result; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringHandler.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringHandler.java new file mode 100644 index 000000000..708ca44c2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringHandler.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + + +import java.util.List; + +/** + * A Handler for CharStringCommands. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public abstract class CharStringHandler +{ + + /** + * Handler for a sequence of CharStringCommands. + * + * @param sequence of CharStringCommands + * + * @return may return a command sequence of a subroutine + */ + @SuppressWarnings(value = { "unchecked" }) + public List handleSequence(List sequence) + { + List numbers = null; + int offset = 0; + int size = sequence.size(); + for (int i = 0; i < size; i++) + { + Object object = sequence.get(i); + if (object instanceof CharStringCommand) + { + if (numbers == null) + numbers = (List) sequence.subList(offset, i); + else + numbers.addAll((List) sequence.subList(offset, i)); + List stack = handleCommand(numbers, (CharStringCommand) object); + if (stack != null && !stack.isEmpty()) + numbers = stack; + else + numbers = null; + offset = i + 1; + } + } + if (numbers != null && !numbers.isEmpty()) + return numbers; + else + return null; + } + /** + * Handler for CharStringCommands. + * + * @param numbers a list of numbers + * @param command the CharStringCommand + * + * @return may return a command sequence of a subroutine + */ + public abstract List handleCommand(List numbers, CharStringCommand command); +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringRenderer.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringRenderer.java new file mode 100644 index 000000000..dca58f500 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/CharStringRenderer.java @@ -0,0 +1,344 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents a renderer for a charstring. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class CharStringRenderer extends CharStringHandler +{ + // TODO CharStringRenderer as abstract Class with two inherited classes according to the Charsstring type.... + private boolean isCharstringType1 = true; + private boolean isFirstCommand = true; + + private GeneralPath path = null; + private Point2D sidebearingPoint = null; + private Point2D referencePoint = null; + private int width = 0; + + public CharStringRenderer() { + isCharstringType1 = true; + } + + public CharStringRenderer(boolean isType1) { + isCharstringType1 = isType1; + } + + /** + * Renders the given sequence and returns the result as a GeneralPath. + * @param sequence the given charstring sequence + * @return the rendered GeneralPath + */ + public GeneralPath render(List sequence) throws IOException + { + path = new GeneralPath(); + sidebearingPoint = new Point2D.Float(0, 0); + referencePoint = null; + setWidth(0); + handleSequence(sequence); + return path; + } + + /** + * {@inheritDoc} + */ + public List handleCommand(List numbers, CharStringCommand command) + { + if (isCharstringType1) { + handleCommandType1(numbers, command); + } else { + handleCommandType2(numbers, command); + } + return null; + } + + /** + * + * @param numbers + * @param command + */ + private void handleCommandType2(List numbers, CharStringCommand command) { + String name = CharStringCommand.TYPE2_VOCABULARY.get(command.getKey()); + + if ("vmoveto".equals(name)) // + { + if (isFirstCommand && numbers.size() == 2) { + setWidth(numbers.get(0)); + rmoveTo(Integer.valueOf(0), numbers.get(1)); + } else { + rmoveTo(Integer.valueOf(0), numbers.get(0)); + } + } + else if ("rlineto".equals(name)) // + { + if (isFirstCommand && numbers.size() == 3) { + setWidth(numbers.get(0)); + rlineTo(numbers.get(1), numbers.get(2)); + } else { + rlineTo(numbers.get(0), numbers.get(1)); + } + } + else if ("hlineto".equals(name))// + { + if (isFirstCommand && numbers.size() == 2) { + setWidth(numbers.get(0)); + rlineTo(numbers.get(1), Integer.valueOf(0)); + } else { + rlineTo(numbers.get(0), Integer.valueOf(0)); + } + } + else if ("vlineto".equals(name))// + { + if (isFirstCommand && numbers.size() == 2) { + setWidth(numbers.get(0)); + rlineTo(Integer.valueOf(0), numbers.get(1)); + } else { + rlineTo(Integer.valueOf(0), numbers.get(0)); + } + } + else if ("rrcurveto".equals(name))// + { + if (isFirstCommand && numbers.size() == 7) { + setWidth(numbers.get(0)); + rrcurveTo(numbers.get(1), numbers.get(2), numbers.get(3), numbers + .get(4), numbers.get(5), numbers.get(6)); + } else { + rrcurveTo(numbers.get(0), numbers.get(1), numbers.get(2), numbers + .get(3), numbers.get(4), numbers.get(5)); + } + } + else if ("closepath".equals(name)) + { + closePath(); + } + else if ("rmoveto".equals(name))// + { + if (isFirstCommand && numbers.size() == 3) { + setWidth(numbers.get(0)); + rmoveTo(numbers.get(1), numbers.get(2)); + } else { + rmoveTo(numbers.get(0), numbers.get(1)); + } + } + else if ("hmoveto".equals(name)) // + { + if (isFirstCommand && numbers.size() == 2) { + setWidth(numbers.get(0)); + rmoveTo(numbers.get(1), Integer.valueOf(0)); + } else { + rmoveTo(numbers.get(0), Integer.valueOf(0)); + } + } + else if ("vhcurveto".equals(name)) + { + if (isFirstCommand && numbers.size() == 5) { + setWidth(numbers.get(0)); + rrcurveTo(Integer.valueOf(0), numbers.get(1), numbers.get(2), + numbers.get(3), numbers.get(4), Integer.valueOf(0)); + } else { + rrcurveTo(Integer.valueOf(0), numbers.get(0), numbers.get(1), + numbers.get(2), numbers.get(3), Integer.valueOf(0)); + } + + } + else if ("hvcurveto".equals(name)) + { + if (isFirstCommand && numbers.size() == 5) { + setWidth(numbers.get(0)); + rrcurveTo(numbers.get(1), Integer.valueOf(0), numbers.get(2), + numbers.get(3), Integer.valueOf(0), numbers.get(4)); + } else { + rrcurveTo(numbers.get(0), Integer.valueOf(0), numbers.get(1), + numbers.get(2), Integer.valueOf(0), numbers.get(3)); + } + } + else if ("hstem".equals(name)) { + if (numbers.size() % 2 == 1 ) { + setWidth(numbers.get(0)); + } + } + else if ("vstem".equals(name)) { + if (numbers.size() % 2 == 1 ) { + setWidth(numbers.get(0)); + } + } + else if ("hstemhm".equals(name)) { + if (numbers.size() % 2 == 1 ) { + setWidth(numbers.get(0)); + } + } + else if ("hstemhm".equals(name)) { + if (numbers.size() % 2 == 1) { + setWidth(numbers.get(0)); + } + } + else if ("cntrmask".equals(name)) { + if (numbers.size() == 1 ) { + setWidth(numbers.get(0)); + } + } + else if ("hintmask".equals(name)) { + if (numbers.size() == 1 ) { + setWidth(numbers.get(0)); + } + }else if ("endchar".equals(name)) { + if (numbers.size() == 1 ) { + setWidth(numbers.get(0)); + } + } + + if (isFirstCommand) { isFirstCommand = false; } + } + + /** + * + * @param numbers + * @param command + */ + private void handleCommandType1(List numbers, CharStringCommand command) { + String name = CharStringCommand.TYPE1_VOCABULARY.get(command.getKey()); + + if ("vmoveto".equals(name)) + { + rmoveTo(Integer.valueOf(0), numbers.get(0)); + } + else if ("rlineto".equals(name)) + { + rlineTo(numbers.get(0), numbers.get(1)); + } + else if ("hlineto".equals(name)) + { + rlineTo(numbers.get(0), Integer.valueOf(0)); + } + else if ("vlineto".equals(name)) + { + rlineTo(Integer.valueOf(0), numbers.get(0)); + } + else if ("rrcurveto".equals(name)) + { + rrcurveTo(numbers.get(0), numbers.get(1), numbers.get(2), numbers + .get(3), numbers.get(4), numbers.get(5)); + } + else if ("closepath".equals(name)) + { + closePath(); + } + else if ("sbw".equals(name)) + { + pointSb(numbers.get(0), numbers.get(1)); + setWidth(numbers.get(2).intValue()); + } + else if ("hsbw".equals(name)) + { + pointSb(numbers.get(0), Integer.valueOf(0)); + setWidth(numbers.get(1).intValue()); + } + else if ("rmoveto".equals(name)) + { + rmoveTo(numbers.get(0), numbers.get(1)); + } + else if ("hmoveto".equals(name)) + { + rmoveTo(numbers.get(0), Integer.valueOf(0)); + } + else if ("vhcurveto".equals(name)) + { + rrcurveTo(Integer.valueOf(0), numbers.get(0), numbers.get(1), + numbers.get(2), numbers.get(3), Integer.valueOf(0)); + } + else if ("hvcurveto".equals(name)) + { + rrcurveTo(numbers.get(0), Integer.valueOf(0), numbers.get(1), + numbers.get(2), Integer.valueOf(0), numbers.get(3)); + } + } + + private void rmoveTo(Number dx, Number dy) + { + Point2D point = referencePoint; + if (point == null) + { + point = sidebearingPoint; + } + referencePoint = null; + path.moveTo((float)(point.getX() + dx.doubleValue()), + (float)(point.getY() + dy.doubleValue())); + } + + private void rlineTo(Number dx, Number dy) + { + Point2D point = path.getCurrentPoint(); + path.lineTo((float)(point.getX() + dx.doubleValue()), + (float)(point.getY() + dy.doubleValue())); + } + + private void rrcurveTo(Number dx1, Number dy1, Number dx2, Number dy2, + Number dx3, Number dy3) + { + Point2D point = path.getCurrentPoint(); + float x1 = (float) point.getX() + dx1.floatValue(); + float y1 = (float) point.getY() + dy1.floatValue(); + float x2 = x1 + dx2.floatValue(); + float y2 = y1 + dy2.floatValue(); + float x3 = x2 + dx3.floatValue(); + float y3 = y2 + dy3.floatValue(); + path.curveTo(x1, y1, x2, y2, x3, y3); + } + + private void closePath() + { + referencePoint = path.getCurrentPoint(); + path.closePath(); + } + + private void pointSb(Number x, Number y) + { + sidebearingPoint = new Point2D.Float(x.floatValue(), y.floatValue()); + } + + /** + * Returns the bounds of the renderer path. + * @return the bounds as Rectangle2D + */ + public Rectangle2D getBounds() + { + return path.getBounds2D(); + } + + /** + * Returns the width of the current command. + * @return the width + */ + public int getWidth() + { + return width; + } + + private void setWidth(int width) + { + this.width = width; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/DataInput.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/DataInput.java new file mode 100644 index 000000000..3687f193b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/DataInput.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.EOFException; +import java.io.IOException; + +/** + * This class contains some functionality to read a byte buffer. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class DataInput +{ + + private byte[] inputBuffer = null; + private int bufferPosition = 0; + + /** + * Constructor. + * @param buffer the buffer to be read + */ + public DataInput(byte[] buffer) + { + inputBuffer = buffer; + } + + /** + * Determines if there are any bytes left to read or not. + * @return true if there are any bytes left to read + */ + public boolean hasRemaining() + { + return bufferPosition < inputBuffer.length; + } + + /** + * Returns the current position. + * @return current position + */ + public int getPosition() + { + return bufferPosition; + } + + /** + * Sets the current position to the given value. + * @param position the given position + */ + public void setPosition(int position) + { + bufferPosition = position; + } + + /** + * Returns the buffer as an ISO-8859-1 string. + * @return the buffer as string + * @throws IOException if an error occurs during reading + */ + public String getString() throws IOException + { + return new String(inputBuffer, "ISO-8859-1"); + } + + /** + * Read one single byte from the buffer. + * @return the byte + * @throws IOException if an error occurs during reading + */ + public byte readByte() throws IOException + { + return (byte) readUnsignedByte(); + } + + /** + * Read one single unsigned byte from the buffer. + * @return the unsigned byte as int + * @throws IOException if an error occurs during reading + */ + public int readUnsignedByte() throws IOException + { + int b = read(); + if (b < 0) + { + throw new EOFException(); + } + return b; + } + + /** + * Read one single short value from the buffer. + * @return the short value + * @throws IOException if an error occurs during reading + */ + public short readShort() throws IOException + { + return (short) readUnsignedShort(); + } + + /** + * Read one single unsigned short (2 bytes) value from the buffer. + * @return the unsigned short value as int + * @throws IOException if an error occurs during reading + */ + public int readUnsignedShort() throws IOException + { + int b1 = read(); + int b2 = read(); + if ((b1 | b2) < 0) + { + throw new EOFException(); + } + return b1 << 8 | b2; + } + + /** + * Read one single int (4 bytes) from the buffer. + * @return the int value + * @throws IOException if an error occurs during reading + */ + public int readInt() throws IOException + { + int b1 = read(); + int b2 = read(); + int b3 = read(); + int b4 = read(); + if ((b1 | b2 | b3 | b4) < 0) + { + throw new EOFException(); + } + return b1 << 24 | b2 << 16 | b3 << 8 | b4; + } + + /** + * Read a number of single byte values from the buffer. + * @param length the number of bytes to be read + * @return an array with containing the bytes from the buffer + * @throws IOException if an error occurs during reading + */ + public byte[] readBytes(int length) throws IOException + { + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) + { + bytes[i] = readByte(); + } + return bytes; + } + + private int read() + { + try + { + int value = inputBuffer[bufferPosition] & 0xff; + bufferPosition++; + return value; + } + catch (RuntimeException re) + { + return -1; + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/DataOutput.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/DataOutput.java new file mode 100644 index 000000000..0939b509b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/DataOutput.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class DataOutput +{ + + private ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); + + private String outputEncoding = null; + + /** + * Constructor. + */ + public DataOutput() + { + this("ISO-8859-1"); + } + + /** + * Constructor with a given encoding. + * @param encoding the encoding to be used for writing + */ + public DataOutput(String encoding) + { + this.outputEncoding = encoding; + } + + /** + * Returns the written data buffer as byte array. + * @return the data buffer as byte array + */ + public byte[] getBytes() + { + return outputBuffer.toByteArray(); + } + + /** + * Write an int value to the buffer. + * @param value the given value + */ + public void write(int value) + { + outputBuffer.write(value); + } + + /** + * Write a byte array to the buffer. + * @param buffer the given byte array + */ + public void write(byte[] buffer) + { + outputBuffer.write(buffer, 0, buffer.length); + } + + /** + * Write a part of a byte array to the buffer. + * @param buffer the given byte buffer + * @param offset the offset where to start + * @param length the amount of bytes to be written from the array + */ + public void write(byte[] buffer, int offset, int length) + { + outputBuffer.write(buffer, offset, length); + } + + /** + * Write the given string to the buffer using the given encoding. + * @param string the given string + * @throws IOException If an error occurs during writing the data to the buffer + */ + public void print(String string) throws IOException + { + write(string.getBytes(outputEncoding)); + } + + /** + * Write the given string to the buffer using the given encoding. + * A newline is added after the given string + * @param string the given string + * @throws IOException If an error occurs during writing the data to the buffer + */ + public void println(String string) throws IOException + { + write(string.getBytes(outputEncoding)); + write('\n'); + } + + /** + * Add a newline to the given string. + */ + public void println() + { + write('\n'); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/IndexData.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/IndexData.java new file mode 100644 index 000000000..7ef324bde --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/IndexData.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.util.Arrays; + +/** + * Class holding the IndexData of a CFF font. + */ +public class IndexData +{ + private int count; + private int[] offset; + private int[] data; + + /** + * Constructor. + * + * @param count number of index values + */ + public IndexData(int count) + { + this.count = count; + this.offset = new int[count+1]; + } + + public byte[] getBytes(int index) + { + int length = offset[index + 1] - offset[index]; + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) + { + bytes[i] = (byte) data[offset[index] - 1 + i]; + } + return bytes; + } + + @Override + public String toString() + { + return getClass().getName() + "[count=" + count + + ", offset=" + Arrays.toString(offset) + + ", data=" + Arrays.toString(data) + "]"; + } + + /** + * Returns the count value. + * @return the count value + */ + public int getCount() + { + return count; + } + + /** + * Sets the offset value to the given value. + * @param index the index of the offset value + * @param value the given offset value + */ + public void setOffset(int index, int value) + { + offset[index] = value; + } + + /** + * Returns the offset at the given index. + * @param index the index + * @return the offset value at the given index + */ + public int getOffset(int index) + { + return offset[index]; + } + + /** + * Initializes the data array with the given size. + * @param dataSize the size of the data array + */ + public void initData(int dataSize) + { + data = new int[dataSize]; + } + + /** + * Sets the data value to the given value. + * @param index the index of the data value + * @param value the given data value + */ + public void setData(int index, int value) + { + data[index] = value; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringFormatter.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringFormatter.java new file mode 100644 index 000000000..ba564cf57 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringFormatter.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.ByteArrayOutputStream; +import java.util.List; + +/** + * This class represents a formatter for CharString commands of a Type1 font. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class Type1CharStringFormatter +{ + + private ByteArrayOutputStream output = null; + + /** + * Formats the given command sequence to a byte array. + * @param sequence the given command sequence + * @return the formatted seuqence as byte array + */ + public byte[] format(List sequence) + { + output = new ByteArrayOutputStream(); + + for (Object object : sequence) + { + if (object instanceof CharStringCommand) + { + writeCommand((CharStringCommand) object); + } + else if (object instanceof Integer) + { + writeNumber((Integer) object); + } + else + { + throw new IllegalArgumentException(); + } + } + return output.toByteArray(); + } + + private void writeCommand(CharStringCommand command) + { + int[] value = command.getKey().getValue(); + for (int i = 0; i < value.length; i++) + { + output.write(value[i]); + } + } + + private void writeNumber(Integer number) + { + int value = number.intValue(); + if (value >= -107 && value <= 107) + { + output.write(value + 139); + } + else if (value >= 108 && value <= 1131) + { + int b1 = (value - 108) % 256; + int b0 = (value - 108 - b1) / 256 + 247; + output.write(b0); + output.write(b1); + } + else if (value >= -1131 && value <= -108) + { + int b1 = -((value + 108) % 256); + int b0 = -((value + 108 + b1) / 256 - 251); + output.write(b0); + output.write(b1); + } + else + { + int b1 = value >>> 24 & 0xff; + int b2 = value >>> 16 & 0xff; + int b3 = value >>> 8 & 0xff; + int b4 = value >>> 0 & 0xff; + output.write(255); + output.write(b1); + output.write(b2); + output.write(b3); + output.write(b4); + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringParser.java new file mode 100644 index 000000000..8a39ef6f4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1CharStringParser.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents a converter for a mapping into a Type1-sequence. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class Type1CharStringParser +{ + + private DataInput input = null; + private List sequence = null; + + /** + * The given byte array will be parsed and converted to a Type1 sequence. + * @param bytes the given mapping as byte array + * @return the Type1 sequence + * @throws IOException if an error occurs during reading + */ + public List parse(byte[] bytes) throws IOException + { + input = new DataInput(bytes); + sequence = new ArrayList(); + while (input.hasRemaining()) + { + int b0 = input.readUnsignedByte(); + + if (b0 >= 0 && b0 <= 31) + { + sequence.add(readCommand(b0)); + } + else if (b0 >= 32 && b0 <= 255) + { + sequence.add(readNumber(b0)); + } + else + { + throw new IllegalArgumentException(); + } + } + return sequence; + } + + private CharStringCommand readCommand(int b0) throws IOException + { + if (b0 == 12) + { + int b1 = input.readUnsignedByte(); + return new CharStringCommand(b0, b1); + } + return new CharStringCommand(b0); + } + + private Integer readNumber(int b0) throws IOException + { + if (b0 >= 32 && b0 <= 246) + { + return Integer.valueOf(b0 - 139); + } + else if (b0 >= 247 && b0 <= 250) + { + int b1 = input.readUnsignedByte(); + return Integer.valueOf((b0 - 247) * 256 + b1 + 108); + } + else if (b0 >= 251 && b0 <= 254) + { + int b1 = input.readUnsignedByte(); + return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108); + } + else if (b0 == 255) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + int b3 = input.readUnsignedByte(); + int b4 = input.readUnsignedByte(); + + return Integer.valueOf(b1 << 24 | b2 << 16 | b3 << 8 | b4); + } + else + { + throw new IllegalArgumentException(); + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontFormatter.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontFormatter.java new file mode 100644 index 000000000..053b597b1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontFormatter.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Collection; +import java.util.Locale; + +/** + * This class represents a formatter for a given Type1 font. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class Type1FontFormatter +{ + + private Type1FontFormatter() + { + } + + /** + * Read and convert a given CFFFont. + * @param font the given CFFFont + * @return the Type1 font + * @throws IOException if an error occurs during reading the given font + */ + public static byte[] format(CFFFont font) throws IOException + { + DataOutput output = new DataOutput(); + printFont(font, output); + return output.getBytes(); + } + + private static void printFont(CFFFont font, DataOutput output) + throws IOException + { + output.println("%!FontType1-1.0 " + font.getName() + " " + + font.getProperty("version")); + + printFontDictionary(font, output); + + for (int i = 0; i < 8; i++) + { + StringBuilder sb = new StringBuilder(); + + for (int j = 0; j < 64; j++) + { + sb.append("0"); + } + + output.println(sb.toString()); + } + + output.println("cleartomark"); + } + + private static void printFontDictionary(CFFFont font, DataOutput output) + throws IOException + { + output.println("10 dict begin"); + output.println("/FontInfo 10 dict dup begin"); + output.println("/version (" + font.getProperty("version") + + ") readonly def"); + output.println("/Notice (" + font.getProperty("Notice") + + ") readonly def"); + output.println("/FullName (" + font.getProperty("FullName") + + ") readonly def"); + output.println("/FamilyName (" + font.getProperty("FamilyName") + + ") readonly def"); + output.println("/Weight (" + font.getProperty("Weight") + + ") readonly def"); + output.println("/ItalicAngle " + font.getProperty("ItalicAngle") + + " def"); + output.println("/isFixedPitch " + font.getProperty("isFixedPitch") + + " def"); + output.println("/UnderlinePosition " + + font.getProperty("UnderlinePosition") + " def"); + output.println("/UnderlineThickness " + + font.getProperty("UnderlineThickness") + " def"); + output.println("end readonly def"); + output.println("/FontName /" + font.getName() + " def"); + output.println("/PaintType " + font.getProperty("PaintType") + " def"); + output.println("/FontType 1 def"); + NumberFormat matrixFormat = new DecimalFormat("0.########", new DecimalFormatSymbols(Locale.US)); + output.println("/FontMatrix " + + formatArray(font.getProperty("FontMatrix"), matrixFormat, false) + + " readonly def"); + output.println("/FontBBox " + + formatArray(font.getProperty("FontBBox"), false) + + " readonly def"); + output.println("/StrokeWidth " + font.getProperty("StrokeWidth") + + " def"); + + Collection mappings = font.getMappings(); + + output.println("/Encoding 256 array"); + output.println("0 1 255 {1 index exch /.notdef put} for"); + + for (CFFFont.Mapping mapping : mappings) + { + output.println("dup " + mapping.getCode() + " /" + + mapping.getName() + " put"); + } + + output.println("readonly def"); + output.println("currentdict end"); + + DataOutput eexecOutput = new DataOutput(); + + printEexecFontDictionary(font, eexecOutput); + + output.println("currentfile eexec"); + + byte[] eexecBytes = Type1FontUtil.eexecEncrypt(eexecOutput.getBytes()); + + String hexString = Type1FontUtil.hexEncode(eexecBytes); + for (int i = 0; i < hexString.length();) + { + String hexLine = hexString.substring(i, Math.min(i + 72, hexString + .length())); + + output.println(hexLine); + + i += hexLine.length(); + } + } + + private static void printEexecFontDictionary(CFFFont font, DataOutput output) + throws IOException + { + output.println("dup /Private 15 dict dup begin"); + output + .println("/RD {string currentfile exch readstring pop} executeonly def"); + output.println("/ND {noaccess def} executeonly def"); + output.println("/NP {noaccess put} executeonly def"); + output.println("/BlueValues " + + formatArray(font.getProperty("BlueValues"), true) + " ND"); + output.println("/OtherBlues " + + formatArray(font.getProperty("OtherBlues"), true) + " ND"); + output.println("/BlueScale " + font.getProperty("BlueScale") + " def"); + output.println("/BlueShift " + font.getProperty("BlueShift") + " def"); + output.println("/BlueFuzz " + font.getProperty("BlueFuzz") + " def"); + output.println("/StdHW " + formatArray(font.getProperty("StdHW"), true) + + " ND"); + output.println("/StdVW " + formatArray(font.getProperty("StdVW"), true) + + " ND"); + output.println("/ForceBold " + font.getProperty("ForceBold") + " def"); + output.println("/MinFeature {16 16} def"); + output.println("/password 5839 def"); + + Collection mappings = font.getMappings(); + + output.println("2 index /CharStrings " + mappings.size() + + " dict dup begin"); + + Type1CharStringFormatter formatter = new Type1CharStringFormatter(); + + for (CFFFont.Mapping mapping : mappings) + { + byte[] type1Bytes = formatter.format(mapping.toType1Sequence()); + + byte[] charstringBytes = Type1FontUtil.charstringEncrypt( + type1Bytes, 4); + + output.print("/" + mapping.getName() + " " + charstringBytes.length + + " RD "); + output.write(charstringBytes); + output.print(" ND"); + + output.println(); + } + + output.println("end"); + output.println("end"); + + output.println("readonly put"); + output.println("noaccess put"); + output.println("dup /FontName get exch definefont pop"); + output.println("mark currentfile closefile"); + } + + private static String formatArray(Object object, boolean executable) + { + return formatArray(object, null, executable); + } + + private static String formatArray(Object object, NumberFormat format, boolean executable) + { + StringBuffer sb = new StringBuffer(); + + sb.append(executable ? "{" : "["); + + if (object instanceof Collection) + { + String sep = ""; + + Collection elements = (Collection) object; + for (Object element : elements) + { + sb.append(sep).append(formatElement(element, format)); + + sep = " "; + } + } + else if (object instanceof Number) + { + sb.append(formatElement(object, format)); + } + + sb.append(executable ? "}" : "]"); + + return sb.toString(); + } + + private static String formatElement(Object object, NumberFormat format) + { + if(format != null) + { + if (object instanceof Double || object instanceof Float) + { + Number number = (Number)object; + return format.format(number.doubleValue()); + } + else if (object instanceof Long || object instanceof Integer) + { + Number number = (Number)object; + return format.format(number.longValue()); + } + } + return String.valueOf(object); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontUtil.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontUtil.java new file mode 100644 index 000000000..22a168d98 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type1FontUtil.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +/** + * This class contains some helper methods handling Type1-Fonts. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class Type1FontUtil +{ + + private Type1FontUtil() + { + } + + /** + * Converts a byte-array into a string with the corresponding hex value. + * @param bytes the byte array + * @return the string with the hex value + */ + public static String hexEncode(byte[] bytes) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) + { + String string = Integer.toHexString(bytes[i] & 0xff); + if (string.length() == 1) + { + sb.append("0"); + } + sb.append(string.toUpperCase()); + } + return sb.toString(); + } + + /** + * Converts a string representing a hex value into a byte array. + * @param string the string representing the hex value + * @return the hex value as byte array + */ + public static byte[] hexDecode(String string) + { + if (string.length() % 2 != 0) + { + throw new IllegalArgumentException(); + } + byte[] bytes = new byte[string.length() / 2]; + for (int i = 0; i < string.length(); i += 2) + { + bytes[i / 2] = (byte) Integer.parseInt(string.substring(i, i + 2), 16); + } + return bytes; + } + + /** + * Encrypt eexec. + * @param buffer the given data + * @return the encrypted data + */ + public static byte[] eexecEncrypt(byte[] buffer) + { + return encrypt(buffer, 55665, 4); + } + + /** + * Encrypt charstring. + * @param buffer the given data + * @param n blocksize? + * @return the encrypted data + */ + public static byte[] charstringEncrypt(byte[] buffer, int n) + { + return encrypt(buffer, 4330, n); + } + + private static byte[] encrypt(byte[] plaintextBytes, int r, int n) + { + byte[] buffer = new byte[plaintextBytes.length + n]; + + for (int i = 0; i < n; i++) + { + buffer[i] = 0; + } + + System.arraycopy(plaintextBytes, 0, buffer, n, buffer.length - n); + + int c1 = 52845; + int c2 = 22719; + + byte[] ciphertextBytes = new byte[buffer.length]; + + for (int i = 0; i < buffer.length; i++) + { + int plain = buffer[i] & 0xff; + int cipher = plain ^ r >> 8; + + ciphertextBytes[i] = (byte) cipher; + + r = (cipher + r) * c1 + c2 & 0xffff; + } + + return ciphertextBytes; + } + + /** + * Decrypt eexec. + * @param buffer the given encrypted data + * @return the decrypted data + */ + public static byte[] eexecDecrypt(byte[] buffer) + { + return decrypt(buffer, 55665, 4); + } + + /** + * Decrypt charstring. + * @param buffer the given encrypted data + * @param n blocksize? + * @return the decrypted data + */ + public static byte[] charstringDecrypt(byte[] buffer, int n) + { + return decrypt(buffer, 4330, n); + } + + private static byte[] decrypt(byte[] ciphertextBytes, int r, int n) + { + byte[] buffer = new byte[ciphertextBytes.length]; + + int c1 = 52845; + int c2 = 22719; + + for (int i = 0; i < ciphertextBytes.length; i++) + { + int cipher = ciphertextBytes[i] & 0xff; + int plain = cipher ^ r >> 8; + + buffer[i] = (byte) plain; + + r = (cipher + r) * c1 + c2 & 0xffff; + } + + byte[] plaintextBytes = new byte[ciphertextBytes.length - n]; + System.arraycopy(buffer, n, plaintextBytes, 0, plaintextBytes.length); + + return plaintextBytes; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type2CharStringParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type2CharStringParser.java new file mode 100644 index 000000000..dc1a1cac0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/Type2CharStringParser.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents a converter for a mapping into a Type2-sequence. + * @author Villu Ruusmann + * @version $Revision: 1.0 $ + */ +public class Type2CharStringParser +{ + + private DataInput input = null; + private int hstemCount = 0; + private int vstemCount = 0; + private List sequence = null; + + /** + * The given byte array will be parsed and converted to a Type2 sequence. + * @param bytes the given mapping as byte array + * @return the Type2 sequence + * @throws IOException if an error occurs during reading + */ + public List parse(byte[] bytes) throws IOException + { + input = new DataInput(bytes); + + hstemCount = 0; + vstemCount = 0; + + sequence = new ArrayList(); + + while (input.hasRemaining()) + { + int b0 = input.readUnsignedByte(); + + if (b0 >= 0 && b0 <= 27) + { + sequence.add(readCommand(b0)); + } + else if (b0 == 28) + { + sequence.add(readNumber(b0)); + } + else if (b0 >= 29 && b0 <= 31) + { + sequence.add(readCommand(b0)); + } + else if (b0 >= 32 && b0 <= 255) + { + sequence.add(readNumber(b0)); + } + else + { + throw new IllegalArgumentException(); + } + } + + return sequence; + } + + private CharStringCommand readCommand(int b0) throws IOException + { + + if (b0 == 1 || b0 == 18) + { + hstemCount += peekNumbers().size() / 2; + } + else if (b0 == 3 || b0 == 19 || b0 == 20 || b0 == 23) + { + vstemCount += peekNumbers().size() / 2; + } // End if + + if (b0 == 12) + { + int b1 = input.readUnsignedByte(); + + return new CharStringCommand(b0, b1); + } + else if (b0 == 19 || b0 == 20) + { + int[] value = new int[1 + getMaskLength()]; + value[0] = b0; + + for (int i = 1; i < value.length; i++) + { + value[i] = input.readUnsignedByte(); + } + + return new CharStringCommand(value); + } + + return new CharStringCommand(b0); + } + + private Integer readNumber(int b0) throws IOException + { + + if (b0 == 28) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + + return Integer.valueOf((short) (b1 << 8 | b2)); + } + else if (b0 >= 32 && b0 <= 246) + { + return Integer.valueOf(b0 - 139); + } + else if (b0 >= 247 && b0 <= 250) + { + int b1 = input.readUnsignedByte(); + + return Integer.valueOf((b0 - 247) * 256 + b1 + 108); + } + else if (b0 >= 251 && b0 <= 254) + { + int b1 = input.readUnsignedByte(); + + return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108); + } + else if (b0 == 255) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + int b3 = input.readUnsignedByte(); + int b4 = input.readUnsignedByte(); + + // The lower bytes are representing the digits after + // the decimal point and aren't needed in this context + return Integer.valueOf((short)(b1 << 8 | b2)); + } + else + { + throw new IllegalArgumentException(); + } + } + + private int getMaskLength() + { + int length = 1; + + int hintCount = hstemCount + vstemCount; + while ((hintCount -= 8) > 0) + { + length++; + } + + return length; + } + + private List peekNumbers() + { + List numbers = new ArrayList(); + + for (int i = sequence.size() - 1; i > -1; i--) + { + Object object = sequence.get(i); + + if (object instanceof Number) + { + Number number = (Number) object; + + numbers.add(0, number); + + continue; + } + + return numbers; + } + + return numbers; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFCharset.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFCharset.java new file mode 100644 index 000000000..96b0e489b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFCharset.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.charset; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is the superclass for all CFFFont charsets. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public abstract class CFFCharset +{ + private List entries = new ArrayList(); + + /** + * Determines if the charset is font specific or not. + * @return if the charset is font specific + */ + public boolean isFontSpecific() + { + return false; + } + + /** + * Returns the SID corresponding to the given name. + * @param name the given SID + * @return the corresponding SID + */ + public int getSID(String name) + { + for(Entry entry : this.entries) + { + if((entry.entryName).equals(name)) + { + return entry.entrySID; + } + } + return -1; + } + + /** + * Returns the name corresponding to the given SID. + * @param sid the given SID + * @return the corresponding name + */ + public String getName(int sid) + { + for(Entry entry : this.entries) + { + if(entry.entrySID == sid) + { + return entry.entryName; + } + } + return null; + } + + /** + * Adds a new SID/name combination to the charset. + * @param sid the given SID + * @param name the given name + */ + public void register(int sid, String name) + { + entries.add(new Entry(sid,name)); + } + + /** + * Add a single entry. + * @param entry the entry to be added + */ + public void addEntry(Entry entry) + { + entries.add(entry); + } + + /** + * A list of all entries within this charset. + * @return a list of all entries + */ + public List getEntries() + { + return entries; + } + + /** + * This class represents a single SID/name mapping of the charset. + * + */ + public static class Entry + { + private int entrySID; + private String entryName; + + /** + * Create a new instance of Entry with the given values. + * @param sid the SID + * @param name the Name + */ + protected Entry(int sid, String name) + { + this.entrySID = sid; + this.entryName = name; + } + + /** + * The SID of this entry. + * @return the SID + */ + public int getSID() + { + return entrySID; + } + + /** + * The Name of this entry. + * @return the name + */ + public String getName() + { + return entryName; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return "[sid=" + entrySID + ", name=" + entryName + "]"; + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertCharset.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertCharset.java new file mode 100644 index 000000000..9d1df0e73 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertCharset.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.charset; + +/** + * This is specialized CFFCharset. It's used if the CharsetId of a font is set to 1. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFExpertCharset extends CFFCharset +{ + + private CFFExpertCharset() + { + } + + /** + * Returns an instance of the CFFExpertCharset class. + * @return an instance of CFFExpertCharset + */ + public static CFFExpertCharset getInstance() + { + return CFFExpertCharset.INSTANCE; + } + + private static final CFFExpertCharset INSTANCE = new CFFExpertCharset(); + + static + { + INSTANCE.register(1, "space"); + INSTANCE.register(13, "comma"); + INSTANCE.register(14, "hyphen"); + INSTANCE.register(15, "period"); + INSTANCE.register(27, "colon"); + INSTANCE.register(28, "semicolon"); + INSTANCE.register(99, "fraction"); + INSTANCE.register(109, "fi"); + INSTANCE.register(110, "fl"); + INSTANCE.register(150, "onesuperior"); + INSTANCE.register(155, "onehalf"); + INSTANCE.register(158, "onequarter"); + INSTANCE.register(163, "threequarters"); + INSTANCE.register(164, "twosuperior"); + INSTANCE.register(169, "threesuperior"); + INSTANCE.register(229, "exclamsmall"); + INSTANCE.register(230, "Hungarumlautsmall"); + INSTANCE.register(231, "dollaroldstyle"); + INSTANCE.register(232, "dollarsuperior"); + INSTANCE.register(233, "ampersandsmall"); + INSTANCE.register(234, "Acutesmall"); + INSTANCE.register(235, "parenleftsuperior"); + INSTANCE.register(236, "parenrightsuperior"); + INSTANCE.register(237, "twodotenleader"); + INSTANCE.register(238, "onedotenleader"); + INSTANCE.register(239, "zerooldstyle"); + INSTANCE.register(240, "oneoldstyle"); + INSTANCE.register(241, "twooldstyle"); + INSTANCE.register(242, "threeoldstyle"); + INSTANCE.register(243, "fouroldstyle"); + INSTANCE.register(244, "fiveoldstyle"); + INSTANCE.register(245, "sixoldstyle"); + INSTANCE.register(246, "sevenoldstyle"); + INSTANCE.register(247, "eightoldstyle"); + INSTANCE.register(248, "nineoldstyle"); + INSTANCE.register(249, "commasuperior"); + INSTANCE.register(250, "threequartersemdash"); + INSTANCE.register(251, "periodsuperior"); + INSTANCE.register(252, "questionsmall"); + INSTANCE.register(253, "asuperior"); + INSTANCE.register(254, "bsuperior"); + INSTANCE.register(255, "centsuperior"); + INSTANCE.register(256, "dsuperior"); + INSTANCE.register(257, "esuperior"); + INSTANCE.register(258, "isuperior"); + INSTANCE.register(259, "lsuperior"); + INSTANCE.register(260, "msuperior"); + INSTANCE.register(261, "nsuperior"); + INSTANCE.register(262, "osuperior"); + INSTANCE.register(263, "rsuperior"); + INSTANCE.register(264, "ssuperior"); + INSTANCE.register(265, "tsuperior"); + INSTANCE.register(266, "ff"); + INSTANCE.register(267, "ffi"); + INSTANCE.register(268, "ffl"); + INSTANCE.register(269, "parenleftinferior"); + INSTANCE.register(270, "parenrightinferior"); + INSTANCE.register(271, "Circumflexsmall"); + INSTANCE.register(272, "hyphensuperior"); + INSTANCE.register(273, "Gravesmall"); + INSTANCE.register(274, "Asmall"); + INSTANCE.register(275, "Bsmall"); + INSTANCE.register(276, "Csmall"); + INSTANCE.register(277, "Dsmall"); + INSTANCE.register(278, "Esmall"); + INSTANCE.register(279, "Fsmall"); + INSTANCE.register(280, "Gsmall"); + INSTANCE.register(281, "Hsmall"); + INSTANCE.register(282, "Ismall"); + INSTANCE.register(283, "Jsmall"); + INSTANCE.register(284, "Ksmall"); + INSTANCE.register(285, "Lsmall"); + INSTANCE.register(286, "Msmall"); + INSTANCE.register(287, "Nsmall"); + INSTANCE.register(288, "Osmall"); + INSTANCE.register(289, "Psmall"); + INSTANCE.register(290, "Qsmall"); + INSTANCE.register(291, "Rsmall"); + INSTANCE.register(292, "Ssmall"); + INSTANCE.register(293, "Tsmall"); + INSTANCE.register(294, "Usmall"); + INSTANCE.register(295, "Vsmall"); + INSTANCE.register(296, "Wsmall"); + INSTANCE.register(297, "Xsmall"); + INSTANCE.register(298, "Ysmall"); + INSTANCE.register(299, "Zsmall"); + INSTANCE.register(300, "colonmonetary"); + INSTANCE.register(301, "onefitted"); + INSTANCE.register(302, "rupiah"); + INSTANCE.register(303, "Tildesmall"); + INSTANCE.register(304, "exclamdownsmall"); + INSTANCE.register(305, "centoldstyle"); + INSTANCE.register(306, "Lslashsmall"); + INSTANCE.register(307, "Scaronsmall"); + INSTANCE.register(308, "Zcaronsmall"); + INSTANCE.register(309, "Dieresissmall"); + INSTANCE.register(310, "Brevesmall"); + INSTANCE.register(311, "Caronsmall"); + INSTANCE.register(312, "Dotaccentsmall"); + INSTANCE.register(313, "Macronsmall"); + INSTANCE.register(314, "figuredash"); + INSTANCE.register(315, "hypheninferior"); + INSTANCE.register(316, "Ogoneksmall"); + INSTANCE.register(317, "Ringsmall"); + INSTANCE.register(318, "Cedillasmall"); + INSTANCE.register(319, "questiondownsmall"); + INSTANCE.register(320, "oneeighth"); + INSTANCE.register(321, "threeeighths"); + INSTANCE.register(322, "fiveeighths"); + INSTANCE.register(323, "seveneighths"); + INSTANCE.register(324, "onethird"); + INSTANCE.register(325, "twothirds"); + INSTANCE.register(326, "zerosuperior"); + INSTANCE.register(327, "foursuperior"); + INSTANCE.register(328, "fivesuperior"); + INSTANCE.register(329, "sixsuperior"); + INSTANCE.register(330, "sevensuperior"); + INSTANCE.register(331, "eightsuperior"); + INSTANCE.register(332, "ninesuperior"); + INSTANCE.register(333, "zeroinferior"); + INSTANCE.register(334, "oneinferior"); + INSTANCE.register(335, "twoinferior"); + INSTANCE.register(336, "threeinferior"); + INSTANCE.register(337, "fourinferior"); + INSTANCE.register(338, "fiveinferior"); + INSTANCE.register(339, "sixinferior"); + INSTANCE.register(340, "seveninferior"); + INSTANCE.register(341, "eightinferior"); + INSTANCE.register(342, "nineinferior"); + INSTANCE.register(343, "centinferior"); + INSTANCE.register(344, "dollarinferior"); + INSTANCE.register(345, "periodinferior"); + INSTANCE.register(346, "commainferior"); + INSTANCE.register(347, "Agravesmall"); + INSTANCE.register(348, "Aacutesmall"); + INSTANCE.register(349, "Acircumflexsmall"); + INSTANCE.register(350, "Atildesmall"); + INSTANCE.register(351, "Adieresissmall"); + INSTANCE.register(352, "Aringsmall"); + INSTANCE.register(353, "AEsmall"); + INSTANCE.register(354, "Ccedillasmall"); + INSTANCE.register(355, "Egravesmall"); + INSTANCE.register(356, "Eacutesmall"); + INSTANCE.register(357, "Ecircumflexsmall"); + INSTANCE.register(358, "Edieresissmall"); + INSTANCE.register(359, "Igravesmall"); + INSTANCE.register(360, "Iacutesmall"); + INSTANCE.register(361, "Icircumflexsmall"); + INSTANCE.register(362, "Idieresissmall"); + INSTANCE.register(363, "Ethsmall"); + INSTANCE.register(364, "Ntildesmall"); + INSTANCE.register(365, "Ogravesmall"); + INSTANCE.register(366, "Oacutesmall"); + INSTANCE.register(367, "Ocircumflexsmall"); + INSTANCE.register(368, "Otildesmall"); + INSTANCE.register(369, "Odieresissmall"); + INSTANCE.register(370, "OEsmall"); + INSTANCE.register(371, "Oslashsmall"); + INSTANCE.register(372, "Ugravesmall"); + INSTANCE.register(373, "Uacutesmall"); + INSTANCE.register(374, "Ucircumflexsmall"); + INSTANCE.register(375, "Udieresissmall"); + INSTANCE.register(376, "Yacutesmall"); + INSTANCE.register(377, "Thornsmall"); + INSTANCE.register(378, "Ydieresissmall"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java new file mode 100644 index 000000000..0119b45b5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.charset; + + +/** + * This is specialized CFFCharset. It's used if the CharsetId of a font is set to 2. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFExpertSubsetCharset extends CFFCharset +{ + + private CFFExpertSubsetCharset() + { + } + + /** + * Returns an instance of the CFFExpertSubsetCharset class. + * @return an instance of CFFExpertSubsetCharset + */ + public static CFFExpertSubsetCharset getInstance() + { + return CFFExpertSubsetCharset.INSTANCE; + } + + private static final CFFExpertSubsetCharset INSTANCE = new CFFExpertSubsetCharset(); + + static + { + INSTANCE.register(1, "space"); + INSTANCE.register(13, "comma"); + INSTANCE.register(14, "hyphen"); + INSTANCE.register(15, "period"); + INSTANCE.register(27, "colon"); + INSTANCE.register(28, "semicolon"); + INSTANCE.register(99, "fraction"); + INSTANCE.register(109, "fi"); + INSTANCE.register(110, "fl"); + INSTANCE.register(150, "onesuperior"); + INSTANCE.register(155, "onehalf"); + INSTANCE.register(158, "onequarter"); + INSTANCE.register(163, "threequarters"); + INSTANCE.register(164, "twosuperior"); + INSTANCE.register(169, "threesuperior"); + INSTANCE.register(231, "dollaroldstyle"); + INSTANCE.register(232, "dollarsuperior"); + INSTANCE.register(235, "parenleftsuperior"); + INSTANCE.register(236, "parenrightsuperior"); + INSTANCE.register(237, "twodotenleader"); + INSTANCE.register(238, "onedotenleader"); + INSTANCE.register(239, "zerooldstyle"); + INSTANCE.register(240, "oneoldstyle"); + INSTANCE.register(241, "twooldstyle"); + INSTANCE.register(242, "threeoldstyle"); + INSTANCE.register(243, "fouroldstyle"); + INSTANCE.register(244, "fiveoldstyle"); + INSTANCE.register(245, "sixoldstyle"); + INSTANCE.register(246, "sevenoldstyle"); + INSTANCE.register(247, "eightoldstyle"); + INSTANCE.register(248, "nineoldstyle"); + INSTANCE.register(249, "commasuperior"); + INSTANCE.register(250, "threequartersemdash"); + INSTANCE.register(251, "periodsuperior"); + INSTANCE.register(253, "asuperior"); + INSTANCE.register(254, "bsuperior"); + INSTANCE.register(255, "centsuperior"); + INSTANCE.register(256, "dsuperior"); + INSTANCE.register(257, "esuperior"); + INSTANCE.register(258, "isuperior"); + INSTANCE.register(259, "lsuperior"); + INSTANCE.register(260, "msuperior"); + INSTANCE.register(261, "nsuperior"); + INSTANCE.register(262, "osuperior"); + INSTANCE.register(263, "rsuperior"); + INSTANCE.register(264, "ssuperior"); + INSTANCE.register(265, "tsuperior"); + INSTANCE.register(266, "ff"); + INSTANCE.register(267, "ffi"); + INSTANCE.register(268, "ffl"); + INSTANCE.register(269, "parenleftinferior"); + INSTANCE.register(270, "parenrightinferior"); + INSTANCE.register(272, "hyphensuperior"); + INSTANCE.register(300, "colonmonetary"); + INSTANCE.register(301, "onefitted"); + INSTANCE.register(302, "rupiah"); + INSTANCE.register(305, "centoldstyle"); + INSTANCE.register(314, "figuredash"); + INSTANCE.register(315, "hypheninferior"); + INSTANCE.register(320, "oneeighth"); + INSTANCE.register(321, "threeeighths"); + INSTANCE.register(322, "fiveeighths"); + INSTANCE.register(323, "seveneighths"); + INSTANCE.register(324, "onethird"); + INSTANCE.register(325, "twothirds"); + INSTANCE.register(326, "zerosuperior"); + INSTANCE.register(327, "foursuperior"); + INSTANCE.register(328, "fivesuperior"); + INSTANCE.register(329, "sixsuperior"); + INSTANCE.register(330, "sevensuperior"); + INSTANCE.register(331, "eightsuperior"); + INSTANCE.register(332, "ninesuperior"); + INSTANCE.register(333, "zeroinferior"); + INSTANCE.register(334, "oneinferior"); + INSTANCE.register(335, "twoinferior"); + INSTANCE.register(336, "threeinferior"); + INSTANCE.register(337, "fourinferior"); + INSTANCE.register(338, "fiveinferior"); + INSTANCE.register(339, "sixinferior"); + INSTANCE.register(340, "seveninferior"); + INSTANCE.register(341, "eightinferior"); + INSTANCE.register(342, "nineinferior"); + INSTANCE.register(343, "centinferior"); + INSTANCE.register(344, "dollarinferior"); + INSTANCE.register(345, "periodinferior"); + INSTANCE.register(346, "commainferior"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFISOAdobeCharset.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFISOAdobeCharset.java new file mode 100644 index 000000000..8942ba96c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/CFFISOAdobeCharset.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.charset; + + +/** + * This is specialized CFFCharset. It's used if the CharsetId of a font is set to 0. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFISOAdobeCharset extends CFFCharset +{ + + private CFFISOAdobeCharset() + { + } + + /** + * Returns an instance of the CFFExpertSubsetCharset class. + * @return an instance of CFFExpertSubsetCharset + */ + public static CFFISOAdobeCharset getInstance() + { + return CFFISOAdobeCharset.INSTANCE; + } + + private static final CFFISOAdobeCharset INSTANCE = new CFFISOAdobeCharset(); + + static + { + INSTANCE.register(1, "space"); + INSTANCE.register(2, "exclam"); + INSTANCE.register(3, "quotedbl"); + INSTANCE.register(4, "numbersign"); + INSTANCE.register(5, "dollar"); + INSTANCE.register(6, "percent"); + INSTANCE.register(7, "ampersand"); + INSTANCE.register(8, "quoteright"); + INSTANCE.register(9, "parenleft"); + INSTANCE.register(10, "parenright"); + INSTANCE.register(11, "asterisk"); + INSTANCE.register(12, "plus"); + INSTANCE.register(13, "comma"); + INSTANCE.register(14, "hyphen"); + INSTANCE.register(15, "period"); + INSTANCE.register(16, "slash"); + INSTANCE.register(17, "zero"); + INSTANCE.register(18, "one"); + INSTANCE.register(19, "two"); + INSTANCE.register(20, "three"); + INSTANCE.register(21, "four"); + INSTANCE.register(22, "five"); + INSTANCE.register(23, "six"); + INSTANCE.register(24, "seven"); + INSTANCE.register(25, "eight"); + INSTANCE.register(26, "nine"); + INSTANCE.register(27, "colon"); + INSTANCE.register(28, "semicolon"); + INSTANCE.register(29, "less"); + INSTANCE.register(30, "equal"); + INSTANCE.register(31, "greater"); + INSTANCE.register(32, "question"); + INSTANCE.register(33, "at"); + INSTANCE.register(34, "A"); + INSTANCE.register(35, "B"); + INSTANCE.register(36, "C"); + INSTANCE.register(37, "D"); + INSTANCE.register(38, "E"); + INSTANCE.register(39, "F"); + INSTANCE.register(40, "G"); + INSTANCE.register(41, "H"); + INSTANCE.register(42, "I"); + INSTANCE.register(43, "J"); + INSTANCE.register(44, "K"); + INSTANCE.register(45, "L"); + INSTANCE.register(46, "M"); + INSTANCE.register(47, "N"); + INSTANCE.register(48, "O"); + INSTANCE.register(49, "P"); + INSTANCE.register(50, "Q"); + INSTANCE.register(51, "R"); + INSTANCE.register(52, "S"); + INSTANCE.register(53, "T"); + INSTANCE.register(54, "U"); + INSTANCE.register(55, "V"); + INSTANCE.register(56, "W"); + INSTANCE.register(57, "X"); + INSTANCE.register(58, "Y"); + INSTANCE.register(59, "Z"); + INSTANCE.register(60, "bracketleft"); + INSTANCE.register(61, "backslash"); + INSTANCE.register(62, "bracketright"); + INSTANCE.register(63, "asciicircum"); + INSTANCE.register(64, "underscore"); + INSTANCE.register(65, "quoteleft"); + INSTANCE.register(66, "a"); + INSTANCE.register(67, "b"); + INSTANCE.register(68, "c"); + INSTANCE.register(69, "d"); + INSTANCE.register(70, "e"); + INSTANCE.register(71, "f"); + INSTANCE.register(72, "g"); + INSTANCE.register(73, "h"); + INSTANCE.register(74, "i"); + INSTANCE.register(75, "j"); + INSTANCE.register(76, "k"); + INSTANCE.register(77, "l"); + INSTANCE.register(78, "m"); + INSTANCE.register(79, "n"); + INSTANCE.register(80, "o"); + INSTANCE.register(81, "p"); + INSTANCE.register(82, "q"); + INSTANCE.register(83, "r"); + INSTANCE.register(84, "s"); + INSTANCE.register(85, "t"); + INSTANCE.register(86, "u"); + INSTANCE.register(87, "v"); + INSTANCE.register(88, "w"); + INSTANCE.register(89, "x"); + INSTANCE.register(90, "y"); + INSTANCE.register(91, "z"); + INSTANCE.register(92, "braceleft"); + INSTANCE.register(93, "bar"); + INSTANCE.register(94, "braceright"); + INSTANCE.register(95, "asciitilde"); + INSTANCE.register(96, "exclamdown"); + INSTANCE.register(97, "cent"); + INSTANCE.register(98, "sterling"); + INSTANCE.register(99, "fraction"); + INSTANCE.register(100, "yen"); + INSTANCE.register(101, "florin"); + INSTANCE.register(102, "section"); + INSTANCE.register(103, "currency"); + INSTANCE.register(104, "quotesingle"); + INSTANCE.register(105, "quotedblleft"); + INSTANCE.register(106, "guillemotleft"); + INSTANCE.register(107, "guilsinglleft"); + INSTANCE.register(108, "guilsinglright"); + INSTANCE.register(109, "fi"); + INSTANCE.register(110, "fl"); + INSTANCE.register(111, "endash"); + INSTANCE.register(112, "dagger"); + INSTANCE.register(113, "daggerdbl"); + INSTANCE.register(114, "periodcentered"); + INSTANCE.register(115, "paragraph"); + INSTANCE.register(116, "bullet"); + INSTANCE.register(117, "quotesinglbase"); + INSTANCE.register(118, "quotedblbase"); + INSTANCE.register(119, "quotedblright"); + INSTANCE.register(120, "guillemotright"); + INSTANCE.register(121, "ellipsis"); + INSTANCE.register(122, "perthousand"); + INSTANCE.register(123, "questiondown"); + INSTANCE.register(124, "grave"); + INSTANCE.register(125, "acute"); + INSTANCE.register(126, "circumflex"); + INSTANCE.register(127, "tilde"); + INSTANCE.register(128, "macron"); + INSTANCE.register(129, "breve"); + INSTANCE.register(130, "dotaccent"); + INSTANCE.register(131, "dieresis"); + INSTANCE.register(132, "ring"); + INSTANCE.register(133, "cedilla"); + INSTANCE.register(134, "hungarumlaut"); + INSTANCE.register(135, "ogonek"); + INSTANCE.register(136, "caron"); + INSTANCE.register(137, "emdash"); + INSTANCE.register(138, "AE"); + INSTANCE.register(139, "ordfeminine"); + INSTANCE.register(140, "Lslash"); + INSTANCE.register(141, "Oslash"); + INSTANCE.register(142, "OE"); + INSTANCE.register(143, "ordmasculine"); + INSTANCE.register(144, "ae"); + INSTANCE.register(145, "dotlessi"); + INSTANCE.register(146, "lslash"); + INSTANCE.register(147, "oslash"); + INSTANCE.register(148, "oe"); + INSTANCE.register(149, "germandbls"); + INSTANCE.register(150, "onesuperior"); + INSTANCE.register(151, "logicalnot"); + INSTANCE.register(152, "mu"); + INSTANCE.register(153, "trademark"); + INSTANCE.register(154, "Eth"); + INSTANCE.register(155, "onehalf"); + INSTANCE.register(156, "plusminus"); + INSTANCE.register(157, "Thorn"); + INSTANCE.register(158, "onequarter"); + INSTANCE.register(159, "divide"); + INSTANCE.register(160, "brokenbar"); + INSTANCE.register(161, "degree"); + INSTANCE.register(162, "thorn"); + INSTANCE.register(163, "threequarters"); + INSTANCE.register(164, "twosuperior"); + INSTANCE.register(165, "registered"); + INSTANCE.register(166, "minus"); + INSTANCE.register(167, "eth"); + INSTANCE.register(168, "multiply"); + INSTANCE.register(169, "threesuperior"); + INSTANCE.register(170, "copyright"); + INSTANCE.register(171, "Aacute"); + INSTANCE.register(172, "Acircumflex"); + INSTANCE.register(173, "Adieresis"); + INSTANCE.register(174, "Agrave"); + INSTANCE.register(175, "Aring"); + INSTANCE.register(176, "Atilde"); + INSTANCE.register(177, "Ccedilla"); + INSTANCE.register(178, "Eacute"); + INSTANCE.register(179, "Ecircumflex"); + INSTANCE.register(180, "Edieresis"); + INSTANCE.register(181, "Egrave"); + INSTANCE.register(182, "Iacute"); + INSTANCE.register(183, "Icircumflex"); + INSTANCE.register(184, "Idieresis"); + INSTANCE.register(185, "Igrave"); + INSTANCE.register(186, "Ntilde"); + INSTANCE.register(187, "Oacute"); + INSTANCE.register(188, "Ocircumflex"); + INSTANCE.register(189, "Odieresis"); + INSTANCE.register(190, "Ograve"); + INSTANCE.register(191, "Otilde"); + INSTANCE.register(192, "Scaron"); + INSTANCE.register(193, "Uacute"); + INSTANCE.register(194, "Ucircumflex"); + INSTANCE.register(195, "Udieresis"); + INSTANCE.register(196, "Ugrave"); + INSTANCE.register(197, "Yacute"); + INSTANCE.register(198, "Ydieresis"); + INSTANCE.register(199, "Zcaron"); + INSTANCE.register(200, "aacute"); + INSTANCE.register(201, "acircumflex"); + INSTANCE.register(202, "adieresis"); + INSTANCE.register(203, "agrave"); + INSTANCE.register(204, "aring"); + INSTANCE.register(205, "atilde"); + INSTANCE.register(206, "ccedilla"); + INSTANCE.register(207, "eacute"); + INSTANCE.register(208, "ecircumflex"); + INSTANCE.register(209, "edieresis"); + INSTANCE.register(210, "egrave"); + INSTANCE.register(211, "iacute"); + INSTANCE.register(212, "icircumflex"); + INSTANCE.register(213, "idieresis"); + INSTANCE.register(214, "igrave"); + INSTANCE.register(215, "ntilde"); + INSTANCE.register(216, "oacute"); + INSTANCE.register(217, "ocircumflex"); + INSTANCE.register(218, "odieresis"); + INSTANCE.register(219, "ograve"); + INSTANCE.register(220, "otilde"); + INSTANCE.register(221, "scaron"); + INSTANCE.register(222, "uacute"); + INSTANCE.register(223, "ucircumflex"); + INSTANCE.register(224, "udieresis"); + INSTANCE.register(225, "ugrave"); + INSTANCE.register(226, "yacute"); + INSTANCE.register(227, "ydieresis"); + INSTANCE.register(228, "zcaron"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/package.html new file mode 100644 index 000000000..75a742cb8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/charset/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds classes used to handle the charsets of CFF/Type2-Fonts (aka Type1C-Fonts). + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFEncoding.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFEncoding.java new file mode 100644 index 000000000..77bc430b8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFEncoding.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.encoding; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is the superclass for all CFFFont encodings. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public abstract class CFFEncoding +{ + + private List entries = new ArrayList(); + + /** + * Determines if the encoding is font specific or not. + * @return if the encoding is font specific + */ + public boolean isFontSpecific() + { + return false; + } + + /** + * Returns the code corresponding to the given SID. + * @param sid the given SID + * @return the corresponding code + */ + public int getCode(int sid) + { + for(Entry entry : entries) + { + if(entry.entrySID == sid) + { + return entry.entryCode; + } + } + return -1; + } + + /** + * Returns the SID corresponding to the given code. + * @param code the given code + * @return the corresponding SID + */ + public int getSID(int code) + { + for(Entry entry : entries) + { + if(entry.entryCode == code) + { + return entry.entrySID; + } + } + return -1; + } + + /** + * Adds a new code/SID combination to the encoding. + * @param code the given code + * @param sid the given SID + */ + public void register(int code, int sid) + { + entries.add(new Entry(code, sid)); + } + + /** + * Add a single entry. + * @param entry the entry to be added + */ + public void addEntry(Entry entry) + { + entries.add(entry); + } + + /** + * A list of all entries within this encoding. + * @return a list of all entries + */ + public List getEntries() + { + return entries; + } + + /** + * This class represents a single code/SID mapping of the encoding. + * + */ + public static class Entry + { + private int entryCode; + private int entrySID; + + /** + * Create a new instance of Entry with the given values. + * @param code the code + * @param sid the SID + */ + protected Entry(int code, int sid) + { + this.entryCode = code; + this.entrySID = sid; + } + + /** + * The code of the entry. + * @return the code + */ + public int getCode() + { + return this.entryCode; + } + + /** + * The SID of the entry. + * @return the SID + */ + public int getSID() + { + return this.entrySID; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return "[code=" + entryCode + ", sid=" + entrySID + "]"; + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFExpertEncoding.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFExpertEncoding.java new file mode 100644 index 000000000..cfe4d9cd6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFExpertEncoding.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.encoding; + +/** + * This is specialized CFFEncoding. It's used if the EncodingId of a font is set to 1. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFExpertEncoding extends CFFEncoding +{ + + private CFFExpertEncoding() + { + } + + /** + * Returns an instance of the CFFExportEncoding class. + * @return an instance of CFFExportEncoding + */ + public static CFFExpertEncoding getInstance() + { + return CFFExpertEncoding.INSTANCE; + } + + private static final CFFExpertEncoding INSTANCE = new CFFExpertEncoding(); + + static + { + INSTANCE.register(0, 0); + INSTANCE.register(1, 0); + INSTANCE.register(2, 0); + INSTANCE.register(3, 0); + INSTANCE.register(4, 0); + INSTANCE.register(5, 0); + INSTANCE.register(6, 0); + INSTANCE.register(7, 0); + INSTANCE.register(8, 0); + INSTANCE.register(9, 0); + INSTANCE.register(10, 0); + INSTANCE.register(11, 0); + INSTANCE.register(12, 0); + INSTANCE.register(13, 0); + INSTANCE.register(14, 0); + INSTANCE.register(15, 0); + INSTANCE.register(16, 0); + INSTANCE.register(17, 0); + INSTANCE.register(18, 0); + INSTANCE.register(19, 0); + INSTANCE.register(20, 0); + INSTANCE.register(21, 0); + INSTANCE.register(22, 0); + INSTANCE.register(23, 0); + INSTANCE.register(24, 0); + INSTANCE.register(25, 0); + INSTANCE.register(26, 0); + INSTANCE.register(27, 0); + INSTANCE.register(28, 0); + INSTANCE.register(29, 0); + INSTANCE.register(30, 0); + INSTANCE.register(31, 0); + INSTANCE.register(32, 1); + INSTANCE.register(33, 229); + INSTANCE.register(34, 230); + INSTANCE.register(35, 0); + INSTANCE.register(36, 231); + INSTANCE.register(37, 232); + INSTANCE.register(38, 233); + INSTANCE.register(39, 234); + INSTANCE.register(40, 235); + INSTANCE.register(41, 236); + INSTANCE.register(42, 237); + INSTANCE.register(43, 238); + INSTANCE.register(44, 13); + INSTANCE.register(45, 14); + INSTANCE.register(46, 15); + INSTANCE.register(47, 99); + INSTANCE.register(48, 239); + INSTANCE.register(49, 240); + INSTANCE.register(50, 241); + INSTANCE.register(51, 242); + INSTANCE.register(52, 243); + INSTANCE.register(53, 244); + INSTANCE.register(54, 245); + INSTANCE.register(55, 246); + INSTANCE.register(56, 247); + INSTANCE.register(57, 248); + INSTANCE.register(58, 27); + INSTANCE.register(59, 28); + INSTANCE.register(60, 249); + INSTANCE.register(61, 250); + INSTANCE.register(62, 251); + INSTANCE.register(63, 252); + INSTANCE.register(64, 0); + INSTANCE.register(65, 253); + INSTANCE.register(66, 254); + INSTANCE.register(67, 255); + INSTANCE.register(68, 256); + INSTANCE.register(69, 257); + INSTANCE.register(70, 0); + INSTANCE.register(71, 0); + INSTANCE.register(72, 0); + INSTANCE.register(73, 258); + INSTANCE.register(74, 0); + INSTANCE.register(75, 0); + INSTANCE.register(76, 259); + INSTANCE.register(77, 260); + INSTANCE.register(78, 261); + INSTANCE.register(79, 262); + INSTANCE.register(80, 0); + INSTANCE.register(81, 0); + INSTANCE.register(82, 263); + INSTANCE.register(83, 264); + INSTANCE.register(84, 265); + INSTANCE.register(85, 0); + INSTANCE.register(86, 266); + INSTANCE.register(87, 109); + INSTANCE.register(88, 110); + INSTANCE.register(89, 267); + INSTANCE.register(90, 268); + INSTANCE.register(91, 269); + INSTANCE.register(92, 0); + INSTANCE.register(93, 270); + INSTANCE.register(94, 271); + INSTANCE.register(95, 272); + INSTANCE.register(96, 273); + INSTANCE.register(97, 274); + INSTANCE.register(98, 275); + INSTANCE.register(99, 276); + INSTANCE.register(100, 277); + INSTANCE.register(101, 278); + INSTANCE.register(102, 279); + INSTANCE.register(103, 280); + INSTANCE.register(104, 281); + INSTANCE.register(105, 282); + INSTANCE.register(106, 283); + INSTANCE.register(107, 284); + INSTANCE.register(108, 285); + INSTANCE.register(109, 286); + INSTANCE.register(110, 287); + INSTANCE.register(111, 288); + INSTANCE.register(112, 289); + INSTANCE.register(113, 290); + INSTANCE.register(114, 291); + INSTANCE.register(115, 292); + INSTANCE.register(116, 293); + INSTANCE.register(117, 294); + INSTANCE.register(118, 295); + INSTANCE.register(119, 296); + INSTANCE.register(120, 297); + INSTANCE.register(121, 298); + INSTANCE.register(122, 299); + INSTANCE.register(123, 300); + INSTANCE.register(124, 301); + INSTANCE.register(125, 302); + INSTANCE.register(126, 303); + INSTANCE.register(127, 0); + INSTANCE.register(128, 0); + INSTANCE.register(129, 0); + INSTANCE.register(130, 0); + INSTANCE.register(131, 0); + INSTANCE.register(132, 0); + INSTANCE.register(133, 0); + INSTANCE.register(134, 0); + INSTANCE.register(135, 0); + INSTANCE.register(136, 0); + INSTANCE.register(137, 0); + INSTANCE.register(138, 0); + INSTANCE.register(139, 0); + INSTANCE.register(140, 0); + INSTANCE.register(141, 0); + INSTANCE.register(142, 0); + INSTANCE.register(143, 0); + INSTANCE.register(144, 0); + INSTANCE.register(145, 0); + INSTANCE.register(146, 0); + INSTANCE.register(147, 0); + INSTANCE.register(148, 0); + INSTANCE.register(149, 0); + INSTANCE.register(150, 0); + INSTANCE.register(151, 0); + INSTANCE.register(152, 0); + INSTANCE.register(153, 0); + INSTANCE.register(154, 0); + INSTANCE.register(155, 0); + INSTANCE.register(156, 0); + INSTANCE.register(157, 0); + INSTANCE.register(158, 0); + INSTANCE.register(159, 0); + INSTANCE.register(160, 0); + INSTANCE.register(161, 304); + INSTANCE.register(162, 305); + INSTANCE.register(163, 306); + INSTANCE.register(164, 0); + INSTANCE.register(165, 0); + INSTANCE.register(166, 307); + INSTANCE.register(167, 308); + INSTANCE.register(168, 309); + INSTANCE.register(169, 310); + INSTANCE.register(170, 311); + INSTANCE.register(171, 0); + INSTANCE.register(172, 312); + INSTANCE.register(173, 0); + INSTANCE.register(174, 0); + INSTANCE.register(175, 313); + INSTANCE.register(176, 0); + INSTANCE.register(177, 0); + INSTANCE.register(178, 314); + INSTANCE.register(179, 315); + INSTANCE.register(180, 0); + INSTANCE.register(181, 0); + INSTANCE.register(182, 316); + INSTANCE.register(183, 317); + INSTANCE.register(184, 318); + INSTANCE.register(185, 0); + INSTANCE.register(186, 0); + INSTANCE.register(187, 0); + INSTANCE.register(188, 158); + INSTANCE.register(189, 155); + INSTANCE.register(190, 163); + INSTANCE.register(191, 319); + INSTANCE.register(192, 320); + INSTANCE.register(193, 321); + INSTANCE.register(194, 322); + INSTANCE.register(195, 323); + INSTANCE.register(196, 324); + INSTANCE.register(197, 325); + INSTANCE.register(198, 0); + INSTANCE.register(199, 0); + INSTANCE.register(200, 326); + INSTANCE.register(201, 150); + INSTANCE.register(202, 164); + INSTANCE.register(203, 169); + INSTANCE.register(204, 327); + INSTANCE.register(205, 328); + INSTANCE.register(206, 329); + INSTANCE.register(207, 330); + INSTANCE.register(208, 331); + INSTANCE.register(209, 332); + INSTANCE.register(210, 333); + INSTANCE.register(211, 334); + INSTANCE.register(212, 335); + INSTANCE.register(213, 336); + INSTANCE.register(214, 337); + INSTANCE.register(215, 338); + INSTANCE.register(216, 339); + INSTANCE.register(217, 340); + INSTANCE.register(218, 341); + INSTANCE.register(219, 342); + INSTANCE.register(220, 343); + INSTANCE.register(221, 344); + INSTANCE.register(222, 345); + INSTANCE.register(223, 346); + INSTANCE.register(224, 347); + INSTANCE.register(225, 348); + INSTANCE.register(226, 349); + INSTANCE.register(227, 350); + INSTANCE.register(228, 351); + INSTANCE.register(229, 352); + INSTANCE.register(230, 353); + INSTANCE.register(231, 354); + INSTANCE.register(232, 355); + INSTANCE.register(233, 356); + INSTANCE.register(234, 357); + INSTANCE.register(235, 358); + INSTANCE.register(236, 359); + INSTANCE.register(237, 360); + INSTANCE.register(238, 361); + INSTANCE.register(239, 362); + INSTANCE.register(240, 363); + INSTANCE.register(241, 364); + INSTANCE.register(242, 365); + INSTANCE.register(243, 366); + INSTANCE.register(244, 367); + INSTANCE.register(245, 368); + INSTANCE.register(246, 369); + INSTANCE.register(247, 370); + INSTANCE.register(248, 371); + INSTANCE.register(249, 372); + INSTANCE.register(250, 373); + INSTANCE.register(251, 374); + INSTANCE.register(252, 375); + INSTANCE.register(253, 376); + INSTANCE.register(254, 377); + INSTANCE.register(255, 378); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFStandardEncoding.java b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFStandardEncoding.java new file mode 100644 index 000000000..4503367f7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/CFFStandardEncoding.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff.encoding; + +/** + * This is specialized CFFEncoding. It's used if the EncodingId of a font is set to 0. + * + * @author Villu Ruusmann + * @version $Revision$ + */ +public class CFFStandardEncoding extends CFFEncoding +{ + + private CFFStandardEncoding() + { + } + + /** + * Returns an instance of the CFFStandardEncoding class. + * @return an instance of CFFStandardEncoding + */ + public static CFFStandardEncoding getInstance() + { + return CFFStandardEncoding.INSTANCE; + } + + private static final CFFStandardEncoding INSTANCE = new CFFStandardEncoding(); + + static + { + INSTANCE.register(0, 0); + INSTANCE.register(1, 0); + INSTANCE.register(2, 0); + INSTANCE.register(3, 0); + INSTANCE.register(4, 0); + INSTANCE.register(5, 0); + INSTANCE.register(6, 0); + INSTANCE.register(7, 0); + INSTANCE.register(8, 0); + INSTANCE.register(9, 0); + INSTANCE.register(10, 0); + INSTANCE.register(11, 0); + INSTANCE.register(12, 0); + INSTANCE.register(13, 0); + INSTANCE.register(14, 0); + INSTANCE.register(15, 0); + INSTANCE.register(16, 0); + INSTANCE.register(17, 0); + INSTANCE.register(18, 0); + INSTANCE.register(19, 0); + INSTANCE.register(20, 0); + INSTANCE.register(21, 0); + INSTANCE.register(22, 0); + INSTANCE.register(23, 0); + INSTANCE.register(24, 0); + INSTANCE.register(25, 0); + INSTANCE.register(26, 0); + INSTANCE.register(27, 0); + INSTANCE.register(28, 0); + INSTANCE.register(29, 0); + INSTANCE.register(30, 0); + INSTANCE.register(31, 0); + INSTANCE.register(32, 1); + INSTANCE.register(33, 2); + INSTANCE.register(34, 3); + INSTANCE.register(35, 4); + INSTANCE.register(36, 5); + INSTANCE.register(37, 6); + INSTANCE.register(38, 7); + INSTANCE.register(39, 8); + INSTANCE.register(40, 9); + INSTANCE.register(41, 10); + INSTANCE.register(42, 11); + INSTANCE.register(43, 12); + INSTANCE.register(44, 13); + INSTANCE.register(45, 14); + INSTANCE.register(46, 15); + INSTANCE.register(47, 16); + INSTANCE.register(48, 17); + INSTANCE.register(49, 18); + INSTANCE.register(50, 19); + INSTANCE.register(51, 20); + INSTANCE.register(52, 21); + INSTANCE.register(53, 22); + INSTANCE.register(54, 23); + INSTANCE.register(55, 24); + INSTANCE.register(56, 25); + INSTANCE.register(57, 26); + INSTANCE.register(58, 27); + INSTANCE.register(59, 28); + INSTANCE.register(60, 29); + INSTANCE.register(61, 30); + INSTANCE.register(62, 31); + INSTANCE.register(63, 32); + INSTANCE.register(64, 33); + INSTANCE.register(65, 34); + INSTANCE.register(66, 35); + INSTANCE.register(67, 36); + INSTANCE.register(68, 37); + INSTANCE.register(69, 38); + INSTANCE.register(70, 39); + INSTANCE.register(71, 40); + INSTANCE.register(72, 41); + INSTANCE.register(73, 42); + INSTANCE.register(74, 43); + INSTANCE.register(75, 44); + INSTANCE.register(76, 45); + INSTANCE.register(77, 46); + INSTANCE.register(78, 47); + INSTANCE.register(79, 48); + INSTANCE.register(80, 49); + INSTANCE.register(81, 50); + INSTANCE.register(82, 51); + INSTANCE.register(83, 52); + INSTANCE.register(84, 53); + INSTANCE.register(85, 54); + INSTANCE.register(86, 55); + INSTANCE.register(87, 56); + INSTANCE.register(88, 57); + INSTANCE.register(89, 58); + INSTANCE.register(90, 59); + INSTANCE.register(91, 60); + INSTANCE.register(92, 61); + INSTANCE.register(93, 62); + INSTANCE.register(94, 63); + INSTANCE.register(95, 64); + INSTANCE.register(96, 65); + INSTANCE.register(97, 66); + INSTANCE.register(98, 67); + INSTANCE.register(99, 68); + INSTANCE.register(100, 69); + INSTANCE.register(101, 70); + INSTANCE.register(102, 71); + INSTANCE.register(103, 72); + INSTANCE.register(104, 73); + INSTANCE.register(105, 74); + INSTANCE.register(106, 75); + INSTANCE.register(107, 76); + INSTANCE.register(108, 77); + INSTANCE.register(109, 78); + INSTANCE.register(110, 79); + INSTANCE.register(111, 80); + INSTANCE.register(112, 81); + INSTANCE.register(113, 82); + INSTANCE.register(114, 83); + INSTANCE.register(115, 84); + INSTANCE.register(116, 85); + INSTANCE.register(117, 86); + INSTANCE.register(118, 87); + INSTANCE.register(119, 88); + INSTANCE.register(120, 89); + INSTANCE.register(121, 90); + INSTANCE.register(122, 91); + INSTANCE.register(123, 92); + INSTANCE.register(124, 93); + INSTANCE.register(125, 94); + INSTANCE.register(126, 95); + INSTANCE.register(127, 0); + INSTANCE.register(128, 0); + INSTANCE.register(129, 0); + INSTANCE.register(130, 0); + INSTANCE.register(131, 0); + INSTANCE.register(132, 0); + INSTANCE.register(133, 0); + INSTANCE.register(134, 0); + INSTANCE.register(135, 0); + INSTANCE.register(136, 0); + INSTANCE.register(137, 0); + INSTANCE.register(138, 0); + INSTANCE.register(139, 0); + INSTANCE.register(140, 0); + INSTANCE.register(141, 0); + INSTANCE.register(142, 0); + INSTANCE.register(143, 0); + INSTANCE.register(144, 0); + INSTANCE.register(145, 0); + INSTANCE.register(146, 0); + INSTANCE.register(147, 0); + INSTANCE.register(148, 0); + INSTANCE.register(149, 0); + INSTANCE.register(150, 0); + INSTANCE.register(151, 0); + INSTANCE.register(152, 0); + INSTANCE.register(153, 0); + INSTANCE.register(154, 0); + INSTANCE.register(155, 0); + INSTANCE.register(156, 0); + INSTANCE.register(157, 0); + INSTANCE.register(158, 0); + INSTANCE.register(159, 0); + INSTANCE.register(160, 0); + INSTANCE.register(161, 96); + INSTANCE.register(162, 97); + INSTANCE.register(163, 98); + INSTANCE.register(164, 99); + INSTANCE.register(165, 100); + INSTANCE.register(166, 101); + INSTANCE.register(167, 102); + INSTANCE.register(168, 103); + INSTANCE.register(169, 104); + INSTANCE.register(170, 105); + INSTANCE.register(171, 106); + INSTANCE.register(172, 107); + INSTANCE.register(173, 108); + INSTANCE.register(174, 109); + INSTANCE.register(175, 110); + INSTANCE.register(176, 0); + INSTANCE.register(177, 111); + INSTANCE.register(178, 112); + INSTANCE.register(179, 113); + INSTANCE.register(180, 114); + INSTANCE.register(181, 0); + INSTANCE.register(182, 115); + INSTANCE.register(183, 116); + INSTANCE.register(184, 117); + INSTANCE.register(185, 118); + INSTANCE.register(186, 119); + INSTANCE.register(187, 120); + INSTANCE.register(188, 121); + INSTANCE.register(189, 122); + INSTANCE.register(190, 0); + INSTANCE.register(191, 123); + INSTANCE.register(192, 0); + INSTANCE.register(193, 124); + INSTANCE.register(194, 125); + INSTANCE.register(195, 126); + INSTANCE.register(196, 127); + INSTANCE.register(197, 128); + INSTANCE.register(198, 129); + INSTANCE.register(199, 130); + INSTANCE.register(200, 131); + INSTANCE.register(201, 0); + INSTANCE.register(202, 132); + INSTANCE.register(203, 133); + INSTANCE.register(204, 0); + INSTANCE.register(205, 134); + INSTANCE.register(206, 135); + INSTANCE.register(207, 136); + INSTANCE.register(208, 137); + INSTANCE.register(209, 0); + INSTANCE.register(210, 0); + INSTANCE.register(211, 0); + INSTANCE.register(212, 0); + INSTANCE.register(213, 0); + INSTANCE.register(214, 0); + INSTANCE.register(215, 0); + INSTANCE.register(216, 0); + INSTANCE.register(217, 0); + INSTANCE.register(218, 0); + INSTANCE.register(219, 0); + INSTANCE.register(220, 0); + INSTANCE.register(221, 0); + INSTANCE.register(222, 0); + INSTANCE.register(223, 0); + INSTANCE.register(224, 0); + INSTANCE.register(225, 138); + INSTANCE.register(226, 0); + INSTANCE.register(227, 139); + INSTANCE.register(228, 0); + INSTANCE.register(229, 0); + INSTANCE.register(230, 0); + INSTANCE.register(231, 0); + INSTANCE.register(232, 140); + INSTANCE.register(233, 141); + INSTANCE.register(234, 142); + INSTANCE.register(235, 143); + INSTANCE.register(236, 0); + INSTANCE.register(237, 0); + INSTANCE.register(238, 0); + INSTANCE.register(239, 0); + INSTANCE.register(240, 0); + INSTANCE.register(241, 144); + INSTANCE.register(242, 0); + INSTANCE.register(243, 0); + INSTANCE.register(244, 0); + INSTANCE.register(245, 145); + INSTANCE.register(246, 0); + INSTANCE.register(247, 0); + INSTANCE.register(248, 146); + INSTANCE.register(249, 147); + INSTANCE.register(250, 148); + INSTANCE.register(251, 149); + INSTANCE.register(252, 0); + INSTANCE.register(253, 0); + INSTANCE.register(254, 0); + INSTANCE.register(255, 0); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/package.html new file mode 100644 index 000000000..be708c265 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/encoding/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds classes used to handle the encoding of CFF/Type2-Fonts (aka Type1C-Fonts). + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cff/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/cff/package.html new file mode 100644 index 000000000..107badad6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cff/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds classes used to parse CFF/Type2-Fonts (aka Type1C-Fonts). + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CIDRange.java b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CIDRange.java new file mode 100644 index 000000000..637c44185 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CIDRange.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cmap; + + +/** + * Range of continuous CIDs between two Unicode characters. + */ +class CIDRange { + + private char from; + + private char to; + + private int cid; + + public CIDRange(char from, char to, int cid) { + this.from = from; + this.to = to; + this.cid = cid; + } + + /** + * Maps the given Unicode character to the corresponding CID in this range. + * + * @param ch Unicode character + * @return corresponding CID, or -1 if the character is out of range + */ + public int map(char ch) { + if (from <= ch && ch <= to) { + return cid + (ch - from); + } else { + return -1; + } + } + + /** + * Maps the given CID to the corresponding Unicode character in this range. + * + * @param code CID + * @return corresponding Unicode character, or -1 if the CID is out of range + */ + public int unmap(int code) { + if (cid <= code && code <= cid + (to - from)) { + return from + (code - cid); + } else { + return -1; + } + } + +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMap.java b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMap.java new file mode 100644 index 000000000..b93f70072 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMap.java @@ -0,0 +1,453 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cmap; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Iterator; + +/** + * This class represents a CMap file. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.3 $ + */ +public class CMap +{ + + private int wmode = 0; + private String cmapName = null; + private String cmapVersion = null; + private int cmapType = -1; + + private String registry = null; + private String ordering = null; + private int supplement = 0; + + private List codeSpaceRanges = new ArrayList(); + private Map singleByteMappings = new HashMap(); + private Map doubleByteMappings = new HashMap(); + + private final Map cid2charMappings = new HashMap(); + private final Map char2CIDMappings = new HashMap(); + private final List cidRanges = new LinkedList(); + + /** + * 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 tell if this cmap has any CID mappings. + * + * @return true If there are any CID mappings, false otherwise. + */ + public boolean hasCIDMappings() + { + return !char2CIDMappings.isEmpty() || !cidRanges.isEmpty(); + } + + /** + * 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 ) + { + return lookup(getCodeFromArray(code, offset, length), length); + } + + /** + * This will perform a lookup into the map. + * + * @param code The code used to lookup. + * @param length The length of the data we are getting. + * + * @return The string that matches the lookup. + */ + public String lookup( int code, int length ) + { + String result = null; + if( length == 1 ) + { + result = singleByteMappings.get( code ); + } + else if( length == 2 ) + { + result = doubleByteMappings.get( code ); + } + return result; + } + + /** + * This will perform a lookup into the CID map. + * + * @param cid The CID used to lookup. + * + * @return The string that matches the lookup. + */ + public String lookupCID(int cid) { + if (cid2charMappings.containsKey(cid)) { + return cid2charMappings.get(cid); + } else { + for (CIDRange range : cidRanges) { + int ch = range.unmap(cid); + if (ch != -1) { + return Character.toString((char) ch); + } + } + return null; + } + } + + /** + * This will perform a lookup into the CID map. + * + * @param code The code used to lookup. + * + * @return The CID that matches the lookup. + */ + public int lookupCID(byte[] code, int offset, int length) { + if (isInCodeSpaceRanges(code,offset,length)) { + int codeAsInt = getCodeFromArray(code, offset, length); + if (char2CIDMappings.containsKey(codeAsInt)) { + return char2CIDMappings.get(codeAsInt); + } else { + for (CIDRange range : cidRanges) { + int ch = range.map((char)codeAsInt); + if (ch != -1) { + return ch; + } + } + return -1; + } + } + return -1; + } + + /** + * Convert the given part of a byte array to an integer. + * @param data the byte array + * @param offset The offset into the byte array. + * @param length The length of the data we are getting. + * @return the resulting integer + */ + private int getCodeFromArray( byte[] data, int offset, int length ) + { + int code = 0; + for( int i=0; i getCodeSpaceRanges() + { + return codeSpaceRanges; + } + + /** + * Implementation of the usecmap operator. This will + * copy all of the mappings from one cmap to another. + * + * @param cmap The cmap to load mappings from. + */ + public void useCmap( CMap cmap ) + { + this.codeSpaceRanges.addAll( cmap.codeSpaceRanges ); + this.singleByteMappings.putAll( cmap.singleByteMappings ); + this.doubleByteMappings.putAll( cmap.doubleByteMappings ); + } + + /** + * Check whether the given byte array is in codespace ranges or not. + * + * @param code The byte array to look for in the codespace range. + * + * @return true if the given byte array is in the codespace range. + */ + public boolean isInCodeSpaceRanges( byte[] code ) + { + return isInCodeSpaceRanges(code, 0, code.length); + } + + /** + * Check whether the given byte array is in codespace ranges or not. + * + * @param code The byte array to look for in the codespace range. + * @param offset The starting offset within the byte array. + * @param length The length of the part of the array. + * + * @return true if the given byte array is in the codespace range. + */ + public boolean isInCodeSpaceRanges( byte[] code, int offset, int length ) + { + Iterator it = codeSpaceRanges.iterator(); + while ( it.hasNext() ) + { + CodespaceRange range = it.next(); + if ( range != null && range.isInRange(code, offset, length) ) + { + return true; + } + } + return false; + } + + /** + * Returns the WMode of a CMap. + * + * 0 represents a horizontal and 1 represents a vertical orientation. + * + * @return + */ + public int getWMode() + { + return wmode; + } + + /** + * Sets the WMode of a CMap. + * + * @param newWMode the new WMode. + */ + public void setWMode(int newWMode) + { + wmode = newWMode; + } + + /** + * Returns the name of the CMap. + * + * @return the CMap name. + */ + public String getName() + { + return cmapName; + } + + /** + * Sets the name of the CMap. + * + * @param name the CMap name. + */ + public void setName(String name) + { + cmapName = name; + } + + /** + * Returns the version of the CMap. + * + * @return the CMap version. + */ + public String getVersion() + { + return cmapVersion; + } + + /** + * Sets the version of the CMap. + * + * @param version the CMap version. + */ + public void setVersion(String version) + { + cmapVersion = version; + } + + /** + * Returns the type of the CMap. + * + * @return the CMap type. + */ + public int getType() + { + return cmapType; + } + + /** + * Sets the type of the CMap. + * + * @param type the CMap type. + */ + public void setType(int type) + { + cmapType = type; + } + + /** + * Returns the registry of the CIDSystemInfo. + * + * @return the registry. + */ + public String getRegistry() + { + return registry; + } + + /** + * Sets the registry of the CIDSystemInfo. + * + * @param newRegistry the registry. + */ + public void setRegistry(String newRegistry) + { + registry = newRegistry; + } + + /** + * Returns the ordering of the CIDSystemInfo. + * + * @return the ordering. + */ + public String getOrdering() + { + return ordering; + } + + /** + * Sets the ordering of the CIDSystemInfo. + * + * @param newOrdering the ordering. + */ + public void setOrdering(String newOrdering) + { + ordering = newOrdering; + } + + /** + * Returns the supplement of the CIDSystemInfo. + * + * @return the supplement. + */ + public int getSupplement() + { + return supplement; + } + + /** + * Sets the supplement of the CIDSystemInfo. + * + * @param newSupplement the supplement. + */ + public void setSupplement(int newSupplement) + { + supplement = newSupplement; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMapParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMapParser.java new file mode 100644 index 000000000..13c4d20c9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CMapParser.java @@ -0,0 +1,645 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cmap; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.PushbackInputStream; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.fontbox.util.ResourceLoader; + +/** + * This will parser a CMap stream. + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +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 static final String BEGIN_CID_CHAR = "begincidchar"; + private static final String BEGIN_CID_RANGE = "begincidrange"; + private static final String USECMAP = "usecmap"; + + private static final String WMODE = "WMode"; + private static final String CMAP_NAME = "CMapName"; + private static final String CMAP_VERSION = "CMapVersion"; + private static final String CMAP_TYPE = "CMapType"; + private static final String REGISTRY = "Registry"; + private static final String ORDERING = "Ordering"; + private static final String SUPPLEMENT = "Supplement"; + + private static final String MARK_END_OF_DICTIONARY = ">>"; + private static final String MARK_END_OF_ARRAY = "]"; + + + private byte[] tokenParserByteBuffer = new byte[512]; + + /** + * Creates a new instance of CMapParser. + */ + public CMapParser() + { + } + + /** + * Parse a CMAP file on the file system. + * + * @param file The file to parse. + * + * @return A parsed CMAP file. + * + * @throws IOException If there is an issue while parsing the CMAP. + */ + public CMap parse( File file ) throws IOException + { + String rootDir = file.getParent() + File.separator; + FileInputStream input = null; + try + { + input = new FileInputStream( file ); + return parse( rootDir, input ); + } + finally + { + if( input != null ) + { + input.close(); + } + } + + } + + /** + * This will parse the stream and create a cmap object. + * + * @param resourceRoot The root path to the cmap file. This will be used + * to find referenced cmap files. It can be null. + * @param input The CMAP stream to parse. + * + * @return The parsed stream as a java object. + * + * @throws IOException If there is an error parsing the stream. + */ + public CMap parse( String resourceRoot, InputStream input ) throws IOException + { + PushbackInputStream cmapStream = new PushbackInputStream( input ); + CMap result = new CMap(); + Object previousToken = null; + Object token = null; + while( (token = parseNextToken( cmapStream )) != null ) + { + if( token instanceof Operator ) + { + Operator op = (Operator)token; + if( op.op.equals( USECMAP ) ) + { + LiteralName useCmapName = (LiteralName)previousToken; + InputStream useStream = ResourceLoader.loadResource( resourceRoot + useCmapName.name ); + if( useStream == null ) + { + throw new IOException( "Error: Could not find referenced cmap stream " + useCmapName.name ); + } + CMap useCMap = parse( resourceRoot, useStream ); + result.useCmap( useCMap ); + } + else if( op.op.equals( BEGIN_CODESPACE_RANGE ) ) + { + Number cosCount = (Number)previousToken; + for( int j=0; j array = null; + byte[] tokenBytes = null; + if( nextToken instanceof List ) + { + array = (List)nextToken; + tokenBytes = array.get( 0 ); + } + else + { + tokenBytes = (byte[])nextToken; + } + + String value = null; + + int arrayIndex = 0; + boolean done = false; + while( !done ) + { + if( compare( startCode, endCode ) >= 0 ) + { + done = true; + } + value = createStringFromBytes( tokenBytes ); + result.addMapping( startCode, value ); + increment( startCode ); + + if( array == null ) + { + increment( tokenBytes ); + } + else + { + arrayIndex++; + if( arrayIndex < array.size() ) + { + tokenBytes = (byte[])array.get( arrayIndex ); + } + } + } + } + } + else if( op.op.equals( BEGIN_CID_CHAR ) ) + { + Number cosCount = (Number)previousToken; + for( int j=0; j': + { + int secondCloseBrace = is.read(); + if( secondCloseBrace == '>' ) + { + retval = MARK_END_OF_DICTIONARY; + } + else + { + throw new IOException( "Error: expected the end of a dictionary."); + } + break; + } + case ']': + { + retval = MARK_END_OF_ARRAY; + break; + } + case '[': + { + List list = new ArrayList(); + + Object nextToken = parseNextToken( is ); + while( nextToken != null && nextToken != MARK_END_OF_ARRAY ) + { + list.add( nextToken ); + nextToken = parseNextToken( is ); + } + retval = list; + break; + } + case '<': + { + int theNextByte = is.read(); + if( theNextByte == '<' ) + { + Map result = new HashMap(); + //we are reading a dictionary + Object key = parseNextToken( is ); + while( key instanceof LiteralName && key != MARK_END_OF_DICTIONARY ) + { + Object value = parseNextToken( is ); + result.put( ((LiteralName)key).name, value ); + key = parseNextToken( is ); + } + retval = result; + } + else + { + //won't read more than 512 bytes + + int multiplyer = 16; + int bufferIndex = -1; + while( theNextByte != -1 && theNextByte != '>' ) + { + int intValue = 0; + if( theNextByte >= '0' && theNextByte <= '9' ) + { + intValue = theNextByte - '0'; + } + else if( theNextByte >= 'A' && theNextByte <= 'F' ) + { + intValue = 10 + theNextByte - 'A'; + } + else if( theNextByte >= 'a' && theNextByte <= 'f' ) + { + intValue = 10 + theNextByte - 'a'; + } + else if( theNextByte == 0x20 ) + { + // skipping whitespaces + theNextByte = is.read(); + continue; + } + else + { + throw new IOException( "Error: expected hex character and not " + + (char)theNextByte + ":" + theNextByte ); + } + intValue *= multiplyer; + if( multiplyer == 16 ) + { + bufferIndex++; + tokenParserByteBuffer[bufferIndex] = 0; + multiplyer = 1; + } + else + { + multiplyer = 16; + } + tokenParserByteBuffer[bufferIndex]+= intValue; + theNextByte = is.read(); + } + byte[] finalResult = new byte[bufferIndex+1]; + System.arraycopy(tokenParserByteBuffer,0,finalResult, 0, bufferIndex+1); + retval = finalResult; + } + break; + } + case '/': + { + StringBuffer buffer = new StringBuffer(); + int stringByte = is.read(); + + while( !isWhitespaceOrEOF( stringByte ) ) + { + buffer.append( (char)stringByte ); + stringByte = is.read(); + } + retval = new LiteralName( buffer.toString() ); + break; + } + case -1: + { + //EOF return null; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + StringBuffer buffer = new StringBuffer(); + buffer.append( (char)nextByte ); + nextByte = is.read(); + + while( !isWhitespaceOrEOF( nextByte ) && + (Character.isDigit( (char)nextByte )|| + nextByte == '.' ) ) + { + buffer.append( (char)nextByte ); + nextByte = is.read(); + } + is.unread( nextByte ); + String value = buffer.toString(); + if( value.indexOf( '.' ) >=0 ) + { + retval = new Double( value ); + } + else + { + retval = new Integer( buffer.toString() ); + } + break; + } + default: + { + StringBuffer buffer = new StringBuffer(); + buffer.append( (char)nextByte ); + nextByte = is.read(); + + while( !isWhitespaceOrEOF( nextByte ) ) + { + buffer.append( (char)nextByte ); + nextByte = is.read(); + } + retval = new Operator( buffer.toString() ); + + break; + } + } + return retval; + } + + private void readUntilEndOfLine( InputStream is, StringBuffer buf ) throws IOException + { + int nextByte = is.read(); + while( nextByte != -1 && nextByte != 0x0D && nextByte != 0x0A ) + { + buf.append( (char)nextByte ); + nextByte = is.read(); + } + } + + private boolean isWhitespaceOrEOF( int aByte ) + { + return aByte == -1 || aByte == 0x20 || aByte == 0x0D || aByte == 0x0A; + } + + + 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 int createIntFromBytes(byte[] bytes) + { + int intValue = (bytes[0]+256)%256; + if (bytes.length == 2) + { + intValue <<= 8; + intValue += (bytes[1]+256)%256; + } + return intValue; + } + + private String createStringFromBytes( byte[] bytes ) throws IOException + { + String retval = null; + if( bytes.length == 1 ) + { + retval = new String( bytes, "ISO-8859-1" ); + } + else + { + retval = new String( bytes, "UTF-16BE" ); + } + return retval; + } + + private int compare( byte[] first, byte[] second ) + { + int retval = 1; + int firstLength = first.length; + for( int i=0; i" ); + System.exit( -1 ); + } + CMapParser parser = new CMapParser( ); + File cmapFile = new File( args[0] ); + CMap result = parser.parse( cmapFile ); + System.out.println( "Result:" + result ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CodespaceRange.java b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CodespaceRange.java new file mode 100644 index 000000000..3e50e2c20 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/CodespaceRange.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cmap; + +/** + * This represents a single entry in the codespace range. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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; + } + + /** + * Check whether the given byte array is in this codespace range or ot. + * @param code The byte array to look for in the codespace range. + * @param offset The starting offset within the byte array. + * @param length The length of the part of the array. + * + * @return true if the given byte array is in the codespace range. + */ + public boolean isInRange(byte[] code, int offset, int length) + { + if ( length < start.length || length > end.length ) + { + return false; + } + + if ( end.length == length ) + { + for ( int i = 0; i < end.length; i++ ) + { + int endInt = ((int)end[i]) & 0xFF; + int codeInt = ((int)code[offset + i]) & 0xFF; + if ( endInt < codeInt ) + { + return false; + } + } + } + if ( start.length == length ) + { + for ( int i = 0; i < end.length; i++ ) + { + int startInt = ((int)start[i]) & 0xFF; + int codeInt = ((int)code[offset + i]) & 0xFF; + if ( startInt > codeInt ) + { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/cmap/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/package.html new file mode 100644 index 000000000..ca1ecfd18 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/cmap/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds classes that are necessary to parse cmap files. + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/encoding/Encoding.java b/fluidbook/tools/fwstk/src/apache/fontbox/encoding/Encoding.java new file mode 100644 index 000000000..65dacb491 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/encoding/Encoding.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.encoding; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; + +/** + * This is an interface to a text encoder. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public abstract class Encoding +{ + + + /** + * 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(); + + /** + * 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, String name ) + { + codeToName.put( code, name ); + nameToCode.put( name, code ); + } + + /** + * 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( String name ) throws IOException + { + Integer code = nameToCode.get( name ); + if( code == null ) + { + throw new IOException( "No character code for character name '" + name + "'" ); + } + 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 String getName( int code ) throws IOException + { + String name = codeToName.get( code ); + if( name == null ) + { + //lets be forgiving for now + name = "space"; + //throw new IOException( getClass().getName() + + // ": No name for character code '" + code + "'" ); + } + 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 String getNameFromCharacter( char c ) throws IOException + { + String name = 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 + { + return getCharacter( getName( code ) ); + } + + /** + * 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( String name ) + { + String character = NAME_TO_CHARACTER.get( name ); + if( character == null ) + { + character = name; + } + return character; + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/encoding/MacRomanEncoding.java b/fluidbook/tools/fwstk/src/apache/fontbox/encoding/MacRomanEncoding.java new file mode 100644 index 000000000..0e856f0cb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/encoding/MacRomanEncoding.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.encoding; + +/** + * This is an interface to a text encoder. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class MacRomanEncoding extends Encoding +{ + /** + * Constructor. + */ + public MacRomanEncoding() + { + addCharacterEncoding( 0101, "A" ); + addCharacterEncoding( 0256, "AE" ); + addCharacterEncoding( 0347, "Aacute" ); + addCharacterEncoding( 0345, "Acircumflex" ); + addCharacterEncoding( 0200, "Adieresis" ); + addCharacterEncoding( 0313, "Agrave" ); + addCharacterEncoding( 0201, "Aring" ); + addCharacterEncoding( 0314, "Atilde" ); + addCharacterEncoding( 0102, "B" ); + addCharacterEncoding( 0103, "C" ); + addCharacterEncoding( 0202, "Ccedilla" ); + addCharacterEncoding( 0104, "D" ); + addCharacterEncoding( 0105, "E" ); + addCharacterEncoding( 0203, "Eacute" ); + addCharacterEncoding( 0346, "Ecircumflex" ); + addCharacterEncoding( 0350, "Edieresis" ); + addCharacterEncoding( 0351, "Egrave" ); + addCharacterEncoding( 0106, "F" ); + addCharacterEncoding( 0107, "G" ); + addCharacterEncoding( 0110, "H" ); + addCharacterEncoding( 0111, "I" ); + addCharacterEncoding( 0352, "Iacute" ); + addCharacterEncoding( 0353, "Icircumflex" ); + addCharacterEncoding( 0354, "Idieresis" ); + addCharacterEncoding( 0355, "Igrave" ); + addCharacterEncoding( 0112, "J" ); + addCharacterEncoding( 0113, "K" ); + addCharacterEncoding( 0114, "L" ); + addCharacterEncoding( 0115, "M" ); + addCharacterEncoding( 0116, "N" ); + addCharacterEncoding( 0204, "Ntilde" ); + addCharacterEncoding( 0117, "O" ); + addCharacterEncoding( 0316, "OE" ); + addCharacterEncoding( 0356, "Oacute" ); + addCharacterEncoding( 0357, "Ocircumflex" ); + addCharacterEncoding( 0205, "Odieresis" ); + addCharacterEncoding( 0361, "Ograve" ); + addCharacterEncoding( 0257, "Oslash" ); + addCharacterEncoding( 0315, "Otilde" ); + addCharacterEncoding( 0120, "P" ); + addCharacterEncoding( 0121, "Q" ); + addCharacterEncoding( 0122, "R" ); + addCharacterEncoding( 0123, "S" ); + addCharacterEncoding( 0124, "T" ); + addCharacterEncoding( 0125, "U" ); + addCharacterEncoding( 0362, "Uacute" ); + addCharacterEncoding( 0363, "Ucircumflex" ); + addCharacterEncoding( 0206, "Udieresis" ); + addCharacterEncoding( 0364, "Ugrave" ); + addCharacterEncoding( 0126, "V" ); + addCharacterEncoding( 0127, "W" ); + addCharacterEncoding( 0130, "X" ); + addCharacterEncoding( 0131, "Y" ); + addCharacterEncoding( 0331, "Ydieresis" ); + addCharacterEncoding( 0132, "Z" ); + addCharacterEncoding( 0141, "a" ); + addCharacterEncoding( 0207, "aacute" ); + addCharacterEncoding( 0211, "acircumflex" ); + addCharacterEncoding( 0253, "acute" ); + addCharacterEncoding( 0212, "adieresis" ); + addCharacterEncoding( 0276, "ae" ); + addCharacterEncoding( 0210, "agrave" ); + addCharacterEncoding( 046, "ampersand" ); + addCharacterEncoding( 0214, "aring" ); + addCharacterEncoding( 0136, "asciicircum" ); + addCharacterEncoding( 0176, "asciitilde" ); + addCharacterEncoding( 052, "asterisk" ); + addCharacterEncoding( 0100, "at" ); + addCharacterEncoding( 0213, "atilde" ); + addCharacterEncoding( 0142, "b" ); + addCharacterEncoding( 0134, "backslash" ); + addCharacterEncoding( 0174, "bar" ); + addCharacterEncoding( 0173, "braceleft" ); + addCharacterEncoding( 0175, "braceright" ); + addCharacterEncoding( 0133, "bracketleft" ); + addCharacterEncoding( 0135, "bracketright" ); + addCharacterEncoding( 0371, "breve" ); + addCharacterEncoding( 0245, "bullet" ); + addCharacterEncoding( 0143, "c" ); + addCharacterEncoding( 0377, "caron" ); + addCharacterEncoding( 0215, "ccedilla" ); + addCharacterEncoding( 0374, "cedilla" ); + addCharacterEncoding( 0242, "cent" ); + addCharacterEncoding( 0366, "circumflex" ); + addCharacterEncoding( 072, "colon" ); + addCharacterEncoding( 054, "comma" ); + addCharacterEncoding( 0251, "copyright" ); + addCharacterEncoding( 0333, "currency" ); + addCharacterEncoding( 0144, "d" ); + addCharacterEncoding( 0240, "dagger" ); + addCharacterEncoding( 0340, "daggerdbl" ); + addCharacterEncoding( 0241, "degree" ); + addCharacterEncoding( 0254, "dieresis" ); + addCharacterEncoding( 0326, "divide" ); + addCharacterEncoding( 044, "dollar" ); + addCharacterEncoding( 0372, "dotaccent" ); + addCharacterEncoding( 0365, "dotlessi" ); + addCharacterEncoding( 0145, "e" ); + addCharacterEncoding( 0216, "eacute" ); + addCharacterEncoding( 0220, "ecircumflex" ); + addCharacterEncoding( 0221, "edieresis" ); + addCharacterEncoding( 0217, "egrave" ); + addCharacterEncoding( 070, "eight" ); + addCharacterEncoding( 0311, "ellipsis" ); + addCharacterEncoding( 0321, "emdash" ); + addCharacterEncoding( 0320, "endash" ); + addCharacterEncoding( 075, "equal" ); + addCharacterEncoding( 041, "exclam" ); + addCharacterEncoding( 0301, "exclamdown" ); + addCharacterEncoding( 0146, "f" ); + addCharacterEncoding( 0336, "fi" ); + addCharacterEncoding( 065, "five" ); + addCharacterEncoding( 0337, "fl" ); + addCharacterEncoding( 0304, "florin" ); + addCharacterEncoding( 064, "four" ); + addCharacterEncoding( 0332, "fraction" ); + addCharacterEncoding( 0147, "g" ); + addCharacterEncoding( 0247, "germandbls" ); + addCharacterEncoding( 0140, "grave" ); + addCharacterEncoding( 076, "greater" ); + addCharacterEncoding( 0307, "guillemotleft" ); + addCharacterEncoding( 0310, "guillemotright" ); + addCharacterEncoding( 0334, "guilsinglleft" ); + addCharacterEncoding( 0335, "guilsinglright" ); + addCharacterEncoding( 0150, "h" ); + addCharacterEncoding( 0375, "hungarumlaut" ); + addCharacterEncoding( 055, "hyphen" ); + addCharacterEncoding( 0151, "i" ); + addCharacterEncoding( 0222, "iacute" ); + addCharacterEncoding( 0224, "icircumflex" ); + addCharacterEncoding( 0225, "idieresis" ); + addCharacterEncoding( 0223, "igrave" ); + addCharacterEncoding( 0152, "j" ); + addCharacterEncoding( 0153, "k" ); + addCharacterEncoding( 0154, "l" ); + addCharacterEncoding( 074, "less" ); + addCharacterEncoding( 0302, "logicalnot" ); + addCharacterEncoding( 0155, "m" ); + addCharacterEncoding( 0370, "macron" ); + addCharacterEncoding( 0265, "mu" ); + addCharacterEncoding( 0156, "n" ); + addCharacterEncoding( 071, "nine" ); + addCharacterEncoding( 0226, "ntilde" ); + addCharacterEncoding( 043, "numbersign" ); + addCharacterEncoding( 0157, "o" ); + addCharacterEncoding( 0227, "oacute" ); + addCharacterEncoding( 0231, "ocircumflex" ); + addCharacterEncoding( 0232, "odieresis" ); + addCharacterEncoding( 0317, "oe" ); + addCharacterEncoding( 0376, "ogonek" ); + addCharacterEncoding( 0230, "ograve" ); + addCharacterEncoding( 061, "one" ); + addCharacterEncoding( 0273, "ordfeminine" ); + addCharacterEncoding( 0274, "ordmasculine" ); + addCharacterEncoding( 0277, "oslash" ); + addCharacterEncoding( 0233, "otilde" ); + addCharacterEncoding( 0160, "p" ); + addCharacterEncoding( 0246, "paragraph" ); + addCharacterEncoding( 050, "parenleft" ); + addCharacterEncoding( 051, "parenright" ); + addCharacterEncoding( 045, "percent" ); + addCharacterEncoding( 056, "period" ); + addCharacterEncoding( 0341, "periodcentered" ); + addCharacterEncoding( 0344, "perthousand" ); + addCharacterEncoding( 053, "plus" ); + addCharacterEncoding( 0261, "plusminus" ); + addCharacterEncoding( 0161, "q" ); + addCharacterEncoding( 077, "question" ); + addCharacterEncoding( 0300, "questiondown" ); + addCharacterEncoding( 042, "quotedbl" ); + addCharacterEncoding( 0343, "quotedblbase" ); + addCharacterEncoding( 0322, "quotedblleft" ); + addCharacterEncoding( 0323, "quotedblright" ); + addCharacterEncoding( 0324, "quoteleft" ); + addCharacterEncoding( 0325, "quoteright" ); + addCharacterEncoding( 0342, "quotesinglbase" ); + addCharacterEncoding( 047, "quotesingle" ); + addCharacterEncoding( 0162, "r" ); + addCharacterEncoding( 0250, "registered" ); + addCharacterEncoding( 0373, "ring" ); + addCharacterEncoding( 0163, "s" ); + addCharacterEncoding( 0244, "section" ); + addCharacterEncoding( 073, "semicolon" ); + addCharacterEncoding( 067, "seven" ); + addCharacterEncoding( 066, "six" ); + addCharacterEncoding( 057, "slash" ); + addCharacterEncoding( 040, "space" ); + addCharacterEncoding( 0243, "sterling" ); + addCharacterEncoding( 0164, "t" ); + addCharacterEncoding( 063, "three" ); + addCharacterEncoding( 0367, "tilde" ); + addCharacterEncoding( 0252, "trademark" ); + addCharacterEncoding( 062, "two" ); + addCharacterEncoding( 0165, "u" ); + addCharacterEncoding( 0234, "uacute" ); + addCharacterEncoding( 0236, "ucircumflex" ); + addCharacterEncoding( 0237, "udieresis" ); + addCharacterEncoding( 0235, "ugrave" ); + addCharacterEncoding( 0137, "underscore" ); + addCharacterEncoding( 0166, "v" ); + addCharacterEncoding( 0167, "w" ); + addCharacterEncoding( 0170, "x" ); + addCharacterEncoding( 0171, "y" ); + addCharacterEncoding( 0330, "ydieresis" ); + addCharacterEncoding( 0264, "yen" ); + addCharacterEncoding( 0172, "z" ); + addCharacterEncoding( 060, "zero" ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/encoding/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/encoding/package.html new file mode 100644 index 000000000..0c6203685 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/encoding/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains the implementations for all of the encodings that are used in PDF documents. + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/pfb/PfbParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/pfb/PfbParser.java new file mode 100644 index 000000000..e71fa501c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/pfb/PfbParser.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.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 Ben Litchfield (ben@benlitchfield.com) + * @author Michael Niedermair + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/pfb/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/pfb/package.html new file mode 100644 index 000000000..c0080196c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/pfb/package.html @@ -0,0 +1,25 @@ + + + + + + + +Classes that are used to parse pfb files. + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/ttf/AbstractTTFParser.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/AbstractTTFParser.java new file mode 100644 index 000000000..0fe9c9c51 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/AbstractTTFParser.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +abstract class AbstractTTFParser { + + protected boolean isEmbedded = false; + + public AbstractTTFParser(boolean isEmbedded) { + this.isEmbedded = isEmbedded; + } + + /** + * 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 ttfData The TTF data to parse. + * @return A true type font. + * @throws IOException If there is an error parsing the true type font. + */ + public TrueTypeFont parseTTF( InputStream ttfData ) throws IOException + { + return parseTTF( new MemoryTTFDataStream( ttfData )); + } + + /** + * 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 initialized = new ArrayList(); + HeaderTable head = font.getHeader(); + if (head == null) + { + throw new IOException("head is mandatory"); + } + raf.seek( head.getOffset() ); + head.initData( font, raf ); + initialized.add( head ); + + HorizontalHeaderTable hh = font.getHorizontalHeader(); + if (hh == null) + { + throw new IOException("hhead is mandatory"); + } + raf.seek( hh.getOffset() ); + hh.initData( font, raf ); + initialized.add( hh ); + + MaximumProfileTable maxp = font.getMaximumProfile(); + if (maxp != null) + { + raf.seek( maxp.getOffset() ); + maxp.initData( font, raf ); + initialized.add( maxp ); + } + else + { + throw new IOException("maxp is mandatory"); + } + + PostScriptTable post = font.getPostScript(); + if (post != null) { + raf.seek( post.getOffset() ); + post.initData( font, raf ); + initialized.add( post ); + } + else if ( !isEmbedded ) + { + // in an embedded font this table is optional + throw new IOException("post is mandatory"); + } + + IndexToLocationTable loc = font.getIndexToLocation(); + if (loc == null) + { + throw new IOException("loca is mandatory"); + } + raf.seek( loc.getOffset() ); + loc.initData( font, raf ); + initialized.add( loc ); + + boolean cvt = false, prep = false, fpgm = false; + Iterator iter = font.getTables().iterator(); + while( iter.hasNext() ) + { + TTFTable table = iter.next(); + if( !initialized.contains( table ) ) + { + raf.seek( table.getOffset() ); + table.initData( font, raf ); + } + if (table.getTag().startsWith("cvt")) + { + cvt = true; + } + else if ("prep".equals(table.getTag())) + { + prep = true; + } + else if ("fpgm".equals(table.getTag())) + { + fpgm = true; + } + } + + // check others mandatory tables + if ( font.getGlyph() == null ) + { + throw new IOException("glyf is mandatory"); + } + if ( font.getNaming() == null && !isEmbedded ) + { + throw new IOException("name is mandatory"); + } + if ( font.getHorizontalMetrics() == null ) + { + throw new IOException("hmtx is mandatory"); + } + + if (isEmbedded) { + // in a embedded truetype font prep, cvt_ and fpgm tables + // are mandatory + if (!fpgm) + { + throw new IOException("fpgm is mandatory"); + } + if (!prep) + { + throw new IOException("prep is mandatory"); + } + if (!cvt) + { + throw new IOException("cvt_ is mandatory"); + } + } + } + + 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( 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/ttf/CIDFontType2Parser.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/CIDFontType2Parser.java new file mode 100644 index 000000000..af1c9222b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/CIDFontType2Parser.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + + +public class CIDFontType2Parser extends AbstractTTFParser +{ + public CIDFontType2Parser() { + super(false); + } + + public CIDFontType2Parser(boolean isEmbedded) { + super(isEmbedded); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/ttf/CMAPEncodingEntry.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/CMAPEncodingEntry.java new file mode 100644 index 000000000..d51d2e8a7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/CMAPEncodingEntry.java @@ -0,0 +1,574 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +/** + * An encoding entry for a cmap. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class CMAPEncodingEntry +{ + + private int platformId; + private int platformEncodingId; + private long subTableOffset; + private int[] glyphIdToCharacterCode; + private Map characterCodeToGlyphId = new HashMap(); + + /** + * 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(); + long length; + long version; + int numGlyphs; + if (subtableFormat < 8) { + length = data.readUnsignedShort(); + version = data.readUnsignedShort(); + numGlyphs = ttf.getMaximumProfile().getNumGlyphs(); + } else { + // read an other UnsignedShort to read a Fixed32 + data.readUnsignedShort(); + length = data.readUnsignedInt(); + version = data.readUnsignedInt(); + numGlyphs = ttf.getMaximumProfile().getNumGlyphs(); + } + + switch (subtableFormat) { + case 0: + processSubtype0(ttf, data); + break; + case 2: + processSubtype2(ttf, data, numGlyphs); + break; + case 4: + processSubtype4(ttf, data, numGlyphs); + break; + case 6: + processSubtype6(ttf, data, numGlyphs); + break; + case 8: + processSubtype8(ttf, data, numGlyphs); + break; + case 10: + processSubtype10(ttf, data, numGlyphs); + break; + case 12: + processSubtype12(ttf, data, numGlyphs); + break; + case 13: + processSubtype13(ttf, data, numGlyphs); + break; + case 14: + processSubtype14(ttf, data, numGlyphs); + break; + default: + throw new IOException( "Unknown cmap format:" + subtableFormat ); + } + } + + /** + * Reads a format 8 subtable. + * @param ttf the TrueTypeFont instance holding the parsed data. + * @param data the data stream of the to be parsed ttf font + * @param numGlyphs number of glyphs to be read + * @throws IOException If there is an error parsing the true type font. + */ + protected void processSubtype8( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) + throws IOException { + // --- is32 is a 65536 BITS array ( = 8192 BYTES) + int[] is32 = data.readUnsignedByteArray(8192); + long nbGroups = data.readUnsignedInt(); + + // --- nbGroups shouldn't be greater than 65536 + if (nbGroups > 65536) { + throw new IOException("CMap ( Subtype8 ) is invalid"); + } + + glyphIdToCharacterCode = new int[numGlyphs]; + // -- Read all sub header + for (long i = 0; i <= nbGroups ; ++i ) + { + long firstCode = data.readUnsignedInt(); + long endCode = data.readUnsignedInt(); + long startGlyph = data.readUnsignedInt(); + + // -- process simple validation + if (firstCode > endCode || 0 > firstCode) { + throw new IOException("Range invalid"); + } + + for (long j = firstCode; j <= endCode; ++j) { + // -- Convert the Character code in decimal + if (j > Integer.MAX_VALUE) { + throw new IOException("[Sub Format 8] Invalid Character code"); + } + + int currentCharCode; + if ( (is32[ (int)j / 8 ] & (1 << ((int)j % 8 ))) == 0) { + currentCharCode = (int)j; + } else { + // the character code uses a 32bits format + // convert it in decimal : see http://www.unicode.org/faq//utf_bom.html#utf16-4 + long LEAD_OFFSET = 0xD800 - (0x10000 >> 10); + long SURROGATE_OFFSET = 0x10000 - (0xD800 << 10) - 0xDC00; + long lead = LEAD_OFFSET + (j >> 10); + long trail = 0xDC00 + (j & 0x3FF); + + long codepoint = (lead << 10) + trail + SURROGATE_OFFSET; + if (codepoint > Integer.MAX_VALUE) { + throw new IOException("[Sub Format 8] Invalid Character code"); + } + currentCharCode = (int)codepoint; + } + + long glyphIndex = startGlyph + (j-firstCode); + if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE) { + throw new IOException("CMap contains an invalid glyph index"); + } + + glyphIdToCharacterCode[(int)glyphIndex] = currentCharCode; + characterCodeToGlyphId.put(currentCharCode, (int)glyphIndex); + } + } + } + + /** + * Reads a format 10 subtable. + * @param ttf the TrueTypeFont instance holding the parsed data. + * @param data the data stream of the to be parsed ttf font + * @param numGlyphs number of glyphs to be read + * @throws IOException If there is an error parsing the true type font. + */ + protected void processSubtype10( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) + throws IOException { + long startCode = data.readUnsignedInt(); + long numChars = data.readUnsignedInt(); + if (numChars > Integer.MAX_VALUE) { + throw new IOException("Invalid number of Characters"); + } + + if ( startCode < 0 || startCode > 0x0010FFFF + || (startCode + numChars) > 0x0010FFFF + || ((startCode + numChars) >= 0x0000D800 && (startCode + numChars) <= 0x0000DFFF)) { + throw new IOException("Invalid Characters codes"); + + } + } + + /** + * Reads a format 12 subtable. + * @param ttf the TrueTypeFont instance holding the parsed data. + * @param data the data stream of the to be parsed ttf font + * @param numGlyphs number of glyphs to be read + * @throws IOException If there is an error parsing the true type font. + */ + protected void processSubtype12( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) + throws IOException { + long nbGroups = data.readUnsignedInt(); + glyphIdToCharacterCode = new int[numGlyphs]; + for (long i = 0; i <= nbGroups ; ++i ) + { + long firstCode = data.readUnsignedInt(); + long endCode = data.readUnsignedInt(); + long startGlyph = data.readUnsignedInt(); + + if ( firstCode < 0 || firstCode > 0x0010FFFF + || ( firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF ) ) { + throw new IOException("Invalid Characters codes"); + } + + if ( endCode > 0 && (endCode < firstCode || endCode > 0x0010FFFF + || ( endCode >= 0x0000D800 && endCode <= 0x0000DFFF ) ) ) { + throw new IOException("Invalid Characters codes"); + } + + for (long j = 0; j <= (endCode - firstCode); ++j) { + + if ( (firstCode + j) > Integer.MAX_VALUE ) { + throw new IOException("Character Code greater than Integer.MAX_VALUE"); + } + + long glyphIndex = (startGlyph + j); + if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE) { + throw new IOException("CMap contains an invalid glyph index"); + } + glyphIdToCharacterCode[(int)glyphIndex] = (int)(firstCode + j); + characterCodeToGlyphId.put((int)(firstCode + j), (int)glyphIndex); + } + } + } + + /** + * Reads a format 13 subtable. + * @param ttf the TrueTypeFont instance holding the parsed data. + * @param data the data stream of the to be parsed ttf font + * @param numGlyphs number of glyphs to be read + * @throws IOException If there is an error parsing the true type font. + */ + protected void processSubtype13( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) + throws IOException { + long nbGroups = data.readUnsignedInt(); + for (long i = 0; i <= nbGroups ; ++i ) + { + long firstCode = data.readUnsignedInt(); + long endCode = data.readUnsignedInt(); + long glyphId = data.readUnsignedInt(); + + if (glyphId > numGlyphs) { + throw new IOException("CMap contains an invalid glyph index"); + } + + if ( firstCode < 0 || firstCode > 0x0010FFFF + || ( firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF ) ) { + throw new IOException("Invalid Characters codes"); + } + + if ( endCode > 0 && (endCode < firstCode || endCode > 0x0010FFFF + || ( endCode >= 0x0000D800 && endCode <= 0x0000DFFF )) ) { + throw new IOException("Invalid Characters codes"); + } + + for (long j = 0; j <= (endCode - firstCode); ++j) { + + if ( (firstCode + j) > Integer.MAX_VALUE ) { + throw new IOException("Character Code greater than Integer.MAX_VALUE"); + } + glyphIdToCharacterCode[(int)glyphId] = (int)(firstCode + j); + characterCodeToGlyphId.put((int)(firstCode + j), (int)glyphId); + } + } + } + + /** + * Reads a format 14 subtable. + * @param ttf the TrueTypeFont instance holding the parsed data. + * @param data the data stream of the to be parsed ttf font + * @param numGlyphs number of glyphs to be read + * @throws IOException If there is an error parsing the true type font. + */ + protected void processSubtype14( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) + throws IOException { + throw new IOException("CMap subtype 14 not yet implemented"); + } + + /** + * Reads a format 6 subtable. + * @param ttf the TrueTypeFont instance holding the parsed data. + * @param data the data stream of the to be parsed ttf font + * @param numGlyphs number of glyphs to be read + * @throws IOException If there is an error parsing the true type font. + */ + protected void processSubtype6( TrueTypeFont ttf, TTFDataStream data, int numGlyphs ) + throws IOException { + int firstCode = data.readUnsignedShort(); + int entryCount = data.readUnsignedShort(); + glyphIdToCharacterCode = new int[numGlyphs]; + int[] glyphIdArray = data.readUnsignedShortArray( entryCount ); + for( int i=0; i 0 ) + { + endPointsOfContours = new int[ numberOfContours ]; + for( int i=0; i 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 getNameRecords() + { + return nameRecords; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/ttf/OS2WindowsMetricsTable.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/OS2WindowsMetricsTable.java new file mode 100644 index 000000000..b9a1c428d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/OS2WindowsMetricsTable.java @@ -0,0 +1,680 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +/** + * A table in a true type font. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @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/fluidbook/tools/fwstk/src/apache/fontbox/ttf/PostScriptTable.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/PostScriptTable.java new file mode 100644 index 000000000..7626d9ef7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/PostScriptTable.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +import org.apache.fontbox.encoding.MacRomanEncoding; + +/** + * A table in a true type font. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @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= 258 ) + { + nameArray = new String[ maxIndex-258 +1 ]; + for( int i=0; i= 258 && index <= 32767 ) + { + glyphNames[i] = nameArray[index-258]; + } + else + { + // PDFBOX-808: Index numbers between 32768 and 65535 are + // reserved for future use, so we should just ignore them + glyphNames[i] = ".undefined"; + } + } + } + else if( formatType == 2.5f ) + { + int[] glyphNameIndex = new int[maxp.getNumGlyphs()]; + for( int i=0; i + * usage: java org.pdfbox.ttf.TTFParser <ttf-file> + * + * @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 " ); + System.exit( -1 ); + } + TTFParser parser = new TTFParser(); + TrueTypeFont font = parser.parseTTF( args[0] ); + System.out.println( "Font:" + font ); + } + + /** + * {@inheritDoc} + */ + protected void parseTables(TrueTypeFont font, TTFDataStream raf) throws IOException + { + super.parseTables(font, raf); + + // check others mandatory tables + if ( font.getCMAP() == null ){ + throw new IOException("cmap is mandatory"); + } + } + +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/ttf/TTFTable.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/TTFTable.java new file mode 100644 index 000000000..b466ad269 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/TTFTable.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.io.IOException; + +/** + * A table in a true type font. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @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/fluidbook/tools/fwstk/src/apache/fontbox/ttf/TrueTypeFont.java b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/TrueTypeFont.java new file mode 100644 index 000000000..852d6a144 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/TrueTypeFont.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.ttf; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A class to hold true type font information. + * + * @author Ben Litchfield (ben@benlitchfield.com) + * @version $Revision: 1.2 $ + */ +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 data 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 + * + * @throws IOException If there is an error getting the font data. + */ + public InputStream getOriginalData() throws IOException + { + return data.getOriginalData(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/ttf/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/package.html new file mode 100644 index 000000000..0575b1731 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/ttf/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains classes to parse a TTF file. + + diff --git a/fluidbook/tools/fwstk/src/apache/fontbox/util/BoundingBox.java b/fluidbook/tools/fwstk/src/apache/fontbox/util/BoundingBox.java new file mode 100644 index 000000000..3dbbf3af8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/util/BoundingBox.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.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@benlitchfield.com) + * @version $Revision: 1.1 $ + */ +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/fluidbook/tools/fwstk/src/apache/fontbox/util/ResourceLoader.java b/fluidbook/tools/fwstk/src/apache/fontbox/util/ResourceLoader.java new file mode 100644 index 000000000..815f441ec --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/util/ResourceLoader.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.util.Properties; + +/** + * This class will handle loading resource files(AFM/CMAP). This was originally + * written for PDFBox but FontBox uses it as well. For now each project will + * have their own version. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class ResourceLoader +{ + + /** + * 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 + { + 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/fluidbook/tools/fwstk/src/apache/fontbox/util/package.html b/fluidbook/tools/fwstk/src/apache/fontbox/util/package.html new file mode 100644 index 000000000..fdc5aed40 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/fontbox/util/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains utility classes used by various font types. + + diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/impl/DateConverter.java b/fluidbook/tools/fwstk/src/apache/jempbox/impl/DateConverter.java new file mode 100644 index 000000000..38abf835d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/impl/DateConverter.java @@ -0,0 +1,260 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.impl; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.SimpleTimeZone; + +/** + * 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 + * @author Christopher Oezbek + * + * @version $Revision: 1.6 $ + */ +public class DateConverter +{ + //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"), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"), + new SimpleDateFormat("MM/dd/yyyy hh:mm:ss"), + new SimpleDateFormat("MM/dd/yyyy"), + new SimpleDateFormat("EEEE, MMM dd, yyyy"), // Acrobat Distiller 1.0.2 for Macintosh + new SimpleDateFormat("EEEE MMM dd, yyyy HH:mm:ss"), // ECMP5 + new SimpleDateFormat("EEEE MMM dd HH:mm:ss z yyyy"), // GNU Ghostscript 7.0.7 + new SimpleDateFormat("EEEE, MMM dd, yyyy 'at' hh:mma") // Acrobat Net Distiller 1.0 for Windows + }; + + private DateConverter() + { + //utility class should not be constructed. + } + + /** + * 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 && date.trim().length() > 0 ) + { + //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() ); + } + date = date.replaceAll("[-:T]", ""); + + 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 ) ); + } + 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 ) + { + if( sign == '+' ) + { + //parseInt cannot handle the + sign + hours = Integer.parseInt( date.substring( 15, 17 ) ); + } + else + { + hours = -Integer.parseInt( date.substring( 14, 16 ) ); + } + } + if( sign=='+' ) + { + if( date.length() >= 19 ) + { + minutes = Integer.parseInt( date.substring( 17, 19 ) ); + } + } + else + { + if( date.length() >= 18 ) + { + minutes = Integer.parseInt( date.substring( 16, 18 ) ); + } + } + zone = new SimpleTimeZone( hours*60*60*1000 + minutes*60*1000, "Unknown" ); + } + } + if( zone == null ) + { + retval = new GregorianCalendar(); + } + else + { + retval = new GregorianCalendar( zone ); + } + retval.clear(); + retval.set( year, month-1, day, hour, minute, second ); + } + catch( NumberFormatException e ) + { + + // remove the arbitrary : in the timezone. SimpleDateFormat + // can't handle it + if (date.substring(date.length()-3,date.length()-2).equals(":") && + (date.substring(date.length()-6,date.length()-5).equals("+") || + date.substring(date.length()-6,date.length()-5).equals("-"))) + { + //thats a timezone string, remove the : + date = date.substring(0,date.length()-3) + + date.substring(date.length()-2); + } + for( int i=0; retval == null && iBen Litchfield + * @author Christopher Oezbek + * + * @version $Revision: 1.4 $ + */ +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 parse an InputSource 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( InputSource 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 parse an XML stream and create a DOM document. + * + * @param fileName The file to get the XML from. + * @return The DOM document. + * @throws IOException It there is an error creating the dom. + */ + public static Document parse( String fileName ) throws IOException + { + try + { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + return builder.parse( fileName ); + } + catch( Exception e ) + { + IOException thrown = new IOException( e.getMessage() ); + throw thrown; + } + } + + /** + * Create a new blank XML document. + * + * @return The new blank XML document. + * + * @throws IOException If there is an error creating the XML document. + */ + public static Document newDocument() throws IOException + { + try + { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + return builder.newDocument(); + } + catch( Exception e ) + { + IOException thrown = new IOException( e.getMessage() ); + throw thrown; + } + } + + /** + * Get the first instance of an element by name. + * + * @param parent The parent to get the element from. + * @param elementName The name of the element to look for. + * @return The element or null if it is not found. + */ + public static Element getElement( Element parent, String elementName ) + { + Element retval = null; + NodeList children = parent.getElementsByTagName( elementName ); + if( children.getLength() > 0 ) + { + retval = (Element)children.item( 0 ); + } + return retval; + } + + /** + * Get the integer value of a subnode. + * + * @param parent The parent element that holds the values. + * @param nodeName The name of the node that holds the integer value. + * + * @return The integer value of the node. + */ + public static Integer getIntValue( Element parent, String nodeName ) + { + String intVal = XMLUtil.getStringValue( XMLUtil.getElement( parent, nodeName ) ); + Integer retval = null; + if( intVal != null ) + { + retval = new Integer( intVal ); + } + return retval; + } + + /** + * Set the integer value of an element. + * + * @param parent The parent element that will hold this subelement. + * @param nodeName The name of the subelement. + * @param intValue The value to set. + */ + public static void setIntValue( Element parent, String nodeName, Integer intValue ) + { + Element currentValue = getElement( parent, nodeName ); + if( intValue == null ) + { + if( currentValue != null ) + { + parent.removeChild( currentValue ); + } + else + { + //it doesn't exist so we don't need to remove it. + } + } + else + { + if( currentValue == null ) + { + currentValue = parent.getOwnerDocument().createElement( nodeName ); + parent.appendChild( currentValue ); + } + XMLUtil.setStringValue( currentValue, intValue.toString() ); + } + } + + /** + * Get the value of a subnode. + * + * @param parent The parent element that holds the values. + * @param nodeName The name of the node that holds the value. + * + * @return The value of the sub node. + */ + public static String getStringValue( Element parent, String nodeName ) + { + return XMLUtil.getStringValue( XMLUtil.getElement( parent, nodeName ) ); + } + + /** + * Set the value of an element. + * + * @param parent The parent element that will hold this subelement. + * @param nodeName The name of the subelement. + * @param nodeValue The value to set. + */ + public static void setStringValue( Element parent, String nodeName, String nodeValue ) + { + Element currentValue = getElement( parent, nodeName ); + if( nodeValue == null ) + { + if( currentValue != null ) + { + parent.removeChild( currentValue ); + } + else + { + //it doesn't exist so we don't need to remove it. + } + } + else + { + if( currentValue == null ) + { + currentValue = parent.getOwnerDocument().createElement( nodeName ); + parent.appendChild( currentValue ); + } + XMLUtil.setStringValue( currentValue, nodeValue ); + } + } + + /** + * 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 getStringValue( Element node ) + { + String retval = ""; + NodeList children = node.getChildNodes(); + for( int i=0; i + + + + + + +The impl package contains internal implementation classes for JempBox. + + diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/Elementable.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/Elementable.java new file mode 100644 index 000000000..aca8033f1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/Elementable.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.w3c.dom.Element; + +/** + * A simple class that allows access to an XML element. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public interface Elementable +{ + /** + * Get the XML element that this object represents. + * + * @return The xml element. + */ + public Element getElement(); +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceEvent.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceEvent.java new file mode 100644 index 000000000..49c62c44e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceEvent.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.io.IOException; +import java.util.Calendar; + +import org.apache.jempbox.impl.DateConverter; +import org.apache.jempbox.impl.XMLUtil; +import org.w3c.dom.Element; + +/** + * This class represents a high level event that occured during the processing + * of this resource. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class ResourceEvent implements Elementable +{ + + /** + * Namespace for a resource event. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"; + + /** + * A predefined action. + */ + public static final String ACTION_CONVERTED = "converted"; + /** + * A predefined action. + */ + public static final String ACTION_COPIED = "copied"; + /** + * A predefined action. + */ + public static final String ACTION_CREATED = "created"; + /** + * A predefined action. + */ + public static final String ACTION_CROPPED = "cropped"; + /** + * A predefined action. + */ + public static final String ACTION_EDITED = "edited"; + /** + * A predefined action. + */ + public static final String ACTION_FILTERED = "filtered"; + /** + * A predefined action. + */ + public static final String ACTION_FORMATTED = "formatted"; + /** + * A predefined action. + */ + public static final String ACTION_VERSION_UPDATED = "version_updated"; + /** + * A predefined action. + */ + public static final String ACTION_PRINTED = "printed"; + /** + * A predefined action. + */ + public static final String ACTION_PUBLISHED = "published"; + /** + * A predefined action. + */ + public static final String ACTION_MANAGED = "managed"; + /** + * A predefined action. + */ + public static final String ACTION_PRODUCED = "produced"; + /** + * A predefined action. + */ + public static final String ACTION_RESIZED = "resized"; + + /** + * The DOM representation of this object. + */ + protected Element parent = null; + + /** + * Create a resource reference based on a existing parent property set. + * + * @param parentElement The parent element that will store the resource properties. + */ + public ResourceEvent( Element parentElement ) + { + parent = parentElement; + if( !parent.hasAttribute( "xmlns:stEvt" ) ) + { + parent.setAttributeNS( + XMPSchema.NS_NAMESPACE, + "xmlns:stEvt", + NAMESPACE ); + } + } + + /** + * Create resource event based on schema. + * + * @param schema The schema that this event will be part of. + */ + public ResourceEvent( XMPSchema schema ) + { + parent = schema.getElement().getOwnerDocument().createElement( "rdf:li" ); + parent.setAttributeNS( + XMPSchema.NS_NAMESPACE, + "xmlns:stEvt", + NAMESPACE ); + } + + /** + * Get the underlying XML element. + * + * @return The XML element that this object represents. + */ + public Element getElement() + { + return parent; + } + + /** + * Get the action that occured. See the ACTION_XXX constants. + * + * @return An action key, such as 'created' or 'printed'. + */ + public String getAction() + { + return XMLUtil.getStringValue( parent, "stEvt:action" ); + } + + /** + * Set the action that this event represents. See the ACTION_XXX constants. + * + * @param action The action that this event represents. + */ + public void setAction( String action ) + { + XMLUtil.setStringValue( parent, "stEvt:action", action ); + } + + /** + * Get the referenced resource's instance id. + * + * @return The id of the reference document instance. + */ + public String getInstanceID() + { + return XMLUtil.getStringValue( parent, "stEvt:instanceID" ); + } + + /** + * Set the referenced resource's document instance id. + * + * @param id The id of the reference document instance. + */ + public void setInstanceID( String id ) + { + XMLUtil.setStringValue( parent, "stEvt:instanceID", id ); + } + + /** + * Get an additional description of the event. + * + * @return Additional description of this event + */ + public String getParameters() + { + return XMLUtil.getStringValue( parent, "stEvt:parameters" ); + } + + /** + * Set some addition description to this event. + * + * @param param The additional action parameters. + */ + public void setParameters( String param ) + { + XMLUtil.setStringValue( parent, "stEvt:parameters", param ); + } + + /** + * Get the software that performed this action. + * + * @return The software that performed the action. + */ + public String getSoftwareAgent() + { + return XMLUtil.getStringValue( parent, "stEvt:softwareAgent" ); + } + + /** + * Set the software that performed this operation. + * + * @param software The name of the software that performed this action. + */ + public void setSoftwareAgent( String software ) + { + XMLUtil.setStringValue( parent, "stEvt:softwareAgent", software ); + } + + /** + * Get the date/time that this event occured. + * + * @return The date of the event. + * + * @throws IOException If there is an error creating the date. + */ + public Calendar getWhen() throws IOException + { + return DateConverter.toCalendar( XMLUtil.getStringValue( parent, "stEvt:when" ) ); + } + + /** + * Set when the event occured. + * + * @param when The date that the event occured. + */ + public void setWhen( Calendar when ) + { + XMLUtil.setStringValue( parent, "stEvt:when", DateConverter.toISO8601( when ) ); + } + + /** + * Get name of the asset management system that manages this resource. + * + * @return The name of a asset management system. + */ + public String getManager() + { + return XMLUtil.getStringValue( parent, "stRef:manager" ); + } + + /** + * Set the name of the system that manages this resource. + * + * @param manager The name of the management system. + */ + public void setMangager( String manager ) + { + XMLUtil.setStringValue( parent, "stRef:manager", manager ); + } + + /** + * Get name of the variant of asset management system that manages this resource. + * + * @return The name of a asset management system. + */ + public String getManagerVariant() + { + return XMLUtil.getStringValue( parent, "stRef:managerVariant" ); + } + + /** + * Set the name of the variant of the system that manages this resource. + * + * @param managerVariant The name of the management system. + */ + public void setMangagerVariant( String managerVariant ) + { + XMLUtil.setStringValue( parent, "stRef:managerVariant", managerVariant ); + } + + /** + * URI identifying the managed resource. + * + * @return The URI to resource. + */ + public String getManagerTo() + { + return XMLUtil.getStringValue( parent, "stRef:managerTo" ); + } + + /** + * Set the URI to the managed resource. + * + * @param managerTo The URI to the managed resource. + */ + public void setMangagerTo( String managerTo ) + { + XMLUtil.setStringValue( parent, "stRef:managerTo", managerTo ); + } + + /** + * URI to info about the managed resource. + * + * @return The URI to the resource info. + */ + public String getManagerUI() + { + return XMLUtil.getStringValue( parent, "stRef:managerUI" ); + } + + /** + * Set the URI to the info about the managed resource. + * + * @param managerUI The URI to the managed resource information. + */ + public void setMangagerUI( String managerUI ) + { + XMLUtil.setStringValue( parent, "stRef:managerUI", managerUI ); + } + +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceRef.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceRef.java new file mode 100644 index 000000000..be45040f0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/ResourceRef.java @@ -0,0 +1,257 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.apache.jempbox.impl.XMLUtil; +import org.w3c.dom.Element; + +/** + * This class represents a multiple part reference to a resource. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class ResourceRef implements Elementable +{ + /** + * The DOM representation of this object. + */ + protected Element parent = null; + + /** + * Create a resource reference based on a existing parent property set. + * + * @param parentElement The parent element that will store the resource properties. + */ + public ResourceRef( Element parentElement ) + { + parent = parentElement; + if( !parent.hasAttribute( "xmlns:stRef" ) ) + { + parent.setAttributeNS( + "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", + "xmlns:stRef", + "http://ns.adobe.com/xap/1.0/sType/ResourceRef#" ); + } + } + + /** + * Get the underlying XML element. + * + * @return The XML element that this object represents. + */ + public Element getElement() + { + return parent; + } + + /** + * Get the referenced resource's id. + * + * @return The id of the reference. + */ + public String getInstanceID() + { + return XMLUtil.getStringValue( parent, "stRef:instanceID" ); + } + + /** + * Set the referenced resource's id. + * + * @param id The id of the reference. + */ + public void setInstanceID( String id ) + { + XMLUtil.setStringValue( parent, "stRef:instanceID", id ); + } + + /** + * Get the referenced resource's document id. + * + * @return The id of the reference document. + */ + public String getDocumentID() + { + return XMLUtil.getStringValue( parent, "stRef:documentID" ); + } + + /** + * Set the referenced resource's document id. + * + * @param id The id of the reference document. + */ + public void setDocumentID( String id ) + { + XMLUtil.setStringValue( parent, "stRef:documentID", id ); + } + + /** + * Get the referenced resource's document version id. + * + * @return The id of the reference document version. + */ + public String getVersionID() + { + return XMLUtil.getStringValue( parent, "stRef:versionID" ); + } + + /** + * Set the referenced resource's version id. + * + * @param id The id of the reference document version. + */ + public void setVersionID( String id ) + { + XMLUtil.setStringValue( parent, "stRef:veresionID", id ); + } + + /** + * Get the rendition class. + * + * @return The value of the rendition class property. + * + * @see ResourceRef#setRenditionClass( String ) + */ + public String getRenditionClass() + { + return XMLUtil.getStringValue( parent, "stRef:renditionClass" ); + } + + /** + * Set the rendition class. The rendition class is derived from a defined + * set of names. The value is series of colon separated tokens and parameters.
+ * Defined values are:
+ * + * + * + * + * + * + * + * + * + *
Token NameDescription
defaultSpecifies master document, no additional tokens allowed
thumbnailA simplied preview. Recommended order is: thumbnailformat:size:colorspace
screenScreen resolution
proofA review proof
draftA review rendition
low-resA low resolution, full size stand-in
+ * + * + * @param renditionClass The rendition class. + */ + public void setRenditionClass( String renditionClass ) + { + XMLUtil.setStringValue( parent, "stRef:renditionClass", renditionClass ); + } + + /** + * Get the extra rendition params. + * + * @return Additional rendition parameters. + */ + public String getRenditionParams() + { + return XMLUtil.getStringValue( parent, "stRef:renditionParams" ); + } + + /** + * Set addition rendition params. + * + * @param params Additional rendition parameters that are too complex for the rendition class. + */ + public void setRenditionParams( String params ) + { + XMLUtil.setStringValue( parent, "stRef:renditionParams", params ); + } + + /** + * Get name of the asset management system that manages this resource. + * + * @return The name of a asset management system. + */ + public String getManager() + { + return XMLUtil.getStringValue( parent, "stRef:manager" ); + } + + /** + * Set the name of the system that manages this resource. + * + * @param manager The name of the management system. + */ + public void setMangager( String manager ) + { + XMLUtil.setStringValue( parent, "stRef:manager", manager ); + } + + /** + * Get name of the variant of asset management system that manages this resource. + * + * @return The name of a asset management system. + */ + public String getManagerVariant() + { + return XMLUtil.getStringValue( parent, "stRef:managerVariant" ); + } + + /** + * Set the name of the variant of the system that manages this resource. + * + * @param managerVariant The name of the management system. + */ + public void setMangagerVariant( String managerVariant ) + { + XMLUtil.setStringValue( parent, "stRef:managerVariant", managerVariant ); + } + + /** + * URI identifying the managed resource. + * + * @return The URI to resource. + */ + public String getManagerTo() + { + return XMLUtil.getStringValue( parent, "stRef:managerTo" ); + } + + /** + * Set the URI to the managed resource. + * + * @param managerTo The URI to the managed resource. + */ + public void setMangagerTo( String managerTo ) + { + XMLUtil.setStringValue( parent, "stRef:managerTo", managerTo ); + } + + /** + * URI to info about the managed resource. + * + * @return The URI to the resource info. + */ + public String getManagerUI() + { + return XMLUtil.getStringValue( parent, "stRef:managerUI" ); + } + + /** + * Set the URI to the info about the managed resource. + * + * @param managerUI The URI to the managed resource information. + */ + public void setMangagerUI( String managerUI ) + { + XMLUtil.setStringValue( parent, "stRef:managerUI", managerUI ); + } + +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/Thumbnail.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/Thumbnail.java new file mode 100644 index 000000000..00bb9b4a7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/Thumbnail.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.apache.jempbox.impl.XMLUtil; +import org.w3c.dom.Element; + +/** + * This class represents a thumbnail datatype. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class Thumbnail +{ + /** + * A supported thumnail format. + */ + public static final String FORMAT_JPEG = "JPEG"; + + + /** + * The DOM representation of this object. + */ + protected Element parent = null; + + /** + * Create a new thumbnail element. + * + * @param metadata The metadata document that his thumbnail will be part of. + */ + public Thumbnail( XMPMetadata metadata ) + { + this( metadata.xmpDocument.createElement( "rdf:li" ) ); + } + + /** + * Create a thumnail based on a parent property set. + * + * @param parentElement The parent element that will store the thumbnail properties. + */ + public Thumbnail( Element parentElement ) + { + parent = parentElement; + parent.setAttributeNS( + XMPSchema.NS_NAMESPACE, + "xmlns:xapGImg", + "http://ns.adobe.com/xap/1.0/g/img/" ); + } + + /** + * Get the underlying XML element. + * + * @return The XML element that this object represents. + */ + public Element getElement() + { + return parent; + } + + /** + * Get the height of the image in pixels. + * + * @return The height of the image in pixels. + */ + public Integer getHeight() + { + return XMLUtil.getIntValue( parent, "xapGImg:height" ); + } + + /** + * Set the height of the element. + * + * @param height The updated height of the element. + */ + public void setHeight( Integer height ) + { + XMLUtil.setIntValue( parent, "xapGImg:height", height ); + } + + /** + * Get the width of the image in pixels. + * + * @return The width of the image in pixels. + */ + public Integer getWidth() + { + return XMLUtil.getIntValue( parent, "xapGImg:width" ); + } + + /** + * Set the width of the element. + * + * @param width The updated width of the element. + */ + public void setWidth( Integer width ) + { + XMLUtil.setIntValue( parent, "xapGImg:width", width ); + } + + /** + * Set the format of the thumbnail, currently only JPEG is supported. See FORMAT_XXX constants. + * + * @param format The image format. + */ + public void setFormat( String format ) + { + XMLUtil.setStringValue( parent, "xapGImg:format", format ); + } + + /** + * Get the format of the thumbnail. See FORMAT_XXX constants. + * + * @return The image format. + */ + public String getFormat() + { + return XMLUtil.getStringValue( parent, "xapGImg:format" ); + } + + /** + * Set the image data in base 64 encoding. + * + * @param image The image. + */ + public void setImage( String image ) + { + XMLUtil.setStringValue( parent, "xapGImg:image", image ); + } + + /** + * Get the image data in base 64 encoding. + * + * @return The image data. + */ + public String getImage() + { + return XMLUtil.getStringValue( parent, "xapGImg:image" ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPMetadata.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPMetadata.java new file mode 100644 index 000000000..ef17062af --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPMetadata.java @@ -0,0 +1,776 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.xml.transform.TransformerException; + +import org.apache.jempbox.impl.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ProcessingInstruction; +import org.xml.sax.InputSource; + +/** + * This class represents the top level XMP data structure and gives access to + * the various schemas that are available as part of the XMP specification. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +public class XMPMetadata +{ + /** + * Supported encoding for persisted XML. + */ + public static final String ENCODING_UTF8 = "UTF-8"; + + /** + * Supported encoding for persisted XML. + */ + public static final String ENCODING_UTF16BE = "UTF-16BE"; + + /** + * Supported encoding for persisted XML. + */ + public static final String ENCODING_UTF16LE = "UTF-16LE"; + + /** + * The DOM representation of the metadata. + */ + protected Document xmpDocument; + + /** + * The encoding of the XMP document. Default is UTF8. + */ + protected String encoding = ENCODING_UTF8; + + /** + * A mapping of namespaces. + */ + protected Map> nsMappings = new HashMap>(); + + /** + * Default constructor, creates blank XMP doc. + * + * @throws IOException + * If there is an error creating the initial document. + */ + public XMPMetadata() throws IOException + { + xmpDocument = XMLUtil.newDocument(); + ProcessingInstruction beginXPacket = xmpDocument + .createProcessingInstruction("xpacket", + "begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\""); + + xmpDocument.appendChild(beginXPacket); + Element xmpMeta = xmpDocument.createElementNS("adobe:ns:meta/", + "x:xmpmeta"); + xmpMeta.setAttributeNS(XMPSchema.NS_NAMESPACE, "xmlns:x", + "adobe:ns:meta/"); + + xmpDocument.appendChild(xmpMeta); + + Element rdf = xmpDocument.createElement("rdf:RDF"); + rdf.setAttributeNS(XMPSchema.NS_NAMESPACE, "xmlns:rdf", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + + xmpMeta.appendChild(rdf); + + ProcessingInstruction endXPacket = xmpDocument + .createProcessingInstruction("xpacket", "end=\"w\""); + xmpDocument.appendChild(endXPacket); + init(); + } + + /** + * Constructor from an existing XML document. + * + * @param doc + * The root XMP document. + */ + public XMPMetadata(Document doc) + { + xmpDocument = doc; + init(); + } + + private void init() + { + nsMappings.put(XMPSchemaPDF.NAMESPACE, XMPSchemaPDF.class); + nsMappings.put(XMPSchemaBasic.NAMESPACE, XMPSchemaBasic.class); + nsMappings + .put(XMPSchemaDublinCore.NAMESPACE, XMPSchemaDublinCore.class); + nsMappings.put(XMPSchemaMediaManagement.NAMESPACE, + XMPSchemaMediaManagement.class); + nsMappings.put(XMPSchemaRightsManagement.NAMESPACE, + XMPSchemaRightsManagement.class); + nsMappings.put(XMPSchemaBasicJobTicket.NAMESPACE, + XMPSchemaBasicJobTicket.class); + nsMappings.put(XMPSchemaDynamicMedia.NAMESPACE, + XMPSchemaDynamicMedia.class); + nsMappings.put(XMPSchemaPagedText.NAMESPACE, XMPSchemaPagedText.class); + nsMappings.put(XMPSchemaIptc4xmpCore.NAMESPACE, + XMPSchemaIptc4xmpCore.class); + nsMappings.put(XMPSchemaPhotoshop.NAMESPACE, XMPSchemaPhotoshop.class); + } + + /** + * Will add a XMPSchema to the set of identified schemas. + * + * The class needs to have a constructor with parameter Element + * + * @param namespace + * The namespace URI of the schmema for instance + * http://purl.org/dc/elements/1.1/. + * @param xmpSchema + * The schema to associated this identifier with. + */ + public void addXMLNSMapping(String namespace, Class xmpSchema) + { + + if (!(XMPSchema.class.isAssignableFrom(xmpSchema))) + { + throw new IllegalArgumentException( + "Only XMPSchemas can be mapped to."); + } + + nsMappings.put(namespace, xmpSchema); + } + + /** + * Get the PDF Schema. + * + * @return The first PDF schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaPDF getPDFSchema() throws IOException + { + return (XMPSchemaPDF) getSchemaByClass(XMPSchemaPDF.class); + } + + /** + * Get the Basic Schema. + * + * @return The first Basic schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaBasic getBasicSchema() throws IOException + { + return (XMPSchemaBasic) getSchemaByClass(XMPSchemaBasic.class); + } + + /** + * Get the Dublin Core Schema. + * + * @return The first Dublin schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaDublinCore getDublinCoreSchema() throws IOException + { + return (XMPSchemaDublinCore) getSchemaByClass(XMPSchemaDublinCore.class); + } + + /** + * Get the Media Management Schema. + * + * @return The first Media Management schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaMediaManagement getMediaManagementSchema() + throws IOException + { + return (XMPSchemaMediaManagement) getSchemaByClass(XMPSchemaMediaManagement.class); + } + + /** + * Get the Schema Rights Schema. + * + * @return The first Schema Rights schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaRightsManagement getRightsManagementSchema() + throws IOException + { + return (XMPSchemaRightsManagement) getSchemaByClass(XMPSchemaRightsManagement.class); + } + + /** + * Get the Job Ticket Schema. + * + * @return The first Job Ticket schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaBasicJobTicket getBasicJobTicketSchema() throws IOException + { + return (XMPSchemaBasicJobTicket) getSchemaByClass(XMPSchemaBasicJobTicket.class); + } + + /** + * Get the Dynamic Media Schema. + * + * @return The first Dynamic Media schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaDynamicMedia getDynamicMediaSchema() throws IOException + { + return (XMPSchemaDynamicMedia) getSchemaByClass(XMPSchemaDynamicMedia.class); + } + + /** + * Get the Paged Text Schema. + * + * @return The first Paged Text schema in the list. + * + * @throws IOException + * If there is an error accessing the schema. + */ + public XMPSchemaPagedText getPagedTextSchema() throws IOException + { + return (XMPSchemaPagedText) getSchemaByClass(XMPSchemaPagedText.class); + } + + /** + * Add a new Media Management schema. + * + * @return The newly added schema. + */ + public XMPSchemaMediaManagement addMediaManagementSchema() + { + XMPSchemaMediaManagement schema = new XMPSchemaMediaManagement(this); + return (XMPSchemaMediaManagement) basicAddSchema(schema); + } + + /** + * Add a new Rights Managment schema. + * + * @return The newly added schema. + */ + public XMPSchemaRightsManagement addRightsManagementSchema() + { + XMPSchemaRightsManagement schema = new XMPSchemaRightsManagement(this); + return (XMPSchemaRightsManagement) basicAddSchema(schema); + } + + /** + * Add a new Job Ticket schema. + * + * @return The newly added schema. + */ + public XMPSchemaBasicJobTicket addBasicJobTicketSchema() + { + XMPSchemaBasicJobTicket schema = new XMPSchemaBasicJobTicket(this); + return (XMPSchemaBasicJobTicket) basicAddSchema(schema); + } + + /** + * Add a new Dynamic Media schema. + * + * @return The newly added schema. + */ + public XMPSchemaDynamicMedia addDynamicMediaSchema() + { + XMPSchemaDynamicMedia schema = new XMPSchemaDynamicMedia(this); + return (XMPSchemaDynamicMedia) basicAddSchema(schema); + } + + /** + * Add a new Paged Text schema. + * + * @return The newly added schema. + */ + public XMPSchemaPagedText addPagedTextSchema() + { + XMPSchemaPagedText schema = new XMPSchemaPagedText(this); + return (XMPSchemaPagedText) basicAddSchema(schema); + } + + /** + * Add a custom schema to the root rdf. The schema has to have been created + * as a child of this XMPMetadata. + * + * @param schema + * The schema to add. + */ + public void addSchema(XMPSchema schema) + { + Element rdf = getRDFElement(); + rdf.appendChild(schema.getElement()); + } + + /** + * Save the XMP document to a file. + * + * @param file + * The file to save the XMP document to. + * + * @throws Exception + * If there is an error while writing to the stream. + */ + public void save(String file) throws Exception + { + XMLUtil.save(xmpDocument, file, encoding); + } + + /** + * Save the XMP document to a stream. + * + * @param outStream + * The stream to save the XMP document to. + * + * @throws TransformerException + * If there is an error while writing to the stream. + */ + public void save(OutputStream outStream) throws TransformerException + { + XMLUtil.save(xmpDocument, outStream, encoding); + } + + /** + * Get the XML document as a byte array. + * + * @return The metadata as an XML byte stream. + * @throws Exception + * If there is an error creating the stream. + */ + public byte[] asByteArray() throws Exception + { + return XMLUtil.asByteArray(xmpDocument, encoding); + } + + /** + * Get the XML document from this object. + * + * @return This object as an XML document. + */ + public Document getXMPDocument() + { + return xmpDocument; + } + + /** + * Generic add schema method. + * + * @param schema + * The schema to add. + * + * @return The newly added schema. + */ + protected XMPSchema basicAddSchema(XMPSchema schema) + { + Element rdf = getRDFElement(); + rdf.appendChild(schema.getElement()); + return schema; + } + + /** + * Create and add a new PDF Schema to this metadata. Typically a XMP + * document will only have one PDF schema (but multiple are supported) so it + * is recommended that you first check the existence of a PDF scheme by + * using getPDFSchema() + * + * @return A new blank PDF schema that is now part of the metadata. + */ + public XMPSchemaPDF addPDFSchema() + { + XMPSchemaPDF schema = new XMPSchemaPDF(this); + return (XMPSchemaPDF) basicAddSchema(schema); + } + + /** + * Create and add a new Dublin Core Schema to this metadata. Typically a XMP + * document will only have one schema for each type (but multiple are + * supported) so it is recommended that you first check the existence of a + * this scheme by using getDublinCoreSchema() + * + * @return A new blank PDF schema that is now part of the metadata. + */ + public XMPSchemaDublinCore addDublinCoreSchema() + { + XMPSchemaDublinCore schema = new XMPSchemaDublinCore(this); + return (XMPSchemaDublinCore) basicAddSchema(schema); + } + + /** + * Create and add a new Basic Schema to this metadata. Typically a XMP + * document will only have one schema for each type (but multiple are + * supported) so it is recommended that you first check the existence of a + * this scheme by using getDublinCoreSchema() + * + * @return A new blank PDF schema that is now part of the metadata. + */ + public XMPSchemaBasic addBasicSchema() + { + XMPSchemaBasic schema = new XMPSchemaBasic(this); + return (XMPSchemaBasic) basicAddSchema(schema); + } + + /** + * Create and add a new IPTC schema to this metadata. + * + * @return A new blank IPTC schema that is now part of the metadata. + */ + public XMPSchemaIptc4xmpCore addIptc4xmpCoreSchema() + { + XMPSchemaIptc4xmpCore schema = new XMPSchemaIptc4xmpCore(this); + return (XMPSchemaIptc4xmpCore) basicAddSchema(schema); + } + + /** + * Create and add a new Photoshop schema to this metadata. + * + * @return A new blank Photoshop schema that is now part of the metadata. + */ + public XMPSchemaPhotoshop addPhotoshopSchema() + { + XMPSchemaPhotoshop schema = new XMPSchemaPhotoshop(this); + return (XMPSchemaPhotoshop) basicAddSchema(schema); + } + + /** + * The encoding used to write the XML. Default value:UTF-8
See the + * ENCODING_XXX constants + * + * @param xmlEncoding + * The encoding to write the XML as. + */ + public void setEncoding(String xmlEncoding) + { + encoding = xmlEncoding; + } + + /** + * Get the current encoding that will be used to write the XML. + * + * @return The current encoding to write the XML to. + */ + public String getEncoding() + { + return encoding; + } + + /** + * Get the root RDF element. + * + * @return The root RDF element. + */ + private Element getRDFElement() + { + Element rdf = null; + NodeList nodes = xmpDocument.getElementsByTagName("rdf:RDF"); + if (nodes.getLength() > 0) + { + rdf = (Element) nodes.item(0); + } + return rdf; + } + + /** + * Load metadata from the filesystem. + * + * @param file + * The file to load the metadata from. + * + * @return The loaded XMP document. + * + * @throws IOException + * If there is an error reading the data. + */ + public static XMPMetadata load(String file) throws IOException + { + return new XMPMetadata(XMLUtil.parse(file)); + } + + /** + * Load a schema from an input source. + * + * @param is + * The input source to load the schema from. + * + * @return The loaded/parsed schema. + * + * @throws IOException + * If there was an error while loading the schema. + */ + public static XMPMetadata load(InputSource is) throws IOException + { + return new XMPMetadata(XMLUtil.parse(is)); + } + + /** + * Load metadata from the filesystem. + * + * @param is + * The stream to load the data from. + * + * @return The loaded XMP document. + * + * @throws IOException + * If there is an error reading the data. + */ + public static XMPMetadata load(InputStream is) throws IOException + { + return new XMPMetadata(XMLUtil.parse(is)); + } + + /** + * Test main program. + * + * @param args + * The command line arguments. + * @throws Exception + * If there is an error. + */ + public static void main(String[] args) throws Exception + { + XMPMetadata metadata = new XMPMetadata(); + XMPSchemaPDF pdf = metadata.addPDFSchema(); + pdf.setAbout("uuid:b8659d3a-369e-11d9-b951-000393c97fd8"); + pdf.setKeywords("ben,bob,pdf"); + pdf.setPDFVersion("1.3"); + pdf.setProducer("Acrobat Distiller 6.0.1 for Macintosh"); + + XMPSchemaDublinCore dc = metadata.addDublinCoreSchema(); + dc.addContributor("Ben Litchfield"); + dc.addContributor("Solar Eclipse"); + dc.addContributor("Some Other Guy"); + + XMPSchemaBasic basic = metadata.addBasicSchema(); + Thumbnail t = new Thumbnail(metadata); + t.setFormat(Thumbnail.FORMAT_JPEG); + t.setImage("IMAGE_DATA"); + t.setHeight(new Integer(100)); + t.setWidth(new Integer(200)); + basic.setThumbnail(t); + basic.setBaseURL("http://www.pdfbox.org/"); + + List schemas = metadata.getSchemas(); + System.out.println("schemas=" + schemas); + + metadata.save("test.xmp"); + } + + /** + * This will get a list of XMPSchema(or subclass) objects. + * + * @return A non null read-only list of schemas that are part of this + * metadata. + * + * @throws IOException + * If there is an error creating a specific schema. + */ + public List getSchemas() throws IOException + { + NodeList schemaList = xmpDocument + .getElementsByTagName("rdf:Description"); + List retval = new ArrayList(schemaList.getLength()); + for (int i = 0; i < schemaList.getLength(); i++) + { + Element schema = (Element) schemaList.item(i); + boolean found = false; + NamedNodeMap attributes = schema.getAttributes(); + for (int j = 0; j < attributes.getLength(); j++) + { + Node attribute = attributes.item(j); + String name = attribute.getNodeName(); + String value = attribute.getNodeValue(); + if (name.startsWith("xmlns:") && nsMappings.containsKey(value)) + { + Class schemaClass = nsMappings.get(value); + try + { + Constructor ctor = schemaClass + .getConstructor(new Class[] { Element.class, + String.class }); + retval.add((XMPSchema)ctor.newInstance(new Object[] { schema, + name.substring(6) })); + found = true; + } + catch(NoSuchMethodException e) + { + throw new IOException( + "Error: Class " + + schemaClass.getName() + + " must have a constructor with the signature of " + + schemaClass.getName() + + "( org.w3c.dom.Element, java.lang.String )"); + } + catch(Exception e) + { + e.printStackTrace(); + throw new IOException(e.getMessage()); + } + } + } + if (!found) + { + retval.add(new XMPSchema(schema, null)); + } + } + return retval; + } + + /** + * Will return all schemas that fit the given namespaceURI. Which is only + * done by using the namespace mapping (nsMapping) and not by actually + * checking the xmlns property. + * + * @param namespaceURI + * The namespaceURI to filter for. + * @return A list containing the found schemas or an empty list if non are + * found or the namespaceURI could not be found in the namespace + * mapping. + * @throws IOException + * If an operation on the document fails. + */ + public List getSchemasByNamespaceURI(String namespaceURI) + throws IOException + { + + List l = getSchemas(); + List result = new LinkedList(); + + Class schemaClass = nsMappings.get(namespaceURI); + if (schemaClass == null) + { + return result; + } + + Iterator i = l.iterator(); + while (i.hasNext()) + { + XMPSchema schema = i.next(); + + if (schemaClass.isAssignableFrom(schema.getClass())) + { + result.add(schema); + } + } + return result; + } + + /** + * This will return true if the XMP contains an unknown schema. + * + * @return True if an unknown schema is found, false otherwise + * + * @throws IOException + * If there is an error + */ + public boolean hasUnknownSchema() throws IOException + { + NodeList schemaList = xmpDocument + .getElementsByTagName("rdf:Description"); + for (int i = 0; i < schemaList.getLength(); i++) + { + Element schema = (Element) schemaList.item(i); + NamedNodeMap attributes = schema.getAttributes(); + for (int j = 0; j < attributes.getLength(); j++) + { + Node attribute = attributes.item(j); + String name = attribute.getNodeName(); + String value = attribute.getNodeValue(); + if (name.startsWith("xmlns:") && !nsMappings.containsKey(value) + && !value.equals(ResourceEvent.NAMESPACE)) + { + return true; + } + } + } + return false; + } + + /** + * Tries to retrieve a schema from this by classname. + * + * @param targetSchema + * Class for targetSchema. + * + * @return XMPSchema or null if no target is found. + * + * @throws IOException + * if there was an error creating the schemas of this. + */ + public XMPSchema getSchemaByClass(Class targetSchema) throws IOException + { + Iterator iter = getSchemas().iterator(); + while (iter.hasNext()) + { + XMPSchema element = (XMPSchema) iter.next(); + if (element.getClass().getName().equals(targetSchema.getName())) + { + return element; + } + } + // not found + return null; + } + + /** + * Merge this metadata with the given metadata object. + * + * @param metadata The metadata to merge with this document. + * + * @throws IOException If there is an error merging the data. + */ + public void merge(XMPMetadata metadata) throws IOException + { + List schemas2 = metadata.getSchemas(); + for (Iterator iterator = schemas2.iterator(); iterator.hasNext();) + { + XMPSchema schema2 = iterator.next(); + XMPSchema schema1 = getSchemaByClass(schema2.getClass()); + if (schema1 == null) + { + Element rdf = getRDFElement(); + rdf.appendChild(xmpDocument.importNode(schema2.getElement(), + true)); + } + else + { + schema1.merge(schema2); + } + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchema.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchema.java new file mode 100644 index 000000000..eb8feec5f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchema.java @@ -0,0 +1,1084 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.jempbox.impl.DateConverter; +import org.apache.jempbox.impl.XMLUtil; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * This class represents a metadata schema that can be stored in an XMP + * document. It handles all generic properties that are available. See + * subclasses for access to specific properties. + * + * @author Ben Litchfield + * @version $Revision: 1.8 $ + */ +public class XMPSchema +{ + /** + * The standard xmlns namespace. + */ + public static final String NS_NAMESPACE = "http://www.w3.org/2000/xmlns/"; + + /** + * The XML schema prefix. + */ + protected String prefix; + + /** + * The DOM representation of this object. + */ + protected Element schema = null; + + /** + * Create a new blank schema that can be populated. + * + * @param parent + * The parent XMP document that this schema will be part of. + * @param namespaceName + * The name of the namespace, ie pdf,dc,... + * @param namespaceURI + * The URI of the namespace, ie "http://ns.adobe.com/pdf/1.3/" + */ + public XMPSchema(XMPMetadata parent, String namespaceName, + String namespaceURI) + { + schema = parent.xmpDocument.createElementNS( + "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdf:Description"); + prefix = namespaceName; + schema.setAttributeNS(NS_NAMESPACE, "xmlns:" + namespaceName, + namespaceURI); + } + + /** + * Create schema from an existing XML element. + * + * @param element + * The existing XML element. + * @param aPrefix + * The XML prefix. + */ + public XMPSchema(Element element, String aPrefix) + { + schema = element; + if (aPrefix != null) + { + prefix = aPrefix; + } + else + { + prefix = ""; + } + } + + /** + * Get the XML element that is represented by this schema. + * + * @return The root XML element of this schema. + */ + public Element getElement() + { + return schema; + } + + /** + * Get the RDF about attribute. + * + * @return The RDF 'about' attribute. + */ + public String getAbout() + { + return getTextProperty("rdf:about"); + } + + /** + * Set the RDF 'about' attribute. Passing in null will clear this attribute. + * + * @param about + * The new RFD about value. + */ + public void setAbout(String about) + { + if (about == null) + { + schema.removeAttribute("rdf:about"); + } + else + { + schema.setAttribute("rdf:about", about); + } + } + + /** + * Set a simple text property on the schema. + * + * @param propertyName + * The name of the property, it must contain the namespace + * prefix, ie "pdf:Keywords" + * @param propertyValue + * The value for the property, can be any string. Passing null + * will remove the property. + */ + public void setTextProperty(String propertyName, String propertyValue) + { + if (propertyValue == null) + { + schema.removeAttribute(propertyName); + NodeList keywordList = schema.getElementsByTagName(propertyName); + for (int i = 0; i < keywordList.getLength(); i++) + { + schema.removeChild(keywordList.item(i)); + } + + } + else + { + if (schema.hasAttribute(propertyName)) + { + schema.setAttribute(propertyName, propertyValue); + } + else + { + if (schema.hasChildNodes()) + { + NodeList nodeList = schema + .getElementsByTagName(propertyName); + if (nodeList.getLength() > 0) + { + Element node = (Element) nodeList.item(0); + node.setNodeValue(propertyValue); + } + else + { + Element textNode = schema.getOwnerDocument() + .createElement(propertyName); + XMLUtil.setStringValue(textNode, propertyValue); + schema.appendChild(textNode); + } + } + else + { + schema.setAttribute(propertyName, propertyValue); + } + } + } + } + + /** + * Get the value of a simple text property. + * + * @param propertyName + * The name of the property to get, it must include the namespace + * prefix. ie "pdf:Keywords". + * + * @return The value of the text property or the null if there is no value. + */ + public String getTextProperty(String propertyName) + { + // propertyValue == null does not work, since getAttribute returns the + // empty string if the attribute is not found + + if (schema.hasAttribute(propertyName)) + { + return schema.getAttribute(propertyName); + } + else + { + NodeList nodes = schema.getElementsByTagName(propertyName); + if (nodes.getLength() > 0) + { + Element node = (Element) nodes.item(0); + return XMLUtil.getStringValue(node); + } + return null; + } + } + + /** + * Get the value of the property as a date. + * + * @param propertyName + * The fully qualified property name for the date. + * + * @return The value of the property as a date. + * + * @throws IOException + * If there is an error converting the value to a date. + */ + public Calendar getDateProperty(String propertyName) throws IOException + { + return DateConverter.toCalendar(getTextProperty(propertyName)); + } + + /** + * Set the value of the property as a date. + * + * @param propertyName + * The fully qualified property name for the date. + * @param date + * The date to set, or null to clear. + */ + public void setDateProperty(String propertyName, Calendar date) + { + setTextProperty(propertyName, DateConverter.toISO8601(date)); + } + + /** + * Get the value of the property as a boolean. + * + * @param propertyName + * The fully qualified property name for the boolean. + * + * @return The value of the property as a boolean. + */ + public Boolean getBooleanProperty(String propertyName) + { + Boolean value = null; + String stringValue = getTextProperty(propertyName); + if (stringValue != null) + { + value = stringValue.equals("True") ? Boolean.TRUE : Boolean.FALSE; + } + return value; + } + + /** + * Set the value of the property as a boolean. + * + * @param propertyName + * The fully qualified property name for the boolean. + * @param bool + * The boolean to set, or null to clear. + */ + public void setBooleanProperty(String propertyName, Boolean bool) + { + String value = null; + if (bool != null) + { + value = bool.booleanValue() ? "True" : "False"; + } + setTextProperty(propertyName, value); + } + + /** + * Get the value of the property as an integer. + * + * @param propertyName + * The fully qualified property name for the integer. + * + * @return The value of the property as an integer. + */ + public Integer getIntegerProperty(String propertyName) + { + Integer retval = null; + String intProperty = getTextProperty(propertyName); + if (intProperty != null && intProperty.length() > 0) + { + retval = new Integer(intProperty); + } + return retval; + } + + /** + * Set the value of the property as an integer. + * + * @param propertyName + * The fully qualified property name for the integer. + * @param intValue + * The int to set, or null to clear. + */ + public void setIntegerProperty(String propertyName, Integer intValue) + { + String textValue = null; + if (intValue != null) + { + textValue = intValue.toString(); + } + setTextProperty(propertyName, textValue); + } + + /** + * Remove all matching entries with the given value from the bag. + * + * @param bagName + * The name of the bag, it must include the namespace prefix. ie + * "pdf:Keywords". + * @param bagValue + * The value to remove from the bagList. + */ + public void removeBagValue(String bagName, String bagValue) + { + Element bagElement = null; + NodeList nodes = schema.getElementsByTagName(bagName); + if (nodes.getLength() > 0) + { + Element contElement = (Element) nodes.item(0); + NodeList bagList = contElement.getElementsByTagName("rdf:Bag"); + if (bagList.getLength() > 0) + { + bagElement = (Element) bagList.item(0); + NodeList items = bagElement.getElementsByTagName("rdf:li"); + for (int i = items.getLength() - 1; i >= 0; i--) + { + Element li = (Element) items.item(i); + String value = XMLUtil.getStringValue(li); + if (value.equals(bagValue)) + { + bagElement.removeChild(li); + } + } + } + } + } + + /** + * Add an entry to a bag property. + * + * @param bagName + * The name of the bag, it must include the namespace prefix. ie + * "pdf:Keywords". + * @param bagValue + * The value to add to the bagList. + */ + public void addBagValue(String bagName, String bagValue) + { + Element bagElement = null; + NodeList nodes = schema.getElementsByTagName(bagName); + if (nodes.getLength() > 0) + { + Element contElement = (Element) nodes.item(0); + NodeList bagList = contElement.getElementsByTagName("rdf:Bag"); + if (bagList.getLength() > 0) + { + bagElement = (Element) bagList.item(0); + } + } + else + { + Element contElement = schema.getOwnerDocument().createElement( + bagName); + schema.appendChild(contElement); + bagElement = schema.getOwnerDocument().createElement("rdf:Bag"); + contElement.appendChild(bagElement); + } + Element liElement = schema.getOwnerDocument().createElement("rdf:li"); + XMLUtil.setStringValue(liElement, bagValue); + bagElement.appendChild(liElement); + } + + /** + * Get all the values of the bag property. This will return a list of + * java.lang.String objects, this is a read-only list. + * + * @param bagName + * The name of the bag property to get, it must include the + * namespace prefix. ie "pdf:Keywords" + * + * @return All of the values of the bag property in a list. + */ + public List getBagList(String bagName) + { + List retval = null; + NodeList nodes = schema.getElementsByTagName(bagName); + if (nodes.getLength() > 0) + { + Element contributor = (Element) nodes.item(0); + NodeList bagList = contributor.getElementsByTagName("rdf:Bag"); + if (bagList.getLength() > 0) + { + Element bag = (Element) bagList.item(0); + retval = new ArrayList(); + NodeList items = bag.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + retval.add(XMLUtil.getStringValue(li)); + } + retval = Collections.unmodifiableList(retval); + } + } + + return retval; + } + + /** + * Remove all matching values from a sequence property. + * + * @param seqName + * The name of the sequence property. It must include the + * namespace prefix. ie "pdf:Keywords". + * @param seqValue + * The value to remove from the list. + */ + public void removeSequenceValue(String seqName, String seqValue) + { + Element bagElement = null; + NodeList nodes = schema.getElementsByTagName(seqName); + if (nodes.getLength() > 0) + { + Element contElement = (Element) nodes.item(0); + NodeList bagList = contElement.getElementsByTagName("rdf:Seq"); + if (bagList.getLength() > 0) + { + bagElement = (Element) bagList.item(0); + NodeList items = bagElement.getElementsByTagName("rdf:li"); + for (int i = items.getLength() - 1; i >= 0; i--) + { + Element li = (Element) items.item(i); + String value = XMLUtil.getStringValue(li); + if (value.equals(seqValue)) + { + bagElement.removeChild(li); + } + } + } + } + } + + /** + * Remove a value from a sequence property. This will remove all entries + * from the list. + * + * @param seqName + * The name of the sequence property. It must include the + * namespace prefix. ie "pdf:Keywords". + * @param seqValue + * The value to remove from the list. + */ + public void removeSequenceValue(String seqName, Elementable seqValue) + { + Element bagElement = null; + NodeList nodes = schema.getElementsByTagName(seqName); + if (nodes.getLength() > 0) + { + Element contElement = (Element) nodes.item(0); + NodeList bagList = contElement.getElementsByTagName("rdf:Seq"); + if (bagList.getLength() > 0) + { + bagElement = (Element) bagList.item(0); + NodeList items = bagElement.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + if (li == seqValue.getElement()) + { + bagElement.removeChild(li); + } + } + } + } + } + + /** + * Add a new value to a sequence property. + * + * @param seqName + * The name of the sequence property, it must include the + * namespace prefix. ie "pdf:Keywords" + * @param seqValue + * The value to add to the sequence. + */ + public void addSequenceValue(String seqName, String seqValue) + { + Element bagElement = null; + NodeList nodes = schema.getElementsByTagName(seqName); + if (nodes.getLength() > 0) + { + Element contElement = (Element) nodes.item(0); + NodeList bagList = contElement.getElementsByTagName("rdf:Seq"); + if (bagList.getLength() > 0) + { + bagElement = (Element) bagList.item(0); + } + else + { + // xml is crap discard it + schema.removeChild(nodes.item(0)); + } + } + if (bagElement == null) + { + Element contElement = schema.getOwnerDocument().createElement( + seqName); + schema.appendChild(contElement); + bagElement = schema.getOwnerDocument().createElement("rdf:Seq"); + contElement.appendChild(bagElement); + } + Element liElement = schema.getOwnerDocument().createElement("rdf:li"); + liElement.appendChild(schema.getOwnerDocument() + .createTextNode(seqValue)); + bagElement.appendChild(liElement); + } + + /** + * Add a new value to a sequence property. + * + * @param seqName + * The name of the sequence property, it must include the + * namespace prefix. ie "pdf:Keywords" + * @param seqValue + * The value to add to the sequence. + */ + public void addSequenceValue(String seqName, Elementable seqValue) + { + Element bagElement = null; + NodeList nodes = schema.getElementsByTagName(seqName); + if (nodes.getLength() > 0) + { + Element contElement = (Element) nodes.item(0); + NodeList bagList = contElement.getElementsByTagName("rdf:Seq"); + if (bagList.getLength() > 0) + { + bagElement = (Element) bagList.item(0); + } + } + else + { + Element contElement = schema.getOwnerDocument().createElement( + seqName); + schema.appendChild(contElement); + bagElement = schema.getOwnerDocument().createElement("rdf:Seq"); + contElement.appendChild(bagElement); + } + bagElement.appendChild(seqValue.getElement()); + } + + /** + * Get all the values in a sequence property. + * + * @param seqName + * The name of the sequence property, it must include the + * namespace prefix. ie "pdf:Keywords". + * + * @return A read-only list of java.lang.String objects or null if the + * property does not exist. + */ + public List getSequenceList(String seqName) + { + List retval = null; + NodeList nodes = schema.getElementsByTagName(seqName); + if (nodes.getLength() > 0) + { + Element contributor = (Element) nodes.item(0); + NodeList bagList = contributor.getElementsByTagName("rdf:Seq"); + if (bagList.getLength() > 0) + { + Element bag = (Element) bagList.item(0); + retval = new ArrayList(); + NodeList items = bag.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + retval.add(XMLUtil.getStringValue(li)); + } + retval = Collections.unmodifiableList(retval); + } + } + + return retval; + } + + /** + * Get a list of ResourceEvent objects. + * + * @param seqName + * The name of the sequence to retrieve. + * + * @return A list of ResourceEvent objects or null if they do not exist. + */ + public List getEventSequenceList(String seqName) + { + List retval = null; + NodeList nodes = schema.getElementsByTagName(seqName); + if (nodes.getLength() > 0) + { + Element contributor = (Element) nodes.item(0); + NodeList bagList = contributor.getElementsByTagName("rdf:Seq"); + if (bagList.getLength() > 0) + { + Element bag = (Element) bagList.item(0); + retval = new ArrayList(); + NodeList items = bag.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + retval.add(new ResourceEvent(li)); + } + retval = Collections.unmodifiableList(retval); + } + } + + return retval; + } + + /** + * Remove a date sequence value from the list. + * + * @param seqName + * The name of the sequence property, it must include the + * namespace prefix. ie "pdf:Keywords" + * @param date + * The date to remove from the sequence property. + */ + public void removeSequenceDateValue(String seqName, Calendar date) + { + String dateAsString = DateConverter.toISO8601(date); + removeSequenceValue(seqName, dateAsString); + } + + /** + * Add a date sequence value to the list. + * + * @param seqName + * The name of the sequence property, it must include the + * namespace prefix. ie "pdf:Keywords" + * @param date + * The date to add to the sequence property. + */ + public void addSequenceDateValue(String seqName, Calendar date) + { + String dateAsString = DateConverter.toISO8601(date); + addSequenceValue(seqName, dateAsString); + } + + /** + * Get all the date values in a sequence property. + * + * @param seqName + * The name of the sequence property, it must include the + * namespace prefix. ie "pdf:Keywords". + * + * @return A read-only list of java.util.Calendar objects or null if the + * property does not exist. + * + * @throws IOException + * If there is an error converting the value to a date. + */ + public List getSequenceDateList(String seqName) throws IOException + { + List strings = getSequenceList(seqName); + List retval = null; + if (strings != null) + { + retval = new ArrayList(); + for (int i = 0; i < strings.size(); i++) + { + retval.add(DateConverter.toCalendar(strings.get(i))); + } + } + return retval; + } + + /** + * Set the value of a multi-lingual property. + * + * @param propertyName + * The name of the property, it must include the namespace + * prefix. ie "pdf:Keywords" + * @param language + * The language code of the value. If null then "x-default" is + * assumed. + * @param value + * The value of the property in the specified language. + */ + public void setLanguageProperty(String propertyName, String language, + String value) + { + NodeList nodes = schema.getElementsByTagName(propertyName); + Element property = null; + if (nodes.getLength() == 0) + { + if (value == null) + { + // value is null, it doesn't already exist so there + // is nothing to do. + return; + } + property = schema.getOwnerDocument().createElement(propertyName); + schema.appendChild(property); + } + else + { + property = (Element) nodes.item(0); + } + Element alt = null; + NodeList altList = property.getElementsByTagName("rdf:Alt"); + if (altList.getLength() == 0) + { + if (value == null) + { + // value is null, it doesn't already exist so there + // is nothing to do. + return; + } + alt = schema.getOwnerDocument().createElement("rdf:Alt"); + property.appendChild(alt); + } + else + { + alt = (Element) altList.item(0); + } + NodeList items = alt.getElementsByTagName("rdf:li"); + if (language == null) + { + language = "x-default"; + } + boolean foundValue = false; + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + if (value == null) + { + alt.removeChild(li); + } + else if (language.equals(li.getAttribute("xml:lang"))) + { + foundValue = true; + XMLUtil.setStringValue(li, value); + } + } + if (value != null && !foundValue) + { + Element li = schema.getOwnerDocument().createElement("rdf:li"); + li.setAttribute("xml:lang", language); + XMLUtil.setStringValue(li, value); + if (language.equals("x-default")) + { + // default should be first element, see XMP spec + alt.insertBefore(li, alt.getFirstChild()); + } + else + { + alt.appendChild(li); + } + + } + } + + /** + * Get the value of a multi-lingual property. + * + * @param propertyName + * The name of the property, it must include the namespace + * prefix. ie "pdf:Keywords" + * @param language + * The language code of the value. If null then "x-default" is + * assumed. + * + * @return The value of the language property. + */ + public String getLanguageProperty(String propertyName, String language) + { + String retval = null; + if (language == null) + { + language = "x-default"; + } + + NodeList nodes = schema.getElementsByTagName(propertyName); + if (nodes.getLength() > 0) + { + Element property = (Element) nodes.item(0); + NodeList altList = property.getElementsByTagName("rdf:Alt"); + if (altList.getLength() > 0) + { + Element alt = (Element) altList.item(0); + NodeList items = alt.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength() && retval == null; i++) + { + Element li = (Element) items.item(i); + String elementLanguage = li.getAttribute("xml:lang"); + if (language.equals(elementLanguage)) + { + retval = XMLUtil.getStringValue(li); + } + } + } + else if (property.getChildNodes().getLength() == 1 && Node.TEXT_NODE == property.getFirstChild().getNodeType()) + { + retval = property.getFirstChild().getNodeValue(); + } + } + return retval; + } + + /** + * Set the value of a multi-lingual property. + * + * @param propertyName + * The name of the property, it must include the namespace + * prefix. ie "pdf:Keywords" + * @param language + * The language code of the value. If null then "x-default" is + * assumed. + * @param value + * The value of the property in the specified language. + */ + public void setThumbnailProperty(String propertyName, String language, + Thumbnail value) + { + NodeList nodes = schema.getElementsByTagName(propertyName); + Element property = null; + if (nodes.getLength() == 0) + { + if (value == null) + { + // value is null, it doesn't already exist so there + // is nothing to do. + return; + } + property = schema.getOwnerDocument().createElement(propertyName); + schema.appendChild(property); + } + else + { + property = (Element) nodes.item(0); + } + Element alt = null; + NodeList altList = property.getElementsByTagName("rdf:Alt"); + if (altList.getLength() == 0) + { + if (value == null) + { + // value is null, it doesn't already exist so there + // is nothing to do. + return; + } + alt = schema.getOwnerDocument().createElement("rdf:Alt"); + property.appendChild(alt); + } + else + { + alt = (Element) altList.item(0); + } + NodeList items = alt.getElementsByTagName("rdf:li"); + if (language == null) + { + language = "x-default"; + } + boolean foundValue = false; + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + if (value == null) + { + alt.removeChild(li); + } + else if (language.equals(li.getAttribute("xml:lang"))) + { + foundValue = true; + alt.replaceChild(li, value.getElement()); + } + } + if (value != null && !foundValue) + { + Element li = value.getElement(); + li.setAttribute("xml:lang", language); + if (language.equals("x-default")) + { + // default should be first element, see XMP spec + alt.insertBefore(li, alt.getFirstChild()); + } + else + { + alt.appendChild(li); + } + + } + } + + /** + * Get the value of a multi-lingual property. + * + * @param propertyName + * The name of the property, it must include the namespace + * prefix. ie "pdf:Keywords" + * @param language + * The language code of the value. If null then "x-default" is + * assumed. + * + * @return The value of the language property. + */ + public Thumbnail getThumbnailProperty(String propertyName, String language) + { + Thumbnail retval = null; + if (language == null) + { + language = "x-default"; + } + + NodeList nodes = schema.getElementsByTagName(propertyName); + if (nodes.getLength() > 0) + { + Element property = (Element) nodes.item(0); + NodeList altList = property.getElementsByTagName("rdf:Alt"); + if (altList.getLength() > 0) + { + Element alt = (Element) altList.item(0); + NodeList items = alt.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength() && retval == null; i++) + { + Element li = (Element) items.item(i); + String elementLanguage = li.getAttribute("xml:lang"); + if (language.equals(elementLanguage)) + { + retval = new Thumbnail(li); + } + } + } + } + return retval; + } + + /** + * Get a list of all languages that are currently defined for a specific + * property. + * + * @param propertyName + * The name of the property, it must include the namespace + * prefix. ie "pdf:Keywords" + * + * @return A list of all languages, this will return an non-null empty list + * if none have been defined. + */ + public List getLanguagePropertyLanguages(String propertyName) + { + List retval = new ArrayList(); + + NodeList nodes = schema.getElementsByTagName(propertyName); + if (nodes.getLength() > 0) + { + Element property = (Element) nodes.item(0); + NodeList altList = property.getElementsByTagName("rdf:Alt"); + if (altList.getLength() > 0) + { + Element alt = (Element) altList.item(0); + NodeList items = alt.getElementsByTagName("rdf:li"); + for (int i = 0; i < items.getLength(); i++) + { + Element li = (Element) items.item(i); + String elementLanguage = li.getAttribute("xml:lang"); + if (elementLanguage == null) + { + retval.add("x-default"); + } + else + { + retval.add(elementLanguage); + } + } + } + } + return retval; + } + + /** + * A basic schema merge, it merges bags and sequences and replace everything + * else. + * + * @param xmpSchema The schema to merge. + * @throws IOException If there is an error during the merge. + */ + public void merge(XMPSchema xmpSchema) throws IOException + { + if (!xmpSchema.getClass().equals(this.getClass())) + { + throw new IOException("Can only merge schemas of the same type."); + } + + NamedNodeMap attributes = xmpSchema.getElement().getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Node a = attributes.item(i); + String name = a.getNodeName(); + if (name.startsWith(prefix)) + { + String newValue = xmpSchema.getTextProperty(name); + setTextProperty(name, newValue); + } + } + NodeList nodes = xmpSchema.getElement().getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) + { + Node a = nodes.item(i); + String name = a.getNodeName(); + if (name.startsWith(prefix)) + { + if (a instanceof Element) + { + Element e = (Element) a; + if (nodes.getLength() > 0) + { + NodeList seqList = e.getElementsByTagName("rdf:Seq"); + if (seqList.getLength() > 0) + { + List newList = xmpSchema.getSequenceList(name); + List oldList = getSequenceList(name); + + Iterator it = newList.iterator(); + + while (it.hasNext()) + { + String object = it.next(); + if (oldList == null + || !oldList.contains(object)) + { + addSequenceValue(name, object); + } + } + continue; + } + NodeList bagList = e.getElementsByTagName("rdf:Bag"); + if (bagList.getLength() > 0) + { + List newList = xmpSchema.getBagList(name); + List oldList = getBagList(name); + + Iterator it = newList.iterator(); + + while (it.hasNext()) + { + String object = it.next(); + if (oldList == null + || !oldList.contains(object)) + { + addBagValue(name, object); + } + } + continue; + } + } + } + String newValue = xmpSchema.getTextProperty(name); + setTextProperty(name, newValue); + } + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasic.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasic.java new file mode 100644 index 000000000..6dd1023e0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasic.java @@ -0,0 +1,374 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.io.IOException; +import java.util.Calendar; +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Define XMP properties that are common to all schemas. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class XMPSchemaBasic extends XMPSchema +{ + /** + * The namespace of this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/"; + + /** + * Construct a new blank PDF schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaBasic( XMPMetadata parent ) + { + super( parent, "xmp", NAMESPACE ); + schema.setAttributeNS( + NS_NAMESPACE, + "xmlns:xapGImg", + "http://ns.adobe.com/xap/1.0/g/img/" ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaBasic( Element element, String prefix ) + { + super( element, prefix ); + if( schema.getAttribute( "xmlns:xapGImg" ) == null ) + { + schema.setAttributeNS( + NS_NAMESPACE, + "xmlns:xapGImg", + "http://ns.adobe.com/xap/1.0/g/img/" ); + } + } + + /** + * Remove an Advisory xpath expression. + * + * @param advisory An xpath expression specifying properties that + * were edited outside of the authoring application. + */ + public void removeAdvisory( String advisory ) + { + removeBagValue( prefix + ":Advisory", advisory ); + } + + /** + * Add an advisory to the list. + * + * @param advisory The new advisory xpath expression. + */ + public void addAdvisory( String advisory ) + { + addBagValue( prefix + ":Advisory", advisory ); + } + + /** + * Get the complete list of advisories. + * + * @return The list of advisories. + */ + public List getAdvisories() + { + return getBagList( prefix + ":Advisory" ); + } + + /** + * The base URL of the resource, for relative URLs in the document. + * + * @param url The base URL. + */ + public void setBaseURL( String url ) + { + setTextProperty( prefix + ":BaseURL", url ); + } + + /** + * Get the base URL of the resource. + * + * @return The base URL. + */ + public String getBaseURL() + { + return getTextProperty( prefix + ":BaseURL" ); + } + + /** + * Set the creation date of the resource. + * + * @param date The creation date of the resource. + */ + public void setCreateDate( Calendar date ) + { + setDateProperty( prefix + ":CreateDate", date ); + } + + /** + * Get the creation date of the resource. + * + * @return The creation date of the resource. + * + * @throws IOException If there is an error while converting this property to + * a date. + */ + public Calendar getCreateDate() throws IOException + { + return getDateProperty( prefix + ":CreateDate" ); + } + + /** + * The creator tool for the resource. In the form of "vendor app version", ie + * "Adobe Acrobat Distiller 5.0" + * + * @param creator The tool that was used to create the resource. + */ + public void setCreatorTool( String creator ) + { + setTextProperty( prefix + ":CreatorTool", creator ); + } + + /** + * Get the tool that created this resource, in the form of "vendor app version", ie + * "Adobe Acrobat Distiller 5.0". + * + * @return The creator tool. + */ + public String getCreatorTool() + { + return getTextProperty( prefix + ":CreatorTool" ); + } + + /** + * Remove an identifier to this resource. + * + * @param id An identifier to this resource. + */ + public void removeIdentifier( String id ) + { + removeBagValue( prefix + ":Identifier", id ); + } + + /** + * Add a new identifier for this resource. + * + * @param id A new identifier for this resource. + */ + public void addIdentifier( String id ) + { + addBagValue( prefix + ":Identifier", id ); + } + + /** + * Get the complete list of identifiers. + * + * @return The list of identifiers. + */ + public List getIdentifiers() + { + return getBagList( prefix + ":Identifier" ); + } + + /** + * Set a short phrase that identifies this resource. + * + * @param label A short description of this resource. + */ + public void setLabel( String label ) + { + setTextProperty( prefix + ":Label", label ); + } + + /** + * Get the short phrase that describes this resource. + * + * @return The label for this resource. + */ + public String getLabel() + { + return getTextProperty( prefix + "p:Label" ); + } + + /** + * Set a Title for this resource. + * + * @param title A title denoting this resource + */ + public void setTitle( String title ) + { + setTextProperty( prefix + ":Title", title); + } + + /** + * Get the title for this resource. + * + * @return The titled denoting this resource. + */ + public String getTitle() + { + return getTextProperty( prefix + ":Title" ); + } + + /** + * Set the date that any metadata was updated. + * + * @param date The metadata change date for this resource. + */ + public void setMetadataDate( Calendar date ) + { + setDateProperty( prefix + ":MetadataDate", date ); + } + + /** + * Get the metadata change date for this resource. + * + * @return The metadata change date of the resource. + * + * @throws IOException If there is an error while converting this property to + * a date. + */ + public Calendar getMetadataDate() throws IOException + { + return getDateProperty( prefix + ":MetadataDate" ); + } + + /** + * Set the date that the resource was last modified. + * + * @param date The modify date for this resource. + */ + public void setModifyDate( Calendar date ) + { + setDateProperty( prefix + ":ModifyDate", date ); + } + + /** + * Get the date the resource was last modified. + * + * @return The modify date of the resource. + * + * @throws IOException If there is an error while converting this property to + * a date. + */ + public Calendar getModifyDate() throws IOException + { + return getDateProperty( prefix + ":ModifyDate" ); + } + + /** + * Set a short informal name for the resource. + * + * @param nickname A short name of this resource. + */ + public void setNickname( String nickname ) + { + setTextProperty( prefix + ":Nickname", nickname ); + } + + /** + * Get the short informal name for this resource. + * + * @return The short name for this resource. + */ + public String getNickname() + { + return getTextProperty( prefix + ":Nickname" ); + } + + /** + * Get a number that indicates the documents status. + * + * @return The rating of the document. + */ + public Integer getRating() + { + return getIntegerProperty( prefix + ":Rating" ); + } + + /** + * Set the document status. + * + * @param rating A number indicating status relative to other documents. + */ + public void setRating( Integer rating ) + { + setIntegerProperty( prefix + ":Rating", rating ); + } + + /** + * Set the default value for the thumbnail. + * + * @param thumbnail The thumbnail of this resource. + */ + public void setThumbnail( Thumbnail thumbnail ) + { + setThumbnailProperty( prefix + ":Thumbnails", null, thumbnail ); + } + + /** + * Get the default value for the thumbnail. + * + * @return The thumbnail of this resource. + */ + public Thumbnail getThumbnail() + { + return getThumbnailProperty( prefix + ":Thumbnails", null ); + } + + /** + * Set the thumbnail of this resource in a specific language. + * + * @param language The language code. + * @param thumbnail The thumbnail in a specific language. + */ + public void setThumbnail( String language, Thumbnail thumbnail ) + { + setThumbnailProperty( prefix + ":Thumbnails", language, thumbnail ); + } + + /** + * Get the thumbnail in a specific language. + * + * @param language The language code to get the description for. + * + * @return The thumbnail in the specified language or null if it does not exist. + */ + public Thumbnail getThumbnail( String language ) + { + return getThumbnailProperty( prefix + ":Thumbnails", language ); + } + + /** + * Get a list of all languages that a thumbnail exists for. + * + * @return A non-null list of languages, potentially an empty list. + */ + public List getThumbnailLanguages() + { + return getLanguagePropertyLanguages( prefix + ":Thumbnails" ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasicJobTicket.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasicJobTicket.java new file mode 100644 index 000000000..17a9c63a8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaBasicJobTicket.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.w3c.dom.Element; + +/** + * Implementation of Basic Job Ticket Schema. + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.2 $ + */ +public class XMPSchemaBasicJobTicket extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/bj/"; + + /** + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaBasicJobTicket( XMPMetadata parent ) + { + super( parent, "xmpBJ", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaBasicJobTicket( Element element, String prefix ) + { + super( element , prefix); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDublinCore.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDublinCore.java new file mode 100644 index 000000000..482e9119d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDublinCore.java @@ -0,0 +1,531 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.io.IOException; +import java.util.Calendar; +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Define XMP properties used with the Dublin Core specification. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class XMPSchemaDublinCore extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://purl.org/dc/elements/1.1/"; + /** + * Construct a new blank Dublin Core schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaDublinCore( XMPMetadata parent ) + { + super( parent, "dc", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaDublinCore( Element element, String prefix ) + { + super( element, prefix ); + } + + /** + * Remove a contributor from the list of contributors. + * + * @param contributor The contributor to remove. + */ + public void removeContributor( String contributor ) + { + removeBagValue( prefix + ":contributor", contributor ); + } + + /** + * Add a contributor to the list of contributors. A contributor is someone other than an author. + * + * @param contributor The name of the contributor. + */ + public void addContributor( String contributor ) + { + addBagValue( prefix + ":contributor", contributor ); + } + + /** + * Get the complete list of contributors. + * + * @return The list of contributors. + */ + public List getContributors() + { + return getBagList( prefix + ":contributor" ); + } + + /** + * Set the coverage property. + * + * @param coverage The extend or scope of the resource. + */ + public void setCoverage( String coverage ) + { + setTextProperty( prefix + ":coverage", coverage ); + } + + /** + * Get the coverage property. + * + * @return The extent or scope of the resource. + */ + public String getCoverage() + { + return getTextProperty( prefix + ":coverage" ); + } + + /** + * Remove a creator from the list of creators. + * + * @param creator The author of the resource. + */ + public void removeCreator( String creator ) + { + removeSequenceValue( prefix + ":creator", creator ); + } + + /** + * Add a creator. + * + * @param creator The author of the resource. + */ + public void addCreator( String creator ) + { + addSequenceValue( prefix + ":creator", creator ); + } + + /** + * Get a complete list of creators. + * + * @return A list of java.lang.String objects. + */ + public List getCreators() + { + return getSequenceList( prefix + ":creator" ); + } + + /** + * Remove a date from the list of 'interesting' dates. + * + * @param date The date to remove. + */ + public void removeDate( Calendar date ) + { + removeSequenceDateValue( prefix + ":date", date ); + } + + /** + * Add a date of interest to this schema. + * + * @param date The date to add to the schema. + */ + public void addDate( Calendar date ) + { + addSequenceDateValue( prefix + ":date", date ); + } + + /** + * Get a list of all dates of interest to this resource. + * + * @return A list of java.util.Calendar objects. + * + * @throws IOException If there is an error creating the date object. + */ + public List getDates() throws IOException + { + return getSequenceDateList( prefix + ":date" ); + } + + /** + * Set the default value for the description. + * + * @param description The description of this resource. + */ + public void setDescription( String description ) + { + setLanguageProperty( prefix + ":description", null, description ); + } + + /** + * Get the default value for the description. + * + * @return The description of this resource. + */ + public String getDescription() + { + return getLanguageProperty( prefix + ":description", null ); + } + + /** + * Set the description of this resource in a specific language. + * + * @param language The language code. + * @param description The description in a specific language. + */ + public void setDescription( String language, String description ) + { + setLanguageProperty( prefix + ":description", language, description ); + } + + /** + * Get the description in a specific language. + * + * @param language The language code to get the description for. + * + * @return The description in the specified language or null if it does not exist. + */ + public String getDescription( String language ) + { + return getLanguageProperty( prefix + ":description", language ); + } + + /** + * Get a list of all languages that a description exists for. + * + * @return A non-null list of languages, potentially an empty list. + */ + public List getDescriptionLanguages() + { + return getLanguagePropertyLanguages( prefix + ":description" ); + } + + /** + * Set the format property. + * + * @param format The mime-type of the saved resource. + */ + public void setFormat( String format ) + { + setTextProperty( prefix + ":format", format ); + } + + /** + * Get the format property. + * + * @return The mime-type of the resource. + */ + public String getFormat() + { + return getTextProperty( prefix + ":format" ); + } + + /** + * Set the resource identifier. + * + * @param id An id to the resource. + */ + public void setIdentifier( String id ) + { + setTextProperty( prefix + ":identifier", id ); + } + + /** + * Get the resource id. + * + * @return A key that identifies this resource. + */ + public String getIdentifier() + { + return getTextProperty( prefix + ":identifier" ); + } + + /** + * Remove a language from the list of languages. + * + * @param language The language to remove. + */ + public void removeLanguage( String language ) + { + removeBagValue( prefix + ":language", language ); + } + + /** + * Add a language to the list of languages. + * + * @param language The name of the language. + */ + public void addLanguage( String language ) + { + addBagValue( prefix + ":language", language ); + } + + /** + * Get the complete list of languages. + * + * @return The list of languages. + */ + public List getLanguages() + { + return getBagList( prefix + ":language" ); + } + + /** + * Remove a publisher from the list of publishers. + * + * @param publisher The publisher to remove. + */ + public void removePublisher( String publisher ) + { + removeBagValue( prefix + ":publisher", publisher ); + } + + /** + * Add a publisher to the list of publishers. + * + * @param publisher The name of the publisher. + */ + public void addPublisher( String publisher ) + { + addBagValue( prefix + ":publisher", publisher ); + } + + /** + * Get the complete list of publishers. + * + * @return The list of publishers. + */ + public List getPublishers() + { + return getBagList( prefix + ":publisher" ); + } + + /** + * Remove a relation from the list of relationships. + * A relationship to another resource. + * + * @param relation The publisher to remove. + */ + public void removeRelation( String relation ) + { + removeBagValue( prefix + ":relation", relation ); + } + + /** + * Add a relation to the list of relationships. + * A relationship to another resource. + * + * @param relation The relation to the other resource. + */ + public void addRelation( String relation ) + { + addBagValue( prefix + ":relation", relation ); + } + + /** + * Get the complete list of relationships. + * + * @return The list of relationships. + */ + public List getRelationships() + { + return getBagList( prefix + ":relation" ); + } + + /** + * Set the default value for the rights of this document. This property + * specifies informal rights of the document. + * + * @param rights The rights for this resource. + */ + public void setRights( String rights ) + { + setLanguageProperty( prefix + ":rights", null, rights ); + } + + /** + * Get the default value for the rights of this document. + * + * @return The informal rights for this resource. + */ + public String getRights() + { + return getLanguageProperty( prefix + ":rights", null ); + } + + /** + * Set the rights for this resource in a specific language. + * + * @param language The language code. + * @param rights The rights in a specific language. + */ + public void setRights( String language, String rights ) + { + setLanguageProperty( prefix + ":rights", language, rights ); + } + + /** + * Get the rights in a specific language. + * + * @param language The language code to get the description for. + * + * @return The rights in the specified language or null if it does not exist. + */ + public String getRights( String language ) + { + return getLanguageProperty( prefix + ":rights", language ); + } + + /** + * Get a list of all languages that a rights description exists for. + * + * @return A non-null list of languages, potentially an empty list. + */ + public List getRightsLanguages() + { + return getLanguagePropertyLanguages( prefix + ":rights" ); + } + + /** + * Set the resource source identifier. + * + * @param id An id to the resource source. + */ + public void setSource( String id ) + { + setTextProperty( prefix + ":source", id ); + } + + /** + * Get the resource source id. + * + * @return A key that identifies this source of this resource. + */ + public String getSource() + { + return getTextProperty( prefix + ":source" ); + } + + /** + * Remove a subject from the list of subjects. + * + * @param subject The subject to remove. + */ + public void removeSubject( String subject ) + { + removeBagValue( prefix + ":subject", subject ); + } + + /** + * Add a subject to the list of subjects. + * + * @param subject The subject of this resource. + */ + public void addSubject( String subject ) + { + addBagValue( prefix + ":subject", subject ); + } + + /** + * Get the complete list of subjects. + * + * @return The list of subjects. + */ + public List getSubjects() + { + return getBagList( prefix + ":subject" ); + } + + /** + * Set the default value for the title. + * + * @param title The title of this resource. + */ + public void setTitle( String title ) + { + setLanguageProperty( prefix + ":title", null, title ); + } + + /** + * Get the default value for the title. + * + * @return The title of this resource. + */ + public String getTitle() + { + return getLanguageProperty( prefix + ":title", null ); + } + + /** + * Set the title of this resource in a specific language. + * + * @param language The language code. + * @param title The title in a specific language. + */ + public void setTitle( String language, String title ) + { + setLanguageProperty( prefix + ":title", language, title ); + } + + /** + * Get the title in a specific language. + * + * @param language The language code to get the description for. + * + * @return The title in the specified language or null if it does not exist. + */ + public String getTitle( String language ) + { + return getLanguageProperty( prefix + ":title", language ); + } + + /** + * Get a list of all languages that a title exists for. + * + * @return A non-null list of languages, potentially an empty list. + */ + public List getTitleLanguages() + { + return getLanguagePropertyLanguages( prefix + ":title" ); + } + + /** + * Add a type to the bag of types of this resource. + * + * @param type The type of resource to add (poem, novel). + */ + public void addType( String type ) + { + addBagValue(prefix + ":type", type ); + } + + /** + * Get the list of types for this resource. + * + * @return A list of types for this resource. + */ + public List getTypes() + { + return getBagList(prefix + ":type" ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDynamicMedia.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDynamicMedia.java new file mode 100644 index 000000000..2d88eb4ab --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaDynamicMedia.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.w3c.dom.Element; + +/** + * Dynamic Media schema. + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.2 $ + */ +public class XMPSchemaDynamicMedia extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xmp/1.0/DynamicMedia/"; + + /** + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaDynamicMedia( XMPMetadata parent ) + { + super( parent, "xmpDM", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaDynamicMedia( Element element, String prefix) + { + super( element , prefix); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaIptc4xmpCore.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaIptc4xmpCore.java new file mode 100644 index 000000000..254570420 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaIptc4xmpCore.java @@ -0,0 +1,314 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Define XMP properties used with IPTC specification. + * + * @author $Author: benlitchfield $ + * @version $Revision: 1.3 $ + */ +public class XMPSchemaIptc4xmpCore extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"; + + /** + * Construct a new blank IPTC schema. + * + * @param metadata The parent metadata schema that this will be part of. + */ + public XMPSchemaIptc4xmpCore(XMPMetadata metadata) + { + super(metadata, "Iptc4xmpCore", NAMESPACE); + } + + /** + * Constructor from an existing element. + * + * @param element The XML element. + * @param aPrefix The XML prefix; Iptc4xmpCore. + */ + public XMPSchemaIptc4xmpCore(Element element, String aPrefix) + { + super(element, aPrefix); + } + + /** + * Contact Info Address City. + * + * @param city The city name. + */ + public void setCiAdrCity( String city ) + { + setTextProperty(prefix + ":CiAdrCity", city); + } + + /** + * Contact Info Address City. + * + * @return The city. + */ + public String getCiAdrCity() + { + return getTextProperty(prefix + ":CiAdrCity"); + } + + /** + * Contact Info country. + * + * @param country The CI country. + */ + public void setCiAdrCtry( String country ) + { + setTextProperty(prefix + ":CiAdrCtry", country); + } + + /** + * Contact Info country. + * + * @return The CI country. + */ + public String getCiAdrCtry() + { + return getTextProperty(prefix + ":CiAdrCtry"); + } + + /** + * Contact Info Extended Address(company name). + * + * @param adr Address info. + */ + public void setCiAdrExtadr( String adr ) + { + setTextProperty(prefix + ":CiAdrExtadr", adr); + } + + /** + * Contact Info Extended Address(company name). + * + * @return The extended address info. + */ + public String getCiAdrExtadr() + { + return getTextProperty(prefix + ":CiAdrExtadr"); + } + + /** + * Postal code. + * + * @param po The postal code. + */ + public void setCiAdrPcode( String po ) + { + setTextProperty(prefix + ":CiAdrPcode", po); + } + + /** + * Postal code. + * + * @return The postal code. + */ + public String getCiAdrPcode() + { + return getTextProperty(prefix + ":CiAdrPcode"); + } + + /** + * Postal region or state. + * + * @param state The postal region + */ + public void setCiAdrRegion( String state ) + { + setTextProperty(prefix + ":CiAdrRegion", state); + } + + /** + * Postal region or state. + * + * @return The postal state. + */ + public String getCiAdrRegion() + { + return getTextProperty(prefix + ":CiAdrRegion"); + } + + /** + * Work email. + * + * @param email The work email. + */ + public void setCiEmailWork( String email ) + { + setTextProperty(prefix + ":CiEmailWork", email); + } + + /** + * Work email. + * + * @return The work email. + */ + public String getCiEmailWork() + { + return getTextProperty(prefix + ":CiEmailWork"); + } + + /** + * Work telephone. + * + * @param tel The work telephone. + */ + public void setCiTelWork( String tel ) + { + setTextProperty(prefix + ":CiTelWork", tel); + } + + /** + * Work Telephone. + * + * @return The work telephone. + */ + public String getCiTelWork() + { + return getTextProperty(prefix + ":CiTelWork"); + } + + /** + * Work URL. + * + * @param url The work URL. + */ + public void setCiUrlWork( String url ) + { + setTextProperty(prefix + ":CiUrlWork", url); + } + + /** + * Work URL. + * + * @return work URL. + */ + public String getCiUrlWork() + { + return getTextProperty(prefix + ":CiUrlWork"); + } + + /** + * Name of location that the content is focussing on. + * + * @param loc The location. + */ + public void setLocation( String loc ) + { + setTextProperty(prefix + ":Location", loc); + } + + /** + * Name of location that the content is focussing on. + * @return The location. + */ + public String getLocation() + { + return getTextProperty(prefix + ":Location"); + } + + /** + * The IPTC scene. + * + * @param scene The IPTC scene. + */ + public void addScene( String scene ) + { + addBagValue(prefix + ":Scene", scene); + } + + /** + * A list of all the scenes. + * + * @return The list of scenes. + */ + public List getScenes() + { + return getBagList(prefix + ":Scene"); + } + + /** + * Add IPTC subject code. + * @param subject The IPTC subject. + */ + public void addSubjectCode( String subject ) + { + addBagValue(prefix + ":SubjectCode", subject); + } + + /** + * Get a list of all IPTC subject codes. + * + * @return All IPTC subject codes. + */ + public List getSubjectCodes() + { + return getBagList(prefix + ":SubjectCode"); + } + + /** + * Nature of a news object. + * + * @param genre The news genre. + */ + public void setIntellectualGenre( String genre ) + { + setTextProperty(prefix + ":IntellectualGenre", genre); + } + + /** + * Nature of a news object. + * + * @return The news genre. + */ + public String getIntellectualGenre() + { + return getTextProperty(prefix + ":IntellectualGenre"); + } + + /** + * ISO Country Code. + * + * @param code The country code. + */ + public void setCountryCode( String code ) + { + setTextProperty(prefix + ":CountryCode", code); + } + + /** + * ISO Country Code. + * + * @return The country code. + */ + public String getCountryCode() + { + return getTextProperty(prefix + ":CountryCode"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaMediaManagement.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaMediaManagement.java new file mode 100644 index 000000000..e723e0bb7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaMediaManagement.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.util.List; + +import org.apache.jempbox.impl.XMLUtil; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * Define XMP properties that are related to digital asset management. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class XMPSchemaMediaManagement extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/mm/"; + + /** + * Construct a new blank PDF schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaMediaManagement( XMPMetadata parent ) + { + super( parent, "xmpMM", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaMediaManagement( Element element, String prefix ) + { + super( element, prefix ); + } + + /** + * Get a reference to the original document that this document is + * derived from. + * + * @return A reference to the derived document, or null if one does not exist. + */ + public ResourceRef getDerivedFrom() + { + ResourceRef retval = null; + NodeList nodes = schema.getElementsByTagName( prefix + ":DerivedFrom" ); + if( nodes.getLength() > 0 ) + { + Element derived = (Element)nodes.item( 0 ); + retval = new ResourceRef(derived); + } + else + { + //the old name was RenditionOf, this is now deprecated but lets + //try to find it in case of older XMP documents. + NodeList deprecatedNodes = schema.getElementsByTagName( "xmpMM:RenditionOf" ); + if( deprecatedNodes.getLength() > 0 ) + { + Element derived = (Element)deprecatedNodes.item( 0 ); + retval = new ResourceRef(derived); + } + } + return retval; + } + + /** + * Create a new Derived From resource ref that can be populated. You + * will still need to call setDerivedFrom after this is created. + * + * @return A new blank derived from instance. + */ + public ResourceRef createDerivedFrom() + { + Element node = schema.getOwnerDocument().createElement( prefix + ":DerivedFrom" ); + ResourceRef ref = new ResourceRef( node ); + return ref; + } + + /** + * Set or clear the derived from value. + * + * @param resource The resource reference to set. + * + * @see XMPSchemaMediaManagement#createDerivedFrom() + */ + public void setDerivedFrom( ResourceRef resource ) + { + XMLUtil.setElementableValue( schema, prefix + ":DerivedFrom", resource ); + } + + /** + * Set the common identifier to all versions of this document. It should + * be based on a UUID. + * + * @param id An identifier for the document. + */ + public void setDocumentID( String id ) + { + setTextProperty( prefix + ":DocumentID", id ); + } + + /** + * Get id that identifies all versions of this document. + * + * @return The document id. + */ + public String getDocumentID() + { + return getTextProperty( prefix + ":DocumentID" ); + } + + /** + * + * @param id An identifier for the current version. + */ + public void setVersionID( String id ) + { + setTextProperty( prefix + ":VersionID", id ); + } + + /** + * + * @return The current version id. + */ + public String getVersionID() + { + return getTextProperty( prefix + ":VersionID" ); + } + + /** + * Get a list of all historical events that have occured for this resource. + * + * @return A list of ResourceEvent objects or null. + */ + public List getHistory() + { + return getEventSequenceList( prefix + ":History" ); + } + + /** + * Remove an event from the list of events. + * + * @param event The event to remove. + */ + public void removeHistory( ResourceEvent event ) + { + removeSequenceValue( prefix + ":History", event ); + } + + /** + * Add a new historical event. + * + * @param event The event to add to the list of history. + */ + public void addHistory( ResourceEvent event ) + { + addSequenceValue( prefix + ":History", event ); + } + + /** + * Get a reference to the document prior to it being managed. + * + * @return A reference to the managed document. + */ + public ResourceRef getManagedFrom() + { + ResourceRef retval = null; + NodeList nodes = schema.getElementsByTagName( prefix + ":ManagedFrom" ); + if( nodes.getLength() > 0 ) + { + Element derived = (Element)nodes.item( 0 ); + retval = new ResourceRef(derived); + } + return retval; + } + + /** + * Create a new Managed From resource ref that can be populated. You + * will still need to call setManagedFrom after this is created. + * + * @return A new blank managed from instance. + */ + public ResourceRef createManagedFrom() + { + Element node = schema.getOwnerDocument().createElement( prefix + ":ManagedFrom" ); + ResourceRef ref = new ResourceRef( node ); + return ref; + } + + /** + * Set or clear the managed from value. + * + * @param resource The resource reference to set. + * + * @see XMPSchemaMediaManagement#createManagedFrom() + */ + public void setManagedFrom( ResourceRef resource ) + { + XMLUtil.setElementableValue( schema, prefix + ":DerivedFrom", resource ); + } + + /** + * Set the asset management system that manages this resource. + * + * @param manager The name of the asset management system. + */ + public void setManager( String manager ) + { + setTextProperty( prefix + ":Manager", manager ); + } + + /** + * Get the name of the asset management system that manages this resource. + * + * @return The name of the asset management system. + */ + public String getManager() + { + return getTextProperty( prefix + ":Manager" ); + } + + /** + * Set the URI identifying the managed resource. + * + * @param uri URI to the managed resource. + */ + public void setManageTo( String uri ) + { + setTextProperty( prefix + ":ManageTo", uri ); + } + + /** + * Get the URI to the managed resource. + * + * @return The managed resource URI. + */ + public String getManageTo() + { + return getTextProperty( prefix + ":ManageTo" ); + } + + /** + * Set the URI identifying information about the managed resource. + * + * @param uri URI to the managed resource info. + */ + public void setManageUI( String uri ) + { + setTextProperty( prefix + ":ManageUI", uri ); + } + + /** + * Get the URI to the managed resource information. + * + * @return The managed resource information URI. + */ + public String getManageUI() + { + return getTextProperty( prefix + ":ManageUI" ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPDF.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPDF.java new file mode 100644 index 000000000..f9ff56c19 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPDF.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.w3c.dom.Element; + +/** + * Define XMP properties used with Adobe PDF documents. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class XMPSchemaPDF extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/pdf/1.3/"; + + /** + * Construct a new blank PDF schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPDF( XMPMetadata parent ) + { + super( parent, "pdf", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaPDF( Element element, String prefix ) + { + super( element, prefix ); + } + + /** + * PDF Keywords. + * + * @param keywords The PDF keywords. + */ + public void setKeywords( String keywords ) + { + setTextProperty( prefix + ":Keywords", keywords ); + } + + /** + * Get the PDF keywords. + * + * @return The PDF keywords. + */ + public String getKeywords() + { + return getTextProperty( prefix + ":Keywords" ); + } + + /** + * Set the PDF file version. 1.2,1.3,... + * + * @param pdfVersion The version of the PDF file format. + */ + public void setPDFVersion( String pdfVersion ) + { + setTextProperty( prefix + ":PDFVersion", pdfVersion ); + } + + /** + * Get the PDF version. + * + * @return The value of the PDF version property. + */ + public String getPDFVersion() + { + return getTextProperty( prefix + ":PDFVersion" ); + } + + /** + * Set the PDF producer. + * + * @param producer The tool that created the PDF. + */ + public void setProducer( String producer ) + { + setTextProperty( prefix + ":Producer", producer ); + } + + /** + * Get the value of the producer property. + * + * @return The producer property. + */ + public String getProducer() + { + return getTextProperty( prefix + ":Producer" ); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPagedText.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPagedText.java new file mode 100644 index 000000000..676554c93 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPagedText.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import org.w3c.dom.Element; + +/** + * Paged Text Schema. + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.2 $ + */ +public class XMPSchemaPagedText extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/t/pg/"; + + /** + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPagedText( XMPMetadata parent ) + { + super( parent, "xmpTPg", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The XML prefix. + */ + public XMPSchemaPagedText( Element element, String prefix ) + { + super( element , prefix); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPhotoshop.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPhotoshop.java new file mode 100644 index 000000000..976559eea --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaPhotoshop.java @@ -0,0 +1,358 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Define XMP properties used with Adobe Photoshop documents. + * + * @author $Author: benlitchfield $ + * @version $Revision: 1.2 $ + */ +public class XMPSchemaPhotoshop extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/photoshop/1.0/"; + + /** + * Construct a new blank Photoshop schema. + * + * @param parent + * The parent metadata schema that this will be part of. + */ + public XMPSchemaPhotoshop(XMPMetadata parent) + { + super(parent, "photoshop", NAMESPACE); + } + + /** + * Constructor for existing XML element. + * + * @param element The XML element. + * @param aPrefix The XML prefix; photoshop. + */ + public XMPSchemaPhotoshop(Element element, String aPrefix) + { + super(element, aPrefix); + } + + /** + * By-line title. + * + * @param s The authors position. + */ + public void setAuthorsPosition( String s ) + { + setTextProperty(prefix + ":AuthorsPosition", s); + } + + /** + * By-line title. + * + * @return The authors position. + */ + public String getAuthorsPosition() + { + return getTextProperty(prefix + ":AuthorsPosition"); + } + + /** + * Writer/editor. + * + * @param s The caption writer. + */ + public void setCaptionWriter( String s ) + { + setTextProperty(prefix + ":CaptionWriter", s); + } + + /** + * Writer/editor. + * + * @return The caption writer. + */ + public String getCaptionWriter() + { + return getTextProperty(prefix + ":CaptionWriter"); + } + + /** + * Category; limited to 3 7-bit characters. + * @param s The category. + */ + public void setCategory( String s ) + { + if( s != null && s.length() > 3 ) + { + throw new RuntimeException( "Error: photoshop:Category is limited to three characters value='" + s + "'" ); + } + setTextProperty(prefix + ":Category", s); + } + + /** + * The category. + * + * @return The category. + */ + public String getCategory() + { + return getTextProperty(prefix + ":Category"); + } + + /** + * The city. + * + * @param s The city. + */ + public void setCity( String s ) + { + setTextProperty(prefix + ":City", s); + } + + /** + * The city. + * + * @return The city. + */ + public String getCity() + { + return getTextProperty(prefix + ":City"); + } + + /** + * The country. + * + * @param s The country. + */ + public void setCountry( String s ) + { + setTextProperty(prefix + ":Country", s); + } + + /** + * The country. + * + * @return The country. + */ + public String getCountry() + { + return getTextProperty(prefix + ":Country"); + } + + /** + * Credit. + * + * @param s The credit property. + */ + public void setCredit( String s ) + { + setTextProperty(prefix + ":Credit", s); + } + + /** + * The credit property. + * + * @return The credit property. + */ + public String getCredit() + { + return getTextProperty(prefix + ":Credit"); + } + + /** + * Date created; creation date of the source document which may be + * earlier than the digital representation. + * + * @param s The date created. + */ + public void setDateCreated( String s ) + { + setTextProperty(prefix + ":DateCreated", s); + } + + /** + * Creation date. + * + * @return The creation date. + */ + public String getDateCreated() + { + return getTextProperty(prefix + ":DateCreated"); + } + + /** + * The headline. + * + * @param s The headline. + */ + public void setHeadline( String s ) + { + setTextProperty(prefix + ":Headline", s); + } + + /** + * Headline. + * + * @return The headline. + */ + public String getHeadline() + { + return getTextProperty(prefix + ":Headline"); + } + + /** + * Instructions. + * + * @param s The instructions. + */ + public void setInstructions( String s ) + { + setTextProperty(prefix + ":Instructions", s); + } + + /** + * The instructions. + * + * @return The instructions. + */ + public String getInstructions() + { + return getTextProperty(prefix + ":Instructions"); + } + + /** + * The source. + * + * @param s The source. + */ + public void setSource( String s ) + { + setTextProperty(prefix + ":Source", s); + } + + /** + * The source. + * + * @return The source. + */ + public String getSource() + { + return getTextProperty(prefix + ":Source"); + } + + /** + * The state. + * + * @param s The state. + */ + public void setState( String s ) + { + setTextProperty(prefix + ":State", s); + } + + /** + * The state. + * + * @return The state. + */ + public String getState() + { + return getTextProperty(prefix + ":State"); + } + + /** + * Add a new supplemental category. + * + * @param s The supplemental category. + */ + public void addSupplementalCategory( String s ) + { + addBagValue(prefix + ":SupplementalCategories", s); + } + + /** + * Get a list of all supplemental categories. + * + * @return The supplemental categories. + */ + public List getSupplementalCategories() + { + return getBagList(prefix + ":SupplementalCategories"); + } + + /** + * Remove a supplemental category. + * + * @param s The supplemental category. + */ + public void removeSupplementalCategory( String s ) + { + removeBagValue(prefix + ":SupplementalCategories", s); + } + + /** + * The transmission reference. + * + * @param s The transmission reference. + */ + public void setTransmissionReference( String s ) + { + setTextProperty(prefix + ":TransmissionReference", s); + } + + /** + * The transmission reference. + * + * @return The transmission reference. + */ + public String getTransmissionReference() + { + return getTextProperty(prefix + ":TransmissionReference"); + } + + /** + * The urgency. + * + * @param s The urgency. + */ + public void setUrgency( Integer s ) + { + if( s != null ) + { + if( s.intValue() < 1 || s.intValue() > 8 ) + { + throw new RuntimeException( "Error: photoshop:Urgency must be between 1 and 8. value=" + s ); + } + } + setIntegerProperty(prefix + ":Urgency", s); + } + + /** + * The urgency. + * + * @return The urgency. + */ + public Integer getUrgency() + { + return getIntegerProperty(prefix + ":Urgency"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaRightsManagement.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaRightsManagement.java new file mode 100644 index 000000000..ce379031c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/XMPSchemaRightsManagement.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp; + +import java.util.List; + +import org.w3c.dom.Element; + +/** + * Define XMP properties that are related to rights management. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class XMPSchemaRightsManagement extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/rights/"; + + /** + * Construct a new blank PDF schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaRightsManagement(XMPMetadata parent) + { + super(parent, "xmpRights", NAMESPACE); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaRightsManagement(Element element, String prefix) + { + super(element, prefix); + } + + /** + * The online rights management certificate. + * + * @param certificate The URL to the rights cert. + */ + public void setCertificateURL( String certificate ) + { + setTextProperty("xmpRights:Certificate", certificate); + } + + /** + * Get the URL of the rights managment certificate. + * + * @return The rights management certificate URL. + */ + public String getCertificateURL() + { + return getTextProperty(prefix + ":Certificate"); + } + + /** + * Flag indicating if this is a rights managed resource. + * + * @param marked The marked value. + */ + public void setMarked( Boolean marked ) + { + setBooleanProperty(prefix + ":Marked", marked); + } + + /** + * Get the flag that indicates if this is a marked resource.. + * + * @return The value of the marked flag. + */ + public Boolean getMarked() + { + Boolean b = getBooleanProperty(prefix + ":Marked"); + return b != null ? b : Boolean.FALSE; + } + + /** + * Remove an owner from the list. + * + * @param owner The owner to remove. + */ + public void removeOwner( String owner ) + { + removeBagValue(prefix + ":Owner", owner); + } + + /** + * Add an owner to the list. + * + * @param owner A new legal owner to this resource. + */ + public void addOwner( String owner ) + { + addBagValue(prefix + ":Owner", owner); + } + + /** + * Get the complete list of legal owners. + * + * @return The list of owners. + */ + public List getOwners() + { + return getBagList(prefix + ":Owner"); + } + + /** + * Set the default usage terms for this resource. + * + * @param terms The resource usage terms. + */ + public void setUsageTerms( String terms ) + { + setLanguageProperty(prefix + ":UsageTerms", null, terms); + } + + /** + * Get the default usage terms for the document. + * + * @return The terms for this resource. + */ + public String getUsageTerms() + { + return getLanguageProperty(prefix + ":UsageTerms", null); + } + + /** + * Set the usage terms of this resource in a specific language. + * + * @param language The language code. + * @param terms The terms of this resource. + */ + public void setDescription( String language, String terms ) + { + setLanguageProperty(prefix + ":UsageTerms", language, terms); + } + + /** + * Get the usage terms in a specific language. + * + * @param language The language code to get the description for. + * + * @return The usage terms in the specified language or null if it does not exist. + */ + public String getUsageTerms( String language ) + { + return getLanguageProperty(prefix + ":UsageTerms", language); + } + + /** + * Get a list of all languages that a usage term exists for. + * + * @return A non-null list of languages, potentially an empty list. + */ + public List getUsageTermsLanguages() + { + return getLanguagePropertyLanguages(prefix + ":UsageTerms"); + } + + /** + * Set the external link that describes the owners/rights of this resource. + * + * @param webStatement The URL to a terms site. + */ + public void setWebStatement( String webStatement ) + { + setTextProperty(prefix + ":WebStatement", webStatement); + } + + /** + * Get the URL that describes the terms of this resource. + * + * @return The usage rights URL. + */ + public String getWebStatement() + { + return getTextProperty(prefix + ":WebStatement"); + } + + /** + * Set the copyright information. + * + * @param copyright The copyright information. + */ + public void setCopyright( String copyright ) + { + setTextProperty(prefix + ":Copyright", copyright); + } + + /** + * Get the copyright information. + * + * @return The copyright information. + */ + public String getCopyright() + { + return getTextProperty(prefix + ":Copyright"); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/package.html b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/package.html new file mode 100644 index 000000000..0423f3924 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds data access classes for XMP data structures. + + diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPMetadataPDFA.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPMetadataPDFA.java new file mode 100644 index 000000000..3bd61e5de --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPMetadataPDFA.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp.pdfa; + +import java.io.IOException; + +import org.apache.jempbox.impl.XMLUtil; +import org.apache.jempbox.xmp.XMPMetadata; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +/** + * PDFA Metadata. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class XMPMetadataPDFA extends XMPMetadata +{ + + /** + * Constructor. + * + * @throws IOException If there is an error creating this metadata. + */ + public XMPMetadataPDFA() throws IOException + { + super(); + init(); + } + + /** + * Constructor. + * + * @param doc The XML document that maps to the metadata. + */ + public XMPMetadataPDFA(Document doc) + { + super(doc); + init(); + } + + private void init() + { + // PDFA specific schemas + nsMappings.put( XMPSchemaPDFAField.NAMESPACE, XMPSchemaPDFAField.class ); + nsMappings.put( XMPSchemaPDFAId.NAMESPACE, XMPSchemaPDFAId.class ); + nsMappings.put( XMPSchemaPDFAProperty.NAMESPACE, XMPSchemaPDFAProperty.class ); + nsMappings.put( XMPSchemaPDFASchema.NAMESPACE, XMPSchemaPDFASchema.class ); + nsMappings.put( XMPSchemaPDFAType.NAMESPACE, XMPSchemaPDFAType.class ); + } + + /** + * Load a a PDFA metadata. + * + * @param is An XML input stream + * @return A PDFA metadata. + * @throws IOException If there is an error loading the XML document. + */ + public static XMPMetadata load( InputSource is ) throws IOException + { + return new XMPMetadataPDFA( XMLUtil.parse( is ) ); + } + + /** + * Get the PDFAField schema. + * + * @return A PDFAField schema. + * + * @throws IOException If there is an error finding the scheam. + */ + public XMPSchemaPDFAField getPDFAFieldSchema() throws IOException + { + return (XMPSchemaPDFAField) getSchemaByClass(XMPSchemaPDFAField.class); + } + + /** + * Add a new PDFAField schema. + * + * @return The newly added PDFA schema. + */ + public XMPSchemaPDFAField addPDFAFieldSchema() + { + XMPSchemaPDFAField schema = new XMPSchemaPDFAField(this); + return (XMPSchemaPDFAField) basicAddSchema(schema); + } + + /** + * Get the PDFA ID schema. + * @return The PDFA ID schema. + * @throws IOException If there is an error accessing the PDFA id schema. + */ + public XMPSchemaPDFAId getPDFAIdSchema() throws IOException + { + return (XMPSchemaPDFAId) getSchemaByClass(XMPSchemaPDFAId.class); + } + + /** + * Add a PDFA Id schema and return the result. + * + * @return The newly created PDFA Id schema. + */ + public XMPSchemaPDFAId addPDFAIdSchema() + { + XMPSchemaPDFAId schema = new XMPSchemaPDFAId(this); + return (XMPSchemaPDFAId) basicAddSchema(schema); + } + + /** + * Get the PDFA property schema. + * + * @return The PDFA property schema. + * + * @throws IOException If there is an error accessing the PDFA property schema. + */ + public XMPSchemaPDFAProperty getPDFAPropertySchema() throws IOException + { + return (XMPSchemaPDFAProperty) getSchemaByClass(XMPSchemaPDFAProperty.class); + } + + /** + * Create a PDFA property schema. + * + * @return The newly created property schema. + */ + public XMPSchemaPDFAProperty addPDFAPropertySchema() + { + XMPSchemaPDFAProperty schema = new XMPSchemaPDFAProperty(this); + return (XMPSchemaPDFAProperty) basicAddSchema(schema); + } + + /** + * Get the PDFA schema. + * + * @return The PDFA schema. + * + * @throws IOException If there is an error getting the PDFA schema. + */ + public XMPSchemaPDFASchema getPDFASchema() throws IOException + { + return (XMPSchemaPDFASchema) getSchemaByClass(XMPSchemaPDFASchema.class); + } + + /** + * Add a PDFA schema. + * + * @return The newly created PDFA schema. + */ + public XMPSchemaPDFASchema addPDFASchema() + { + XMPSchemaPDFASchema schema = new XMPSchemaPDFASchema(this); + return (XMPSchemaPDFASchema) basicAddSchema(schema); + } + + /** + * Get the PDFA type schema. + * + * @return The PDFA type schema. + * + * @throws IOException If there is an error accessing the PDFA type schema. + */ + public XMPSchemaPDFAType getPDFATypeSchema() throws IOException + { + return (XMPSchemaPDFAType) getSchemaByClass(XMPSchemaPDFAType.class); + } + + /** + * Add a new PDFA type schema. + * + * @return The newly created PDFA type schema. + */ + public XMPSchemaPDFAType addPDFATypeSchema() + { + XMPSchemaPDFAType schema = new XMPSchemaPDFAType(this); + return (XMPSchemaPDFAType) basicAddSchema(schema); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAField.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAField.java new file mode 100644 index 000000000..215fd030d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAField.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp.pdfa; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchema; +import org.w3c.dom.Element; + +/** + * Define XMP properties used PDFA extension schema description schemas. + * TODO 2 naked so far, implement + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * + * @version $Revision: 1.1 $ + */ +public class XMPSchemaPDFAField extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://www.aiim.org/pdfa/ns/field"; + + /** + * Construct a new blank PDFA schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPDFAField( XMPMetadata parent ) + { + super( parent, "pdfaField", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaPDFAField( Element element , String prefix) + { + super( element , prefix); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAId.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAId.java new file mode 100644 index 000000000..3a56e57c3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAId.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp.pdfa; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchema; +import org.w3c.dom.Element; + +/** + * Define XMP properties used PDFA extension schema description schemas. + * TODO 2 naked so far, implement + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.1 $ + */ +public class XMPSchemaPDFAId extends XMPSchema +{ + /** + * The namespace for this schema. + * This is the future amendment of the PDFA Spec with the trailing slash at end + */ + public static final String NAMESPACE = "http://www.aiim.org/pdfa/ns/id/"; + + /** + * Construct a new blank PDFA schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPDFAId( XMPMetadata parent ) + { + super( parent, "pdfaid", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaPDFAId( Element element , String prefix) + { + super( element , prefix); + } + + /** + * Get the ISO19005 part number. + * + * @return The ISO 19005 part number. + */ + public Integer getPart() + { + return getIntegerProperty( prefix+":part" ); + } + + /** + * Set the ISO19005 part number. + * + * @param part The ISO 19005 part number. + */ + public void setPart( Integer part ) + { + setIntegerProperty( prefix+":part", part); + } + + /** + * Set the amendment idenitifier. + * + * @param amd The amendment idenitifier. + */ + public void setAmd( String amd ) + { + setTextProperty( prefix+":amd", amd); + } + + /** + * Get the amendment idenitifier. + * + * @return The amendment idenitifier. + */ + public String getAmd() + { + return getTextProperty( prefix+":amd" ); + } + + /** + * Set the conformance level. + * + * @param conformance The conformance level. + */ + public void setConformance( String conformance ) + { + setTextProperty( prefix+":conformance", conformance); + } + + /** + * Get the conformance level. + * + * @return The conformance level. + */ + public String getConformance() + { + return getTextProperty( prefix+":conformance" ); + } + +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAProperty.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAProperty.java new file mode 100644 index 000000000..52770f005 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAProperty.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp.pdfa; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchema; +import org.w3c.dom.Element; + +/** + * Define XMP properties used PDFA extension schema description schemas. + * TODO 2 naked so far, implement + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.1 $ + */ +public class XMPSchemaPDFAProperty extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://www.aiim.org/pdfa/ns/property"; + + /** + * Construct a new blank PDFA schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPDFAProperty( XMPMetadata parent ) + { + super( parent, "pdfaProperty", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaPDFAProperty( Element element , String prefix) + { + super( element , prefix); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFASchema.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFASchema.java new file mode 100644 index 000000000..83ce67677 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFASchema.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp.pdfa; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchema; +import org.w3c.dom.Element; + +/** + * Define XMP properties used PDFA extension schema description schemas. + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.1 $ + */ +public class XMPSchemaPDFASchema extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://www.aiim.org/pdfa/ns/schema"; + + /** + * Construct a new blank PDFA schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPDFASchema( XMPMetadata parent ) + { + super( parent, "pdfaSchema", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaPDFASchema( Element element, String prefix ) + { + super( element , prefix); + } + + /** + * PDFA schema. + * + * @param schema The optional description of schema. + */ + public void setSchema( String schema ) + { + setTextProperty( "pdfaSchema:schema", schema); + } + + /** + * Get the PDFA schema. + * + * @return The optional description of schema. + */ + public String getSchema() + { + return getTextProperty( "pdfaSchema:schema" ); + } + + /** + * PDFA Schema prefix. + * + * @param prefix Preferred schema namespace prefix. + */ + public void setPrefix( String prefix) + { + setTextProperty( "pdfaSchema:prefix", prefix); + } + + /** + * Get the PDFA Schema prefix. + * + * @return Preferred schema namespace prefix. + */ + public String getPrefix() + { + return getTextProperty( "pdfaSchema:prefix" ); + } + + +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAType.java b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAType.java new file mode 100644 index 000000000..9b13a9c91 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/XMPSchemaPDFAType.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jempbox.xmp.pdfa; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchema; +import org.w3c.dom.Element; + +/** + * Define XMP properties used PDFA extension schema description schemas. + * TODO 2 naked so far, implement + * + * @author Karsten Krieg (kkrieg@intarsys.de) + * @version $Revision: 1.1 $ + */ +public class XMPSchemaPDFAType extends XMPSchema +{ + /** + * The namespace for this schema. + */ + public static final String NAMESPACE = "http://www.aiim.org/pdfa/ns/type"; + + /** + * Construct a new blank PDFA schema. + * + * @param parent The parent metadata schema that this will be part of. + */ + public XMPSchemaPDFAType( XMPMetadata parent ) + { + super( parent, "pdfaType", NAMESPACE ); + } + + /** + * Constructor from existing XML element. + * + * @param element The existing element. + * @param prefix The schema prefix. + */ + public XMPSchemaPDFAType( Element element, String prefix ) + { + super( element , prefix); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/package.html b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/package.html new file mode 100644 index 000000000..f7625bdf2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/jempbox/xmp/pdfa/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds data access classes for XMP PDFA data structures. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ConvertColorspace.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ConvertColorspace.java new file mode 100644 index 000000000..948971d98 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ConvertColorspace.java @@ -0,0 +1,470 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdfwriter.ContentStreamWriter; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.util.PDFOperator; + +/** + * This is the main program that simply parses the pdf document and replace + * change a PDF to use a specific colorspace. + * + * @author Ben Litchfield + * @author Pierre-Yves Landuré (pierre-yves@landure.org) + * @version $Revision: 1.5 $ + */ +public class ConvertColorspace +{ + + private static final String PASSWORD = "-password"; + private static final String CONVERSION = "-equiv"; + private static final String DEST_COLORSPACE = "-toColorspace"; + + /** + * private constructor. + */ + private ConvertColorspace() + { + //static class + } + + /** + * The method that replace RGB colors by CMYK ones. + * + * @param inputFile input file name. + * @param colorEquivalents a dictionnary for the color equivalents. + * @param destColorspace The destination colorspace, currently CMYK is supported. + * + * @throws IOException If there is an error parsing the document. + */ + private void replaceColors( PDDocument inputFile, + Hashtable colorEquivalents, + String destColorspace ) throws IOException + { + if( !destColorspace.equals( "CMYK" ) ) + { + throw new IOException( "Error: Unknown colorspace " + destColorspace ); + } + List pagesList = inputFile.getDocumentCatalog().getAllPages(); + + PDPage currentPage = null; + PDFStreamParser parser = null; + List pageTokens = null; + List editedPageTokens = null; + + for(int pageCounter = 0; pageCounter < pagesList.size(); pageCounter++) // For each document page + { + currentPage = (PDPage)pagesList.get( pageCounter ); + + parser = new PDFStreamParser(currentPage.getContents().getStream()); + parser.parse(); + pageTokens = parser.getTokens(); + editedPageTokens = new ArrayList(); + + for( int counter = 0; counter < pageTokens.size(); counter++) // For each page token + { + Object token = pageTokens.get( counter ); + if( token instanceof PDFOperator ) // Test if PDFOperator + { + PDFOperator tokenOperator = (PDFOperator)token; + + if(tokenOperator.getOperation().equals("rg")) // Test if "rg" Operator. + { + if( destColorspace.equals( "CMYK" ) ) + { + replaceRGBTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); + editedPageTokens.add( PDFOperator.getOperator( "k" )); + } + } + else if(tokenOperator.getOperation().equals("RG")) // Test if "rg" Operator. + { + if( destColorspace.equals( "CMYK" ) ) + { + replaceRGBTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); + editedPageTokens.add( PDFOperator.getOperator( "K" )); + } + } + else if(tokenOperator.getOperation().equals("g")) // Test if "rg" Operator. + { + if( destColorspace.equals( "CMYK" ) ) + { + replaceGrayTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); + editedPageTokens.add( PDFOperator.getOperator( "k" )); + } + } + else if(tokenOperator.getOperation().equals("G")) // Test if "rg" Operator. + { + if( destColorspace.equals( "CMYK" ) ) + { + replaceGrayTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); + editedPageTokens.add( PDFOperator.getOperator( "K" )); + } + } + else + { + editedPageTokens.add( token ); + } + } + else // Test if PDFOperator + { + editedPageTokens.add( token ); + } + } // For each page token + + // We replace original page content by the edited one. + PDStream updatedPageContents = new PDStream(inputFile); + ContentStreamWriter contentWriter = new ContentStreamWriter( updatedPageContents.createOutputStream() ); + contentWriter.writeTokens( editedPageTokens ); + currentPage.setContents( updatedPageContents ); + + } // For each document page + } + + private void replaceRGBTokensWithCMYKTokens( List editedPageTokens, + List pageTokens, + int counter, + Hashtable colorEquivalents ) + { +// Get current RGB color. + float red = ((COSNumber)pageTokens.get( counter - 3 )).floatValue(); + float green = ((COSNumber)pageTokens.get( counter - 2 )).floatValue(); + float blue = ((COSNumber)pageTokens.get( counter - 1 )).floatValue(); + + int intRed = Math.round(red * 255.0f); + int intGreen = Math.round(green * 255.0f); + int intBlue = Math.round(blue * 255.0f); + + ColorSpaceInstance rgbColor = new ColorSpaceInstance(); + rgbColor.colorspace = "RGB"; + rgbColor.colorspaceValues = new int[] { intRed, intGreen, intBlue }; + ColorSpaceInstance cmykColor = (ColorSpaceInstance)colorEquivalents.get(rgbColor); + float[] cmyk = null; + + if( cmykColor != null ) + { + cmyk = new float[] { + cmykColor.colorspaceValues[0] / 100.0f, + cmykColor.colorspaceValues[1] / 100.0f, + cmykColor.colorspaceValues[2] / 100.0f, + cmykColor.colorspaceValues[3] / 100.0f + }; + } + else + { + cmyk = convertRGBToCMYK( red, green, blue ); + } + + //remove the RGB components that are already part of the editedPageTokens list + editedPageTokens.remove( editedPageTokens.size() -1 ); + editedPageTokens.remove( editedPageTokens.size() -1 ); + editedPageTokens.remove( editedPageTokens.size() -1 ); + + // Add the new CMYK color + editedPageTokens.add( new COSFloat( cmyk[0] ) ); + editedPageTokens.add( new COSFloat( cmyk[1] ) ); + editedPageTokens.add( new COSFloat( cmyk[2] ) ); + editedPageTokens.add( new COSFloat( cmyk[3] ) ); + } + + private void replaceGrayTokensWithCMYKTokens( List editedPageTokens, + List pageTokens, + int counter, + Hashtable colorEquivalents ) + { +// Get current RGB color. + float gray = ((COSNumber)pageTokens.get( counter - 1 )).floatValue(); + + ColorSpaceInstance grayColor = new ColorSpaceInstance(); + grayColor.colorspace = "Grayscale"; + grayColor.colorspaceValues = new int[] { Math.round( gray * 100 ) }; + ColorSpaceInstance cmykColor = (ColorSpaceInstance)colorEquivalents.get(grayColor); + float[] cmyk = null; + + if( cmykColor != null ) + { + cmyk = new float[] { + cmykColor.colorspaceValues[0] / 100.0f, + cmykColor.colorspaceValues[1] / 100.0f, + cmykColor.colorspaceValues[2] / 100.0f, + cmykColor.colorspaceValues[3] / 100.0f + }; + } + else + { + cmyk = new float[] {0,0,0,gray}; + } + + //remove the Gray components that are already part of the editedPageTokens list + editedPageTokens.remove( editedPageTokens.size() -1 ); + + // Add the new CMYK color + editedPageTokens.add( new COSFloat( cmyk[0] ) ); + editedPageTokens.add( new COSFloat( cmyk[1] ) ); + editedPageTokens.add( new COSFloat( cmyk[2] ) ); + editedPageTokens.add( new COSFloat( cmyk[3] ) ); + } + + private static float[] convertRGBToCMYK( float red, float green, float blue ) + { + // + // RGB->CMYK from From + // http://en.wikipedia.org/wiki/Talk:CMYK_color_model + // + float c = 1.0f - red; + float m = 1.0f - green; + float y = 1.0f - blue; + float k = 1.0f; + + k = Math.min( Math.min( Math.min( c,k ), m), y ); + + c = ( c - k ) / ( 1 - k ); + m = ( m - k ) / ( 1 - k ); + y = ( y - k ) / ( 1 - k ); + return new float[] { c,m,y,k}; + } + + private static int[] stringToIntArray( String string ) + { + String[] ints = string.split( "," ); + int[] retval = new int[ints.length]; + for( int i=0; i= args.length ) + { + usage(); + } + password = args[i]; + } + if( args[i].equals( DEST_COLORSPACE ) ) + { + i++; + if( i >= args.length ) + { + usage(); + } + destColorspace = args[i]; + } + if(args[i].equals( CONVERSION ) ) + { + i++; + if( i >= args.length ) + { + usage(); + } + + colorEquivalentMatcher = colorEquivalentPattern.matcher(args[i]); + if(!colorEquivalentMatcher.matches()) + { + usage(); + } + String srcColorSpace = colorEquivalentMatcher.group(1); + String srcColorvalues = colorEquivalentMatcher.group(2); + String destColorSpace = colorEquivalentMatcher.group(3); + String destColorvalues = colorEquivalentMatcher.group(4); + + ConvertColorspace.ColorSpaceInstance source = new ColorSpaceInstance(); + source.colorspace = srcColorSpace; + source.colorspaceValues = stringToIntArray( srcColorvalues ); + + ColorSpaceInstance dest = new ColorSpaceInstance(); + dest.colorspace = destColorSpace; + dest.colorspaceValues = stringToIntArray( destColorvalues ); + + colorEquivalents.put(source, dest); + + } + else + { + if( inputFile == null ) + { + inputFile = args[i]; + } + else + { + outputFile = args[i]; + } + } + } + + if( inputFile == null ) + { + usage(); + } + + if( outputFile == null || outputFile.equals(inputFile)) + { + usage(); + } + + PDDocument doc = null; + try + { + doc = PDDocument.load( inputFile ); + if( doc.isEncrypted() ) + { + try + { + doc.decrypt( password ); + } + catch( InvalidPasswordException e ) + { + if( !password.equals( "" ) )//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(); + } + } + } + ConvertColorspace converter = new ConvertColorspace(); + converter.replaceColors(doc, colorEquivalents, destColorspace ); + doc.save( outputFile ); + } + finally + { + if( doc != null ) + { + doc.close(); + } + } + + + } + + + /** + * This will print the usage requirements and exit. + */ + private static void usage() + { + System.err.println( "Usage: java org.apache.pdfbox.ConvertColorspace [OPTIONS] " + +"\n" + + " -password Password to decrypt document\n" + + " -equiv Color equivalent to use for conversion.\n" + + " -destColorspace The destination colorspace, CMYK is the only '" + + "supported colorspace." + + " \n" + + " The equiv format is : :(colorspace value)=:(colorspace value)" + + " This option can be used as many times as necessary\n" + + " The supported equiv colorspaces are RGB and CMYK.\n" + + " RGB color values are integers between 0 and 255" + + " CMYK color values are integer between 0 and 100.\n" + + " Example: java org.apache.pdfbox.ConvertColorspace -equiv RGB:(255,0,0)=CMYK(0,99,100,0)" + + " input.pdf output.pdf\n" + + " The PDF document to use\n" + + " The PDF file to write the result to. Must be different of input file\n" + ); + System.exit( 1 ); + } + + /** + * + * + */ + private static class ColorSpaceInstance + { + private String colorspace = null; + private int[] colorspaceValues = null; + + /** + * {@inheritDoc} + */ + public int hashCode() + { + int code = colorspace.hashCode(); + for( int i=0; i
+ * + * usage: java org.apache.pdfbox.Decrypt <password> <inputfile> <outputfile> + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class Decrypt +{ + private static final String ALIAS = "-alias"; + private static final String PASSWORD = "-password"; + private static final String KEYSTORE = "-keyStore"; + + private 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 > 5 ) + { + usage(); + } + else + { + String password = null; + String infile = null; + String outfile = null; + String alias = null; + String keyStore = null; + for( int i=0; i= args.length ) + { + usage(); + } + alias = args[i]; + } + else if( args[i].equals( KEYSTORE ) ) + { + i++; + if( i >= args.length ) + { + usage(); + } + keyStore = args[i]; + } + else if( args[i].equals( PASSWORD ) ) + { + i++; + if( i >= args.length ) + { + usage(); + } + password = args[i]; + } + else if( infile == null ) + { + infile = args[i]; + } + else if( outfile == null ) + { + outfile = args[i]; + } + else + { + usage(); + } + } + if( infile == null ) + { + usage(); + } + if( outfile == null ) + { + outfile = infile; + } + if( password == null ) + { + password = ""; + } + + + PDDocument document = null; + + try + { + document = PDDocument.load( infile ); + + if( document.isEncrypted() ) + { + DecryptionMaterial decryptionMaterial = null; + if( keyStore != null ) + { + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(new FileInputStream(keyStore), password.toCharArray()); + + decryptionMaterial = new PublicKeyDecryptionMaterial(ks, alias, password); + } + else + { + decryptionMaterial = new StandardDecryptionMaterial(password); + } + document.openProtection(decryptionMaterial); + AccessPermission ap = document.getCurrentAccessPermission(); + if(ap.isOwnerPermission()) + { + 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 -jar pdfbox-app-x.y.z.jar Decrypt " + + "[options] [outputfile]" ); + System.err.println( "-alias The alias of the key in the certificate file " + + "(mandatory if several keys are available)"); + System.err.println( "-password The password to open the certificate and extract the private key from it." ); + System.err.println( "-keyStore The KeyStore that holds the certificate." ); + System.exit( -1 ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/Encrypt.java b/fluidbook/tools/fwstk/src/apache/pdfbox/Encrypt.java new file mode 100644 index 000000000..8a7192d38 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/Encrypt.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.apache.pdfbox.pdmodel.encryption.PublicKeyProtectionPolicy; +import org.apache.pdfbox.pdmodel.encryption.PublicKeyRecipient; +import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; + +/** + * This will read a document from the filesystem, encrypt it and and then write + * the results to the filesystem.

+ * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +public class Encrypt +{ + private 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 + { + AccessPermission ap = new AccessPermission(); + + String infile = null; + String outfile = null; + String certFile = null; + String userPassword = ""; + String ownerPassword = ""; + + int keyLength = 40; + + PDDocument document = null; + + try + { + for( int i=0; i [outputfile]" ); + System.err.println( " -O " + + "Set the owner password(ignored if cert is set)" ); + System.err.println( " -U " + + "Set the user password(ignored if cert is set)" ); + System.err.println( " -certFile Path to X.509 certificate" ); + System.err.println( " -canAssemble Set the assemble permission" ); + System.err.println( " -canExtractContent Set the extraction permission" ); + System.err.println( " -canExtractForAccessibility Set the extraction permission" ); + System.err.println( " -canFillInForm Set the fill in form permission" ); + System.err.println( " -canModify Set the modify permission" ); + System.err.println( " -canModifyAnnotations Set the modify annots permission" ); + System.err.println( " -canPrint Set the print permission" ); + System.err.println( " -canPrintDegraded Set the print degraded permission" ); + System.err.println( " -keyLength The length of the key in bits(40)" ); + System.err.println( "\nNote: By default all permissions are set to true!" ); + System.exit( 1 ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ExportFDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ExportFDF.java new file mode 100644 index 000000000..79c0f4839 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ExportFDF.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; + +import org.apache.pdfbox.pdmodel.fdf.FDFDocument; + +/** + * This example will take a PDF document and fill the fields with data from the + * FDF fields. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class ExportFDF +{ + /** + * Creates a new instance of ImportFDF. + */ + public ExportFDF() + { + } + + /** + * This will import an fdf document and write out another pdf. + *
+ * 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.apache.pdfbox.ExortFDF [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(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ExportXFDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ExportXFDF.java new file mode 100644 index 000000000..35a39d8e6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ExportXFDF.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; + +import org.apache.pdfbox.pdmodel.fdf.FDFDocument; + +/** + * This example will take a PDF document and fill the fields with data from the + * FDF fields. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class ExportXFDF +{ + /** + * Creates a new instance of ImportFDF. + */ + public ExportXFDF() + { + } + + /** + * This will import an fdf document and write out another pdf. + *
+ * 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.apache.pdfbox.ExortXFDF [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(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ExtractImages.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ExtractImages.java new file mode 100644 index 000000000..2e885d6b7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ExtractImages.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; + +/** + * This will read a read pdf and extract images.

+ * + * usage: java org.apache.pdfbox.ExtractImages <pdffile> <password> [imageprefix] + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +public class ExtractImages +{ + private int imageCounter = 1; + + private static final String PASSWORD = "-password"; + private static final String PREFIX = "-prefix"; + private static final String ADDKEY = "-addkey"; + + private ExtractImages() + { + } + + /** + * 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; + boolean addKey = false; + for( int i=0; i= args.length ) + { + usage(); + } + password = args[i]; + } + else if( args[i].equals( PREFIX ) ) + { + i++; + if( i >= args.length ) + { + usage(); + } + prefix = args[i]; + } + else if( args[i].equals( ADDKEY ) ) + { + addKey = true; + } + else + { + if( pdfFile == null ) + { + pdfFile = args[i]; + } + } + } + if(pdfFile == null) + { + usage(); + } + else + { + if( prefix == null && pdfFile.length() >4 ) + { + prefix = pdfFile.substring( 0, pdfFile.length() -4 ); + } + + PDDocument document = null; + + try + { + document = PDDocument.load( pdfFile ); + + if( document.isEncrypted() ) + { + + StandardDecryptionMaterial spm = new StandardDecryptionMaterial(password); + document.openProtection(spm); + AccessPermission ap = document.getCurrentAccessPermission(); + + + if( ! ap.canExtractContent() ) + { + throw new IOException( + "Error: 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 = null; + if (addKey) + { + name = getUniqueFileName( prefix + "_" + key, image.getSuffix() ); + } + else + { + name = getUniqueFileName( prefix, 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.apache.pdfbox.ExtractImages [OPTIONS] \n" + + " -password Password to decrypt document\n" + + " -prefix Image prefix(default to pdf name)\n" + + " -addkey add the internal image key to the file name\n" + + " The PDF document to use\n" + ); + System.exit( 1 ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ExtractText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ExtractText.java new file mode 100644 index 000000000..87c90cc43 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ExtractText.java @@ -0,0 +1,311 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial; +import org.apache.pdfbox.util.PDFText2HTML; +import org.apache.pdfbox.util.PDFTextStripper; + +/** + * This is the main program that simply parses the pdf document and transforms it + * into text. + * + * @author Ben Litchfield + * @version $Revision: 1.14 $ + */ +public class ExtractText +{ + 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 IGNORE_BEADS = "-ignoreBeads"; + private static final String DEBUG = "-debug"; + private static final String HTML = "-html"; // jjb - added simple HTML output + private static final String FORCE = "-force"; //enables pdfbox to skip corrupt objects + + /* + * debug flag + */ + private boolean debug = false; + + /** + * 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 + { + ExtractText extractor = new ExtractText(); + extractor.startExtraction(args); + } + + public void startExtraction( String[] args ) throws Exception + { + boolean toConsole = false; + boolean toHTML = false; + boolean force = false; + boolean sort = false; + boolean separateBeads = true; + String password = ""; + String encoding = null; + String pdfFile = null; + String outputFile = null; + // Defaults to text files + String ext = ".txt"; + int startPage = 1; + int endPage = Integer.MAX_VALUE; + for( int i=0; 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; + ext = ".html"; + } + else if( args[i].equals( SORT ) ) + { + sort = true; + } + else if( args[i].equals( IGNORE_BEADS ) ) + { + separateBeads = false; + } + else if( args[i].equals( DEBUG ) ) + { + debug = 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( args[i].equals( FORCE ) ) + { + force = true; + } + else + { + if( pdfFile == null ) + { + pdfFile = args[i]; + } + else + { + outputFile = args[i]; + } + } + } + + if( pdfFile == null ) + { + usage(); + } + else + { + + Writer output = null; + PDDocument document = null; + try + { + long startTime = startProcessing("Loading PDF "+pdfFile); + try + { + //basically try to load it from a url first and if the URL + //is not recognized then try to load it from the file system. + URL url = new URL( pdfFile ); + document = PDDocument.load(url, force); + String fileName = url.getFile(); + if( outputFile == null && fileName.length() >4 ) + { + outputFile = new File( fileName.substring( 0, fileName.length() -4 ) + ext ).getName(); + } + } + catch( MalformedURLException e ) + { + document = PDDocument.load(pdfFile, force); + if( outputFile == null && pdfFile.length() >4 ) + { + outputFile = pdfFile.substring( 0, pdfFile.length() -4 ) + ext; + } + } + stopProcessing("Time for loading: ", startTime); + + if( document.isEncrypted() ) + { + StandardDecryptionMaterial sdm = new StandardDecryptionMaterial( password ); + document.openProtection( sdm ); + AccessPermission ap = document.getCurrentAccessPermission(); + + if( ! ap.canExtractContent() ) + { + throw new IOException( "You do not have permission to extract text" ); + } + } + + if ((encoding == null) && (toHTML)) + { + encoding = "UTF-8"; + } + + if( toConsole ) + { + output = new OutputStreamWriter( System.out ); + } + else + { + if( encoding != null ) + { + output = new OutputStreamWriter( + new FileOutputStream( outputFile ), encoding ); + } + else + { + //use default encoding + output = new OutputStreamWriter( + new FileOutputStream( outputFile ) ); + } + } + + PDFTextStripper stripper = null; + if(toHTML) + { + stripper = new PDFText2HTML(encoding); + } + else + { + stripper = new PDFTextStripper(encoding); + } + stripper.setForceParsing( force ); + stripper.setSortByPosition( sort ); + stripper.setShouldSeparateByBeads( separateBeads ); + stripper.setStartPage( startPage ); + stripper.setEndPage( endPage ); + + startTime = startProcessing("Starting text extraction"); + stripper.writeText( document, output ); + stopProcessing("Time for extraction: ", startTime); + } + finally + { + if( output != null ) + { + output.close(); + } + if( document != null ) + { + document.close(); + } + } + } + } + + private long startProcessing(String message) { + if (debug) + { + System.err.println(message); + } + return System.currentTimeMillis(); + } + + private void stopProcessing(String message, long startTime) { + if (debug) + { + long stopTime = System.currentTimeMillis(); + float elapsedTime = ((float)(stopTime - startTime))/1000; + System.err.println(message + elapsedTime + " seconds"); + } + } + + /** + * This will print the usage requirements and exit. + */ + private static void usage() + { + System.err.println( "Usage: java -jar pdfbox-app-x.y.z.jar ExtractText [OPTIONS] [Text File]\n" + + " -password Password to decrypt document\n" + + " -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" + + " -ignoreBeads Disables the separation by beads\n" + + " -force Enables pdfbox to ignore corrupt objects\n" + + " -debug Enables debug output about the time consumption of every stage\n" + + " -startPage The first page to start extraction(1 based)\n" + + " -endPage The last page to extract(inclusive)\n" + + " The PDF document to use\n" + + " [Text File] The file to write the text to\n" + ); + System.exit( 1 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ImportFDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ImportFDF.java new file mode 100644 index 000000000..ebe2a6377 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ImportFDF.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; + +import org.apache.pdfbox.pdmodel.fdf.FDFDocument; + +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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. + *
+ * 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.apache.pdfbox.ImportFDF " ); + } + + /** + * 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(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/ImportXFDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/ImportXFDF.java new file mode 100644 index 000000000..a670fd025 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/ImportXFDF.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; + +import org.apache.pdfbox.pdmodel.fdf.FDFDocument; + +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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. + *
+ * 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.apache.pdfbox.ImportXFDF " ); + } + + /** + * 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(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/Overlay.java b/fluidbook/tools/fwstk/src/apache/pdfbox/Overlay.java new file mode 100644 index 000000000..2615347ed --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/Overlay.java @@ -0,0 +1,504 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdfparser.PDFParser; +import org.apache.pdfbox.pdfwriter.COSWriter; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.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.
+ * e.g. Overlay an invoice with your company layout
+ *
+ * How it (should) work:
+ * If the document has 10 pages, and the layout 2 the following is the result:
+ *
+ * Document: 1234567890
+ * Layout  : 1212121212
+ * 
+ *
+ * + * @author Mario Ivankovits (mario@ops.co.at) + * @author Ben Litchfield + * + * @version $Revision: 1.7 $ + */ +public class Overlay +{ + /** + * @deprecated use the {@link COSName#XOBJECT} constant instead + */ + public static final COSName XOBJECT = COSName.XOBJECT; + + /** + * @deprecated use the {@link COSName#PROC_SET} constant instead + */ + public static final COSName PROC_SET = COSName.PROC_SET; + + /** + * @deprecated use the {@link COSName#EXT_G_STATE} constant instead + */ + public static final COSName EXT_G_STATE = COSName.EXT_G_STATE; + + 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.

+ * + * usage: java org.apache.pdfbox.Overlay <overlay.pdf> <document.pdf> <result.pdf> + * + * @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 -jar pdfbox-app-x.y.z.jar Overlay " ); + } + + /** + * 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("ISO-8859-1") ); + saveStream.flush(); + + restoreGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() ); + OutputStream restoreStream = restoreGraphicsStateStream.createUnfilteredStream(); + restoreStream.write( " Q\n".getBytes("ISO-8859-1") ); + 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 + // + // + // + 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 ) + { + + for (Map.Entry entry : sourceDict.entrySet()) + { + COSName mappedKey = (COSName) objectNameMap.get(entry.getKey().getName()); + if (mappedKey != null) + { + destDict.setItem(mappedKey, entry.getValue()); + } + } + } + } + + /** + * 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 && sourceDictIdxPDFBOX-687 + */ +public class PDFBox { + + public static void main(String[] args) { + if (args.length > 0) { + String command = args[0]; + String[] arguments = new String[args.length - 1]; + System.arraycopy(args, 1, arguments, 0, arguments.length); + boolean exitAfterCallingMain = true; + try { + if (command.equals("ConvertColorspace")) { + ConvertColorspace.main(arguments); + } else if (command.equals("Decrypt")) { + Decrypt.main(arguments); + } else if (command.equals("Encrypt")) { + Encrypt.main(arguments); + } else if (command.equals("ExtractText")) { + ExtractText.main(arguments); + } else if (command.equals("Overlay")) { + Overlay.main(arguments); + } else if (command.equals("PrintPDF")) { + PrintPDF.main(arguments); + } else if (command.equals("PDFDebugger")) { + PDFDebugger.main(arguments); + exitAfterCallingMain = false; + } else if (command.equals("PDFMerger")) { + PDFMerger.main(arguments); + } else if (command.equals("PDFReader")) { + PDFReader.main(arguments); + exitAfterCallingMain = false; + } else if (command.equals("PDFSplit")) { + PDFSplit.main(arguments); + } else if (command.equals("PDFToImage")) { + PDFToImage.main(arguments); + } else if (command.equals("TextToPDF")) { + TextToPDF.main(arguments); + } else if (command.equals("WriteDecodedDoc")) { + WriteDecodedDoc.main(arguments); + } + else { + showMessageAndExit(); + } + if (exitAfterCallingMain) { + System.exit(0); + } + } catch (Exception e) { + System.err.println( + command + " failed with the following exception:"); + e.printStackTrace(); + System.exit(1); + } + } + else { + showMessageAndExit(); + } + } + + private static void showMessageAndExit() { + System.err.println( + "usage: java pdfbox-app-x.y.z.jar "); + System.exit(1); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/PDFDebugger.java b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFDebugger.java new file mode 100644 index 000000000..5d251916c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFDebugger.java @@ -0,0 +1,435 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; + +import org.apache.pdfbox.pdfviewer.PDFTreeModel; +import org.apache.pdfbox.pdfviewer.PDFTreeCellRenderer; +import org.apache.pdfbox.pdfviewer.ArrayEntry; +import org.apache.pdfbox.pdfviewer.MapEntry; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.pdfbox.util.ExtensionFileFilter; + +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; +import org.apache.pdfbox.cos.COSStream; +import org.apache.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 javax.swing.UIManager; + +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 + * @version $Revision: 1.4 $ + */ +public class PDFDebugger extends javax.swing.JFrame +{ + private File currentDir=new File("."); + private PDDocument document = null; + + /** + * 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); + + ExtensionFileFilter pdfFilter = new ExtensionFileFilter(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) + { + if( document != null ) + { + try + { + document.close(); + } + catch( IOException io ) + { + io.printStackTrace(); + } + } + System.exit(0); + } + + /** + * Exit the Application. + */ + private void exitForm(java.awt.event.WindowEvent evt) + { + if( document != null ) + { + try + { + document.close(); + } + catch( IOException io ) + { + io.printStackTrace(); + } + } + System.exit(0); + } + + /** + * @param args the command line arguments + * + * @throws Exception If anything goes wrong. + */ + public static void main(String[] args) throws Exception + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + PDFDebugger viewer = new PDFDebugger(); + if( args.length >0 ) + { + viewer.readPDFFile( args[0] ); + } + viewer.setVisible(true); + } + + private void readPDFFile(String file) throws Exception + { + if( document != null ) + { + document.close(); + } + InputStream input = null; + File f = new File( file ); + input = new FileInputStream(f); + document = parseDocument( input ); + TreeModel model=new PDFTreeModel(document); + jTree1.setModel(model); + setTitle( "PDFBox - " + f.getAbsolutePath() ); + /* + List pages = document.getDocumentCatalog().getAllPages(); + for( int i=0; iBen Litchfield + * @version $Revision: 1.2 $ + */ +public class PDFMerger +{ + + private PDFMerger() + { + } + /** + * Infamous main method. + * + * @param args Command line arguments, should be at least 3. + * + * @throws Exception If there is an error parsing the document. + */ + public static void main( String[] args ) throws Exception + { + PDFMerger merge = new PDFMerger(); + merge.merge( args ); + } + + private void merge( String[] args ) throws Exception + { + String destinationFileName = ""; + String sourceFileName = null; + + if ( args.length < 3 ) + { + usage(); + } + + PDFMergerUtility merger = new PDFMergerUtility(); + for( int i=0; i \n" + + " 2 or more source PDF documents to merge\n" + + " The PDF document to save the merged documents to\n" + ); + System.exit( 1 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/PDFReader.java b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFReader.java new file mode 100644 index 000000000..b53dd05ec --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFReader.java @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.pdfviewer.PageWrapper; +import org.apache.pdfbox.pdfviewer.ReaderBottomPanel; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.util.ExtensionFileFilter; + +import javax.swing.JFileChooser; +import javax.swing.JScrollPane; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + +import java.awt.print.PrinterException; +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 + * @version $Revision: 1.5 $ + */ +public class PDFReader extends javax.swing.JFrame +{ + private File currentDir=new File("."); + private javax.swing.JMenuItem aboutMenuItem; + private javax.swing.JMenuItem contentsMenuItem; + 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 printMenuItem; + private javax.swing.JMenu viewMenu; + private javax.swing.JMenuItem nextPageItem; + private javax.swing.JMenuItem previousPageItem; + private JPanel documentPanel = new JPanel(); + private ReaderBottomPanel bottomStatusPanel = new ReaderBottomPanel(); + + private PDDocument document = null; + private List pages= null; + + private int currentPage = 0; + private int numberOfPages = 0; + private String currentFilename = null; + + /** + * 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(); + exitMenuItem = new javax.swing.JMenuItem(); + helpMenu = new javax.swing.JMenu(); + contentsMenuItem = new javax.swing.JMenuItem(); + aboutMenuItem = new javax.swing.JMenuItem(); + printMenuItem = new javax.swing.JMenuItem(); + viewMenu = new javax.swing.JMenu(); + nextPageItem = new javax.swing.JMenuItem(); + previousPageItem = new javax.swing.JMenuItem(); + + + setTitle("PDFBox - PDF Reader"); + addWindowListener(new java.awt.event.WindowAdapter() + { + public void windowClosing(java.awt.event.WindowEvent evt) + { + exitApplication(); + } + }); + + + 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); + + printMenuItem.setText( "Print" ); + printMenuItem.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent evt) + { + try + { + if (document != null) + { + document.print(); + } + } + catch( PrinterException e ) + { + e.printStackTrace(); + } + } + }); + fileMenu.add( printMenuItem ); + + exitMenuItem.setText("Exit"); + exitMenuItem.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent evt) + { + exitApplication(); + } + }); + + fileMenu.add(exitMenuItem); + + menuBar.add(fileMenu); + + helpMenu.setText("Help"); + contentsMenuItem.setText("Contents"); + helpMenu.add(contentsMenuItem); + + aboutMenuItem.setText("About"); + helpMenu.add(aboutMenuItem); + + //menuBar.add(helpMenu); + + viewMenu.setText("View"); + nextPageItem.setText("Next page"); + nextPageItem.setAccelerator(KeyStroke.getKeyStroke('+')); + nextPageItem.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent evt) + { + nextPage(); + } + }); + viewMenu.add(nextPageItem); + + previousPageItem.setText("Previous page"); + previousPageItem.setAccelerator(KeyStroke.getKeyStroke('-')); + previousPageItem.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent evt) + { + previousPage(); + } + }); + viewMenu.add(previousPageItem); + + menuBar.add(viewMenu); + + setJMenuBar(menuBar); + + + java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + setBounds((screenSize.width-700)/2, (screenSize.height-600)/2, 700, 600); + } + + + private void updateTitle() { + setTitle( "PDFBox - " + currentFilename + " ("+(currentPage+1)+"/"+numberOfPages+")"); + } + + private void nextPage() + { + if (currentPage < numberOfPages-1) + { + currentPage++; + updateTitle(); + showPage(currentPage); + } + } + + private void previousPage() + { + if (currentPage > 0 ) + { + currentPage--; + updateTitle(); + showPage(currentPage); + } + } + + private void openMenuItemActionPerformed(java.awt.event.ActionEvent evt) + { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(currentDir); + + ExtensionFileFilter pdfFilter = new ExtensionFileFilter(new String[] {"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(); + } + } + } + + private void exitApplication() + { + try + { + if( document != null ) + { + document.close(); + } + } + catch( IOException io ) + { + //do nothing because we are closing the application + } + this.setVisible( false ); + this.dispose(); + } + + /** + * @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.setVisible(true); + } + + 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 ); + pages = document.getDocumentCatalog().getAllPages(); + numberOfPages = pages.size(); + currentFilename = f.getAbsolutePath(); + currentPage = 0; + updateTitle(); + showPage(0); + } + + private void showPage(int pageNumber) + { + try + { + PageWrapper wrapper = new PageWrapper( this ); + wrapper.displayPage( (PDPage)pages.get(pageNumber) ); + if (documentPanel.getComponentCount() > 0) + { + documentPanel.remove(0); + } + documentPanel.add( wrapper.getPanel() ); + pack(); + } + catch (IOException exception) + { + exception.printStackTrace(); + } + } + /** + * 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.apache.pdfbox.exceptions.CryptographyException e ) + { + e.printStackTrace(); + } + } + + return document; + } + + /** + * Get the bottom status panel. + * + * @return The bottom status panel. + */ + public ReaderBottomPanel getBottomStatusPanel() + { + return bottomStatusPanel; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/PDFSplit.java b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFSplit.java new file mode 100644 index 000000000..62c5761c3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFSplit.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +import java.util.List; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdfparser.PDFParser; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.pdfbox.pdfwriter.COSWriter; + +import org.apache.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 + * @version $Revision: 1.6 $ + */ +public class PDFSplit +{ + private static final String PASSWORD = "-password"; + private static final String SPLIT = "-split"; + + private PDFSplit() + { + } + /** + * 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 ) + { + 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(); + } + else + { + + 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\n" + + " -password Password to decrypt document\n" + + " -split split after this many pages\n" + + " The PDF document to use\n" + ); + System.exit( 1 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/PDFToImage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFToImage.java new file mode 100644 index 000000000..2bf300e5a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/PDFToImage.java @@ -0,0 +1,308 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.util.List; + +import javax.imageio.ImageIO; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.util.PDFImageWriter; + +/** + * Convert a PDF document to an image. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +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_FORMAT = "-imageType"; + private static final String OUTPUT_PREFIX = "-outputPrefix"; + private static final String COLOR = "-color"; + private static final String RESOLUTION = "-resolution"; + private static final String CROPBOX = "-cropbox"; + + /** + * 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 imageFormat = "jpg"; + int startPage = 1; + int endPage = Integer.MAX_VALUE; + String color = "rgb"; + int resolution; + float cropBoxLowerLeftX = 0; + float cropBoxLowerLeftY = 0; + float cropBoxUpperRightX = 0; + float cropBoxUpperRightY = 0; + try + { + resolution = Toolkit.getDefaultToolkit().getScreenResolution(); + } + catch( HeadlessException e ) + { + resolution = 96; + } + 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_FORMAT ) ) + { + i++; + imageFormat = args[i]; + } + else if( args[i].equals( OUTPUT_PREFIX ) ) + { + i++; + outputPrefix = args[i]; + } + else if( args[i].equals( COLOR ) ) + { + i++; + color = args[i]; + } + else if( args[i].equals( RESOLUTION ) ) + { + i++; + resolution = Integer.parseInt(args[i]); + } + else if( args[i].equals( CROPBOX ) ) + { + i++; + cropBoxLowerLeftX = Float.valueOf(args[i]).floatValue(); + i++; + cropBoxLowerLeftY = Float.valueOf(args[i]).floatValue(); + i++; + cropBoxUpperRightX = Float.valueOf(args[i]).floatValue(); + i++; + cropBoxUpperRightY = Float.valueOf(args[i]).floatValue(); + } + else + { + if( pdfFile == null ) + { + pdfFile = args[i]; + } + } + } + if( pdfFile == null ) + { + usage(); + } + else + { + 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 supply a password and the default of "" was wrong. + System.err.println( "Error: The document is encrypted." ); + usage(); + } + } + } + int imageType = 24; + if ("bilevel".equalsIgnoreCase(color)) + { + imageType = BufferedImage.TYPE_BYTE_BINARY; + } + else if ("indexed".equalsIgnoreCase(color)) + { + imageType = BufferedImage.TYPE_BYTE_INDEXED; + } + else if ("gray".equalsIgnoreCase(color)) + { + imageType = BufferedImage.TYPE_BYTE_GRAY; + } + else if ("rgb".equalsIgnoreCase(color)) + { + imageType = BufferedImage.TYPE_INT_RGB; + } + else if ("rgba".equalsIgnoreCase(color)) + { + imageType = BufferedImage.TYPE_INT_ARGB; + } + else + { + System.err.println( "Error: the number of bits per pixel must be 1, 8 or 24." ); + System.exit( 2 ); + } + + //if a CropBox has been specified, update the CropBox: + //changeCropBoxes(PDDocument document,float a, float b, float c,float d) + if ( cropBoxLowerLeftX!=0 || cropBoxLowerLeftY!=0 + || cropBoxUpperRightX!=0 || cropBoxUpperRightY!=0 ) + { + changeCropBoxes(document, + cropBoxLowerLeftX, cropBoxLowerLeftY, + cropBoxUpperRightX, cropBoxUpperRightY); + } + + //Make the call + PDFImageWriter imageWriter = new PDFImageWriter(); + boolean success = imageWriter.writeImage(document, imageFormat, password, + startPage, endPage, outputPrefix, imageType, resolution); + if (!success) + { + System.err.println( "Error: no writer found for image format '" + + imageFormat + "'" ); + System.exit(1); + } + } + catch (Exception e) + { + System.err.println(e); + } + finally + { + if( document != null ) + { + document.close(); + } + } + } + } + + /** + * This will print the usage requirements and exit. + */ + private static void usage() + { + System.err.println( "Usage: java -jar pdfbox-app-x.y.z.jar PDFToImage [OPTIONS] \n" + + " -password Password to decrypt document\n" + + " -imageType (" + getImageFormats() + ")\n" + + " -outputPrefix Filename prefix for image files\n" + + " -startPage The first page to start extraction(1 based)\n" + + " -endPage The last page to extract(inclusive)\n" + + " -color The color depth (valid: bilevel, indexed, gray, rgb, rgba)\n" + + " -resolution The bitmap resolution in dpi\n" + + " -cropbox The page area to export\n" + + " 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(); + } + + private static void changeCropBoxes(PDDocument document,float a, float b, float c,float d) + { + List pages = document.getDocumentCatalog().getAllPages(); + for( int i = 0; i < pages.size(); i++ ) + { + System.out.println("resizing page"); + PDPage page = (PDPage)pages.get( i ); + PDRectangle rectangle = new PDRectangle(); + rectangle.setLowerLeftX(a); + rectangle.setLowerLeftY(b); + rectangle.setUpperRightX(c); + rectangle.setUpperRightY(d); + page.setMediaBox(rectangle); + page.setCropBox(rectangle); + + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/PdfDecompressor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/PdfDecompressor.java new file mode 100644 index 000000000..c2a353c7c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/PdfDecompressor.java @@ -0,0 +1,89 @@ +/* + * Copyright 2010 adam. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * under the License. + */ + +package org.apache.pdfbox; + +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdfparser.PDFObjectStreamParser; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.persistence.util.COSObjectKey; + +/** + * This program will just take all of the stream objects in a PDF and dereference + * them. The streams will be gone in the resulting file and the objects will be + * present. This is very helpful when trying to debug problems as it'll make + * it possible to easily look through a PDF using a text editor. It also exposes + * problems which stem from objects inside object streams overwriting other + * objects. + * @author Adam Nichols + */ +public class PdfDecompressor { + + /** + * This is a very simple program, so everything is in the main method. + * @param args arguments to the program + */ + public static void main(String[] args) { + if(args.length < 1) + usage(); + + String inputFilename = args[0]; + String outputFilename; + if(args.length > 1) { + outputFilename = args[1]; + } else { + if(inputFilename.matches(".*\\.[pP][dD][fF]$")) + outputFilename = inputFilename.replaceAll("\\.[pP][dD][fF]$", ".unc.pdf"); + else + outputFilename = inputFilename + ".unc.pdf"; + } + + PDDocument doc = null; + try { + doc = PDDocument.load(inputFilename); + for(COSObject objStream : doc.getDocument().getObjectsByType("ObjStm")) { + COSStream stream = (COSStream)objStream.getObject(); + PDFObjectStreamParser sp = new PDFObjectStreamParser(stream, doc.getDocument()); + sp.parse(); + for(COSObject next : sp.getObjects()) { + COSObjectKey key = new COSObjectKey(next); + COSObject obj = doc.getDocument().getObjectFromPool(key); + obj.setObject(next.getObject()); + } + doc.getDocument().removeObject(new COSObjectKey(objStream)); + } + doc.save(outputFilename); + } catch(Exception e) { + System.out.println("Error processing file: " + e.getMessage()); + } finally { + if(doc != null) + try { doc.close(); } catch(Exception e) { } + } + } + + /** + * Explains how to use the program. + */ + private static void usage() { + System.err.println( "Usage: java -cp /path/to/pdfbox.jar;/path/to/commons-logging-api.jar " + + "org.apache.pdfbox.PdfDecompressor []\n" + + " The PDF document to decompress\n" + + " The output filename (default is to replace .pdf with .unc.pdf)"); + System.exit(1); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/PrintPDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/PrintPDF.java new file mode 100644 index 000000000..84c354845 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/PrintPDF.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.awt.print.PrinterJob; + +import javax.print.PrintService; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import java.io.File; + +/** + * This is a command line program that will print a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class PrintPDF +{ + + private static final String PASSWORD = "-password"; + private static final String SILENT = "-silentPrint"; + private static final String PRINTER_NAME = "-printerName"; + + /** + * 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; + boolean silentPrint = false; + String printerName = null; + for( int i=0; i= args.length ) + { + usage(); + } + password = args[i]; + } + else if( args[i].equals( PRINTER_NAME ) ) + { + i++; + if( i >= args.length ) + { + usage(); + } + printerName = args[i]; + } + else if( args[i].equals( SILENT ) ) + { + silentPrint = true; + } + else + { + pdfFile = args[i]; + } + } + + if( pdfFile == null ) + { + usage(); + } + + PDDocument document = null; + try + { + document = PDDocument.load( pdfFile ); + + if( document.isEncrypted() ) + { + document.decrypt( password ); + } + + PrinterJob printJob = PrinterJob.getPrinterJob(); + printJob.setJobName(new File(pdfFile).getName()); + + if(printerName != null ) + { + PrintService[] printService = PrinterJob.lookupPrintServices(); + boolean printerFound = false; + for(int i = 0; !printerFound && i < printService.length; i++) + { + if(printService[i].getName().indexOf(printerName) != -1) + { + printJob.setPrintService(printService[i]); + printerFound = true; + } + } + } + + if( silentPrint ) + { + document.silentPrint( printJob ); + } + else + { + document.print( printJob ); + } + } + finally + { + if( document != null ) + { + document.close(); + } + } + } + + /** + * This will print the usage requirements and exit. + */ + private static void usage() + { + System.err.println( "Usage: java -jar pdfbox-app-x.y.z.jar PrintPDF [OPTIONS] \n" + + " -password Password to decrypt document\n" + + " -silentPrint Print without prompting for printer info\n" + ); + System.exit( 1 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/TextToPDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/TextToPDF.java new file mode 100644 index 000000000..cad78dc56 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/TextToPDF.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDSimpleFont; +import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; + + +/** + * This will take a text file and ouput a pdf with that text. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 + { + + final 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; + + // There is a special case of creating a PDF document from an empty string. + boolean textIsEmpty = true; + + while( (nextLine = data.readLine()) != null ) + { + + // The input text is nonEmpty. New pages will be created and added + // to the PDF document as they are needed, depending on the length of + // the text. + textIsEmpty = false; + + 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 ) + { + // We have crossed the end-of-page boundary and need to extend the + // document by another page. + 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 ); + + if( contentStream == null ) + { + throw new IOException( "Error:Expected non-null content stream." ); + } + contentStream.moveTextPositionByAmount( 0, -height); + y -= height; + contentStream.drawString( nextLineToDraw.toString() ); + } + + + } + + // If the input text was the empty string, then the above while loop will have short-circuited + // and we will not have added any PDPages to the document. + // So in order to make the resultant PDF document readable by Adobe Reader etc, we'll add an empty page. + if (textIsEmpty) + { + doc.addPage(page); + } + + 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 some text in it. + *
+ * 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 " ); + System.err.println( " -standardFont default:" + PDType1Font.HELVETICA.getBaseFont() ); + for( int i=0; i The TTF font to use."); + System.err.println( " -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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/Version.java b/fluidbook/tools/fwstk/src/apache/pdfbox/Version.java new file mode 100644 index 000000000..082fceff9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/Version.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; + +import java.util.Properties; + +import org.apache.pdfbox.util.ResourceLoader; + + +/** + * A simple command line utility to get the version of PDFBox. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class Version +{ + private static final String PDFBOX_VERSION_PROPERTIES = + "org/apache/pdfbox/resources/pdfbox.properties"; + + 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, false ); + 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() ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/WriteDecodedDoc.java b/fluidbook/tools/fwstk/src/apache/pdfbox/WriteDecodedDoc.java new file mode 100644 index 000000000..4c14f7ce5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/WriteDecodedDoc.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox; + +import java.io.IOException; + +import java.util.Iterator; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.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.apache.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. + *
+ * 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: java -jar pdfbox-app-x.y.z.jar WriteDecodedDoc " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSArray.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSArray.java new file mode 100644 index 000000000..b8ca6dc2c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSArray.java @@ -0,0 +1,556 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * An array of PDFBase objects as part of the PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.24 $ + */ +public class COSArray extends COSBase implements Iterable +{ + 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 ) + { + if( objectList != null ) + { + 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, COSInteger.get(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(); + } + else 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 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 ( index < size() ) + { + Object obj = objects.get( index ); + if( obj instanceof COSNumber ) + { + retval = ((COSNumber)obj).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, COSInteger.get( 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() ) + { + Object obj = objects.get( index ); + if( obj instanceof COSName ) + { + retval = ((COSName)obj).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() ) + { + Object obj = objects.get( index ); + if( obj instanceof COSString ) + { + retval = ((COSString)obj).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 objects.remove( i ); + } + + /** + * This will remove an element from the array. + * + * @param o The object to remove. + * + * @return true if the object was removed, false + * otherwise + */ + public boolean remove( COSBase o ) + { + return objects.remove( o ); + } + + /** + * This will remove an element from the array. + * This method will also remove a reference to the object. + * + * @param o The object to remove. + * @return true if the object was removed, false + * otherwise + */ + public boolean removeObject(COSBase o) + { + boolean removed = this.remove(o); + if (!removed) + { + for (int i = 0; i < this.size(); i++) + { + COSBase entry = this.get(i); + if (entry instanceof COSObject) + { + COSObject objEntry = (COSObject) entry; + if (objEntry.getObject().equals(o)) + { + return this.remove(entry); + } + } + } + } + return removed; + } + + /** + * {@inheritDoc} + */ + @Override + 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 toList() + { + ArrayList retList = new ArrayList(size()); + for (int i = 0; i < size(); i++) + { + retList.add(get(i)); + } + return retList; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBase.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBase.java new file mode 100644 index 000000000..442682a8c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBase.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import org.apache.pdfbox.filter.FilterManager; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +/** + * The base object that all objects in the PDF document will extend. + * + * @author Ben Litchfield + * @version $Revision: 1.14 $ + */ +public abstract class COSBase implements COSObjectable +{ + /** + * Constructor. + */ + + private boolean needToBeUpdate; + + private boolean direct; + + public COSBase() + { + needToBeUpdate = false; + } + + /** + * 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; + + public void setNeedToBeUpdate(boolean flag) + { + needToBeUpdate = flag; + } + + /** + * If the state is set true, the dictionary will be written direct into the called object. + * This means, no indirect object will be created. + * + * @return the state + */ + public boolean isDirect() + { + return direct; + } + + /** + * Set the state true, if the dictionary should be written as a direct object and not indirect. + * + * @param direct set it true, for writting direct object + */ + public void setDirect(boolean direct) + { + this.direct = direct; + } + + public boolean isNeedToBeUpdate() + { + return needToBeUpdate; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBoolean.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBoolean.java new file mode 100644 index 000000000..f37922b32 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSBoolean.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +/** + * This class represents a boolean value in the PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.14 $ + */ +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 ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionary.java new file mode 100644 index 000000000..e2b9710b6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionary.java @@ -0,0 +1,1431 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.util.DateConverter; + +/** + * This class represents a dictionary where name/value pairs reside. + * + * @author Ben Litchfield + * @version $Revision: 1.32 $ + */ +public class COSDictionary extends COSBase +{ + private static final String PATH_SEPARATOR = "/"; + + /** + * The name-value pairs of this dictionary. The pairs are kept in the + * order they were added to the dictionary. + */ + protected final Map items = + new LinkedHashMap(); + + /** + * 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.putAll( dict.items ); + } + + /** + * @see java.util.Map#containsValue(java.lang.Object) + * + * @param value The value to find in the map. + * + * @return true if the map contains this value. + */ + public boolean containsValue( Object value ) + { + boolean contains = items.containsValue( value ); + if( !contains && value instanceof COSObject ) + { + contains = items.containsValue( ((COSObject)value).getObject()); + } + return contains; + } + + /** + * Search in the map for the value that matches the parameter + * and return the first key that maps to that value. + * + * @param value The value to search for in the map. + * @return The key for the value in the map or null if it does not exist. + */ + public COSName getKeyForValue( Object value ) + { + for( Map.Entry entry : items.entrySet() ) + { + Object nextValue = entry.getValue(); + if( nextValue.equals( value ) || + (nextValue instanceof COSObject && + ((COSObject)nextValue).getObject().equals( value)) + ) + { + return entry.getKey(); + } + } + + return null; + } + + /** + * This will return the number of elements in this dictionary. + * + * @return The number of elements in the dictionary. + */ + public int size() + { + return items.size(); + } + + /** + * This will clear all items in the map. + */ + public void clear() + { + items.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 firstKey The first key to try. + * @param secondKey The second key to try. + * + * @return The object that matches the key. + * + * @deprecated use {@link #getDictionaryObject(COSName, COSName)} using COSName constants instead + */ + public COSBase getDictionaryObject( String firstKey, String secondKey ) + { + COSBase retval = getDictionaryObject( COSName.getPDFName( firstKey ) ); + if( retval == null ) + { + retval = getDictionaryObject( COSName.getPDFName( secondKey ) ); + } + return retval; + } + + /** + * 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 firstKey The first key to try. + * @param secondKey The second key to try. + * + * @return The object that matches the key. + */ + public COSBase getDictionaryObject( COSName firstKey, COSName secondKey ) + { + COSBase retval = getDictionaryObject( firstKey ); + if( retval == null && secondKey != null) + { + retval = getDictionaryObject( secondKey ); + } + return retval; + } + /** + * 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() + { + return new ArrayList(items.keySet()); + } + + /** + * Returns the names of the entries in this dictionary. The returned + * set is in the order the entries were added to the dictionary. + * + * @since Apache PDFBox 1.1.0 + * @return names of the entries in this dictionary + */ + public Set keySet() + { + return items.keySet(); + } + + /** + * Returns the name-value entries in this dictionary. The returned + * set is in the order the entries were added to the dictionary. + * + * @since Apache PDFBox 1.1.0 + * @return name-value entries in this dictionary + */ + public Set> entrySet() + { + return items.entrySet(); + } + + /** + * 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. + * Only called when adding keys to a trailer that already exists. + * + * @param dic The dic to get the keys from. + */ + public void addAll( COSDictionary dic ) + { + for( Map.Entry entry : dic.entrySet() ) + { + /* + * If we're at a second trailer, we have a linearized + * pdf file, meaning that the first Size entry represents + * all of the objects so we don't need to grab the second. + */ + if(!entry.getKey().getName().equals("Size") + || !items.containsKey(COSName.getPDFName("Size"))) + { + setItem( entry.getKey(), entry.getValue() ); + } + } + } + + /** + * 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 ) + { + for( Map.Entry entry : dic.entrySet() ) + { + if( getItem( entry.getKey() ) == null ) + { + setItem( entry.getKey(), entry.getValue() ); + } + } + } + + /** + * 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; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder retVal = new StringBuilder("COSDictionary{"); + for(COSName key : items.keySet()) { + retVal.append("("); + retVal.append(key); + retVal.append(":"); + if(getDictionaryObject(key) != null) + retVal.append(getDictionaryObject(key).toString()); + else + retVal.append(""); + retVal.append(") "); + } + retVal.append("}"); + return retVal.toString(); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionaryLateBinding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionaryLateBinding.java new file mode 100644 index 000000000..32bde9a91 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDictionaryLateBinding.java @@ -0,0 +1,61 @@ +/* + * Copyright 2011 adam. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * under the License. + */ + +package org.apache.pdfbox.cos; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.pdfparser.ConformingPDFParser; + +/** + * + * @author adam + */ +public class COSDictionaryLateBinding extends COSDictionary { + public static final Log log = LogFactory.getLog(COSDictionaryLateBinding.class); + ConformingPDFParser parser; + + public COSDictionaryLateBinding(ConformingPDFParser parser) { + super(); + this.parser = parser; + } + + /** + * 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. + */ + @Override + public COSBase getDictionaryObject(COSName key) { + COSBase retval = items.get(key); + if(retval instanceof COSObject) { + int objectNumber = ((COSObject)retval).getObjectNumber().intValue(); + int generation = ((COSObject)retval).getGenerationNumber().intValue(); + try { + retval = parser.getObject(objectNumber, generation); + } catch(Exception e) { + log.warn("Unable to read information for object " + objectNumber); + } + } + if(retval instanceof COSNull) { + retval = null; + } + return retval; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDocument.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDocument.java new file mode 100644 index 000000000..af6f0b82c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSDocument.java @@ -0,0 +1,626 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.io.RandomAccess; +import org.apache.pdfbox.io.RandomAccessBuffer; +import org.apache.pdfbox.io.RandomAccessFile; +import org.apache.pdfbox.pdfparser.PDFObjectStreamParser; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; +import org.apache.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 + * @version $Revision: 1.28 $ + */ +public class COSDocument extends COSBase +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(COSDocument.class); + + private float version; + + /** + * Maps ObjectKeys to a COSObject. Note that references to these objects + * are also stored in COSDictionary objects that map a name to a specific object. + */ + private final Map objectPool = + new HashMap(); + + /** + * Maps object and generation ids to object byte offsets. + */ + private final Map xrefTable = + new HashMap(); + + /** + * Document trailer dictionary. + */ + private COSDictionary trailer; + + /** + * Document signature dictionary + */ + private COSDictionary signDictionary = null; + + /** + * Some doc + */ + private SignatureInterface signatureInterface; + + /** + * This file will store the streams in order to conserve memory. + */ + private final RandomAccess scratchFile; + + private final File tmpFile; + + private String headerString = "%PDF-1.4"; + + private boolean warnMissingClose = true; + + private int startXref; + + private boolean closed = false; + + /** + * Flag to skip malformed or otherwise unparseable input where possible. + */ + private final boolean forceParsing; + + /** + * Constructor that will use the given 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 scratchFile the random access file to use for storage + * @param forceParsing flag to skip malformed or otherwise unparseable + * document content where possible + */ + public COSDocument(RandomAccess scratchFile, boolean forceParsing) { + this.scratchFile = scratchFile; + this.tmpFile = null; + this.forceParsing = forceParsing; + } + + /** + * Constructor that will use a temporary file in the given directory + * for storage of the PDF streams. The temporary file is automatically + * removed when this document gets closed. + * + * @param scratchDir directory for the temporary file, + * or null to use the system default + * @param forceParsing flag to skip malformed or otherwise unparseable + * document content where possible + */ + public COSDocument(File scratchDir, boolean forceParsing) + throws IOException { + this.tmpFile = File.createTempFile("pdfbox-", ".tmp", scratchDir); + this.scratchFile = new RandomAccessFile(tmpFile, "rw"); + this.forceParsing = forceParsing; + } + + /** + * Constructor. Uses memory to store stream. + * + * @throws IOException If there is an error creating the tmp file. + */ + public COSDocument() throws IOException { + this(new RandomAccessBuffer(), false); + } + + /** + * 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 { + this(scratchDir, false); + } + + /** + * 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(RandomAccess file) { + this(file, false); + } + + /** + * This will get the scratch file for this document. + * + * @return The scratch file. + */ + public RandomAccess 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. + * @throws IOException If there is an error getting the object + */ + public COSObject getObjectByType( String type ) throws IOException + { + 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. + * @throws IOException If there is an error getting the object + */ + public COSObject getObjectByType( COSName type ) throws IOException + { + for( COSObject object : objectPool.values() ) + { + + COSBase realObject = object.getObject(); + if( realObject instanceof COSDictionary ) + { + try + { + COSDictionary dic = (COSDictionary)realObject; + COSName objectType = (COSName)dic.getItem( COSName.TYPE ); + if( objectType != null && objectType.equals( type ) ) + { + return object; + } + } + catch (ClassCastException e) + { + log.warn(e, e); + } + } + } + return null; + } + + /** + * 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. + * @throws IOException If there is an error getting the object + */ + public List getObjectsByType( String type ) throws IOException + { + 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. + * @throws IOException If there is an error getting the object + */ + public List getObjectsByType( COSName type ) throws IOException + { + List retval = new ArrayList(); + for( COSObject object : objectPool.values() ) + { + COSBase realObject = object.getObject(); + if( realObject instanceof COSDictionary ) + { + try + { + COSDictionary dic = (COSDictionary)realObject; + COSName objectType = (COSName)dic.getItem( COSName.TYPE ); + if( objectType != null && objectType.equals( type ) ) + { + retval.add( object ); + } + } + catch (ClassCastException e) + { + log.warn(e, e); + } + } + } + return retval; + } + + /** + * This will print contents to stdout. + */ + public void print() + { + for( COSObject object : objectPool.values() ) + { + 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( COSName.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.ENCRYPT ); + } + + public SignatureInterface getSignatureInterface() { + return signatureInterface; + } + + /** + * This will set the encryption dictionary, this should only be called when + * encrypting the document. + * + * @param encDictionary The encryption dictionary. + */ + public void setEncryptionDictionary( COSDictionary encDictionary ) + { + trailer.setItem( COSName.ENCRYPT, encDictionary ); + } + + public COSDictionary getLastSignatureDictionary() throws IOException { + if (signDictionary == null) + { + COSObject documentCatalog = getCatalog(); + if (documentCatalog != null) + { + COSDictionary acroForm = (COSDictionary)documentCatalog.getDictionaryObject(COSName.ACRO_FORM); + if (acroForm !=null) + { + COSArray fields = (COSArray)acroForm.getDictionaryObject(COSName.FIELDS); + for ( Object object : fields ) + { + COSObject dict = (COSObject)object; + if(dict.getItem(COSName.FT).equals(COSName.SIG)) + { + COSBase dictionaryObject = dict.getDictionaryObject(COSName.V); + + if (dictionaryObject != null) + { + signDictionary = (COSDictionary)dictionaryObject; + } + } + } + } + } + } + return signDictionary; + } + + /** + * This will get the document ID. + * + * @return The document id. + */ + public COSArray getDocumentID() + { + return (COSArray) getTrailer().getItem(COSName.ID); + } + + /** + * This will set the document ID. + * + * @param id The document id. + */ + public void setDocumentID( COSArray id ) + { + getTrailer().setItem(COSName.ID, id); + } + + public void setSignatureInterface(SignatureInterface signatureInterface) { + this.signatureInterface = signatureInterface; + } + + /** + * 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 = 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(objectPool.values()); + } + + /** + * 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. + */ + @Override + 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 (!closed) { + scratchFile.close(); + if (tmpFile != null) { + tmpFile.delete(); + } + closed = true; + } + } + + /** + * Warn the user in the finalizer if he didn't close the PDF document. The method also + * closes the document just in case, to avoid abandoned temporary files. It's still a good + * idea for the user to close the PDF document at the earliest possible to conserve resources. + * @throws IOException if an error occurs while closing the temporary files + */ + @Override + protected void finalize() throws IOException + { + if (!closed) { + if (warnMissingClose) { + log.warn( "Warning: You did not close a PDF Document" ); + } + close(); + } + } + + /** + * Controls whether this instance shall issue a warning if the PDF document wasn't closed + * properly through a call to the {@link #close()} method. If the PDF document is held in + * a cache governed by soft references it is impossible to reliably close the document + * before the warning is raised. By default, the warning is enabled. + * @param warn true enables the warning, false disables it. + */ + public void setWarnMissingClose(boolean warn) + { + this.warnMissingClose = warn; + } + + /** + * @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 + { + for( COSObject objStream : getObjectsByType( COSName.OBJ_STM ) ) + { + COSStream stream = (COSStream)objStream.getObject(); + PDFObjectStreamParser parser = + new PDFObjectStreamParser(stream, this, forceParsing); + parser.parse(); + for( COSObject next : parser.getObjects() ) + { + COSObjectKey key = new COSObjectKey( next ); + if(objectPool.get(key) == null || objectPool.get(key).getObject() == null) + { + COSObject obj = getObjectFromPool(key); + obj.setObject(next.getObject()); + } + } + } + } + + /** + * 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 = objectPool.get(key); + } + if (obj == null) + { + // this was a forward reference, make "proxy" object + obj = new COSObject(null); + if( key != null ) + { + obj.setObjectNumber( COSInteger.get( key.getNumber() ) ); + obj.setGenerationNumber( COSInteger.get( key.getGeneration() ) ); + objectPool.put(key, obj); + } + } + return obj; + } + + /** + * Removes an object from the object pool. + * @param key the object key + * @return the object that was removed or null if the object was not found + */ + public COSObject removeObject(COSObjectKey key) + { + COSObject obj = objectPool.remove(key); + return obj; + } + + /** + * Populate XRef HashMap with given values. + * Each entry maps ObjectKeys to byte offsets in the file. + * @param _xrefTable xref table entries to be added + */ + public void addXRefTable( Map xrefTable ) + { + this.xrefTable.putAll( xrefTable ); + } + + /** + * Returns the xrefTable which is a mapping of ObjectKeys + * to byte offsets in the file. + * @return mapping of ObjectsKeys to byte offsets + */ + public Map getXrefTable() + { + return xrefTable; + } + + /** + * This method set the startxref value of the document. This will only + * be needed for incremental updates. + * + * @param readInt + */ + public void setStartXref(int startXref) + { + this.startXref = startXref; + } + + /** + * Return the startXref Position of the parsed document. This will only be needed for incremental updates. + * + * @return a int with the old position of the startxref + */ + public int getStartXref() + { + return startXref; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSFloat.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSFloat.java new file mode 100644 index 000000000..57c063766 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSFloat.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.IOException; +import java.io.OutputStream; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +/** + * This class represents a floating point number in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.17 $ + */ +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 + "'" ); + } + } + + /** + * Set the value of the float object. + * + * @param floatValue The new float value. + */ + public void setValue( float floatValue ) + { + value = floatValue; + } + + /** + * 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; + } + + /** + * {@inheritDoc} + */ + public boolean equals( Object o ) + { + return o instanceof COSFloat && Float.floatToIntBits(((COSFloat)o).value) == Float.floatToIntBits(value); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return Float.floatToIntBits(value); + } + + /** + * {@inheritDoc} + */ + 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("ISO-8859-1")); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSInteger.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSInteger.java new file mode 100644 index 000000000..36d990c09 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSInteger.java @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +/** + * This class represents an integer number in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.12 $ + */ +public class COSInteger extends COSNumber +{ + + /** + * The lowest integer to be kept in the {@link #STATIC} array. + */ + private static int LOW = -100; + + /** + * The highest integer to be kept in the {@link #STATIC} array. + */ + private static int HIGH = 256; + + /** + * Static instances of all COSIntegers in the range from {@link #LOW} + * to {@link #HIGH}. + */ + private static final COSInteger[] STATIC = new COSInteger[HIGH - LOW + 1]; + + /** + * Constant for the number zero. + * @since Apache PDFBox 1.1.0 + */ + public static final COSInteger ZERO = get(0); + + /** + * Constant for the number one. + * @since Apache PDFBox 1.1.0 + */ + public static final COSInteger ONE = get(1); + + /** + * Constant for the number two. + * @since Apache PDFBox 1.1.0 + */ + public static final COSInteger TWO = get(2); + + /** + * Constant for the number three. + * @since Apache PDFBox 1.1.0 + */ + public static final COSInteger THREE = get(3); + + /** + * Returns a COSInteger instance with the given value. + * + * @param val integer value + * @return COSInteger instance + */ + public static COSInteger get(long val) { + if (LOW <= val && val <= HIGH) { + int index = (int) val - LOW; + // no synchronization needed + if (STATIC[index] == null) { + STATIC[index] = new COSInteger(val); + } + return STATIC[index]; + } else { + return new COSInteger(val); + } + } + + private long value; + + /** + * constructor. + * + * @deprecated use the static {@link #get(long)} method instead + * @param val The integer value of this object. + */ + public COSInteger( long val ) + { + value = val; + } + + /** + * constructor. + * + * @deprecated use the static {@link #get(long)} method instead + * @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. + * @deprecated use the static {@link #get(long)} method instead + * @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 + "'" ); + } + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object o) + { + return o instanceof COSInteger && ((COSInteger)o).intValue() == intValue(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + //taken from java.lang.Long + return (int)(value ^ (value >> 32)); + } + + /** + * {@inheritDoc} + */ + 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 value; + } + + /** + * polymorphic access to value as float. + * + * @return The double value of this object. + */ + public double doubleValue() + { + return 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("ISO-8859-1")); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSName.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSName.java new file mode 100644 index 000000000..5c11f4726 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSName.java @@ -0,0 +1,1382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.persistence.util.COSHEXTable; + + +/** + * This class represents a PDF named object. + * + * @author Ben Litchfield + * @version $Revision: 1.42 $ + */ +public final class COSName extends COSBase implements Comparable +{ + /** + * Note: This is a ConcurrentHashMap because a HashMap must be synchronized if accessed by + * multiple threads. + */ + private static Map nameMap = new ConcurrentHashMap(8192); + + /** + * All common COSName values are stored in a simple HashMap. They are already defined as + * static constants and don't need to be synchronized for multithreaded environments. + */ + private static Map commonNameMap = + new HashMap(); + + /** + * A common COSName value. + */ + public static final COSName A = new COSName( "A" ); + /** + * 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" ); + /** + * "ActualText" + */ + public static final COSName ACTUAL_TEXT = new COSName("ActualText"); + /** + * A common COSName value. + */ + public static final COSName AIS = new COSName( "AIS" ); + /** + * "Alt" + */ + public static final COSName ALT = new COSName("Alt"); + /** + * "Alt" + */ + public static final COSName ALTERNATE = new COSName("Alternate"); + /** + * A common COSName value. + */ + public static final COSName ANNOT = new COSName( "Annot" ); + /** + * A common COSName value. + */ + public static final COSName ANNOTS = new COSName( "Annots" ); + /** + * A common COSName value. + */ + public static final COSName ANTI_ALIAS = new COSName( "AntiAlias" ); + /** + * A common COSName value. + */ + public static final COSName AP_REF = new COSName( "APRef" ); + /** + * "Artifact" + */ + public static final COSName ARTIFACT = new COSName("Artifact"); + /** + * A common COSName value. + */ + public static final COSName ART_BOX = new COSName("ArtBox" ); + /** + * A common COSName value. + */ + public static final COSName AS = new COSName( "AS" ); + /** + * 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" ); + /** + * "Attached" + */ + public static final COSName ATTACHED = new COSName("Attached"); + /** + * A common COSName value. + */ + public static final COSName ASCENT = new COSName( "Ascent" ); + /** + * 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" ); + + /** "AP" */ + public static final COSName AP = new COSName( "AP" ); + + /** + * A common COSName value. + */ + public static final COSName AUTHOR = new COSName( "Author" ); + + /** + * A common COSName value. + */ + public static final COSName AVG_WIDTH = new COSName( "AvgWidth" ); + + /** + * 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" ); + + /** the COSName for "BaseState". */ + public static final COSName BASE_STATE = new COSName( "BaseState" ); + + /** + * A common COSName value. + */ + public static final COSName BBOX = new COSName( "BBox" ); + /** + * A common COSName value. + */ + public static final COSName BLACK_IS_1 = new COSName( "BlackIs1" ); + /** + * A common COSName value. + */ + public static final COSName BLACK_POINT = new COSName( "BlackPoint" ); + + /** + * A common COSName value. + */ + public static final COSName BLEED_BOX = new COSName("BleedBox" ); + /** + * A common COSName value. + */ + public static final COSName BITS_PER_COMPONENT = new COSName("BitsPerComponent" ); + /** + * A common COSName value. + */ + public static final COSName BITS_PER_SAMPLE = new COSName("BitsPerSample" ); + /** + * A common COSName value. + */ + public static final COSName BOUNDS = new COSName("Bounds" ); + /** + * A common COSName value. + */ + public static final COSName BPC = new COSName("BPC" ); + /** + * A common COSName value. + */ + public static final COSName CATALOG = new COSName( "Catalog" ); + /** + * "C" + */ + public static final COSName C = new COSName("C"); + /** + * A common COSName value. + */ + public static final COSName C0 = new COSName( "C0" ); + /** + * A common COSName value. + */ + public static final COSName C1 = new COSName( "C1" ); + /** + * A common COSName value. + */ + public static final COSName CA = new COSName( "CA" ); + /** + * A common COSName value. + */ + public static final COSName CA_NS = new COSName( "ca" ); + /** + * 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 CAP_HEIGHT = new COSName( "CapHeight" ); + /** + * 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 CF = new COSName( "CF" ); + /** + * A common COSName value. + */ + public static final COSName CFM = new COSName( "CFM" ); + /** + * A common COSName value. + */ + public static final COSName CHAR_PROCS = new COSName( "CharProcs" ); + /** + * A common COSName value. + */ + public static final COSName CHAR_SET = new COSName( "CharSet" ); + /** + * A common COSName value. + */ + public static final COSName CID_FONT_TYPE0 = new COSName( "CIDFontType0" ); + /** + * A common COSName value. + */ + public static final COSName CID_FONT_TYPE2 = new COSName( "CIDFontType2" ); + /** + * A common COSName value. + */ + public static final COSName CIDSYSTEMINFO = new COSName( "CIDSystemInfo" ); + /** + * A common COSName value. + */ + public static final COSName COLORANTS = new COSName( "Colorants" ); + /** + * A common COSName value. + */ + public static final COSName COLORS = new COSName( "Colors" ); + /** + * A common COSName value. + */ + public static final COSName COLORSPACE = new COSName( "ColorSpace" ); + /** + * A common COSName value. + */ + public static final COSName COLUMNS = new COSName( "Columns" ); + /** + * A common COSName value. + */ + public static final COSName CONTENTS = new COSName( "Contents" ); + /** + * A common COSName value. + */ + public static final COSName COORDS = new COSName( "Coords" ); + /** + * A common COSName value. + */ + public static final COSName COUNT = new COSName( "Count" ); + /** + * A common COSName value. + */ + public static final COSName CLR_F = new COSName( "ClrF" ); + /** + * A common COSName value. + */ + public static final COSName CLR_FF = new COSName( "ClrFf" ); + /** + * A common COSName value. + */ + public static final COSName CREATION_DATE = new COSName( "CreationDate" ); + /** + * A common COSName value. + */ + public static final COSName CREATOR = new COSName( "Creator" ); + /** + * A common COSName value. + */ + public static final COSName CROP_BOX = new COSName( "CropBox" ); + /** + * The Crypt filter. + */ + public static final COSName CRYPT = new COSName( "Crypt" ); + /** + * A common COSName value. + */ + public static final COSName CS = new COSName( "CS" ); + + /** "D" */ + public static final COSName D = new COSName( "D" ); + + /** + * A common COSName value. + */ + public static final COSName DA = new COSName( "DA" ); + /** + * 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 DECODE = new COSName( "Decode" ); + /** "DecodeParms" */ + public static final COSName DECODE_PARMS = new COSName( "DecodeParms" ); + + /** + * A common COSName value. + */ + public static final COSName DESCENT = new COSName( "Descent" ); + /** + * A common COSName value. + */ + public static final COSName DESCENDANT_FONTS = new COSName( "DescendantFonts" ); + /** + * A common COSName value. + */ + public static final COSName DEST = new COSName( "Dest" ); + + /** "Dests" */ + public static final COSName DESTS = new COSName( "Dests" ); + + /** + * 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 DIFFERENCES = new COSName( "Differences" ); + + /** "DL" */ + public static final COSName DL = new COSName( "DL" ); + + /** + * A common COSName value. + */ + public static final COSName DOMAIN = new COSName( "Domain" ); + + /** "DP" */ + public static final COSName DP = new COSName( "DP" ); + + /** + * A common COSName value. + */ + public static final COSName DR = new COSName( "DR" ); + /** + * A common COSName value. + */ + public static final COSName DV = new COSName( "DV" ); + /** + * A common COSName value. + */ + public static final COSName DW = new COSName( "DW" ); + + /** + * "E" + */ + public static final COSName E = new COSName("E"); + + /** "EmbeddedFiles" */ + public static final COSName EMBEDDED_FILES = new COSName( "EmbeddedFiles" ); + + /** + * A common COSName value. + */ + public static final COSName ENCODE = new COSName( "Encode" ); + /** + * A common COSName value. + */ + public static final COSName ENCODING = new COSName( "Encoding" ); + /** + * A common COSName value. + */ + public static final COSName ENCODING_90MS_RKSJ_H = new COSName( "90ms-RKSJ-H" ); + /** + * A common COSName value. + */ + public static final COSName ENCODING_90MS_RKSJ_V = new COSName( "90ms-RKSJ-V" ); + /** + * A common COSName value. + */ + public static final COSName ENCODING_ETEN_B5_H = new COSName( "ETen?B5?H" ); + /** + * A common COSName value. + */ + public static final COSName ENCODING_ETEN_B5_V = new COSName( "ETen?B5?V" ); + + /** "Encrypt" */ + public static final COSName ENCRYPT = new COSName( "Encrypt" ); + + /** "EncryptMetaData" */ + public static final COSName ENCRYPT_META_DATA = new COSName( "EncryptMetadata" ); + + /** "ExtGState" */ + public static final COSName EXT_G_STATE = new COSName( "ExtGState" ); + + /** + * A common COSName value. + */ + public static final COSName EXTEND = new COSName( "Extend" ); + + /** "Extends" */ + public static final COSName EXTENDS = new COSName( "Extends" ); + + /** "F" */ + public static final COSName F = new COSName( "F" ); + + /** "FDecodeParms" */ + public static final COSName F_DECODE_PARMS = new COSName( "FDecodeParms" ); + + /** "FFilter" */ + public static final COSName F_FILTER = new COSName( "FFilter" ); + + /** + * A common COSName value. + */ + public static final COSName FF = new COSName( "Ff" ); + /** + * 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 FL = new COSName( "FL" ); + /** + * A common COSName value. + */ + public static final COSName FLAGS = new COSName( "Flags" ); + /** + * 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_BBOX = new COSName( "FontBBox" ); + /** + * A common COSName value. + */ + public static final COSName FONT_FAMILY = new COSName("FontFamily"); + /** + * 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 FONT_NAME = new COSName("FontName" ); + /** + * A common COSName value. + */ + public static final COSName FONT_STRETCH = new COSName("FontStretch" ); + /** + * A common COSName value. + */ + public static final COSName FONT_WEIGHT = new COSName("FontWeight" ); + /** + * A common COSName value. + */ + public static final COSName FORM = new COSName( "Form" ); + /** + * 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 FT = new COSName( "FT" ); + /** + * A common COSName value. + */ + public static final COSName FUNCTION = new COSName( "Function" ); + /** + * A common COSName value. + */ + public static final COSName FUNCTION_TYPE = new COSName( "FunctionType" ); + /** + * A common COSName value. + */ + public static final COSName FUNCTIONS = new COSName( "Functions" ); + /** + * A common COSName value. + */ + public static final COSName GAMMA = new COSName( "Gamma" ); + /** + * A common COSName value. + */ + public static final COSName H = new COSName( "H" ); + /** + * A common COSName value. + */ + public static final COSName HEIGHT = new COSName( "Height" ); + /** + * A common COSName value. + */ + public static final COSName ICCBASED = new COSName( "ICCBased" ); + + /** "ID" */ + public static final COSName ID = new COSName("ID"); + + /** "IDTree" */ + public static final COSName ID_TREE = new COSName("IDTree"); + + /** + * A common COSName value. + */ + public static final COSName IDENTITY = new COSName( "Identity" ); + /** + * 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 IMAGE_MASK = new COSName( "ImageMask" ); + + /** "Index" */ + public static final COSName INDEX = new COSName( "Index" ); + + /** + * 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 ITALIC_ANGLE = new COSName( "ItalicAngle" ); + + /** "JavaScript" */ + public static final COSName JAVA_SCRIPT = new COSName( "JavaScript" ); + + /** + * A common COSName value. + */ + public static final COSName JBIG2_DECODE = new COSName( "JBIG2Decode" ); + /** + * A common COSName value. + */ + public static final COSName JPX_DECODE = new COSName( "JPXDecode" ); + + /** "K" */ + public static final COSName K = new COSName("K"); + + /** + * A common COSName value. + */ + public static final COSName KEYWORDS = new COSName( "Keywords" ); + + /** "Kids" */ + public static final COSName KIDS = new COSName( "Kids" ); + + /** + * A common COSName value. + */ + public static final COSName LAB = new COSName( "Lab" ); + + /** "Lang" */ + public static final COSName LANG = new COSName("Lang"); + + /** + * A common COSName value. + */ + public static final COSName LAST_CHAR = new COSName( "LastChar" ); + /** + * A common COSName value. + */ + public static final COSName LC = new COSName( "LC" ); + /** + * A common COSName value. + */ + public static final COSName LEADING = new COSName( "Leading" ); + /** + * A common COSName value. + */ + public static final COSName LENGTH = new COSName( "Length" ); + /** + * A common COSName value. + */ + public static final COSName LENGTH1 = new COSName( "Length1" ); + + /** "Limits" */ + public static final COSName LIMITS = new COSName( "Limits" ); + /** + * A common COSName value. + */ + public static final COSName LJ = new COSName( "LJ" ); + /** + * A common COSName value. + */ + public static final COSName LW = new COSName( "LW" ); + /** + * 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 M = new COSName( "M" ); + /** + * A common COSName value. + */ + public static final COSName MAC_ROMAN_ENCODING = new COSName( "MacRomanEncoding" ); + + /** "MarkInfo" */ + public static final COSName MARK_INFO = new COSName("MarkInfo"); + + /** + * A common COSName value. + */ + public static final COSName MASK = new COSName( "Mask" ); + /** + * A common COSName value. + */ + public static final COSName MATRIX = new COSName( "Matrix" ); + /** + * A common COSName value. + */ + public static final COSName MAX_WIDTH = new COSName( "MaxWidth" ); + /** + * "MCID" + */ + public static final COSName MCID = new COSName("MCID"); + /** + * 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 MISSING_WIDTH = new COSName( "MissingWidth" ); + /** + * A common COSName value. + */ + public static final COSName ML = new COSName( "ML" ); + /** + * A common COSName value. + */ + public static final COSName MM_TYPE1 = new COSName( "MMType1" ); + /** + * A common COSName value. + */ + public static final COSName MOD_DATE = new COSName( "ModDate" ); + /** + * A common COSName value. + */ + public static final COSName N = new COSName( "N" ); + /** + * A common COSName value. + */ + public static final COSName NAME = new COSName( "Name" ); + + /** "Names" */ + public static final COSName NAMES = new COSName( "Names" ); + + /** + * A common COSName value. + */ + public static final COSName NEXT = new COSName( "Next" ); + /** + * A common COSName value. + */ + public static final COSName NM = new COSName( "NM" ); + /** "Nums" */ + public static final COSName NUMS = new COSName( "Nums" ); + + /** + * "O" + */ + public static final COSName O = new COSName("O"); + + /** + * "Obj" + */ + public static final COSName OBJ = new COSName("Obj"); + + /** + * A common COSName value. + */ + public static final COSName OBJ_STM = new COSName( "ObjStm" ); + + /** the COSName for the content group tag. */ + public static final COSName OC = new COSName("OC"); + /** the COSName for an optional content group. */ + public static final COSName OCG = new COSName("OCG"); + /** the COSName for the optional content group list. */ + public static final COSName OCGS = new COSName("OCGs"); + /** the COSName for the optional content properties. */ + public static final COSName OCPROPERTIES = new COSName("OCProperties"); + + /** the COSName for the "OFF" value. */ + public static final COSName OFF = new COSName("OFF"); + /** the COSName for the "ON" value. */ + public static final COSName ON = new COSName("ON"); + + /** + * A common COSName value. + */ + public static final COSName OP = new COSName( "OP" ); + /** + * A common COSName value. + */ + public static final COSName OP_NS = new COSName( "op" ); + /** + * A common COSName value. + */ + public static final COSName OPM = new COSName( "OPM" ); + /** + * A common COSName value. + */ + public static final COSName OPT = new COSName( "Opt" ); + /** "Outlines" */ + public static final COSName OUTLINES = new COSName("Outlines"); + + /** "OpenAction" */ + public static final COSName OPEN_ACTION = new COSName("OpenAction"); + + /** A common COSName value. */ + public static final COSName ORDER = new COSName( "Order" ); + + /** + * A common COSName value. + */ + public static final COSName ORDERING = new COSName( "Ordering" ); + /** + * A common COSName value. + */ + public static final COSName P = new COSName( "P" ); + /** + * A common COSName value. + */ + public static final COSName PAGE = new COSName( "Page" ); + + /** "PageLabels" */ + public static final COSName PAGE_LABELS = new COSName("PageLabels"); + + /** "PageLayout" */ + public static final COSName PAGE_LAYOUT = new COSName("PageLayout"); + + /** "PageMode" */ + public static final COSName PAGE_MODE = new COSName("PageMode"); + + /** + * A common COSName value. + */ + public static final COSName PAGES = new COSName( "Pages" ); + /** + * A common COSName value. + */ + public static final COSName PARENT = new COSName( "Parent" ); + /** + * "ParentTreeNextKey" + */ + public static final COSName PARENT_TREE_NEXT_KEY = new COSName("ParentTreeNextKey"); + /** + * 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" ); + /** + * "Pg" + */ + public static final COSName PG = new COSName("Pg"); + /** + * A common COSName value. + */ + public static final COSName PREDICTOR = new COSName( "Predictor" ); + /** + * A common COSName value. + */ + public static final COSName PREV = new COSName( "Prev" ); + + /** The COSName value for "ProcSet". */ + public static final COSName PROC_SET = new COSName( "ProcSet" ); + + /** + * A common COSName value. + */ + public static final COSName PRODUCER = new COSName( "Producer" ); + + /** The COSName value for "Properties". */ + public static final COSName PROPERTIES = new COSName( "Properties" ); + + /** + * A common COSName value. + */ + public static final COSName Q = new COSName( "Q" ); + /** + * A common COSName value. + */ + public static final COSName R = new COSName( "R" ); + /** + * A common COSName value. + */ + public static final COSName RANGE = new COSName( "Range" ); + /** + * A common COSName value. + */ + public static final COSName RECIPIENTS = new COSName( "Recipients" ); + /** + * A common COSName value. + */ + public static final COSName RECT = new COSName( "Rect" ); + /** + * A common COSName value. + */ + public static final COSName REGISTRY = new COSName( "Registry" ); + /** + * A common COSName value. + */ + public static final COSName RESOURCES = new COSName( "Resources" ); + /** + * "RoleMap" + */ + public static final COSName RI = new COSName( "RI" ); + /** + * A common COSName value. + */ + public static final COSName ROLE_MAP = new COSName("RoleMap"); + /** + * 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 ROWS = new COSName( "Rows" ); + /** + * 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 RV = new COSName( "RV" ); + /** + * "S" + */ + public static final COSName S = new COSName("S"); + /** + * A common COSName value. + */ + public static final COSName SA = new COSName( "SA" ); + /** + * A common COSName value. + */ + public static final COSName SE = new COSName( "SE" ); + /** + * A common COSName value. + */ + public static final COSName SEPARATION = new COSName( "Separation" ); + /** + * A common COSName value. + */ + public static final COSName SET_F = new COSName( "SetF" ); + /** + * A common COSName value. + */ + public static final COSName SET_FF = new COSName( "SetFf" ); + + /** "Shading" */ + public static final COSName SHADING = new COSName( "Shading" ); + /** + * A common COSName value. + */ + public static final COSName SHADING_TYPE = new COSName( "ShadingType" ); + /** + * A common COSName value. + */ + public static final COSName SM = new COSName( "SM" ); + /** + * A common COSName value. + */ + public static final COSName SMASK = new COSName( "SMask" ); + /** "Size" */ + public static final COSName SIZE = new COSName( "Size" ); + + /** + * A common COSName value. + */ + public static final COSName STANDARD_ENCODING = new COSName( "StandardEncoding" ); + /** + * A common COSName value. + */ + public static final COSName STD_CF = new COSName( "StdCF" ); + /** + * A common COSName value. + */ + public static final COSName STEM_H = new COSName( "StemH" ); + /** + * A common COSName value. + */ + public static final COSName STEM_V = new COSName( "StemV" ); + /** + * A common COSName value. + */ + public static final COSName STM_F = new COSName( "StmF" ); + /** + * A common COSName value. + */ + public static final COSName STR_F = new COSName( "StrF" ); + + /** "StructTreeRoot" */ + public static final COSName STRUCT_TREE_ROOT = new COSName("StructTreeRoot"); + + /** + * A common COSName value. + */ + public static final COSName SUB_FILTER = new COSName( "SubFilter" ); + /** + * A common COSName value. + */ + public static final COSName SUBJ = new COSName( "Subj" ); + /** + * A common COSName value. + */ + public static final COSName SUBJECT = new COSName( "Subject" ); + /** + * A common COSName value. + */ + public static final COSName SUPPLEMENT = new COSName( "Supplement" ); + /** + * A common COSName value. + */ + public static final COSName SUBTYPE = new COSName( "Subtype" ); + + /** + * "T" + */ + public static final COSName T = new COSName("T"); + + /** "Threads" */ + public static final COSName THREADS = new COSName("Threads"); + + /** + * A common COSName value. + */ + public static final COSName TITLE = new COSName( "Title" ); + /** + * A common COSName value. + */ + public static final COSName TK = new COSName( "TK" ); + /** + * A common COSName value. + */ + public static final COSName TRAPPED = new COSName("Trapped" ); + /** + * 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 TU = new COSName("TU" ); + /** + * 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 TYPE1 = new COSName( "Type1" ); + /** + * A common COSName value. + */ + public static final COSName TYPE3 = new COSName( "Type3" ); + + /** + * A common COSName value. + */ + public static final COSName U = new COSName( "U" ); + /** the COSName for the "Unchanged" value. */ + public static final COSName UNCHANGED = new COSName("Unchanged"); + /** "URI" */ + public static final COSName URI = new COSName("URI"); + + /** + * A common COSName value. + */ + public static final COSName V = new COSName( "V" ); + /** + * A common COSName value. + */ + public static final COSName VERSION = new COSName( "Version" ); + + /** "ViewerPreferences" */ + public static final COSName VIEWER_PREFERENCES = new COSName("ViewerPreferences"); + + /** + * A common COSName value. + */ + public static final COSName W = new COSName( "W" ); + /** + * A common COSName value. + */ + public static final COSName WIDTH = new COSName( "Width" ); + /** + * 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 WHITE_POINT = new COSName( "WhitePoint" ); + + /** + * A common COSName value. + */ + public static final COSName XHEIGHT = new COSName( "XHeight" ); + + /** "XObject" */ + public static final COSName XOBJECT = new COSName( "XObject" ); + /** + * A common COSName value. + */ + public static final COSName XREF = new COSName( "XRef" ); + /** + * 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 + + /** + * A common COSName value. + */ + public static final COSName SUBFILTER = new COSName("SubFilter"); + /** + * A signature filter value. + */ + public static final COSName ADOBE_PPKLITE = new COSName("Adobe.PPKLite"); + /** + * A signature filter value. + */ + public static final COSName ENTRUST_PPKEF = new COSName("Entrust.PPKEF"); + /** + * A signature filter value. + */ + public static final COSName CICI_SIGNIT = new COSName("CICI.SignIt"); + /** + * A signature filter value. + */ + public static final COSName VERISIGN_PPKVS = new COSName("VeriSign.PPKVS"); + /** + * A signature subfilter value. + */ + public static final COSName ADBE_X509_RSA_SHA1 = new COSName("adbe.x509.rsa_sha1"); + /** + * A signature subfilter value. + */ + public static final COSName ADBE_PKCS7_DETACHED = new COSName("adbe.pkcs7.detached"); + /** + * A signature subfilter value.www + */ + public static final COSName ADBE_PKCS7_SHA1 = new COSName("adbe.pkcs7.sha1"); + /** + * A common COSName value. + */ + public static final COSName LOCATION = new COSName("Location"); + /** + * A common COSName value. + */ + public static final COSName REASON = new COSName("Reason"); + /** + * A common COSName value. + */ + public static final COSName BYTERANGE = new COSName("ByteRange"); + /** + * A common COSName value. + */ + public static final COSName SIG = new COSName("Sig"); + /** + * A common COSName value. + */ + public static final COSName SIG_FLAGS = new COSName("SigFlags"); + + + 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 ) + { + // Is it a common COSName ?? + name = commonNameMap.get( aName ); + if( name == null ) + { + // It seems to be a document specific COSName + name = nameMap.get( aName ); + if( name == null ) + { + //name is added to the synchronized map in the constructor + name = new COSName( aName, false ); + } + } + } + return name; + } + + /** + * Private constructor. This will limit the number of COSName objects. + * that are created. + * + * @param aName The name of the COSName object. + * @param staticValue Indicates if the COSName object is static so that it can + * be stored in the HashMap without synchronizing. + */ + private COSName( String aName, boolean staticValue ) + { + name = aName; + if ( staticValue ) + { + commonNameMap.put( aName, this); + } + else + { + nameMap.put( aName, this ); + } + hashCode = name.hashCode(); + } + + /** + * 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 ) + { + this( aName, true ); + } + + /** + * This will get the name of this COSName object. + * + * @return The name of the object. + */ + public String getName() + { + return name; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return "COSName{" + name + "}"; + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return hashCode; + } + + /** + * {@inheritDoc} + */ + public int compareTo(COSName other) + { + 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("ISO-8859-1"); + 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); + } + } + } + + /** + * Not usually needed except if resources need to be reclaimed in a ong + * running process. + * Patch provided by flester@GMail.com + * incorporated 5/23/08, Danielwilson@users.SourceForge.net + */ + public static synchronized void clearResources() + { + // Clear them all + nameMap.clear(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNull.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNull.java new file mode 100644 index 000000000..0bcfc92b9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNull.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + + + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +/** + * This class represents a null PDF object. + * + * @author Ben Litchfield + * @version $Revision: 1.13 $ + */ +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); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNumber.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNumber.java new file mode 100644 index 000000000..826ea4a9d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSNumber.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.IOException; + +/** + * This class represents an abstract number in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +public abstract class COSNumber extends COSBase +{ + + /** + * @deprecated Use the {@link COSInteger#ZERO} constant instead + */ + public static final COSInteger ZERO = COSInteger.ZERO; + + /** + * @deprecated Use the {@link COSInteger#ONE} constant instead + */ + public static final COSInteger ONE = COSInteger.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 + { + if (number.length() == 1) { + char digit = number.charAt(0); + if ('0' <= digit && digit <= '9') { + return COSInteger.get(digit - '0'); + } else if (digit == '-' || digit == '.') { + // See https://issues.apache.org/jira/browse/PDFBOX-592 + return COSInteger.ZERO; + } else { + throw new IOException("Not a number: " + number); + } + } else if (number.indexOf('.') == -1) { + try + { + return COSInteger.get( Long.parseLong( number ) ); + } + catch( NumberFormatException e ) + { + throw new IOException( "Value is not an integer: " + number ); + } + } else { + return new COSFloat(number); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSObject.java new file mode 100644 index 000000000..a52cfea3b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSObject.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import java.io.IOException; + +/** + * This class represents a PDF object. + * + * @author Ben Litchfield + * @version $Revision: 1.37 $ + */ +public class COSObject extends COSBase +{ + 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; iBen Litchfield + * @version $Revision: 1.41 $ + */ +public class COSStream extends COSDictionary +{ + private static final int BUFFER_SIZE=16384; + + private RandomAccess 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( RandomAccess 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, RandomAccess 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 RandomAccess 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 + { +// FIXME: We shouldn't keep the same reference? + unFilteredStream = filteredStream; + + COSBase filters = getFilters(); + if( filters == null ) + { + //then do nothing + } + else if( filters instanceof COSName ) + { + doDecode( (COSName)filters, 0 ); + } + else if( filters instanceof COSArray ) + { + COSArray filterArray = (COSArray)filters; + for( int i=0; i=0; i-- ) + { + COSName filterName = (COSName)filterArray.get( i ); + doEncode( filterName, i ); + } + } + } + + /** + * This will encode applying a single filter on the stream. + * + * @param filterName The name of the filter. + * @param filterIndex The index to the filter. + * + * @throws IOException If there is an error parsing the stream. + */ + private void doEncode( COSName filterName, int filterIndex ) 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, filterIndex ); + } + + /** + * 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSString.java b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSString.java new file mode 100644 index 000000000..a3558cc57 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/cos/COSString.java @@ -0,0 +1,459 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.cos; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.persistence.util.COSHEXTable; + +/** + * This represents a string object in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.30 $ + */ +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 = null; + private String str = null; + + /** + * Forces the string to be serialized in hex form but not literal form, the default is to stream + * in literal form. + */ + private boolean forceHexForm = false; + + /** + * Constructor. + */ + public COSString() + { + out = new ByteArrayOutputStream(); + } + + /** + * 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(); + int length = chars.length; + for( int i=0; i 255 ) + { + unicode16 = true; + break; + } + } + if( unicode16 ) + { + byte[] data = value.getBytes( "UTF-16BE" ); + out = new ByteArrayOutputStream( data.length +2); + out.write( 0xFE ); + out.write( 0xFF ); + out.write( data ); + } + else + { + byte[] data = value.getBytes("ISO-8859-1"); + out = new ByteArrayOutputStream( data.length ); + out.write( data ); + } + } + 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 = new ByteArrayOutputStream( value.length ); + out.write( value ); + } + catch (IOException ignore) + { + ignore.printStackTrace(); + //should never happen + } + } + + /** + * Forces the string to be written in literal form instead of hexadecimal form. + * + * @param v if v is true the string will be written in literal form, otherwise it will + * be written in hexa if necessary. + */ + + public void setForceLiteralForm(boolean v) + { + forceHexForm = !v; + } + + /** + * Forces the string to be written in hexadecimal form instead of literal form. + * + * @param v if v is true the string will be written in hexadecimal form otherwise it will be written in literal if + * necessary. + */ + + public void setForceHexForm(boolean v) + { + forceHexForm = v; + } + + /** + * 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 { + return createFromHexString(hex, false); + } + + /** + * Creates a COS string from a string of hex characters, optionally + * ignoring malformed input. + * + * @param hex A hex string. + * @param force flag to ignore malformed input + * @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, boolean force) + throws IOException { + COSString retval = new COSString(); + StringBuilder hexBuffer = new StringBuilder( hex.trim() ); + //if odd number then the last hex digit is assumed to be 0 + if( hexBuffer.length() % 2 != 0 ) + { + hexBuffer.append( '0' ); + } + int length = hexBuffer.length(); + for (int i = 0; i < length; i += 2) { + try { + retval.append( + Integer.parseInt(hexBuffer.substring(i, i + 2), 16)); + } catch (NumberFormatException e) { + if (force) { + retval.append('?'); + } else { + IOException exception = + new IOException("Invalid hex string: " + hex); + exception.initCause(e); + throw exception; + } + } + } + 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() + { + StringBuilder retval = new StringBuilder( out.size() * 2 ); + byte[] data = getBytes(); + int length = data.length; + for( int i=0; i 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 + { + retval = new String( getBytes(), start, data.length-start, encoding ); + } + catch( UnsupportedEncodingException e ) + { + //should never happen + e.printStackTrace(); + retval = new String( getBytes() ); + } + this.str = retval; + 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.str = null; + } + + /** + * 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.str = null; + } + + /** + * This will reset the internal buffer. + */ + public void reset() + { + out.reset(); + this.str = null; + } + + /** + * This will get the bytes of the string. + * + * @return A byte array that represents the string. + */ + public byte[] getBytes() + { + return out.toByteArray(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + return "COSString{" + this.getString() + "}"; + } + + /** + * 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(); + int length = bytes.length; + for( int i=0; i + + + + + + +These are the low level objects that make up a PDF document. +

+ +See the PDF Reference 1.4. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/AFMEncoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/AFMEncoding.java new file mode 100644 index 000000000..e3cec2aec --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/AFMEncoding.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import java.util.Iterator; + +import org.apache.fontbox.afm.CharMetric; +import org.apache.fontbox.afm.FontMetric; + +import org.apache.pdfbox.cos.COSBase; + +/** + * This will handle the encoding from an AFM font. + * + * @author Ben Litchfield + * @version $Revision: 1.8 $ + */ +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(), 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/DictionaryEncoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/DictionaryEncoding.java new file mode 100644 index 000000000..e0061bf60 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/DictionaryEncoding.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; + +/** + * This will perform the encoding from a dictionary. + * + * @author Ben Litchfield + * @version $Revision: 1.13 $ + */ +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." + + // The default base encoding is standardEncoding + Encoding baseEncoding = StandardEncoding.INSTANCE; + COSName baseEncodingName = + (COSName) encoding.getDictionaryObject(COSName.BASE_ENCODING); + if (baseEncodingName != null) { + baseEncoding = + EncodingManager.INSTANCE.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 && iBen Litchfield + * @version $Revision: 1.15 $ + */ +public abstract class Encoding implements COSObjectable +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(Encoding.class); + + /** Identifies a non-mapped character. */ + public static final String NOTDEF = ".notdef"; + + /** + * This is a mapping from a character code to a character name. + */ + protected final Map codeToName = + new HashMap(); + + /** + * This is a mapping from a character name to a character code. + */ + protected final Map nameToCode = + new HashMap(); + + private static final Map NAME_TO_CHARACTER = + new HashMap(); + + private static final Map CHARACTER_TO_NAME = + new HashMap(); + + static + { + //Loads the official Adobe Glyph List + loadGlyphList("org/apache/pdfbox/resources/glyphlist.txt"); + //Loads some additional glyph mappings + loadGlyphList("org/apache/pdfbox/resources/additional_glyphlist.txt"); + + // Load an external glyph list file that user can give as JVM property + String location = System.getProperty("glyphlist_ext"); + if(location != null) + { + File external = new File(location); + if(external.exists()) + { + loadGlyphList(location); + } + } + + NAME_TO_CHARACTER.put( NOTDEF, "" ); + NAME_TO_CHARACTER.put( "fi", "fi" ); + NAME_TO_CHARACTER.put( "fl", "fl" ); + NAME_TO_CHARACTER.put( "ffi", "ffi" ); + NAME_TO_CHARACTER.put( "ff", "ff" ); + NAME_TO_CHARACTER.put( "pi", "pi" ); + + for( Map.Entry entry : NAME_TO_CHARACTER.entrySet() ) + { + CHARACTER_TO_NAME.put( entry.getValue(), entry.getKey() ); + } + } + + /** + * Loads a glyph list from a given location and populates the NAME_TO_CHARACTER hashmap + * for character lookups. + * @param location - The string location of the glyphlist file + */ + private static void loadGlyphList(String location) + { + BufferedReader glyphStream = null; + try + { + InputStream resource = ResourceLoader.loadResource( location ); + if (resource == null) + { + throw new MissingResourceException("Glyphlist not found: " + location, + Encoding.class.getName(), location); + } + 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; + } + if (NAME_TO_CHARACTER.containsKey(characterName)) + { + log.warn("duplicate value for characterName="+characterName+","+value); + } + else + { + NAME_TO_CHARACTER.put( characterName, value ); + } + } + catch( NumberFormatException nfe ) + { + nfe.printStackTrace(); + } + } + } + } + } + catch( IOException io ) + { + io.printStackTrace(); + } + finally + { + if( glyphStream != null ) + { + try + { + glyphStream.close(); + } + catch( IOException e ) + { + e.printStackTrace(); + } + + } + } + } + + /** + * Returns an unmodifiable view of the Code2Name mapping. + * @return the Code2Name map + */ + public Map getCodeToNameMap() + { + return Collections.unmodifiableMap(codeToName); + } + + /** + * Returns an unmodifiable view of the Name2Code mapping. + * @return the Name2Code map + */ + public Map getNameToCodeMap() + { + return Collections.unmodifiableMap(nameToCode); + } + + /** + * This will add a character encoding. + * + * @param code The character code that matches the character. + * @param name The name of the character. + */ + public void addCharacterEncoding( int code, String name ) + { + codeToName.put( code, name ); + nameToCode.put( name, code ); + } + + /** + * 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( String name ) throws IOException + { + Integer code = nameToCode.get( name ); + if( code == null ) + { + throw new IOException( "No character code for character name '" + name + "'" ); + } + return code; + } + + /** + * 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 String getName( int code ) throws IOException + { + return codeToName.get( code ); + } + + /** + * 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 String getNameFromCharacter( char c ) throws IOException + { + String name = CHARACTER_TO_NAME.get( Character.toString(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 name = getName( code ); + if (name != null) + { + return getCharacter( getName( code ) ); + } + return null; + } + + /** + * This will get the character from the name. + * + * @param name The name of the character. + * + * @return The printable character for the code. + */ + public String getCharacter( String name ) + { + String character = NAME_TO_CHARACTER.get( name ); + if( character == null ) + { + // test if we have a suffix and if so remove it + if ( name.indexOf('.') > 0 ) + { + character = getCharacter(name.substring( 0, name.indexOf('.') )); + } + // test for Unicode name + // (uniXXXX - XXXX must be a multiple of four; + // each representing a hexadecimal Unicode code point) + else if ( name.startsWith( "uni" ) ) + { + int nameLength = name.length(); + StringBuilder uniStr = new StringBuilder(); + try + { + for ( int chPos = 3; chPos + 4 <= nameLength; chPos += 4 ) + { + int characterCode = Integer.parseInt( name.substring( chPos, chPos + 4), 16 ); + + if ( ( characterCode > 0xD7FF ) && ( characterCode < 0xE000 ) ) + { + log.warn( "Unicode character name with not allowed code area: " + name ); + } + else + { + uniStr.append( (char) characterCode ); + } + } + character = uniStr.toString(); + NAME_TO_CHARACTER.put(name, character); + } + catch (NumberFormatException nfe) + { + log.warn( "Not a number in Unicode character name: " + name ); + character = name; + } + } + else if (nameToCode.containsKey(name)) + { + int code = nameToCode.get(name); + character = Character.toString((char)code); + } + else + { + character = name; + } + } + return character; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/EncodingManager.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/EncodingManager.java new file mode 100644 index 000000000..4701e5566 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/EncodingManager.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSName; + +/** + * This class will handle getting the appropriate encodings. + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +public class EncodingManager +{ + + /** + * Default singleton instance of this class. + * + * @since Apache PDFBox 1.3.0 + */ + public static final EncodingManager INSTANCE = new EncodingManager(); + + /** + * This will get the standard encoding. + * + * @return The standard encoding. + */ + public Encoding getStandardEncoding() { + return StandardEncoding.INSTANCE; + } + + /** + * 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 no encoding with that name. + */ + public Encoding getEncoding( COSName name ) throws IOException { + if (COSName.STANDARD_ENCODING.equals(name)) { + return StandardEncoding.INSTANCE; + } else if (COSName.WIN_ANSI_ENCODING.equals(name)) { + return WinAnsiEncoding.INSTANCE; + } else if (COSName.MAC_ROMAN_ENCODING.equals(name)) { + return MacRomanEncoding.INSTANCE; + } else if (COSName.PDF_DOC_ENCODING.equals(name)) { + return PdfDocEncoding.INSTANCE; + } else { + throw new IOException( + "Unknown encoding for '" + name.getName() + "'"); + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/MacRomanEncoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/MacRomanEncoding.java new file mode 100644 index 000000000..2238284c6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/MacRomanEncoding.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; + +/** + * This is an interface to a text encoder. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +public class MacRomanEncoding extends Encoding +{ + + /** + * Singleton instance of this class. + * + * @since Apache PDFBox 1.3.0 + */ + public static final MacRomanEncoding INSTANCE = new MacRomanEncoding(); + + /** + * Constructor. + */ + public MacRomanEncoding() + { + addCharacterEncoding( 0101, "A" ); + addCharacterEncoding( 0256, "AE" ); + addCharacterEncoding( 0347, "Aacute" ); + addCharacterEncoding( 0345, "Acircumflex" ); + addCharacterEncoding( 0200, "Adieresis" ); + addCharacterEncoding( 0313, "Agrave" ); + addCharacterEncoding( 0201, "Aring" ); + addCharacterEncoding( 0314, "Atilde" ); + addCharacterEncoding( 0102, "B" ); + addCharacterEncoding( 0103, "C" ); + addCharacterEncoding( 0202, "Ccedilla" ); + addCharacterEncoding( 0104, "D" ); + addCharacterEncoding( 0105, "E" ); + addCharacterEncoding( 0203, "Eacute" ); + addCharacterEncoding( 0346, "Ecircumflex" ); + addCharacterEncoding( 0350, "Edieresis" ); + addCharacterEncoding( 0351, "Egrave" ); + addCharacterEncoding( 0106, "F" ); + addCharacterEncoding( 0107, "G" ); + addCharacterEncoding( 0110, "H" ); + addCharacterEncoding( 0111, "I" ); + addCharacterEncoding( 0352, "Iacute" ); + addCharacterEncoding( 0353, "Icircumflex" ); + addCharacterEncoding( 0354, "Idieresis" ); + addCharacterEncoding( 0355, "Igrave" ); + addCharacterEncoding( 0112, "J" ); + addCharacterEncoding( 0113, "K" ); + addCharacterEncoding( 0114, "L" ); + addCharacterEncoding( 0115, "M" ); + addCharacterEncoding( 0116, "N" ); + addCharacterEncoding( 0204, "Ntilde" ); + addCharacterEncoding( 0117, "O" ); + addCharacterEncoding( 0316, "OE" ); + addCharacterEncoding( 0356, "Oacute" ); + addCharacterEncoding( 0357, "Ocircumflex" ); + addCharacterEncoding( 0205, "Odieresis" ); + addCharacterEncoding( 0361, "Ograve" ); + addCharacterEncoding( 0257, "Oslash" ); + addCharacterEncoding( 0315, "Otilde" ); + addCharacterEncoding( 0120, "P" ); + addCharacterEncoding( 0121, "Q" ); + addCharacterEncoding( 0122, "R" ); + addCharacterEncoding( 0123, "S" ); + addCharacterEncoding( 0124, "T" ); + addCharacterEncoding( 0125, "U" ); + addCharacterEncoding( 0362, "Uacute" ); + addCharacterEncoding( 0363, "Ucircumflex" ); + addCharacterEncoding( 0206, "Udieresis" ); + addCharacterEncoding( 0364, "Ugrave" ); + addCharacterEncoding( 0126, "V" ); + addCharacterEncoding( 0127, "W" ); + addCharacterEncoding( 0130, "X" ); + addCharacterEncoding( 0131, "Y" ); + addCharacterEncoding( 0331, "Ydieresis" ); + addCharacterEncoding( 0132, "Z" ); + addCharacterEncoding( 0141, "a" ); + addCharacterEncoding( 0207, "aacute" ); + addCharacterEncoding( 0211, "acircumflex" ); + addCharacterEncoding( 0253, "acute" ); + addCharacterEncoding( 0212, "adieresis" ); + addCharacterEncoding( 0276, "ae" ); + addCharacterEncoding( 0210, "agrave" ); + addCharacterEncoding( 046, "ampersand" ); + addCharacterEncoding( 0214, "aring" ); + addCharacterEncoding( 0136, "asciicircum" ); + addCharacterEncoding( 0176, "asciitilde" ); + addCharacterEncoding( 052, "asterisk" ); + addCharacterEncoding( 0100, "at" ); + addCharacterEncoding( 0213, "atilde" ); + addCharacterEncoding( 0142, "b" ); + addCharacterEncoding( 0134, "backslash" ); + addCharacterEncoding( 0174, "bar" ); + addCharacterEncoding( 0173, "braceleft" ); + addCharacterEncoding( 0175, "braceright" ); + addCharacterEncoding( 0133, "bracketleft" ); + addCharacterEncoding( 0135, "bracketright" ); + addCharacterEncoding( 0371, "breve" ); + addCharacterEncoding( 0245, "bullet" ); + addCharacterEncoding( 0143, "c" ); + addCharacterEncoding( 0377, "caron" ); + addCharacterEncoding( 0215, "ccedilla" ); + addCharacterEncoding( 0374, "cedilla" ); + addCharacterEncoding( 0242, "cent" ); + addCharacterEncoding( 0366, "circumflex" ); + addCharacterEncoding( 072, "colon" ); + addCharacterEncoding( 054, "comma" ); + addCharacterEncoding( 0251, "copyright" ); + addCharacterEncoding( 0333, "currency" ); + addCharacterEncoding( 0144, "d" ); + addCharacterEncoding( 0240, "dagger" ); + addCharacterEncoding( 0340, "daggerdbl" ); + addCharacterEncoding( 0241, "degree" ); + addCharacterEncoding( 0254, "dieresis" ); + addCharacterEncoding( 0326, "divide" ); + addCharacterEncoding( 044, "dollar" ); + addCharacterEncoding( 0372, "dotaccent" ); + addCharacterEncoding( 0365, "dotlessi" ); + addCharacterEncoding( 0145, "e" ); + addCharacterEncoding( 0216, "eacute" ); + addCharacterEncoding( 0220, "ecircumflex" ); + addCharacterEncoding( 0221, "edieresis" ); + addCharacterEncoding( 0217, "egrave" ); + addCharacterEncoding( 070, "eight" ); + addCharacterEncoding( 0311, "ellipsis" ); + addCharacterEncoding( 0321, "emdash" ); + addCharacterEncoding( 0320, "endash" ); + addCharacterEncoding( 075, "equal" ); + addCharacterEncoding( 041, "exclam" ); + addCharacterEncoding( 0301, "exclamdown" ); + addCharacterEncoding( 0146, "f" ); + addCharacterEncoding( 0336, "fi" ); + addCharacterEncoding( 065, "five" ); + addCharacterEncoding( 0337, "fl" ); + addCharacterEncoding( 0304, "florin" ); + addCharacterEncoding( 064, "four" ); + addCharacterEncoding( 0332, "fraction" ); + addCharacterEncoding( 0147, "g" ); + addCharacterEncoding( 0247, "germandbls" ); + addCharacterEncoding( 0140, "grave" ); + addCharacterEncoding( 076, "greater" ); + addCharacterEncoding( 0307, "guillemotleft" ); + addCharacterEncoding( 0310, "guillemotright" ); + addCharacterEncoding( 0334, "guilsinglleft" ); + addCharacterEncoding( 0335, "guilsinglright" ); + addCharacterEncoding( 0150, "h" ); + addCharacterEncoding( 0375, "hungarumlaut" ); + addCharacterEncoding( 055, "hyphen" ); + addCharacterEncoding( 0151, "i" ); + addCharacterEncoding( 0222, "iacute" ); + addCharacterEncoding( 0224, "icircumflex" ); + addCharacterEncoding( 0225, "idieresis" ); + addCharacterEncoding( 0223, "igrave" ); + addCharacterEncoding( 0152, "j" ); + addCharacterEncoding( 0153, "k" ); + addCharacterEncoding( 0154, "l" ); + addCharacterEncoding( 074, "less" ); + addCharacterEncoding( 0302, "logicalnot" ); + addCharacterEncoding( 0155, "m" ); + addCharacterEncoding( 0370, "macron" ); + addCharacterEncoding( 0265, "mu" ); + addCharacterEncoding( 0156, "n" ); + addCharacterEncoding( 071, "nine" ); + addCharacterEncoding( 0226, "ntilde" ); + addCharacterEncoding( 043, "numbersign" ); + addCharacterEncoding( 0157, "o" ); + addCharacterEncoding( 0227, "oacute" ); + addCharacterEncoding( 0231, "ocircumflex" ); + addCharacterEncoding( 0232, "odieresis" ); + addCharacterEncoding( 0317, "oe" ); + addCharacterEncoding( 0376, "ogonek" ); + addCharacterEncoding( 0230, "ograve" ); + addCharacterEncoding( 061, "one" ); + addCharacterEncoding( 0273, "ordfeminine" ); + addCharacterEncoding( 0274, "ordmasculine" ); + addCharacterEncoding( 0277, "oslash" ); + addCharacterEncoding( 0233, "otilde" ); + addCharacterEncoding( 0160, "p" ); + addCharacterEncoding( 0246, "paragraph" ); + addCharacterEncoding( 050, "parenleft" ); + addCharacterEncoding( 051, "parenright" ); + addCharacterEncoding( 045, "percent" ); + addCharacterEncoding( 056, "period" ); + addCharacterEncoding( 0341, "periodcentered" ); + addCharacterEncoding( 0344, "perthousand" ); + addCharacterEncoding( 053, "plus" ); + addCharacterEncoding( 0261, "plusminus" ); + addCharacterEncoding( 0161, "q" ); + addCharacterEncoding( 077, "question" ); + addCharacterEncoding( 0300, "questiondown" ); + addCharacterEncoding( 042, "quotedbl" ); + addCharacterEncoding( 0343, "quotedblbase" ); + addCharacterEncoding( 0322, "quotedblleft" ); + addCharacterEncoding( 0323, "quotedblright" ); + addCharacterEncoding( 0324, "quoteleft" ); + addCharacterEncoding( 0325, "quoteright" ); + addCharacterEncoding( 0342, "quotesinglbase" ); + addCharacterEncoding( 047, "quotesingle" ); + addCharacterEncoding( 0162, "r" ); + addCharacterEncoding( 0250, "registered" ); + addCharacterEncoding( 0373, "ring" ); + addCharacterEncoding( 0163, "s" ); + addCharacterEncoding( 0244, "section" ); + addCharacterEncoding( 073, "semicolon" ); + addCharacterEncoding( 067, "seven" ); + addCharacterEncoding( 066, "six" ); + addCharacterEncoding( 057, "slash" ); + addCharacterEncoding( 040, "space" ); + addCharacterEncoding( 0243, "sterling" ); + addCharacterEncoding( 0164, "t" ); + addCharacterEncoding( 063, "three" ); + addCharacterEncoding( 0367, "tilde" ); + addCharacterEncoding( 0252, "trademark" ); + addCharacterEncoding( 062, "two" ); + addCharacterEncoding( 0165, "u" ); + addCharacterEncoding( 0234, "uacute" ); + addCharacterEncoding( 0236, "ucircumflex" ); + addCharacterEncoding( 0237, "udieresis" ); + addCharacterEncoding( 0235, "ugrave" ); + addCharacterEncoding( 0137, "underscore" ); + addCharacterEncoding( 0166, "v" ); + addCharacterEncoding( 0167, "w" ); + addCharacterEncoding( 0170, "x" ); + addCharacterEncoding( 0171, "y" ); + addCharacterEncoding( 0330, "ydieresis" ); + addCharacterEncoding( 0264, "yen" ); + addCharacterEncoding( 0172, "z" ); + addCharacterEncoding( 060, "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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/PdfDocEncoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/PdfDocEncoding.java new file mode 100644 index 000000000..c0477a9c0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/PdfDocEncoding.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; + +/** + * This is an interface to a text encoder. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +public class PdfDocEncoding extends Encoding +{ + + /** + * Singleton instance of this class. + * + * @since Apache PDFBox 1.3.0 + */ + public static final PdfDocEncoding INSTANCE = new PdfDocEncoding(); + + /** + * Constructor. + */ + public PdfDocEncoding() + { + addCharacterEncoding( 0101, "A" ); + addCharacterEncoding( 0306, "AE" ); + addCharacterEncoding( 0301, "Aacute" ); + addCharacterEncoding( 0302, "Acircumflex" ); + addCharacterEncoding( 0304, "Adieresis" ); + addCharacterEncoding( 0300, "Agrave" ); + addCharacterEncoding( 0305, "Aring" ); + addCharacterEncoding( 0303, "Atilde" ); + addCharacterEncoding( 0102, "B" ); + addCharacterEncoding( 0103, "C" ); + addCharacterEncoding( 0307, "Ccedilla" ); + addCharacterEncoding( 0104, "D" ); + addCharacterEncoding( 0105, "E" ); + addCharacterEncoding( 0311, "Eacute" ); + addCharacterEncoding( 0312, "Ecircumflex" ); + addCharacterEncoding( 0313, "Edieresis" ); + addCharacterEncoding( 0310, "Egrave" ); + addCharacterEncoding( 0320, "Eth" ); + addCharacterEncoding( 0240, "Euro" ); + addCharacterEncoding( 0106, "F" ); + addCharacterEncoding( 0107, "G" ); + addCharacterEncoding( 0110, "H" ); + addCharacterEncoding( 0111, "I" ); + addCharacterEncoding( 0315, "Iacute" ); + addCharacterEncoding( 0316, "Icircumflex" ); + addCharacterEncoding( 0317, "Idieresis" ); + addCharacterEncoding( 0314, "Igrave" ); + addCharacterEncoding( 0112, "J" ); + addCharacterEncoding( 0113, "K" ); + addCharacterEncoding( 0114, "L" ); + addCharacterEncoding( 0225, "Lslash" ); + addCharacterEncoding( 0115, "M" ); + addCharacterEncoding( 0116, "N" ); + addCharacterEncoding( 0321, "Ntilde" ); + addCharacterEncoding( 0117, "O" ); + addCharacterEncoding( 0226, "OE" ); + addCharacterEncoding( 0323, "Oacute" ); + addCharacterEncoding( 0324, "Ocircumflex" ); + addCharacterEncoding( 0326, "Odieresis" ); + addCharacterEncoding( 0322, "Ograve" ); + addCharacterEncoding( 0330, "Oslash" ); + addCharacterEncoding( 0325, "Otilde" ); + addCharacterEncoding( 0120, "P" ); + addCharacterEncoding( 0121, "Q" ); + addCharacterEncoding( 0122, "R" ); + addCharacterEncoding( 0123, "S" ); + addCharacterEncoding( 0227, "Scaron" ); + addCharacterEncoding( 0124, "T" ); + addCharacterEncoding( 0336, "Thorn" ); + addCharacterEncoding( 0125, "U" ); + addCharacterEncoding( 0332, "Uacute" ); + addCharacterEncoding( 0333, "Ucircumflex" ); + addCharacterEncoding( 0334, "Udieresis" ); + addCharacterEncoding( 0331, "Ugrave" ); + addCharacterEncoding( 0126, "V" ); + addCharacterEncoding( 0127, "W" ); + addCharacterEncoding( 0130, "X" ); + addCharacterEncoding( 0131, "Y" ); + addCharacterEncoding( 0335, "Yacute" ); + addCharacterEncoding( 0230, "Ydieresis" ); + addCharacterEncoding( 0132, "Z" ); + addCharacterEncoding( 0231, "Zcaron" ); + addCharacterEncoding( 0141, "a" ); + addCharacterEncoding( 0341, "aacute" ); + addCharacterEncoding( 0342, "acircumflex" ); + addCharacterEncoding( 0264, "acute" ); + addCharacterEncoding( 0344, "adieresis" ); + addCharacterEncoding( 0346, "ae" ); + addCharacterEncoding( 0340, "agrave" ); + addCharacterEncoding( 046, "ampersand" ); + addCharacterEncoding( 0345, "aring" ); + addCharacterEncoding( 0136, "asciicircum" ); + addCharacterEncoding( 0176, "asciitilde" ); + addCharacterEncoding( 052, "asterisk" ); + addCharacterEncoding( 0100, "at" ); + addCharacterEncoding( 0343, "atilde" ); + addCharacterEncoding( 0142, "b" ); + addCharacterEncoding( 0134, "backslash" ); + addCharacterEncoding( 0174, "bar" ); + addCharacterEncoding( 0173, "braceleft" ); + addCharacterEncoding( 0175, "braceright" ); + addCharacterEncoding( 0133, "bracketleft" ); + addCharacterEncoding( 0135, "bracketright" ); + addCharacterEncoding( 030, "breve" ); + addCharacterEncoding( 0246, "brokenbar" ); + addCharacterEncoding( 0200, "bullet" ); + addCharacterEncoding( 0143, "c" ); + addCharacterEncoding( 031, "caron" ); + addCharacterEncoding( 0347, "ccedilla" ); + addCharacterEncoding( 0270, "cedilla" ); + addCharacterEncoding( 0242, "cent" ); + addCharacterEncoding( 032, "circumflex" ); + addCharacterEncoding( 072, "colon" ); + addCharacterEncoding( 054, "comma" ); + addCharacterEncoding( 0251, "copyright" ); + addCharacterEncoding( 0244, "currency" ); + addCharacterEncoding( 0144, "d" ); + addCharacterEncoding( 0201, "dagger" ); + addCharacterEncoding( 0202, "daggerdbl" ); + addCharacterEncoding( 0260, "degree" ); + addCharacterEncoding( 0250, "dieresis" ); + addCharacterEncoding( 0367, "divide" ); + addCharacterEncoding( 044, "dollar" ); + addCharacterEncoding( 033, "dotaccent" ); + addCharacterEncoding( 0232, "dotlessi" ); + addCharacterEncoding( 0145, "e" ); + addCharacterEncoding( 0351, "eacute" ); + addCharacterEncoding( 0352, "ecircumflex" ); + addCharacterEncoding( 0353, "edieresis" ); + addCharacterEncoding( 0350, "egrave" ); + addCharacterEncoding( 070, "eight" ); + addCharacterEncoding( 0203, "ellipsis" ); + addCharacterEncoding( 0204, "emdash" ); + addCharacterEncoding( 0205, "endash" ); + addCharacterEncoding( 075, "equal" ); + addCharacterEncoding( 0360, "eth" ); + addCharacterEncoding( 041, "exclam" ); + addCharacterEncoding( 0241, "exclamdown" ); + addCharacterEncoding( 0146, "f" ); + addCharacterEncoding( 0223, "fi" ); + addCharacterEncoding( 065, "five" ); + addCharacterEncoding( 0224, "fl" ); + addCharacterEncoding( 0206, "florin" ); + addCharacterEncoding( 064, "four" ); + addCharacterEncoding( 0207, "fraction" ); + addCharacterEncoding( 0147, "g" ); + addCharacterEncoding( 0337, "germandbls" ); + addCharacterEncoding( 0140, "grave" ); + addCharacterEncoding( 076, "greater" ); + addCharacterEncoding( 0253, "guillemotleft" ); + addCharacterEncoding( 0273, "guillemotright" ); + addCharacterEncoding( 0210, "guilsinglleft" ); + addCharacterEncoding( 0211, "guilsinglright" ); + addCharacterEncoding( 0150, "h" ); + addCharacterEncoding( 034, "hungarumlaut" ); + addCharacterEncoding( 055, "hyphen" ); + addCharacterEncoding( 0151, "i" ); + addCharacterEncoding( 0355, "iacute" ); + addCharacterEncoding( 0356, "icircumflex" ); + addCharacterEncoding( 0357, "idieresis" ); + addCharacterEncoding( 0354, "igrave" ); + addCharacterEncoding( 0152, "j" ); + addCharacterEncoding( 0153, "k" ); + addCharacterEncoding( 0154, "l" ); + addCharacterEncoding( 074, "less" ); + addCharacterEncoding( 0254, "logicalnot" ); + addCharacterEncoding( 0233, "lslash" ); + addCharacterEncoding( 0155, "m" ); + addCharacterEncoding( 0257, "macron" ); + addCharacterEncoding( 0212, "minus" ); + addCharacterEncoding( 0265, "mu" ); + addCharacterEncoding( 0327, "multiply" ); + addCharacterEncoding( 0156, "n" ); + addCharacterEncoding( 071, "nine" ); + addCharacterEncoding( 0361, "ntilde" ); + addCharacterEncoding( 043, "numbersign" ); + addCharacterEncoding( 0157, "o" ); + addCharacterEncoding( 0363, "oacute" ); + addCharacterEncoding( 0364, "ocircumflex" ); + addCharacterEncoding( 0366, "odieresis" ); + addCharacterEncoding( 0234, "oe" ); + addCharacterEncoding( 035, "ogonek" ); + addCharacterEncoding( 0362, "ograve" ); + addCharacterEncoding( 061, "one" ); + addCharacterEncoding( 0275, "onehalf" ); + addCharacterEncoding( 0274, "onequarter" ); + addCharacterEncoding( 0271, "onesuperior" ); + addCharacterEncoding( 0252, "ordfeminine" ); + addCharacterEncoding( 0272, "ordmasculine" ); + addCharacterEncoding( 0370, "oslash" ); + addCharacterEncoding( 0365, "otilde" ); + addCharacterEncoding( 0160, "p" ); + addCharacterEncoding( 0266, "paragraph" ); + addCharacterEncoding( 050, "parenleft" ); + addCharacterEncoding( 051, "parenright" ); + addCharacterEncoding( 045, "percent" ); + addCharacterEncoding( 056, "period" ); + addCharacterEncoding( 0267, "periodcentered" ); + addCharacterEncoding( 0213, "perthousand" ); + addCharacterEncoding( 053, "plus" ); + addCharacterEncoding( 0261, "plusminus" ); + addCharacterEncoding( 0161, "q" ); + addCharacterEncoding( 077, "question" ); + addCharacterEncoding( 0277, "questiondown" ); + addCharacterEncoding( 042, "quotedbl" ); + addCharacterEncoding( 0214, "quotedblbase" ); + addCharacterEncoding( 0215, "quotedblleft" ); + addCharacterEncoding( 0216, "quotedblright" ); + addCharacterEncoding( 0217, "quoteleft" ); + addCharacterEncoding( 0220, "quoteright" ); + addCharacterEncoding( 0221, "quotesinglbase" ); + addCharacterEncoding( 047, "quotesingle" ); + addCharacterEncoding( 0162, "r" ); + addCharacterEncoding( 0256, "registered" ); + addCharacterEncoding( 036, "ring" ); + addCharacterEncoding( 0163, "s" ); + addCharacterEncoding( 0235, "scaron" ); + addCharacterEncoding( 0247, "section" ); + addCharacterEncoding( 073, "semicolon" ); + addCharacterEncoding( 067, "seven" ); + addCharacterEncoding( 066, "six" ); + addCharacterEncoding( 057, "slash" ); + addCharacterEncoding( 040, "space" ); + addCharacterEncoding( 0243, "sterling" ); + addCharacterEncoding( 0164, "t" ); + addCharacterEncoding( 0376, "thorn" ); + addCharacterEncoding( 063, "three" ); + addCharacterEncoding( 0276, "threequarters" ); + addCharacterEncoding( 0263, "threesuperior" ); + addCharacterEncoding( 037, "tilde" ); + addCharacterEncoding( 0222, "trademark" ); + addCharacterEncoding( 062, "two" ); + addCharacterEncoding( 0262, "twosuperior" ); + addCharacterEncoding( 0165, "u" ); + addCharacterEncoding( 0372, "uacute" ); + addCharacterEncoding( 0373, "ucircumflex" ); + addCharacterEncoding( 0374, "udieresis" ); + addCharacterEncoding( 0371, "ugrave" ); + addCharacterEncoding( 0137, "underscore" ); + addCharacterEncoding( 0166, "v" ); + addCharacterEncoding( 0167, "w" ); + addCharacterEncoding( 0170, "x" ); + addCharacterEncoding( 0171, "y" ); + addCharacterEncoding( 0375, "yacute" ); + addCharacterEncoding( 0377, "ydieresis" ); + addCharacterEncoding( 0245, "yen" ); + addCharacterEncoding( 0172, "z" ); + addCharacterEncoding( 0236, "zcaron" ); + addCharacterEncoding( 060, "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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/StandardEncoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/StandardEncoding.java new file mode 100644 index 000000000..222a3a265 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/StandardEncoding.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; + +/** + * This is an interface to a text encoder. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +public class StandardEncoding extends Encoding +{ + + /** + * Singleton instance of this class. + * + * @since Apache PDFBox 1.3.0 + */ + public static final StandardEncoding INSTANCE = new StandardEncoding(); + + /** + * Constructor. + */ + public StandardEncoding() + { + addCharacterEncoding( 0101, "A" ); + addCharacterEncoding( 0341, "AE" ); + addCharacterEncoding( 0102, "B" ); + addCharacterEncoding( 0103, "C" ); + addCharacterEncoding( 0104, "D" ); + addCharacterEncoding( 0105, "E" ); + addCharacterEncoding( 0106, "F" ); + addCharacterEncoding( 0107, "G" ); + addCharacterEncoding( 0110, "H" ); + addCharacterEncoding( 0111, "I" ); + addCharacterEncoding( 0112, "J" ); + addCharacterEncoding( 0113, "K" ); + addCharacterEncoding( 0114, "L" ); + addCharacterEncoding( 0350, "Lslash" ); + addCharacterEncoding( 0115, "M" ); + addCharacterEncoding( 0116, "N" ); + addCharacterEncoding( 0117, "O" ); + addCharacterEncoding( 0352, "OE" ); + addCharacterEncoding( 0351, "Oslash" ); + addCharacterEncoding( 0120, "P" ); + addCharacterEncoding( 0121, "Q" ); + addCharacterEncoding( 0122, "R" ); + addCharacterEncoding( 0123, "S" ); + addCharacterEncoding( 0124, "T" ); + addCharacterEncoding( 0125, "U" ); + addCharacterEncoding( 0126, "V" ); + addCharacterEncoding( 0127, "W" ); + addCharacterEncoding( 0130, "X" ); + addCharacterEncoding( 0131, "Y" ); + addCharacterEncoding( 0132, "Z" ); + addCharacterEncoding( 0141, "a" ); + addCharacterEncoding( 0302, "acute" ); + addCharacterEncoding( 0361, "ae" ); + addCharacterEncoding( 0046, "ampersand" ); + addCharacterEncoding( 0136, "asciicircum" ); + addCharacterEncoding( 0176, "asciitilde" ); + addCharacterEncoding( 0052, "asterisk" ); + addCharacterEncoding( 0100, "at" ); + addCharacterEncoding( 0142, "b" ); + addCharacterEncoding( 0134, "backslash" ); + addCharacterEncoding( 0174, "bar" ); + addCharacterEncoding( 0173, "braceleft" ); + addCharacterEncoding( 0175, "braceright" ); + addCharacterEncoding( 0133, "bracketleft" ); + addCharacterEncoding( 0135, "bracketright" ); + addCharacterEncoding( 0306, "breve" ); + addCharacterEncoding( 0267, "bullet" ); + addCharacterEncoding( 0143, "c" ); + addCharacterEncoding( 0317, "caron" ); + addCharacterEncoding( 0313, "cedilla" ); + addCharacterEncoding( 0242, "cent" ); + addCharacterEncoding( 0303, "circumflex" ); + addCharacterEncoding( 0072, "colon" ); + addCharacterEncoding( 0054, "comma" ); + addCharacterEncoding( 0250, "currency" ); + addCharacterEncoding( 0144, "d" ); + addCharacterEncoding( 0262, "dagger" ); + addCharacterEncoding( 0263, "daggerdbl" ); + addCharacterEncoding( 0310, "dieresis" ); + addCharacterEncoding( 0044, "dollar" ); + addCharacterEncoding( 0307, "dotaccent" ); + addCharacterEncoding( 0365, "dotlessi" ); + addCharacterEncoding( 0145, "e" ); + addCharacterEncoding( 0070, "eight" ); + addCharacterEncoding( 0274, "ellipsis" ); + addCharacterEncoding( 0320, "emdash" ); + addCharacterEncoding( 0261, "endash" ); + addCharacterEncoding( 0075, "equal" ); + addCharacterEncoding( 0041, "exclam" ); + addCharacterEncoding( 0241, "exclamdown" ); + addCharacterEncoding( 0146, "f" ); + addCharacterEncoding( 0256, "fi" ); + addCharacterEncoding( 0065, "five" ); + addCharacterEncoding( 0257, "fl" ); + addCharacterEncoding( 0246, "florin" ); + addCharacterEncoding( 0064, "four" ); + addCharacterEncoding( 0244, "fraction" ); + addCharacterEncoding( 0147, "g" ); + addCharacterEncoding( 0373, "germandbls" ); + addCharacterEncoding( 0301, "grave" ); + addCharacterEncoding( 0076, "greater" ); + addCharacterEncoding( 0253, "guillemotleft" ); + addCharacterEncoding( 0273, "guillemotright" ); + addCharacterEncoding( 0254, "guilsinglleft" ); + addCharacterEncoding( 0255, "guilsinglright" ); + addCharacterEncoding( 0150, "h" ); + addCharacterEncoding( 0315, "hungarumlaut" ); + addCharacterEncoding( 0055, "hyphen" ); + addCharacterEncoding( 0151, "i" ); + addCharacterEncoding( 0152, "j" ); + addCharacterEncoding( 0153, "k" ); + addCharacterEncoding( 0154, "l" ); + addCharacterEncoding( 0074, "less" ); + addCharacterEncoding( 0370, "lslash" ); + addCharacterEncoding( 0155, "m" ); + addCharacterEncoding( 0305, "macron" ); + addCharacterEncoding( 0156, "n" ); + addCharacterEncoding( 0071, "nine" ); + addCharacterEncoding( 0043, "numbersign" ); + addCharacterEncoding( 0157, "o" ); + addCharacterEncoding( 0372, "oe" ); + addCharacterEncoding( 0316, "ogonek" ); + addCharacterEncoding( 0061, "one" ); + addCharacterEncoding( 0343, "ordfeminine" ); + addCharacterEncoding( 0353, "ordmasculine" ); + addCharacterEncoding( 0371, "oslash" ); + addCharacterEncoding( 0160, "p" ); + addCharacterEncoding( 0266, "paragraph" ); + addCharacterEncoding( 0050, "parenleft" ); + addCharacterEncoding( 0051, "parenright" ); + addCharacterEncoding( 0045, "percent" ); + addCharacterEncoding( 0056, "period" ); + addCharacterEncoding( 0264, "periodcentered" ); + addCharacterEncoding( 0275, "perthousand" ); + addCharacterEncoding( 0053, "plus" ); + addCharacterEncoding( 0161, "q" ); + addCharacterEncoding( 0077, "question" ); + addCharacterEncoding( 0277, "questiondown" ); + addCharacterEncoding( 0042, "quotedbl" ); + addCharacterEncoding( 0271, "quotedblbase" ); + addCharacterEncoding( 0252, "quotedblleft" ); + addCharacterEncoding( 0272, "quotedblright" ); + addCharacterEncoding( 0140, "quoteleft" ); + addCharacterEncoding( 0047, "quoteright" ); + addCharacterEncoding( 0270, "quotesinglbase" ); + addCharacterEncoding( 0251, "quotesingle" ); + addCharacterEncoding( 0162, "r" ); + addCharacterEncoding( 0312, "ring" ); + addCharacterEncoding( 0163, "s" ); + addCharacterEncoding( 0247, "section" ); + addCharacterEncoding( 0073, "semicolon" ); + addCharacterEncoding( 0067, "seven" ); + addCharacterEncoding( 0066, "six" ); + addCharacterEncoding( 0057, "slash" ); + addCharacterEncoding( 0040, "space" ); + addCharacterEncoding( 0243, "sterling" ); + addCharacterEncoding( 0164, "t" ); + addCharacterEncoding( 0063, "three" ); + addCharacterEncoding( 0304, "tilde" ); + addCharacterEncoding( 0062, "two" ); + addCharacterEncoding( 0165, "u" ); + addCharacterEncoding( 0137, "underscore" ); + addCharacterEncoding( 0166, "v" ); + addCharacterEncoding( 0167, "w" ); + addCharacterEncoding( 0170, "x" ); + addCharacterEncoding( 0171, "y" ); + addCharacterEncoding( 0245, "yen" ); + addCharacterEncoding( 0172, "z" ); + addCharacterEncoding( 0060, "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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/Type1Encoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/Type1Encoding.java new file mode 100644 index 000000000..4ee66c95c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/Type1Encoding.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding; + +import org.apache.pdfbox.cos.COSBase; + +/** + * This class represents an encoding which was read from a type1 font. + * + */ +public class Type1Encoding extends Encoding +{ + public Type1Encoding(int size) + { + for (int i=1;iBen Litchfield + * @version $Revision: 1.10 $ + */ +public class WinAnsiEncoding extends Encoding +{ + + /** + * Singleton instance of this class. + * + * @since Apache PDFBox 1.3.0 + */ + public static final WinAnsiEncoding INSTANCE = new WinAnsiEncoding(); + + /** + * Constructor. + */ + public WinAnsiEncoding() + { + addCharacterEncoding( 0101, "A" );; + addCharacterEncoding( 0306, "AE" );; + addCharacterEncoding( 0301, "Aacute" );; + addCharacterEncoding( 0302, "Acircumflex" );; + addCharacterEncoding( 0304, "Adieresis" );; + addCharacterEncoding( 0300, "Agrave" );; + addCharacterEncoding( 0305, "Aring" );; + addCharacterEncoding( 0303, "Atilde" );; + addCharacterEncoding( 0102, "B" );; + addCharacterEncoding( 0103, "C" );; + addCharacterEncoding( 0307, "Ccedilla" );; + addCharacterEncoding( 0104, "D" );; + addCharacterEncoding( 0105, "E" );; + addCharacterEncoding( 0311, "Eacute" );; + addCharacterEncoding( 0312, "Ecircumflex" );; + addCharacterEncoding( 0313, "Edieresis" );; + addCharacterEncoding( 0310, "Egrave" );; + addCharacterEncoding( 0320, "Eth" );; + addCharacterEncoding( 0200, "Euro" );; + addCharacterEncoding( 0106, "F" );; + addCharacterEncoding( 0107, "G" );; + addCharacterEncoding( 0110, "H" );; + addCharacterEncoding( 0111, "I" );; + addCharacterEncoding( 0315, "Iacute" );; + addCharacterEncoding( 0316, "Icircumflex" );; + addCharacterEncoding( 0317, "Idieresis" );; + addCharacterEncoding( 0314, "Igrave" );; + addCharacterEncoding( 0112, "J" );; + addCharacterEncoding( 0113, "K" );; + addCharacterEncoding( 0114, "L" );; + addCharacterEncoding( 0115, "M" );; + addCharacterEncoding( 0116, "N" );; + addCharacterEncoding( 0321, "Ntilde" );; + addCharacterEncoding( 0117, "O" );; + addCharacterEncoding( 0214, "OE" );; + addCharacterEncoding( 0323, "Oacute" );; + addCharacterEncoding( 0324, "Ocircumflex" );; + addCharacterEncoding( 0326, "Odieresis" );; + addCharacterEncoding( 0322, "Ograve" );; + addCharacterEncoding( 0330, "Oslash" );; + addCharacterEncoding( 0325, "Otilde" );; + addCharacterEncoding( 0120, "P" );; + addCharacterEncoding( 0121, "Q" );; + addCharacterEncoding( 0122, "R" );; + addCharacterEncoding( 0123, "S" );; + addCharacterEncoding( 0212, "Scaron" );; + addCharacterEncoding( 0124, "T" );; + addCharacterEncoding( 0336, "Thorn" );; + addCharacterEncoding( 0125, "U" );; + addCharacterEncoding( 0332, "Uacute" );; + addCharacterEncoding( 0333, "Ucircumflex" );; + addCharacterEncoding( 0334, "Udieresis" );; + addCharacterEncoding( 0331, "Ugrave" );; + addCharacterEncoding( 0126, "V" );; + addCharacterEncoding( 0127, "W" );; + addCharacterEncoding( 0130, "X" );; + addCharacterEncoding( 0131, "Y" );; + addCharacterEncoding( 0335, "Yacute" );; + addCharacterEncoding( 0237, "Ydieresis" );; + addCharacterEncoding( 0132, "Z" );; + addCharacterEncoding( 0216, "Zcaron" );; + addCharacterEncoding( 0141, "a" );; + addCharacterEncoding( 0341, "aacute" );; + addCharacterEncoding( 0342, "acircumflex" );; + addCharacterEncoding( 0264, "acute" );; + addCharacterEncoding( 0344, "adieresis" );; + addCharacterEncoding( 0346, "ae" );; + addCharacterEncoding( 0340, "agrave" );; + addCharacterEncoding( 046, "ampersand" );; + addCharacterEncoding( 0345, "aring" );; + addCharacterEncoding( 0136, "asciicircum" );; + addCharacterEncoding( 0176, "asciitilde" );; + addCharacterEncoding( 052, "asterisk" );; + addCharacterEncoding( 0100, "at" );; + addCharacterEncoding( 0343, "atilde" );; + addCharacterEncoding( 0142, "b" );; + addCharacterEncoding( 0134, "backslash" );; + addCharacterEncoding( 0174, "bar" );; + addCharacterEncoding( 0173, "braceleft" );; + addCharacterEncoding( 0175, "braceright" );; + addCharacterEncoding( 0133, "bracketleft" );; + addCharacterEncoding( 0135, "bracketright" );; + addCharacterEncoding( 0246, "brokenbar" );; + addCharacterEncoding( 0225, "bullet" );; + addCharacterEncoding( 0143, "c" );; + addCharacterEncoding( 0347, "ccedilla" );; + addCharacterEncoding( 0270, "cedilla" );; + addCharacterEncoding( 0242, "cent" );; + addCharacterEncoding( 0210, "circumflex" );; + addCharacterEncoding( 072, "colon" );; + addCharacterEncoding( 054, "comma" );; + addCharacterEncoding( 0251, "copyright" );; + addCharacterEncoding( 0244, "currency" );; + addCharacterEncoding( 0144, "d" );; + addCharacterEncoding( 0206, "dagger" );; + addCharacterEncoding( 0207, "daggerdbl" );; + addCharacterEncoding( 0260, "degree" );; + addCharacterEncoding( 0250, "dieresis" );; + addCharacterEncoding( 0367, "divide" );; + addCharacterEncoding( 044, "dollar" );; + addCharacterEncoding( 0145, "e" );; + addCharacterEncoding( 0351, "eacute" );; + addCharacterEncoding( 0352, "ecircumflex" );; + addCharacterEncoding( 0353, "edieresis" );; + addCharacterEncoding( 0350, "egrave" );; + addCharacterEncoding( 070, "eight" );; + addCharacterEncoding( 0205, "ellipsis" );; + addCharacterEncoding( 0227, "emdash" );; + addCharacterEncoding( 0226, "endash" );; + addCharacterEncoding( 075, "equal" );; + addCharacterEncoding( 0360, "eth" );; + addCharacterEncoding( 041, "exclam" );; + addCharacterEncoding( 0241, "exclamdown" );; + addCharacterEncoding( 0146, "f" );; + addCharacterEncoding( 065, "five" );; + addCharacterEncoding( 0203, "florin" );; + addCharacterEncoding( 064, "four" );; + addCharacterEncoding( 0147, "g" );; + addCharacterEncoding( 0337, "germandbls" );; + addCharacterEncoding( 0140, "grave" );; + addCharacterEncoding( 076, "greater" );; + addCharacterEncoding( 0253, "guillemotleft" );; + addCharacterEncoding( 0273, "guillemotright" );; + addCharacterEncoding( 0213, "guilsinglleft" );; + addCharacterEncoding( 0233, "guilsinglright" );; + addCharacterEncoding( 0150, "h" );; + addCharacterEncoding( 055, "hyphen" );; + addCharacterEncoding( 0151, "i" );; + addCharacterEncoding( 0355, "iacute" );; + addCharacterEncoding( 0356, "icircumflex" );; + addCharacterEncoding( 0357, "idieresis" );; + addCharacterEncoding( 0354, "igrave" );; + addCharacterEncoding( 0152, "j" );; + addCharacterEncoding( 0153, "k" );; + addCharacterEncoding( 0154, "l" );; + addCharacterEncoding( 074, "less" );; + addCharacterEncoding( 0254, "logicalnot" );; + addCharacterEncoding( 0155, "m" );; + addCharacterEncoding( 0257, "macron" );; + addCharacterEncoding( 0265, "mu" );; + addCharacterEncoding( 0327, "multiply" );; + addCharacterEncoding( 0156, "n" );; + addCharacterEncoding( 071, "nine" );; + addCharacterEncoding( 0361, "ntilde" );; + addCharacterEncoding( 043, "numbersign" );; + addCharacterEncoding( 0157, "o" );; + addCharacterEncoding( 0363, "oacute" );; + addCharacterEncoding( 0364, "ocircumflex" );; + addCharacterEncoding( 0366, "odieresis" );; + addCharacterEncoding( 0234, "oe" );; + addCharacterEncoding( 0362, "ograve" );; + addCharacterEncoding( 061, "one" );; + addCharacterEncoding( 0275, "onehalf" );; + addCharacterEncoding( 0274, "onequarter" );; + addCharacterEncoding( 0271, "onesuperior" );; + addCharacterEncoding( 0252, "ordfeminine" );; + addCharacterEncoding( 0272, "ordmasculine" );; + addCharacterEncoding( 0370, "oslash" );; + addCharacterEncoding( 0365, "otilde" );; + addCharacterEncoding( 0160, "p" );; + addCharacterEncoding( 0266, "paragraph" );; + addCharacterEncoding( 050, "parenleft" );; + addCharacterEncoding( 051, "parenright" );; + addCharacterEncoding( 045, "percent" );; + addCharacterEncoding( 056, "period" );; + addCharacterEncoding( 0267, "periodcentered" );; + addCharacterEncoding( 0211, "perthousand" );; + addCharacterEncoding( 053, "plus" );; + addCharacterEncoding( 0261, "plusminus" );; + addCharacterEncoding( 0161, "q" );; + addCharacterEncoding( 077, "question" );; + addCharacterEncoding( 0277, "questiondown" );; + addCharacterEncoding( 042, "quotedbl" );; + addCharacterEncoding( 0204, "quotedblbase" );; + addCharacterEncoding( 0223, "quotedblleft" );; + addCharacterEncoding( 0224, "quotedblright" );; + addCharacterEncoding( 0221, "quoteleft" );; + addCharacterEncoding( 0222, "quoteright" );; + addCharacterEncoding( 0202, "quotesinglbase" );; + addCharacterEncoding( 047, "quotesingle" );; + addCharacterEncoding( 0162, "r" );; + addCharacterEncoding( 0256, "registered" );; + addCharacterEncoding( 0163, "s" );; + addCharacterEncoding( 0232, "scaron" );; + addCharacterEncoding( 0247, "section" );; + addCharacterEncoding( 073, "semicolon" );; + addCharacterEncoding( 067, "seven" );; + addCharacterEncoding( 066, "six" );; + addCharacterEncoding( 057, "slash" );; + addCharacterEncoding( 040, "space" );; + addCharacterEncoding( 0243, "sterling" );; + addCharacterEncoding( 0164, "t" );; + addCharacterEncoding( 0376, "thorn" );; + addCharacterEncoding( 063, "three" );; + addCharacterEncoding( 0276, "threequarters" );; + addCharacterEncoding( 0263, "threesuperior" );; + addCharacterEncoding( 0230, "tilde" );; + addCharacterEncoding( 0231, "trademark" );; + addCharacterEncoding( 062, "two" );; + addCharacterEncoding( 0262, "twosuperior" );; + addCharacterEncoding( 0165, "u" );; + addCharacterEncoding( 0372, "uacute" );; + addCharacterEncoding( 0373, "ucircumflex" );; + addCharacterEncoding( 0374, "udieresis" );; + addCharacterEncoding( 0371, "ugrave" );; + addCharacterEncoding( 0137, "underscore" );; + addCharacterEncoding( 0166, "v" );; + addCharacterEncoding( 0167, "w" );; + addCharacterEncoding( 0170, "x" );; + addCharacterEncoding( 0171, "y" );; + addCharacterEncoding( 0375, "yacute" );; + addCharacterEncoding( 0377, "ydieresis" );; + addCharacterEncoding( 0245, "yen" );; + addCharacterEncoding( 0172, "z" );; + addCharacterEncoding( 0236, "zcaron" );; + addCharacterEncoding( 060, "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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKConverter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKConverter.java new file mode 100644 index 000000000..ef18cfce1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKConverter.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.encoding.conversion; + +import org.apache.fontbox.cmap.CMap; +import java.io.UnsupportedEncodingException; + + +/** + * CJKConverter converts encodings defined in CJKEncodings. + * + * @author Pin Xue (http://www.pinxue.net), Holly Lee (holly.lee (at) gmail.com) + * @version $Revision: 1.0 $ + */ +public class CJKConverter implements EncodingConverter +{ + // The encoding + private String encodingName = null; + // The java charset name + private String charsetName = null; + + + /** + * Constructs a CJKConverter from a PDF encoding name. + * + * @param encoding the encoding to be used + */ + public CJKConverter(String encoding) + { + encodingName = encoding; + charsetName = CJKEncodings.getCharset(encoding); + } + + /** + * Convert a string. It occurs when a cmap lookup returned + * converted bytes successfully, but we still need to convert its + * encoding. The parameter s is constructs as one byte or a UTF-16BE + * encoded string. + * + * Note: pdfbox set string to UTF-16BE charset before calling into + * this. + * + * {@inheritDoc} + */ + public String convertString(String s) + { + if ( s.length() == 1 ) + { + return s; + } + + if ( charsetName.equalsIgnoreCase("UTF-16BE") ) + { + return s; + } + + try + { + return new String(s.getBytes("UTF-16BE"), charsetName); + } + catch ( UnsupportedEncodingException uee ) + { + return s; + } + } + + /** + * Convert bytes to a string. We just convert bytes within + * coderange defined in CMap. + * + * {@inheritDoc} + */ + public String convertBytes(byte [] c, int offset, int length, CMap cmap) + { + if ( cmap != null ) + { + try + { + if ( cmap.isInCodeSpaceRanges(c, offset, length) ) + { + return new String(c, offset, length, charsetName); + } + else + { + return null; + } + + } + catch ( UnsupportedEncodingException uee ) + { + return new String(c, offset, length); + } + } + // No cmap? + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKEncoding.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKEncoding.java new file mode 100644 index 000000000..a1c8d95ef --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CJKEncoding.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding.conversion; + +import java.util.HashMap; +import java.util.Iterator; + +/** + * This class represents PDF encoding name to Java charset name mapping. + * + * @author Pin Xue (http://www.pinxue.net), Holly Lee (holly.lee (at) gmail.com) + * @version $Revision: 1.0 $ + */ +class CJKEncodings +{ + // Mapping: PDF encoding name -> Java (IANA) charset name + private static HashMap charsetMapping = new HashMap(); + + private CJKEncodings() + { + } + + static + { + // Chinese (Simplified) + // Microsoft Code Page 936 (lfCharSet 0x86), GB 2312-80 character set, EUC-CN encoding + charsetMapping.put("GB-EUC-H", "GB2312"); + // Vertical version of GB-EUC-H + charsetMapping.put("GB-EUC-V", "GB2312"); + // Mac OS, GB 2312-80 character set, EUC-CN encoding, Script Manager code 19 + charsetMapping.put("GBpc-EUC-H", "GB2312"); + // Vertical version of GBpc-EUC-H + charsetMapping.put("GBpc-EUC-V", "GB2312"); + // Microsoft Code Page 936 (lfCharSet 0x86), GBK character set, GBK encoding + charsetMapping.put("GBK-EUC-H", "GBK"); + // Vertical version of GBK-EUC-H + charsetMapping.put("GBK-EUC-V", "GBK"); + // Same as GBK-EUC-H but replaces half-width Latin characters with proportional + // forms and maps character code 0x24 to a dollar sign ($) instead of a yuan symbol (♀∴) + charsetMapping.put("GBKp-EUC-H", "GBK"); + // Vertical version of GBKp-EUC-H + charsetMapping.put("GBKp-EUC-V", "GBK"); + // GB 18030-2000 character set, mixed 1-, 2-, and 4-byte encoding + charsetMapping.put("GBK2K-H", "GB18030"); + // Vertical version of GBK2K-H + charsetMapping.put("GBK2K-V", "GB18030"); + // Unicode (UCS-2) encoding for the Adobe-GB1 character collection + charsetMapping.put("UniGB-UCS2-H", "ISO-10646-UCS-2"); + // Vertical version of UniGB-UCS2-H + charsetMapping.put("UniGB-UCS2-V", "ISO-10646-UCS-2"); + // Unicode (UTF-16BE) encoding for the Adobe-GB1 character collection; contains mappings + // for all characters in the GB18030-2000 character set + charsetMapping.put("UniGB-UTF16-H", "UTF-16BE"); + // Vertical version of UniGB-UTF16-H + charsetMapping.put("UniGB-UTF16-V", "UTF-16BE"); + + // Chinese (Traditional) + // Mac OS, Big Five character set, Big Five encoding, Script Manager code 2 + charsetMapping.put("B5pc-H", "BIG5"); + // Vertical version of B5pc-H + charsetMapping.put("B5pc-V", "BIG5"); + // Hong Kong SCS, an extension to the Big Five character set and encoding + charsetMapping.put("HKscs-B5-H", "Big5-HKSCS"); + // Vertical version of HKscs-B5-H + charsetMapping.put("HKscs-B5-V", "Big5-HKSCS"); + // Microsoft Code Page 950 (lfCharSet 0x88), Big Five character set with ETen extensions + charsetMapping.put("ETen-B5-H", "BIG5"); + // Vertical version of ETen-B5-H + charsetMapping.put("ETen-B5-V", "BIG5"); + // Same as ETen-B5-H but replaces half-width Latin characters with proportional forms + charsetMapping.put("ETenms-B5-H", "BIG5"); + // Vertical version of ETenms-B5-H + charsetMapping.put("ETenms-B5-V", "BIG5"); + // CNS 11643-1992 character set, EUC-TW encoding + charsetMapping.put("CNS-EUC-H", "HZ"); + // Vertical version of CNS-EUC-H + charsetMapping.put("CNS-EUC-V", "HZ"); + // Unicode (UCS-2) encoding for the Adobe-CNS1 character collection + charsetMapping.put("UniCNS-UCS2-H", "ISO-10646-UCS-2"); + // Vertical version of UniCNS-UCS2-H + charsetMapping.put("UniCNS-UCS2-V", "ISO-10646-UCS-2"); + // Unicode (UTF-16BE) encoding for the Adobe-CNS1 character collection; + // contains mappings for all the characters in the HKSCS-2001 character set and + // contains both 2- and 4- byte character codes + charsetMapping.put("UniCNS-UTF16-H", "UTF-16BE"); + // Vertical version of UniCNS-UTF16-H + charsetMapping.put("UniCNS-UTF16-V", "UTF-16BE"); + + //Japanese + // Mac OS, JIS X 0208 character set with KanjiTalk6 extensions, Shift-JIS encoding, Script Manager code 1 + charsetMapping.put("83pv-RKSJ-H", "JIS"); + // Microsoft Code Page 932 (lfCharSet 0x80), JIS X 0208 character set with NEC and IBM- extensions + charsetMapping.put("90ms-RKSJ-H", "JIS"); + // Vertical version of 90ms-RKSJ-H + charsetMapping.put("90ms-RKSJ-V", "JIS"); + // Same as 90ms-RKSJ-H but replaces half-width Latin characters with proportional forms + charsetMapping.put("90msp-RKSJ-H", "JIS"); + // Vertical version of 90msp-RKSJ-H + charsetMapping.put("90msp-RKSJ-V", "JIS"); + // Mac OS, JIS X 0208 character set with KanjiTalk7 extensions, Shift-JIS encoding, Script Manager code 1 + charsetMapping.put("90pv-RKSJ-H", "JIS"); + // JIS X 0208 character set with Fujitsu FMR extensions, Shift-JIS encoding + charsetMapping.put("Add-RKSJ-H", "JIS"); + // Vertical version of Add-RKSJ-H + charsetMapping.put("Add-RKSJ-V", "JIS"); + // JIS X 0208 character set, EUC-JP encoding + charsetMapping.put("EUC-H", "JIS"); + // Vertical version of EUC-H + charsetMapping.put("EUC-V", "JIS"); + // JIS C 6226 (JIS78) character set with NEC extensions, Shift-JIS encoding + charsetMapping.put("Ext-RKSJ-H", "JIS"); + // Vertical version of Ext-RKSJ-H + charsetMapping.put("Ext-RKSJ-V", "JIS"); + // JIS X 0208 character set, ISO-2022-JP encoding + charsetMapping.put("H", "JIS"); + // Vertical version of H + charsetMapping.put("V", "JIS"); + // Unicode (UCS-2) encoding for the Adobe-Japan1 character collection + charsetMapping.put("UniJIS-UCS2-H", "ISO-10646-UCS-2"); + // Vertical version of UniJIS-UCS2-H + charsetMapping.put("UniJIS-UCS2-V", "ISO-10646-UCS-2"); + // Same as UniJIS-UCS2-H but replaces proportional Latin characters with half-width forms + charsetMapping.put("UniJIS-UCS2-HW-H", "ISO-10646-UCS-2"); + // Vertical version of UniJIS-UCS2-HW-H + charsetMapping.put("UniJIS-UCS2-HW-V", "ISO-10646-UCS-2"); + // Unicode (UTF-16BE) encoding for the Adobe-Japan1 character collection; + // contains mappings for all characters in the JIS X 0213:1000 character set + charsetMapping.put("UniJIS-UTF16-H", "UTF-16BE"); + // Vertical version of UniJIS-UTF16-H + charsetMapping.put("UniJIS-UTF16-V", "UTF-16BE"); + // JIS X 0208 character set, ISO-2022-JP encoding + charsetMapping.put("Identity-H", "JIS"); + // Vertical version of H + charsetMapping.put("Identity-V", "JIS"); + + //Korean + // KS X 1001:1992 character set, EUC-KR encoding + charsetMapping.put("KSC-EUC-H", "KSC"); + // Vertical version of KSC-EUC-H + charsetMapping.put("KSC-EUC-V", "KSC"); + // Microsoft Code Page 949 (lfCharSet 0x81), KS X 1001:1992 character set + // plus 8822.putitional hangul, Unified Hangul Code (UHC) encoding + charsetMapping.put("KSCms-UHC-H", "KSC"); + // Vertical version of KSCms-UHC-H + charsetMapping.put("KSCms-UHC-V", "KSC"); + // Same as KSCms-UHC-H but replaces proportional Latin characters with half-width forms + charsetMapping.put("KSCms-UHC-HW-H", "KSC"); + // Vertical version of KSCms-UHC-HW-H + charsetMapping.put("KSCms-UHC-HW-V", "KSC"); + // Mac OS, KS X 1001:1992 character set with Mac OS KH extensions, Script Manager Code 3 + charsetMapping.put("KSCpc-EUC-H", "KSC"); + // Unicode (UCS-2) encoding for the Adobe-Korea1 character collection + charsetMapping.put("UniKS-UCS2-H", "ISO-10646-UCS-2"); + // Vertical version of UniKS-UCS2-H + charsetMapping.put("UniKS-UCS2-V", "ISO-10646-UCS-2"); + // Unicode (UTF-16BE) encoding for the Adobe-Korea1 character collection + charsetMapping.put("UniKS-UTF16-H", "UTF-16BE"); + // Vertical version of UniKS-UTF16-H + charsetMapping.put("UniKS-UTF16-V", "UTF-16BE"); + } + + + /** + * Get respective Java charset name from given PDF encoding name. + * + * @param encoding PDF encoding name + * @return Java charset name, or null if not found + */ + public static final String getCharset( String encoding ) + { + if ( encoding.startsWith("COSName")) + { + encoding = encoding.substring(8, encoding.length()-1); + } + return (String)(charsetMapping.get(encoding)); + } + + /** + * Return an iterator to iterate through all encodings. + */ + public static final Iterator getEncodingIterator() + { + return charsetMapping.keySet().iterator(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CMapSubstitution.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CMapSubstitution.java new file mode 100644 index 000000000..2f335773e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/CMapSubstitution.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding.conversion; + +import java.util.HashMap; + +/** + * This class provides a mapping from char code to unicode mapping files used for CJK-encoding. + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + * + */ + +public class CMapSubstitution +{ + + private static HashMap cmapSubstitutions = new HashMap(); + + private CMapSubstitution() + { + } + + static + { + // I don't know if these mappings are complete. Perhaps there + // has to be added still one or more + + // chinese simplified + cmapSubstitutions.put( "Adobe-GB1-4", "Adobe-GB1-UCS2" ); + cmapSubstitutions.put( "GBK-EUC-H", "GBK-EUC-UCS2" ); + cmapSubstitutions.put( "GBK-EUC-V", "GBK-EUC-UCS2" ); + cmapSubstitutions.put( "GBpc-EUC-H", "GBpc-EUC-UCS2C" ); + cmapSubstitutions.put( "GBpc-EUC-V", "GBpc-EUC-UCS2C" ); + + // chinese traditional + cmapSubstitutions.put( "Adobe-CNS1-4", "Adobe-CNS1-UCS2" ); + cmapSubstitutions.put( "B5pc-H", "B5pc-UCS2" ); + cmapSubstitutions.put( "B5pc-V", "B5pc-UCS2" ); + cmapSubstitutions.put( "ETen-B5-H", "ETen-B5-UCS2" ); + cmapSubstitutions.put( "ETen-B5-V", "ETen-B5-UCS2" ); + cmapSubstitutions.put( "ETenms-B5-H", "ETen-B5-UCS2" ); + cmapSubstitutions.put( "ETenms-B5-V", "ETen-B5-UCS2" ); + + // japanese + cmapSubstitutions.put( "90ms-RKSJ-H", "90ms-RKSJ-UCS2" ); + cmapSubstitutions.put( "90ms-RKSJ-V", "90ms-RKSJ-UCS2" ); + cmapSubstitutions.put( "90msp-RKSJ-H", "90ms-RKSJ-UCS2" ); + cmapSubstitutions.put( "90msp-RKSJ-V", "90ms-RKSJ-UCS2" ); + cmapSubstitutions.put( "90pv-RKSJ-H", "90pv-RKSJ-UCS2"); + cmapSubstitutions.put( "UniJIS-UCS2-HW-H", "UniJIS-UCS2-H" ); + cmapSubstitutions.put( "Adobe-Japan1-4", "Adobe-Japan1-UCS2"); + + cmapSubstitutions.put( "Adobe-Identity-0", "Identity-H"); + cmapSubstitutions.put( "Adobe-Identity-1", "Identity-H"); + } + + /** + * + * @param cmapName The name of a cmap for which we have to find a possible substitution + * @return the substitution for the given cmap name + */ + public static String substituteCMap(String cmapName) + { + if (cmapSubstitutions.containsKey(cmapName)) + { + return (String)cmapSubstitutions.get(cmapName); + } + return cmapName; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConversionManager.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConversionManager.java new file mode 100644 index 000000000..3020fa6ce --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConversionManager.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding.conversion; + +import java.util.Iterator; +import java.util.HashMap; + +/** + * EncodingConversionManager maintains relationship between PDF encoding name + * and respective EncodingConverter instance. Those PDF encoding name like + * GBK-EUC-H should be converted to java charset name before constructing a + * java string instance + * + * @author Pin Xue (http://www.pinxue.net), Holly Lee (holly.lee (at) gmail.com) + * @version $Revision: 1.0 $ + */ +public class EncodingConversionManager +{ + /** + * Mapping from PDF encoding name to EncodingConverter instance. + */ + private static HashMap encodingMap = new HashMap(); + + private EncodingConversionManager() + { + } + + /** + * Initialize the encodingMap before anything calls us. + */ + static { + + // Add CJK encodings to map + Iterator it = CJKEncodings.getEncodingIterator(); + + while ( it.hasNext() ) + { + String encodingName = (String)(it.next()); + encodingMap.put(encodingName, new CJKConverter(encodingName)); + } + // If there is any other encoding conversions, please add it here. + + } + + /** + * Get converter from given encoding name. If no converted defined, + * a null is returned. + * + * @param encoding search for a converter for the given encoding name + * @return the converter for the given encoding name + */ + public static final EncodingConverter getConverter(String encoding) + { + return (EncodingConverter)(encodingMap.get(encoding)); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConverter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConverter.java new file mode 100644 index 000000000..c80cd3d85 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/EncodingConverter.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encoding.conversion; + +import org.apache.fontbox.cmap.CMap; + +/** + * EncodingConverter converts string or characters in one encoding, which is specified in PDF + * file, to another string with respective java charset. The mapping from + * PDF encoding name to java charset name is maintained by EncodingConversionManager + + * @author Pin Xue (http://www.pinxue.net), Holly Lee (holly.lee (at) gmail.com) + * @version $Revision: 1.0 $ + */ +public interface EncodingConverter +{ + /** + * Convert a string. + * + * @param s the string to be converted + * @return the converted string + */ + public String convertString(String s); + + /** + * Convert bytes to a string. + * + * @param c the byte array to be converted + * @param offset the starting offset of the array + * @param length the number of bytes + * @param cmap the cmap to be used for conversion + * @return the converted string + */ + public String convertBytes(byte [] c, int offset, int length, CMap cmap); +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/package.html new file mode 100644 index 000000000..9ef0fd9fe --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/conversion/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains the implementations for conversions of encodings and CMaps that are used in PDF documents. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/package.html new file mode 100644 index 000000000..0c6203685 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encoding/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains the implementations for all of the encodings that are used in PDF documents. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/ARCFour.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/ARCFour.java new file mode 100644 index 000000000..6773828dc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/ARCFour.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.8 $ + */ +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 ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/DocumentEncryption.java b/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/DocumentEncryption.java new file mode 100644 index 000000000..b333ee255 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/DocumentEncryption.java @@ -0,0 +1,409 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.encryption; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.PDStandardEncryption; + +/** + * This class will deal with encrypting/decrypting a document. + * + * @author Ben Litchfield + * @version $Revision: 1.13 $ + * + * @deprecated use the new security API instead. + * + * @see org.apache.pdfbox.pdmodel.encryption.StandardSecurityHandler + */ +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("ISO-8859-1") ); + md.update( userPassword.getBytes("ISO-8859-1") ); + md.update( document.toString().getBytes() ); + byte[] id = md.digest( this.toString().getBytes("ISO-8859-1") ); + 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("ISO-8859-1"), u, + o, permissions, id.getBytes(), revision, length ); + boolean isOwnerPassword = + encryption.isOwnerPassword( password.getBytes("ISO-8859-1"), u, + o, permissions, id.getBytes(), revision, length ); + + if( isUserPassword ) + { + encryptionKey = + encryption.computeEncryptedKey( + password.getBytes("ISO-8859-1"), o, + permissions, id.getBytes(), revision, length ); + } + else if( isOwnerPassword ) + { + byte[] computedUserPassword = + encryption.getUserPassword( + password.getBytes("ISO-8859-1"), + 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 entry : dictionary.entrySet() ) + { + //if we are a signature dictionary and contain a Contents entry then + //we don't decrypt it. + if( !(entry.getKey().getName().equals( "Contents" ) && + entry.getValue() instanceof COSString && + potentialSignatures.contains( dictionary ))) + { + decrypt( entry.getValue(), 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. + */ + private 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; iBen Litchfield + * @version $Revision: 1.15 $ + * + * @deprecated use the new security layer instead + * + * @see org.apache.pdfbox.pdmodel.encryption.StandardSecurityHandler + */ +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>> 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/package.html new file mode 100644 index 000000000..725c31c20 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/encryption/package.html @@ -0,0 +1,25 @@ + + + + + + + +These classes deal with encryption algorithms that are used in the PDF Document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/AbstractExample.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/AbstractExample.java new file mode 100644 index 000000000..57b85892c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/AbstractExample.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.pdfbox.cos.COSDocument; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.pdfbox.pdfwriter.COSWriter; + +/** + * A simple class which has some methods used by all examples. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/PrintFields.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/PrintFields.java new file mode 100644 index 000000000..7908a76ae --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/PrintFields.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.fdf; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.List; + +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; + +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.exceptions.InvalidPasswordException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; + +/** + * This example will take a PDF document and print all the fields from the file. + * + * @author Ben Litchfield + * @version $Revision: 1.16 $ + */ +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. + *
+ * 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.apache.pdfbox.examples.fdf.PrintFields " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/SetField.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/SetField.java new file mode 100644 index 000000000..c6b96e151 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/SetField.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.examples.AbstractExample; + +/** + * This example will take a PDF document and set a FDF field in it. + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +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. + *
+ * 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.apache.pdfbox.examples.fdf.SetField " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/package.html new file mode 100644 index 000000000..bfbcc46f0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/fdf/package.html @@ -0,0 +1,25 @@ + + + + + + + +These are examples that use the FDF features of a PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/package.html new file mode 100644 index 000000000..b610b5cfa --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/package.html @@ -0,0 +1,25 @@ + + + + + + + +The packages in this package will show how to use the PDFBox API. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddImageToPDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddImageToPDF.java new file mode 100644 index 000000000..40ccff97a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddImageToPDF.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.io.RandomAccessFile; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; + +import org.apache.pdfbox.pdmodel.graphics.xobject.PDCcitt; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; + + +/** + * This is an example that creates a reads a document and adds an image to it.. + * + * The example is taken from the pdf file format specification. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class AddImageToPDF +{ + + /** + * Add an image to an existing PDF document. + * + * @param inputFile The input PDF to add the image to. + * @param image The filename of the image to put in the PDF. + * @param outputFile The file to write to 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 createPDFFromImage( String inputFile, String image, String outputFile ) + throws IOException, COSVisitorException + { + // the document + PDDocument doc = null; + try + { + doc = PDDocument.load( inputFile ); + + //we will add the image to the first page. + PDPage page = (PDPage)doc.getDocumentCatalog().getAllPages().get( 0 ); + + 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, true, true); + + contentStream.drawImage( ximage, 20, 20 ); + + contentStream.close(); + doc.save( outputFile ); + } + finally + { + if( doc != null ) + { + doc.close(); + } + } + } + + /** + * This will load a PDF document and add a single image on it. + *
+ * see usage() for commandline + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + AddImageToPDF app = new AddImageToPDF(); + try + { + if( args.length != 3 ) + { + app.usage(); + } + else + { + app.createPDFFromImage( 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddJavascript.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddJavascript.java new file mode 100644 index 000000000..e3bdae3d2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddJavascript.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.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 + * @version $Revision: 1.3 $ + */ +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.apache.pdfbox.examples.pdmodel.AddJavascript " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java new file mode 100644 index 000000000..a39ad58d8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMessageToEachPage.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; + + +/** + * This is an example of how to add a message to every page + * in a pdf document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 = 36.0f; + + for( int i=0; i + * 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java new file mode 100644 index 000000000..0b8f7bdbe --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/AddMetadataFromDocInfo.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchemaBasic; +import org.apache.jempbox.xmp.XMPSchemaDublinCore; +import org.apache.jempbox.xmp.XMPSchemaPDF; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDDocumentInformation; +import org.apache.pdfbox.pdmodel.common.PDMetadata; + +import java.util.GregorianCalendar; + +/** + * This is an example on how to add metadata to a document. + * + * Usage: java org.apache.pdfbox.examples.pdmodel.AddMetadataToDocument <input-pdf> <output-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class AddMetadataFromDocInfo +{ + 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(); + + XMPMetadata metadata = new XMPMetadata(); + + XMPSchemaPDF pdfSchema = metadata.addPDFSchema(); + pdfSchema.setKeywords( info.getKeywords() ); + pdfSchema.setProducer( info.getProducer() ); + + XMPSchemaBasic basicSchema = metadata.addBasicSchema(); + basicSchema.setModifyDate( info.getModificationDate() ); + basicSchema.setCreateDate( info.getCreationDate() ); + basicSchema.setCreatorTool( info.getCreator() ); + basicSchema.setMetadataDate( new GregorianCalendar() ); + + XMPSchemaDublinCore dcSchema = metadata.addDublinCoreSchema(); + dcSchema.setTitle( info.getTitle() ); + dcSchema.addCreator( "PDFBox" ); + dcSchema.setDescription( info.getSubject() ); + + PDMetadata metadataStream = new PDMetadata(document); + metadataStream.importXMPMetadata( metadata ); + catalog.setMetadata( metadataStream ); + + 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.apache.pdfbox.examples.pdmodel.AddMetadataFromDocInfo " + + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/Annotation.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/Annotation.java new file mode 100644 index 000000000..7612aa3b9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/Annotation.java @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLine; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationSquareCircle; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary; + + +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.2 $ + */ +public class Annotation +{ + private Annotation() + { + //utility class, should not be instantiated. + } + + /** + * This will create a doucument showing various annotations. + * + * @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 = new PDDocument(); + + try + { + PDPage page = new PDPage(); + document.addPage(page); + List annotations = page.getAnnotations(); + + // Setup some basic reusable objects/constants + // Annotations themselves can only be used once! + + float inch = 72; + PDGamma colourRed = new PDGamma(); + colourRed.setR(1); + PDGamma colourBlue = new PDGamma(); + colourBlue.setB(1); + PDGamma colourBlack = new PDGamma(); + + PDBorderStyleDictionary borderThick = new PDBorderStyleDictionary(); + borderThick.setWidth(inch/12); // 12th inch + PDBorderStyleDictionary borderThin = new PDBorderStyleDictionary(); + borderThin.setWidth(inch/72); // 1 point + PDBorderStyleDictionary borderULine = new PDBorderStyleDictionary(); + borderULine.setStyle(PDBorderStyleDictionary.STYLE_UNDERLINE); + borderULine.setWidth(inch/72); // 1 point + + + float pw = page.getMediaBox().getUpperRightX(); + float ph = page.getMediaBox().getUpperRightY(); + + + // First add some text, two lines we'll add some annotations to this later + + + PDFont font = PDType1Font.HELVETICA_BOLD; + + PDPageContentStream contentStream = new PDPageContentStream(document, page); + contentStream.beginText(); + contentStream.setFont( font, 18 ); + contentStream.moveTextPositionByAmount( inch, ph-inch-18); + contentStream.drawString( "PDFBox" ); + contentStream.moveTextPositionByAmount( 0,-(inch/2)); + contentStream.drawString( "Click Here" ); + contentStream.endText(); + + contentStream.close(); + + // Now add the markup annotation, a highlight to PDFBox text + PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); + txtMark.setColour(colourBlue); + txtMark.setConstantOpacity((float)0.2); // Make the highlight 20% transparent + + // Set the rectangle containing the markup + + float textWidth = (font.getStringWidth( "PDFBox" )/1000) * 18; + PDRectangle position = new PDRectangle(); + position.setLowerLeftX(inch); + position.setLowerLeftY( ph-inch-18 ); + position.setUpperRightX(72 + textWidth); + position.setUpperRightY(ph-inch); + txtMark.setRectangle(position); + + // work out the points forming the four corners of the annotations + // set out in anti clockwise form (Completely wraps the text) + // OK, the below doesn't match that description. + // It's what acrobat 7 does and displays properly! + float[] quads = new float[8]; + + quads[0] = position.getLowerLeftX(); // x1 + quads[1] = position.getUpperRightY()-2; // y1 + quads[2] = position.getUpperRightX(); // x2 + quads[3] = quads[1]; // y2 + quads[4] = quads[0]; // x3 + quads[5] = position.getLowerLeftY()-2; // y3 + quads[6] = quads[2]; // x4 + quads[7] = quads[5]; // y5 + + txtMark.setQuadPoints(quads); + txtMark.setContents("Highlighted since it's important"); + + annotations.add(txtMark); + + // Now add the link annotation, so the clickme works + PDAnnotationLink txtLink = new PDAnnotationLink(); + txtLink.setBorderStyle(borderULine); + + // Set the rectangle containing the link + + textWidth = (font.getStringWidth( "Click Here" )/1000) * 18; + position = new PDRectangle(); + position.setLowerLeftX(inch); + position.setLowerLeftY( ph-(float)(1.5*inch)-20); // down a couple of points + position.setUpperRightX(72 + textWidth); + position.setUpperRightY(ph-(float)(1.5*inch)); + txtLink.setRectangle(position); + + // add an action + PDActionURI action = new PDActionURI(); + action.setURI("http://www.pdfbox.org"); + txtLink.setAction(action); + + annotations.add(txtLink); + + + // Now draw a few more annotations + + PDAnnotationSquareCircle aCircle = + new PDAnnotationSquareCircle( PDAnnotationSquareCircle.SUB_TYPE_CIRCLE); + aCircle.setContents("Circle Annotation"); + aCircle.setInteriorColour(colourRed); // Fill in circle in red + aCircle.setColour(colourBlue); // The border itself will be blue + aCircle.setBorderStyle(borderThin); + + // Place the annotation on the page, we'll make this 1" round + // 3" down, 1" in on the page + + position = new PDRectangle(); + position.setLowerLeftX(inch); + position.setLowerLeftY(ph-(3*inch)-inch); // 1" height, 3" down + position.setUpperRightX(2*inch); // 1" in, 1" width + position.setUpperRightY(ph-(3*inch)); // 3" down + aCircle.setRectangle(position); + + // add to the annotations on the page + annotations.add(aCircle); + + // Now a square annotation + + PDAnnotationSquareCircle aSquare = + new PDAnnotationSquareCircle( PDAnnotationSquareCircle.SUB_TYPE_SQUARE); + aSquare.setContents("Square Annotation"); + aSquare.setColour(colourRed); // Outline in red, not setting a fill + aSquare.setBorderStyle(borderThick); + + // Place the annotation on the page, we'll make this 1" (72points) square + // 3.5" down, 1" in from the right on the page + + position = new PDRectangle(); // Reuse the variable, but note it's a new object! + position.setLowerLeftX(pw-(2*inch)); // 1" in from right, 1" wide + position.setLowerLeftY(ph-(float)(3.5*inch) - inch); // 1" height, 3.5" down + position.setUpperRightX(pw-inch); // 1" in from right + position.setUpperRightY(ph-(float)(3.5*inch)); // 3.5" down + aSquare.setRectangle(position); + + // add to the annotations on the page + annotations.add(aSquare); + + // Now we want to draw a line between the two, one end with an open arrow + + PDAnnotationLine aLine = new PDAnnotationLine(); + + aLine.setEndPointEndingStyle( PDAnnotationLine.LE_OPEN_ARROW ); + aLine.setContents("Circle->Square"); + aLine.setCaption(true); // Make the contents a caption on the line + + // Set the rectangle containing the line + + position = new PDRectangle(); // Reuse the variable, but note it's a new object! + position.setLowerLeftX(2*inch); // 1" in + width of circle + position.setLowerLeftY(ph-(float)(3.5*inch)-inch); // 1" height, 3.5" down + position.setUpperRightX(pw-inch-inch); // 1" in from right, and width of square + position.setUpperRightY(ph-(3*inch)); // 3" down (top of circle) + aLine.setRectangle(position); + + // Now set the line position itself + float[] linepos = new float[4]; + linepos[0] = 2*inch; // x1 = rhs of circle + linepos[1] = ph-(float)(3.5*inch); // y1 halfway down circle + linepos[2] = pw-(2*inch); // x2 = lhs of square + linepos[3] = ph-(4*inch); // y2 halfway down square + aLine.setLine(linepos); + + aLine.setBorderStyle(borderThick); + aLine.setColour(colourBlack); + + // add to the annotations on the page + annotations.add(aLine); + + + // Finally all done + + + document.save( args[0] ); + } + finally + { + document.close(); + } + } + } + + /** + * This will print the usage for this document. + */ + private static void usage() + { + System.err.println( "Usage: java org.apache.pdfbox.examples.pdmodel.Annotation " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBlankPDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBlankPDF.java new file mode 100644 index 000000000..ac9750e66 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBlankPDF.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +/** + * This will create a blank PDF and write the contents to a file. + * + * usage: java org.apache.pdfbox.examples.pdmodel.CreateBlankPDF <outputfile.pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +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; + 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 ); + document.save( file ); + } + finally + { + 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.apache.pdfbox.examples.pdmodel.CreateBlankPDF " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBookmarks.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBookmarks.java new file mode 100644 index 000000000..ea1abaeef --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateBookmarks.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; + +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.apache.pdfbox.examples.pdmodel.CreateBookmarks <input-pdf> <output-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateLandscapePDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateLandscapePDF.java new file mode 100644 index 000000000..8beb2ce37 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/CreateLandscapePDF.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; + + +/** + * This is an example of how to create a page with a landscape orientation. + * @version $Revision: 1.0 $ + */ +public class CreateLandscapePDF +{ + /** + * Constructor. + */ + public CreateLandscapePDF() + { + super(); + } + + /** + * creates a sample document with a landscape orientation and some text surrounded by a box. + * + * @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 message, String outfile ) throws IOException, COSVisitorException + { + // the document + PDDocument doc = null; + try + { + doc = new PDDocument(); + + PDFont font = PDType1Font.HELVETICA; + PDPage page = new PDPage(); + page.setMediaBox(PDPage.PAGE_SIZE_A4); + page.setRotation(90); + doc.addPage(page); + PDRectangle pageSize = page.findMediaBox(); + float pageWidth = pageSize.getWidth(); + float fontSize = 12; + float stringWidth = font.getStringWidth( message )*fontSize/1000f; + float startX = 100; + float startY = 100; + PDPageContentStream contentStream = new PDPageContentStream(doc, page, false, false); + // add the rotation using the current transformation matrix + // including a translation of pageWidth to use the lower left corner as 0,0 reference + contentStream.concatenate2CTM(0, 1, -1, 0, pageWidth, 0); + contentStream.setFont( font, fontSize ); + contentStream.beginText(); + contentStream.moveTextPositionByAmount(startX, startY); + contentStream.drawString( message); + contentStream.moveTextPositionByAmount(0, 100); + contentStream.drawString( message); + contentStream.moveTextPositionByAmount(100, 100); + contentStream.drawString( message); + contentStream.endText(); + + contentStream.drawLine(startX-2, startY-2, startX-2, startY+200+fontSize); + contentStream.drawLine(startX-2, startY+200+fontSize, startX+100+stringWidth+2, startY+200+fontSize); + contentStream.drawLine(startX+100+stringWidth+2, startY+200+fontSize, startX+100+stringWidth+2, startY-2); + contentStream.drawLine(startX+100+stringWidth+2, startY-2, startX-2, startY-2); + contentStream.close(); + + doc.save( outfile ); + } + finally + { + if( doc != null ) + { + doc.close(); + } + } + } + + /** + * This will create a PDF document with a landscape orientation and some text surrounded by a box. + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + CreateLandscapePDF app = new CreateLandscapePDF(); + 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java new file mode 100644 index 000000000..81fb1d7d1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/EmbeddedFiles.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; +import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; +import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; + + +/** + * This is an example that creates a simple document and embeds a file into it.. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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("ISO-8859-1"); + 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. + efTree.setNames( Collections.singletonMap( "My first attachment", fs ) ); + 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. + *
+ * 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ExtractMetadata.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ExtractMetadata.java new file mode 100644 index 000000000..c4b273917 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ExtractMetadata.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.jempbox.xmp.XMPSchemaBasic; +import org.apache.jempbox.xmp.XMPSchemaDublinCore; +import org.apache.jempbox.xmp.XMPSchemaPDF; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDDocumentInformation; +import org.apache.pdfbox.pdmodel.common.PDMetadata; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Iterator; +import java.util.List; + +/** + * This is an example on how to extract metadata from a PDF document. + *

+ * Usage: java org.apache.pdfbox.examples.pdmodel.ExtractDocument <input-pdf> + * + * @version $Revision$ + */ +public class ExtractMetadata +{ + private ExtractMetadata() + { + //utility class + } + + /** + * This is the main method. + * + * @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(); + System.exit(1); + } + else + { + PDDocument document = null; + + try + { + document = PDDocument.load( args[0] ); + if (document.isEncrypted()) + { + try + { + document.decrypt(""); + } + catch( InvalidPasswordException e ) + { + System.err.println( "Error: The document is encrypted." ); + } + catch( org.apache.pdfbox.exceptions.CryptographyException e ) + { + e.printStackTrace(); + } + } + PDDocumentCatalog catalog = document.getDocumentCatalog(); + PDMetadata meta = catalog.getMetadata(); + if ( meta != null) + { + XMPMetadata metadata = meta.exportXMPMetadata(); + + XMPSchemaDublinCore dc = metadata.getDublinCoreSchema(); + if (dc != null) + { + display("Title:", dc.getTitle()); + display("Description:", dc.getDescription()); + list("Creators: ", dc.getCreators()); + list("Dates:", dc.getDates()); + } + + XMPSchemaPDF pdf = metadata.getPDFSchema(); + if (pdf != null) + { + display("Keywords:", pdf.getKeywords()); + display("PDF Version:", pdf.getPDFVersion()); + display("PDF Producer:", pdf.getProducer()); + } + + XMPSchemaBasic basic = metadata.getBasicSchema(); + if (basic != null) + { + display("Create Date:", basic.getCreateDate()); + display("Modify Date:", basic.getModifyDate()); + display("Creator Tool:", basic.getCreatorTool()); + } + } + else + { + // The pdf doesn't contain any metadata, try to use the document information instead + PDDocumentInformation information = document.getDocumentInformation(); + if ( information != null) + { + showDocumentInformation(information); + } + } + + } + finally + { + if( document != null ) + { + document.close(); + } + } + } + } + + private static void showDocumentInformation(PDDocumentInformation information) + { + display("Title:", information.getTitle()); + display("Subject:", information.getSubject()); + display("Author:", information.getAuthor()); + display("Creator:", information.getCreator()); + display("Producer:", information.getProducer()); + } + + private static void list(String title, List list) + { + if (list == null) + { + return; + } + System.out.println(title); + Iterator iter = list.iterator(); + while (iter.hasNext()) + { + Object o = iter.next(); + System.out.println(" " + format(o)); + } + } + + private static String format(Object o) + { + if (o instanceof Calendar) + { + Calendar cal = (Calendar)o; + return DateFormat.getDateInstance().format(cal.getTime()); + } + else + { + return o.toString(); + } + } + + private static void display(String title, Object value) + { + if (value != null) + { + System.out.println(title + " " + format(value)); + } + } + + /** + * This will print the usage for this program. + */ + private static void usage() + { + System.err.println( "Usage: java " + ExtractMetadata.class.getName() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/GoToSecondBookmarkOnOpen.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/GoToSecondBookmarkOnOpen.java new file mode 100644 index 000000000..43eb2b2b7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/GoToSecondBookmarkOnOpen.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionGoTo; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; + +import java.io.IOException; +import java.util.List; + +/** + * This is an example on how to an action to go to the second page when the PDF is opened. + * + * Usage: java org.apache.pdfbox.examples.pdmodel.GoToSecondPageOnOpen <input-pdf> <output-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class GoToSecondBookmarkOnOpen +{ + private GoToSecondBookmarkOnOpen() + { + //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 bookmark destination to encrypted documents." ); + System.exit( 1 ); + } + + List pages = document.getDocumentCatalog().getAllPages(); + if( pages.size() < 2 ) + { + throw new IOException( "Error: The PDF must have at least 2 pages."); + } + PDDocumentOutline bookmarks = document.getDocumentCatalog().getDocumentOutline(); + if( bookmarks == null ) + { + throw new IOException( "Error: The PDF does not contain any bookmarks" ); + } + PDOutlineItem item = bookmarks.getFirstChild().getNextSibling(); + PDDestination dest = item.getDestination(); + PDActionGoTo action = new PDActionGoTo(); + action.setDestination(dest); + document.getDocumentCatalog().setOpenAction(action); + + 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.apache.pdfbox.examples.pdmodel.GoToSecondBookmarkOnOpen" + + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorld.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorld.java new file mode 100644 index 000000000..0806fea6a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorld.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.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 + * @version $Revision: 1.6 $ + */ +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. + *
+ * 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldTTF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldTTF.java new file mode 100644 index 000000000..1eb2bc4c4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldTTF.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont; + +/** + * This is an example that creates a simple document + * with a ttf-font. + * + * @author Michael Niedermair + * @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. + *
+ * 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() + + " "); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java new file mode 100644 index 000000000..88dc834b3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/HelloWorldType1AfmPfb.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1AfmPfbFont; + +/** + * This is an example that creates a simple document + * with a Type 1 font (afm + pfb). + * + * @author Michael Niedermair + * @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. + *
+ * 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() + + " "); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ImageToPDF.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ImageToPDF.java new file mode 100644 index 000000000..f1208a043 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ImageToPDF.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.io.RandomAccessFile; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; + +import org.apache.pdfbox.pdmodel.graphics.xobject.PDCcitt; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg; +import org.apache.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 + * @version $Revision: 1.7 $ + */ +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. + *
+ * 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintBookmarks.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintBookmarks.java new file mode 100644 index 000000000..36450a32c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintBookmarks.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; + +import org.apache.pdfbox.pdfparser.PDFParser; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode; + +import java.io.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.apache.pdfbox.examples.pdmodel.PrintBookmarks <input-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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.apache.pdfbox.examples.pdmodel.PrintBookmarks " ); + } + + /** + * 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(); + } + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintDocumentMetaData.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintDocumentMetaData.java new file mode 100644 index 000000000..d00b93212 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintDocumentMetaData.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; + +import org.apache.pdfbox.pdfparser.PDFParser; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDDocumentInformation; +import org.apache.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.apache.pdfbox.examples.pdmodel.PrintDocumentMetaData <input-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.11 $ + */ +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.apache.pdfbox.examples.pdmodel.PrintDocumentMetaData " ); + } + + /** + * 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintURLs.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintURLs.java new file mode 100644 index 000000000..a80a51c6f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/PrintURLs.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.awt.geom.Rectangle2D; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; +import org.apache.pdfbox.util.PDFTextStripperByArea; + + +/** + * This is an example of how to access a URL in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class PrintURLs +{ + /** + * Constructor. + */ + private PrintURLs() + { + //utility class + } + + /** + * This will create a hello world PDF document. + *
+ * see usage() for commandline + * + * @param args Command line arguments. + * + * @throws Exception If there is an error extracting the URLs. + */ + public static void main(String[] args) throws Exception + { + PDDocument doc = null; + try + { + if( args.length != 1 ) + { + usage(); + } + else + { + doc = PDDocument.load( args[0] ); + List allPages = doc.getDocumentCatalog().getAllPages(); + for( int i=0; i" ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RemoveFirstPage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RemoveFirstPage.java new file mode 100644 index 000000000..bd52782bc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RemoveFirstPage.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import java.io.IOException; + +/** + * This is an example on how to remove pages from a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +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.apache.pdfbox.examples.pdmodel.RemoveFirstPage " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceString.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceString.java new file mode 100644 index 000000000..31de41b98 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceString.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdfwriter.ContentStreamWriter; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.common.PDStream; + +import org.apache.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 + * @version $Revision: 1.3 $ + */ +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 + * 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() + + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceURLs.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceURLs.java new file mode 100644 index 000000000..1bddbab57 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ReplaceURLs.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; + + +/** + * This is an example of how to replace a URL in a PDF document. This + * will only replace the URL that the text refers to and not the text + * itself. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class ReplaceURLs +{ + /** + * Constructor. + */ + private ReplaceURLs() + { + //utility class + } + + /** + * This will read in a document and replace all of the urls with + * http://www.pdfbox.org. + *
+ * see usage() for commandline + * + * @param args Command line arguments. + * + * @throws Exception If there is an error during the process. + */ + public static void main(String[] args) throws Exception + { + PDDocument doc = null; + try + { + if( args.length != 2 ) + { + usage(); + } + else + { + doc = PDDocument.load( args[0] ); + List allPages = doc.getDocumentCatalog().getAllPages(); + for( int i=0; i " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStamp.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStamp.java new file mode 100644 index 000000000..14b2b4043 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStamp.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.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.apache.pdfbox.examples.pdmodel.RubberStamp " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java new file mode 100644 index 000000000..be250a922 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/RubberStampWithImage.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationRubberStamp; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; +import org.apache.pdfbox.util.MapUtil; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * This is an example on how to add a rubber stamp with an image to pages of a PDF document. + * + * @version $Revision: 1.0 $ + */ +public class RubberStampWithImage +{ + + 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 SPACE = " "; + + private static NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US ); + + /** + * Add a rubber stamp with an jpg image to every page of the given document. + * @param args the command line arguments + * @throws IOException an exception is thrown if something went wrong + */ + public void doIt( String[] args ) throws IOException + { + if( args.length != 3 ) + { + 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); + int numberOfPages = allpages.size(); + + for (int i=0; i < numberOfPages; i++) + { + PDPage apage = (PDPage) allpages.get(i); + List annotations = apage.getAnnotations(); + PDAnnotationRubberStamp rubberStamp = new PDAnnotationRubberStamp(); + rubberStamp.setName(PDAnnotationRubberStamp.NAME_TOP_SECRET); + rubberStamp.setRectangle(new PDRectangle(100,100)); + rubberStamp.setContents("A top secret note"); + + // Create a PDXObjectImage with the given jpg + FileInputStream fin = new FileInputStream( args[2] ); + PDJpeg mypic = new PDJpeg(document,fin); + + //Define and set the target rectangle + PDRectangle myrect = new PDRectangle(); + myrect.setUpperRightX(275); + myrect.setUpperRightY(575); + myrect.setLowerLeftX(250); + myrect.setLowerLeftY(550); + + // Create a PDXObjectForm + PDStream formstream = new PDStream(document); + OutputStream os = formstream.createOutputStream(); + PDXObjectForm form = new PDXObjectForm(formstream); + form.setResources(new PDResources()); + form.setBBox(myrect); + form.setFormType(1); + // adjust the image to the target rectangle and add it to the stream + drawXObject(mypic, form.getResources(), os, 250, 550, 25, 25); + os.close(); + + PDAppearanceStream myDic = new PDAppearanceStream(form.getCOSStream()); + PDAppearanceDictionary appearance = new PDAppearanceDictionary(new COSDictionary()); + appearance.setNormalAppearance(myDic); + rubberStamp.setAppearance(appearance); + rubberStamp.setRectangle(myrect); + + //Add the new RubberStamp to the document + annotations.add(rubberStamp); + + } + document.save( args[1] ); + } + catch(COSVisitorException exception) + { + System.err.println("An error occured during saving the document."); + System.err.println("Exception:"+exception); + } + finally + { + if( document != null ) + { + document.close(); + } + } + } + } + + private void drawXObject( PDXObjectImage xobject, PDResources resources, OutputStream os, + float x, float y, float width, float height ) throws IOException + { + // This is similar to PDPageContentStream.drawXObject() + String xObjectPrefix = "Im"; + String objMapping = MapUtil.getNextUniqueKey( resources.getImages(), xObjectPrefix ); + resources.getXObjects().put( objMapping, xobject ); + + appendRawCommands( os, SAVE_GRAPHICS_STATE ); + appendRawCommands( os, formatDecimal.format( width ) ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, formatDecimal.format( 0 ) ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, formatDecimal.format( 0 ) ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, formatDecimal.format( height ) ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, formatDecimal.format( x ) ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, formatDecimal.format( y ) ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, CONCATENATE_MATRIX ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, "/" ); + appendRawCommands( os, objMapping ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, XOBJECT_DO ); + appendRawCommands( os, SPACE ); + appendRawCommands( os, RESTORE_GRAPHICS_STATE ); + } + + private void appendRawCommands(OutputStream os, String commands) throws IOException + { + os.write( commands.getBytes("ISO-8859-1")); + } + + /** + * This creates an instance of RubberStampWithImage. + * + * @param args The command line arguments. + * + * @throws Exception If there is an error parsing the document. + */ + public static void main( String[] args ) throws Exception + { + RubberStampWithImage rubberStamp = new RubberStampWithImage(); + rubberStamp.doIt(args); + } + + /** + * This will print the usage for this example. + */ + private void usage() + { + System.err.println( "Usage: java "+getClass().getName()+" " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java new file mode 100644 index 000000000..4cb8b6261 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/ShowColorBoxes.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.awt.Color; +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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. + *
+ * 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/UsingTextMatrix.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/UsingTextMatrix.java new file mode 100644 index 000000000..e38abeae3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/UsingTextMatrix.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; + + +/** + * This is an example of how to use a text matrix. + * @version $Revision: 1.0 $ + */ +public class UsingTextMatrix +{ + /** + * Constructor. + */ + public UsingTextMatrix() + { + super(); + } + + /** + * creates a sample document with some text using a text matrix. + * + * @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 message, String outfile ) throws IOException, COSVisitorException + { + // the document + PDDocument doc = null; + try + { + doc = new PDDocument(); + + // Page 1 + PDFont font = PDType1Font.HELVETICA; + PDPage page = new PDPage(); + page.setMediaBox(PDPage.PAGE_SIZE_A4); + doc.addPage(page); + float fontSize = 12.0f; + + PDRectangle pageSize = page.findMediaBox(); + float centeredXPosition = (pageSize.getWidth() - fontSize/1000f)/2f; + float stringWidth = font.getStringWidth( message ); + float centeredYPosition = (pageSize.getHeight() - (stringWidth*fontSize)/1000f)/3f; + + PDPageContentStream contentStream = new PDPageContentStream(doc, page, false, false); + contentStream.setFont( font, fontSize ); + contentStream.beginText(); + // counterclockwise rotation + for (int i=0;i<8;i++) + { + contentStream.setTextRotation(i*Math.PI*0.25, centeredXPosition, + pageSize.getHeight()-centeredYPosition); + contentStream.drawString( message + " " + i); + } + // clockwise rotation + for (int i=0;i<8;i++) + { + contentStream.setTextRotation(-i*Math.PI*0.25, centeredXPosition, centeredYPosition); + contentStream.drawString( message + " " + i); + } + + contentStream.endText(); + contentStream.close(); + + // Page 2 + page = new PDPage(); + page.setMediaBox(PDPage.PAGE_SIZE_A4); + doc.addPage(page); + fontSize = 1.0f; + + contentStream = new PDPageContentStream(doc, page, false, false); + contentStream.setFont( font, fontSize ); + contentStream.beginText(); + + // text scaling + for (int i=0;i<10;i++) + { + contentStream.setTextScaling(12+(i*6), 12+(i*6), 100, 100+i*50); + contentStream.drawString( message + " " +i); + } + contentStream.endText(); + contentStream.close(); + + // Page 3 + page = new PDPage(); + page.setMediaBox(PDPage.PAGE_SIZE_A4); + doc.addPage(page); + fontSize = 1.0f; + + contentStream = new PDPageContentStream(doc, page, false, false); + contentStream.setFont( font, fontSize ); + contentStream.beginText(); + + int i = 0; + // text scaling combined with rotation + contentStream.setTextMatrix(12, 0, 0, 12, centeredXPosition, centeredYPosition*1.5); + contentStream.drawString( message + " " +i++); + + contentStream.setTextMatrix(0, 18, -18, 0, centeredXPosition, centeredYPosition*1.5); + contentStream.drawString( message + " " +i++); + + contentStream.setTextMatrix(-24, 0, 0, -24, centeredXPosition, centeredYPosition*1.5); + contentStream.drawString( message + " " +i++); + + contentStream.setTextMatrix(0, -30, 30, 0, centeredXPosition, centeredYPosition*1.5); + contentStream.drawString( message + " " +i++); + + contentStream.endText(); + contentStream.close(); + + doc.save( outfile ); + } + finally + { + if( doc != null ) + { + doc.close(); + } + } + } + + /** + * This will create a PDF document with some examples how to use a text matrix. + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + UsingTextMatrix app = new UsingTextMatrix(); + 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/package.html new file mode 100644 index 000000000..c56dd2cd6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/pdmodel/package.html @@ -0,0 +1,25 @@ + + + + + + + +These examples show how to use the classes in the PDModel package. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/CopyDoc.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/CopyDoc.java new file mode 100644 index 000000000..f686d736e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/CopyDoc.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.persistence; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDocument; + + + +import org.apache.pdfbox.pdfparser.PDFParser; + +import org.apache.pdfbox.pdfwriter.COSWriter; +import org.apache.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. + *
+ * 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() + " " ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/package.html new file mode 100644 index 000000000..35d4e3bdd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/persistence/package.html @@ -0,0 +1,25 @@ + + + + + + + +These examples will show how to use the persistence features of the PDFBox. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/ShowSignature.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/ShowSignature.java new file mode 100644 index 000000000..b8427660c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/ShowSignature.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.signature; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import java.security.cert.CertificateFactory; + +import java.util.Collection; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * This will read a document from the filesystem, decrypt it and do something with the signature. + * + * usage: java org.apache.pdfbox.examples.signature.ShowSignature <password> <inputfile> + * + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +public class ShowSignature +{ + + private 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.ACRO_FORM ); + COSArray fields = (COSArray)acroForm.getDictionaryObject( COSName.FIELDS ); + for( int i=0; i " ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/package.html new file mode 100644 index 000000000..f6fb8cedc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/signature/package.html @@ -0,0 +1,25 @@ + + + + + + + +These examples will show how to gain access to the PDF signature. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/ExtractTextByArea.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/ExtractTextByArea.java new file mode 100644 index 000000000..66348f48f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/ExtractTextByArea.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.util; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.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.apache.pdfbox.examples.util.ExtractTextByArea <input-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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.apache.pdfbox.examples.util.ExtractTextByArea " ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintImageLocations.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintImageLocations.java new file mode 100644 index 000000000..4124382ef --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintImageLocations.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.util; + +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.exceptions.WrappedIOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.PDFStreamEngine; +import org.apache.pdfbox.util.ResourceLoader; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.io.IOException; + +import java.util.List; +import java.util.Map; + +/** + * This is an example on how to get the x/y coordinates of image locations. + * + * Usage: java org.apache.pdfbox.examples.util.PrintImageLocations <input-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class PrintImageLocations extends PDFStreamEngine +{ + /** + * Default constructor. + * + * @throws IOException If there is an error loading text stripper properties. + */ + public PrintImageLocations() throws IOException + { + super( ResourceLoader.loadProperties( + "org/apache/pdfbox/resources/PDFTextStripper.properties", true ) ); + } + + /** + * 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; + 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 ); + } + } + PrintImageLocations printer = new PrintImageLocations(); + List allPages = document.getDocumentCatalog().getAllPages(); + for( int i=0; i" ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintTextLocations.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintTextLocations.java new file mode 100644 index 000000000..da55305e2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/PrintTextLocations.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.util; + +import org.apache.pdfbox.exceptions.InvalidPasswordException; + + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.util.PDFTextStripper; +import org.apache.pdfbox.util.TextPosition; + +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.apache.pdfbox.examples.util.PrintTextLocations <input-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +public class PrintTextLocations extends PDFTextStripper +{ + /** + * Default constructor. + * + * @throws IOException If there is an error loading text stripper properties. + */ + public PrintTextLocations() throws IOException + { + super.setSortByPosition( true ); + } + + /** + * 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; + 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 ); + } + } + PrintTextLocations printer = new PrintTextLocations(); + List allPages = document.getDocumentCatalog().getAllPages(); + for( int i=0; i" ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/RemoveAllText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/RemoveAllText.java new file mode 100644 index 000000000..6fd3da0e7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/RemoveAllText.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.examples.util; + +import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdfwriter.ContentStreamWriter; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.util.PDFOperator; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is an example on how to remove all text from PDF document. + * + * Usage: java org.apache.pdfbox.examples.util.RemoveAllText <input-pdf> <output-pdf> + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class RemoveAllText +{ + /** + * Default constructor. + */ + private RemoveAllText() + { + //example class should not be instantiated + } + + /** + * This will remove all text from a PDF document. + * + * @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: Encrypted documents are not supported for this example." ); + System.exit( 1 ); + } + List allPages = document.getDocumentCatalog().getAllPages(); + for( int i=0; i " ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/package.html new file mode 100644 index 000000000..91429eea1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/examples/util/package.html @@ -0,0 +1,25 @@ + + + + + + + +The packages in this package will show how to use the PDFBox util API. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/COSVisitorException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/COSVisitorException.java new file mode 100644 index 000000000..15a771557 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/COSVisitorException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 ); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/CryptographyException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/CryptographyException.java new file mode 100644 index 000000000..03df8f994 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/CryptographyException.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.exceptions; + +/** + * An exception that indicates that something has gone wrong during a + * cryptography operation. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/InvalidPasswordException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/InvalidPasswordException.java new file mode 100644 index 000000000..e3d8cda78 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/InvalidPasswordException.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.exceptions; + +/** + * An exception that indicates an invalid password was supplied. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class InvalidPasswordException extends Exception +{ + + /** + * Constructor. + * + * @param msg A msg to go with this exception. + */ + public InvalidPasswordException( String msg ) + { + super( msg ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/OutlineNotLocalException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/OutlineNotLocalException.java new file mode 100644 index 000000000..cf299dd70 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/OutlineNotLocalException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.2 $ + */ +public class OutlineNotLocalException extends IOException +{ + + /** + * Constructor. + * + * @param msg An error message. + */ + public OutlineNotLocalException( String msg ) + { + super( msg ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/SignatureException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/SignatureException.java new file mode 100644 index 000000000..17c8f2da6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/SignatureException.java @@ -0,0 +1,95 @@ +package org.apache.pdfbox.exceptions; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * An exception that indicates a problem during the signing process. + * + * @author Thomas Chojecki + * @version $Revision: $ + */ +public class SignatureException extends Exception +{ + + public final static int WRONG_PASSWORD = 1; + + public final static int UNSUPPORTED_OPERATION = 2; + + public final static int CERT_PATH_CHECK_INVALID = 3; + + public final static int NO_SUCH_ALGORITHM = 4; + + public final static int INVALID_PAGE_FOR_SIGNATURE = 5; + + public final static int VISUAL_SIGNATURE_INVALID = 6; + + private int no; + + /** + * Constructor. + * + * @param msg A msg to go with this exception. + */ + public SignatureException( String msg ) + { + super( msg ); + } + + /** + * Constructor. + * + * @param errno A error number to fulfill this exception + * @param msg A msg to go with this exception. + */ + public SignatureException( int errno , String msg ) + { + super( msg ); + no = errno; + } + + /** + * Constructor. + * + * @param e The exception that should be encapsulate. + */ + public SignatureException(Throwable e) + { + super(e); + } + + /** + * Constructor. + * + * @param errno A error number to fulfill this exception + * @param e The exception that should be encapsulate. + */ + public SignatureException( int errno, Throwable e) + { + super(e); + } + + /** + * A error number to fulfill this exception + * + * @return the error number if available, otherwise 0 + */ + public int getErrNo() + { + return no; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedException.java new file mode 100644 index 000000000..e7a581e82 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedException.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.exceptions; + +import java.io.PrintStream; + +/** + * An exception that that holds a sub exception. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 PrintStream to use for output + */ + public void printStackTrace(PrintStream s) + { + super.printStackTrace( s ); + wrapped.printStackTrace( s ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedIOException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedIOException.java new file mode 100644 index 000000000..c8b38d38b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/WrappedIOException.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.exceptions; + +import java.io.IOException; + +/** + * An simple class that allows a sub exception to be stored. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class WrappedIOException extends IOException +{ + /** + * constructor comment. + * + * @param e The root exception that caused this exception. + */ + public WrappedIOException( Throwable e ) + { + initCause( e ); + } + + /** + * constructor comment. + * + * @param message Descriptive text for the exception. + * @param e The root exception that caused this exception. + */ + public WrappedIOException( String message, Throwable e ) + { + super( message ); + initCause( e ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/package.html new file mode 100644 index 000000000..c14ba6ed2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/exceptions/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package is a place holder for exceptions that are used in the PDFBox project. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCII85Filter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCII85Filter.java new file mode 100644 index 000000000..c39cf2552 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCII85Filter.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.pdfbox.io.ASCII85InputStream; +import org.apache.pdfbox.io.ASCII85OutputStream; + +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This is the used for the ASCIIHexDecode filter. + * + * @author Ben Litchfield + * @version $Revision: 1.8 $ + */ +public class ASCII85Filter implements Filter +{ + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + 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(); + } + } + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + 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(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCIIHexFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCIIHexFilter.java new file mode 100644 index 000000000..a5f08494b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/ASCIIHexFilter.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.persistence.util.COSHEXTable; + +/** + * This is the used for the ASCIIHexDecode filter. + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +public class ASCIIHexFilter implements Filter +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(ASCIIHexFilter.class); + /** + * Whitespace. + * 0 0x00 Null (NUL) + * 9 0x09 Tab (HT) + * 10 0x0A Line feed (LF) + * 12 0x0C Form feed (FF) + * 13 0x0D Carriage return (CR) + * 32 0x20 Space (SP) + */ + + private boolean isWhitespace(int c) + { + return c == 0 || c == 9 || c == 10 || c == 12 || c == 13 || c == 32; + } + + private boolean isEOD(int c) + { + return (c == 62); // '>' - EOD + } + + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + int value = 0; + int firstByte = 0; + int secondByte = 0; + while ((firstByte = compressedData.read()) != -1) + { + // always after first char + while(isWhitespace(firstByte)) + { + firstByte = compressedData.read(); + } + if(isEOD(firstByte)) + { + break; + } + + if(REVERSE_HEX[firstByte] == -1) + { + log.error("Invalid Hex Code; int: " + firstByte + " char: " + (char) firstByte); + } + value = REVERSE_HEX[firstByte] * 16; + secondByte = compressedData.read(); + + if(isEOD(secondByte)) + { + // second value behaves like 0 in case of EOD + result.write( value ); + break; + } + if(secondByte >= 0) + { + if(REVERSE_HEX[secondByte] == -1) + { + log.error("Invalid Hex Code; int: " + secondByte + " char: " + (char) secondByte); + } + 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 + }; + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + int byteRead = 0; + while( (byteRead = rawData.read()) != -1 ) + { + int value = (byteRead+256)%256; + result.write( COSHEXTable.TABLE[value] ); + } + result.flush(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/CCITTFaxDecodeFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/CCITTFaxDecodeFilter.java new file mode 100644 index 000000000..2635f13f9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/CCITTFaxDecodeFilter.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is a filter for the CCITTFax Decoder. + * + * @author Ben Litchfield + * @author Marcel Kammer + * @author Paul King + * @version $Revision: 1.13 $ + */ +public class CCITTFaxDecodeFilter implements Filter +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(CCITTFaxDecodeFilter.class); + + /** + * Constructor. + */ + public CCITTFaxDecodeFilter() + { + } + + /** + * {@inheritDoc} + */ + public void decode(InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex) + throws IOException + { + + COSBase decodeP = options.getDictionaryObject(COSName.DECODE_PARMS, COSName.DP); + COSDictionary decodeParms = null; + if (decodeP instanceof COSDictionary) + { + decodeParms = (COSDictionary)decodeP; + } + else if (decodeP instanceof COSArray) + { + decodeParms = (COSDictionary)((COSArray)decodeP).get(0); + } + int length = options.getInt(COSName.LENGTH, -1); + byte[] compressed = null; + if (length != -1) + { + compressed = new byte[length]; + compressedData.read(compressed, 0, length); + } + else + { + // inline images don't provide the length of the stream so that + // we have to read until the end of the stream to find out the length + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + // the streams inline images are stored in are mostly small ones + int tempBufferlength = 512; + byte[] tempBuffer = new byte[tempBufferlength]; + int bytesRead = 0; + while ( (bytesRead = compressedData.read(tempBuffer, 0, tempBufferlength)) != -1) + { + baos.write(tempBuffer, 0, bytesRead); + } + compressed = baos.toByteArray(); + } + int cols = decodeParms.getInt(COSName.COLUMNS, 1728); + int rows = decodeParms.getInt(COSName.ROWS, 0); + int height = options.getInt(COSName.HEIGHT, COSName.H, 0); + if (rows > 0 && height > 0) + { + // ensure that rows doesn't contain implausible data, see PDFBOX-771 + rows = Math.min(rows, height); + } + else + { + // at least one of the values has to have a valid value + rows = Math.max(rows, height); + } + int k = decodeParms.getInt(COSName.K, 0); + int arraySize = (cols + 7) / 8 * rows; + byte[] decompressed = new byte[arraySize]; + TIFFFaxDecoder faxDecoder = new TIFFFaxDecoder(1, cols, rows); + // TODO possible options?? + long tiffOptions = 0; + if (k == 0) + { + faxDecoder.decode1D(decompressed, compressed, 0, rows); + } + else if (k > 0) + { + faxDecoder.decode2D(decompressed, compressed, 0, rows, tiffOptions); + } + else if (k < 0) + { + faxDecoder.decodeT6(decompressed, compressed, 0, rows, tiffOptions); + } + result.write(decompressed); + } + + /** + * {@inheritDoc} + */ + public void encode(InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + log.warn("CCITTFaxDecode.encode is not implemented yet, skipping this stream."); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/CryptFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/CryptFilter.java new file mode 100644 index 000000000..f10ff622c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/CryptFilter.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * + * @author adam.nichols + */ +public class CryptFilter implements Filter +{ + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + COSName encryptionName = (COSName)options.getDictionaryObject(COSName.NAME); + if(encryptionName == null || encryptionName.equals(COSName.IDENTITY)) + { + // currently the only supported implementation is the Identity crypt filter + Filter identityFilter = new IdentityFilter(); + identityFilter.decode(compressedData, result, options, filterIndex); + } + else + { + throw new IOException("Unsupported crypt filter "+encryptionName.getName()); + } + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + COSName encryptionName = (COSName)options.getDictionaryObject(COSName.NAME); + if(encryptionName == null || encryptionName.equals(COSName.IDENTITY)) + { + // currently the only supported implementation is the Identity crypt filter + Filter identityFilter = new IdentityFilter(); + identityFilter.encode(rawData, result, options, filterIndex); + } + else + { + throw new IOException("Unsupported crypt filter "+encryptionName.getName()); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/DCTFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/DCTFilter.java new file mode 100644 index 000000000..875f9861c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/DCTFilter.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This is the used for the DCTDecode filter. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +public class DCTFilter implements Filter +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(DCTFilter.class); + + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + log.warn( "DCTFilter.decode is not implemented yet, skipping this stream." ); + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + log.warn( "DCTFilter.encode is not implemented yet, skipping this stream." ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/Filter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/Filter.java new file mode 100644 index 000000000..e036fb804 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/Filter.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This is the interface that will be used to apply filters to a byte stream. + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +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. + * @param filterIndex The index to the filter being decoded. + * + * @throws IOException If there is an error decompressing the stream. + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + 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. + * @param filterIndex The index to the filter being encoded. + * + * @throws IOException If there is an error compressing the stream. + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException; +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/FilterManager.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/FilterManager.java new file mode 100644 index 000000000..3595940b1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/FilterManager.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.pdfbox.cos.COSName; + +/** + * This will contain manage all the different types of filters that are available. + * + * @author Ben Litchfield + * @version $Revision: 1.13 $ + */ +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(); + Filter cryptFilter = new CryptFilter(); + Filter jpxFilter = new JPXFilter(); + Filter jbig2Filter = new JBIG2Filter(); + + 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 ); + addFilter( COSName.CRYPT, cryptFilter ); + addFilter( COSName.JPX_DECODE, jpxFilter ); + addFilter( COSName.JBIG2_DECODE, jbig2Filter ); + + } + + /** + * 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 ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/FlateFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/FlateFilter.java new file mode 100644 index 000000000..f0e5b8de4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/FlateFilter.java @@ -0,0 +1,354 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.EOFException; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; +import java.util.zip.ZipException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is the used for the FlateDecode filter. + * + * @author Ben Litchfield + * @author Marcel Kammer + * @version $Revision: 1.12 $ + */ +public class FlateFilter implements Filter +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(FlateFilter.class); + + private static final int BUFFER_SIZE = 2048; + + /** + * {@inheritDoc} + */ + public void decode(InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + COSBase baseObj = options.getDictionaryObject(COSName.DECODE_PARMS, COSName.DP); + COSDictionary dict = null; + if( baseObj instanceof COSDictionary ) + { + dict = (COSDictionary)baseObj; + } + else if( baseObj instanceof COSArray ) + { + COSArray paramArray = (COSArray)baseObj; + if( filterIndex < paramArray.size() ) + { + dict = (COSDictionary)paramArray.getObject( filterIndex ); + } + } + else if( baseObj == null ) + { + //do nothing + } + else + { + throw new IOException( "Error: Expected COSArray or COSDictionary and not " + + baseObj.getClass().getName() ); + } + + + 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(COSName.PREDICTOR); + if(predictor > 1) + { + colors = dict.getInt(COSName.COLORS); + bitsPerPixel = options.getInt(COSName.BITS_PER_COMPONENT); + columns = dict.getInt(COSName.COLUMNS); + } + } + + try + { + // Decompress data to temporary ByteArrayOutputStream + decompressor = new InflaterInputStream(compressedData); + int amountRead; + int mayRead = compressedData.available(); + + if (mayRead > 0) + { + byte[] buffer = new byte[Math.min(mayRead,BUFFER_SIZE)]; + + // Decode data using given predictor + if (predictor==-1 || predictor == 1 ) + { + try + { + // decoding not needed + while ((amountRead = decompressor.read(buffer, 0, Math.min(mayRead,BUFFER_SIZE))) != -1) + { + result.write(buffer, 0, amountRead); + } + } + catch (OutOfMemoryError exception) + { + // if the stream is corrupt an OutOfMemoryError may occur + log.error("Stop reading corrupt stream"); + } + catch (ZipException exception) + { + // if the stream is corrupt an OutOfMemoryError may occur + log.error("Stop reading corrupt stream"); + } + catch (EOFException exception) + { + // if the stream is corrupt an OutOfMemoryError may occur + log.error("Stop reading corrupt stream"); + } + } + else + { + /* + * Reverting back to default values + */ + if( colors == -1 ) + { + colors = 1; + } + if( bitsPerPixel == -1 ) + { + bitsPerPixel = 8; + } + if( columns == -1 ) + { + columns = 1; + } + + baos = new ByteArrayOutputStream(); + while ((amountRead = decompressor.read(buffer, 0, Math.min(mayRead,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 = null; + + result.write(decodedData); + } + } + + 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 ) + { + // No prediction + int i = 0; + while ((i = data.read(buffer)) != -1) + { + baos.write(buffer, 0, i); + } + } + else + { + // calculate sizes + int bpp = colors * (bitsPerComponent % 8 +1); + int rowlength = columns * bpp; + byte[] actline = new byte[rowlength]; + // Initialize lastline with Zeros according to PNG-specification + byte[] lastline = new byte[rowlength]; + + boolean done = false; + int linepredictor = predictor; + + while (!done && data.available() > 0) + { + // test for PNG predictor; each value >= 10 (not only 15) indicates usage of PNG predictor + if (predictor >= 10) + { + // PNG predictor; each row starts with predictor type (0, 1, 2, 3, 4) + linepredictor = data.read();// read per line predictor + if (linepredictor == -1) + { + done = true;// reached EOF + break; + } + else + { + linepredictor += 10; // add 10 to tread value 0 as 10, 1 as 11, ... + } + } + + // read line + int i = 0; + int offset = 0; + 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 with bitsPerComponent != 8; + * e.g. for 4 bpc each nibble must be subtracted separately + */ + if ( bitsPerComponent != 8 ) + { + throw new IOException("TIFF-Predictor with " + bitsPerComponent + + " bits per component not supported"); + } + // for 8 bits per component it is the same algorithm as PRED SUB of PNG format + for (int p = 0; p < rowlength; p++) + { + int sub = actline[p] & 0xff; + int left = p - bpp >= 0 ? actline[p - bpp] & 0xff : 0; + actline[p] = (byte) (sub + left); + } + break; + case 10:// PRED NONE + // do nothing + break; + case 11:// PRED SUB + for (int p = 0; p < rowlength; p++) + { + int sub = actline[p]; + int left = p - bpp >= 0 ? actline[p - bpp]: 0; + actline[p] = (byte) (sub + left); + } + break; + case 12:// PRED UP + for (int p = 0; p < rowlength; p++) + { + int up = actline[p] & 0xff; + int prior = lastline[p] & 0xff; + actline[p] = (byte) ((up + prior) & 0xff); + } + break; + case 13:// PRED AVG + for (int p = 0; p < rowlength; p++) + { + int avg = actline[p] & 0xff; + int left = p - bpp >= 0 ? actline[p - bpp] & 0xff: 0; + int up = lastline[p] & 0xff; + actline[p] = (byte) ((avg + (int)Math.floor( (left + up)/2 ) ) & 0xff); + } + break; + case 14:// PRED PAETH + for (int p = 0; p < rowlength; p++) + { + int paeth = actline[p] & 0xff; + int a = p - bpp >= 0 ? actline[p - bpp] & 0xff : 0;// left + int b = lastline[p] & 0xff;// upper + int c = p - bpp >= 0 ? lastline[p - bpp] & 0xff : 0;// 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 + a) & 0xff); + } + else if (absb <= absc) + { + actline[p] = (byte) ((paeth + b) & 0xff); + } + else + { + actline[p] = (byte) ((paeth + c) & 0xff); + } + } + break; + default: + break; + } + lastline = actline.clone(); + baos.write(actline, 0, actline.length); + } + } + return baos.toByteArray(); + } + + /** + * {@inheritDoc} + */ + public void encode(InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + DeflaterOutputStream out = new DeflaterOutputStream(result); + int amountRead = 0; + int mayRead = rawData.available(); + if (mayRead > 0) + { + byte[] buffer = new byte[Math.min(mayRead,BUFFER_SIZE)]; + while ((amountRead = rawData.read(buffer, 0, Math.min(mayRead,BUFFER_SIZE))) != -1) + { + out.write(buffer, 0, amountRead); + } + } + out.close(); + result.flush(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/IdentityFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/IdentityFilter.java new file mode 100644 index 000000000..fcdc2fbb6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/IdentityFilter.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.pdfbox.cos.COSDictionary; + +/** + * The IdentityFilter filter just passes the data through without any modifications. + * This is defined in section 7.6.5 of the PDF 1.7 spec and also stated in table + * 26. + * + * @author adam.nichols + */ +public class IdentityFilter implements Filter +{ + private static final int BUFFER_SIZE = 1024; + + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + byte[] buffer = new byte[BUFFER_SIZE]; + int amountRead = 0; + while( (amountRead = compressedData.read( buffer, 0, BUFFER_SIZE )) != -1 ) + { + result.write( buffer, 0, amountRead ); + } + result.flush(); + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + byte[] buffer = new byte[BUFFER_SIZE]; + int amountRead = 0; + while( (amountRead = rawData.read( buffer, 0, BUFFER_SIZE )) != -1 ) + { + result.write( buffer, 0, amountRead ); + } + result.flush(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/JBIG2Filter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/JBIG2Filter.java new file mode 100644 index 000000000..215a8c7e1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/JBIG2Filter.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; + +/** + * Modeled on the JBIG2Decode filter. + * + * thanks to Timo Böhme + */ + +public class JBIG2Filter implements Filter +{ + + /** Log instance. */ + private static final Log log = LogFactory.getLog(JBIG2Filter.class); + + /** + * Decode JBIG2 data using Java ImageIO library. + * + * {@inheritDoc} + * + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + BufferedImage bi = ImageIO.read(compressedData); + if ( bi != null ) + { + DataBuffer dBuf = bi.getData().getDataBuffer(); + if ( dBuf.getDataType() == DataBuffer.TYPE_BYTE ) + { + result.write( ( ( DataBufferByte ) dBuf ).getData() ); + } + else + { + log.error( "Image data buffer not of type byte but type " + dBuf.getDataType() ); + } + } + else + { + Iterator reader = ImageIO.getImageReadersByFormatName("JBIG2"); + if (!reader.hasNext()) + { + log.error( "Can't find an ImageIO plugin to decode the JBIG2 encoded datastream."); + } + else + { + log.error( "Something went wrong when decoding the JBIG2 encoded datastream."); + } + } + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + System.err.println( "Warning: JBIG2.encode is not implemented yet, skipping this stream." ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/JPXFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/JPXFilter.java new file mode 100644 index 000000000..fe97f33bb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/JPXFilter.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.imageio.ImageIO; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This is used for the JPXDecode filter. + * + * @author Timo Böhme + * + */ +public class JPXFilter implements Filter +{ + + /** Log instance. */ + private static final Log log = LogFactory.getLog(JPXFilter.class); + + /** + * Decode JPEG2000 data using Java ImageIO library. + * + * {@inheritDoc} + * + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + BufferedImage bi = ImageIO.read( compressedData ); + if ( bi != null ) + { + DataBuffer dBuf = bi.getData().getDataBuffer(); + if ( dBuf.getDataType() == DataBuffer.TYPE_BYTE ) + { + result.write( ( ( DataBufferByte ) dBuf ).getData() ); + } + else + { + log.error( "Image data buffer not of type byte but type " + dBuf.getDataType() ); + } + } + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + System.err.println( "Warning: JPXFilter.encode is not implemented yet, skipping this stream." ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWDictionary.java new file mode 100644 index 000000000..cdbbe17b4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWDictionary.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.4 $ + */ +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= 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWFilter.java new file mode 100644 index 000000000..acc57711b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWFilter.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.io.NBitInputStream; +import org.apache.pdfbox.io.NBitOutputStream; + +/** + * This is the used for the LZWDecode filter. + * + * @author Ben Litchfield + * @version $Revision: 1.15 $ + */ +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; + + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + 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(); + } + + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + 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(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWNode.java new file mode 100644 index 000000000..8ea85d294 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/LZWNode.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.filter; + +import java.util.HashMap; +import java.util.Map; + +/** + * This is the used for the LZWDecode filter. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 + * 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. + * + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class RunLengthDecodeFilter implements Filter +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(RunLengthDecodeFilter.class); + + private static final int RUN_LENGTH_EOD = 128; + + /** + * Constructor. + */ + public RunLengthDecodeFilter() + { + //default constructor + } + + /** + * {@inheritDoc} + */ + public void decode( InputStream compressedData, OutputStream result, COSDictionary options, int filterIndex ) + 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 ); + } + } + } + } + + /** + * {@inheritDoc} + */ + public void encode( InputStream rawData, OutputStream result, COSDictionary options, int filterIndex ) + throws IOException + { + log.warn( "RunLengthDecodeFilter.encode is not implemented yet, skipping this stream." ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/TIFFFaxDecoder.java b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/TIFFFaxDecoder.java new file mode 100644 index 000000000..dd7090d37 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/TIFFFaxDecoder.java @@ -0,0 +1,1446 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.pdfbox.filter; + +class TIFFFaxDecoder { + + private int bitPointer, bytePointer; + private byte[] data; + private int w, h; + private int fillOrder; + + // Data structures needed to store changing elements for the previous + // and the current scanline + private int changingElemSize = 0; + private int[] prevChangingElems; + private int[] currChangingElems; + + // Element at which to start search in getNextChangingElement + private int lastChangingElement = 0; + + private int compression = 2; + + // Variables set by T4Options + private int uncompressedMode = 0; + private int fillBits = 0; + private int oneD; + + static int[] table1 = { + 0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN + 0x01, // 1 bits are left in first byte + 0x03, // 2 bits are left in first byte + 0x07, // 3 bits are left in first byte + 0x0f, // 4 bits are left in first byte + 0x1f, // 5 bits are left in first byte + 0x3f, // 6 bits are left in first byte + 0x7f, // 7 bits are left in first byte + 0xff // 8 bits are left in first byte + }; + + static int[] table2 = { + 0x00, // 0 + 0x80, // 1 + 0xc0, // 2 + 0xe0, // 3 + 0xf0, // 4 + 0xf8, // 5 + 0xfc, // 6 + 0xfe, // 7 + 0xff // 8 + }; + + // Table to be used when fillOrder = 2, for flipping bytes. + static byte[] flipTable = { + 0, -128, 64, -64, 32, -96, 96, -32, + 16, -112, 80, -48, 48, -80, 112, -16, + 8, -120, 72, -56, 40, -88, 104, -24, + 24, -104, 88, -40, 56, -72, 120, -8, + 4, -124, 68, -60, 36, -92, 100, -28, + 20, -108, 84, -44, 52, -76, 116, -12, + 12, -116, 76, -52, 44, -84, 108, -20, + 28, -100, 92, -36, 60, -68, 124, -4, + 2, -126, 66, -62, 34, -94, 98, -30, + 18, -110, 82, -46, 50, -78, 114, -14, + 10, -118, 74, -54, 42, -86, 106, -22, + 26, -102, 90, -38, 58, -70, 122, -6, + 6, -122, 70, -58, 38, -90, 102, -26, + 22, -106, 86, -42, 54, -74, 118, -10, + 14, -114, 78, -50, 46, -82, 110, -18, + 30, -98, 94, -34, 62, -66, 126, -2, + 1, -127, 65, -63, 33, -95, 97, -31, + 17, -111, 81, -47, 49, -79, 113, -15, + 9, -119, 73, -55, 41, -87, 105, -23, + 25, -103, 89, -39, 57, -71, 121, -7, + 5, -123, 69, -59, 37, -91, 101, -27, + 21, -107, 85, -43, 53, -75, 117, -11, + 13, -115, 77, -51, 45, -83, 109, -19, + 29, -99, 93, -35, 61, -67, 125, -3, + 3, -125, 67, -61, 35, -93, 99, -29, + 19, -109, 83, -45, 51, -77, 115, -13, + 11, -117, 75, -53, 43, -85, 107, -21, + 27, -101, 91, -37, 59, -69, 123, -5, + 7, -121, 71, -57, 39, -89, 103, -25, + 23, -105, 87, -41, 55, -73, 119, -9, + 15, -113, 79, -49, 47, -81, 111, -17, + 31, -97, 95, -33, 63, -65, 127, -1, + }; + + // The main 10 bit white runs lookup table + static short[] white = { + // 0 - 7 + 6430, 6400, 6400, 6400, 3225, 3225, 3225, 3225, + // 8 - 15 + 944, 944, 944, 944, 976, 976, 976, 976, + // 16 - 23 + 1456, 1456, 1456, 1456, 1488, 1488, 1488, 1488, + // 24 - 31 + 718, 718, 718, 718, 718, 718, 718, 718, + // 32 - 39 + 750, 750, 750, 750, 750, 750, 750, 750, + // 40 - 47 + 1520, 1520, 1520, 1520, 1552, 1552, 1552, 1552, + // 48 - 55 + 428, 428, 428, 428, 428, 428, 428, 428, + // 56 - 63 + 428, 428, 428, 428, 428, 428, 428, 428, + // 64 - 71 + 654, 654, 654, 654, 654, 654, 654, 654, + // 72 - 79 + 1072, 1072, 1072, 1072, 1104, 1104, 1104, 1104, + // 80 - 87 + 1136, 1136, 1136, 1136, 1168, 1168, 1168, 1168, + // 88 - 95 + 1200, 1200, 1200, 1200, 1232, 1232, 1232, 1232, + // 96 - 103 + 622, 622, 622, 622, 622, 622, 622, 622, + // 104 - 111 + 1008, 1008, 1008, 1008, 1040, 1040, 1040, 1040, + // 112 - 119 + 44, 44, 44, 44, 44, 44, 44, 44, + // 120 - 127 + 44, 44, 44, 44, 44, 44, 44, 44, + // 128 - 135 + 396, 396, 396, 396, 396, 396, 396, 396, + // 136 - 143 + 396, 396, 396, 396, 396, 396, 396, 396, + // 144 - 151 + 1712, 1712, 1712, 1712, 1744, 1744, 1744, 1744, + // 152 - 159 + 846, 846, 846, 846, 846, 846, 846, 846, + // 160 - 167 + 1264, 1264, 1264, 1264, 1296, 1296, 1296, 1296, + // 168 - 175 + 1328, 1328, 1328, 1328, 1360, 1360, 1360, 1360, + // 176 - 183 + 1392, 1392, 1392, 1392, 1424, 1424, 1424, 1424, + // 184 - 191 + 686, 686, 686, 686, 686, 686, 686, 686, + // 192 - 199 + 910, 910, 910, 910, 910, 910, 910, 910, + // 200 - 207 + 1968, 1968, 1968, 1968, 2000, 2000, 2000, 2000, + // 208 - 215 + 2032, 2032, 2032, 2032, 16, 16, 16, 16, + // 216 - 223 + 10257, 10257, 10257, 10257, 12305, 12305, 12305, 12305, + // 224 - 231 + 330, 330, 330, 330, 330, 330, 330, 330, + // 232 - 239 + 330, 330, 330, 330, 330, 330, 330, 330, + // 240 - 247 + 330, 330, 330, 330, 330, 330, 330, 330, + // 248 - 255 + 330, 330, 330, 330, 330, 330, 330, 330, + // 256 - 263 + 362, 362, 362, 362, 362, 362, 362, 362, + // 264 - 271 + 362, 362, 362, 362, 362, 362, 362, 362, + // 272 - 279 + 362, 362, 362, 362, 362, 362, 362, 362, + // 280 - 287 + 362, 362, 362, 362, 362, 362, 362, 362, + // 288 - 295 + 878, 878, 878, 878, 878, 878, 878, 878, + // 296 - 303 + 1904, 1904, 1904, 1904, 1936, 1936, 1936, 1936, + // 304 - 311 + -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221, + // 312 - 319 + 590, 590, 590, 590, 590, 590, 590, 590, + // 320 - 327 + 782, 782, 782, 782, 782, 782, 782, 782, + // 328 - 335 + 1584, 1584, 1584, 1584, 1616, 1616, 1616, 1616, + // 336 - 343 + 1648, 1648, 1648, 1648, 1680, 1680, 1680, 1680, + // 344 - 351 + 814, 814, 814, 814, 814, 814, 814, 814, + // 352 - 359 + 1776, 1776, 1776, 1776, 1808, 1808, 1808, 1808, + // 360 - 367 + 1840, 1840, 1840, 1840, 1872, 1872, 1872, 1872, + // 368 - 375 + 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157, + // 376 - 383 + 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157, + // 384 - 391 + -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275, + // 392 - 399 + -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275, + // 400 - 407 + 14353, 14353, 14353, 14353, 16401, 16401, 16401, 16401, + // 408 - 415 + 22547, 22547, 24595, 24595, 20497, 20497, 20497, 20497, + // 416 - 423 + 18449, 18449, 18449, 18449, 26643, 26643, 28691, 28691, + // 424 - 431 + 30739, 30739, -32749, -32749, -30701, -30701, -28653, -28653, + // 432 - 439 + -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461, + // 440 - 447 + 8207, 8207, 8207, 8207, 8207, 8207, 8207, 8207, + // 448 - 455 + 72, 72, 72, 72, 72, 72, 72, 72, + // 456 - 463 + 72, 72, 72, 72, 72, 72, 72, 72, + // 464 - 471 + 72, 72, 72, 72, 72, 72, 72, 72, + // 472 - 479 + 72, 72, 72, 72, 72, 72, 72, 72, + // 480 - 487 + 72, 72, 72, 72, 72, 72, 72, 72, + // 488 - 495 + 72, 72, 72, 72, 72, 72, 72, 72, + // 496 - 503 + 72, 72, 72, 72, 72, 72, 72, 72, + // 504 - 511 + 72, 72, 72, 72, 72, 72, 72, 72, + // 512 - 519 + 104, 104, 104, 104, 104, 104, 104, 104, + // 520 - 527 + 104, 104, 104, 104, 104, 104, 104, 104, + // 528 - 535 + 104, 104, 104, 104, 104, 104, 104, 104, + // 536 - 543 + 104, 104, 104, 104, 104, 104, 104, 104, + // 544 - 551 + 104, 104, 104, 104, 104, 104, 104, 104, + // 552 - 559 + 104, 104, 104, 104, 104, 104, 104, 104, + // 560 - 567 + 104, 104, 104, 104, 104, 104, 104, 104, + // 568 - 575 + 104, 104, 104, 104, 104, 104, 104, 104, + // 576 - 583 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 584 - 591 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 592 - 599 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 600 - 607 + 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, + // 608 - 615 + 266, 266, 266, 266, 266, 266, 266, 266, + // 616 - 623 + 266, 266, 266, 266, 266, 266, 266, 266, + // 624 - 631 + 266, 266, 266, 266, 266, 266, 266, 266, + // 632 - 639 + 266, 266, 266, 266, 266, 266, 266, 266, + // 640 - 647 + 298, 298, 298, 298, 298, 298, 298, 298, + // 648 - 655 + 298, 298, 298, 298, 298, 298, 298, 298, + // 656 - 663 + 298, 298, 298, 298, 298, 298, 298, 298, + // 664 - 671 + 298, 298, 298, 298, 298, 298, 298, 298, + // 672 - 679 + 524, 524, 524, 524, 524, 524, 524, 524, + // 680 - 687 + 524, 524, 524, 524, 524, 524, 524, 524, + // 688 - 695 + 556, 556, 556, 556, 556, 556, 556, 556, + // 696 - 703 + 556, 556, 556, 556, 556, 556, 556, 556, + // 704 - 711 + 136, 136, 136, 136, 136, 136, 136, 136, + // 712 - 719 + 136, 136, 136, 136, 136, 136, 136, 136, + // 720 - 727 + 136, 136, 136, 136, 136, 136, 136, 136, + // 728 - 735 + 136, 136, 136, 136, 136, 136, 136, 136, + // 736 - 743 + 136, 136, 136, 136, 136, 136, 136, 136, + // 744 - 751 + 136, 136, 136, 136, 136, 136, 136, 136, + // 752 - 759 + 136, 136, 136, 136, 136, 136, 136, 136, + // 760 - 767 + 136, 136, 136, 136, 136, 136, 136, 136, + // 768 - 775 + 168, 168, 168, 168, 168, 168, 168, 168, + // 776 - 783 + 168, 168, 168, 168, 168, 168, 168, 168, + // 784 - 791 + 168, 168, 168, 168, 168, 168, 168, 168, + // 792 - 799 + 168, 168, 168, 168, 168, 168, 168, 168, + // 800 - 807 + 168, 168, 168, 168, 168, 168, 168, 168, + // 808 - 815 + 168, 168, 168, 168, 168, 168, 168, 168, + // 816 - 823 + 168, 168, 168, 168, 168, 168, 168, 168, + // 824 - 831 + 168, 168, 168, 168, 168, 168, 168, 168, + // 832 - 839 + 460, 460, 460, 460, 460, 460, 460, 460, + // 840 - 847 + 460, 460, 460, 460, 460, 460, 460, 460, + // 848 - 855 + 492, 492, 492, 492, 492, 492, 492, 492, + // 856 - 863 + 492, 492, 492, 492, 492, 492, 492, 492, + // 864 - 871 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 872 - 879 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 880 - 887 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 888 - 895 + 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, + // 896 - 903 + 200, 200, 200, 200, 200, 200, 200, 200, + // 904 - 911 + 200, 200, 200, 200, 200, 200, 200, 200, + // 912 - 919 + 200, 200, 200, 200, 200, 200, 200, 200, + // 920 - 927 + 200, 200, 200, 200, 200, 200, 200, 200, + // 928 - 935 + 200, 200, 200, 200, 200, 200, 200, 200, + // 936 - 943 + 200, 200, 200, 200, 200, 200, 200, 200, + // 944 - 951 + 200, 200, 200, 200, 200, 200, 200, 200, + // 952 - 959 + 200, 200, 200, 200, 200, 200, 200, 200, + // 960 - 967 + 232, 232, 232, 232, 232, 232, 232, 232, + // 968 - 975 + 232, 232, 232, 232, 232, 232, 232, 232, + // 976 - 983 + 232, 232, 232, 232, 232, 232, 232, 232, + // 984 - 991 + 232, 232, 232, 232, 232, 232, 232, 232, + // 992 - 999 + 232, 232, 232, 232, 232, 232, 232, 232, + // 1000 - 1007 + 232, 232, 232, 232, 232, 232, 232, 232, + // 1008 - 1015 + 232, 232, 232, 232, 232, 232, 232, 232, + // 1016 - 1023 + 232, 232, 232, 232, 232, 232, 232, 232, + }; + + // Additional make up codes for both White and Black runs + static short[] additionalMakeup = { + 28679, 28679, 31752, (short)32777, + (short)33801, (short)34825, (short)35849, (short)36873, + (short)29703, (short)29703, (short)30727, (short)30727, + (short)37897, (short)38921, (short)39945, (short)40969 + }; + + // Initial black run look up table, uses the first 4 bits of a code + static short[] initBlack = { + // 0 - 7 + 3226, 6412, 200, 168, 38, 38, 134, 134, + // 8 - 15 + 100, 100, 100, 100, 68, 68, 68, 68 + }; + + // + static short[] twoBitBlack = {292, 260, 226, 226}; // 0 - 3 + + // Main black run table, using the last 9 bits of possible 13 bit code + static short[] black = { + // 0 - 7 + 62, 62, 30, 30, 0, 0, 0, 0, + // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, + // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, + // 24 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, + // 32 - 39 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 40 - 47 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 48 - 55 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 56 - 63 + 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, + // 64 - 71 + 588, 588, 588, 588, 588, 588, 588, 588, + // 72 - 79 + 1680, 1680, 20499, 22547, 24595, 26643, 1776, 1776, + // 80 - 87 + 1808, 1808, -24557, -22509, -20461, -18413, 1904, 1904, + // 88 - 95 + 1936, 1936, -16365, -14317, 782, 782, 782, 782, + // 96 - 103 + 814, 814, 814, 814, -12269, -10221, 10257, 10257, + // 104 - 111 + 12305, 12305, 14353, 14353, 16403, 18451, 1712, 1712, + // 112 - 119 + 1744, 1744, 28691, 30739, -32749, -30701, -28653, -26605, + // 120 - 127 + 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, + // 128 - 135 + 424, 424, 424, 424, 424, 424, 424, 424, + // 136 - 143 + 424, 424, 424, 424, 424, 424, 424, 424, + // 144 - 151 + 424, 424, 424, 424, 424, 424, 424, 424, + // 152 - 159 + 424, 424, 424, 424, 424, 424, 424, 424, + // 160 - 167 + 750, 750, 750, 750, 1616, 1616, 1648, 1648, + // 168 - 175 + 1424, 1424, 1456, 1456, 1488, 1488, 1520, 1520, + // 176 - 183 + 1840, 1840, 1872, 1872, 1968, 1968, 8209, 8209, + // 184 - 191 + 524, 524, 524, 524, 524, 524, 524, 524, + // 192 - 199 + 556, 556, 556, 556, 556, 556, 556, 556, + // 200 - 207 + 1552, 1552, 1584, 1584, 2000, 2000, 2032, 2032, + // 208 - 215 + 976, 976, 1008, 1008, 1040, 1040, 1072, 1072, + // 216 - 223 + 1296, 1296, 1328, 1328, 718, 718, 718, 718, + // 224 - 231 + 456, 456, 456, 456, 456, 456, 456, 456, + // 232 - 239 + 456, 456, 456, 456, 456, 456, 456, 456, + // 240 - 247 + 456, 456, 456, 456, 456, 456, 456, 456, + // 248 - 255 + 456, 456, 456, 456, 456, 456, 456, 456, + // 256 - 263 + 326, 326, 326, 326, 326, 326, 326, 326, + // 264 - 271 + 326, 326, 326, 326, 326, 326, 326, 326, + // 272 - 279 + 326, 326, 326, 326, 326, 326, 326, 326, + // 280 - 287 + 326, 326, 326, 326, 326, 326, 326, 326, + // 288 - 295 + 326, 326, 326, 326, 326, 326, 326, 326, + // 296 - 303 + 326, 326, 326, 326, 326, 326, 326, 326, + // 304 - 311 + 326, 326, 326, 326, 326, 326, 326, 326, + // 312 - 319 + 326, 326, 326, 326, 326, 326, 326, 326, + // 320 - 327 + 358, 358, 358, 358, 358, 358, 358, 358, + // 328 - 335 + 358, 358, 358, 358, 358, 358, 358, 358, + // 336 - 343 + 358, 358, 358, 358, 358, 358, 358, 358, + // 344 - 351 + 358, 358, 358, 358, 358, 358, 358, 358, + // 352 - 359 + 358, 358, 358, 358, 358, 358, 358, 358, + // 360 - 367 + 358, 358, 358, 358, 358, 358, 358, 358, + // 368 - 375 + 358, 358, 358, 358, 358, 358, 358, 358, + // 376 - 383 + 358, 358, 358, 358, 358, 358, 358, 358, + // 384 - 391 + 490, 490, 490, 490, 490, 490, 490, 490, + // 392 - 399 + 490, 490, 490, 490, 490, 490, 490, 490, + // 400 - 407 + 4113, 4113, 6161, 6161, 848, 848, 880, 880, + // 408 - 415 + 912, 912, 944, 944, 622, 622, 622, 622, + // 416 - 423 + 654, 654, 654, 654, 1104, 1104, 1136, 1136, + // 424 - 431 + 1168, 1168, 1200, 1200, 1232, 1232, 1264, 1264, + // 432 - 439 + 686, 686, 686, 686, 1360, 1360, 1392, 1392, + // 440 - 447 + 12, 12, 12, 12, 12, 12, 12, 12, + // 448 - 455 + 390, 390, 390, 390, 390, 390, 390, 390, + // 456 - 463 + 390, 390, 390, 390, 390, 390, 390, 390, + // 464 - 471 + 390, 390, 390, 390, 390, 390, 390, 390, + // 472 - 479 + 390, 390, 390, 390, 390, 390, 390, 390, + // 480 - 487 + 390, 390, 390, 390, 390, 390, 390, 390, + // 488 - 495 + 390, 390, 390, 390, 390, 390, 390, 390, + // 496 - 503 + 390, 390, 390, 390, 390, 390, 390, 390, + // 504 - 511 + 390, 390, 390, 390, 390, 390, 390, 390, + }; + + static byte[] twoDCodes = { + // 0 - 7 + 80, 88, 23, 71, 30, 30, 62, 62, + // 8 - 15 + 4, 4, 4, 4, 4, 4, 4, 4, + // 16 - 23 + 11, 11, 11, 11, 11, 11, 11, 11, + // 24 - 31 + 11, 11, 11, 11, 11, 11, 11, 11, + // 32 - 39 + 35, 35, 35, 35, 35, 35, 35, 35, + // 40 - 47 + 35, 35, 35, 35, 35, 35, 35, 35, + // 48 - 55 + 51, 51, 51, 51, 51, 51, 51, 51, + // 56 - 63 + 51, 51, 51, 51, 51, 51, 51, 51, + // 64 - 71 + 41, 41, 41, 41, 41, 41, 41, 41, + // 72 - 79 + 41, 41, 41, 41, 41, 41, 41, 41, + // 80 - 87 + 41, 41, 41, 41, 41, 41, 41, 41, + // 88 - 95 + 41, 41, 41, 41, 41, 41, 41, 41, + // 96 - 103 + 41, 41, 41, 41, 41, 41, 41, 41, + // 104 - 111 + 41, 41, 41, 41, 41, 41, 41, 41, + // 112 - 119 + 41, 41, 41, 41, 41, 41, 41, 41, + // 120 - 127 + 41, 41, 41, 41, 41, 41, 41, 41, + }; + + /** + * @param fillOrder The fill order of the compressed data bytes. + * @param w The width of the image in pixels + * @param h The height of the image in pixels + */ + public TIFFFaxDecoder(int fillOrder, int w, int h) { + this.fillOrder = fillOrder; + this.w = w; + this.h = h; + + this.bitPointer = 0; + this.bytePointer = 0; + this.prevChangingElems = new int[w]; + this.currChangingElems = new int[w]; + } + + // One-dimensional decoding methods + + public void decode1D(byte[] buffer, byte[] compData, + int startX, int height) { + this.data = compData; + + int lineOffset = 0; + int scanlineStride = (w + 7)/8; + + bitPointer = 0; + bytePointer = 0; + + for (int i = 0; i < height; i++) { + decodeNextScanline(buffer, lineOffset, startX); + lineOffset += scanlineStride; + } + } + + public void decodeNextScanline(byte[] buffer, + int lineOffset, int bitOffset) { + int bits = 0, code = 0, isT = 0; + int current, entry, twoBits; + boolean isWhite = true; + + // Initialize starting of the changing elements array + changingElemSize = 0; + + // While scanline not complete + while (bitOffset < w) { + while (isWhite) { + // White run + current = nextNBits(10); + entry = white[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x0f; + + if (bits == 12) { // Additional Make up code + // Get the next 2 bits + twoBits = nextLesserThan8Bits(2); + // Consolidate the 2 new bits and last 2 bits into 4 bits + current = ((current << 2) & 0x000c) | twoBits; + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + bitOffset += code; // Skip white run + + updatePointer(4 - bits); + } else if (bits == 0) { // ERROR + throw new Error("TIFFFaxDecoder0"); + } else if (bits == 15) { // EOL + throw new Error("TIFFFaxDecoder1"); + } else { + // 11 bits - 0000 0111 1111 1111 = 0x07ff + code = (entry >>> 5) & 0x07ff; + bitOffset += code; + + updatePointer(10 - bits); + if (isT == 0) { + isWhite = false; + currChangingElems[changingElemSize++] = bitOffset; + } + } + } + + // Check whether this run completed one width, if so + // advance to next byte boundary for compression = 2. + if (bitOffset == w) { + if (compression == 2) { + advancePointer(); + } + break; + } + + while (!isWhite) { + // Black run + current = nextLesserThan8Bits(4); + entry = initBlack[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (code == 100) { + current = nextNBits(9); + entry = black[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (bits == 12) { + // Additional makeup codes + updatePointer(5); + current = nextLesserThan8Bits(4); + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(4 - bits); + } else if (bits == 15) { + // EOL code + throw new Error("TIFFFaxDecoder2"); + } else { + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(9 - bits); + if (isT == 0) { + isWhite = true; + currChangingElems[changingElemSize++] = bitOffset; + } + } + } else if (code == 200) { + // Is a Terminating code + current = nextLesserThan8Bits(2); + entry = twoBitBlack[current]; + code = (entry >>> 5) & 0x07ff; + bits = (entry >>> 1) & 0x0f; + + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(2 - bits); + isWhite = true; + currChangingElems[changingElemSize++] = bitOffset; + } else { + // Is a Terminating code + setToBlack(buffer, lineOffset, bitOffset, code); + bitOffset += code; + + updatePointer(4 - bits); + isWhite = true; + currChangingElems[changingElemSize++] = bitOffset; + } + } + + // Check whether this run completed one width + if (bitOffset == w) { + if (compression == 2) { + advancePointer(); + } + break; + } + } + + currChangingElems[changingElemSize++] = bitOffset; + } + + // Two-dimensional decoding methods + + public void decode2D(byte[] buffer, + byte[] compData, + int startX, + int height, + long tiffT4Options) { + this.data = compData; + compression = 3; + + bitPointer = 0; + bytePointer = 0; + + int scanlineStride = (w + 7)/8; + + int a0, a1, b1, b2; + int[] b = new int[2]; + int entry, code, bits; + boolean isWhite; + int currIndex = 0; + int[] temp; + + // fillBits - dealt with this in readEOL + // 1D/2D encoding - dealt with this in readEOL + + // uncompressedMode - haven't dealt with this yet. + + + oneD = (int)(tiffT4Options & 0x01); + uncompressedMode = (int)((tiffT4Options & 0x02) >> 1); + fillBits = (int)((tiffT4Options & 0x04) >> 2); + + // The data must start with an EOL code + if (readEOL() != 1) { + throw new Error("TIFFFaxDecoder3"); + } + + int lineOffset = 0; + int bitOffset; + + // Then the 1D encoded scanline data will occur, changing elements + // array gets set. + decodeNextScanline(buffer, lineOffset, startX); + lineOffset += scanlineStride; + + for (int lines = 1; lines < height; lines++) { + + // Every line must begin with an EOL followed by a bit which + // indicates whether the following scanline is 1D or 2D encoded. + if (readEOL() == 0) { + // 2D encoded scanline follows + + // Initialize previous scanlines changing elements, and + // initialize current scanline's changing elements array + temp = prevChangingElems; + prevChangingElems = currChangingElems; + currChangingElems = temp; + currIndex = 0; + + // a0 has to be set just before the start of this scanline. + a0 = -1; + isWhite = true; + bitOffset = startX; + + lastChangingElement = 0; + + while (bitOffset < w) { + // Get the next changing element + getNextChangingElement(a0, isWhite, b); + + b1 = b[0]; + b2 = b[1]; + + // Get the next seven bits + entry = nextLesserThan8Bits(7); + + // Run these through the 2DCodes table + entry = (int)(twoDCodes[entry] & 0xff); + + // Get the code and the number of bits used up + code = (entry & 0x78) >>> 3; + bits = entry & 0x07; + + if (code == 0) { + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + b2 - bitOffset); + } + bitOffset = a0 = b2; + + // Set pointer to consume the correct number of bits. + updatePointer(7 - bits); + } else if (code == 1) { + // Horizontal + updatePointer(7 - bits); + + // identify the next 2 codes. + int number; + if (isWhite) { + number = decodeWhiteCodeWord(); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + } else { + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + + number = decodeWhiteCodeWord(); + bitOffset += number; + currChangingElems[currIndex++] = bitOffset; + } + + a0 = bitOffset; + } else if (code <= 8) { + // Vertical + a1 = b1 + (code - 5); + + currChangingElems[currIndex++] = a1; + + // We write the current color till a1 - 1 pos, + // since a1 is where the next color starts + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + a1 - bitOffset); + } + bitOffset = a0 = a1; + isWhite = !isWhite; + + updatePointer(7 - bits); + } else { + throw new Error("TIFFFaxDecoder4"); + } + } + + // Add the changing element beyond the current scanline for the + // other color too + currChangingElems[currIndex++] = bitOffset; + changingElemSize = currIndex; + } else { + // 1D encoded scanline follows + decodeNextScanline(buffer, lineOffset, startX); + } + + lineOffset += scanlineStride; + } + } + + public synchronized void decodeT6(byte[] buffer, + byte[] compData, + int startX, + int height, + long tiffT6Options) { + this.data = compData; + compression = 4; + + bitPointer = 0; + bytePointer = 0; + + int scanlineStride = (w + 7)/8; + + int a0, a1, b1, b2; + int entry, code, bits; + boolean isWhite; + int currIndex; + int[] temp; + + // Return values from getNextChangingElement + int[] b = new int[2]; + + // uncompressedMode - have written some code for this, but this + // has not been tested due to lack of test images using this optional + + uncompressedMode = (int)((tiffT6Options & 0x02) >> 1); + + // Local cached reference + int[] cce = currChangingElems; + + // Assume invisible preceding row of all white pixels and insert + // both black and white changing elements beyond the end of this + // imaginary scanline. + changingElemSize = 0; + cce[changingElemSize++] = w; + cce[changingElemSize++] = w; + + int lineOffset = 0; + int bitOffset; + + for (int lines = 0; lines < height; lines++) { + // a0 has to be set just before the start of the scanline. + a0 = -1; + isWhite = true; + + // Assign the changing elements of the previous scanline to + // prevChangingElems and start putting this new scanline's + // changing elements into the currChangingElems. + temp = prevChangingElems; + prevChangingElems = currChangingElems; + cce = currChangingElems = temp; + currIndex = 0; + + // Start decoding the scanline at startX in the raster + bitOffset = startX; + + // Reset search start position for getNextChangingElement + lastChangingElement = 0; + + // Till one whole scanline is decoded + while (bitOffset < w) { + // Get the next changing element + getNextChangingElement(a0, isWhite, b); + b1 = b[0]; + b2 = b[1]; + + // Get the next seven bits + entry = nextLesserThan8Bits(7); + // Run these through the 2DCodes table + entry = (int)(twoDCodes[entry] & 0xff); + + // Get the code and the number of bits used up + code = (entry & 0x78) >>> 3; + bits = entry & 0x07; + + if (code == 0) { // Pass + // We always assume WhiteIsZero format for fax. + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + b2 - bitOffset); + } + bitOffset = a0 = b2; + + // Set pointer to only consume the correct number of bits. + updatePointer(7 - bits); + } else if (code == 1) { // Horizontal + // Set pointer to only consume the correct number of bits. + updatePointer(7 - bits); + + // identify the next 2 alternating color codes. + int number; + if (isWhite) { + // Following are white and black runs + number = decodeWhiteCodeWord(); + bitOffset += number; + cce[currIndex++] = bitOffset; + + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + cce[currIndex++] = bitOffset; + } else { + // First a black run and then a white run follows + number = decodeBlackCodeWord(); + setToBlack(buffer, lineOffset, bitOffset, number); + bitOffset += number; + cce[currIndex++] = bitOffset; + + number = decodeWhiteCodeWord(); + bitOffset += number; + cce[currIndex++] = bitOffset; + } + + a0 = bitOffset; + } else if (code <= 8) { // Vertical + a1 = b1 + (code - 5); + cce[currIndex++] = a1; + + // We write the current color till a1 - 1 pos, + // since a1 is where the next color starts + if (!isWhite) { + setToBlack(buffer, lineOffset, bitOffset, + a1 - bitOffset); + } + bitOffset = a0 = a1; + isWhite = !isWhite; + + updatePointer(7 - bits); + } else if (code == 11) { + if (nextLesserThan8Bits(3) != 7) { + throw new Error("TIFFFaxDecoder5"); + } + + int zeros = 0; + boolean exit = false; + + while (!exit) { + while (nextLesserThan8Bits(1) != 1) { + zeros++; + } + + if (zeros > 5) { + // Exit code + + // Zeros before exit code + zeros = zeros - 6; + + if (!isWhite && (zeros > 0)) { + cce[currIndex++] = bitOffset; + } + + // Zeros before the exit code + bitOffset += zeros; + if (zeros > 0) { + // Some zeros have been written + isWhite = true; + } + + // Read in the bit which specifies the color of + // the following run + if (nextLesserThan8Bits(1) == 0) { + if (!isWhite) { + cce[currIndex++] = bitOffset; + } + isWhite = true; + } else { + if (isWhite) { + cce[currIndex++] = bitOffset; + } + isWhite = false; + } + + exit = true; + } + + if (zeros == 5) { + if (!isWhite) { + cce[currIndex++] = bitOffset; + } + bitOffset += zeros; + + // Last thing written was white + isWhite = true; + } else { + bitOffset += zeros; + + cce[currIndex++] = bitOffset; + setToBlack(buffer, lineOffset, bitOffset, 1); + ++bitOffset; + + // Last thing written was black + isWhite = false; + } + + } + } else { + throw new Error("TIFFFaxDecoder5"); + } + } + + // Add the changing element beyond the current scanline for the + // other color too + cce[currIndex++] = bitOffset; + + // Number of changing elements in this scanline. + changingElemSize = currIndex; + + lineOffset += scanlineStride; + } + } + + private void setToBlack(byte[] buffer, + int lineOffset, int bitOffset, + int numBits) { + int bitNum = 8*lineOffset + bitOffset; + int lastBit = bitNum + numBits; + + int byteNum = bitNum >> 3; + + // Handle bits in first byte + int shift = bitNum & 0x7; + if (shift > 0) { + int maskVal = 1 << (7 - shift); + byte val = buffer[byteNum]; + while (maskVal > 0 && bitNum < lastBit) { + val |= maskVal; + maskVal >>= 1; + ++bitNum; + } + buffer[byteNum] = val; + } + + // Fill in 8 bits at a time + byteNum = bitNum >> 3; + while (bitNum < lastBit - 7) { + buffer[byteNum++] = (byte)255; + bitNum += 8; + } + + // Fill in remaining bits + while (bitNum < lastBit) { + byteNum = bitNum >> 3; + buffer[byteNum] |= 1 << (7 - (bitNum & 0x7)); + ++bitNum; + } + } + + // Returns run length + private int decodeWhiteCodeWord() { + int current, entry, bits, isT, twoBits, code = -1; + int runLength = 0; + boolean isWhite = true; + + while (isWhite) { + current = nextNBits(10); + entry = white[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x0f; + + if (bits == 12) { // Additional Make up code + // Get the next 2 bits + twoBits = nextLesserThan8Bits(2); + // Consolidate the 2 new bits and last 2 bits into 4 bits + current = ((current << 2) & 0x000c) | twoBits; + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + runLength += code; + updatePointer(4 - bits); + } else if (bits == 0) { // ERROR + throw new Error("TIFFFaxDecoder0"); + } else if (bits == 15) { // EOL + throw new Error("TIFFFaxDecoder1"); + } else { + // 11 bits - 0000 0111 1111 1111 = 0x07ff + code = (entry >>> 5) & 0x07ff; + runLength += code; + updatePointer(10 - bits); + if (isT == 0) { + isWhite = false; + } + } + } + + return runLength; + } + + // Returns run length + private int decodeBlackCodeWord() { + int current, entry, bits, isT, code = -1; + int runLength = 0; + boolean isWhite = false; + + while (!isWhite) { + current = nextLesserThan8Bits(4); + entry = initBlack[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (code == 100) { + current = nextNBits(9); + entry = black[current]; + + // Get the 3 fields from the entry + isT = entry & 0x0001; + bits = (entry >>> 1) & 0x000f; + code = (entry >>> 5) & 0x07ff; + + if (bits == 12) { + // Additional makeup codes + updatePointer(5); + current = nextLesserThan8Bits(4); + entry = additionalMakeup[current]; + bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 + code = (entry >>> 4) & 0x0fff; // 12 bits + runLength += code; + + updatePointer(4 - bits); + } else if (bits == 15) { + // EOL code + throw new Error("TIFFFaxDecoder2"); + } else { + runLength += code; + updatePointer(9 - bits); + if (isT == 0) { + isWhite = true; + } + } + } else if (code == 200) { + // Is a Terminating code + current = nextLesserThan8Bits(2); + entry = twoBitBlack[current]; + code = (entry >>> 5) & 0x07ff; + runLength += code; + bits = (entry >>> 1) & 0x0f; + updatePointer(2 - bits); + isWhite = true; + } else { + // Is a Terminating code + runLength += code; + updatePointer(4 - bits); + isWhite = true; + } + } + + return runLength; + } + + private int readEOL() { + if (fillBits == 0) { + if (nextNBits(12) != 1) { + throw new Error("TIFFFaxDecoder6"); + } + } else if (fillBits == 1) { + + // First EOL code word xxxx 0000 0000 0001 will occur + // As many fill bits will be present as required to make + // the EOL code of 12 bits end on a byte boundary. + + int bitsLeft = 8 - bitPointer; + + if (nextNBits(bitsLeft) != 0) { + throw new Error("TIFFFaxDecoder8"); + } + + // If the number of bitsLeft is less than 8, then to have a 12 + // bit EOL sequence, two more bytes are certainly going to be + // required. The first of them has to be all zeros, so ensure + // that. + if (bitsLeft < 4) { + if (nextNBits(8) != 0) { + throw new Error("TIFFFaxDecoder8"); + } + } + + // There might be a random number of fill bytes with 0s, so + // loop till the EOL of 0000 0001 is found, as long as all + // the bytes preceding it are 0's. + int n; + while ((n = nextNBits(8)) != 1) { + + // If not all zeros + if (n != 0) { + throw new Error("TIFFFaxDecoder8"); + } + } + } + + // If one dimensional encoding mode, then always return 1 + if (oneD == 0) { + return 1; + } else { + // Otherwise for 2D encoding mode, + // The next one bit signifies 1D/2D encoding of next line. + return nextLesserThan8Bits(1); + } + } + + private void getNextChangingElement(int a0, boolean isWhite, int[] ret) { + // Local copies of instance variables + int[] pce = this.prevChangingElems; + int ces = this.changingElemSize; + + // If the previous match was at an odd element, we still + // have to search the preceeding element. + // int start = lastChangingElement & ~0x1; + int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0; + if (isWhite) { + start &= ~0x1; // Search even numbered elements + } else { + start |= 0x1; // Search odd numbered elements + } + + int i = start; + for (; i < ces; i += 2) { + int temp = pce[i]; + if (temp > a0) { + lastChangingElement = i; + ret[0] = temp; + break; + } + } + + if (i + 1 < ces) { + ret[1] = pce[i + 1]; + } + } + + private int nextNBits(int bitsToGet) { + byte b, next, next2next; + int l = data.length - 1; + int bp = this.bytePointer; + + if (fillOrder == 1) { + b = data[bp]; + + if (bp == l) { + next = 0x00; + next2next = 0x00; + } else if ((bp + 1) == l) { + next = data[bp + 1]; + next2next = 0x00; + } else { + next = data[bp + 1]; + next2next = data[bp + 2]; + } + } else if (fillOrder == 2) { + b = flipTable[data[bp] & 0xff]; + + if (bp == l) { + next = 0x00; + next2next = 0x00; + } else if ((bp + 1) == l) { + next = flipTable[data[bp + 1] & 0xff]; + next2next = 0x00; + } else { + next = flipTable[data[bp + 1] & 0xff]; + next2next = flipTable[data[bp + 2] & 0xff]; + } + } else { + throw new Error("TIFFFaxDecoder7"); + } + + int bitsLeft = 8 - bitPointer; + int bitsFromNextByte = bitsToGet - bitsLeft; + int bitsFromNext2NextByte = 0; + if (bitsFromNextByte > 8) { + bitsFromNext2NextByte = bitsFromNextByte - 8; + bitsFromNextByte = 8; + } + + bytePointer++; + + int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft); + int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte); + + int i3 = 0; + if (bitsFromNext2NextByte != 0) { + i2 <<= bitsFromNext2NextByte; + i3 = (next2next & table2[bitsFromNext2NextByte]) >>> + (8 - bitsFromNext2NextByte); + i2 |= i3; + bytePointer++; + bitPointer = bitsFromNext2NextByte; + } else { + if (bitsFromNextByte == 8) { + bitPointer = 0; + bytePointer++; + } else { + bitPointer = bitsFromNextByte; + } + } + + int i = i1 | i2; + return i; + } + + private int nextLesserThan8Bits(int bitsToGet) { + byte b, next; + int l = data.length - 1; + int bp = this.bytePointer; + + if (fillOrder == 1) { + b = data[bp]; + if (bp == l) { + next = 0x00; + } else { + next = data[bp + 1]; + } + } else if (fillOrder == 2) { + b = flipTable[data[bp] & 0xff]; + if (bp == l) { + next = 0x00; + } else { + next = flipTable[data[bp + 1] & 0xff]; + } + } else { + throw new Error("TIFFFaxDecoder7"); + } + + int bitsLeft = 8 - bitPointer; + int bitsFromNextByte = bitsToGet - bitsLeft; + + int shift = bitsLeft - bitsToGet; + int i1, i2; + if (shift >= 0) { + i1 = (b & table1[bitsLeft]) >>> shift; + bitPointer += bitsToGet; + if (bitPointer == 8) { + bitPointer = 0; + bytePointer++; + } + } else { + i1 = (b & table1[bitsLeft]) << (-shift); + i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte); + + i1 |= i2; + bytePointer++; + bitPointer = bitsFromNextByte; + } + + return i1; + } + + // Move pointer backwards by given amount of bits + private void updatePointer(int bitsToMoveBack) { + int i = bitPointer - bitsToMoveBack; + + if (i < 0) { + bytePointer--; + bitPointer = 8 + i; + } else { + bitPointer = i; + } + } + + // Move to the next byte boundary + private boolean advancePointer() { + if (bitPointer != 0) { + bytePointer++; + bitPointer = 0; + } + + return true; + } +} + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/filter/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/package.html new file mode 100644 index 000000000..80a1dcf45 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/filter/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package will hold the PDFBox implementations of the filters that are used in PDF documents. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/ASCII85InputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/ASCII85InputStream.java new file mode 100644 index 000000000..9a6a5c3b2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/ASCII85InputStream.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.6 $ + */ +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;iBen Litchfield + * @version $Revision: 1.7 $ + */ +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 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= 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/fluidbook/tools/fwstk/src/apache/pdfbox/io/FastByteArrayOutputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/FastByteArrayOutputStream.java new file mode 100644 index 000000000..8c4c5974d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/FastByteArrayOutputStream.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.ByteArrayOutputStream; + +/** + * An byte array output stream that allows direct access to the byte array. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitInputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitInputStream.java new file mode 100644 index 000000000..d302d6afb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitInputStream.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.4 $ + */ +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> (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; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitOutputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitOutputStream.java new file mode 100644 index 000000000..e3d7e5652 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/NBitOutputStream.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.4 $ + */ +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; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/PushBackInputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/PushBackInputStream.java new file mode 100644 index 000000000..e79c5deac --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/PushBackInputStream.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; + +/** + * A simple subclass that adds a few convience methods. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class PushBackInputStream extends java.io.PushbackInputStream +{ + /* + * The current position in the file. + */ + private int offset = 0; + + /** + * 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; + } + + /** + * Returns the current byte offset in the file. + * @return the int byte offset + */ + public int getOffset() + { + return offset; + } + + /** + * {@inheritDoc} + */ + public int read() throws IOException + { + int retval = super.read(); + if (retval != -1) + { + offset++; + } + return retval; + } + + /** + * {@inheritDoc} + */ + public int read(byte[] b) throws IOException + { + return this.read(b, 0, b.length); + } + /** + * {@inheritDoc} + */ + public int read(byte[] b, int off, int len) throws IOException + { + int retval = super.read(b, off, len); + if (retval != -1) + { + offset += retval; + } + return retval; + } + + /** + * {@inheritDoc} + */ + public void unread(int b) throws IOException + { + offset--; + super.unread(b); + } + + /** + * {@inheritDoc} + */ + public void unread(byte[] b) throws IOException + { + this.unread(b, 0, b.length); + } + + /** + * {@inheritDoc} + */ + public void unread(byte[] b, int off, int len) throws IOException + { + if (len > 0) + { + offset -= len; + super.unread(b, off, len); + } + } + + /** + * 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; + } + + /** + * This is a method used to fix PDFBox issue 974661, the PDF parsing code needs + * to know if there is at least x amount of data left in the stream, but the available() + * method returns how much data will be available without blocking. PDFBox is willing to + * block to read the data, so we will first fill the internal buffer. + * + * @throws IOException If there is an error filling the buffer. + */ + public void fillBuffer() throws IOException + { + int bufferLength = buf.length; + byte[] tmpBuffer = new byte[bufferLength]; + int amountRead = 0; + int totalAmountRead = 0; + while( amountRead != -1 && totalAmountRead < bufferLength ) + { + amountRead = this.read( tmpBuffer, totalAmountRead, bufferLength - totalAmountRead ); + if( amountRead != -1 ) + { + totalAmountRead += amountRead; + } + } + this.unread( tmpBuffer, 0, totalAmountRead ); + } + + /** + * Reads a given number of bytes from the underlying stream. + * @param length the number of bytes to be read + * @return a byte array containing the bytes just read + * @throws IOException if an I/O error occurs while reading data + */ + public byte[] readFully(int length) throws IOException + { + byte[] data = new byte[length]; + int pos = 0; + while (pos < length) + { + int amountRead = read( data, pos, length - pos ); + if (amountRead < 0) + { + throw new EOFException("Premature end of file"); + } + pos += amountRead; + } + return data; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccess.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccess.java new file mode 100644 index 000000000..ef159153c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccess.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.IOException; + +/** + * An interface to allow PDF files to be stored completely in memory or + * to use a scratch file on the disk. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public interface RandomAccess +{ + + /** + * Release resources that are being held. + * + * @throws IOException If there is an error closing this resource. + */ + public void close() throws IOException; + + /** + * Seek to a position in the data. + * + * @param position The position to seek to. + * @throws IOException If there is an error while seeking. + */ + public void seek(long position) throws IOException; + + /** + * Read a single byte of data. + * + * @return The byte of data that is being read. + * + * @throws IOException If there is an error while reading the data. + */ + public int read() throws IOException; + + /** + * Read a buffer of data. + * + * @param b The buffer to write the data to. + * @param offset Offset into the buffer to start writing. + * @param length The amount of data to attempt to read. + * @return The number of bytes that were actually read. + * @throws IOException If there was an error while reading the data. + */ + public int read(byte[] b, int offset, int length) throws IOException; + + /** + * The total number of bytes that are available. + * + * @return The number of bytes available. + * + * @throws IOException If there is an IO error while determining the + * length of the data stream. + */ + public long length() throws IOException; + + /** + * Write a byte to the stream. + * + * @param b The byte to write. + * @throws IOException If there is an IO error while writing. + */ + public void write(int b) throws IOException; + + /** + * Write a buffer of data to the stream. + * + * @param b The buffer to get the data from. + * @param offset An offset into the buffer to get the data from. + * @param length The length of data to write. + * @throws IOException If there is an error while writing the data. + */ + public void write(byte[] b, int offset, int length) throws IOException; + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessBuffer.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessBuffer.java new file mode 100644 index 000000000..e6273d440 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessBuffer.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.IOException; + +/** + * An interface to allow PDF files to be stored completely in memory. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class RandomAccessBuffer implements RandomAccess +{ + + private byte[] buffer; + private long pointer; + private long size; + + /** + * Default constructor. + */ + public RandomAccessBuffer() + { + // starting with a 16kb buffer + buffer = new byte[16384]; + pointer = 0; + size = 0; + } + + /** + * {@inheritDoc} + */ + public void close() throws IOException + { + buffer = null; + pointer = 0; + size = 0; + } + + /** + * {@inheritDoc} + */ + public void seek(long position) throws IOException + { + this.pointer = position; + } + + /** + * {@inheritDoc} + */ + public int read() throws IOException + { + if (pointer >= this.size) + { + return -1; + } + return buffer[(int)pointer++] & 0xff; + } + + /** + * {@inheritDoc} + */ + public int read(byte[] b, int offset, int length) throws IOException + { + if (pointer >= this.size) + { + return 0; + } + int maxLength = (int) Math.min(length, this.size-pointer); + System.arraycopy(buffer, (int) pointer, b, offset, maxLength); + pointer += maxLength; + return maxLength; + } + + /** + * {@inheritDoc} + */ + public long length() throws IOException + { + return size; + } + + /** + * {@inheritDoc} + */ + public void write(int b) throws IOException + { + if (pointer >= buffer.length) + { + if (pointer >= Integer.MAX_VALUE) + { + throw new IOException("RandomAccessBuffer overflow"); + } + buffer = expandBuffer(buffer, (int)Math.min(2L * buffer.length, Integer.MAX_VALUE)); + } + buffer[(int)pointer++] = (byte)b; + if (pointer > this.size) + { + this.size = pointer; + } + } + + /** + * {@inheritDoc} + */ + public void write(byte[] b, int offset, int length) throws IOException + { + long newSize = pointer+length; + if (newSize >= buffer.length) + { + if (newSize > Integer.MAX_VALUE) + { + throw new IOException("RandomAccessBuffer overflow"); + } + newSize = Math.min(Math.max(2L * buffer.length, newSize), Integer.MAX_VALUE); + buffer = expandBuffer(buffer, (int)newSize); + } + System.arraycopy(b, offset, buffer, (int)pointer, length); + pointer += length; + if (pointer > this.size) + { + this.size = pointer; + } + } + + /** + * expand the given buffer to the new size. + * + * @param buffer the given buffer + * @param newSize the new size + * @return the expanded buffer + * + */ + private byte[] expandBuffer(byte[] buffer, int newSize) + { + byte[] expandedBuffer = new byte[newSize]; + System.arraycopy(buffer, 0, expandedBuffer, 0, buffer.length); + return expandedBuffer; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFile.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFile.java new file mode 100644 index 000000000..28bdf43d5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFile.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * An interface to allow temp PDF data to be stored in a scratch + * file on the disk to reduce memory consumption. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class RandomAccessFile implements RandomAccess +{ + private java.io.RandomAccessFile ras; + + /** + * Constructor. + * + * @param file The file to write the data to. + * @param mode The writing mode. + * @throws FileNotFoundException If the file cannot be created. + */ + public RandomAccessFile(File file, String mode) throws FileNotFoundException + { + ras = new java.io.RandomAccessFile(file, mode); + } + + /** + * {@inheritDoc} + */ + public void close() throws IOException + { + ras.close(); + } + + /** + * {@inheritDoc} + */ + public void seek(long position) throws IOException + { + ras.seek(position); + } + + /** + * {@inheritDoc} + */ + public int read() throws IOException + { + return ras.read(); + } + + /** + * {@inheritDoc} + */ + public int read(byte[] b, int offset, int length) throws IOException + { + return ras.read(b, offset, length); + } + + /** + * {@inheritDoc} + */ + public long length() throws IOException + { + return ras.length(); + } + + /** + * {@inheritDoc} + */ + public void write(byte[] b, int offset, int length) throws IOException + { + ras.write(b, offset, length); + } + + /** + * {@inheritDoc} + */ + public void write(int b) throws IOException + { + ras.write(b); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileInputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileInputStream.java new file mode 100644 index 000000000..211cd4116 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileInputStream.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.InputStream; +import java.io.IOException; + +/** + * This class allows a section of a RandomAccessFile to be accessed as an + * input stream. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class RandomAccessFileInputStream extends InputStream +{ + private RandomAccess 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( RandomAccess raFile, long startPosition, long length ) + { + file = raFile; + currentPosition = startPosition; + endPosition = currentPosition+length; + } + /** + * {@inheritDoc} + */ + public int available() + { + return (int)(endPosition - currentPosition); + } + /** + * {@inheritDoc} + */ + public void close() + { + //do nothing because we want to leave the random access file open. + } + /** + * {@inheritDoc} + */ + public int read() throws IOException + { + synchronized(file) + { + int retval = -1; + if( currentPosition < endPosition ) + { + file.seek( currentPosition ); + currentPosition++; + retval = file.read(); + } + return retval; + } + } + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public long skip( long amountToSkip ) + { + long amountSkipped = Math.min( amountToSkip, available() ); + currentPosition+= amountSkipped; + return amountSkipped; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileOutputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileOutputStream.java new file mode 100644 index 000000000..47f247408 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/RandomAccessFileOutputStream.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.io; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSObject; +import org.apache.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 + * @version $Revision: 1.6 $ + */ +public class RandomAccessFileOutputStream extends OutputStream +{ + private RandomAccess 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( RandomAccess 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; + } + + /** + * Get the amount of data that was actually written to the stream, in theory this + * should be the same as the length specified but in some cases it doesn't match. + * + * @return The number of bytes actually written to this stream. + */ + public long getLengthWritten() + { + return lengthWritten; + } + + /** + * 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; + } + + /** + * {@inheritDoc} + */ + public void write( byte[] b, int offset, int length ) throws IOException + { + file.seek( position+lengthWritten ); + lengthWritten += length; + file.write( b, offset, length ); + + } + /** + * {@inheritDoc} + */ + 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/io/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/io/package.html new file mode 100644 index 000000000..cb2a53054 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/io/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains IO streams. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/package.html new file mode 100644 index 000000000..43b285e41 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package holds executable classes that interact with the PDFBox application. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/BaseParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/BaseParser.java new file mode 100644 index 000000000..f9b5db972 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/BaseParser.java @@ -0,0 +1,1402 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfparser; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.io.PushBackInputStream; +import org.apache.pdfbox.io.RandomAccess; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.persistence.util.COSObjectKey; + +/** + * This class is used to contain parsing logic that will be used by both the + * PDFParser and the COSStreamParser. + * + * @author Ben Litchfield + * @version $Revision: 1.61 $ + */ +public abstract class BaseParser +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(BaseParser.class); + + private static final int E = 'e'; + private static final int N = 'n'; + private static final int D = 'd'; + + private static final int S = 's'; + private static final int T = 't'; + private static final int R = 'r'; + //private static final int E = 'e'; + private static final int A = 'a'; + private static final int M = 'm'; + + private static final int O = 'o'; + private static final int B = 'b'; + private static final int J = 'j'; + + /** + * This is a byte array that will be used for comparisons. + */ + public static final byte[] ENDSTREAM = + new byte[] { E, N, D, S, T, R, E, A, M }; + + /** + * This is a byte array that will be used for comparisons. + */ + public static final byte[] ENDOBJ = + new byte[] { E, N, D, O, B, J }; + + /** + * This is a byte array that will be used for comparisons. + */ + public static final String DEF = "def"; + + /** + * Default value of the {@link #forceParsing} flag. + */ + protected static final boolean FORCE_PARSING = + Boolean.getBoolean("org.apache.pdfbox.forceParsing"); + + /** + * This is the stream that will be read from. + */ + protected PushBackInputStream pdfSource; + + /** + * This is the document that will be parsed. + */ + protected COSDocument document; + + /** + * Flag to skip malformed or otherwise unparseable input where possible. + */ + protected final boolean forceParsing; + + public BaseParser() + { + this.forceParsing = FORCE_PARSING; + } + + /** + * Constructor. + * + * @since Apache PDFBox 1.3.0 + * @param input The input stream to read the data from. + * @param forceParcing flag to skip malformed or otherwise unparseable + * input where possible + * @throws IOException If there is an error reading the input stream. + */ + public BaseParser(InputStream input, boolean forceParsing) + throws IOException + { + this.pdfSource = new PushBackInputStream( + new BufferedInputStream(input, 16384), 4096); + this.forceParsing = forceParsing; + } + + /** + * 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 { + this(input, FORCE_PARSING); + } + + /** + * 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 { + this(new ByteArrayInputStream(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 + { + 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 + { + 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 + if(c != '/') + { + //an invalid dictionary, we are expecting + //the key, read until we can recover + log.warn("Invalid dictionary, found: '" + c + "' but expected: '/'"); + int read = pdfSource.read(); + while(read != -1 && read != '/' && read != '>') + { + // in addition to stopping when we find / or >, we also want + // to stop when we find endstream or endobj. + if(read==E) { + read = pdfSource.read(); + if(read==N) { + read = pdfSource.read(); + if(read==D) { + read = pdfSource.read(); + if(read==S) { + read = pdfSource.read(); + if(read==T) { + read = pdfSource.read(); + if(read==R) { + read = pdfSource.read(); + if(read==E) { + read = pdfSource.read(); + if(read==A) { + read = pdfSource.read(); + if(read==M) { + return obj; // we're done reading this object! + } + } + } + } + } + } else if(read==O) { + read = pdfSource.read(); + if(read==B) { + read = pdfSource.read(); + if(read==J) { + return obj; // we're done reading this object! + } + } + } + } + } + } + read = pdfSource.read(); + } + if(read != -1) + { + pdfSource.unread(read); + } + else + { + return obj; + } + } + 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("ISO-8859-1") ); + } + else + { + skipSpaces(); + } + } + + if( value == null ) + { + log.warn("Bad Dictionary Declaration " + pdfSource ); + } + else + { + 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 + "'" ); + } + 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, RandomAccess file ) throws IOException + { + 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. + } + } + 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 ); + } + + /*This needs to be dic.getItem because when we are parsing, the underlying object + * might still be null. + */ + COSBase streamLength = dic.getItem(COSName.LENGTH); + + //Need to keep track of the + out = stream.createFilteredStream( streamLength ); + + String endStream = null; + readUntilEndStream(out); + skipSpaces(); + endStream = readString(); + + if (!endStream.equals("endstream")) + { + /* + * Sometimes stream objects don't have an endstream tag so readUntilEndStream(out) + * also can stop on endobj tags. If that's the case we need to make sure to unread + * the endobj so parseObject() can handle that case normally. + */ + if (endStream.startsWith("endobj")) + { + byte[] endobjarray = endStream.getBytes("ISO-8859-1"); + pdfSource.unread(endobjarray); + } + /* + * Some PDF files don't contain a new line after endstream so we + * need to make sure that the next object number is getting read separately + * and not part of the endstream keyword. Ex. Some files would have "endstream8" + * instead of "endstream" + */ + else if(endStream.startsWith("endstream")) + { + String extra = endStream.substring(9, endStream.length()); + endStream = endStream.substring(0, 9); + byte[] array = extra.getBytes("ISO-8859-1"); + pdfSource.unread(array); + } + else + { + /* + * If for some reason we get something else here, Read until we find the next + * "endstream" + */ + readUntilEndStream( out ); + endStream = readString(); + if( !endStream.equals( "endstream" ) ) + { + throw new IOException("expected='endstream' actual='" + endStream + "' " + pdfSource); + } + } + } + } + finally + { + if( out != null ) + { + out.close(); + } + } + return stream; + } + + /** + * This method will read through the current stream object until + * we find the keyword "endstream" meaning we're at the end of this + * object. Some pdf files, however, forget to write some endstream tags + * and just close off objects with an "endobj" tag so we have to handle + * this case as well. + * @param out The stream we write out to. + * @throws IOException + */ + private void readUntilEndStream( OutputStream out ) throws IOException{ + int byteRead; + do{ //use a fail fast test for end of stream markers + byteRead = pdfSource.read(); + if(byteRead==E){//only branch if "e" + byteRead = pdfSource.read(); + if(byteRead==N){ //only continue branch if "en" + byteRead = pdfSource.read(); + if(byteRead==D){//up to "end" now + byteRead = pdfSource.read(); + if(byteRead==S){ + byteRead = pdfSource.read(); + if(byteRead==T){ + byteRead = pdfSource.read(); + if(byteRead==R){ + byteRead = pdfSource.read(); + if(byteRead==E){ + byteRead = pdfSource.read(); + if(byteRead==A){ + byteRead = pdfSource.read(); + if(byteRead==M){ + //found the whole marker + pdfSource.unread( ENDSTREAM ); + return; + }else{ + out.write(ENDSTREAM, 0, 8); + } + }else{ + out.write(ENDSTREAM, 0, 7); + } + }else{ + out.write(ENDSTREAM, 0, 6); + } + }else{ + out.write(ENDSTREAM, 0, 5); + } + }else{ + out.write(ENDSTREAM, 0, 4); + } + }else if(byteRead==O){ + byteRead = pdfSource.read(); + if(byteRead==B){ + byteRead = pdfSource.read(); + if(byteRead==J){ + //found whole marker + pdfSource.unread( ENDOBJ ); + return; + }else{ + out.write(ENDOBJ, 0, 5); + } + }else{ + out.write(ENDOBJ, 0, 4); + } + }else{ + out.write(E); + out.write(N); + out.write(D); + } + }else{ + out.write(E); + out.write(N); + } + }else{ + out.write(E); + } + } + if(byteRead!=-1) + { + out.write(byteRead); + } + + }while(byteRead!=-1); + } + + /** + * This is really a bug in the Document creators code, but it caused a crash + * in PDFBox, the first bug was in this format: + * /Title ( (5) + * /Creator which was patched in 1 place. + * However it missed the case where the Close Paren was escaped + * + * The second bug was in this format + * /Title (c:\) + * /Producer + * + * This patch moves this code out of the parseCOSString method, so it can be used twice. + * + * + * @param bracesParameter the number of braces currently open. + * + * @return the corrected value of the brace counter + * @throws IOException + */ + private int checkForMissingCloseParen(final int bracesParameter) throws IOException + { + int braces = bracesParameter; + 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 + // 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 // Look for a carriage return + && nextThreeBytes[1] == 0x0a // Look for a new line + && nextThreeBytes[2] == 0x2f ) // Look for a slash / + // Add a second case without a new line + || (nextThreeBytes[0] == 0x0d // Look for a carriage return + && nextThreeBytes[1] == 0x2f )) // Look for a slash / + { + braces = 0; + } + } + if (amountRead > 0) + { + pdfSource.unread( nextThreeBytes, 0, amountRead ); + } + return braces; + } + /** + * 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 + { + 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(ch == closeBrace) + { + + braces--; + braces = checkForMissingCloseParen(braces); + 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 ')': + // PDFBox 276 /Title (c:\) + braces = checkForMissingCloseParen(braces); + if( braces != 0 ) + { + retval.append( next ); + } + else + { + retval.append('\\'); + } + break; + 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(), forceParsing); + } + 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 + { + 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 ) + { + // We have to check if the expected values are there or not PDFBOX-385 + if (po.get(po.size()-1) instanceof COSInteger) + { + COSInteger genNumber = (COSInteger)po.remove( po.size() -1 ); + if (po.get(po.size()-1) instanceof COSInteger) + { + COSInteger number = (COSInteger)po.remove( po.size() -1 ); + COSObjectKey key = new COSObjectKey(number.intValue(), genNumber.intValue()); + pbo = document.getObjectFromPool(key); + } + else + { + // the object reference is somehow wrong + pbo = null; + } + } + else + { + pbo = null; + } + } + if( pbo != null ) + { + po.add( pbo ); + } + else + { + //it could be a bad object in the array which is just skipped + log.warn("Corrupt object reference" ); + + // This could also be an "endobj" or "endstream" which means we can assume that + // the array has ended. + String isThisTheEnd = readString(); + pdfSource.unread(isThisTheEnd.getBytes("ISO-8859-1")); + if("endobj".equals(isThisTheEnd) || "endstream".equals(isThisTheEnd)) + { + return po; + } + } + skipSpaces(); + } + pdfSource.read(); //read ']' + skipSpaces(); + return po; + } + + /** + * Determine if a character terminates a PDF name. + * + * @param ch The character + * @return true if the character terminates a PDF name, otherwise false. + */ + 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 + { + COSName retval = null; + int c = pdfSource.read(); + if( (char)c != '/') + { + throw new IOException("expected='/' actual='" + (char)c + "'-" + c + " " + pdfSource ); + } + // costruisce il nome + StringBuilder buffer = new StringBuilder(); + 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) + { + 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' ) + { + String trueString = new String( pdfSource.readFully( 4 ), "ISO-8859-1" ); + if( !trueString.equals( "true" ) ) + { + throw new IOException( "Error parsing boolean: expected='true' actual='" + trueString + "'" ); + } + else + { + retval = COSBoolean.TRUE; + } + } + else if( c == 'f' ) + { + String falseString = new String( pdfSource.readFully( 5 ), "ISO-8859-1" ); + 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 + { + 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': + { + String trueString = new String( pdfSource.readFully(4), "ISO-8859-1" ); + if( trueString.equals( "true" ) ) + { + retval = COSBoolean.TRUE; + } + else + { + throw new IOException( "expected true actual='" + trueString + "' " + pdfSource ); + } + break; + } + case 'f': + { + String falseString = new String( pdfSource.readFully(5), "ISO-8859-1" ); + 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 == '.') + { + StringBuilder buf = new StringBuilder(); + 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( 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 it's an endstream/endobj, we want to put it back so the caller will see it + if("endobj".equals(badString) || "endstream".equals(badString)) + { + pdfSource.unread(badString.getBytes("ISO-8859-1")); + } + } + } + } + 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(); + StringBuilder buffer = new StringBuilder(); + 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(); + } + StringBuilder buffer = new StringBuilder( 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 + { + pdfSource.unread(buffer.toString().getBytes("ISO-8859-1")); + 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. + StringBuilder buffer = new StringBuilder(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 first end of line marker occurs. + * Note: if you later unread the results of this function, you'll + * need to add a newline character to the end of the string. + * + * @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 + { + if (pdfSource.isEOF()) + { + throw new IOException( "Error: End-of-File, expected line"); + } + + StringBuilder buffer = new StringBuilder( 11 ); + + int c; + while ((c = pdfSource.read()) != -1) + { + if (isEOL(c)) + { + break; + } + buffer.append( (char)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. These values are + * specified in table 1 (page 12) of ISO 32000-1:2008. + * @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 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 != 60 && //see sourceforge bug 1714707 + lastByte != 0 && //See sourceforge bug 853328 + lastByte != -1 ) + { + intBuffer.append( (char)lastByte ); + } + if( lastByte != -1 ) + { + pdfSource.unread( lastByte ); + } + + try + { + retval = Integer.parseInt( intBuffer.toString() ); + } + catch( NumberFormatException e ) + { + pdfSource.unread(intBuffer.toString().getBytes("ISO-8859-1")); + throw new IOException( "Error: Expected an integer type, actual='" + intBuffer + "'" ); + } + return retval; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/ConformingPDFParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/ConformingPDFParser.java new file mode 100644 index 000000000..e035324ad --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/ConformingPDFParser.java @@ -0,0 +1,696 @@ +/* + * Copyright 2010 adam. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * under the License. + */ + +package org.apache.pdfbox.pdfparser; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.cos.COSUnread; +import org.apache.pdfbox.io.RandomAccess; +import org.apache.pdfbox.io.RandomAccessFile; +import org.apache.pdfbox.pdmodel.ConformingPDDocument; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.XrefEntry; +import org.apache.pdfbox.persistence.util.COSObjectKey; + +/** + * + * @author Adam Nichols + */ +public class ConformingPDFParser extends BaseParser { + protected RandomAccess inputFile; + List xrefEntries; + private long currentOffset; + private ConformingPDDocument doc = null; + private boolean throwNonConformingException = true; + private boolean recursivlyRead = true; + + /** + * Constructor. + * + * @param input The input stream that contains the PDF document. + * + * @throws IOException If there is an error initializing the stream. + */ + public ConformingPDFParser(File inputFile) throws IOException { + this.inputFile = new RandomAccessFile(inputFile, "r"); + } + + /** + * This will parse the stream and populate the COSDocument object. This will close + * the stream when it is done parsing. + * + * @throws IOException If there is an error reading from the stream or corrupt data + * is found. + */ + public void parse() throws IOException { + document = new COSDocument(); + doc = new ConformingPDDocument(document); + currentOffset = inputFile.length()-1; + long xRefTableLocation = parseTrailerInformation(); + currentOffset = xRefTableLocation; + parseXrefTable(); + // now that we read the xref table and put null references in the doc, + // we can deference those objects now. + boolean oldValue = recursivlyRead; + recursivlyRead = false; + List keys = doc.getObjectKeysFromPool(); + for(COSObjectKey key : keys) { + // getObject will put it into the document's object pool for us + getObject(key.getNumber(), key.getGeneration()); + } + recursivlyRead = oldValue; + } + + /** + * 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 doc; + } + + private boolean parseXrefTable() throws IOException { + String currentLine = readLine(); + if(throwNonConformingException) { + if(!"xref".equals(currentLine)) + throw new AssertionError("xref table not found.\nExpected: xref\nFound: "+currentLine); + } + + int objectNumber = readInt(); + int entries = readInt(); + xrefEntries = new ArrayList(entries); + for(int i=0; i")) { + // string of hex codes + return COSString.createFromHexString(string.replaceAll("^<", "").replaceAll(">$", "")); + } + return null; + } + + protected COSBase readObjectBackwards() throws IOException { + COSBase obj = null; + consumeWhitespaceBackwards(); + String lastSection = readBackwardUntilWhitespace(); + if("R".equals(lastSection)) { + // indirect reference + long gen = readLongBackwards(); + long number = readLongBackwards(); + // We just put a placeholder in the pool for now, we'll read the data later + doc.putObjectInPool(new COSUnread(), number, gen); + obj = new COSUnread(number, gen, this); + } else if(">>".equals(lastSection)) { + // dictionary + throw new RuntimeException("Not yet implemented"); + } else if(lastSection != null && lastSection.endsWith("]")) { + // array + COSArray array = new COSArray(); + lastSection = lastSection.replaceAll("]$", ""); + while(!lastSection.startsWith("[")) { + if(lastSection.matches("^\\s*<.*>\\s*$")) // it's a hex string + array.add(COSString.createFromHexString(lastSection.replaceAll("^\\s*<", "").replaceAll(">\\s*$", ""))); + lastSection = readBackwardUntilWhitespace(); + } + lastSection = lastSection.replaceAll("^\\[", ""); + if(lastSection.matches("^\\s*<.*>\\s*$")) // it's a hex string + array.add(COSString.createFromHexString(lastSection.replaceAll("^\\s*<", "").replaceAll(">\\s*$", ""))); + obj = array; + } else if(lastSection != null && lastSection.endsWith(">")) { + // string of hex codes + obj = processCosObject(lastSection); + } else { + // try a number, otherwise fall back on a string + try { + Long.parseLong(lastSection); + obj = COSNumber.get(lastSection); + } catch(NumberFormatException e) { + throw new RuntimeException("Not yet implemented"); + } + } + + return obj; + } + + protected COSName readNameBackwards() throws IOException { + String name = readBackwardUntilWhitespace(); + name = name.replaceAll("^/", ""); + return COSName.getPDFName(name); + } + + public COSBase getObject(long objectNumber, long generation) throws IOException { + // we could optionally, check to see if parse() have been called & + // throw an exception here, but I don't think that's really necessary + XrefEntry entry = xrefEntries.get((int)objectNumber); + currentOffset = entry.getByteOffset(); + return readObject(objectNumber, generation); + } + + /** + * This will read an object from the inputFile at whatever our currentOffset + * is. If the object and generation are not the expected values and this + * object is set to throw an exception for non-conforming documents, then an + * exception will be thrown. + * @param objectNumber the object number you expect to read + * @param generation the generation you expect this object to be + * @return + */ + public COSBase readObject(long objectNumber, long generation) throws IOException { + // when recursivly reading, we always pull the object from the filesystem + if(document != null && recursivlyRead) { + // check to see if it is in the document cache before hitting the filesystem + COSBase obj = doc.getObjectFromPool(objectNumber, generation); + if(obj != null) + return obj; + } + + int actualObjectNumber = readInt(); + if(objectNumber != actualObjectNumber) + if(throwNonConformingException) + throw new AssertionError("Object numer expected was " + + objectNumber + " but actual was " + actualObjectNumber); + consumeWhitespace(); + + int actualGeneration = readInt(); + if(generation != actualGeneration) + if(throwNonConformingException) + throw new AssertionError("Generation expected was " + + generation + " but actual was " + actualGeneration); + consumeWhitespace(); + + String obj = readWord(); + if(!"obj".equals(obj)) + if(throwNonConformingException) + throw new AssertionError("Expected keyword 'obj' but found " + obj); + + // put placeholder object in doc to prevent infinite recursion + // e.g. read Root -> dereference object -> read object which has /Parent -> GOTO read Root + doc.putObjectInPool(new COSObject(null), objectNumber, generation); + COSBase object = readObject(); + doc.putObjectInPool(object, objectNumber, generation); + return object; + } + + /** + * This actually reads the object data. + * @return the object which is read + * @throws IOException + */ + protected COSBase readObject() throws IOException { + consumeWhitespace(); + String string = readWord(); + if(string.startsWith("<<")) { + // this is a dictionary + COSDictionary dictionary = new COSDictionary(); + boolean atEndOfDictionary = false; + // remove the marker for the beginning of the dictionary + string = string.replaceAll("^<<", ""); + + if("".equals(string) || string.matches("^\\w$")) + string = readWord().trim(); + while(!atEndOfDictionary) { + COSName name = COSName.getPDFName(string); + COSBase object = readObject(); + dictionary.setItem(name, object); + + byte singleByte = consumeWhitespace(); + if(singleByte == '>') { + readByte(); // get rid of the second '>' + atEndOfDictionary = true; + } + if(!atEndOfDictionary) + string = readWord().trim(); + } + return dictionary; + } else if(string.startsWith("/")) { + // it's a dictionary label. i.e. /Type or /Pages or something similar + COSBase name = COSName.getPDFName(string); + return name; + } else if(string.startsWith("-")) { + // it's a negitive number + return parseNumber(string); + } else if(string.charAt(0) >= '0' && string.charAt(0) <= '9' ) { + // it's a COSInt or COSFloat, or a weak reference (i.e. "3 0 R") + // we'll have to peek ahead a little to see if it's a reference or not + long tempOffset = this.currentOffset; + consumeWhitespace(); + String tempString = readWord(); + if(tempString.matches("^[0-9]+$")) { + // it is an int, might be a weak reference... + tempString = readWord(); + if(!"R".equals(tempString)) { + // it's just a number, not a weak reference + this.currentOffset = tempOffset; + return parseNumber(string); + } + } else { + // it's just a number, not a weak reference + this.currentOffset = tempOffset; + return parseNumber(string); + } + + // it wasn't a number, so we need to parse the weak-reference + this.currentOffset = tempOffset; + int number = Integer.parseInt(string); + int gen = readInt(); + String r = readWord(); + + if(!"R".equals(r)) + if(throwNonConformingException) + throw new AssertionError("Expected keyword 'R' but found " + r); + + if(recursivlyRead) { + // seek to the object, read it, seek back to current location + long tempLocation = this.currentOffset; + this.currentOffset = this.xrefEntries.get(number).getByteOffset(); + COSBase returnValue = readObject(number, gen); + this.currentOffset = tempLocation; + return returnValue; + } else { + // Put a COSUnknown there as a placeholder + COSObject obj = new COSObject(new COSUnread()); + obj.setObjectNumber(COSInteger.get(number)); + obj.setGenerationNumber(COSInteger.get(gen)); + return obj; + } + } else if(string.startsWith("]")) { + // end of an array, just return null + if("]".equals(string)) + return null; + int oldLength = string.length(); + this.currentOffset -= oldLength; + return null; + } else if(string.startsWith("[")) { + // array of values + // we'll just pay attention to the first part (this is in case there + // is no whitespace between the "[" and the first element) + int oldLength = string.length(); + string = "["; + this.currentOffset -= (oldLength - string.length() + 1); + + COSArray array = new COSArray(); + COSBase object = readObject(); + while(object != null) { + array.add(object); + object = readObject(); + } + return array; + } else if(string.startsWith("(")) { + // this is a string (not hex encoded), strip off the '(' and read until ')' + StringBuilder sb = new StringBuilder(string.substring(1)); + byte singleByte = readByte(); + while(singleByte != ')') { + sb.append((char)singleByte); + singleByte = readByte(); + } + return new COSString(sb.toString()); + } else { + throw new RuntimeException("Not yet implemented: " + string + + " loation=" + this.currentOffset); + } + } + + /** + * 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. + */ + @Override + protected String readString() throws IOException { + consumeWhitespace(); + StringBuilder buffer = new StringBuilder(); + 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(); + } + + protected COSDictionary readDictionaryBackwards() throws IOException { + COSDictionary dict = new COSDictionary(); + + // consume the last two '>' chars which signify the end of the dictionary + consumeWhitespaceBackwards(); + byte singleByte = readByteBackwards(); + if(throwNonConformingException) { + if(singleByte != '>') + throw new AssertionError(""); + } + singleByte = readByteBackwards(); + if(throwNonConformingException) { + if(singleByte != '>') + throw new AssertionError(""); + } + + // check to see if we're at the end of the dictionary + boolean atEndOfDictionary = false; + singleByte = consumeWhitespaceBackwards(); + if(singleByte == '<') { + inputFile.seek(currentOffset-1); + atEndOfDictionary = ((byte)inputFile.read()) == '<'; + } + + COSDictionary backwardsDictionary = new COSDictionary(); + // while we're not at the end of the dictionary, read in entries + while(!atEndOfDictionary) { + COSBase object = readObjectBackwards(); + COSName name = readNameBackwards(); + backwardsDictionary.setItem(name, object); + + singleByte = consumeWhitespaceBackwards(); + if(singleByte == '<') { + inputFile.seek(currentOffset-1); + atEndOfDictionary = ((byte)inputFile.read()) == '<'; + } + } + + // the dictionaries preserve the order keys were added, as such we shall + // add them in the proper order, not the reverse order + Set backwardsKeys = backwardsDictionary.keySet(); + for(int i = backwardsKeys.size()-1; i >=0; i--) + dict.setItem((COSName)backwardsKeys.toArray()[i], backwardsDictionary.getItem((COSName)backwardsKeys.toArray()[i])); + + // consume the last two '<' chars + readByteBackwards(); + readByteBackwards(); + + return dict; + } + + /** + * This will read a line starting with the byte at offset and going + * backwards until it finds a newline. This should only be used if we are + * certain that the data will only be text, and not binary data. + * + * @param offset the location of the file where we should start reading + * @return the string which was read + * @throws IOException if there was an error reading data from the file + */ + protected String readLineBackwards() throws IOException { + StringBuilder sb = new StringBuilder(); + boolean endOfObject = false; + + do { + // first we read the %%EOF marker + byte singleByte = readByteBackwards(); + if(singleByte == '\n') { + // if ther's a preceeding \r, we'll eat that as well + inputFile.seek(currentOffset); + if((byte)inputFile.read() == '\r') + currentOffset--; + endOfObject = true; + } else if(singleByte == '\r') { + endOfObject = true; + } else { + sb.insert(0, (char)singleByte); + } + } while(!endOfObject); + + return sb.toString(); + } + + /** + * This will read a line starting with the byte at offset and going + * forward until it finds a newline. This should only be used if we are + * certain that the data will only be text, and not binary data. + * @param offset the location of the file where we should start reading + * @return the string which was read + * @throws IOException if there was an error reading data from the file + */ + @Override + protected String readLine() throws IOException { + StringBuilder sb = new StringBuilder(); + boolean endOfLine = false; + + do { + // first we read the %%EOF marker + byte singleByte = readByte(); + if(singleByte == '\n') { + // if ther's a preceeding \r, we'll eat that as well + inputFile.seek(currentOffset); + if((byte)inputFile.read() == '\r') + currentOffset++; + endOfLine = true; + } else if(singleByte == '\r') { + endOfLine = true; + } else { + sb.append((char)singleByte); + } + } while(!endOfLine); + + return sb.toString(); + } + + protected String readWord() throws IOException { + StringBuilder sb = new StringBuilder(); + boolean stop = true; + do { + byte singleByte = readByte(); + stop = this.isWhitespace(singleByte); + + // there are some additional characters which indicate the next element/word has begun + // ignore the first char we read, b/c the first char is the beginnging of this object, not the next one + if(!stop && sb.length() > 0) { + stop = singleByte == '/' || singleByte == '[' + || singleByte == ']' + || (singleByte == '>' && !">".equals(sb.toString())); + if(stop) // we're stopping on a non-whitespace char, decrement the + this.currentOffset--; // counter so we don't miss this character + } + if(!stop) + sb.append((char)singleByte); + } while(!stop); + + return sb.toString(); + } + + /** + * @return the recursivlyRead + */ + public boolean isRecursivlyRead() { + return recursivlyRead; + } + + /** + * @param recursivlyRead the recursivlyRead to set + */ + public void setRecursivlyRead(boolean recursivlyRead) { + this.recursivlyRead = recursivlyRead; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFObjectStreamParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFObjectStreamParser.java new file mode 100644 index 000000000..0b50ff333 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFObjectStreamParser.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfparser; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; + +/** + * This will parse a PDF 1.5 object stream and extract all of the objects from the stream. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class PDFObjectStreamParser extends BaseParser +{ + /** + * Log instance. + */ + private static final Log log = + LogFactory.getLog(PDFObjectStreamParser.class); + + private List streamObjects = null; + private List objectNumbers = null; + private COSStream stream; + + /** + * Constructor. + * + * @since Apache PDFBox 1.3.0 + * @param strm The stream to parse. + * @param doc The document for the current parsing. + * @param forceParcing flag to skip malformed or otherwise unparseable + * input where possible + * @throws IOException If there is an error initializing the stream. + */ + public PDFObjectStreamParser( + COSStream strm, COSDocument doc, boolean forceParsing) + throws IOException { + super(strm.getUnfilteredStream(), forceParsing); + setDocument( doc ); + stream = strm; + } + + /** + * 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 { + this(strm, doc, FORCE_PARSING); + } + + /** + * 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 + { + try + { + //need to first parse the header. + int numberOfObjects = stream.getInt( "N" ); + objectNumbers = new ArrayList( numberOfObjects ); + streamObjects = new ArrayList( numberOfObjects ); + for( int i=0; iBen Litchfield + * @version $Revision: 1.53 $ + */ +public class PDFParser extends BaseParser +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDFParser.class); + + private static final int SPACE_BYTE = 32; + + private static final String PDF_HEADER = "%PDF-"; + private static final String FDF_HEADER = "%FDF-"; + /** + * A list of duplicate objects found when Parsing the PDF + * File. + */ + private List conflictList = new ArrayList(); + + /** Collects all Xref/trailer objects and resolves them into single + * object using startxref reference */ + private XrefTrailerResolver xrefTrailerResolver = new XrefTrailerResolver(); + + /** + * Temp file directory. + */ + private File tempDirectory = null; + + private RandomAccess 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, FORCE_PARSING); + } + + /** + * 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, RandomAccess rafi) + throws IOException { + this(input, rafi, FORCE_PARSING); + } + + /** + * Constructor to allow control over RandomAccessFile. + * Also enables parser to skip corrupt objects to try and force parsing + * @param input The input stream that contains the PDF document. + * @param rafi The RandomAccessFile to be used in internal COSDocument + * @param force When true, the parser will skip corrupt pdf objects and + * will continue parsing at the next object in the file + * + * @throws IOException If there is an error initializing the stream. + */ + public PDFParser(InputStream input, RandomAccess rafi, boolean force) + throws IOException { + super(input, force); + 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; + } + + /** + * Returns true if parsing should be continued. By default, forceParsing is returned. + * This can be overridden to add application specific handling (for example to stop + * parsing when the number of exceptions thrown exceed a certain number). + * + * @param e The exception if vailable. Can be null if there is no exception available + */ + protected boolean isContinueOnError(Exception e) + { + return forceParsing; + } + + /** + * This will parse the stream and populate the COSDocument object. This will close + * the stream when it is done parsing. + * + * @throws IOException If there is an error reading from the stream or corrupt data + * is found. + */ + public void parse() throws IOException + { + try + { + if ( raf == null ) + { + if( tempDirectory != null ) + { + document = new COSDocument( tempDirectory ); + } + else + { + document = new COSDocument(); + } + } + else + { + document = new COSDocument( raf ); + } + setDocument( document ); + + parseHeader(); + + //Some PDF files have garbage between the header and the + //first object + skipToNextObj(); + + boolean wasLastParsedObjectEOF = false; + while(true) + { + if(pdfSource.isEOF()) + { + break; + } + try + { + wasLastParsedObjectEOF = parseObject(); + } + catch(IOException e) + { + /* + * PDF files may have random data after the EOF marker. Ignore errors if + * last object processed is EOF. + */ + if( wasLastParsedObjectEOF ) + { + break; + } + if(isContinueOnError(e)) + { + /* + * Warning is sent to the PDFBox.log and to the Console that + * we skipped over an object + */ + log.warn("Parsing Error, Skipping Object", e); + skipToNextObj(); + } + else + { + throw e; + } + } + skipSpaces(); + } + + // set xref to start with + xrefTrailerResolver.setStartxref( document.getStartXref() ); + + // get resolved xref table + trailer + document.setTrailer( xrefTrailerResolver.getTrailer() ); + document.addXRefTable( xrefTrailerResolver.getXrefTable() ); + + if( !document.isEncrypted() ) + { + document.dereferenceObjectStreams(); + } + ConflictObj.resolveConflicts(document, conflictList); + } + 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(); + } + } + + /** + * Skip to the start of the next object. This is used to recover + * from a corrupt object. This should handle all cases that parseObject + * supports. This assumes that the next object will + * start on its own line. + * + * @throws IOException + */ + private void skipToNextObj() throws IOException + { + byte[] b = new byte[16]; + Pattern p = Pattern.compile("\\d+\\s+\\d+\\s+obj.*", Pattern.DOTALL); + /* Read a buffer of data each time to see if it starts with a + * known keyword. This is not the most efficient design, but we should + * rarely be needing this function. We could update this to use the + * circular buffer, like in readUntilEndStream(). + */ + while(!pdfSource.isEOF()) + { + int l = pdfSource.read(b); + if(l < 1) + { + break; + } + String s = new String(b, "US-ASCII"); + if(s.startsWith("trailer") || + s.startsWith("xref") || + s.startsWith("startxref") || + s.startsWith("stream") || + p.matcher(s).matches()) + { + pdfSource.unread(b); + break; + } + else + { + pdfSource.unread(b, 1, l-1); + } + } + } + + private void parseHeader() throws IOException + { + // read first line + String header = readLine(); + // some pdf-documents are broken and the pdf-version is in one of the following lines + if ((header.indexOf( PDF_HEADER ) == -1) && (header.indexOf( FDF_HEADER ) == -1)) + { + header = readLine(); + while ((header.indexOf( PDF_HEADER ) == -1) && (header.indexOf( FDF_HEADER ) == -1)) + { + // if a line starts with a digit, it has to be the first one with data in it + if ((header.length() > 0) && (Character.isDigit(header.charAt(0)))) + { + break; + } + header = readLine(); + } + } + + // nothing found + if ((header.indexOf( PDF_HEADER ) == -1) && (header.indexOf( FDF_HEADER ) == -1)) + { + throw new IOException( "Error: Header doesn't contain versioninfo" ); + } + + //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 ); + if (headerStart == -1) + { + headerStart = header.indexOf(FDF_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() ); + } + + /* + * This is used if there is garbage after the header on the same line + */ + if (header.startsWith(PDF_HEADER)) + { + if(!header.matches(PDF_HEADER + "\\d.\\d")) + { + String headerGarbage = header.substring(PDF_HEADER.length()+3, header.length()) + "\n"; + header = header.substring(0, PDF_HEADER.length()+3); + pdfSource.unread(headerGarbage.getBytes("ISO-8859-1")); + } + } + else + { + if(!header.matches(FDF_HEADER + "\\d.\\d")) + { + String headerGarbage = header.substring(FDF_HEADER.length()+3, header.length()) + "\n"; + header = header.substring(0, FDF_HEADER.length()+3); + pdfSource.unread(headerGarbage.getBytes("ISO-8859-1")); + } + } + document.setHeaderString(header); + + try + { + if (header.startsWith( PDF_HEADER )) + { + float pdfVersion = Float. parseFloat( + header.substring( PDF_HEADER.length(), Math.min( header.length(), PDF_HEADER .length()+3) ) ); + document.setVersion( pdfVersion ); + } + else + { + float pdfVersion = Float. parseFloat( + header.substring( FDF_HEADER.length(), Math.min( header.length(), FDF_HEADER.length()+3) ) ); + document.setVersion( pdfVersion ); + } + } + catch ( NumberFormatException e ) + { + throw new IOException( "Error getting pdf version:" + e ); + } + } + + /** + * 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 the next object from the stream and add it to + * the local state. + * + * @return Returns true if the processed object had an endOfFile marker + * + * @throws IOException If an IO error occurs. + */ + private boolean parseObject() throws IOException + { + int currentObjByteOffset = pdfSource.getOffset(); + boolean isEndOfFile = false; + skipSpaces(); + //peek at the next character to determine the type of object we are parsing + char peekedChar = (char)pdfSource.peek(); + + //ignore endobj and endstream sections. + while( peekedChar == 'e' ) + { + //there are times when there are multiple endobj, so lets + //just read them and move on. + readString(); + skipSpaces(); + peekedChar = (char)pdfSource.peek(); + } + if( pdfSource.isEOF()) + { + //"Skipping because of EOF" ); + //end of file we will return a false and call it a day. + } + //xref table. Note: The contents of the Xref table are currently ignored + else if( peekedChar == 'x') + { + parseXrefTable( currentObjByteOffset ); + } + // Note: startxref can occur in either a trailer section or by itself + else if (peekedChar == 't' || peekedChar == 's') + { + if(peekedChar == 't') + { + parseTrailer(); + peekedChar = (char)pdfSource.peek(); + } + if (peekedChar == 's') + { + parseStartXref(); + // readString() calls skipSpaces() will skip comments... that's + // bad for us b/c the %%EOF flag is a comment + while(isWhitespace(pdfSource.peek()) && !pdfSource.isEOF()) + { + pdfSource.read(); // read (get rid of) all the whitespace + } + String eof = ""; + if(!pdfSource.isEOF()) + { + eof = readLine(); // if there's more data to read, get the EOF flag + } + + // verify that EOF exists (see PDFBOX-979 for documentation on special cases) + if(!"%%EOF".equals(eof)) { + if(eof.startsWith("%%EOF")) { + // content after marker -> unread with first space byte for read newline + pdfSource.unread(SPACE_BYTE); // we read a whole line; add space as newline replacement + pdfSource.unread(eof.substring(5).getBytes("ISO-8859-1")); + } else { + // PDF does not conform to spec, we should warn someone + log.warn("expected='%%EOF' actual='" + eof + "'"); + // if we're not at the end of a file, just put it back and move on + if(!pdfSource.isEOF()) { + pdfSource.unread( SPACE_BYTE ); // we read a whole line; add space as newline replacement + pdfSource.unread(eof.getBytes("ISO-8859-1")); + } + } + } + isEndOfFile = true; + } + } + //we are going to parse an normal object + else + { + int number = -1; + int genNum = -1; + String objectKey = null; + boolean missingObjectNumber = false; + try + { + char peeked = (char)pdfSource.peek(); + if( peeked == '<' ) + { + missingObjectNumber = true; + } + else + { + 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(); + } + if( !missingObjectNumber ) + { + skipSpaces(); + genNum = readInt(); + + objectKey = readString( 3 ); + //System.out.println( "parseObject() num=" + number + + //" genNumber=" + genNum + " key='" + objectKey + "'" ); + if( !objectKey.equals( "obj" ) ) + { + if (!isContinueOnError(null) || !objectKey.equals("o")) { + throw new IOException("expected='obj' actual='" + objectKey + "' " + pdfSource); + } + //assume that "o" was meant to be "obj" (this is a workaround for + // PDFBOX-773 attached PDF Andersens_Fairy_Tales.pdf). + } + } + else + { + number = -1; + genNum = -1; + } + + skipSpaces(); + COSBase pb = parseDirObject(); + String endObjectKey = readString(); + + if( endObjectKey.equals( "stream" ) ) + { + pdfSource.unread( endObjectKey.getBytes("ISO-8859-1") ); + pdfSource.unread( ' ' ); + if( pb instanceof COSDictionary ) + { + pb = parseCOSStream( (COSDictionary)pb, getDocument().getScratchFile() ); + + // test for XRef type + final COSStream strmObj = (COSStream) pb; + final COSName objectType = (COSName)strmObj.getItem( COSName.TYPE ); + if( objectType != null && objectType.equals( COSName.XREF ) ) + { + // XRef stream + parseXrefStream( strmObj, currentObjByteOffset ); + } + } + 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"); + } + skipSpaces(); + endObjectKey = readLine(); + } + + COSObjectKey key = new COSObjectKey( number, genNum ); + COSObject pdfObject = document.getObjectFromPool( key ); + if(pdfObject.getObject() == null) + { + pdfObject.setObject(pb); + } + /* + * If the object we returned already has a baseobject, then we have a conflict + * which we will resolve using information after we parse the xref table. + */ + else + { + addObjectToConflicts(currentObjByteOffset, key, pb); + } + + if( !endObjectKey.equals( "endobj" ) ) + { + if (endObjectKey.startsWith( "endobj" ) ) + { + /* + * Some PDF files don't contain a new line after endobj so we + * need to make sure that the next object number is getting read separately + * and not part of the endobj keyword. Ex. Some files would have "endobj28" + * instead of "endobj" + */ + pdfSource.unread( SPACE_BYTE ); // add a space first in place of the newline consumed by readline() + pdfSource.unread( endObjectKey.substring( 6 ).getBytes("ISO-8859-1") ); + } + else if(endObjectKey.trim().endsWith("endobj")) + { + /* + * Some PDF files contain junk (like ">> ", in the case of a PDF + * I found which was created by Exstream Dialogue Version 5.0.039) + * in which case we ignore the data before endobj and just move on + */ + log.warn("expected='endobj' actual='" + endObjectKey + "' "); + } + else if( !pdfSource.isEOF() ) + { + //It is possible that the endobj is missing, there + //are several PDFs out there that do that so. Unread + //and assume that endobj was missing + pdfSource.unread( SPACE_BYTE ); // add a space first in place of the newline consumed by readline() + pdfSource.unread( endObjectKey.getBytes("ISO-8859-1") ); + } + } + skipSpaces(); + } + return isEndOfFile; + } + + /** + * Adds a new ConflictObj to the conflictList. + * @param offset the offset of the ConflictObj + * @param key The COSObjectKey of this object + * @param pb The COSBase of this conflictObj + * @throws IOException + */ + private void addObjectToConflicts(int offset, COSObjectKey key, COSBase pb) throws IOException + { + COSObject obj = new COSObject(null); + obj.setObjectNumber( COSInteger.get( key.getNumber() ) ); + obj.setGenerationNumber( COSInteger.get( key.getGeneration() ) ); + obj.setObject(pb); + ConflictObj conflictObj = new ConflictObj(offset, key, obj); + conflictList.add(conflictObj); + } + + /** + * This will parse the startxref section from the stream. + * The startxref value is ignored. + * + * @return false on parsing error + * @throws IOException If an IO error occurs. + */ + private boolean parseStartXref() throws IOException + { + if(pdfSource.peek() != 's') + { + return false; + } + String startXRef = readString(); + if( !startXRef.trim().equals( "startxref" ) ) + { + return false; + } + skipSpaces(); + /* This integer is the byte offset of the first object referenced by the xref or xref stream + * Needed for the incremental update (PREV) + */ + getDocument().setStartXref(readInt()); + return true; + } + + + /** + * This will parse the xref table from the stream and add it to the state + * The XrefTable contents are ignored. + * @param startByteOffset the offset to start at + * @return false on parsing error + * @throws IOException If an IO error occurs. + */ + private boolean parseXrefTable( int startByteOffset ) throws IOException + { + if(pdfSource.peek() != 'x') + { + return false; + } + String xref = readString(); + if( !xref.trim().equals( "xref" ) ) + { + return false; + } + + // signal start of new XRef + xrefTrailerResolver.nextXrefObj( startByteOffset ); + + /* + * Xref tables can have multiple sections. + * Each starts with a starting object id and a count. + */ + while(true) + { + int currObjID = readInt(); // first obj id + int count = readInt(); // the number of objects in the xref table + skipSpaces(); + for(int i = 0; i < count; i++) + { + if(pdfSource.isEOF() || isEndOfName((char)pdfSource.peek())) + { + break; + } + if(pdfSource.peek() == 't') + { + break; + } + //Ignore table contents + String currentLine = readLine(); + String[] splitString = currentLine.split(" "); + if (splitString.length < 3) + { + log.warn("invalid xref line: " + currentLine); + break; + } + /* This supports the corrupt table as reported in + * PDFBOX-474 (XXXX XXX XX n) */ + if(splitString[splitString.length-1].equals("n")) + { + try + { + int currOffset = Integer.parseInt(splitString[0]); + int currGenID = Integer.parseInt(splitString[1]); + COSObjectKey objKey = new COSObjectKey(currObjID, currGenID); + xrefTrailerResolver.setXRef(objKey, currOffset); + } + catch(NumberFormatException e) + { + throw new IOException(e.getMessage()); + } + } + else if(!splitString[2].equals("f")) + { + throw new IOException("Corrupt XRefTable Entry - ObjID:" + currObjID); + } + currObjID++; + skipSpaces(); + } + skipSpaces(); + char c = (char)pdfSource.peek(); + if(c < '0' || c > '9') + { + break; + } + } + return true; + } + + /** + * This will parse the trailer from the stream and add it to the state. + * + * @return false on parsing error + * @throws IOException If an IO error occurs. + */ + private boolean parseTrailer() throws IOException + { + if(pdfSource.peek() != 't') + { + return false; + } + //read "trailer" + String nextLine = readLine(); + if( !nextLine.trim().equals( "trailer" ) ) + { + // in some cases the EOL is missing and the trailer immediately + // continues with "<<" or with a blank character + // even if this does not comply with PDF reference we want to support as many PDFs as possible + // Acrobat reader can also deal with this. + if (nextLine.startsWith("trailer")) + { + byte[] b = nextLine.getBytes("ISO-8859-1"); + int len = "trailer".length(); + pdfSource.unread('\n'); + pdfSource.unread(b, len, b.length-len); + } + else + { + return false; + } + } + + // in some cases the EOL is missing and the trailer continues with " <<" + // even if this does not comply with PDF reference we want to support as many PDFs as possible + // Acrobat reader can also deal with this. + skipSpaces(); + + COSDictionary parsedTrailer = parseCOSDictionary(); + xrefTrailerResolver.setTrailer( parsedTrailer ); + + // The version can also be specified within the document /Catalog + readVersionInTrailer(parsedTrailer); + + skipSpaces(); + return true; + } + + /** + * The document catalog can also have a /Version parameter which overrides the version specified + * in the header if, and only if it is greater. + * + * @param parsedTrailer the parsed catalog in the trailer + */ + private void readVersionInTrailer(COSDictionary parsedTrailer) + { + COSObject root = (COSObject) parsedTrailer.getItem(COSName.ROOT); + if (root != null) + { + COSName version = (COSName) root.getItem(COSName.VERSION); + if (version != null) + { + float trailerVersion = Float.valueOf(version.getName()); + if (trailerVersion > document.getVersion()) + { + document.setVersion(trailerVersion); + } + } + } + } + + /** + * Fills XRefTrailerResolver with data of given stream. + * Stream must be of type XRef. + * @param stream the stream to be read + * @param objByteOffset the offset to start at + * @throws IOException if there is an error parsing the stream + */ + public void parseXrefStream( COSStream stream, int objByteOffset ) throws IOException + { + xrefTrailerResolver.nextXrefObj( objByteOffset ); + xrefTrailerResolver.setTrailer( stream ); + PDFXrefStreamParser parser = + new PDFXrefStreamParser( stream, document, forceParsing, xrefTrailerResolver ); + parser.parse(); + } + + /** + * Used to resolve conflicts when a PDF Document has multiple objects with + * the same id number. Ideally, we could use the Xref table when parsing + * the document to be able to determine which of the objects with the same ID + * is correct, but we do not have access to the Xref Table during parsing. + * Instead, we queue up the conflicts and resolve them after the Xref has + * been parsed. The Objects listed in the Xref Table are kept and the + * others are ignored. + */ + private static class ConflictObj + { + + private int offset; + private COSObjectKey objectKey; + private COSObject object; + + public ConflictObj(int offsetValue, COSObjectKey key, COSObject pdfObject) + { + this.offset = offsetValue; + this.objectKey = key; + this.object = pdfObject; + } + @Override + public String toString() + { + return "Object(" + offset + ", " + objectKey + ")"; + } + + /** + * Sometimes pdf files have objects with the same ID number yet are + * not referenced by the Xref table and therefore should be excluded. + * This method goes through the conflicts list and replaces the object stored + * in the objects array with this one if it is referenced by the xref + * table. + * @throws IOException + */ + private static void resolveConflicts(COSDocument document, List conflictList) throws IOException + { + Iterator conflicts = conflictList.iterator(); + while(conflicts.hasNext()) + { + ConflictObj o = conflicts.next(); + Integer offset = new Integer(o.offset); + if(document.getXrefTable().containsValue(offset)) + { + COSObject pdfObject = document.getObjectFromPool(o.objectKey); + pdfObject.setObject(o.object.getObject()); + } + } + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFStreamParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFStreamParser.java new file mode 100644 index 000000000..7f4b6134d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFStreamParser.java @@ -0,0 +1,484 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfparser; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.io.RandomAccess; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.util.ImageParameters; +import org.apache.pdfbox.util.PDFOperator; + +/** + * This will parse a PDF byte stream and extract operands and such. + * + * @author Ben Litchfield + * @version $Revision: 1.32 $ + */ +public class PDFStreamParser extends BaseParser +{ + private List streamObjects = new ArrayList( 100 ); + private RandomAccess file; + private PDFOperator lastBIToken = null; + + /** + * Constructor that takes a stream to parse. + * + * @since Apache PDFBox 1.3.0 + * @param stream The stream to read data from. + * @param raf The random access file. + * @param forceParcing flag to skip malformed or otherwise unparseable + * input where possible + * @throws IOException If there is an error reading from the stream. + */ + public PDFStreamParser( + InputStream stream, RandomAccess raf, boolean forceParsing) + throws IOException { + super(stream, forceParsing); + file = raf; + } + + /** + * 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, RandomAccess raf) + throws IOException { + this(stream, raf, FORCE_PARSING); + } + + /** + * Constructor. + * + * @param stream The stream to parse. + * + * @throws IOException If there is an error initializing the stream. + */ + public PDFStreamParser( PDStream stream ) throws IOException + { + this( stream.createInputStream(), stream.getStream().getScratchFile() ); + } + + /** + * Constructor. + * + * @since Apache PDFBox 1.3.0 + * @param stream The stream to parse. + * @param forceParcing flag to skip malformed or otherwise unparseable + * input where possible + * @throws IOException If there is an error initializing the stream. + */ + public PDFStreamParser(COSStream stream, boolean forceParsing) + throws IOException { + this(stream.getUnfilteredStream(), stream.getScratchFile(), forceParsing); + } + + /** + * 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 + { + try + { + Object token = null; + while( (token = parseNextToken()) != null ) + { + streamObjects.add( token ); + //logger().fine( "parsed=" + token ); + } + } + finally + { + pdfSource.close(); + } + } + + /** + * This will get the tokens that were parsed from the stream. + * + * @return All of the tokens in the stream. + */ + public List getTokens() + { + return streamObjects; + } + + public void close() throws IOException + { + pdfSource.close(); + } + + /** + * This will get an iterator which can be used to parse the stream + * one token after the other. + * + * @return an iterator to get one token after the other + */ + public Iterator getTokenIterator() + { + return new Iterator() + { + private Object token; + + private void tryNext() + { + try + { + if (token == null) + { + token = parseNextToken(); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + public boolean hasNext() + { + tryNext(); + return token != null; + } + + /** {@inheritDoc} */ + public Object next() { + tryNext(); + Object tmp = token; + if (tmp == null) + { + throw new NoSuchElementException(); + } + token = null; + return tmp; + } + + /** {@inheritDoc} */ + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * 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 + { + 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 '.': + { + /* We will be filling buf with the rest of the number. Only + * allow 1 "." and "-" and "+" at start of number. */ + StringBuffer buf = new StringBuffer(); + buf.append( c ); + pdfSource.read(); + + boolean dotNotRead = (c != '.'); + while( Character.isDigit(( c = (char)pdfSource.peek()) ) || (dotNotRead && (c == '.')) ) + { + buf.append( c ); + pdfSource.read(); + + if (dotNotRead && (c == '.')) + { + dotNotRead = false; + } + } + retval = COSNumber.get( buf.toString() ); + 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 EI. + 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 ); + } + } + + } + + 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); + int nextChar = pdfSource.peek(); + while( + nextChar != -1 && // EOF + !isWhitespace(nextChar) && + !isClosing(nextChar) && + nextChar != '[' && + nextChar != '<' && + nextChar != '(' && + nextChar != '/' && + (nextChar < '0' || + nextChar > '9' ) ) + { + char currentChar = (char)pdfSource.read(); + nextChar = pdfSource.peek(); + buffer.append( currentChar ); + // Type3 Glyph description has operators with a number in the name + if (currentChar == 'd' && (nextChar == '0' || nextChar == '1') ) { + buffer.append( (char)pdfSource.read() ); + nextChar = pdfSource.peek(); + } + } + return buffer.toString(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFXrefStreamParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFXrefStreamParser.java new file mode 100644 index 000000000..da576ed38 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/PDFXrefStreamParser.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfparser; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.persistence.util.COSObjectKey; + +/** + * This will parse a PDF 1.5 (or better) Xref stream and + * extract the xref information from the stream. + * + * @author Justin LeFebvre + * @version $Revision: 1.0 $ + */ +public class PDFXrefStreamParser extends BaseParser +{ + private COSStream stream; + private XrefTrailerResolver xrefTrailerResolver; + + /** + * Constructor. + * + * @since 1.3.0 + * @param strm The stream to parse. + * @param doc The document for the current parsing. + * @param forceParsing flag to skip malformed or otherwise unparseable + * input where possible + * @param xrefTrailerResolver resolver to read the xref/trailer information + * + * @throws IOException If there is an error initializing the stream. + */ + public PDFXrefStreamParser( + COSStream strm, COSDocument doc, boolean forceParsing, + XrefTrailerResolver xrefTrailerResolver ) + throws IOException + { + super(strm.getUnfilteredStream(), forceParsing); + setDocument(doc); + stream = strm; + this.xrefTrailerResolver = xrefTrailerResolver; + } + + /** + * Parses through the unfiltered stream and populates the xrefTable HashMap. + * @throws IOException If there is an error while parsing the stream. + */ + public void parse() throws IOException + { + try + { + COSArray xrefFormat = (COSArray)stream.getDictionaryObject(COSName.W); + COSArray indexArray = (COSArray)stream.getDictionaryObject(COSName.INDEX); + /* + * If Index doesn't exist, we will use the default values. + */ + if(indexArray == null) + { + indexArray = new COSArray(); + indexArray.add(COSInteger.ZERO); + indexArray.add(stream.getDictionaryObject(COSName.SIZE)); + } + + ArrayList objNums = new ArrayList(); + + /* + * Populates objNums with all object numbers available + */ + Iterator indexIter = indexArray.iterator(); + while(indexIter.hasNext()) + { + int objID = ((COSInteger)indexIter.next()).intValue(); + int size = ((COSInteger)indexIter.next()).intValue(); + for(int i = 0; i < size; i++) + { + objNums.add(new Integer(objID + i)); + } + } + Iterator objIter = objNums.iterator(); + /* + * Calculating the size of the line in bytes + */ + int w0 = xrefFormat.getInt(0); + int w1 = xrefFormat.getInt(1); + int w2 = xrefFormat.getInt(2); + int lineSize = w0 + w1 + w2; + + while(pdfSource.available() > 0 && objIter.hasNext()) + { + byte[] currLine = new byte[lineSize]; + pdfSource.read(currLine); + + int type = 0; + /* + * Grabs the number of bytes specified for the first column in + * the W array and stores it. + */ + for(int i = 0; i < w0; i++) + { + type += (currLine[i] & 0x00ff) << ((w0 - i - 1)* 8); + } + //Need to remember the current objID + Integer objID = objIter.next(); + /* + * 3 different types of entries. + */ + switch(type) + { + case 0: + /* + * Skipping free objects + */ + break; + case 1: + int offset = 0; + for(int i = 0; i < w1; i++) + { + offset += (currLine[i + w0] & 0x00ff) << ((w1 - i - 1) * 8); + } + int genNum = 0; + for(int i = 0; i < w2; i++) + { + genNum += (currLine[i + w0 + w1] & 0x00ff) << ((w2 - i - 1) * 8); + } + COSObjectKey objKey = new COSObjectKey(objID.intValue(), genNum); + xrefTrailerResolver.setXRef(objKey, offset); + break; + case 2: + /* + * These objects are handled by the dereferenceObjects() method + * since they're only pointing to object numbers + */ + break; + default: + break; + } + } + } + finally + { + pdfSource.close(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/VisualSignatureParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/VisualSignatureParser.java new file mode 100644 index 000000000..8bd6cda4d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/VisualSignatureParser.java @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfparser; + +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.pdfwriter.COSWriter; +import org.apache.pdfbox.persistence.util.COSObjectKey; + +public class VisualSignatureParser extends BaseParser { + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDFParser.class); + + public VisualSignatureParser(InputStream input) throws IOException { + super(input); + } + + public void parse() throws IOException { + document = new COSDocument(); + skipToNextObj(); + + boolean wasLastParsedObjectEOF = false; + try { + while(!wasLastParsedObjectEOF) { + if(pdfSource.isEOF()) { + break; + } + try { + wasLastParsedObjectEOF = parseObject(); + } catch(IOException e) { + /* + * Warning is sent to the PDFBox.log and to the Console that + * we skipped over an object + */ + log.warn("Parsing Error, Skipping Object", e); + skipToNextObj(); + } + skipSpaces(); + } + } catch(IOException e) { + /* + * PDF files may have random data after the EOF marker. Ignore errors if + * last object processed is EOF. + */ + if(!wasLastParsedObjectEOF) { + throw e; + } + } + } + + private void skipToNextObj() throws IOException { + byte[] b = new byte[16]; + Pattern p = Pattern.compile("\\d+\\s+\\d+\\s+obj.*", Pattern.DOTALL); + /* Read a buffer of data each time to see if it starts with a + * known keyword. This is not the most efficient design, but we should + * rarely be needing this function. We could update this to use the + * circular buffer, like in readUntilEndStream(). + */ + while(!pdfSource.isEOF()) { + int l = pdfSource.read(b); + if(l < 1) { + break; + } + String s = new String(b, "US-ASCII"); + if(s.startsWith("trailer") + || s.startsWith("xref") + || s.startsWith("startxref") + || s.startsWith("stream") + || p.matcher(s).matches()) { + pdfSource.unread(b); + break; + } else { + pdfSource.unread(b, 1, l - 1); + } + } + } + + private boolean parseObject() throws IOException { + boolean isEndOfFile = false; + skipSpaces(); + //peek at the next character to determine the type of object we are parsing + char peekedChar = (char) pdfSource.peek(); + + //ignore endobj and endstream sections. + while(peekedChar == 'e') { + //there are times when there are multiple endobj, so lets + //just read them and move on. + readString(); + skipSpaces(); + peekedChar = (char) pdfSource.peek(); + } + if(pdfSource.isEOF()) { + // end of file we will return a false and call it a day. + } else if(peekedChar == 'x') { + //xref table. Note: The contents of the Xref table are currently ignored + return true; + } else if(peekedChar == 't' || peekedChar == 's') { + // Note: startxref can occur in either a trailer section or by itself + if(peekedChar == 't') { + return true; + } + if(peekedChar == 's') { + skipToNextObj(); + //verify that EOF exists + String eof = readExpectedString("%%EOF"); + if(eof.indexOf("%%EOF") == -1 && !pdfSource.isEOF()) { + throw new IOException("expected='%%EOF' actual='" + eof + "' next=" + readString() + + " next=" + readString()); + } + isEndOfFile = true; + } + } else { + //we are going to parse an normal object + int number = -1; + int genNum = -1; + String objectKey = null; + boolean missingObjectNumber = false; + try { + char peeked = (char) pdfSource.peek(); + if(peeked == '<') { + missingObjectNumber = true; + } else { + 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(); + } + if(!missingObjectNumber) { + skipSpaces(); + genNum = readInt(); + + 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); + } + } else { + number = -1; + genNum = -1; + } + + 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); + pb.setNeedToBeUpdate(true); + pdfObject.setObject(pb); + + if(!endObjectKey.equals("endobj")) { + if(endObjectKey.startsWith("endobj")) { + /* + * Some PDF files don't contain a new line after endobj so we + * need to make sure that the next object number is getting read separately + * and not part of the endobj keyword. Ex. Some files would have "endobj28" + * instead of "endobj" + */ + pdfSource.unread(endObjectKey.substring(6).getBytes()); + } else 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(COSWriter.SPACE); + pdfSource.unread(endObjectKey.getBytes()); + } 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")) { + if(isClosing()) { + //found a case with 17506.pdf object 41 that was like this + //41 0 obj [/Pattern /DeviceGray] ] endobj + //notice the second array close, here we are reading it + //and ignoring and attempting to continue + pdfSource.read(); + } + skipSpaces(); + String thirdPossibleEndObj = readString(); + if(!thirdPossibleEndObj.equals("endobj")) { + throw new IOException("expected='endobj' firstReadAttempt='" + endObjectKey + "' " + + "secondReadAttempt='" + secondEndObjectKey + "' " + pdfSource); + } + } + } + } + } + skipSpaces(); + } + return isEndOfFile; + } + + public COSDocument getDocument() throws IOException { + if(document == null) { + throw new IOException("You must call parse() before calling getDocument()"); + } + return document; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/XrefTrailerResolver.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/XrefTrailerResolver.java new file mode 100644 index 000000000..80a69b7ab --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/XrefTrailerResolver.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfparser; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.persistence.util.COSObjectKey; + +/** + * This class will collect all XRef/trailer objects and creates correct + * xref/trailer information after all objects are read using startxref + * and 'Prev' information (unused XRef/trailer objects are discarded). + * + * In case of missing startxref or wrong startxref pointer all + * XRef/trailer objects are used to create xref table / trailer dictionary + * in order they occur. + * + * For each new xref object/XRef stream method {@link #nextXrefObj(int)} + * must be called with start byte position. All following calls to + * {@link #setXRef(COSObjectKey, int)} or {@link #setTrailer(COSDictionary)} + * will add the data for this byte position. + * + * After all objects are parsed the startxref position must be provided + * using {@link #setStartxref(int)}. This is used to build the chain of + * active xref/trailer objects used for creating document trailer and xref table. + * + * @author Timo Böhme (timo.boehme at ontochem.com) + */ +public class XrefTrailerResolver +{ + + /** + * A class which represents a xref/trailer object. + */ + class XrefTrailerObj + { + private COSDictionary trailer = null; + private final Map xrefTable = new HashMap(); + } + + private final Map bytePosToXrefMap = new HashMap(); + private XrefTrailerObj curXrefTrailerObj = null; + private XrefTrailerObj resolvedXrefTrailer = null; + + /** Log instance. */ + private static final Log log = LogFactory.getLog( XrefTrailerResolver.class ); + + /** + * Signals that a new XRef object (table or stream) starts. + * @param startBytePos the offset to start at + * + */ + public void nextXrefObj( final int startBytePos ) + { + bytePosToXrefMap.put( startBytePos, curXrefTrailerObj = new XrefTrailerObj() ); + } + + /** + * Populate XRef HashMap of current XRef object. + * Will add an Xreftable entry that maps ObjectKeys to byte offsets in the file. + * @param objKey The objkey, with id and gen numbers + * @param offset The byte offset in this file + */ + public void setXRef( COSObjectKey objKey, int offset ) + { + if ( curXrefTrailerObj == null ) + { + // should not happen... + log.warn( "Cannot add XRef entry for '" + objKey.getNumber() + "' because XRef start was not signalled." ); + return; + } + curXrefTrailerObj.xrefTable.put( objKey, offset ); + } + + /** + * Adds trailer information for current XRef object. + * + * @param trailer the current document trailer dictionary + */ + public void setTrailer( COSDictionary trailer ) + { + if ( curXrefTrailerObj == null ) + { + // should not happen... + log.warn( "Cannot add trailer because XRef start was not signalled." ); + return; + } + curXrefTrailerObj.trailer = trailer; + } + + /** + * Sets the byte position of the first XRef + * (has to be called after very last startxref was read). + * This is used to resolve chain of active XRef/trailer. + * + * In case startxref position is not found we output a + * warning and use all XRef/trailer objects combined + * in byte position order. + * Thus for incomplete PDF documents with missing + * startxref one could call this method with parameter value -1. + */ + public void setStartxref( int startxrefBytePos ) + { + if ( resolvedXrefTrailer != null ) + { + log.warn( "Method must be called only ones with last startxref value." ); + return; + } + + resolvedXrefTrailer = new XrefTrailerObj(); + resolvedXrefTrailer.trailer = new COSDictionary(); + + XrefTrailerObj curObj = bytePosToXrefMap.get( startxrefBytePos ); + List xrefSeqBytePos = new ArrayList(); + + if ( curObj == null ) + { + // no XRef at given position + log.warn( "Did not found XRef object at specified startxref position " + startxrefBytePos ); + + // use all objects in byte position order (last entries overwrite previous ones) + xrefSeqBytePos.addAll( bytePosToXrefMap.keySet() ); + Collections.sort( xrefSeqBytePos ); + } + else + { + // found starting Xref object + // add this and follow chain defined by 'Prev' keys + xrefSeqBytePos.add( startxrefBytePos ); + while ( curObj.trailer != null ) + { + int prevBytePos = curObj.trailer.getInt( COSName.PREV, -1 ); + if ( prevBytePos == -1 ) + { + break; + } + + curObj = bytePosToXrefMap.get( prevBytePos ); + if ( curObj == null ) + { + log.warn( "Did not found XRef object pointed to by 'Prev' key at position " + prevBytePos ); + break; + } + xrefSeqBytePos.add( prevBytePos ); + + // sanity check to prevent infinite loops + if ( xrefSeqBytePos.size() >= bytePosToXrefMap.size() ) + { + break; + } + } + // have to reverse order so that later XRefs will overwrite previous ones + Collections.reverse( xrefSeqBytePos ); + } + + // merge used and sorted XRef/trailer + for ( Integer bPos : xrefSeqBytePos ) + { + curObj = bytePosToXrefMap.get( bPos ); + if ( curObj.trailer != null ) + { + resolvedXrefTrailer.trailer.addAll( curObj.trailer ); + } + resolvedXrefTrailer.xrefTable.putAll( curObj.xrefTable ); + } + + } + + /** + * Gets the resolved trailer. Might return null in case + * {@link #setStartxref(int)} was not called before. + * + */ + public COSDictionary getTrailer() + { + return ( resolvedXrefTrailer == null ) ? null : resolvedXrefTrailer.trailer; + } + + /** + * Gets the resolved xref table. Might return null in case + * {@link #setStartxref(int)} was not called before. + * + */ + public Map getXrefTable() + { + return ( resolvedXrefTrailer == null ) ? null : resolvedXrefTrailer.xrefTable; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/package.html new file mode 100644 index 000000000..f19200de3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfparser/package.html @@ -0,0 +1,25 @@ + + + + + + + +The pdfparser package contains classes to parse PDF documents and objects within the document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ArrayEntry.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ArrayEntry.java new file mode 100644 index 000000000..2b45385bf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ArrayEntry.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfviewer; + +/** + * This is a simple class that will contain an index and a value. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/MapEntry.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/MapEntry.java new file mode 100644 index 000000000..09de149d0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/MapEntry.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfviewer; + +import org.apache.pdfbox.cos.COSName; + + +/** + * This is a simple class that will contain a key and a value. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFPagePanel.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFPagePanel.java new file mode 100644 index 000000000..8e6bf0588 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFPagePanel.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfviewer; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; + +import java.io.IOException; + +import javax.swing.JPanel; + +import org.apache.pdfbox.pdmodel.PDPage; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +/** + * This is a simple JPanel that can be used to display a PDF page. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class PDFPagePanel extends JPanel +{ + + private PDPage page; + private PageDrawer drawer = null; + private Dimension pageDimension = null; + private Dimension drawDimension = 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(); + drawDimension = pageSize.createDimension(); + int rotation = page.findRotation(); + if (rotation == 90 || rotation == 270) + { + pageDimension = new Dimension(drawDimension.height, drawDimension.width); + } + else + { + pageDimension = drawDimension; + } + setSize( pageDimension ); + setBackground( java.awt.Color.white ); + } + + /** + * {@inheritDoc} + */ + public void paint(Graphics g ) + { + try + { + g.setColor( getBackground() ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + + int rotation = page.findRotation(); + if (rotation == 90 || rotation == 270) + { + Graphics2D g2D = (Graphics2D)g; + g2D.translate(pageDimension.getWidth(), 0.0f); + g2D.rotate(Math.toRadians(rotation)); + } + + drawer.drawPage( g, page, drawDimension ); + } + catch( IOException e ) + { + e.printStackTrace(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeCellRenderer.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeCellRenderer.java new file mode 100644 index 000000000..86ff6ceca --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeCellRenderer.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfviewer; + +import java.awt.Component; + +import javax.swing.JTree; + +import javax.swing.tree.DefaultTreeCellRenderer; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; + +/** + * A class to render tree cells for the pdfviewer. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class PDFTreeCellRenderer extends DefaultTreeCellRenderer +{ + /** + * {@inheritDoc} + */ + 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; + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeModel.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeModel.java new file mode 100644 index 000000000..0ab659150 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PDFTreeModel.java @@ -0,0 +1,319 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfviewer; + +/** + * A tree model that uses a cos document. + * + * + * @author wurtz + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeModel; + +//import java.awt.event.*; +import javax.swing.event.TreeModelListener; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A class to model a PDF document as a tree structure. + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +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 TreeModelEvent + * 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 parent at index index + * in the parent's + * child array. parent must be a node previously obtained + * from this data source. This should not return null + * if index + * is a valid index for parent (that is index >= 0 && + * index < getChildCount(parent)). + * + * @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 parent at index index + * + */ + 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 = new ArrayList(dict.keySet()); + 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 parent. + * Returns 0 if the node + * is a leaf or if it has no children. parent 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 parent + * + */ + 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 parent + * is null or child is null, + * 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 + * child or parent are null + * + */ + 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 = new ArrayList(dict.keySet()); + Collections.sort( keys ); + for( int i=0; retval == -1 && inull + * only if the tree has no nodes. + * + * @return the root of the tree + * + */ + public Object getRoot() + { + return document.getDocument().getTrailer(); + } + + /** Returns true if node is a leaf. + * It is possible for this method to return false + * even if node 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 node 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 + * addTreeModelListener. + * + * @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 path to newValue. + * If newValue signifies a truly new value + * the model should post a treeNodesChanged 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 + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PageDrawer.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PageDrawer.java new file mode 100644 index 000000000..a07768bb7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/PageDrawer.java @@ -0,0 +1,547 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfviewer; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Area; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.Image; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDMatrix; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState; +import org.apache.pdfbox.pdmodel.graphics.PDShading; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; +import org.apache.pdfbox.pdmodel.text.PDTextState; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFStreamEngine; +import org.apache.pdfbox.util.ResourceLoader; +import org.apache.pdfbox.util.TextPosition; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSDictionary; + + +/** + * This will paint a page in a PDF document to a graphics context. + * + * @author Ben Litchfield + * @version $Revision: 1.22 $ + */ +public class PageDrawer extends PDFStreamEngine +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PageDrawer.class); + + private Graphics2D graphics; + protected Dimension pageSize; + protected PDPage page; + + private GeneralPath linePath = new GeneralPath(); + + /** + * 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( + "org/apache/pdfbox/resources/PageDrawer.properties", true ) ); + } + + /** + * 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 ); + graphics.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON ); + // Only if there is some content, we have to process it. + // Otherwise we are done here and we will produce an empty page + if ( page.getContents() != null) + { + PDResources resources = page.findResources(); + processStream( page, resources, page.getContents().getStream() ); + } + List annotations = page.getAnnotations(); + for( int i=0; iBen Litchfield + * @version $Revision: 1.5 $ + */ +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; + } + + /** + * {@inheritDoc} + */ + public void mouseDragged(MouseEvent e) + { + //do nothing when mouse moves. + } + + /** + * {@inheritDoc} + */ + public void mouseMoved(MouseEvent e) + { + //reader.getBottomStatusPanel().getStatusLabel().setText( e.getX() + "," + (pagePanel.getHeight() - e.getY()) ); + reader.getBottomStatusPanel().getStatusLabel().setText( e.getX() + "," + e.getY() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ReaderBottomPanel.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ReaderBottomPanel.java new file mode 100644 index 000000000..6efd6ca9e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/ReaderBottomPanel.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/package.html new file mode 100644 index 000000000..bfad6c8c0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfviewer/package.html @@ -0,0 +1,25 @@ + + + + + + + +The pdfviewer package contains classes to graphically display information about a PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSFilterInputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSFilterInputStream.java new file mode 100644 index 000000000..dbed0da0f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSFilterInputStream.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfwriter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + + +public class COSFilterInputStream extends FilterInputStream +{ + int[] byteRange; + long position = 0; + + public COSFilterInputStream(InputStream in, int[] byteRange) + { + super(in); + this.byteRange = byteRange; + } + + public COSFilterInputStream(byte[] in, int[] byteRange) + { + super(new ByteArrayInputStream(in)); + this.byteRange = byteRange; + } + + @Override + public int read() throws IOException + { + nextAvailable(); + int i = super.read(); + if (i>-1) + ++position; + return i; + } + + @Override + public int read(byte[] b) throws IOException + { + return read(b,0,b.length); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int c = read(); + if (c == -1) { + return -1; + } + b[off] = (byte)c; + + int i = 1; + try { + for (; i < len ; i++) { + c = read(); + if (c == -1) { + break; + } + b[off + i] = (byte)c; + } + } catch (IOException ee) { + } + return i; + } + + private boolean inRange() throws IOException { + long pos = position; + for (int i = 0; ipos) + { + return true; + } + } + return false; + } + + private void nextAvailable() throws IOException { + while (!inRange()) { + ++position; + if(super.read()<0) + break; + } + } + + public byte[] toByteArray() throws IOException + { + ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int c; + while ((c = this.read(buffer)) != -1) + byteOS.write(buffer, 0, c); + return byteOS.toByteArray(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSStandardOutputStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSStandardOutputStream.java new file mode 100644 index 000000000..eb8666629 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSStandardOutputStream.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfwriter; + + + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; + +import org.apache.pdfbox.util.StringUtil; + +/** + * simple output stream with some minor features for generating "pretty" + * pdf files. + * + * @author Michael Traut + * @version $Revision: 1.5 $ + */ +public class COSStandardOutputStream extends FilterOutputStream +{ + + /** + * To be used when 2 byte sequence is enforced. + */ + public static final byte[] CRLF = StringUtil.getBytes("\r\n"); + + /** + * Line feed character. + */ + public static final byte[] LF = StringUtil.getBytes("\n"); + + /** + * standard line separator. + */ + public static final byte[] EOL = StringUtil.getBytes("\n"); + + // current byte pos in the output stream + private long pos = 0; + // flag to prevent generating two newlines in sequence + private boolean onNewLine = false; + private FileChannel fileChannel = null; + private FileDescriptor fileDescriptor = null; + private long mark = -1; + + /** + * COSOutputStream constructor comment. + * + * @param out The underlying stream to write to. + */ + public COSStandardOutputStream(OutputStream out) + { + super(out); + if(out instanceof FileOutputStream) { + try { + fileChannel = ((FileOutputStream)out).getChannel(); + fileDescriptor = ((FileOutputStream)out).getFD(); + pos = fileChannel.position(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * This will get the current position in the stream. + * + * @return The current position in the stream. + */ + public long getPos() + { + return pos; + } + + /** + * This will get the current position in the stream. + * + * @return The current position in the stream. + * @throws IOException + */ + public void setPos(long pos) throws IOException + { + if(fileChannel!=null) { + checkPos(); + this.pos=pos; + fileChannel.position(pos); + } + } + + /** + * This will tell if we are on a newline. + * + * @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 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. + */ + @Override + public void write(byte[] b, int off, int len) throws IOException + { + checkPos(); + 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. + */ + @Override + public void write(int b) throws IOException + { + checkPos(); + 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); + } + + /** + * 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); + } + + public void mark() throws IOException + { + checkPos(); + mark = getPos(); + } + + public void reset() throws IOException + { + if(mark<0) + return; + setPos(mark); + } + + private void checkPos() throws IOException + { + if(fileChannel!=null && fileChannel.position() != getPos()) + throw new IOException("OutputStream has an invalid position"); + } + + public byte[] getFileInBytes(int[] byteRange) throws IOException + { + return null; + } + + public InputStream getFilterInputStream(int[] byteRange) + { + return new COSFilterInputStream(new FileInputStream(fileDescriptor), byteRange); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriter.java new file mode 100644 index 000000000..9e2caf1c6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriter.java @@ -0,0 +1,1362 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfwriter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.cos.ICOSVisitor; +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.exceptions.SignatureException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.SecurityHandler; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; +import org.apache.pdfbox.persistence.util.COSObjectKey; +import org.apache.pdfbox.util.StringUtil; + +/** + * 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 + * @version $Revision: 1.36 $ + */ +public class COSWriter implements ICOSVisitor +{ + /** + * The dictionary open token. + */ + public static final byte[] DICT_OPEN = StringUtil.getBytes("<<"); + /** + * The dictionary close token. + */ + public static final byte[] DICT_CLOSE = StringUtil.getBytes(">>"); + /** + * space character. + */ + public static final byte[] SPACE = StringUtil.getBytes(" "); + /** + * The start to a PDF comment. + */ + public static final byte[] COMMENT = StringUtil.getBytes("%"); + + /** + * The output version of the PDF. + */ + public static final byte[] VERSION = StringUtil.getBytes("PDF-1.4"); + /** + * 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 = StringUtil.getBytes("%%EOF"); + // pdf tokens + + /** + * The reference token. + */ + public static final byte[] REFERENCE = StringUtil.getBytes("R"); + /** + * The XREF token. + */ + public static final byte[] XREF = StringUtil.getBytes("xref"); + /** + * The xref free token. + */ + public static final byte[] XREF_FREE = StringUtil.getBytes("f"); + /** + * The xref used token. + */ + public static final byte[] XREF_USED = StringUtil.getBytes("n"); + /** + * The trailer token. + */ + public static final byte[] TRAILER = StringUtil.getBytes("trailer"); + /** + * The start xref token. + */ + public static final byte[] STARTXREF = StringUtil.getBytes("startxref"); + /** + * The starting object token. + */ + public static final byte[] OBJ = StringUtil.getBytes("obj"); + /** + * The end object token. + */ + public static final byte[] ENDOBJ = StringUtil.getBytes("endobj"); + /** + * The array open token. + */ + public static final byte[] ARRAY_OPEN = StringUtil.getBytes("["); + /** + * The array close token. + */ + public static final byte[] ARRAY_CLOSE = StringUtil.getBytes("]"); + /** + * The open stream token. + */ + public static final byte[] STREAM = StringUtil.getBytes("stream"); + /** + * The close stream token. + */ + public static final byte[] ENDSTREAM = StringUtil.getBytes("endstream"); + + 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 references 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(); + private Map keyObject = new Hashtable(); + + // the list of x ref entries to be made so far + private List xRefEntries = new ArrayList(); + private HashSet objectsToWriteSet = new HashSet(); + + //A list of objects to write. + private LinkedList objectsToWrite = new LinkedList(); + + //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 boolean willEncrypt = false; + + private boolean incrementalUpdate = false; + + private boolean reachedSignature = false; + + private int[] signaturePosition = new int[2]; + + private int[] byterangePosition = new int[2]; + + private FileInputStream in; + + /** + * COSWriter constructor comment. + * + * @param os The wrapped output stream. + */ + public COSWriter(OutputStream os) + { + super(); + setOutput(os); + setStandardOutput(new COSStandardOutputStream(output)); + formatDecimal.setMaximumFractionDigits( 10 ); + formatDecimal.setGroupingUsed( false ); + } + + /** + * COSWriter constructor for incremental updates. + * + * @param os The wrapped output stream. + */ + public COSWriter(OutputStream os, FileInputStream is) + { + this(os); + in = is; + incrementalUpdate = true; + } + + protected void prepareIncrement(PDDocument doc) + { + try + { + if (doc != null) + { + COSDocument cosDoc = doc.getDocument(); + + Map xrefTable = cosDoc.getXrefTable(); + Set keySet = xrefTable.keySet(); + long highestNumber=0; + for ( COSObjectKey cosObjectKey : keySet ) { + COSBase object = cosDoc.getObjectFromPool(cosObjectKey).getObject(); + if (object != null && cosObjectKey!= null && !(object instanceof COSNumber)) + { + objectKeys.put(object, cosObjectKey); + keyObject.put(cosObjectKey,object); + } + + long num = cosObjectKey.getNumber(); + if (num > highestNumber) + highestNumber=num; + } + setNumber(highestNumber); + // xrefTable.clear(); + + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 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 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 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.INFO ); + COSDictionary encrypt = (COSDictionary)trailer.getDictionaryObject( COSName.ENCRYPT ); + if( root != null ) + { + addObjectToWrite( root ); + } + if( info != null ) + { + addObjectToWrite( info ); + } + + while( objectsToWrite.size() > 0 ) + { + COSBase nextObject = (COSBase)objectsToWrite.removeFirst(); + objectsToWriteSet.remove(nextObject); + doWriteObject( nextObject ); + } + + + willEncrypt = false; + + if( encrypt != null ) + { + addObjectToWrite( encrypt ); + } + + while( objectsToWrite.size() > 0 ) + { + COSBase nextObject = (COSBase)objectsToWrite.removeFirst(); + objectsToWriteSet.remove(nextObject); + doWriteObject( nextObject ); + } + } + + private void addObjectToWrite( COSBase object ) + { + COSBase actual = object; + if( actual instanceof COSObject ) + { + actual = ((COSObject)actual).getObject(); + } + + if( !writtenObjects.contains( object ) && + !objectsToWriteSet.contains( object ) && + !actualsAdded.contains( actual ) ) + { + COSBase cosBase=null; + COSObjectKey cosObjectKey = null; + if(actual != null) + cosObjectKey= objectKeys.get(actual); + + if(cosObjectKey!=null) + cosBase = keyObject.get(cosObjectKey); + + if(actual != null && objectKeys.containsKey(actual) && + !object.isNeedToBeUpdate() && (cosBase!= null && + !cosBase.isNeedToBeUpdate())) + { + return; + } + + objectsToWrite.add( object ); + objectsToWriteSet.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 ); + if(obj instanceof COSDictionary) + { + COSDictionary dict = (COSDictionary)obj; + COSName item = (COSName)dict.getItem(COSName.TYPE); + if(COSName.SIG.equals(item)) { + reachedSignature = true; + } + } + + // 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("ISO-8859-1")); + getStandardOutput().write(SPACE); + getStandardOutput().write(String.valueOf(currentObjectKey.getGeneration()).getBytes("ISO-8859-1")); + 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("ISO-8859-1") ); + 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 = getXRefEntries().get( getXRefEntries().size()-1); + trailer.setInt(COSName.SIZE, (int)lastEntry.getKey().getNumber()+1); + // Only need to stay, if an incremental update will be performed + if (!incrementalUpdate) + trailer.removeItem( COSName.PREV ); + // Remove a checksum if present + trailer.removeItem( COSName.getPDFName("DocChecksum") ); + + /** + 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("ISO-8859-1")); + getStandardOutput().writeEOL(); + getStandardOutput().write(EOF); + getStandardOutput().writeEOL(); + } + + /** + * 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 + { + // sort xref, needed only if object keys not regenerated + Collections.sort(getXRefEntries()); + COSWriterXRefEntry lastEntry = 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 + writeXrefRange(0, lastEntry.getKey().getNumber() + 1); + // write initial start object with ref to first deleted object and magic generation number + writeXrefEntry(COSWriterXRefEntry.getNullEntry()); + // write entry for every object + long lastObjectNumber = 0; + for (Iterator i = getXRefEntries().iterator(); i.hasNext();) + { + COSWriterXRefEntry entry = i.next(); + while( lastObjectNumber0 && byterangePosition[1] > 0) + { + int left = (int)getStandardOutput().getPos()-signaturePosition[1]; + String newByteRange = "0 "+signaturePosition[0]+" "+signaturePosition[1]+" "+left+"]"; + int leftByterange = byterangePosition[1]-byterangePosition[0]-newByteRange.length(); + if(leftByterange<0) + throw new IOException("Can't write new ByteRange, not enough space"); + getStandardOutput().setPos(byterangePosition[0]); + getStandardOutput().write(newByteRange.getBytes()); + for(int i=0;i + * example: 0 1 2 5 6 7 8 10 + *

+ * will create a array with follow ranges + *

+ * 0 3 5 4 10 1 + *

+ * this mean that the element 0 is followed by two other related numbers + * that represent a cluster of the size 3. 5 is follow by three other + * related numbers and create a cluster of size 4. etc. + * + * @param xRefEntries list with the xRef entries that was written + * @return a integer array with the ranges + */ + protected Integer[] getXRefRanges(List xRefEntries) + { + int nr = 0; + int last = -2; + int count = 1; + + ArrayList list = new ArrayList(); + for( Object object : xRefEntries ) + { + nr = (int)((COSWriterXRefEntry)object).getKey().getNumber(); + if (nr == last + 1) + { + ++count; + last = nr; + } + else if (last == -2) + { + last = nr; + } + else + { + list.add(last - count + 1); + list.add(count); + last = nr; + count = 1; + } + } + // If no new entry is found, we need to write out the last result + if(xRefEntries.size() > 0) + { + list.add(last - count + 1); + list.add(count); + } + return list.toArray(new Integer[list.size()]); + } + + /** + * 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 = objectKeys.get(actual); + } + if( key == null ) + { + key = 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 = 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 if( current instanceof COSString ) + { + COSString copy = new COSString(((COSString)current).getString()); + copy.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 (Map.Entry entry : obj.entrySet()) + { + COSBase value = entry.getValue(); + if (value != null) + { + entry.getKey().accept(this); + getStandardOutput().write(SPACE); + if( value instanceof COSDictionary ) + { + COSDictionary dict = (COSDictionary)value; + + // write all XObjects as direct objects, this will save some size + COSBase item = dict.getItem(COSName.XOBJECT); + if(item!=null) + { + item.setDirect(true); + } + item = dict.getItem(COSName.RESOURCES); + if(item!=null) + { + item.setDirect(true); + } + + if(dict.isDirect()) + { + // If the object should be written direct, we need + // to pass the dictionary to the visitor again. + visitFromDictionary(dict); + } + else + { + addObjectToWrite( dict ); + writeReference( dict ); + } + } + 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 + { + // If we reach the pdf signature, we need to determinate the position of the + // content and byterange + if(reachedSignature && COSName.CONTENTS.equals(entry.getKey())) + { + signaturePosition = new int[2]; + signaturePosition[0] = (int)getStandardOutput().getPos(); + value.accept(this); + signaturePosition[1] = (int)getStandardOutput().getPos(); + } + else if(reachedSignature && COSName.BYTERANGE.equals(entry.getKey())) + { + byterangePosition = new int[2]; + byterangePosition[0] = (int)getStandardOutput().getPos()+1; + value.accept(this); + byterangePosition[1] = (int)getStandardOutput().getPos()-1; + reachedSignature = false; + } + else + { + value.accept(this); + } + } + getStandardOutput().writeEOL(); + + } + else + { + //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 + } + } + 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 + { + if(!incrementalUpdate) + doWriteHeader(doc); + doWriteBody(doc); + if(incrementalUpdate) + doWriteXRefInc(doc); + else + doWriteXRef(doc); + doWriteTrailer(doc); + doWriteSignature(doc); + + return null; + } + catch (IOException e) + { + throw new COSVisitorException(e); + } + catch (SignatureException 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("ISO-8859-1")); + getStandardOutput().write(SPACE); + getStandardOutput().write(String.valueOf(key.getGeneration()).getBytes("ISO-8859-1")); + 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 + { + InputStream input = null; + + try + { + if(willEncrypt) + { + document.getSecurityHandler().encryptStream( + obj, + currentObjectKey.getNumber(), + currentObjectKey.getGeneration()); + } + + 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( COSInteger.get( totalAmountWritten ) ); + getStandardOutput().writeCRLF(); + getStandardOutput().write(ENDSTREAM); + getStandardOutput().writeEOL(); + return null; + } + catch( Exception e ) + { + throw new COSVisitorException(e); + } + finally + { + if (input != null) + { + try + { + input.close(); + } + catch (IOException 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(willEncrypt) + { + document.getSecurityHandler().decryptString( + obj, + currentObjectKey.getNumber(), + currentObjectKey.getGeneration()); + } + + obj.writePDF( getStandardOutput() ); + } + catch (Exception 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(incrementalUpdate) + prepareIncrement(doc); + + // if the document says we should remove encryption, then we shouldn't encrypt + if(doc.isAllSecurityToBeRemoved()) + { + this.willEncrypt = false; + // also need to get rid of the "Encrypt" in the trailer so readers + // don't try to decrypt a document which is not encrypted + COSDocument cosDoc = doc.getDocument(); + COSDictionary trailer = cosDoc.getTrailer(); + trailer.removeItem(COSName.ENCRYPT); + } + else + { + SecurityHandler securityHandler = document.getSecurityHandler(); + if(securityHandler != null) + { + try + { + securityHandler.prepareDocumentForEncryption(document); + this.willEncrypt = true; + } + catch(IOException e) + { + throw new COSVisitorException( e ); + } + catch(CryptographyException e) + { + throw new COSVisitorException( e ); + } + } + else + { + this.willEncrypt = false; + } + } + + COSDocument cosDoc = document.getDocument(); + COSDictionary trailer = cosDoc.getTrailer(); + COSArray idArray = (COSArray)trailer.getDictionaryObject( COSName.ID ); + if( idArray == null || incrementalUpdate) + { + try + { + + //algorithm 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("ISO-8859-1") ); + COSDictionary info = (COSDictionary)trailer.getDictionaryObject( COSName.INFO ); + if( info != null ) + { + Iterator values = info.getValues().iterator(); + while( values.hasNext() ) + { + md.update( values.next().toString().getBytes("ISO-8859-1") ); + } + } + idArray = new COSArray(); + COSString id = new COSString( md.digest() ); + idArray.add( id ); + idArray.add( id ); + trailer.setItem( COSName.ID, idArray ); + } + catch( NoSuchAlgorithmException e ) + { + throw new COSVisitorException( e ); + } + catch( UnsupportedEncodingException e ) + { + throw new COSVisitorException( e ); + } + } + cosDoc.accept(this); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java new file mode 100644 index 000000000..3d11ab4fb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfwriter; + +import org.apache.pdfbox.persistence.util.COSObjectKey; + +import org.apache.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.7 $ + */ +public class COSWriterXRefEntry implements Comparable +{ + private long offset; + private COSBase object; + private COSObjectKey key; + private boolean free = false; + private static COSWriterXRefEntry nullEntry; + + + /** + * {@inheritDoc} + */ + public int compareTo(COSWriterXRefEntry obj) + { + if (obj instanceof COSWriterXRefEntry) + { + return (int)(getKey().getNumber() - obj.getKey().getNumber()); + } + else + { + return -1; + } + } + + /** + * This will return a null entry: 0000000000 65535 f + * + * @return null COSWriterXRefEntry + */ + public static COSWriterXRefEntry getNullEntry() + { + if (nullEntry == null) + { + nullEntry = new COSWriterXRefEntry(0, null, new COSObjectKey(0, 65535)); + nullEntry.setFree(true); + } + return nullEntry; + } + + /** + * 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/ContentStreamWriter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/ContentStreamWriter.java new file mode 100644 index 000000000..58de8c1f8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/ContentStreamWriter.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdfwriter; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.util.ImageParameters; +import org.apache.pdfbox.util.PDFOperator; + +/** + * A class that will take a list of tokens and write out a stream with them. + * + * @author Ben Litchfield + * @version $Revision: 1.8 $ + */ +public class ContentStreamWriter +{ + private OutputStream output; + /** + * space character. + */ + public static final byte[] SPACE = new byte[] { 32 }; + + /** + * standard line separator + */ + public static final byte[] EOL = new byte[] { 0x0A }; + + /** + * 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 entry : obj.entrySet()) + { + if (entry.getValue() != null) + { + writeObject( entry.getKey() ); + output.write( SPACE ); + writeObject( entry.getValue() ); + 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("ISO-8859-1") ); + ImageParameters params = op.getImageParameters(); + COSDictionary dic = params.getDictionary(); + for( COSName key : dic.keySet() ) + { + Object value = dic.getDictionaryObject( key ); + key.writePDF( output ); + output.write( SPACE ); + writeObject( value ); + output.write( EOL ); + } + output.write( "ID".getBytes("ISO-8859-1") ); + output.write( EOL ); + output.write( op.getImageData() ); + } + else + { + output.write( op.getOperation().getBytes("ISO-8859-1") ); + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/package.html new file mode 100644 index 000000000..e0b010eb5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdfwriter/package.html @@ -0,0 +1,25 @@ + + + + + + + +This is the persistence layer used to write the PDFBox documents to a stream. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/ConformingPDDocument.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/ConformingPDDocument.java new file mode 100644 index 000000000..e0bac4e5e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/ConformingPDDocument.java @@ -0,0 +1,115 @@ +/* + * Copyright 2011 adam. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * under the License. + */ + +package org.apache.pdfbox.pdmodel; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.pdfparser.ConformingPDFParser; +import org.apache.pdfbox.persistence.util.COSObjectKey; + +/** + * + * @author adam + */ +public class ConformingPDDocument extends PDDocument { + /** + * Maps ObjectKeys to a COSObject. Note that references to these objects + * are also stored in COSDictionary objects that map a name to a specific object. + */ + private final Map objectPool = + new HashMap(); + private ConformingPDFParser parser = null; + + public ConformingPDDocument() throws IOException { + super(); + } + + public ConformingPDDocument(COSDocument doc) throws IOException { + super(doc); + } + + /** + * This will load a document from an input stream. + * @param input The File which contains the document. + * @return The document that was loaded. + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load(File input) throws IOException { + ConformingPDFParser parser = new ConformingPDFParser(input); + parser.parse(); + return parser.getPDDocument(); + } + + /** + * 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 COSBase getObjectFromPool(COSObjectKey key) throws IOException { + return objectPool.get(key); + } + + /** + * 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 List getObjectKeysFromPool() throws IOException { + List keys = new ArrayList(); + for(COSObjectKey key : objectPool.keySet()) + keys.add(key); + return keys; + } + + /** + * This will get an object from the pool. + * @param number the object number + * @param generation the generation of this object you wish to load + * @return The object in the pool + * @throws IOException If there is an error getting the proxy object. + */ + public COSBase getObjectFromPool(long number, long generation) throws IOException { + return objectPool.get(new COSObjectKey(number, generation)); + } + + public void putObjectInPool(COSBase object, long number, long generation) { + objectPool.put(new COSObjectKey(number, generation), object); + } + + /** + * @return the parser + */ + public ConformingPDFParser getParser() { + return parser; + } + + /** + * @param parser the parser to set + */ + public void setParser(ConformingPDFParser parser) { + this.parser = parser; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDestinationNameTreeNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDestinationNameTreeNode.java new file mode 100644 index 000000000..ee36858c3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDestinationNameTreeNode.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDNameTreeNode; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination; + +/** + * This class holds all of the name trees that are available at the document level. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + } + + /** + * {@inheritDoc} + */ + 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( COSName.D ); + } + return PDDestination.create( destination ); + } + + /** + * {@inheritDoc} + */ + protected PDNameTreeNode createChildNode( COSDictionary dic ) + { + return new PDDestinationNameTreeNode(dic); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocument.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocument.java new file mode 100644 index 000000000..a02bf53b4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocument.java @@ -0,0 +1,1378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.exceptions.SignatureException; +import org.apache.pdfbox.io.RandomAccess; +import org.apache.pdfbox.pdfparser.PDFParser; +import org.apache.pdfbox.pdfwriter.COSWriter; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.apache.pdfbox.pdmodel.encryption.BadSecurityHandlerException; +import org.apache.pdfbox.pdmodel.encryption.DecryptionMaterial; +import org.apache.pdfbox.pdmodel.encryption.PDEncryptionDictionary; +import org.apache.pdfbox.pdmodel.encryption.ProtectionPolicy; +import org.apache.pdfbox.pdmodel.encryption.SecurityHandler; +import org.apache.pdfbox.pdmodel.encryption.SecurityHandlersManager; +import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial; +import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; + +/** + * This is the in-memory representation of the PDF document. You need to call + * close() on this object when you are done using it!! + *

+ * This class implements the {@link Pageable} interface, but since PDFBox + * version 1.3.0 you should be using the {@link PDPageable} adapter instead + * (see PDFBOX-788). + * + * @author Ben Litchfield + * @version $Revision: 1.47 $ + */ +public class PDDocument implements Pageable +{ + + private COSDocument document; + + //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; + + /** + * The security handler used to decrypt / encrypt the document. + */ + private SecurityHandler securityHandler = null; + + + /** + * This assocates object ids with a page number. It's used to determine + * the page number for bookmarks (or page numbers for anything else for + * which you have an object id for that matter). + */ + private Map pageMap = null; + + /** + * This will hold a flag which tells us if we should remove all security + * from this documents + */ + private boolean allSecurityToBeRemoved = 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, COSInteger.ZERO ); + } + + private void generatePageMap() + { + pageMap = new HashMap(); + // these page nodes could be references to pages, + // or references to arrays which have references to pages + // or references to arrays which have references to arrays which have references to pages + // or ... (I think you get the idea...) + processListOfPageReferences(getDocumentCatalog().getPages().getKids()); + } + + private void processListOfPageReferences(List pageNodes) + { + for(int i=0; i < pageNodes.size(); ++i) + { + Object pageOrArray = pageNodes.get(i); + if(pageOrArray instanceof PDPage) + { + List pageArray = ((((PDPage)pageOrArray).getParent()).getKids()); + parseCatalogObject((COSObject)pageArray.get(i)); + } + else if(pageOrArray instanceof PDPageNode) + { + processListOfPageReferences(((PDPageNode)pageOrArray).getKids()); + } + } + } + + /** + * This will either add the page passed in, or, if it's a pointer to an array + * of pages, it'll recursivly call itself and process everything in the list. + */ + private void parseCatalogObject(COSObject thePageOrArrayObject) + { + COSBase arrayCountBase = thePageOrArrayObject.getItem(COSName.COUNT); + int arrayCount = -1; + if(arrayCountBase instanceof COSInteger) + { + arrayCount = ((COSInteger)arrayCountBase).intValue(); + } + + COSBase kidsBase = thePageOrArrayObject.getItem(COSName.KIDS); + int kidsCount = -1; + if(kidsBase instanceof COSArray) + { + kidsCount = ((COSArray)kidsBase).size(); + } + + if(arrayCount == -1 || kidsCount == -1) + { + // these cases occur when we have a page, not an array of pages + String objStr = String.valueOf(thePageOrArrayObject.getObjectNumber().intValue()); + String genStr = String.valueOf(thePageOrArrayObject.getGenerationNumber().intValue()); + getPageMap().put(objStr+","+genStr, new Integer(getPageMap().size()+1)); + } + else + { + // we either have an array of page pointers, or an array of arrays + if(arrayCount == kidsCount) + { + // process the kids... they're all references to pages + COSArray kidsArray = ((COSArray)kidsBase); + for(int i=0; i getPageMap() + { + if (pageMap == null) + { + generatePageMap(); + } + return pageMap; + } + + /** + * 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(); + } + + public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface) throws IOException, SignatureException + { + SignatureOptions defaultOptions = new SignatureOptions(); + defaultOptions.setPage(1); + addSignature(sigObject, signatureInterface,defaultOptions); + } + + /** + * This will add a signature to the document. + * + * @param sigObject is the PDSignature model + * @param signatureInterface is a interface which provides signing capabilities + * @param options + * @throws IOException if there is an error creating required fields + */ + public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface, SignatureOptions options) throws IOException, SignatureException + { + // Reserve content + // We need to reserve some space for the signature. Some signatures including + // big certificate chain and we need enough space to store it. + int preferedSignatureSize = options.getPreferedSignatureSize(); + if (preferedSignatureSize > 0) + { + sigObject.setContents(new byte[preferedSignatureSize * 2 + 2]); + } + else + { + sigObject.setContents(new byte[0x2500 * 2 + 2]); + } + + // Reserve ByteRange + sigObject.setByteRange(new int[] {0,1000000000,1000000000,1000000000}); + + getDocument().setSignatureInterface(signatureInterface); + + // ######################################### + // # Create SignatureForm for signature # + // # and appending it to the document # + // ######################################### + + // Get the first page + PDDocumentCatalog root = getDocumentCatalog(); + PDPageNode rootPages = root.getPages(); + List kids = new ArrayList(); + rootPages.getAllKids(kids); + + int size = (int)rootPages.getCount(); + PDPage page = null; + if (size == 0) + { + throw new SignatureException(SignatureException.INVALID_PAGE_FOR_SIGNATURE, "The PDF file has no pages"); + } + if (options.getPage()>size) + { + page = kids.get(size-1); + } + else if(options.getPage()<=0) + { + page = kids.get(0); + } + else + { + page = kids.get(options.getPage()-1); + } + + + // Get the AcroForm from the Root-Dictionary and append the annotation + PDAcroForm acroForm = root.getAcroForm(); + root.getCOSObject().setNeedToBeUpdate(true); + + if (acroForm==null) + { + acroForm = new PDAcroForm(this); + root.setAcroForm(acroForm); + } + else + { + acroForm.getCOSObject().setNeedToBeUpdate(true); + } + + /* + * For invisible signatures, the annotation has a rectangle array with values [ 0 0 0 0 ]. + * This annotation is usually attached to the viewed page when the signature is created. + * Despite not having an appearance, the annotation AP and N dictionaries may be present + * in some versions of Acrobat. If present, N references the DSBlankXObj (blank) XObject. + */ + + // Create Annotation / Field for signature + PDSignatureField signatureField = new PDSignatureField(acroForm); + signatureField.setSignature(sigObject); // append the signature object + signatureField.getWidget().setPage(page); // backward linking + + // Set the AcroForm Fields + List acroFormFields = acroForm.getFields(); + COSDictionary acroFormDict = acroForm.getDictionary(); + acroFormDict.setDirect(true); + acroFormDict.setInt(COSName.SIG_FLAGS, 3); + acroFormFields.add(signatureField); + + // Get the object from the visual signature + COSDocument visualSignature = options.getVisualSignature(); + + // Distinction of case for visual and non-visual signature + if (visualSignature == null) // non-visual signature + { + // Set rectangle for non-visual signature to 0 0 0 0 + signatureField.getWidget().setRectangle(new PDRectangle()); // rectangle array [ 0 0 0 0 ] + // Clear AcroForm / Set DefaultRessource + acroFormDict.setItem(COSName.DR, null); + // Set empty Appearance-Dictionary + PDAppearanceDictionary ap = new PDAppearanceDictionary(); + COSStream apsStream = new COSStream(getDocument().getScratchFile()); + apsStream.createUnfilteredStream(); + PDAppearanceStream aps = new PDAppearanceStream(apsStream); + COSDictionary cosObject = (COSDictionary)aps.getCOSObject(); + cosObject.setItem(COSName.SUBTYPE, COSName.FORM); + cosObject.setItem(COSName.BBOX, new PDRectangle()); + + ap.setNormalAppearance(aps); + ap.getDictionary().setDirect(true); + signatureField.getWidget().setAppearance(ap); + } + else // visual signature + { + // Obtain visual signature object + List cosObjects = visualSignature.getObjects(); + + boolean annotNotFound = true; + boolean sigFieldNotFound = true; + + for ( COSObject cosObject : cosObjects ) + { + COSBase base = cosObject.getObject(); + if (base != null && base instanceof COSDictionary) + { + COSBase ft = ((COSDictionary)base).getItem(COSName.FT); + COSBase type = ((COSDictionary)base).getItem(COSName.TYPE); + COSBase apDict = ((COSDictionary)base).getItem(COSName.AP); + + // Search for signature annotation + if (annotNotFound && COSName.ANNOT.equals(type)) + { + COSDictionary cosBaseDict = (COSDictionary)base; + + // Read and set the Rectangle for visual signature + COSArray rectAry = (COSArray)cosBaseDict.getItem(COSName.RECT); + PDRectangle rect = new PDRectangle(rectAry); + signatureField.getWidget().setRectangle(rect); + annotNotFound = false; + } + + // Search for Signature-Field + if (sigFieldNotFound && COSName.SIG.equals(ft) && apDict != null) + { + COSDictionary cosBaseDict = (COSDictionary)base; + + // Appearance Dictionary auslesen und setzen + PDAppearanceDictionary ap = new PDAppearanceDictionary((COSDictionary)cosBaseDict.getItem(COSName.AP)); + ap.getDictionary().setDirect(true); + signatureField.getWidget().setAppearance(ap); + + // AcroForm DefaultRessource auslesen und setzen + COSBase dr = cosBaseDict.getItem(COSName.DR); + dr.setDirect(true); + dr.setNeedToBeUpdate(true); + acroFormDict.setItem(COSName.DR, dr); + sigFieldNotFound=false; + } + } + } + + if (annotNotFound || sigFieldNotFound ) + { + throw new SignatureException(SignatureException.VISUAL_SIGNATURE_INVALID, "Could not read all needed objects from template"); + } + } + + // Get the annotations of the page and append the signature-annotation to it + List annotations = page.getAnnotations(); + if (annotations == null) + { + annotations = new COSArrayList(); + page.setAnnotations(annotations); + } + // take care that page and acroforms do not share the same array (if so, we don't need to add it twice) + if (!(annotations instanceof COSArrayList) + || !(acroFormFields instanceof COSArrayList) + || !((COSArrayList)annotations).toList().equals(((COSArrayList)acroFormFields).toList())) + { + annotations.add(signatureField.getWidget()); + } + page.getCOSObject().setNeedToBeUpdate(true); + } + + /** + * 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(); + if(src != null) + { + 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(); + COSBase dictionary = trailer.getDictionaryObject( COSName.ROOT ); + if (dictionary instanceof COSDictionary) { + documentCatalog = + new PDDocumentCatalog(this, (COSDictionary) dictionary); + } else { + documentCatalog = new PDDocumentCatalog(this); + } + + } + 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 ) + { + if( isEncrypted() ) + { + encParameters = new PDEncryptionDictionary(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; + } + + public PDSignature getSignatureDictionary() throws IOException + { + COSDictionary signatureDictionary = document.getLastSignatureDictionary(); + + if (signatureDictionary!= null) + { + return new PDSignature(signatureDictionary); + } + return null; + } + + /** + * 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. + * + * @deprecated + */ + @Deprecated + public boolean isUserPassword( String password ) throws IOException, CryptographyException + { + return false; + /*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("ISO-8859-1"), + 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. + * + * @deprecated + */ + @Deprecated + public boolean isOwnerPassword( String password ) throws IOException, CryptographyException + { + return false; + /*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("ISO-8859-1"), + 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. This method is provided for compatibility reasons only. User should use + * the new security layer instead and the openProtection method especially. + * + * @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 + { + try + { + StandardDecryptionMaterial m = new StandardDecryptionMaterial(password); + this.openProtection(m); + document.dereferenceObjectStreams(); + } + catch(BadSecurityHandlerException e) + { + throw new CryptographyException(e); + } + } + + /** + * 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. + * + * @deprecated use getCurrentAccessPermission instead + */ + @Deprecated + public boolean wasDecryptedWithOwnerPassword() + { + return false; + } + + /** + * This will mark a document to be encrypted. The actual encryption + * will occur when the document is saved. + * This method is provided for compatibility reasons only. User should use + * the new security layer instead and the openProtection method especially. + * + * @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 + { + try + { + StandardProtectionPolicy policy = + new StandardProtectionPolicy(ownerPassword, userPassword, new AccessPermission()); + this.protect(policy); + } + catch(BadSecurityHandlerException e) + { + throw new CryptographyException(e); + } + } + + + /** + * 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. + * + * @deprecated Do not rely on this method anymore. + */ + @Deprecated + public String getOwnerPasswordForEncryption() + { + return null; + } + + /** + * 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. + * + * @deprecated Do not rely on this method anymore. + */ + @Deprecated + public String getUserPasswordForEncryption() + { + return null; + } + + /** + * 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. + * + * @deprecated Do not rely on this method anymore. It is the responsibility of + * COSWriter to hold this state + */ + @Deprecated + public boolean willEncryptWhenSaving() + { + return false; + } + + /** + * This shoule only be called by the COSWriter after encryption has completed. + * + * @deprecated Do not rely on this method anymore. It is the responsability of + * COSWriter to hold this state. + */ + @Deprecated + public void clearWillEncryptWhenSaving() + { + //method is deprecated. + } + + /** + * This will load a document from a url. + * + * @param url The url to load the PDF from. + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load( URL url ) throws IOException + { + return load( url.openStream() ); + } + /** + * This will load a document from a url. Used for skipping corrupt + * pdf objects + * + * @param url The url to load the PDF from. + * @param force When true, the parser will skip corrupt pdf objects and + * will continue parsing at the next object in the file + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load(URL url, boolean force) throws IOException + { + return load(url.openStream(), force); + } + + /** + * This will load a document from a url. + * + * @param url The url to load the PDF from. + * @param scratchFile A location to store temp PDFBox data for this document. + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load( URL url, RandomAccess scratchFile ) throws IOException + { + return load( url.openStream(), scratchFile ); + } + + /** + * 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 FileInputStream( filename ) ); + } + + /** + * This will load a document from a file. Allows for skipping corrupt pdf + * objects + * + * @param filename The name of the file to load. + * @param force When true, the parser will skip corrupt pdf objects and + * will continue parsing at the next object in the file + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load(String filename, boolean force) throws IOException + { + return load(new FileInputStream( filename ), force); + } + + /** + * This will load a document from a file. + * + * @param filename The name of the file to load. + * @param scratchFile A location to store temp PDFBox data for this document. + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load( String filename, RandomAccess scratchFile ) throws IOException + { + return load( new FileInputStream( filename ), scratchFile ); + } + + /** + * 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 FileInputStream( file ) ); + } + + /** + * This will load a document from a file. + * + * @param file The name of the file to load. + * @param scratchFile A location to store temp PDFBox data for this document. + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load( File file, RandomAccess scratchFile ) throws IOException + { + return load( new FileInputStream( file ), scratchFile ); + } + + /** + * 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 + { + return load( input, null ); + } + + /** + * This will load a document from an input stream. + * Allows for skipping corrupt pdf objects + * + * @param input The stream that contains the document. + * @param force When true, the parser will skip corrupt pdf objects and + * will continue parsing at the next object in the file + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load(InputStream input, boolean force) throws IOException + { + return load(input, null, force); + } + + /** + * This will load a document from an input stream. + * + * @param input The stream that contains the document. + * @param scratchFile A location to store temp PDFBox data for this document. + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load( InputStream input, RandomAccess scratchFile ) throws IOException + { + PDFParser parser = new PDFParser( new BufferedInputStream( input ) , scratchFile ); + parser.parse(); + return parser.getPDDocument(); + } + + /** + * This will load a document from an input stream. Allows for skipping corrupt pdf objects + * + * @param input The stream that contains the document. + * @param scratchFile A location to store temp PDFBox data for this document. + * @param force When true, the parser will skip corrupt pdf objects and + * will continue parsing at the next object in the file + * + * @return The document that was loaded. + * + * @throws IOException If there is an error reading from the stream. + */ + public static PDDocument load(InputStream input, RandomAccess scratchFile, boolean force) throws IOException + { + PDFParser parser = new PDFParser( new BufferedInputStream( input ), scratchFile, force); + 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(); + } + } + } + + public void saveIncremental( String fileName ) throws IOException, COSVisitorException + { + saveIncremental( new FileInputStream( fileName ) , new FileOutputStream( fileName , true) ); + } + + public void saveIncremental( FileInputStream input, 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 + { + // Sometimes the original file will be missing a newline at the end + // In order to avoid having %%EOF the first object on the same line + // as the %%EOF, we put a newline here. If there's already one at + // the end of the file, an extra one won't hurt. PDFBOX-1051 + output.write("\r\n".getBytes()); + writer = new COSWriter( output, input ); + 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! + */ + @Deprecated + public int getPageCount() + { + return getNumberOfPages(); + } + + /** + * {@inheritDoc} + */ + public int getNumberOfPages() + { + PDDocumentCatalog cat = getDocumentCatalog(); + return (int)cat.getPages().getCount(); + } + + /** + * Returns the format of the page at the given index when using a + * default printer job returned by {@link PrinterJob#getPrinterJob()}. + * + * @deprecated Use the {@link PDPageable} adapter class + * @param i page index, zero-based + * @return page format + * @throws IndexOutOfBoundsException if the page index is invalid + */ + @Deprecated + public PageFormat getPageFormat(int pageIndex) + { + try { + PrinterJob printerJob = PrinterJob.getPrinterJob(); + return new PDPageable(this, printerJob).getPageFormat(pageIndex); + } catch (PrinterException e) { + throw new RuntimeException(e); + } + } + + /** + * {@inheritDoc} + */ + public Printable getPrintable(int pageIndex) + { + return (Printable)getDocumentCatalog().getAllPages().get( pageIndex ); + } + + /** + * @see PDDocument#print() + * + * @param printJob The printer job. + * + * @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(PrinterJob printJob) throws PrinterException + { + print(printJob, false); + } + + /** + * This will send the PDF document to a printer. The printing functionality + * depends on the org.apache.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 + { + print( PrinterJob.getPrinterJob() ); + } + + /** + * This will send the PDF to the default printer without prompting the user + * for any printer settings. + * + * @see PDDocument#print() + * + * @throws PrinterException If there is an error while printing. + */ + public void silentPrint() throws PrinterException + { + silentPrint( PrinterJob.getPrinterJob() ); + } + + /** + * This will send the PDF to the default printer without prompting the user + * for any printer settings. + * + * @param printJob A printer job definition. + * @see PDDocument#print() + * + * @throws PrinterException If there is an error while printing. + */ + public void silentPrint( PrinterJob printJob ) throws PrinterException + { + print(printJob, true); + } + + private void print(PrinterJob job, boolean silent) throws PrinterException { + if (job == null) { + throw new PrinterException("The given printer job is null."); + } else { + job.setPageable(new PDPageable(this, job)); + if (silent || job.printDialog()) { + job.print(); + } + } + } + + /** + * This will close the underlying COSDocument object. + * + * @throws IOException If there is an error releasing resources. + */ + public void close() throws IOException + { + document.close(); + } + + + /** + * Protects the document with the protection policy pp. The document content will be really encrypted + * when it will be saved. This method only marks the document for encryption. + * + * @see org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy + * @see org.apache.pdfbox.pdmodel.encryption.PublicKeyProtectionPolicy + * + * @param pp The protection policy. + * + * @throws BadSecurityHandlerException If there is an error during protection. + */ + public void protect(ProtectionPolicy pp) throws BadSecurityHandlerException + { + SecurityHandler handler = SecurityHandlersManager.getInstance().getSecurityHandler(pp); + securityHandler = handler; + } + + /** + * Tries to decrypt the document in memory using the provided decryption material. + * + * @see org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial + * @see org.apache.pdfbox.pdmodel.encryption.PublicKeyDecryptionMaterial + * + * @param pm The decryption material (password or certificate). + * + * @throws BadSecurityHandlerException If there is an error during decryption. + * @throws IOException If there is an error reading cryptographic information. + * @throws CryptographyException If there is an error during decryption. + */ + public void openProtection(DecryptionMaterial pm) + throws BadSecurityHandlerException, IOException, CryptographyException + { + PDEncryptionDictionary dict = this.getEncryptionDictionary(); + if(dict.getFilter() != null) + { + securityHandler = SecurityHandlersManager.getInstance().getSecurityHandler(dict.getFilter()); + securityHandler.decryptDocument(this, pm); + document.dereferenceObjectStreams(); + document.setEncryptionDictionary( null ); + } + else + { + throw new RuntimeException("This document does not need to be decrypted"); + } + } + + /** + * Returns the access permissions granted when the document was decrypted. + * If the document was not decrypted this method returns the access permission + * for a document owner (ie can do everything). + * The returned object is in read only mode so that permissions cannot be changed. + * Methods providing access to content should rely on this object to verify if the current + * user is allowed to proceed. + * + * @return the access permissions for the current user on the document. + */ + + public AccessPermission getCurrentAccessPermission() + { + if(this.securityHandler == null) + { + return AccessPermission.getOwnerAccessPermission(); + } + return securityHandler.getCurrentAccessPermission(); + } + + /** + * Get the security handler that is used for document encryption. + * + * @return The handler used to encrypt/decrypt the document. + */ + public SecurityHandler getSecurityHandler() + { + return securityHandler; + } + + public boolean isAllSecurityToBeRemoved() { + return allSecurityToBeRemoved; + } + + public void setAllSecurityToBeRemoved(boolean allSecurityToBeRemoved) { + this.allSecurityToBeRemoved = allSecurityToBeRemoved; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentCatalog.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentCatalog.java new file mode 100644 index 000000000..318097e4f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentCatalog.java @@ -0,0 +1,630 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDDestinationOrAction; +import org.apache.pdfbox.pdmodel.common.PDMetadata; +import org.apache.pdfbox.pdmodel.common.PDPageLabels; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot; +import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; +import org.apache.pdfbox.pdmodel.interactive.action.PDDocumentCatalogAdditionalActions; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDURIDictionary; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.pagenavigation.PDThread; +import org.apache.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences; + +/** + * This class represents the acroform of a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.21 $ + */ +public class PDDocumentCatalog implements COSObjectable +{ + private COSDictionary root; + private PDDocument document; + + private PDAcroForm acroForm = null; + + /** + * Page mode where neither the outline nor the thumbnails + * are displayed. + */ + public static final String PAGE_MODE_USE_NONE = "UseNone"; + /** + * Show bookmarks when pdf is opened. + */ + public static final String PAGE_MODE_USE_OUTLINES = "UseOutlines"; + /** + * Show thumbnails when pdf is opened. + */ + public static final String PAGE_MODE_USE_THUMBS = "UseThumbs"; + /** + * Full screen mode with no menu bar, window controls. + */ + public static final String PAGE_MODE_FULL_SCREEN = "FullScreen"; + /** + * Optional content group panel is visible when opened. + */ + public static final String PAGE_MODE_USE_OPTIONAL_CONTENT = "UseOC"; + /** + * Attachments panel is visible. + */ + public static final String PAGE_MODE_USE_ATTACHMENTS = "UseAttachments"; + + /** + * Display one page at a time. + */ + public static final String PAGE_LAYOUT_SINGLE_PAGE = "SinglePage"; + /** + * Display the pages in one column. + */ + public static final String PAGE_LAYOUT_ONE_COLUMN = "OneColumn"; + /** + * Display the pages in two columns, with odd numbered pagse on the left. + */ + public static final String PAGE_LAYOUT_TWO_COLUMN_LEFT = "TwoColumnLeft"; + /** + * Display the pages in two columns, with odd numbered pagse on the right. + */ + public static final String PAGE_LAYOUT_TWO_COLUMN_RIGHT ="TwoColumnRight"; + /** + * Display the pages two at a time, with odd-numbered pages on the left. + * @since PDF Version 1.5 + */ + public static final String PAGE_LAYOUT_TWO_PAGE_LEFT = "TwoPageLeft"; + /** + * Display the pages two at a time, with odd-numbered pages on the right. + * @since PDF Version 1.5 + */ + public static final String PAGE_LAYOUT_TWO_PAGE_RIGHT = "TwoPageRight"; + + + + /** + * Constructor. + * + * @param doc The document that this catalog is part of. + */ + public PDDocumentCatalog( PDDocument doc ) + { + document = doc; + root = new COSDictionary(); + root.setItem( COSName.TYPE, COSName.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, acroFormDic ); + } + } + return acroForm; + } + + /** + * Set the acro form for this catalog. + * + * @param acro The new acro form. + */ + public void setAcroForm( PDAcroForm acro ) + { + root.setItem( COSName.ACRO_FORM, acro ); + } + + /** + * 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( COSName.VIEWER_PREFERENCES ); + 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( COSName.VIEWER_PREFERENCES, 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( COSName.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( COSName.OUTLINES, outlines ); + } + + /** + * Get the list threads for this pdf document. + * + * @return A list of PDThread objects. + */ + public List getThreads() + { + COSArray array = (COSArray)root.getDictionaryObject( COSName.THREADS ); + if( array == null ) + { + array = new COSArray(); + root.setItem( COSName.THREADS, array ); + } + List pdObjects = new ArrayList(); + for( int i=0; iBen Litchfield + * @version $Revision: 1.12 $ + */ +public class PDDocumentInformation implements COSObjectable +{ + 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( COSName.TITLE ); + } + + /** + * This will set the title of the document. + * + * @param title The new title for the document. + */ + public void setTitle( String title ) + { + info.setString( COSName.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( COSName.AUTHOR ); + } + + /** + * This will set the author of the document. + * + * @param author The new author for the document. + */ + public void setAuthor( String author ) + { + info.setString( COSName.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( COSName.SUBJECT ); + } + + /** + * This will set the subject of the document. + * + * @param subject The new subject for the document. + */ + public void setSubject( String subject ) + { + info.setString( COSName.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( COSName.KEYWORDS ); + } + + /** + * This will set the keywords of the document. + * + * @param keywords The new keywords for the document. + */ + public void setKeywords( String keywords ) + { + info.setString( COSName.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( COSName.CREATOR ); + } + + /** + * This will set the creator of the document. + * + * @param creator The new creator for the document. + */ + public void setCreator( String creator ) + { + info.setString( COSName.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( COSName.PRODUCER ); + } + + /** + * This will set the producer of the document. + * + * @param producer The new producer for the document. + */ + public void setProducer( String producer ) + { + info.setString( COSName.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( COSName.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( COSName.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( COSName.MOD_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( COSName.MOD_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( COSName.TRAPPED ); + } + + /** + * This will get the keys of all metadata information fields for the document. + * + * @return all metadata key strings. + * @since Apache PDFBox 1.3.0 + */ + public Set getMetadataKeys() + { + Set keys = new TreeSet(); + for (COSName key : info.keySet()) { + keys.add(key.getName()); + } + return keys; + } + + /** + * This will get the value of a custom metadata information field for the document. + * This will return null if one is not found. + * + * @param fieldName Name of custom metadata field from pdf document. + * + * @return String Value of metadata field + * + * @author Gerardo Ortiz + */ + public String getCustomMetadataValue(String fieldName) + { + return info.getString( fieldName ); + } + + /** + * Set the custom metadata value. + * + * @param fieldName The name of the custom metadata field. + * @param fieldValue The value to the custom metadata field. + */ + public void setCustomMetadataValue( String fieldName, String fieldValue ) + { + info.setString( fieldName, fieldValue ); + } + + /** + * 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( COSName.TRAPPED, value ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentNameDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentNameDictionary.java new file mode 100644 index 000000000..37579c97f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDDocumentNameDictionary.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class holds all of the name trees that are available at the document level. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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( COSName.DESTS ); + + //The document catalog also contains the Dests entry sometimes + //so check there as well. + if( dic == null ) + { + dic = (COSDictionary)catalog.getCOSDictionary().getDictionaryObject( COSName.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( COSName.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( COSName.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( COSName.EMBEDDED_FILES ); + + 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( COSName.EMBEDDED_FILES, ef ); + } + + /** + * Get the document level javascript entries. The value in this name tree will be PDTextStream. + * + * @return The document level named javascript. + */ + public PDJavascriptNameTreeNode getJavaScript() + { + PDJavascriptNameTreeNode retval = null; + + COSDictionary dic = (COSDictionary)nameDictionary.getDictionaryObject( COSName.JAVA_SCRIPT ); + + if( dic != null ) + { + retval = new PDJavascriptNameTreeNode( dic ); + } + + return retval; + } + + /** + * Set the named javascript entries that are associated with this document. + * + * @param js The new Javascript entries. + */ + public void setJavascript( PDJavascriptNameTreeNode js ) + { + nameDictionary.setItem( COSName.JAVA_SCRIPT, js ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java new file mode 100644 index 000000000..241b67032 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDEmbeddedFilesNameTreeNode.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.common.PDNameTreeNode; +import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; + +/** + * This class holds all of the name trees that are available at the document level. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 ); + } + + /** + * {@inheritDoc} + */ + protected Object convertCOSToPD( COSBase base ) throws IOException + { + COSBase destination = base; + return new PDComplexFileSpecification( (COSDictionary)destination ); + } + + /** + * {@inheritDoc} + */ + protected PDNameTreeNode createChildNode( COSDictionary dic ) + { + return new PDEmbeddedFilesNameTreeNode(dic); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDJavascriptNameTreeNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDJavascriptNameTreeNode.java new file mode 100644 index 000000000..135cd350e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDJavascriptNameTreeNode.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdmodel.common.PDNameTreeNode; +import org.apache.pdfbox.pdmodel.common.PDTextStream; + +/** + * This class holds all of the name trees that are available at the document level. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class PDJavascriptNameTreeNode extends PDNameTreeNode +{ + /** + * Constructor. + */ + public PDJavascriptNameTreeNode() + { + super( PDTextStream.class ); + } + + /** + * Constructor. + * + * @param dic The COS dictionary. + */ + public PDJavascriptNameTreeNode( COSDictionary dic ) + { + super( dic, PDTextStream.class ); + } + + /** + * {@inheritDoc} + */ + protected Object convertCOSToPD( COSBase base ) throws IOException + { + PDTextStream stream = null; + if( base instanceof COSString ) + { + stream = new PDTextStream((COSString)base); + } + else if( base instanceof COSStream ) + { + stream = new PDTextStream((COSStream)base); + } + else + { + throw new IOException( "Error creating Javascript object, expected either COSString or COSStream and not " + + base ); + } + return stream; + } + + /** + * {@inheritDoc} + */ + protected PDNameTreeNode createChildNode( COSDictionary dic ) + { + return new PDJavascriptNameTreeNode(dic); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDPage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDPage.java new file mode 100644 index 000000000..6061476ff --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDPage.java @@ -0,0 +1,850 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDMetadata; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.interactive.action.PDPageAdditionalActions; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.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.image.ImagingOpException; +import java.awt.print.PageFormat; +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. + *

+ * This class implements the {@link Printable} interface, but since PDFBox + * version 1.3.0 you should be using the {@link PDPageable} adapter instead + * (see PDFBOX-788). + * + * @author Ben Litchfield + * @version $Revision: 1.29 $ + */ +public class PDPage implements COSObjectable, Printable +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDPage.class); + + private static final int DEFAULT_USER_SPACE_UNIT_DPI = 72; + + private static final float MM_TO_UNITS = 1/(10*2.54f)*DEFAULT_USER_SPACE_UNIT_DPI; + + /** + * Fully transparent that can fall back to white when image type has no alpha. + */ + private static final Color TRANSPARENT_WHITE = new Color( 255, 255, 255, 0 ); + + private COSDictionary page; + + /** + * A page size of LETTER or 8.5x11. + */ + public static final PDRectangle PAGE_SIZE_LETTER = new PDRectangle( 8.5f*DEFAULT_USER_SPACE_UNIT_DPI, 11f*DEFAULT_USER_SPACE_UNIT_DPI ); + /** + * A page size of A0 Paper. + */ + public static final PDRectangle PAGE_SIZE_A0 = new PDRectangle( 841*MM_TO_UNITS, 1189*MM_TO_UNITS ); + /** + * A page size of A1 Paper. + */ + public static final PDRectangle PAGE_SIZE_A1 = new PDRectangle( 594*MM_TO_UNITS, 841*MM_TO_UNITS ); + /** + * A page size of A2 Paper. + */ + public static final PDRectangle PAGE_SIZE_A2 = new PDRectangle( 420*MM_TO_UNITS, 594*MM_TO_UNITS ); + /** + * A page size of A3 Paper. + */ + public static final PDRectangle PAGE_SIZE_A3 = new PDRectangle( 297*MM_TO_UNITS, 420*MM_TO_UNITS ); + /** + * A page size of A4 Paper. + */ + public static final PDRectangle PAGE_SIZE_A4 = new PDRectangle( 210*MM_TO_UNITS, 297*MM_TO_UNITS ); + /** + * A page size of A5 Paper. + */ + public static final PDRectangle PAGE_SIZE_A5 = new PDRectangle( 148*MM_TO_UNITS, 210*MM_TO_UNITS ); + /** + * A page size of A6 Paper. + */ + public static final PDRectangle PAGE_SIZE_A6 = new PDRectangle( 105*MM_TO_UNITS, 148*MM_TO_UNITS ); + + /** + * 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 size The MediaBox or the page. + */ + public PDPage(PDRectangle size) + { + page = new COSDictionary(); + page.setItem( COSName.TYPE, COSName.PAGE ); + setMediaBox( size ); + } + + + + /** + * 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() + { + if( parent == null) + { + COSDictionary parentDic = (COSDictionary)page.getDictionaryObject( "Parent", "P" ); + if( parentDic != null ) + { + parent = new PDPageNode( parentDic ); + } + } + return parent; + } + + private PDPageNode parent = null; + + /** + * This will set the parent of this page. + * + * @param parent The parent to this page node. + */ + public void setParent( PDPageNode parent ) + { + this.parent = 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() + { + if( mediaBox == null) + { + COSArray array = (COSArray)page.getDictionaryObject( COSName.MEDIA_BOX ); + if( array != null ) + { + mediaBox = new PDRectangle( array ); + } + } + return mediaBox; + } + + private PDRectangle mediaBox = null; + + /** + * 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(); + if( retval == null && getParent() != null ) + { + retval = getParent().findMediaBox(); + } + return retval; + } + + /** + * This will set the mediaBox for this page. + * + * @param mediaBox The new mediaBox for this page. + */ + public void setMediaBox( PDRectangle mediaBox ) + { + this.mediaBox = 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.setInt( COSName.ROTATE, 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; iBen Litchfield + * @version $Revision: 1.8 $ + */ +public class PDPageNode implements COSObjectable +{ + private COSDictionary page; + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDPageNode.class); + + /** + * 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, COSInteger.ZERO ); + } + + /** + * 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.setLong( COSName.COUNT, totalCount ); + return totalCount; + } + + /** + * This will get the count of descendent page objects. + * + * @return The total number of descendent page objects. + */ + public long getCount() + { + if(page == null) + { + return 0L; + } + COSBase num = page.getDictionaryObject(COSName.COUNT); + if(num == null) + { + return 0L; + } + return ((COSNumber) num).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, COSName.P); + 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() ); + } + + /** + * {@inheritDoc} + */ + 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) + { + if(page == null) + return null; + COSArray kids = (COSArray)page.getDictionaryObject( COSName.KIDS ); + if ( kids == null) + { + log.error("No Kids found in getAllKids(). Probably a malformed pdf."); + return null; + } + for( int i=0; iPDFBOX-788 + */ +public class PDPageable implements Pageable, Printable { + + /** + * List of all pages in the given PDF document. + */ + private final List pages = new ArrayList(); + + /** + * The printer job for printing the given PDF document. + */ + private final PrinterJob job; + + /** + * Creates a {@link Pageable} adapter for the given PDF document and + * printer job. + * + * @param document PDF document + * @param job printer job + * @throws IllegalArgumentException if an argument is null + * @throws PrinterException if the document permissions prevent printing + */ + public PDPageable(PDDocument document, PrinterJob job) + throws IllegalArgumentException, PrinterException { + if (document == null || job == null) { + throw new IllegalArgumentException( + "PDPageable(" + document + ", " + job + ")"); + } else if (!document.getCurrentAccessPermission().canPrint()) { + throw new PrinterException( + "You do not have permission to print this document"); + } else { + document.getDocumentCatalog().getPages().getAllKids(pages); + this.job = job; + } + } + + /** + * Creates a {@link Pageable} adapter for the given PDF document using + * a default printer job returned by {@link PrinterJob#getPrinterJob()}. + * + * @param document PDF document + * @throws IllegalArgumentException if the argument is null + * @throws PrinterException if the document permissions prevent printing + */ + public PDPageable(PDDocument document) + throws IllegalArgumentException, PrinterException { + this(document, PrinterJob.getPrinterJob()); + } + + /** + * Returns the printer job for printing the given PDF document. + * + * @return printer job + */ + public PrinterJob getPrinterJob() { + return job; + } + + //------------------------------------------------------------< Pageable > + + /** + * Returns the number of pages in the given PDF document. + * + * @return number of pages + */ + public int getNumberOfPages() { + return pages.size(); + } + + /** + * Returns the format of the page at the given index. + * + * @param i page index, zero-based + * @return page format + * @throws IndexOutOfBoundsException if the page index is invalid + */ + public PageFormat getPageFormat(int i) throws IndexOutOfBoundsException { + PageFormat format = job.defaultPage(); + + PDPage page = pages.get(i); // can throw IOOBE + Dimension media = page.findMediaBox().createDimension(); + Dimension crop = page.findCropBox().createDimension(); + + // Center the ImageableArea if the crop is smaller than the media + double diffWidth = 0.0; + double diffHeight = 0.0; + if (!media.equals(crop)) { + diffWidth = (media.getWidth() - crop.getWidth()) / 2.0; + diffHeight = (media.getHeight() - crop.getHeight()) / 2.0; + } + + Paper paper = format.getPaper(); + PrintService service = job.getPrintService(); // can be null + Class orientation = OrientationRequested.class; + if (service != null + && service.getDefaultAttributeValue(orientation) == LANDSCAPE) { + format.setOrientation(PageFormat.LANDSCAPE); + paper.setImageableArea( + diffHeight, diffWidth, crop.getHeight(), crop.getWidth()); + paper.setSize(media.getHeight(), media.getWidth()); + } else { + format.setOrientation(PageFormat.PORTRAIT); + paper.setImageableArea( + diffWidth, diffHeight, crop.getWidth(), crop.getHeight()); + paper.setSize(media.getWidth(), media.getHeight()); + } + format.setPaper(paper); + + return format; + } + + /** + * Returns a {@link Printable} for the page at the given index. + * Currently this method simply returns the underlying {@link PDPage} + * object that directly implements the {@link Printable} interface, but + * future versions may choose to return a different adapter instance. + * + * @param i page index, zero-based + * @return printable + * @throws IndexOutOfBoundsException if the page index is invalid + */ + public Printable getPrintable(int i) throws IndexOutOfBoundsException { + return pages.get(i); + } + + //-----------------------------------------------------------< Printable > + + /** + * Prints the page at the given index. + * + * @param graphics printing target + * @param format page format + * @param i page index, zero-based + * @return {@link Printable#PAGE_EXISTS} if the page was printed, + * or {@link Printable#NO_SUCH_PAGE} if page index was invalid + * @throws PrinterException if printing failed + */ + @SuppressWarnings("deprecation") + public int print(Graphics graphics, PageFormat format, int i) + throws PrinterException { + if (0 <= i && i < pages.size()) { + return pages.get(i).print(graphics, format, i); + } else { + return NO_SUCH_PAGE; + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDResources.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDResources.java new file mode 100644 index 000000000..5449f32f8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/PDResources.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.common.COSDictionaryMap; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.font.PDFontFactory; +import org.apache.pdfbox.pdmodel.graphics.PDExtendedGraphicsState; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.apache.pdfbox.pdmodel.markedcontent.PDPropertyList; + +/** + * This represents a set of resources available at the page/pages/stream level. + * + * @author Ben Litchfield + * @version $Revision: 1.16 $ + */ +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. + * + * @param fontCache A map of existing PDFont objects to reuse. + * @return The map of fonts. + * + * @throws IOException If there is an error getting the fonts. + */ + public Map getFonts( Map fontCache ) 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 ); + for( COSName fontName : fonts.keySet() ) + { + 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, fontCache )); + } + } + return retval; + } + + /** + * 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 + { + return getFonts( null ); + } + + /** + * 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( COSName.XOBJECT ); + + if( xobjects == null ) + { + xobjects = new COSDictionary(); + resources.setItem( COSName.XOBJECT, xobjects ); + } + + Map actuals = new HashMap(); + retval = new COSDictionaryMap( actuals, xobjects ); + for( COSName objName : xobjects.keySet() ) + { + 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( COSName.XOBJECT ); + + if( images == null ) + { + images = new COSDictionary(); + resources.setItem( COSName.XOBJECT, images ); + } + + Map actuals = new HashMap(); + retval = new COSDictionaryMap( actuals, images ); + for( COSName imageName : images.keySet() ) + { + 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 ); + for( COSName csName : colorspaces.keySet() ) + { + 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.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.EXT_G_STATE ); + + if( states != null ) + { + Map actuals = new HashMap(); + retval = new COSDictionaryMap( actuals, states ); + for( COSName name : states.keySet() ) + { + 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.EXT_G_STATE, dic ); + } + + /** + * Returns the dictionary mapping resource names to property list dictionaries for marked + * content. + * @return the property list + */ + public PDPropertyList getProperties() + { + PDPropertyList retval = null; + COSDictionary props = (COSDictionary)resources.getDictionaryObject(COSName.PROPERTIES); + + if (props != null) + { + retval = new PDPropertyList(props); + } + return retval; + } + + /** + * Sets the dictionary mapping resource names to property list dictionaries for marked + * content. + * @param props the property list + */ + public void setProperties(PDPropertyList props) + { + resources.setItem(COSName.PROPERTIES, props.getCOSObject()); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSArrayList.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSArrayList.java new file mode 100644 index 000000000..06948ad17 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSArrayList.java @@ -0,0 +1,676 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; +import org.apache.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 + * @version $Revision: 1.15 $ + */ +public class COSArrayList implements List +{ + private COSArray array; + private List actual; + + private COSDictionary parentDict; + private COSName dictKey; + + /** + * Default constructor. + */ + public COSArrayList() + { + array = new COSArray(); + actual = new ArrayList(); + } + + /** + * 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, COSName dictionaryKey ) + { + array = new COSArray(); + array.add( item ); + actual = new ArrayList(); + actual.add( actualObject ); + + parentDict = dictionary; + dictKey = dictionaryKey; + } + + /** + * @deprecated use the {@link #COSArrayList(Object, COSBase, COSDictionary, COSName)} method instead + */ + public COSArrayList( Object actualObject, COSBase item, COSDictionary dictionary, String dictionaryKey ) + { + this( actualObject, item, dictionary, COSName.getPDFName(dictionaryKey) ); + } + + /** + * {@inheritDoc} + */ + public int size() + { + return actual.size(); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() + { + return actual.isEmpty(); + } + + /** + * {@inheritDoc} + */ + public boolean contains(Object o) + { + return actual.contains(o); + } + + /** + * {@inheritDoc} + */ + public Iterator iterator() + { + return actual.iterator(); + } + + /** + * {@inheritDoc} + */ + public Object[] toArray() + { + return actual.toArray(); + } + + /** + * {@inheritDoc} + */ + public Object[] toArray(Object[] a) + { + return actual.toArray(a); + + } + + /** + * {@inheritDoc} + */ + 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 + { + if(array != null) + { + array.add(((COSObjectable)o).getCOSObject()); + } + } + return actual.add(o); + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public boolean containsAll(Collection c) + { + return actual.containsAll( c ); + } + + /** + * {@inheritDoc} + */ + 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 ); + } + + /** + * {@inheritDoc} + */ + 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 index && array.get( index ) instanceof DualCOSObjectable ) + { + //remove both objects + array.remove( index ); + array.remove( index ); + } + else + { + array.remove( index ); + } + return actual.remove( index ); + } + + /** + * {@inheritDoc} + */ + public int indexOf(Object o) + { + return actual.indexOf( o ); + } + + /** + * {@inheritDoc} + */ + public int lastIndexOf(Object o) + { + return actual.indexOf( o ); + + } + + /** + * {@inheritDoc} + */ + public ListIterator listIterator() + { + return actual.listIterator(); + } + + /** + * {@inheritDoc} + */ + public ListIterator listIterator(int index) + { + return actual.listIterator( index ); + } + + /** + * {@inheritDoc} + */ + public List subList(int fromIndex, int toIndex) + { + return actual.subList( fromIndex, toIndex ); + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return "COSArrayList{" + array.toString() + "}"; + } + + /** + * This will return then underlying COSArray. + * + * @return the COSArray + */ + public COSArray toList() + { + return array; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSDictionaryMap.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSDictionaryMap.java new file mode 100644 index 000000000..33963b424 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSDictionaryMap.java @@ -0,0 +1,260 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +/** + * This is a Map that will automatically sync the contents to a COSDictionary. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +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; + } + + + /** + * {@inheritDoc} + */ + public int size() + { + return map.size(); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() + { + return size() == 0; + } + + /** + * {@inheritDoc} + */ + public boolean containsKey(Object key) + { + return map.keySet().contains( key ); + } + + /** + * {@inheritDoc} + */ + public boolean containsValue(Object value) + { + return actuals.containsValue( value ); + } + + /** + * {@inheritDoc} + */ + public Object get(Object key) + { + return actuals.get( key ); + } + + /** + * {@inheritDoc} + */ + public Object put(Object key, Object value) + { + COSObjectable object = (COSObjectable)value; + + map.setItem( COSName.getPDFName( (String)key ), object.getCOSObject() ); + return actuals.put( key, value ); + } + + /** + * {@inheritDoc} + */ + public Object remove(Object key) + { + map.removeItem( COSName.getPDFName( (String)key ) ); + return actuals.remove( key ); + } + + /** + * {@inheritDoc} + */ + public void putAll(Map t) + { + throw new RuntimeException( "Not yet implemented" ); + } + + /** + * {@inheritDoc} + */ + public void clear() + { + map.clear(); + actuals.clear(); + } + + /** + * {@inheritDoc} + */ + public Set keySet() + { + return actuals.keySet(); + } + + /** + * {@inheritDoc} + */ + public Collection values() + { + return actuals.values(); + } + + /** + * {@inheritDoc} + */ + public Set entrySet() + { + return Collections.unmodifiableSet(actuals.entrySet()); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object o) + { + boolean retval = false; + if( o instanceof COSDictionaryMap ) + { + COSDictionaryMap other = (COSDictionaryMap)o; + retval = other.map.equals( this.map ); + } + return retval; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return actuals.toString(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return map.hashCode(); + } + + /** + * This will take a map<java.lang.String,org.apache.pdfbox.pdmodel.COSObjectable> + * and convert it into a COSDictionary<COSName,COSBase>. + * + * @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(); + for( COSName key : map.keySet() ) + { + 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( ((COSFloat)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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSObjectable.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSObjectable.java new file mode 100644 index 000000000..e00e6e0d1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSObjectable.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSBase; + +/** + * This is an interface used to get/create the underlying COSObject. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public interface COSObjectable +{ + /** + * Convert this standard java object to a COS object. + * + * @return The cos object that matches this Java object. + */ + public COSBase getCOSObject(); +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSStreamArray.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSStreamArray.java new file mode 100644 index 000000000..99da7a05e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/COSStreamArray.java @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.SequenceInputStream; + +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.ICOSVisitor; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.io.RandomAccess; + +import org.apache.pdfbox.pdfparser.PDFStreamParser; + +/** + * This will take an array of streams and sequence them together. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +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 a stream (or the reference to a stream) from the array. + * + * @param index The index of the requested stream + * @return The stream object or a reference to a stream + */ + public COSBase get( int index ) + { + return streams.get( index ); + } + + /** + * This will get the number of streams in the array. + * + * @return the number of streams + */ + public int getStreamCount() + { + return streams.size(); + } + + /** + * This will get the scratch file associated with this stream. + * + * @return The scratch file where this stream is being stored. + */ + public RandomAccess 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 ); + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return "COSStream{}"; + } + + /** + * 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." ); + } + + /** + * 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("ISO-8859-1"); + + for( int i=0;iBen Litchfield + * @version $Revision: 1.2 $ + */ +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(); +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDestinationOrAction.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDestinationOrAction.java new file mode 100644 index 000000000..6e8bd871c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDestinationOrAction.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +/** + * This is an interface used for some elements such as the document + * OpenAction that can be either a Destination or an Action. + * + * @author Ben Litchfield + * + * @version $Revision: 1.2 $ + */ +public interface PDDestinationOrAction extends COSObjectable +{ + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDictionaryWrapper.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDictionaryWrapper.java new file mode 100644 index 000000000..6f16140d7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDDictionaryWrapper.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +/** + * A wrapper for a COS dictionary. + * + * @author Johannes Koch + * @version $Revision: $ + * + */ +public class PDDictionaryWrapper implements COSObjectable +{ + + private final COSDictionary dictionary; + + /** + * Default constructor + */ + public PDDictionaryWrapper() + { + this.dictionary = new COSDictionary(); + } + + /** + * Creates a new instance with a given COS dictionary. + * + * @param dictionary the dictionary + */ + public PDDictionaryWrapper(COSDictionary dictionary) + { + this.dictionary = dictionary; + } + + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.dictionary; + } + + /** + * Gets the COS dictionary. + * + * @return the COS dictionary + */ + protected COSDictionary getCOSDictionary() + { + return this.dictionary; + } + + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj instanceof PDDictionaryWrapper) + { + return this.dictionary.equals(((PDDictionaryWrapper) obj).dictionary); + } + return false; + } + + @Override + public int hashCode() + { + return this.dictionary.hashCode(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMatrix.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMatrix.java new file mode 100644 index 000000000..06cbb836e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMatrix.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; + +/** + * This class will be used for matrix manipulation. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class PDMatrix implements COSObjectable +{ + private COSArray matrix; + // the number of row elements depends on the number of elements + // within the given matrix + // 3x3 e.g. Matrix of a CalRGB colorspace dictionary + // 3x2 e.g. FontMatrix of a type 3 font + private int numberOfRowElements = 3; + + /** + * 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 ) + { + if ( array.size() == 6) + { + numberOfRowElements = 2; + } + 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*numberOfRowElements + 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*numberOfRowElements+column, new COSFloat( value ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMemoryStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMemoryStream.java new file mode 100644 index 000000000..e8c4deeae --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMemoryStream.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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 + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMetadata.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMetadata.java new file mode 100644 index 000000000..7a26fcd71 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDMetadata.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.transform.TransformerException; + +import org.apache.jempbox.xmp.XMPMetadata; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * This class represents metadata for various objects in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 ); + } + + /** + * Extract the XMP metadata and create and build an in memory object. + * To persist changes back to the PDF you must call importXMPMetadata. + * + * @return A parsed XMP object. + * + * @throws IOException If there is an error parsing the XMP data. + */ + public XMPMetadata exportXMPMetadata() throws IOException + { + return XMPMetadata.load( createInputStream() ); + } + + /** + * Import an XMP stream into the PDF document. + * + * @param xmp The XMP data. + * + * @throws IOException If there is an error generating the XML document. + * @throws TransformerException If there is an error generating the XML document. + */ + public void importXMPMetadata( XMPMetadata xmp ) + throws IOException, TransformerException + { + xmp.save( createOutputStream() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNameTreeNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNameTreeNode.java new file mode 100644 index 000000000..4c9fafaaf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNameTreeNode.java @@ -0,0 +1,333 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.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 + * @version $Revision: 1.4 $ + */ +public class PDNameTreeNode implements COSObjectable +{ + private static final Log log = LogFactory.getLog(PDNameTreeNode.class); + 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( COSName.KIDS ); + if( kids != null ) + { + List pdObjects = new ArrayList(); + for( int i=0; i 0) + { + PDNameTreeNode firstKid = (PDNameTreeNode) kids.get(0); + PDNameTreeNode lastKid = (PDNameTreeNode) kids.get(kids.size() - 1); + String lowerLimit = firstKid.getLowerLimit(); + this.setLowerLimit(lowerLimit); + String upperLimit = lastKid.getUpperLimit(); + this.setUpperLimit(upperLimit); + } + 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(); + if (kids != null) + { + for( int i=0; i= 0 ) + { + retval = childNode.getValue( name ); + } + } + } + else + { + log.warn("NameTreeNode does not have \"names\" nor \"kids\" objects."); + } + } + return retval; + } + + + /** + * This will return a map of names. The key will be a string, and the + * value will depend on where this class is being used. + * + * @return ordered map of cos objects + * @throws IOException If there is an error while creating the sub types. + */ + public Map getNames() throws IOException + { + COSArray namesArray = (COSArray)node.getDictionaryObject( COSName.NAMES ); + if( namesArray != null ) + { + Map names = new LinkedHashMap(); + for( int i=0; inull + */ + public void setNames( Map names ) + { + if( names == null ) + { + node.setItem( "Names", (COSObjectable)null ); + node.setItem( COSName.LIMITS, (COSObjectable)null); + } + else + { + COSArray array = new COSArray(); + List keys = new ArrayList(names.keySet()); + Collections.sort(keys); + for (String key : keys) { + array.add(new COSString(key)); + array.add(names.get(key)); + } + setLowerLimit(keys.get(0)); + setUpperLimit(keys.get(keys.size() - 1)); + 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( COSName.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( COSName.LIMITS ); + if( arr == null ) + { + arr = new COSArray(); + arr.add( null ); + arr.add( null ); + node.setItem(COSName.LIMITS, arr); + } + 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( COSName.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( COSName.LIMITS ); + if( arr == null ) + { + arr = new COSArray(); + arr.add( null ); + arr.add( null ); + node.setItem(COSName.LIMITS, arr); + } + arr.setString( 0, lower ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNamedTextStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNamedTextStream.java new file mode 100644 index 000000000..895c2160a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNamedTextStream.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.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 + * @version $Revision: 1.3 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java new file mode 100644 index 000000000..126265fd1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; + +/** + * This class represents a PDF Number tree. See the PDF Reference 1.7 section + * 7.9.7 for more details. + * + * @author Ben Litchfield, + * Igor Podolskiy + * @version $Revision: 1.4 $ + */ +public class PDNumberTreeNode implements COSObjectable +{ + private COSDictionary node; + private Class valueType = null; + + /** + * Constructor. + * + * @param valueClass The PD Model type of object that is the value. + */ + public PDNumberTreeNode( 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 PDNumberTreeNode( 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 PDNumberTreeNode objects. + * + * @return The list of children or null if there are no children. + */ + public List getKids() + { + + List retval = null; + COSArray kids = (COSArray)node.getDictionaryObject( COSName.KIDS ); + if( kids != null ) + { + List pdObjects = new ArrayList(); + for( int i=0; i= 0 ) + { + retval = childNode.getValue( index ); + } + } + } + return retval; + } + + + /** + * This will return a map of numbers. The key will be a java.lang.Integer, the value will + * depend on where this class is being used. + * + * @return A map of COS objects. + * + * @throws IOException If there is a problem creating the values. + */ + public Map getNumbers() throws IOException + { + Map indices = null; + COSArray namesArray = (COSArray)node.getDictionaryObject( COSName.NUMS ); + if( namesArray != null ) + { + indices = new HashMap(); + for( int i=0; i 0 ) + { + lower = (Integer)keys.get( 0 ); + upper = (Integer)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 Integer getUpperLimit() + { + Integer retval = null; + COSArray arr = (COSArray)node.getDictionaryObject( COSName.LIMITS ); + if( arr != null ) + { + retval = Integer.valueOf(arr.getInt( 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( Integer upper ) + { + COSArray arr = (COSArray)node.getDictionaryObject( COSName.LIMITS ); + if( arr == null ) + { + arr = new COSArray(); + arr.add( null ); + arr.add( null ); + } + arr.setInt( 1, upper.intValue() ); + } + + /** + * Get the lowest value for a key in the name map. + * + * @return The lowest value for a key in the map. + */ + public Integer getLowerLimit() + { + Integer retval = null; + COSArray arr = (COSArray)node.getDictionaryObject( COSName.LIMITS ); + if( arr != null ) + { + retval = Integer.valueOf(arr.getInt( 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( Integer lower ) + { + COSArray arr = (COSArray)node.getDictionaryObject( COSName.LIMITS ); + if( arr == null ) + { + arr = new COSArray(); + arr.add( null ); + arr.add( null ); + } + arr.setInt( 0, lower.intValue() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDObjectStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDObjectStream.java new file mode 100644 index 000000000..fa259c521 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDObjectStream.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.PDDocument; + + + +/** + * A PDStream represents a stream in a PDF document. Streams are tied to a single + * PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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( COSName.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( COSName.EXTENDS, stream ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabelRange.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabelRange.java new file mode 100644 index 000000000..30cc05f81 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabelRange.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * Contains information for a page label range. + * + * @author Igor + * Podolskiy + * + * @see PDPageLabels + * + * @version $Revision$ + */ +public class PDPageLabelRange implements COSObjectable +{ + + private COSDictionary root; + + // Page label dictonary (PDF32000-1:2008 Section 12.4.2, Table 159) + private static final COSName KEY_START = COSName.getPDFName("St"); + private static final COSName KEY_PREFIX = COSName.P; + private static final COSName KEY_STYLE = COSName.getPDFName("S"); + + // Style entry values (PDF32000-1:2008 Section 12.4.2, Table 159) + + /** + * Decimal page numbering style (1, 2, 3, ...). + */ + public static final String STYLE_DECIMAL = "D"; + + /** + * Roman numbers (upper case) numbering style (I, II, III, IV, ...). + */ + public static final String STYLE_ROMAN_UPPER = "R"; + + /** + * Roman numbers (lower case) numbering style (i, ii, iii, iv, ...). + */ + public static final String STYLE_ROMAN_LOWER = "r"; + + /** + * Letter (upper case) numbering style (A, B, ..., Z, AA, BB, ..., ZZ, AAA, + * ...). + */ + public static final String STYLE_LETTERS_UPPER = "A"; + + /** + * Letter (lower case) numbering style (a, b, ..., z, aa, bb, ..., zz, aaa, + * ...). + */ + public static final String STYLE_LETTERS_LOWER = "a"; + + /** + * Creates a new empty page label range object. + */ + public PDPageLabelRange() + { + this(new COSDictionary()); + } + + /** + * Creates a new page label range object from the given dictionary. + * + * @param dict + * the base dictionary for the new object. + */ + public PDPageLabelRange(COSDictionary dict) + { + root = dict; + } + + /** + * Returns the underlying dictionary. + * + * @return the underlying dictionary. + */ + public COSDictionary getCOSDictionary() + { + return root; + } + + public COSBase getCOSObject() + { + return root; + } + + /** + * Returns the numbering style for this page range. + * + * @return one of the STYLE_* constants + */ + public String getStyle() + { + return root.getNameAsString(KEY_STYLE); + } + + /** + * Sets the numbering style for this page range. + * + * @param style + * one of the STYLE_* constants or {@code null} if no page + * numbering is desired. + */ + public void setStyle(String style) + { + if (style != null) + { + root.setName(KEY_STYLE, style); + } + else + { + root.removeItem(KEY_STYLE); + } + } + + /** + * Returns the start value for page numbering in this page range. + * + * @return a positive integer the start value for numbering. + */ + public int getStart() + { + return root.getInt(KEY_START, 1); + } + + /** + * Sets the start value for page numbering in this page range. + * + * @param start + * a positive integer representing the start value. + * @throws IllegalArgumentException + * if {@code start} is not a positive integer + */ + public void setStart(int start) + { + if (start <= 0) + { + throw new IllegalArgumentException( + "The page numbering start value must be a positive integer"); + } + root.setInt(KEY_START, start); + } + + /** + * Returns the page label prefix for this page range. + * + * @return the page label prefix for this page range, or {@code null} if no + * prefix has been defined. + */ + public String getPrefix() + { + return root.getString(KEY_PREFIX); + } + + /** + * Sets the page label prefix for this page range. + * + * @param prefix + * the page label prefix for this page range, or {@code null} to + * unset the prefix. + */ + public void setPrefix(String prefix) + { + if (prefix != null) + { + root.setString(KEY_PREFIX, prefix); + } + else + { + root.removeItem(KEY_PREFIX); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabels.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabels.java new file mode 100644 index 000000000..3d8115291 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDPageLabels.java @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.Map.Entry; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * Represents the page label dictionary of a document. + * + * @author Igor + * Podolskiy + * @version $Revision$ + */ +public class PDPageLabels implements COSObjectable +{ + + private SortedMap labels; + + private PDDocument doc; + + /** + * Creates an empty page label dictionary for the given document. + * + *

+ * Note that the page label dictionary won't be automatically added to the + * document; you will still need to do it manually (see + * {@link PDDocumentCatalog#setPageLabels(PDPageLabels)}. + *

+ * + * @param document + * The document the page label dictionary is created for. + * @see PDDocumentCatalog#setPageLabels(PDPageLabels) + */ + public PDPageLabels(PDDocument document) + { + labels = new TreeMap(); + this.doc = document; + PDPageLabelRange defaultRange = new PDPageLabelRange(); + defaultRange.setStyle(PDPageLabelRange.STYLE_DECIMAL); + labels.put(0, defaultRange); + } + + /** + * Creates an page label dictionary for a document using the information in + * the given COS dictionary. + * + *

+ * Note that the page label dictionary won't be automatically added to the + * document; you will still need to do it manually (see + * {@link PDDocumentCatalog#setPageLabels(PDPageLabels)}. + *

+ * + * @param document + * The document the page label dictionary is created for. + * @param dict + * an existing page label dictionary + * @see PDDocumentCatalog#setPageLabels(PDPageLabels) + * @throws IOException + * If something goes wrong during the number tree conversion. + */ + public PDPageLabels(PDDocument document, COSDictionary dict) throws IOException + { + this(document); + if (dict == null) + { + return; + } + PDNumberTreeNode root = new PDNumberTreeNode(dict, COSDictionary.class); + findLabels(root); + } + + private void findLabels(PDNumberTreeNode node) throws IOException { + if (node.getKids() != null) { + List kids = node.getKids(); + for (PDNumberTreeNode kid : kids) { + findLabels(kid); + } + } + else if (node.getNumbers() != null) { + Map numbers = node.getNumbers(); + for (Entry i : numbers.entrySet()) + { + if(i.getKey() >= 0) + labels.put(i.getKey(), new PDPageLabelRange(i.getValue())); + } + } + } + + + /** + * Returns the number of page label ranges. + * + *

+ * This will be always >= 1, as the required default entry for the page + * range starting at the first page is added automatically by this + * implementation (see PDF32000-1:2008, p. 375). + *

+ * + * @return the number of page label ranges. + */ + public int getPageRangeCount() + { + return labels.size(); + } + + /** + * Returns the page label range starting at the given page, or {@code null} + * if no such range is defined. + * + * @param startPage + * the 0-based page index representing the start page of the page + * range the item is defined for. + * @return the page label range or {@code null} if no label range is defined + * for the given start page. + */ + public PDPageLabelRange getPageLabelRange(int startPage) + { + return labels.get(startPage); + } + + /** + * Sets the page label range beginning at the specified start page. + * + * @param startPage + * the 0-based index of the page representing the start of the + * page label range. + * @param item + * the page label item to set. + */ + public void setLabelItem(int startPage, PDPageLabelRange item) + { + labels.put(startPage, item); + } + + public COSBase getCOSObject() + { + COSDictionary dict = new COSDictionary(); + COSArray arr = new COSArray(); + for (Entry i : labels.entrySet()) + { + arr.add(COSInteger.get(i.getKey())); + arr.add(i.getValue()); + } + dict.setItem(COSName.NUMS, arr); + return dict; + } + + /** + * Returns a mapping with computed page labels as keys and corresponding + * 0-based page indices as values. The returned map will contain at most as + * much entries as the document has pages. + * + *

+ * NOTE: If the document contains duplicate page labels, + * the returned map will contain less entries than the document has + * pages. The page index returned in this case is the highest index + * among all pages sharing the same label. + *

+ * + * @return a mapping from labels to 0-based page indices. + */ + public Map getPageIndicesByLabels() + { + final Map labelMap = + new HashMap(doc.getNumberOfPages()); + computeLabels(new LabelHandler() + { + public void newLabel(int pageIndex, String label) + { + labelMap.put(label, pageIndex); + } + }); + return labelMap; + } + + /** + * Returns a mapping with 0-based page indices as keys and corresponding + * page labels as values as an array. The array will have exactly as much + * entries as the document has pages. + * + * @return an array mapping from 0-based page indices to labels. + */ + public String[] getLabelsByPageIndices() + { + final String[] map = new String[doc.getNumberOfPages()]; + computeLabels(new LabelHandler() + { + public void newLabel(int pageIndex, String label) + { + if(pageIndex < doc.getNumberOfPages()) + { + map[pageIndex] = label; + } + } + }); + return map; + } + + /** + * Internal interface for the control flow support. + * + * @author Igor Podolskiy + */ + private static interface LabelHandler + { + public void newLabel(int pageIndex, String label); + } + + private void computeLabels(LabelHandler handler) + { + Iterator> iterator = + labels.entrySet().iterator(); + if (!iterator.hasNext()) + { + return; + } + int pageIndex = 0; + Entry lastEntry = iterator.next(); + while (iterator.hasNext()) + { + Entry entry = iterator.next(); + int numPages = entry.getKey() - lastEntry.getKey(); + LabelGenerator gen = new LabelGenerator(lastEntry.getValue(), + numPages); + while (gen.hasNext()) + { + handler.newLabel(pageIndex, gen.next()); + pageIndex++; + } + lastEntry = entry; + } + LabelGenerator gen = new LabelGenerator(lastEntry.getValue(), + doc.getNumberOfPages() - lastEntry.getKey()); + while (gen.hasNext()) + { + handler.newLabel(pageIndex, gen.next()); + pageIndex++; + } + } + + /** + * Generates the labels in a page range. + * + * @author Igor Podolskiy + * + */ + private static class LabelGenerator implements Iterator + { + private PDPageLabelRange labelInfo; + private int numPages; + private int currentPage; + + public LabelGenerator(PDPageLabelRange label, int pages) + { + this.labelInfo = label; + this.numPages = pages; + this.currentPage = 0; + } + + public boolean hasNext() + { + return currentPage < numPages; + } + + public String next() + { + if (!hasNext()) + { + throw new NoSuchElementException(); + } + StringBuilder buf = new StringBuilder(); + if (labelInfo.getPrefix() != null) + { + String label = labelInfo.getPrefix(); + // there may be some labels with some null bytes at the end + // which will lead to an incomplete output, see PDFBOX-1047 + while (label.lastIndexOf(0) != -1) + { + label = label.substring(0, label.length()-1); + } + buf.append(label); + } + if (labelInfo.getStyle() != null) + { + buf.append(getNumber(labelInfo.getStart() + currentPage, + labelInfo.getStyle())); + } + currentPage++; + return buf.toString(); + } + + private String getNumber(int pageIndex, String style) + { + if (PDPageLabelRange.STYLE_DECIMAL.equals(style)) + { + return Integer.toString(pageIndex); + } + else if (PDPageLabelRange.STYLE_LETTERS_LOWER.equals(style)) + { + return makeLetterLabel(pageIndex); + } + else if (PDPageLabelRange.STYLE_LETTERS_UPPER.equals(style)) + { + return makeLetterLabel(pageIndex).toUpperCase(); + } + else if (PDPageLabelRange.STYLE_ROMAN_LOWER.equals(style)) + { + return makeRomanLabel(pageIndex); + } + else if (PDPageLabelRange.STYLE_ROMAN_UPPER.equals(style)) + { + return makeRomanLabel(pageIndex).toUpperCase(); + } + else + { + // Fall back to decimals. + return Integer.toString(pageIndex); + } + } + + /** + * Lookup table used by the {@link #makeRomanLabel(int)} method. + */ + private static final String[][] ROMANS = new String[][] { + { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }, + { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }, + { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" } + }; + + private static String makeRomanLabel(int pageIndex) + { + StringBuilder buf = new StringBuilder(); + int power = 0; + while (power < 3 && pageIndex > 0) + { + buf.insert(0, ROMANS[power][pageIndex % 10]); + pageIndex = pageIndex / 10; + power++; + } + // Prepend as many m as there are thousands (which is + // incorrect by the roman numeral rules for numbers > 3999, + // but is unbounded and Adobe Acrobat does it this way). + // This code is somewhat inefficient for really big numbers, + // but those don't occur too often (and the numbers in those cases + // would be incomprehensible even if we and Adobe + // used strict Roman rules). + for (int i = 0; i < pageIndex; i++) + { + buf.insert(0, 'm'); + } + return buf.toString(); + } + + /** + * A..Z, AA..ZZ, AAA..ZZZ ... labeling as described in PDF32000-1:2008, + * Table 159, Page 375. + */ + private static String makeLetterLabel(int num) + { + StringBuilder buf = new StringBuilder(); + int numLetters = num / 26 + Integer.signum(num % 26); + int letter = num % 26 + 26 * (1 - Integer.signum(num % 26)) + 64; + for (int i = 0; i < numLetters; i++) + { + buf.appendCodePoint(letter); + } + return buf.toString(); + } + + public void remove() + { + // This is a generator, no removing allowed. + throw new UnsupportedOperationException(); + } + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRange.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRange.java new file mode 100644 index 000000000..cccb11cdd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRange.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; + +/** + * This class will be used to signify a range. a(min) <= a* <= a(max) + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 new PDRange( array, 1 ). + * + * @param range The array that describes the index + * @param index The range 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*2 ); + 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*2, 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*2+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*2+1, new COSFloat( max ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRectangle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRectangle.java new file mode 100644 index 000000000..54810694f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDRectangle.java @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.fontbox.util.BoundingBox; + +import java.awt.Dimension; + +/** + * This represents a rectangle in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.12 $ + */ +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.
+ * 100, 100, 400, 400 (llx, lly, urx, ury )
+ * 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() +"]"; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDStream.java new file mode 100644 index 000000000..927d6b72d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDStream.java @@ -0,0 +1,550 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.List; +import java.util.Map; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.filter.Filter; +import org.apache.pdfbox.filter.FilterManager; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import org.apache.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 + * @version $Revision: 1.17 $ + */ +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 ) + { + if (((COSArray)base).size() > 0) { + 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(); + String nextFilter = null; + boolean done = false; + for( int i=0; iPDFBOX-636 + * @return the decoded stream length + */ + public int getDecodedStreamLength() + { + return this.stream.getInt(COSName.DL); + } + + /** + * Set the decoded stream length. + * + * @since Apache PDFBox 1.1.0 + * @see PDFBOX-636 + * @param decodedStreamLength the decoded stream length + */ + public void setDecodedStreamLength(int decodedStreamLength) + { + this.stream.setInt(COSName.DL, decodedStreamLength); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTextStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTextStream.java new file mode 100644 index 000000000..3aced6db9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTextStream.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSStream; +import org.apache.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 + * @version $Revision: 1.3 $ + */ +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(), "ISO-8859-1" ); + } + 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTypedDictionaryWrapper.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTypedDictionaryWrapper.java new file mode 100644 index 000000000..c5fe36b52 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/PDTypedDictionaryWrapper.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * A wrapper for a COS dictionary including Type information. + * + * @author Johannes Koch + * @version $Revision: $ + * + */ +public class PDTypedDictionaryWrapper extends PDDictionaryWrapper +{ + + /** + * Creates a new instance with a given type. + * + * @param type the type (Type) + */ + public PDTypedDictionaryWrapper(String type) + { + super(); + this.getCOSDictionary().setName(COSName.TYPE, type); + } + + /** + * Creates a new instance with a given COS dictionary. + * + * @param dictionary the dictionary + */ + public PDTypedDictionaryWrapper(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the type. + * + * @return the type + */ + public String getType() + { + return this.getCOSDictionary().getNameAsString(COSName.TYPE); + } + + // There is no setType(String) method because changing the Type would most + // probably also change the type of PD object. +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/XrefEntry.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/XrefEntry.java new file mode 100644 index 000000000..ee8e22eba --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/XrefEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright 2011 adam. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * under the License. + */ + +package org.apache.pdfbox.pdmodel.common; + +/** + * + * @author adam + */ +public class XrefEntry { + private int objectNumber = 0; + private int byteOffset = 0; + private int generation = 0; + private boolean inUse = true; + + public XrefEntry() { + } + + public XrefEntry(int objectNumber, int byteOffset, int generation, String inUse) { + this.objectNumber = objectNumber; + this.byteOffset = byteOffset; + this.generation = generation; + this.inUse = "n".equals(inUse); + } + + public int getByteOffset() { + return byteOffset; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java new file mode 100644 index 000000000..d357812df --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common.filespecification; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSStream; + +/** + * This represents a file specification. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java new file mode 100644 index 000000000..1ba19f591 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDEmbeddedFile.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common.filespecification; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Calendar; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * This represents an embedded file in a file specification. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class PDEmbeddedFile extends PDStream +{ + + /** + * Constructor. + * + * @param document {@inheritDoc} + */ + public PDEmbeddedFile( PDDocument document ) + { + super( document ); + getStream().setName( "Type", "EmbeddedFile" ); + + } + + /** + * Constructor. + * + * @param str The stream parameter. + */ + public PDEmbeddedFile( COSStream str ) + { + super( str ); + } + + /** + * Constructor. + * + * @param doc {@inheritDoc} + * @param str {@inheritDoc} + * + * @throws IOException {@inheritDoc} + */ + public PDEmbeddedFile( PDDocument doc, InputStream str ) throws IOException + { + super( doc, str ); + getStream().setName( "Type", "EmbeddedFile" ); + } + + /** + * Constructor. + * + * @param doc {@inheritDoc} + * @param str {@inheritDoc} + * @param filtered {@inheritDoc} + * + * @throws IOException {@inheritDoc} + */ + 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); + } + } + + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java new file mode 100644 index 000000000..76cfa5511 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDFileSpecification.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common.filespecification; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This represents a file specification. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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. + * + * @throws IOException If there is an error creating the file spec. + */ + public static PDFileSpecification createFS( COSBase base ) throws IOException + { + PDFileSpecification retval = null; + if( base == null ) + { + //then simply return null + } + else if( base instanceof COSString ) + { + retval = new PDSimpleFileSpecification( (COSString)base ); + } + else if( base instanceof COSDictionary ) + { + retval = new PDComplexFileSpecification( (COSDictionary)base ); + } + else + { + throw new IOException( "Error: Unknown file specification " + 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 ); +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java new file mode 100644 index 000000000..80a129ba6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/PDSimpleFileSpecification.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common.filespecification; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSString; + +/** + * A file specification that is just a string. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/package.html new file mode 100644 index 000000000..6c8252359 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/filespecification/package.html @@ -0,0 +1,25 @@ + + + + + + + +The file specification package defines classes that are used for the PDF File Specification logic. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunction.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunction.java new file mode 100644 index 000000000..f83baa666 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunction.java @@ -0,0 +1,335 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common.function; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRange; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * This class represents a function in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public abstract class PDFunction implements COSObjectable +{ + + private PDStream functionStream = null; + private COSDictionary functionDictionary = null; + private COSArray domain = null; + private COSArray range = null; + + /** + * Constructor. + * + * @param functionStream The function stream. + */ + public PDFunction( COSBase function ) + { + if (function instanceof COSStream) + { + functionStream = new PDStream( (COSStream)function ); + functionStream.getStream().setName( COSName.TYPE, "Function" ); + } + else if (function instanceof COSDictionary) + { + functionDictionary = (COSDictionary)function; + } + } + + /** + * Returns the function type. + * + * Possible values are: + * + * 0 - Sampled function + * 2 - Exponential interpolation function + * 3 - Stitching function + * 4 - PostScript calculator function + * + * @return the function type. + */ + public abstract int getFunctionType(); + + /** + * Returns the COSObject. + * + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + if (functionStream != null) + { + return functionStream.getCOSObject(); + } + else + { + return functionDictionary; + } + } + + /** + * Returns the stream. + * @return The stream for this object. + */ + public COSDictionary getDictionary() + { + if (functionStream != null) + { + return functionStream.getStream(); + } + else + { + return functionDictionary; + } + } + + /** + * Returns the underlying PDStream. + * @return The stream. + */ + protected PDStream getPDStream() + { + return functionStream; + } + /** + * Create the correct PD Model function based on the COS base function. + * + * @param function The COS function dictionary. + * + * @return The PDModel Function object. + * + * @throws IOException If we are unable to create the PDFunction object. + */ + public static PDFunction create( COSBase function ) throws IOException + { + PDFunction retval = null; + if( function instanceof COSObject ) + { + function = ((COSObject)function).getCOSObject(); + } + COSDictionary functionDictionary = (COSDictionary)function; + int functionType = functionDictionary.getInt( COSName.FUNCTION_TYPE ); + if( functionType == 0 ) + { + retval = new PDFunctionType0(functionDictionary); + } + else if( functionType == 2 ) + { + retval = new PDFunctionType2(functionDictionary); + } + else if( functionType == 3 ) + { + retval = new PDFunctionType3(functionDictionary); + } + else if( functionType == 4 ) + { + retval = new PDFunctionType4(functionDictionary); + } + else + { + throw new IOException( "Error: Unknown function type " + functionType ); + } + return retval; + } + + /** + * This will get the number of output parameters that + * have a range specified. A range for output parameters + * is optional so this may return zero for a function + * that does have output parameters, this will simply return the + * number that have the rnage specified. + * + * @return The number of input parameters that have a range + * specified. + */ + public int getNumberOfOutputParameters() + { + COSArray rangeValues = getRangeValues(); + return rangeValues.size() / 2; + } + + /** + * This will get the range for a certain output parameters. This is will never + * return null. If it is not present then the range 0 to 0 will + * be returned. + * + * @param n The output parameter number to get the range for. + * + * @return The range for this component. + */ + public PDRange getRangeForOutput(int n) + { + COSArray rangeValues = getRangeValues(); + return new PDRange( rangeValues, n ); + } + + /** + * This will set the range values. + * + * @param range The new range values. + */ + public void setRangeValues(COSArray rangeValues) + { + range = rangeValues; + getDictionary().setItem(COSName.RANGE, rangeValues); + } + + /** + * This will get the number of input parameters that + * have a domain specified. + * + * @return The number of input parameters that have a domain + * specified. + */ + public int getNumberOfInputParameters() + { + COSArray array = getDomainValues(); + return array.size() / 2; + } + + /** + * This will get the range for a certain input parameter. This is will never + * return null. If it is not present then the range 0 to 0 will + * be returned. + * + * @param n The parameter number to get the domain for. + * + * @return The domain range for this component. + */ + public PDRange getDomainForInput(int n) + { + COSArray domainValues = getDomainValues(); + return new PDRange( domainValues, n ); + } + + /** + * This will set the domain values. + * + * @param range The new domain values. + */ + public void setDomainValues(COSArray domainValues) + { + domain = domainValues; + getDictionary().setItem(COSName.DOMAIN, domainValues); + } + + /** + * Evaluates the function at the given input. + * ReturnValue = f(input) + * + * @param input The array of input values for the function. In many cases will be an array of a single value, but not always. + * @return The of outputs the function returns based on those inputs. In many cases will be an array of a single value, but not always. + */ + public abstract COSArray eval(COSArray input) throws IOException; + + /** + * Returns all ranges for the output values as COSArray . + * Required for type 0 and type 4 functions + * @return the ranges array. + */ + protected COSArray getRangeValues() + { + if (range == null) + { + range = (COSArray)getDictionary().getDictionaryObject( COSName.RANGE ); + } + return range; + } + + /** + * Returns all domains for the input values as COSArray. + * Required for all function types. + * @return the domains array. + */ + private COSArray getDomainValues() + { + if (domain == null) + { + domain = (COSArray)getDictionary().getDictionaryObject( COSName.DOMAIN ); + } + return domain; + } + + /** + * Clip the given input values to the ranges. + * + * @param inputArray the input values + * @return the clipped values + */ + protected COSArray clipToRange(COSArray inputArray) + { + COSArray rangesArray = getRangeValues(); + COSArray result = null; + if (rangesArray != null) + { + float[] inputValues = inputArray.toFloatArray(); + float[] rangeValues = rangesArray.toFloatArray(); + result = new COSArray(); + int numberOfRanges = rangeValues.length/2; + for (int i=0; iBen Litchfield + * @version $Revision: 1.2 $ + */ +public class PDFunctionType0 extends PDFunction +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDFunctionType0.class); + + /** + * An array of 2 × m numbers specifying the linear mapping of input values + * into the domain of the function’s sample table. + * Default value: [ 0 (Size0 − 1) 0 (Size1 − 1) … ]. + */ + private COSArray encode = null; + /** + * An array of 2 × n numbers specifying the linear mapping of sample values + * into the range appropriate for the function’s output values. + * Default value: same as the value of Range + */ + private COSArray decode = null; + /** + * An array of m positive integers specifying the number of samples in each + * input dimension of the sample table. + */ + private COSArray size = null; + /** + * The samples of the function. + */ + private int[][] samples = null; + + /** + * Constructor. + * + * @param functionStream The function . + */ + public PDFunctionType0(COSBase function) + { + super( function ); + } + + /** + * {@inheritDoc} + */ + public int getFunctionType() + { + return 0; + } + /** + * The "Size" entry, which is the number of samples in + * each input dimension of the sample table. + * + * @return A List of java.lang.Integer objects. + */ + public COSArray getSize() + { + if (size == null) + { + size = (COSArray)getDictionary().getDictionaryObject( COSName.SIZE ); + } + return size; + } + + /** + * Get all sample values of this function. + * + * @return an array with all samples. + */ + public int[][] getSamples() + { + if (samples == null) + { + int arraySize = 1; + int numberOfInputValues = getNumberOfInputParameters(); + int numberOfOutputValues = getNumberOfOutputParameters(); + COSArray sizes = getSize(); + for (int i=0;i 0) + { + int bits = Math.min(bitsToRead, bitsLeft); + value = value << bits; + int valueToAdd = currentValue >> (8 - bits); + value |= valueToAdd; + bitsToRead -= bits; + bitsLeft -= bits; + if (bitsLeft == 0 && bitsToRead > 0) + { + currentValue = (samplesArray[arrayIndex++]+256)%256; + bitsLeft = 8; + } + } + samples[index][k] = value; + bitsToRead = bitsPerSample; + } + index++; + } + } + } + catch (IOException exception) + { + log.error("IOException while reading the sample values of this function."); + } + } + return samples; + } + /** + * Get the number of bits that the output value will take up. + * + * Valid values are 1,2,4,8,12,16,24,32. + * + * @return Number of bits for each output value. + */ + public int getBitsPerSample() + { + return getDictionary().getInt( COSName.BITS_PER_SAMPLE ); + } + + /** + * Set the number of bits that the output value will take up. Valid values + * are 1,2,4,8,12,16,24,32. + * + * @param bps The number of bits for each output value. + */ + public void setBitsPerSample( int bps ) + { + getDictionary().setInt( COSName.BITS_PER_SAMPLE, bps ); + } + + /** + * Returns all encode values as COSArray. + * + * @return the encode array. + */ + private COSArray getEncodeValues() + { + if (encode == null) + { + encode = (COSArray)getDictionary().getDictionaryObject( COSName.ENCODE ); + // the default value is [0 (size[0]-1) 0 (size[1]-1) ...] + if (encode == null) + { + encode = new COSArray(); + COSArray sizeValues = getSize(); + int sizeValuesSize = sizeValues.size(); + for (int i=0; i = paramNum*2+1 ) + { + retval = new PDRange(encodeValues, paramNum ); + } + return retval; + } + + /** + * This will set the encode values. + * + * @param range The new encode values. + */ + public void setEncodeValues(COSArray encodeValues) + { + encode = encodeValues; + getDictionary().setItem(COSName.ENCODE, encodeValues); + } + + /** + * Get the decode for the input parameter. + * + * @param paramNum The function parameter number. + * + * @return The decode parameter range or null if none is set. + */ + public PDRange getDecodeForParameter( int paramNum ) + { + PDRange retval = null; + COSArray decodeValues = getDecodeValues(); + if( decodeValues != null && decodeValues.size() >= paramNum*2+1 ) + { + retval = new PDRange(decodeValues, paramNum ); + } + return retval; + } + + /** + * This will set the decode values. + * + * @param range The new decode values. + */ + public void setDecodeValues(COSArray decodeValues) + { + decode = decodeValues; + getDictionary().setItem(COSName.DECODE, decodeValues); + } + + /** + * {@inheritDoc} + */ + public COSArray eval(COSArray input) throws IOException + { + //This involves linear interpolation based on a set of sample points. + //Theoretically it's not that difficult ... see section 3.9.1 of the PDF Reference. + float[] inputValues = input.toFloatArray(); + float[] sizeValues = getSize().toFloatArray(); + int bitsPerSample = getBitsPerSample(); + int numberOfInputValues = inputValues.length; + int numberOfOutputValues = getNumberOfOutputParameters(); + int[] intInputValuesPrevious = new int[numberOfInputValues]; + int[] intInputValuesNext = new int[numberOfInputValues]; + for (int i=0; iBen Litchfield + * @version $Revision: 1.2 $ + */ +public class PDFunctionType2 extends PDFunction +{ + + /** + * The C0 values of the exponential function. + */ + private COSArray C0; + /** + * The C1 values of the exponential function. + */ + private COSArray C1; + + /** + * Constructor. + * + * @param functionStream The function . + */ + public PDFunctionType2(COSBase function) + { + super( function ); + } + + /** + * {@inheritDoc} + */ + public int getFunctionType() + { + return 2; + } + + /** + * {@inheritDoc} + */ + public COSArray eval(COSArray input) throws IOException + { + //This function performs exponential interpolation. + //It uses only a single value as its input, but may produce a multi-valued output. + //See PDF Reference section 3.9.2. + + double inputValue = input.toFloatArray()[0]; + double exponent = getN(); + COSArray c0 = getC0(); + COSArray c1 = getC1(); + COSArray functionResult = new COSArray(); + int c0Size = c0.size(); + for (int j=0;jBen Litchfield + * @version $Revision: 1.2 $ + */ +public class PDFunctionType3 extends PDFunction +{ + + private COSArray functions = null; + private COSArray encode = null; + private COSArray bounds = null; + + /** + * Constructor. + * + * @param functionStream The function . + */ + public PDFunctionType3(COSBase function) + { + super( function ); + } + + /** + * {@inheritDoc} + */ + public int getFunctionType() + { + return 3; + } + + /** + * {@inheritDoc} + */ + public COSArray eval(COSArray input) throws IOException + { + //This function is known as a "stitching" function. Based on the input, it decides which child function to call. + //See PDF Reference section 3.9.3. + PDFunction function = null; + float x = ((COSNumber)input.get(0)).floatValue(); + PDRange domain = getDomainForInput(1); + // clip input value to domain + x = clipToRange(x, domain.getMin(), domain.getMax()); + + float[] boundsValues = getBounds().toFloatArray(); + int boundsSize = boundsValues.length; + if (boundsSize == 0 || x < boundsValues[0]) + { + function = PDFunction.create(getFunctions().get(0)); + PDRange encode = getEncodeForParameter(0); + if (boundsSize == 0) + { + x = interpolate(x, domain.getMin(), domain.getMax(), encode.getMin(), encode.getMax()); + } + else + { + x = interpolate(x, domain.getMin(), boundsValues[0], encode.getMin(), encode.getMax()); + } + } + else + { + for (int i=0; i= boundsValues[i] && x < boundsValues[i+1] ) + { + function = PDFunction.create(getFunctions().get(i+1)); + PDRange encode = getEncodeForParameter(i+1); + x = interpolate(x, boundsValues[i], boundsValues[i+1], encode.getMin(), encode.getMax()); + break; + } + } + if(function==null) //must be in last partition + { + function = PDFunction.create(getFunctions().get(boundsSize+1)); + PDRange encode = getEncodeForParameter(boundsSize+1); + x = interpolate(x, boundsValues[boundsSize-1], domain.getMax(), encode.getMin(), encode.getMax()); + } + } + COSArray functionValues = new COSArray(); + functionValues.add(new COSFloat(x)); + COSArray functionResult = function.eval(functionValues); + // clip to range if available + return clipToRange(functionResult); + } + + /** + * Returns all functions values as COSArray. + * + * @return the functions array. + */ + public COSArray getFunctions() + { + if (functions == null) + { + functions = (COSArray)(getDictionary().getDictionaryObject( COSName.FUNCTIONS )); + } + return functions; + } + + /** + * Returns all bounds values as COSArray. + * + * @return the bounds array. + */ + public COSArray getBounds() + { + if (bounds == null) + { + bounds = (COSArray)(getDictionary().getDictionaryObject( COSName.BOUNDS )); + } + return bounds; + } + + /** + * Returns all encode values as COSArray. + * + * @return the encode array. + */ + public COSArray getEncode() + { + if (encode == null) + { + encode = (COSArray)(getDictionary().getDictionaryObject( COSName.ENCODE )); + } + return encode; + } + + /** + * Get the encode for the input parameter. + * + * @param paramNum The function parameter number. + * + * @return The encode parameter range or null if none is set. + */ + private PDRange getEncodeForParameter(int n) + { + COSArray encodeValues = getEncode(); + return new PDRange( encodeValues, n ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType4.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType4.java new file mode 100644 index 000000000..44d60ebd1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/PDFunctionType4.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.common.function; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; + +import java.io.IOException; + +/** + * This class represents a type 4 function in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class PDFunctionType4 extends PDFunction +{ + + /** + * Constructor. + * + * @param functionStream The function . + */ + public PDFunctionType4(COSBase function) + { + super( function ); + } + + + /** + * {@inheritDoc} + */ + public int getFunctionType() + { + return 4; + } + + /** + * {@inheritDoc} + */ + public COSArray eval(COSArray input) throws IOException + { + //Implementation here will require evaluation of PostScript functions. + //See section 3.9.4 of the PDF Reference. + throw new IOException("Not Implemented"); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/package.html new file mode 100644 index 000000000..6ed4d6d61 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/function/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains functions that are available in the PDF specification. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/package.html new file mode 100644 index 000000000..1a1dffac6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/common/package.html @@ -0,0 +1,25 @@ + + + + + + + +High level PD classes that are used throughout several packages are placed in the PDModel common package. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDAttributeObject.java new file mode 100644 index 000000000..afb86a2e0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDAttributeObject.java @@ -0,0 +1,248 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDDictionaryWrapper; +import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDExportFormatAttributeObject; +import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDLayoutAttributeObject; +import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDListAttributeObject; +import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDPrintFieldAttributeObject; +import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDTableAttributeObject; + +/** + * An attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + * + */ +public abstract class PDAttributeObject extends PDDictionaryWrapper +{ + + /** + * Creates an attribute object. + * + * @param dictionary the dictionary + * @return the attribute object + */ + public static PDAttributeObject create(COSDictionary dictionary) + { + String owner = dictionary.getNameAsString(COSName.O); + if (PDUserAttributeObject.OWNER_USER_PROPERTIES.equals(owner)) + { + return new PDUserAttributeObject(dictionary); + } + else if (PDListAttributeObject.OWNER_LIST.equals(owner)) + { + return new PDListAttributeObject(dictionary); + } + else if (PDPrintFieldAttributeObject.OWNER_PRINT_FIELD.equals(owner)) + { + return new PDPrintFieldAttributeObject(dictionary); + } + else if (PDTableAttributeObject.OWNER_TABLE.equals(owner)) + { + return new PDTableAttributeObject(dictionary); + } + else if (PDLayoutAttributeObject.OWNER_LAYOUT.equals(owner)) + { + return new PDLayoutAttributeObject(dictionary); + } + else if (PDExportFormatAttributeObject.OWNER_XML_1_00.equals(owner) + || PDExportFormatAttributeObject.OWNER_HTML_3_20.equals(owner) + || PDExportFormatAttributeObject.OWNER_HTML_4_01.equals(owner) + || PDExportFormatAttributeObject.OWNER_OEB_1_00.equals(owner) + || PDExportFormatAttributeObject.OWNER_RTF_1_05.equals(owner) + || PDExportFormatAttributeObject.OWNER_CSS_1_00.equals(owner) + || PDExportFormatAttributeObject.OWNER_CSS_2_00.equals(owner)) + { + return new PDExportFormatAttributeObject(dictionary); + } + return new PDDefaultAttributeObject(dictionary); + } + + private PDStructureElement structureElement; + + /** + * Gets the structure element. + * + * @return the structure element + */ + private PDStructureElement getStructureElement() + { + return this.structureElement; + } + + /** + * Sets the structure element. + * + * @param structureElement the structure element + */ + protected void setStructureElement(PDStructureElement structureElement) + { + this.structureElement = structureElement; + } + + + /** + * Default constructor. + */ + public PDAttributeObject() + { + } + + /** + * Creates a new attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Returns the owner of the attributes. + * + * @return the owner of the attributes + */ + public String getOwner() + { + return this.getCOSDictionary().getNameAsString(COSName.O); + } + + /** + * Sets the owner of the attributes. + * + * @param owner the owner of the attributes + */ + protected void setOwner(String owner) + { + this.getCOSDictionary().setName(COSName.O, owner); + } + + /** + * Detects whether there are no properties in the attribute object. + * + * @return true if the attribute object is empty, + * false otherwise + */ + public boolean isEmpty() + { + // only entry is the owner? + return (this.getCOSDictionary().size() == 1) && (this.getOwner() != null); + } + + + /** + * Notifies the attribute object change listeners if the attribute is changed. + * + * @param oldBase old value + * @param newBase new value + */ + protected void potentiallyNotifyChanged(COSBase oldBase, COSBase newBase) + { + if (this.isValueChanged(oldBase, newBase)) + { + this.notifyChanged(); + } + } + + /** + * Is the value changed? + * + * @param oldValue old value + * @param newValue new value + * @return true if the value is changed, false + * otherwise + */ + private boolean isValueChanged(COSBase oldValue, COSBase newValue) + { + if (oldValue == null) + { + if (newValue == null) + { + return false; + } + return true; + } + return !oldValue.equals(newValue); + } + + /** + * Notifies the attribute object change listeners about a change in this + * attribute object. + */ + protected void notifyChanged() + { + if (this.getStructureElement() != null) + { + this.getStructureElement().attributeChanged(this); + } + } + + @Override + public String toString() + { + return new StringBuilder("O=").append(this.getOwner()).toString(); + } + + /** + * Creates a String representation of an Object array. + * + * @param array the Object array + * @return the String representation + */ + protected static String arrayToString(Object[] array) + { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < array.length; i++) + { + if (i > 0) + { + sb.append(", "); + } + sb.append(array[i]); + } + return sb.append(']').toString(); + } + + /** + * Creates a String representation of a float array. + * + * @param array the float array + * @return the String representation + */ + protected static String arrayToString(float[] array) + { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < array.length; i++) + { + if (i > 0) + { + sb.append(", "); + } + sb.append(array[i]); + } + return sb.append(']').toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDDefaultAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDDefaultAttributeObject.java new file mode 100644 index 000000000..f11953a36 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDDefaultAttributeObject.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * A default attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDDefaultAttributeObject extends PDAttributeObject +{ + + /** + * Default constructor. + */ + public PDDefaultAttributeObject() + { + } + + /** + * Creates a default attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDDefaultAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the attribute names. + * + * @return the attribute names + */ + public List getAttributeNames() + { + List attrNames = new ArrayList(); + for (Entry entry : this.getCOSDictionary().entrySet()) + { + COSName key = entry.getKey(); + if (!COSName.O.equals(key)) + { + attrNames.add(key.getName()); + } + } + return attrNames; + } + + /** + * Gets the attribute value for a given name. + * + * @param attrName the given attribute name + * @return the attribute value for a given name + */ + public COSBase getAttributeValue(String attrName) + { + return this.getCOSDictionary().getDictionaryObject(attrName); + } + + /** + * Gets the attribute value for a given name. + * + * @param attrName the given attribute name + * @param defaultValue the default value + * @return the attribute value for a given name + */ + protected COSBase getAttributeValue(String attrName, COSBase defaultValue) + { + COSBase value = this.getCOSDictionary().getDictionaryObject(attrName); + if (value == null) + { + return defaultValue; + } + return value; + } + + /** + * Sets an attribute. + * + * @param attrName the attribute name + * @param attrValue the attribute value + */ + public void setAttribute(String attrName, COSBase attrValue) + { + COSBase old = this.getAttributeValue(attrName); + this.getCOSDictionary().setItem(COSName.getPDFName(attrName), attrValue); + this.potentiallyNotifyChanged(old, attrValue); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder().append(super.toString()) + .append(", attributes={"); + Iterator it = this.getAttributeNames().iterator(); + while (it.hasNext()) + { + String name = it.next(); + sb.append(name).append('=').append(this.getAttributeValue(name)); + if (it.hasNext()) + { + sb.append(", "); + } + } + return sb.append('}').toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java new file mode 100644 index 000000000..63f4fcaae --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkInfo.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * The MarkInfo provides additional information relevant to specialized + * uses of structured documents. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkedContentReference.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkedContentReference.java new file mode 100644 index 000000000..e7a25e09b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDMarkedContentReference.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * A marked-content reference. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDMarkedContentReference implements COSObjectable +{ + + public static final String TYPE = "MCR"; + + private COSDictionary dictionary; + + protected COSDictionary getCOSDictionary() + { + return this.dictionary; + } + + /** + * Default constructor + */ + public PDMarkedContentReference() + { + this.dictionary = new COSDictionary(); + this.dictionary.setName(COSName.TYPE, TYPE); + } + + /** + * Constructor for an existing marked content reference. + * + * @param pageDic the page dictionary + * @param mcid the marked content indentifier + */ + public PDMarkedContentReference(COSDictionary dictionary) + { + this.dictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.dictionary; + } + + /** + * Gets the page. + * + * @return the page + */ + public PDPage getPage() + { + COSDictionary pg = (COSDictionary) this.getCOSDictionary() + .getDictionaryObject(COSName.PG); + if (pg != null) + { + return new PDPage(pg); + } + return null; + } + + /** + * Sets the page. + * + * @param page the page + */ + public void setPage(PDPage page) + { + this.getCOSDictionary().setItem(COSName.PG, page); + } + + /** + * Gets the marked content identifier. + * + * @return the marked content identifier + */ + public int getMCID() + { + return this.getCOSDictionary().getInt(COSName.MCID); + } + + /** + * Sets the marked content identifier. + * + * @param mcid the marked content identifier + */ + public void setMCID(int mcid) + { + this.getCOSDictionary().setInt(COSName.MCID, mcid); + } + + + @Override + public String toString() + { + return new StringBuilder() + .append("mcid=").append(this.getMCID()).toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDObjectReference.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDObjectReference.java new file mode 100644 index 000000000..ee9c73f91 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDObjectReference.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; + +/** + * An object reference. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDObjectReference implements COSObjectable +{ + + public static final String TYPE = "OBJR"; + + private COSDictionary dictionary; + + protected COSDictionary getCOSDictionary() + { + return this.dictionary; + } + + /** + * Default Constructor. + * + */ + public PDObjectReference() + { + this.dictionary = new COSDictionary(); + this.dictionary.setName(COSName.TYPE, TYPE); + } + + /** + * Constructor for an existing object reference. + * + * @param dictionary The existing dictionary. + */ + public PDObjectReference(COSDictionary dictionary) + { + this.dictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.dictionary; + } + + /** + * Gets a higher-level object for the referenced object. + * Currently this method may return a {@link PDAnnotation}, + * a {@link PDXObject} or null. + * + * @return a higher-level object for the referenced object + */ + public COSObjectable getReferencedObject() + { + COSBase obj = this.getCOSDictionary().getDictionaryObject(COSName.OBJ); + try + { + return PDAnnotation.createAnnotation(obj); + } + catch (IOException e) + { + // No Annotation + try + { + return PDXObject.createXObject(obj); + } + catch (IOException e1) + { + // No XObject + // TODO what else can be the target of the object reference? + } + } + return null; + } + + /** + * Sets the referenced annotation. + * + * @param annotation the referenced annotation + */ + public void setReferencedObject(PDAnnotation annotation) + { + this.getCOSDictionary().setItem(COSName.OBJ, annotation); + } + + /** + * Sets the referenced XObject. + * + * @param xobject the referenced XObject + */ + public void setReferencedObject(PDXObject xobject) + { + this.getCOSDictionary().setItem(COSName.OBJ, xobject); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java new file mode 100644 index 000000000..ad1213acd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureElement.java @@ -0,0 +1,755 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.util.Iterator; +import java.util.Map; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; + +/** + * A structure element. + * + * @author Ben Litchfield, + * Johannes Koch + * @version $Revision: 1.3 $ + */ +public class PDStructureElement extends PDStructureNode +{ + public static final String TYPE = "StructElem"; + + + /** + * Constructor with required values. + * + * @param structureType the structure type + * @param parent the parent structure node + */ + public PDStructureElement(String structureType, PDStructureNode parent) + { + super(TYPE); + this.setStructureType(structureType); + this.setParent(parent); + } + + /** + * Constructor for an existing structure element. + * + * @param dic The existing dictionary. + */ + public PDStructureElement( COSDictionary dic ) + { + super(dic); + } + + + /** + * Returns the structure type (S). + * + * @return the structure type + */ + public String getStructureType() + { + return this.getCOSDictionary().getNameAsString(COSName.S); + } + + /** + * Sets the structure type (S). + * + * @param structureType the structure type + */ + public void setStructureType(String structureType) + { + this.getCOSDictionary().setName(COSName.S, structureType); + } + + /** + * Returns the parent in the structure hierarchy (P). + * + * @return the parent in the structure hierarchy + */ + public PDStructureNode getParent() + { + COSDictionary p = (COSDictionary) this.getCOSDictionary() + .getDictionaryObject(COSName.P); + if (p == null) + { + return null; + } + return PDStructureNode.create((COSDictionary) p); + } + + /** + * Sets the parent in the structure hierarchy (P). + * + * @param structureNode the parent in the structure hierarchy + */ + public void setParent(PDStructureNode structureNode) + { + this.getCOSDictionary().setItem(COSName.P, structureNode); + } + + /** + * Returns the element identifier (ID). + * + * @return the element identifier + */ + public String getElementIdentifier() + { + return this.getCOSDictionary().getString(COSName.ID); + } + + /** + * Sets the element identifier (ID). + * + * @param id the element identifier + */ + public void setElementIdentifier(String id) + { + this.getCOSDictionary().setString(COSName.ID, id); + } + + /** + * Returns the page on which some or all of the content items designated by + * the K entry shall be rendered (Pg). + * + * @return the page on which some or all of the content items designated by + * the K entry shall be rendered + */ + public PDPage getPage() + { + COSDictionary pageDic = (COSDictionary) this.getCOSDictionary() + .getDictionaryObject(COSName.PG); + if (pageDic == null) + { + return null; + } + return new PDPage(pageDic); + } + + /** + * Sets the page on which some or all of the content items designated by + * the K entry shall be rendered (Pg). + * @param page the page on which some or all of the content items designated + * by the K entry shall be rendered. + */ + public void setPage(PDPage page) + { + this.getCOSDictionary().setItem(COSName.PG, page); + } + + /** + * Returns the attributes together with their revision numbers (A). + * + * @return the attributes + */ + public Revisions getAttributes() + { + Revisions attributes = + new Revisions(); + COSBase a = this.getCOSDictionary().getDictionaryObject(COSName.A); + if (a instanceof COSArray) + { + COSArray aa = (COSArray) a; + Iterator it = aa.iterator(); + PDAttributeObject ao = null; + while (it.hasNext()) + { + COSBase item = it.next(); + if (item instanceof COSDictionary) + { + ao = PDAttributeObject.create((COSDictionary) item); + ao.setStructureElement(this); + attributes.addObject(ao, 0); + } + else if (item instanceof COSInteger) + { + attributes.setRevisionNumber(ao, + ((COSInteger) item).intValue()); + } + } + } + if (a instanceof COSDictionary) + { + PDAttributeObject ao = PDAttributeObject.create((COSDictionary) a); + ao.setStructureElement(this); + attributes.addObject(ao, 0); + } + return attributes; + } + + /** + * Sets the attributes together with their revision numbers (A). + * + * @param attributes the attributes + */ + public void setAttributes(Revisions attributes) + { + COSName key = COSName.A; + if ((attributes.size() == 1) && (attributes.getRevisionNumber(0) == 0)) + { + PDAttributeObject attributeObject = attributes.getObject(0); + attributeObject.setStructureElement(this); + this.getCOSDictionary().setItem(key, attributeObject); + return; + } + COSArray array = new COSArray(); + for (int i = 0; i < attributes.size(); i++) + { + PDAttributeObject attributeObject = attributes.getObject(i); + attributeObject.setStructureElement(this); + int revisionNumber = attributes.getRevisionNumber(i); + if (revisionNumber < 0) + { + // TODO throw Exception because revision number must be > -1? + } + array.add(attributeObject); + array.add(COSInteger.get(revisionNumber)); + } + this.getCOSDictionary().setItem(key, array); + } + + /** + * Adds an attribute object. + * + * @param attributeObject the attribute object + */ + public void addAttribute(PDAttributeObject attributeObject) + { + COSName key = COSName.A; + attributeObject.setStructureElement(this); + COSBase a = this.getCOSDictionary().getDictionaryObject(key); + COSArray array = null; + if (a instanceof COSArray) + { + array = (COSArray) a; + } + else + { + array = new COSArray(); + if (a != null) + { + array.add(a); + array.add(COSInteger.get(0)); + } + } + this.getCOSDictionary().setItem(key, array); + array.add(attributeObject); + array.add(COSInteger.get(this.getRevisionNumber())); + } + + /** + * Removes an attribute object. + * + * @param attributeObject the attribute object + */ + public void removeAttribute(PDAttributeObject attributeObject) + { + COSName key = COSName.A; + COSBase a = this.getCOSDictionary().getDictionaryObject(key); + if (a instanceof COSArray) + { + COSArray array = (COSArray) a; + array.remove(attributeObject.getCOSObject()); + if ((array.size() == 2) && (array.getInt(1) == 0)) + { + this.getCOSDictionary().setItem(key, array.getObject(0)); + } + } + else + { + COSBase directA = a; + if (a instanceof COSObject) + { + directA = ((COSObject) a).getObject(); + } + if (attributeObject.getCOSObject().equals(directA)) + { + this.getCOSDictionary().setItem(key, null); + } + } + attributeObject.setStructureElement(null); + } + + /** + * Updates the revision number for the given attribute object. + * + * @param attributeObject the attribute object + */ + public void attributeChanged(PDAttributeObject attributeObject) + { + COSName key = COSName.A; + COSBase a = this.getCOSDictionary().getDictionaryObject(key); + if (a instanceof COSArray) + { + COSArray array = (COSArray) a; + for (int i = 0; i < array.size(); i++) + { + COSBase entry = array.getObject(i); + if (entry.equals(attributeObject.getCOSObject())) + { + COSBase next = array.get(i + 1); + if (next instanceof COSInteger) + { + array.set(i + 1, COSInteger.get(this.getRevisionNumber())); + } + } + } + } + else + { + COSArray array = new COSArray(); + array.add(a); + array.add(COSInteger.get(this.getRevisionNumber())); + this.getCOSDictionary().setItem(key, array); + } + } + + /** + * Returns the class names together with their revision numbers (C). + * + * @return the class names + */ + public Revisions getClassNames() + { + COSName key = COSName.C; + Revisions classNames = new Revisions(); + COSBase c = this.getCOSDictionary().getDictionaryObject(key); + if (c instanceof COSName) + { + classNames.addObject(((COSName) c).getName(), 0); + } + if (c instanceof COSArray) + { + COSArray array = (COSArray) c; + Iterator it = array.iterator(); + String className = null; + while (it.hasNext()) + { + COSBase item = it.next(); + if (item instanceof COSName) + { + className = ((COSName) item).getName(); + classNames.addObject(className, 0); + } + else if (item instanceof COSInteger) + { + classNames.setRevisionNumber(className, + ((COSInteger) item).intValue()); + } + } + } + return classNames; + } + + /** + * Sets the class names together with their revision numbers (C). + * + * @param classNames the class names + */ + public void setClassNames(Revisions classNames) + { + if (classNames == null) + { + return; + } + COSName key = COSName.C; + if ((classNames.size() == 1) && (classNames.getRevisionNumber(0) == 0)) + { + String className = classNames.getObject(0); + this.getCOSDictionary().setName(key, className); + return; + } + COSArray array = new COSArray(); + for (int i = 0; i < classNames.size(); i++) + { + String className = classNames.getObject(i); + int revisionNumber = classNames.getRevisionNumber(i); + if (revisionNumber < 0) + { + // TODO throw Exception because revision number must be > -1? + } + array.add(COSName.getPDFName(className)); + array.add(COSInteger.get(revisionNumber)); + } + this.getCOSDictionary().setItem(key, array); + } + + /** + * Adds a class name. + * + * @param className the class name + */ + public void addClassName(String className) + { + if (className == null) + { + return; + } + COSName key = COSName.C; + COSBase c = this.getCOSDictionary().getDictionaryObject(key); + COSArray array = null; + if (c instanceof COSArray) + { + array = (COSArray) c; + } + else + { + array = new COSArray(); + if (c != null) + { + array.add(c); + array.add(COSInteger.get(0)); + } + } + this.getCOSDictionary().setItem(key, array); + array.add(COSName.getPDFName(className)); + array.add(COSInteger.get(this.getRevisionNumber())); + } + + /** + * Removes a class name. + * + * @param className the class name + */ + public void removeClassName(String className) + { + if (className == null) + { + return; + } + COSName key = COSName.C; + COSBase c = this.getCOSDictionary().getDictionaryObject(key); + COSName name = COSName.getPDFName(className); + if (c instanceof COSArray) + { + COSArray array = (COSArray) c; + array.remove(name); + if ((array.size() == 2) && (array.getInt(1) == 0)) + { + this.getCOSDictionary().setItem(key, array.getObject(0)); + } + } + else + { + COSBase directC = c; + if (c instanceof COSObject) + { + directC = ((COSObject) c).getObject(); + } + if (name.equals(directC)) + { + this.getCOSDictionary().setItem(key, null); + } + } + } + + /** + * Returns the revision number (R). + * + * @return the revision number + */ + public int getRevisionNumber() + { + return this.getCOSDictionary().getInt(COSName.R, 0); + } + + /** + * Sets the revision number (R). + * + * @param revisionNumber the revision number + */ + public void setRevisionNumber(int revisionNumber) + { + if (revisionNumber < 0) + { + // TODO throw Exception because revision number must be > -1? + } + this.getCOSDictionary().setInt(COSName.R, revisionNumber); + } + + /** + * Increments th revision number + */ + public void incrementRevisionNumber() + { + this.setRevisionNumber(this.getRevisionNumber() + 1); + } + + /** + * Returns the title (T). + * + * @return the title + */ + public String getTitle() + { + return this.getCOSDictionary().getString(COSName.T); + } + + /** + * Sets the title (T). + * + * @param title the title + */ + public void setTitle(String title) + { + this.getCOSDictionary().setString(COSName.T, title); + } + + /** + * Returns the language (Lang). + * + * @return the language + */ + public String getLanguage() + { + return this.getCOSDictionary().getString(COSName.LANG); + } + + /** + * Sets the language (Lang). + * + * @param language the language + */ + public void setLanguage(String language) + { + this.getCOSDictionary().setString(COSName.LANG, language); + } + + /** + * Returns the alternate description (Alt). + * + * @return the alternate description + */ + public String getAlternateDescription() + { + return this.getCOSDictionary().getString(COSName.ALT); + } + + /** + * Sets the alternate description (Alt). + * + * @param alternateDescription the alternate description + */ + public void setAlternateDescription(String alternateDescription) + { + this.getCOSDictionary().setString(COSName.ALT, alternateDescription); + } + + /** + * Returns the expanded form (E). + * + * @return the expanded form + */ + public String getExpandedForm() + { + return this.getCOSDictionary().getString(COSName.E); + } + + /** + * Sets the expanded form (E). + * + * @param expandedForm the expanded form + */ + public void setExpandedForm(String expandedForm) + { + this.getCOSDictionary().setString(COSName.E, expandedForm); + } + + /** + * Returns the actual text (ActualText). + * + * @return the actual text + */ + public String getActualText() + { + return this.getCOSDictionary().getString(COSName.ACTUAL_TEXT); + } + + /** + * Sets the actual text (ActualText). + * + * @param actualText the actual text + */ + public void setActualText(String actualText) + { + this.getCOSDictionary().setString(COSName.ACTUAL_TEXT, actualText); + } + + /** + * Returns the standard structure type, the actual structure type is mapped + * to in the role map. + * + * @return the standard structure type + */ + public String getStandardStructureType() + { + String type = this.getStructureType(); + String mappedType; + while (true) + { + mappedType = this.getRoleMap().get(type); + if ((mappedType == null) || type.equals(mappedType)) + { + break; + } + type = mappedType; + } + return type; + } + + /** + * Appends a marked-content sequence kid. + * + * @param markedContent the marked-content sequence + */ + public void appendKid(PDMarkedContent markedContent) + { + if (markedContent == null) + { + return; + } + this.appendKid(COSInteger.get(markedContent.getMCID())); + } + + /** + * Appends a marked-content reference kid. + * + * @param markedContentReference the marked-content reference + */ + public void appendKid(PDMarkedContentReference markedContentReference) + { + this.appendObjectableKid(markedContentReference); + } + + /** + * Appends an object reference kid. + * + * @param objectReference the object reference + */ + public void appendKid(PDObjectReference objectReference) + { + this.appendObjectableKid(objectReference); + } + + /** + * Inserts a marked-content identifier kid before a reference kid. + * + * @param markedContentIdentifier the marked-content identifier + * @param refKid the reference kid + */ + public void insertBefore(COSInteger markedContentIdentifier, Object refKid) + { + this.insertBefore((COSBase) markedContentIdentifier, refKid); + } + + /** + * Inserts a marked-content reference kid before a reference kid. + * + * @param markedContentReference the marked-content reference + * @param refKid the reference kid + */ + public void insertBefore(PDMarkedContentReference markedContentReference, + Object refKid) + { + this.insertObjectableBefore(markedContentReference, refKid); + } + + /** + * Inserts an object reference kid before a reference kid. + * + * @param objectReference the object reference + * @param refKid the reference kid + */ + public void insertBefore(PDObjectReference objectReference, Object refKid) + { + this.insertObjectableBefore(objectReference, refKid); + } + + /** + * Removes a marked-content identifier kid. + * + * @param markedContentIdentifier the marked-content identifier + */ + public void removeKid(COSInteger markedContentIdentifier) + { + this.removeKid((COSBase) markedContentIdentifier); + } + + /** + * Removes a marked-content reference kid. + * + * @param markedContentReference the marked-content reference + */ + public void removeKid(PDMarkedContentReference markedContentReference) + { + this.removeObjectableKid(markedContentReference); + } + + /** + * Removes an object reference kid. + * + * @param objectReference the object reference + */ + public void removeKid(PDObjectReference objectReference) + { + this.removeObjectableKid(objectReference); + } + + + /** + * Returns the structure tree root. + * + * @return the structure tree root + */ + private PDStructureTreeRoot getStructureTreeRoot() + { + PDStructureNode parent = this.getParent(); + while (parent instanceof PDStructureElement) + { + parent = ((PDStructureElement) parent).getParent(); + } + if (parent instanceof PDStructureTreeRoot) + { + return (PDStructureTreeRoot) parent; + } + return null; + } + + /** + * Returns the role map. + * + * @return the role map + */ + private Map getRoleMap() + { + PDStructureTreeRoot root = this.getStructureTreeRoot(); + if (root != null) + { + return root.getRoleMap(); + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureNode.java new file mode 100644 index 000000000..82f8cc5e5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureNode.java @@ -0,0 +1,428 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; + +/** + * A node in the structure tree. + * + * @author Koch + * @version $Revision: $ + */ +public abstract class PDStructureNode implements COSObjectable +{ + + /** + * Creates a node in the structure tree. Can be either a structure tree root, + * or a structure element. + * + * @param node the node dictionary + * @return the structure node + */ + public static PDStructureNode create(COSDictionary node) + { + String type = node.getNameAsString(COSName.TYPE); + if ("StructTreeRoot".equals(type)) + { + return new PDStructureTreeRoot(node); + } + if ((type == null) || "StructElem".equals(type)) + { + return new PDStructureElement(node); + } + throw new IllegalArgumentException("Dictionary must not include a Type entry with a value that is neither StructTreeRoot nor StructElem."); + } + + + private COSDictionary dictionary; + + protected COSDictionary getCOSDictionary() + { + return dictionary; + } + + /** + * Constructor. + * + * @param type the type + */ + protected PDStructureNode(String type) + { + this.dictionary = new COSDictionary(); + this.dictionary.setName(COSName.TYPE, type); + } + + /** + * Constructor for an existing structure node. + * + * @param dictionary The existing dictionary. + */ + protected PDStructureNode(COSDictionary dictionary) + { + this.dictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.dictionary; + } + + /** + * Returns the type. + * + * @return the type + */ + public String getType() + { + return this.getCOSDictionary().getNameAsString(COSName.TYPE); + } + + /** + * Returns a list of objects for the kids (K). + * + * @return a list of objects for the kids + */ + public List getKids() + { + List kidObjects = new ArrayList(); + COSBase k = this.getCOSDictionary().getDictionaryObject(COSName.K); + if (k instanceof COSArray) + { + Iterator kids = ((COSArray) k).iterator(); + while (kids.hasNext()) + { + COSBase kid = kids.next(); + Object kidObject = this.createObject(kid); + if (kidObject != null) + { + kidObjects.add(kidObject); + } + } + } + else + { + Object kidObject = this.createObject(k); + if (kidObject != null) + { + kidObjects.add(kidObject); + } + } + return kidObjects; + } + + /** + * Sets the kids (K). + * + * @param kids the kids + */ + public void setKids(List kids) + { + this.getCOSDictionary().setItem(COSName.K, + COSArrayList.converterToCOSArray(kids)); + } + + /** + * Appends a structure element kid. + * + * @param structureElement the structure element + */ + public void appendKid(PDStructureElement structureElement) + { + this.appendObjectableKid(structureElement); + structureElement.setParent(this); + } + + /** + * Appends an objectable kid. + * + * @param objectable the objectable + */ + protected void appendObjectableKid(COSObjectable objectable) + { + if (objectable == null) + { + return; + } + this.appendKid(objectable.getCOSObject()); + } + + /** + * Appends a COS base kid. + * + * @param object the COS base + */ + protected void appendKid(COSBase object) + { + if (object == null) + { + return; + } + COSBase k = this.getCOSDictionary().getDictionaryObject(COSName.K); + if (k == null) + { + // currently no kid: set new kid as kids + this.getCOSDictionary().setItem(COSName.K, object); + } + else if (k instanceof COSArray) + { + // currently more than one kid: add new kid to existing array + COSArray array = (COSArray) k; + array.add(object); + } + else + { + // currently one kid: put current and new kid into array and set array as kids + COSArray array = new COSArray(); + array.add(k); + array.add(object); + this.getCOSDictionary().setItem(COSName.K, array); + } + } + + /** + * Inserts a structure element kid before a reference kid. + * + * @param newKid the structure element + * @param refKid the reference kid + */ + public void insertBefore(PDStructureElement newKid, Object refKid) + { + this.insertObjectableBefore(newKid, refKid); + } + + /** + * Inserts an objectable kid before a reference kid. + * + * @param newKid the objectable + * @param refKid the reference kid + */ + protected void insertObjectableBefore(COSObjectable newKid, Object refKid) + { + if (newKid == null) + { + return; + } + this.insertBefore(newKid.getCOSObject(), refKid); + } + + /** + * Inserts an COS base kid before a reference kid. + * + * @param newKid the COS base + * @param refKid the reference kid + */ + protected void insertBefore(COSBase newKid, Object refKid) + { + if ((newKid == null) || (refKid == null)) + { + return; + } + COSBase k = this.getCOSDictionary().getDictionaryObject(COSName.K); + if (k == null) + { + return; + } + COSBase refKidBase = null; + if (refKid instanceof COSObjectable) + { + refKidBase = ((COSObjectable) refKid).getCOSObject(); + } + else if (refKid instanceof COSInteger) + { + refKidBase = (COSInteger) refKid; + } + if (k instanceof COSArray) + { + COSArray array = (COSArray) k; + int refIndex = array.indexOfObject(refKidBase); + array.add(refIndex, newKid.getCOSObject()); + } + else + { + boolean onlyKid = k.equals(refKidBase); + if (!onlyKid && (k instanceof COSObject)) + { + COSBase kObj = ((COSObject) k).getObject(); + onlyKid = kObj.equals(refKidBase); + } + if (onlyKid) + { + COSArray array = new COSArray(); + array.add(newKid); + array.add(refKidBase); + this.getCOSDictionary().setItem(COSName.K, array); + } + } + } + + /** + * Removes a structure element kid. + * + * @param structureElement the structure element + * @return true if the kid was removed, false otherwise + */ + public boolean removeKid(PDStructureElement structureElement) + { + boolean removed = this.removeObjectableKid(structureElement); + if (removed) + { + structureElement.setParent(null); + } + return removed; + } + + /** + * Removes an objectable kid. + * + * @param objectable the objectable + * @return true if the kid was removed, false otherwise + */ + protected boolean removeObjectableKid(COSObjectable objectable) + { + if (objectable == null) + { + return false; + } + return this.removeKid(objectable.getCOSObject()); + } + + /** + * Removes a COS base kid. + * + * @param object the COS base + * @return true if the kid was removed, false otherwise + */ + protected boolean removeKid(COSBase object) + { + if (object == null) + { + return false; + } + COSBase k = this.getCOSDictionary().getDictionaryObject(COSName.K); + if (k == null) + { + // no kids: objectable is not a kid + return false; + } + else if (k instanceof COSArray) + { + // currently more than one kid: remove kid from existing array + COSArray array = (COSArray) k; + boolean removed = array.removeObject(object); + // if now only one kid: set remaining kid as kids + if (array.size() == 1) + { + this.getCOSDictionary().setItem(COSName.K, array.getObject(0)); + } + return removed; + } + else + { + // currently one kid: if current kid equals given object, remove kids entry + boolean onlyKid = k.equals(object); + if (!onlyKid && (k instanceof COSObject)) + { + COSBase kObj = ((COSObject) k).getObject(); + onlyKid = kObj.equals(object); + } + if (onlyKid) + { + this.getCOSDictionary().setItem(COSName.K, null); + return true; + } + return false; + } + } + + /** + * Creates an object for a kid of this structure node. + * The type of object depends on the type of the kid. It can be + *
    + *
  • a {@link PDStructureElement},
  • + *
  • a {@link PDAnnotation},
  • + *
  • a {@link PDXObject},
  • + *
  • a {@link PDMarkedContentReference}
  • + *
  • a {@link Integer}
  • + *
+ * + * @param kid the kid + * @return the object + */ + protected Object createObject(COSBase kid) + { + COSDictionary kidDic = null; + if (kid instanceof COSDictionary) + { + kidDic = (COSDictionary) kid; + } + else if (kid instanceof COSObject) + { + COSBase base = ((COSObject) kid).getObject(); + if (base instanceof COSDictionary) + { + kidDic = (COSDictionary) base; + } + } + if (kidDic != null) + { + String type = kidDic.getNameAsString(COSName.TYPE); + if ((type == null) || PDStructureElement.TYPE.equals(type)) + { + // A structure element dictionary denoting another structure + // element + return new PDStructureElement(kidDic); + } + else if (PDObjectReference.TYPE.equals(type)) + { + // An object reference dictionary denoting a PDF object + return new PDObjectReference(kidDic); + } + else if (PDMarkedContentReference.TYPE.equals(type)) + { + // A marked-content reference dictionary denoting a + // marked-content sequence + return new PDMarkedContentReference(kidDic); + } + } + else if (kid instanceof COSInteger) + { + // An integer marked-content identifier denoting a + // marked-content sequence + COSInteger mcid = (COSInteger) kid; + return mcid.intValue(); + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureTreeRoot.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureTreeRoot.java new file mode 100644 index 000000000..45acf19a0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDStructureTreeRoot.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSDictionaryMap; +import org.apache.pdfbox.pdmodel.common.PDNameTreeNode; + +/** + * A root of a structure tree. + * + * @author Ben Litchfield, + * Johannes Koch + * @version $Revision: 1.2 $ + */ +public class PDStructureTreeRoot extends PDStructureNode +{ + + public static final String TYPE = "StructTreeRoot"; + + + /** + * Default Constructor. + * + */ + public PDStructureTreeRoot() + { + super(TYPE); + } + + /** + * Constructor for an existing structure element. + * + * @param dic The existing dictionary. + */ + public PDStructureTreeRoot( COSDictionary dic ) + { + super(dic); + } + + + /** + * Returns the ID tree. + * + * @return the ID tree + */ + public PDNameTreeNode getIDTree() + { + COSDictionary idTreeDic = (COSDictionary) this.getCOSDictionary() + .getDictionaryObject(COSName.ID_TREE); + if (idTreeDic != null) + { + return new PDNameTreeNode(idTreeDic, PDStructureElement.class); + } + return null; + } + + /** + * Sets the ID tree. + * + * @param idTree the ID tree + */ + public void setIDTree(PDNameTreeNode idTree) + { + this.getCOSDictionary().setItem(COSName.ID_TREE, idTree); + } + + /** + * Returns the next key in the parent tree. + * + * @return the next key in the parent tree + */ + public int getParentTreeNextKey() + { + return this.getCOSDictionary().getInt(COSName.PARENT_TREE_NEXT_KEY); + } + + /** + * Returns the role map. + * + * @return the role map + */ + @SuppressWarnings("unchecked") + public Map getRoleMap() + { + COSBase rm = this.getCOSDictionary().getDictionaryObject(COSName.ROLE_MAP); + if (rm instanceof COSDictionary) + { + try + { + return COSDictionaryMap.convertBasicTypesToMap((COSDictionary) rm); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + return new Hashtable(); + } + + /** + * Sets the role map. + * + * @param roleMap the role map + */ + public void setRoleMap(Map roleMap) + { + COSDictionary rmDic = new COSDictionary(); + for (String key : roleMap.keySet()) + { + rmDic.setName(key, roleMap.get(key)); + } + this.getCOSDictionary().setItem(COSName.ROLE_MAP, rmDic); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserAttributeObject.java new file mode 100644 index 000000000..e94ac16e9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserAttributeObject.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * A User attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDUserAttributeObject extends PDAttributeObject +{ + + /** + * Attribute owner for user properties + */ + public static final String OWNER_USER_PROPERTIES = "UserProperties"; + + + /** + * Default constructor + */ + public PDUserAttributeObject() + { + this.setOwner(OWNER_USER_PROPERTIES); + } + + /** + * + * @param dictionary the dictionary + */ + public PDUserAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Returns the user properties. + * + * @return the user properties + */ + public List getOwnerUserProperties() + { + COSArray p = (COSArray) this.getCOSDictionary() + .getDictionaryObject(COSName.P); + List properties = new ArrayList(p.size()); + for (int i = 0; i < p.size(); i++) + { + properties.add( + new PDUserProperty((COSDictionary) p.getObject(i), this)); + } + return properties; + } + + /** + * Sets the user properties. + * + * @param userProperties the user properties + */ + public void setUserProperties(List userProperties) + { + COSArray p = new COSArray(); + for (PDUserProperty userProperty : userProperties) + { + p.add(userProperty); + } + this.getCOSDictionary().setItem(COSName.P, p); + } + + /** + * Adds a user property. + * + * @param userProperty the user property + */ + public void addUserProperty(PDUserProperty userProperty) + { + COSArray p = (COSArray) this.getCOSDictionary() + .getDictionaryObject(COSName.P); + p.add(userProperty); + this.notifyChanged(); + } + + /** + * Removes a user property. + * + * @param userProperty the user property + */ + public void removeUserProperty(PDUserProperty userProperty) + { + if (userProperty == null) + { + return; + } + COSArray p = (COSArray) this.getCOSDictionary() + .getDictionaryObject(COSName.P); + p.remove(userProperty.getCOSObject()); + this.notifyChanged(); + } + + public void userPropertyChanged(PDUserProperty userProperty) + { + + } + + @Override + public String toString() + { + return new StringBuilder().append(super.toString()) + .append(", userProperties=") + .append(this.getOwnerUserProperties()).toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserProperty.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserProperty.java new file mode 100644 index 000000000..9b463fe4c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/PDUserProperty.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDDictionaryWrapper; + +/** + * A user property. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDUserProperty extends PDDictionaryWrapper +{ + + private final PDUserAttributeObject userAttributeObject; + + /** + * Creates a new user property. + * + * @param the user attribute object + */ + public PDUserProperty(PDUserAttributeObject userAttributeObject) + { + this.userAttributeObject = userAttributeObject; + } + + /** + * Creates a user property with a given dictionary. + * + * @param dictionary the dictionary + * @param the user attribute object + */ + public PDUserProperty(COSDictionary dictionary, + PDUserAttributeObject userAttributeObject) + { + super(dictionary); + this.userAttributeObject = userAttributeObject; + } + + + /** + * Returns the property name. + * + * @return the property name + */ + public String getName() + { + return this.getCOSDictionary().getNameAsString(COSName.N); + } + + /** + * Sets the property name. + * + * @param name the property name + */ + public void setName(String name) + { + this.potentiallyNotifyChanged(this.getName(), name); + this.getCOSDictionary().setName(COSName.N, name); + } + + /** + * Returns the property value. + * + * @return the property value + */ + public COSBase getValue() + { + return this.getCOSDictionary().getDictionaryObject(COSName.V); + } + + /** + * Sets the property value. + * + * @param value the property value + */ + public void setValue(COSBase value) + { + this.potentiallyNotifyChanged(this.getValue(), value); + this.getCOSDictionary().setItem(COSName.V, value); + } + + /** + * Returns the string for the property value. + * + * @return the string for the property value + */ + public String getFormattedValue() + { + return this.getCOSDictionary().getString(COSName.F); + } + + /** + * Sets the string for the property value. + * + * @param formattedValue the string for the property value + */ + public void setFormattedValue(String formattedValue) + { + this.potentiallyNotifyChanged(this.getFormattedValue(), formattedValue); + this.getCOSDictionary().setString(COSName.F, formattedValue); + } + + /** + * Shall the property be hidden? + * + * @return true if the property shall be hidden, + * false otherwise + */ + public boolean isHidden() + { + return this.getCOSDictionary().getBoolean(COSName.H, false); + } + + /** + * Specifies whether the property shall be hidden. + * + * @param hidden true if the property shall be hidden, + * false otherwise + */ + public void setHidden(boolean hidden) + { + this.potentiallyNotifyChanged(this.isHidden(), hidden); + this.getCOSDictionary().setBoolean(COSName.H, hidden); + } + + + @Override + public String toString() + { + return new StringBuilder("Name=").append(this.getName()) + .append(", Value=").append(this.getValue()) + .append(", FormattedValue=").append(this.getFormattedValue()) + .append(", Hidden=").append(this.isHidden()).toString(); + } + + + /** + * Notifies the user attribute object if the user property is changed. + * + * @param oldEntry old entry + * @param newEntry new entry + */ + private void potentiallyNotifyChanged(Object oldEntry, Object newEntry) + { + if (this.isEntryChanged(oldEntry, newEntry)) + { + this.userAttributeObject.userPropertyChanged(this); + } + } + + /** + * Is the value changed? + * + * @param oldEntry old entry + * @param newEntry new entry + * @return true if the entry is changed, false + * otherwise + */ + private boolean isEntryChanged(Object oldEntry, Object newEntry) + { + if (oldEntry == null) + { + if (newEntry == null) + { + return false; + } + return true; + } + return !oldEntry.equals(newEntry); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/Revisions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/Revisions.java new file mode 100644 index 000000000..68761a42e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/Revisions.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Koch + * @version $Revision: $ + * + * @param the type of object to store the revision numbers with + */ +public class Revisions +{ + + private List objects; + private List revisionNumbers; + + private List getObjects() + { + if (this.objects == null) + { + this.objects = new ArrayList(); + } + return this.objects; + } + + private List getRevisionNumbers() + { + if (this.revisionNumbers == null) + { + this.revisionNumbers = new ArrayList(); + } + return this.revisionNumbers; + } + + + /** + * + */ + public Revisions() + { + } + + + /** + * Returns the object at the specified position. + * + * @param index the position + * @return the object + * @throws IndexOutOfBoundsException if the index is out of range + */ + public T getObject(int index) throws IndexOutOfBoundsException + { + return this.getObjects().get(index); + } + + /** + * Returns the revision number at the specified position. + * + * @param index the position + * @return the revision number + * @throws IndexOutOfBoundsException if the index is out of range + */ + public int getRevisionNumber(int index) throws IndexOutOfBoundsException + { + return this.getRevisionNumbers().get(index); + } + + /** + * Adds an object with a specified revision number. + * + * @param object the object + * @param revisionNumber the revision number + */ + public void addObject(T object, int revisionNumber) + { + this.getObjects().add(object); + this.getRevisionNumbers().add(revisionNumber); + } + + /** + * Sets the revision number of a specified object. + * + * @param object the object + * @param revisionNumber the revision number + */ + protected void setRevisionNumber(T object, int revisionNumber) + { + int index = this.getObjects().indexOf(object); + if (index > -1) + { + this.getRevisionNumbers().set(index, revisionNumber); + } + } + + /** + * Returns the size. + * + * @return the size + */ + public int size() + { + return this.getObjects().size(); + } + + /** + * {@inheritDoc} + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < this.getObjects().size(); i++) + { + if (i > 0) + { + sb.append("; "); + } + sb.append("object=").append(this.getObjects().get(i)) + .append(", revisionNumber=").append(this.getRevisionNumber(i)); + } + return sb.toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html new file mode 100644 index 000000000..6e90f6b6d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/logicalstructure/package.html @@ -0,0 +1,26 @@ + + + + + + + +The logical structure package provides a mechanism for incorporating +structural information about a document's content into a PDF file. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/PDMarkedContent.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/PDMarkedContent.java new file mode 100644 index 000000000..0376f161f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/PDMarkedContent.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.markedcontent; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDArtifactMarkedContent; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.util.TextPosition; + +/** + * A marked content. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDMarkedContent +{ + + /** + * Creates a marked-content sequence. + * + * @param tag the tag + * @param properties the properties + * @return the marked-content sequence + */ + public static PDMarkedContent create(COSName tag, COSDictionary properties) + { + if (COSName.ARTIFACT.equals(tag)) + { + new PDArtifactMarkedContent(properties); + } + return new PDMarkedContent(tag, properties); + } + + + private String tag; + private COSDictionary properties; + private List contents; + + + /** + * Creates a new marked content object. + * + * @param tag the tag + * @param properties the properties + */ + public PDMarkedContent(COSName tag, COSDictionary properties) + { + this.tag = tag == null ? null : tag.getName(); + this.properties = properties; + this.contents = new ArrayList(); + } + + + /** + * Gets the tag. + * + * @return the tag + */ + public String getTag() + { + return this.tag; + } + + /** + * Gets the properties. + * + * @return the properties + */ + public COSDictionary getProperties() + { + return this.properties; + } + + /** + * Gets the marked-content identifier. + * + * @return the marked-content identifier + */ + public int getMCID() + { + return this.getProperties() == null ? null : + this.getProperties().getInt(COSName.MCID); + } + + /** + * Gets the language (Lang). + * + * @return the language + */ + public String getLanguage() + { + return this.getProperties() == null ? null : + this.getProperties().getNameAsString(COSName.LANG); + } + + /** + * Gets the actual text (ActualText). + * + * @return the actual text + */ + public String getActualText() + { + return this.getProperties() == null ? null : + this.getProperties().getString(COSName.ACTUAL_TEXT); + } + + /** + * Gets the alternate description (Alt). + * + * @return the alternate description + */ + public String getAlternateDescription() + { + return this.getProperties() == null ? null : + this.getProperties().getString(COSName.ALT); + } + + /** + * Gets the expanded form (E). + * + * @return the expanded form + */ + public String getExpandedForm() + { + return this.getProperties() == null ? null : + this.getProperties().getString(COSName.E); + } + + /** + * Gets the contents of the marked content sequence. Can be + *
    + *
  • {@link TextPosition},
  • + *
  • {@link PDMarkedContent}, or
  • + *
  • {@link PDXObject}.
  • + *
+ * + * @return the contents of the marked content sequence + */ + public List getContents() + { + return this.contents; + } + + /** + * Adds a text position to the contents. + * + * @param text the text position + */ + public void addText(TextPosition text) + { + this.getContents().add(text); + } + + /** + * Adds a marked content to the contents. + * + * @param markedContent the marked content + */ + public void addMarkedContent(PDMarkedContent markedContent) + { + this.getContents().add(markedContent); + } + + /** + * Adds an XObject to the contents. + * + * @param xobject the XObject + */ + public void addXObject(PDXObject xobject) + { + this.getContents().add(xobject); + } + + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder("tag=").append(this.tag) + .append(", properties=").append(this.properties); + sb.append(", contents=").append(this.contents); + return sb.toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/package.html new file mode 100644 index 000000000..35ba6751f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/markedcontent/package.html @@ -0,0 +1,26 @@ + + + + + + + +The marked content package provides a mechanism for modeling marked-content +sequences. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java new file mode 100644 index 000000000..9c7d62978 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/PDBoxStyle.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.prepress; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.PDLineDashPattern; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; + +/** + * The Box Style specifies visual characteristics for displaying box areas. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 PDColorState 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 ); + } + PDColorState instance = new PDColorState( 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( PDColorState 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( COSInteger.THREE ); + dictionary.setItem( "D", d ); + } + COSArray lineArray = new COSArray(); + lineArray.add( d ); + //dash phase is not specified and assumed to be zero. + lineArray.add( COSInteger.ZERO ); + 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/package.html new file mode 100644 index 000000000..1331a2baa --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/prepress/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains classes for prepress support in PDFBox. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDArtifactMarkedContent.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDArtifactMarkedContent.java new file mode 100644 index 000000000..a00600052 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDArtifactMarkedContent.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; + +/** + * An artifact marked content. + * + * @author Johannes Koch + * @version $Revision: $ + * + */ +public class PDArtifactMarkedContent extends PDMarkedContent +{ + + public PDArtifactMarkedContent(COSDictionary properties) + { + super(COSName.ARTIFACT, properties); + } + + + /** + * Gets the type (Type). + * + * @return the type + */ + public String getType() + { + return this.getProperties().getNameAsString(COSName.TYPE); + } + + /** + * Gets the artifact's bounding box (BBox). + * + * @return the artifact's bounding box + */ + public PDRectangle getBBox() + { + PDRectangle retval = null; + COSArray a = (COSArray) this.getProperties().getDictionaryObject( + COSName.BBOX); + if (a != null) + { + retval = new PDRectangle(a); + } + return retval; + } + + /** + * Is the artifact attached to the top edge? + * + * @return true if the artifact is attached to the top edge, + * false otherwise + */ + public boolean isTopAttached() + { + return this.isAttached("Top"); + } + + /** + * Is the artifact attached to the bottom edge? + * + * @return true if the artifact is attached to the bottom edge, + * false otherwise + */ + public boolean isBottomAttached() + { + return this.isAttached("Bottom"); + } + + /** + * Is the artifact attached to the left edge? + * + * @return true if the artifact is attached to the left edge, + * false otherwise + */ + public boolean isLeftAttached() + { + return this.isAttached("Left"); + } + + /** + * Is the artifact attached to the right edge? + * + * @return true if the artifact is attached to the right edge, + * false otherwise + */ + public boolean isRightAttached() + { + return this.isAttached("Right"); + } + + /** + * Gets the subtype (Subtype). + * + * @return the subtype + */ + public String getSubtype() + { + return this.getProperties().getNameAsString(COSName.SUBTYPE); + } + + + /** + * Is the artifact attached to the given edge? + * + * @param edge the edge + * @return true if the artifact is attached to the given edge, + * false otherwise + */ + private boolean isAttached(String edge) + { + COSArray a = (COSArray) this.getProperties().getDictionaryObject( + COSName.ATTACHED); + if (a != null) + { + for (int i = 0; i < a.size(); i++) + { + if (edge.equals(a.getName(i))) + { + return true; + } + } + } + return false; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDExportFormatAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDExportFormatAttributeObject.java new file mode 100644 index 000000000..00edfd56b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDExportFormatAttributeObject.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; + +/** + * An Export Format attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDExportFormatAttributeObject extends PDLayoutAttributeObject +{ + + /** + * standard attribute owner: XML-1.00 + */ + public static final String OWNER_XML_1_00 = "XML-1.00"; + /** + * standard attribute owner: HTML-3.2 + */ + public static final String OWNER_HTML_3_20 = "HTML-3.2"; + /** + * standard attribute owner: HTML-4.01 + */ + public static final String OWNER_HTML_4_01 = "HTML-4.01"; + /** + * standard attribute owner: OEB-1.00 + */ + public static final String OWNER_OEB_1_00 = "OEB-1.00"; + /** + * standard attribute owner: RTF-1.05 + */ + public static final String OWNER_RTF_1_05 = "RTF-1.05"; + /** + * standard attribute owner: CSS-1.00 + */ + public static final String OWNER_CSS_1_00 = "CSS-1.00"; + /** + * standard attribute owner: CSS-2.00 + */ + public static final String OWNER_CSS_2_00 = "CSS-2.00"; + + + /** + * Default constructor. + */ + public PDExportFormatAttributeObject(String owner) + { + this.setOwner(owner); + } + + /** + * Creates a new ExportFormat attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDExportFormatAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the list numbering (ListNumbering). The default value is + * {@link #LIST_NUMBERING_NONE}. + * + * @return the list numbering + */ + public String getListNumbering() + { + return this.getName(PDListAttributeObject.LIST_NUMBERING, + PDListAttributeObject.LIST_NUMBERING_NONE); + } + + /** + * Sets the list numbering (ListNumbering). The value shall be one of the + * following: + *
    + *
  • {@link #LIST_NUMBERING_NONE},
  • + *
  • {@link #LIST_NUMBERING_DISC},
  • + *
  • {@link #LIST_NUMBERING_CIRCLE},
  • + *
  • {@link #LIST_NUMBERING_SQUARE},
  • + *
  • {@link #LIST_NUMBERING_DECIMAL},
  • + *
  • {@link #LIST_NUMBERING_UPPER_ROMAN},
  • + *
  • {@link #LIST_NUMBERING_LOWER_ROMAN},
  • + *
  • {@link #LIST_NUMBERING_UPPER_ALPHA},
  • + *
  • {@link #LIST_NUMBERING_LOWER_ALPHA}.
  • + *
+ * + * @param listNumbering the list numbering + */ + public void setListNumbering(String listNumbering) + { + this.setName(PDListAttributeObject.LIST_NUMBERING, listNumbering); + } + + /** + * Gets the number of rows in the enclosing table that shall be spanned by + * the cell (RowSpan). The default value is 1. + * + * @return the row span + */ + public int getRowSpan() + { + return this.getInteger(PDTableAttributeObject.ROW_SPAN, 1); + } + + /** + * Sets the number of rows in the enclosing table that shall be spanned by + * the cell (RowSpan). + * + * @param rowSpan the row span + */ + public void setRowSpan(int rowSpan) + { + this.setInteger(PDTableAttributeObject.ROW_SPAN, rowSpan); + } + + /** + * Gets the number of columns in the enclosing table that shall be spanned + * by the cell (ColSpan). The default value is 1. + * + * @return the column span + */ + public int getColSpan() + { + return this.getInteger(PDTableAttributeObject.COL_SPAN, 1); + } + + /** + * Sets the number of columns in the enclosing table that shall be spanned + * by the cell (ColSpan). + * + * @param colSpan the column span + */ + public void setColSpan(int colSpan) + { + this.setInteger(PDTableAttributeObject.COL_SPAN, colSpan); + } + + /** + * Gets the headers (Headers). An array of byte strings, where each string + * shall be the element identifier (see the + * {@link PDStructureElement#getElementIdentifier()}) for a TH structure + * element that shall be used as a header associated with this cell. + * + * @return the headers. + */ + public String[] getHeaders() + { + return this.getArrayOfString(PDTableAttributeObject.HEADERS); + } + + /** + * Sets the headers (Headers). An array of byte strings, where each string + * shall be the element identifier (see the + * {@link PDStructureElement#getElementIdentifier()}) for a TH structure + * element that shall be used as a header associated with this cell. + * + * @param headers the headers + */ + public void setHeaders(String[] headers) + { + this.setArrayOfString(PDTableAttributeObject.HEADERS, headers); + } + + /** + * Gets the scope (Scope). It shall reflect whether the header cell applies + * to the rest of the cells in the row that contains it, the column that + * contains it, or both the row and the column that contain it. + * + * @return the scope + */ + public String getScope() + { + return this.getName(PDTableAttributeObject.SCOPE); + } + + /** + * Sets the scope (Scope). It shall reflect whether the header cell applies + * to the rest of the cells in the row that contains it, the column that + * contains it, or both the row and the column that contain it. The value + * shall be one of the following: + *
    + *
  • {@link #SCOPE_ROW},
  • + *
  • {@link #SCOPE_COLUMN}, or
  • + *
  • {@link #SCOPE_BOTH}.
  • + *
+ * + * @param scope the scope + */ + public void setScope(String scope) + { + this.setName(PDTableAttributeObject.SCOPE, scope); + } + + /** + * Gets the summary of the table’s purpose and structure. + * + * @return the summary + */ + public String getSummary() + { + return this.getString(PDTableAttributeObject.SUMMARY); + } + + /** + * Sets the summary of the table’s purpose and structure. + * + * @param summary the summary + */ + public void setSummary(String summary) + { + this.setString(PDTableAttributeObject.SUMMARY, summary); + } + + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder().append(super.toString()); + if (this.isSpecified(PDListAttributeObject.LIST_NUMBERING)) + { + sb.append(", ListNumbering=").append(this.getListNumbering()); + } + if (this.isSpecified(PDTableAttributeObject.ROW_SPAN)) + { + sb.append(", RowSpan=").append(String.valueOf(this.getRowSpan())); + } + if (this.isSpecified(PDTableAttributeObject.COL_SPAN)) + { + sb.append(", ColSpan=").append(String.valueOf(this.getColSpan())); + } + if (this.isSpecified(PDTableAttributeObject.HEADERS)) + { + sb.append(", Headers=").append(arrayToString(this.getHeaders())); + } + if (this.isSpecified(PDTableAttributeObject.SCOPE)) + { + sb.append(", Scope=").append(this.getScope()); + } + if (this.isSpecified(PDTableAttributeObject.SUMMARY)) + { + sb.append(", Summary=").append(this.getSummary()); + } + return sb.toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDFourColours.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDFourColours.java new file mode 100644 index 000000000..b3f4a4a5d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDFourColours.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNull; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; + +/** + * An object for four colours. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDFourColours implements COSObjectable +{ + + private COSArray array; + + public PDFourColours() + { + this.array = new COSArray(); + this.array.add(COSNull.NULL); + this.array.add(COSNull.NULL); + this.array.add(COSNull.NULL); + this.array.add(COSNull.NULL); + } + + public PDFourColours(COSArray array) + { + this.array = array; + // ensure that array has 4 items + if (this.array.size() < 4) + { + for (int i = (this.array.size() - 1); i < 4; i++) + { + this.array.add(COSNull.NULL); + } + } + } + + + /** + * Gets the colour for the before edge. + * + * @return the colour for the before edge + */ + public PDGamma getBeforeColour() + { + return this.getColourByIndex(0); + } + + /** + * Sets the colour for the before edge. + * + * @param colour the colour for the before edge + */ + public void setBeforeColour(PDGamma colour) + { + this.setColourByIndex(0, colour); + } + + /** + * Gets the colour for the after edge. + * + * @return the colour for the after edge + */ + public PDGamma getAfterColour() + { + return this.getColourByIndex(1); + } + + /** + * Sets the colour for the after edge. + * + * @param colour the colour for the after edge + */ + public void setAfterColour(PDGamma colour) + { + this.setColourByIndex(1, colour); + } + + /** + * Gets the colour for the start edge. + * + * @return the colour for the start edge + */ + public PDGamma getStartColour() + { + return this.getColourByIndex(2); + } + + /** + * Sets the colour for the start edge. + * + * @param colour the colour for the start edge + */ + public void setStartColour(PDGamma colour) + { + this.setColourByIndex(2, colour); + } + + /** + * Gets the colour for the end edge. + * + * @return the colour for the end edge + */ + public PDGamma getEndColour() + { + return this.getColourByIndex(3); + } + + /** + * Sets the colour for the end edge. + * + * @param colour the colour for the end edge + */ + public void setEndColour(PDGamma colour) + { + this.setColourByIndex(3, colour); + } + + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.array; + } + + + /** + * Gets the colour by edge index. + * + * @param index edge index + * @return the colour + */ + private PDGamma getColourByIndex(int index) + { + PDGamma retval = null; + COSBase item = this.array.getObject(index); + if (item instanceof COSArray) + { + retval = new PDGamma((COSArray) item); + } + return retval; + } + + /** + * Sets the colour by edge index. + * + * @param index the edge index + * @param colour the colour + */ + private void setColourByIndex(int index, PDGamma colour) + { + COSBase base; + if (colour == null) + { + base = COSNull.NULL; + } + else + { + base = colour.getCOSArray(); + } + this.array.set(index, base); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDLayoutAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDLayoutAttributeObject.java new file mode 100644 index 000000000..07b81a0dc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDLayoutAttributeObject.java @@ -0,0 +1,1715 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; + +/** + * A Layout attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDLayoutAttributeObject extends PDStandardAttributeObject +{ + + /** + * standard attribute owner: Layout + */ + public static final String OWNER_LAYOUT = "Layout"; + + private static final String PLACEMENT = "Placement"; + private static final String WRITING_MODE = "WritingMode"; + private static final String BACKGROUND_COLOR = "BackgroundColor"; + private static final String BORDER_COLOR = "BorderColor"; + private static final String BORDER_STYLE = "BorderStyle"; + private static final String BORDER_THICKNESS = "BorderThickness"; + private static final String PADDING = "Padding"; + private static final String COLOR = "Color"; + private static final String SPACE_BEFORE = "SpaceBefore"; + private static final String SPACE_AFTER = "SpaceAfter"; + private static final String START_INDENT = "StartIndent"; + private static final String END_INDENT = "EndIndent"; + private static final String TEXT_INDENT = "TextIndent"; + private static final String TEXT_ALIGN = "TextAlign"; + private static final String BBOX = "BBox"; + private static final String WIDTH = "Width"; + private static final String HEIGHT = "Height"; + private static final String BLOCK_ALIGN = "BlockAlign"; + private static final String INLINE_ALIGN = "InlineAlign"; + private static final String T_BORDER_STYLE = "TBorderStyle"; + private static final String T_PADDING = "TPadding"; + private static final String BASELINE_SHIFT = "BaselineShift"; + private static final String LINE_HEIGHT = "LineHeight"; + private static final String TEXT_DECORATION_COLOR = "TextDecorationColor"; + private static final String TEXT_DECORATION_THICKNESS = "TextDecorationThickness"; + private static final String TEXT_DECORATION_TYPE = "TextDecorationType"; + private static final String RUBY_ALIGN = "RubyAlign"; + private static final String RUBY_POSITION = "RubyPosition"; + private static final String GLYPH_ORIENTATION_VERTICAL = "GlyphOrientationVertical"; + private static final String COLUMN_COUNT = "ColumnCount"; + private static final String COLUMN_GAP = "ColumnGap"; + private static final String COLUMN_WIDTHS = "ColumnWidths"; + + /** + * Placement: Block: Stacked in the block-progression direction within an + * enclosing reference area or parent BLSE. + */ + public static final String PLACEMENT_BLOCK = "Block"; + /** + * Placement: Inline: Packed in the inline-progression direction within an + * enclosing BLSE. + */ + public static final String PLACEMENT_INLINE = "Inline"; + /** + * Placement: Before: Placed so that the before edge of the element’s + * allocation rectangle coincides with that of the nearest enclosing + * reference area. The element may float, if necessary, to achieve the + * specified placement. The element shall be treated as a block occupying + * the full extent of the enclosing reference area in the inline direction. + * Other content shall be stacked so as to begin at the after edge of the + * element’s allocation rectangle. + */ + public static final String PLACEMENT_BEFORE = "Before"; + /** + * Placement: Start: Placed so that the start edge of the element’s + * allocation rectangle coincides with that of the nearest enclosing + * reference area. The element may float, if necessary, to achieve the + * specified placement. Other content that would intrude into the element’s + * allocation rectangle shall be laid out as a runaround. + */ + public static final String PLACEMENT_START = "Start"; + /** + * Placement: End: Placed so that the end edge of the element’s allocation + * rectangle coincides with that of the nearest enclosing reference area. + * The element may float, if necessary, to achieve the specified placement. + * Other content that would intrude into the element’s allocation rectangle + * shall be laid out as a runaround. + */ + public static final String PLACEMENT_END = "End"; + /** + * WritingMode: LrTb: Inline progression from left to right; block + * progression from top to bottom. This is the typical writing mode for + * Western writing systems. + */ + public static final String WRITING_MODE_LRTB = "LrTb"; + /** + * WritingMode: RlTb: Inline progression from right to left; block + * progression from top to bottom. This is the typical writing mode for + * Arabic and Hebrew writing systems. + */ + public static final String WRITING_MODE_RLTB = "RlTb"; + /** + * WritingMode: TbRl: Inline progression from top to bottom; block + * progression from right to left. This is the typical writing mode for + * Chinese and Japanese writing systems. + */ + public static final String WRITING_MODE_TBRL = "TbRl"; + /** + * BorderStyle: None: No border. Forces the computed value of + * BorderThickness to be 0. + */ + public static final String BORDER_STYLE_NONE = "None"; + /** + * BorderStyle: Hidden: Same as {@link #BORDER_STYLE_NONE}, except in terms + * of border conflict resolution for table elements. + */ + public static final String BORDER_STYLE_HIDDEN = "Hidden"; + /** + * BorderStyle: Dotted: The border is a series of dots. + */ + public static final String BORDER_STYLE_DOTTED = "Dotted"; + /** + * BorderStyle: Dashed: The border is a series of short line segments. + */ + public static final String BORDER_STYLE_DASHED = "Dashed"; + /** + * BorderStyle: Solid: The border is a single line segment. + */ + public static final String BORDER_STYLE_SOLID = "Solid"; + /** + * BorderStyle: Double: The border is two solid lines. The sum of the two + * lines and the space between them equals the value of BorderThickness. + */ + public static final String BORDER_STYLE_DOUBLE = "Double"; + /** + * BorderStyle: Groove: The border looks as though it were carved into the + * canvas. + */ + public static final String BORDER_STYLE_GROOVE = "Groove"; + /** + * BorderStyle: Ridge: The border looks as though it were coming out of the + * canvas (the opposite of {@link #BORDER_STYLE_GROOVE}). + */ + public static final String BORDER_STYLE_RIDGE = "Ridge"; + /** + * BorderStyle: Inset: The border makes the entire box look as though it + * were embedded in the canvas. + */ + public static final String BORDER_STYLE_INSET = "Inset"; + /** + * BorderStyle: Outset: The border makes the entire box look as though it + * were coming out of the canvas (the opposite of {@link #BORDER_STYLE_INSET}. + */ + public static final String BORDER_STYLE_OUTSET = "Outset"; + /** + * TextAlign: Start: Aligned with the start edge. + */ + public static final String TEXT_ALIGN_START = "Start"; + /** + * TextAlign: Center: Centered between the start and end edges. + */ + public static final String TEXT_ALIGN_CENTER = "Center"; + /** + * TextAlign: End: Aligned with the end edge. + */ + public static final String TEXT_ALIGN_END = "End"; + /** + * TextAlign: Justify: Aligned with both the start and end edges, with + * internal spacing within each line expanded, if necessary, to achieve such + * alignment. The last (or only) line shall be aligned with the start edge + * only. + */ + public static final String TEXT_ALIGN_JUSTIFY = "Justify"; + /** + * Width: Auto + */ + public static final String WIDTH_AUTO = "Auto"; + /** + * Height: Auto + */ + public static final String HEIGHT_AUTO = "Auto"; + /** + * BlockAlign: Before: Before edge of the first child’s allocation rectangle + * aligned with that of the table cell’s content rectangle. + */ + public static final String BLOCK_ALIGN_BEFORE = "Before"; + /** + * BlockAlign: Middle: Children centered within the table cell. The distance + * between the before edge of the first child’s allocation rectangle and + * that of the table cell’s content rectangle shall be the same as the + * distance between the after edge of the last child’s allocation rectangle + * and that of the table cell’s content rectangle. + */ + public static final String BLOCK_ALIGN_MIDDLE = "Middle"; + /** + * BlockAlign: After: After edge of the last child’s allocation rectangle + * aligned with that of the table cell’s content rectangle. + */ + public static final String BLOCK_ALIGN_AFTER = "After"; + /** + * BlockAlign: Justify: Children aligned with both the before and after + * edges of the table cell’s content rectangle. The first child shall be + * placed as described for {@link #BLOCK_ALIGN_BEFORE} and the last child as + * described for {@link #BLOCK_ALIGN_AFTER}, with equal spacing between the + * children. If there is only one child, it shall be aligned with the before + * edge only, as for {@link #BLOCK_ALIGN_BEFORE}. + */ + public static final String BLOCK_ALIGN_JUSTIFY = "Justify"; + /** + * InlineAlign: Start: Start edge of each child’s allocation rectangle + * aligned with that of the table cell’s content rectangle. + */ + public static final String INLINE_ALIGN_START = "Start"; + /** + * InlineAlign: Center: Each child centered within the table cell. The + * distance between the start edges of the child’s allocation rectangle and + * the table cell’s content rectangle shall be the same as the distance + * between their end edges. + */ + public static final String INLINE_ALIGN_CENTER = "Center"; + /** + * InlineAlign: End: End edge of each child’s allocation rectangle aligned + * with that of the table cell’s content rectangle. + */ + public static final String INLINE_ALIGN_END = "End"; + /** + * LineHeight: NormalAdjust the line height to include any nonzero value + * specified for BaselineShift. + */ + public static final String LINE_HEIGHT_NORMAL = "Normal"; + /** + * LineHeight: Auto: Adjustment for the value of BaselineShift shall not be + * made. + */ + public static final String LINE_HEIGHT_AUTO = "Auto"; + /** + * TextDecorationType: None: No text decoration + */ + public static final String TEXT_DECORATION_TYPE_NONE = "None"; + /** + * TextDecorationType: Underline: A line below the text + */ + public static final String TEXT_DECORATION_TYPE_UNDERLINE = "Underline"; + /** + * TextDecorationType: Overline: A line above the text + */ + public static final String TEXT_DECORATION_TYPE_OVERLINE = "Overline"; + /** + * TextDecorationType: LineThrough: A line through the middle of the text + */ + public static final String TEXT_DECORATION_TYPE_LINE_THROUGH = "LineThrough"; + /** + * RubyAlign: Start: The content shall be aligned on the start edge in the + * inline-progression direction. + */ + public static final String RUBY_ALIGN_START = "Start"; + /** + * RubyAlign: Center: The content shall be centered in the + * inline-progression direction. + */ + public static final String RUBY_ALIGN_CENTER = "Center"; + /** + * RubyAlign: End: The content shall be aligned on the end edge in the + * inline-progression direction. + */ + public static final String RUBY_ALIGN_END = "End"; + /** + * RubyAlign: Justify: The content shall be expanded to fill the available + * width in the inline-progression direction. + */ + public static final String RUBY_ALIGN_JUSTIFY = "Justify"; + /** + * RubyAlign: Distribute: The content shall be expanded to fill the + * available width in the inline-progression direction. However, space shall + * also be inserted at the start edge and end edge of the text. The spacing + * shall be distributed using a 1:2:1 (start:infix:end) ratio. It shall be + * changed to a 0:1:1 ratio if the ruby appears at the start of a text line + * or to a 1:1:0 ratio if the ruby appears at the end of the text line. + */ + public static final String RUBY_ALIGN_DISTRIBUTE = "Distribute"; + /** + * RubyPosition: Before: The RT content shall be aligned along the before + * edge of the element. + */ + public static final String RUBY_POSITION_BEFORE = "Before"; + /** + * RubyPosition: After: The RT content shall be aligned along the after edge + * of the element. + */ + public static final String RUBY_POSITION_AFTER = "After"; + /** + * RubyPosition: Warichu: The RT and associated RP elements shall be + * formatted as a warichu, following the RB element. + */ + public static final String RUBY_POSITION_WARICHU = "Warichu"; + /** + * RubyPosition: Inline: The RT and associated RP elements shall be + * formatted as a parenthesis comment, following the RB element. + */ + public static final String RUBY_POSITION_INLINE = "Inline"; + /** + * GlyphOrientationVertical: Auto + */ + public static final String GLYPH_ORIENTATION_VERTICAL_AUTO = "Auto"; + /** + * GlyphOrientationVertical: -180° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_MINUS_180_DEGREES = "-180"; + /** + * GlyphOrientationVertical: -90° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_MINUS_90_DEGREES = "-90"; + /** + * GlyphOrientationVertical: 0° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_ZERO_DEGREES = "0"; + /** + * GlyphOrientationVertical: 90° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_90_DEGREES = "90"; + /** + * GlyphOrientationVertical: 180° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_180_DEGREES = "180"; + /** + * GlyphOrientationVertical: 270° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_270_DEGREES = "270"; + /** + * GlyphOrientationVertical: 360° + */ + public static final String GLYPH_ORIENTATION_VERTICAL_360_DEGREES = "360"; + + + /** + * Default constructor. + */ + public PDLayoutAttributeObject() + { + this.setOwner(OWNER_LAYOUT); + } + + /** + * Creates a new Layout attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDLayoutAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the positioning of the element with respect to the enclosing + * reference area and other content (Placement). The default value is + * {@link #PLACEMENT_INLINE}. + * + * @return the placement + */ + public String getPlacement() + { + return this.getName(PLACEMENT, PLACEMENT_INLINE); + } + + /** + * Sets the positioning of the element with respect to the enclosing + * reference area and other content (Placement). The value should be one of: + *
    + *
  • {@link #PLACEMENT_BLOCK},
  • + *
  • {@link #PLACEMENT_INLINE},
  • + *
  • {@link #PLACEMENT_BEFORE},
  • + *
  • {@link #PLACEMENT_START},
  • + *
  • {@link #PLACEMENT_END}.
  • + *
      + * + * @param placement the placement + */ + public void setPlacement(String placement) + { + this.setName(PLACEMENT, placement); + } + + /** + * Gets the writing mode (WritingMode). The default value is + * {@link #WRITING_MODE_LRTB}. + * + * @return the writing mode + */ + public String getWritingMode() + { + return this.getName(WRITING_MODE, WRITING_MODE_LRTB); + } + + /** + * Sets the writing mode (WritingMode). The value should be one of: + *
        + *
      • {@link #WRITING_MODE_LRTB},
      • + *
      • {@link #WRITING_MODE_RLTB},
      • + *
      • {@link #WRITING_MODE_TBRL}.
      • + *
      + * + * @param writingMode the writing mode + */ + public void setWritingMode(String writingMode) + { + this.setName(WRITING_MODE, writingMode); + } + + /** + * Gets the background colour (BackgroundColor). + * + * @return the background colour + */ + public PDGamma getBackgroundColor() + { + return this.getColor(BACKGROUND_COLOR); + } + + /** + * Sets the background colour (BackgroundColor). + * + * @param backgroundColor the background colour + */ + public void setBackgroundColor(PDGamma backgroundColor) + { + this.setColor(BACKGROUND_COLOR, backgroundColor); + } + + /** + * Gets the border colour (BorderColor). + * + * @return a single border colour ({@link PDGamma}) or four border colours + * ({@link PDFourColours}) + */ + public Object getBorderColors() + { + return this.getColorOrFourColors(BORDER_COLOR); + } + + /** + * Sets the same border colour for all four sides (BorderColor). + * + * @param borderColor the border colour + */ + public void setAllBorderColors(PDGamma borderColor) + { + this.setColor(BORDER_COLOR, borderColor); + } + + /** + * Sets the border colours for four sides separately (BorderColor). + * + * @param borderColors the border colours + */ + public void setBorderColors(PDFourColours borderColors) + { + this.setFourColors(BORDER_COLOR, borderColors); + } + + /** + * Gets the border style (BorderStyle). The default value is + * {@link #BORDER_STYLE_NONE}. + * + * @return the border styles (a String or an array of four Strings) + */ + public Object getBorderStyle() + { + return this.getNameOrArrayOfName(BORDER_STYLE, BORDER_STYLE_NONE); + } + + /** + * Sets the same border style for all four sides (BorderStyle). The value + * should be one of: + *
        + *
      • {@link #BORDER_STYLE_NONE},
      • + *
      • {@link #BORDER_STYLE_HIDDEN},
      • + *
      • {@link #BORDER_STYLE_DOTTED},
      • + *
      • {@link #BORDER_STYLE_DASHED},
      • + *
      • {@link #BORDER_STYLE_SOLID},
      • + *
      • {@link #BORDER_STYLE_DOUBLE},
      • + *
      • {@link #BORDER_STYLE_GROOVE},
      • + *
      • {@link #BORDER_STYLE_RIDGE},
      • + *
      • {@link #BORDER_STYLE_INSET},
      • + *
      • {@link #BORDER_STYLE_OUTSET}.
      • + *
      + * + * @param borderStyle the border style + */ + public void setAllBorderStyles(String borderStyle) + { + this.setName(BORDER_STYLE, borderStyle); + } + + /** + * Sets the border styles for four sides separately (BorderStyle). The + * values should be of: + *
        + *
      • {@link #BORDER_STYLE_NONE},
      • + *
      • {@link #BORDER_STYLE_HIDDEN},
      • + *
      • {@link #BORDER_STYLE_DOTTED},
      • + *
      • {@link #BORDER_STYLE_DASHED},
      • + *
      • {@link #BORDER_STYLE_SOLID},
      • + *
      • {@link #BORDER_STYLE_DOUBLE},
      • + *
      • {@link #BORDER_STYLE_GROOVE},
      • + *
      • {@link #BORDER_STYLE_RIDGE},
      • + *
      • {@link #BORDER_STYLE_INSET},
      • + *
      • {@link #BORDER_STYLE_OUTSET}.
      • + *
      + * + * @param borderStyles the border styles (an array of four Strings) + */ + public void setBorderStyles(String[] borderStyles) + { + this.setArrayOfName(BORDER_STYLE, borderStyles); + } + + /** + * Gets the border thickness (BorderThickness). + * + * @return the border thickness (a Float or an array of four floats) + */ + public Object getBorderThickness() + { + return this.getNumberOrArrayOfNumber(BORDER_THICKNESS, UNSPECIFIED); + } + + /** + * Sets the same border thickness for all four sides (BorderThickness). + * + * @param borderThickness the border thickness + */ + public void setAllBorderThicknesses(float borderThickness) + { + this.setNumber(BORDER_THICKNESS, borderThickness); + } + + /** + * Sets the same border thickness for all four sides (BorderThickness). + * + * @param borderThickness the border thickness + */ + public void setAllBorderThicknesses(int borderThickness) + { + this.setNumber(BORDER_THICKNESS, borderThickness); + } + + /** + * Sets the border thicknesses for four sides separately (BorderThickness). + * + * @param borderThicknesses the border thickness (an array of four floats) + */ + public void setBorderThicknesses(float[] borderThicknesses) + { + this.setArrayOfNumber(BORDER_THICKNESS, borderThicknesses); + } + + /** + * Gets the padding (Padding). The default value is 0. + * + * @return the padding (a Float or an array of float) + */ + public Object getPadding() + { + return this.getNumberOrArrayOfNumber(PADDING, 0.f); + } + + /** + * Sets the same padding for all four sides (Padding). + * + * @param padding the padding + */ + public void setAllPaddings(float padding) + { + this.setNumber(PADDING, padding); + } + + /** + * Sets the same padding for all four sides (Padding). + * + * @param padding the padding + */ + public void setAllPaddings(int padding) + { + this.setNumber(PADDING, padding); + } + + /** + * Sets the paddings for four sides separately (Padding). + * + * @param paddings the paddings (an array of four floats) + */ + public void setPaddings(float[] paddings) + { + this.setArrayOfNumber(PADDING, paddings); + } + + /** + * Gets the color to be used for drawing text and the default value for the + * colour of table borders and text decorations (Color). + * + * @return the colour + */ + public PDGamma getColor() + { + return this.getColor(COLOR); + } + + /** + * Sets the color to be used for drawing text and the default value for the + * colour of table borders and text decorations (Color). + * + * @param color the colour + */ + public void setColor(PDGamma color) + { + this.setColor(COLOR, color); + } + + /** + * Gets the amount of extra space preceding the before edge of the BLSE in + * the block-progression direction (SpaceBefore). The default value is 0. + * + * @return the space before + */ + public float getSpaceBefore() + { + return this.getNumber(SPACE_BEFORE, 0.f); + } + + /** + * Sets the amount of extra space preceding the before edge of the BLSE in + * the block-progression direction (SpaceBefore). + * + * @param spaceBefore the space before + */ + public void setSpaceBefore(float spaceBefore) + { + this.setNumber(SPACE_BEFORE, spaceBefore); + } + + /** + * Sets the amount of extra space preceding the before edge of the BLSE in + * the block-progression direction (SpaceBefore). + * + * @param spaceBefore the space before + */ + public void setSpaceBefore(int spaceBefore) + { + this.setNumber(SPACE_BEFORE, spaceBefore); + } + + /** + * Gets the amount of extra space following the after edge of the BLSE in + * the block-progression direction (SpaceAfter). The default value is 0. + * + * @return the space after + */ + public float getSpaceAfter() + { + return this.getNumber(SPACE_AFTER, 0.f); + } + + /** + * Sets the amount of extra space following the after edge of the BLSE in + * the block-progression direction (SpaceAfter). + * + * @param spaceAfter the space after + */ + public void setSpaceAfter(float spaceAfter) + { + this.setNumber(SPACE_AFTER, spaceAfter); + } + + /** + * Sets the amount of extra space following the after edge of the BLSE in + * the block-progression direction (SpaceAfter). + * + * @param spaceAfter the space after + */ + public void setSpaceAfter(int spaceAfter) + { + this.setNumber(SPACE_AFTER, spaceAfter); + } + + /** + * Gets the distance from the start edge of the reference area to that of + * the BLSE in the inline-progression direction (StartIndent). The default value is 0. + * + * @return the start indent + */ + public float getStartIndent() + { + return this.getNumber(START_INDENT, 0.f); + } + + /** + * Sets the distance from the start edge of the reference area to that of + * the BLSE in the inline-progression direction (StartIndent). + * + * @param startIndent the start indent + */ + public void setStartIndent(float startIndent) + { + this.setNumber(START_INDENT, startIndent); + } + + /** + * Sets the distance from the start edge of the reference area to that of + * the BLSE in the inline-progression direction (StartIndent). + * + * @param startIndent the start indent + */ + public void setStartIndent(int startIndent) + { + this.setNumber(START_INDENT, startIndent); + } + + /** + * Gets the distance from the end edge of the BLSE to that of the reference + * area in the inline-progression direction (EndIndent). The default value + * is 0. + * + * @return the end indent + */ + public float getEndIndent() + { + return this.getNumber(END_INDENT, 0.f); + } + + /** + * Sets the distance from the end edge of the BLSE to that of the reference + * area in the inline-progression direction (EndIndent). + * + * @param endIndent the end indent + */ + public void setEndIndent(float endIndent) + { + this.setNumber(END_INDENT, endIndent); + } + + /** + * Sets the distance from the end edge of the BLSE to that of the reference + * area in the inline-progression direction (EndIndent). + * + * @param endIndent the end indent + */ + public void setEndIndent(int endIndent) + { + this.setNumber(END_INDENT, endIndent); + } + + /** + * Gets the additional distance in the inline-progression direction from the + * start edge of the BLSE, as specified by StartIndent, to that of the first + * line of text (TextIndent). The default value is 0. + * + * @return the text indent + */ + public float getTextIndent() + { + return this.getNumber(TEXT_INDENT, 0.f); + } + + /** + * Sets the additional distance in the inline-progression direction from the + * start edge of the BLSE, as specified by StartIndent, to that of the first + * line of text (TextIndent). + * + * @param textIndent the text indent + */ + public void setTextIndent(float textIndent) + { + this.setNumber(TEXT_INDENT, textIndent); + } + + /** + * Sets the additional distance in the inline-progression direction from the + * start edge of the BLSE, as specified by StartIndent, to that of the first + * line of text (TextIndent). + * + * @param textIndent the text indent + */ + public void setTextIndent(int textIndent) + { + this.setNumber(TEXT_INDENT, textIndent); + } + + /** + * Gets the alignment, in the inline-progression direction, of text and + * other content within lines of the BLSE (TextAlign). The default value is + * {@link #TEXT_ALIGN_START}. + * + * @return the text alignment + */ + public String getTextAlign() + { + return this.getName(TEXT_ALIGN, TEXT_ALIGN_START); + } + + /** + * Sets the alignment, in the inline-progression direction, of text and + * other content within lines of the BLSE (TextAlign). The value should be + * one of: + *
        + *
      • {@link #TEXT_ALIGN_START},
      • + *
      • {@link #TEXT_ALIGN_CENTER},
      • + *
      • {@link #TEXT_ALIGN_END},
      • + *
      • {@link #TEXT_ALIGN_JUSTIFY}.
      • + *
      + * + * @param textIndent the text alignment + */ + public void setTextAlign(String textIndent) + { + this.setName(TEXT_ALIGN, textIndent); + } + + /** + * Gets the bounding box. + * + * @return the bounding box. + */ + public PDRectangle getBBox() + { + COSArray array = + (COSArray) this.getCOSDictionary().getDictionaryObject(BBOX); + if (array != null) + { + return new PDRectangle(array); + } + return null; + } + + /** + * Sets the bounding box. + * + * @param bbox the bounding box + */ + public void setBBox(PDRectangle bbox) + { + String name = BBOX; + COSBase oldValue = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setItem(name, bbox); + COSBase newValue = bbox == null ? null : bbox.getCOSObject(); + this.potentiallyNotifyChanged(oldValue, newValue); + } + + /** + * Gets the width of the element’s content rectangle in the + * inline-progression direction (Width). The default value is + * {@link #WIDTH_AUTO}. + * + * @return the width (a Float or a String) + */ + public Object getWidth() + { + return this.getNumberOrName(WIDTH, WIDTH_AUTO); + } + + /** + * Sets the width of the element’s content rectangle in the + * inline-progression direction (Width) to {@link #WIDTH_AUTO}. + */ + public void setWidthAuto() + { + this.setName(WIDTH, WIDTH_AUTO); + } + + /** + * Sets the width of the element’s content rectangle in the + * inline-progression direction (Width). + * + * @param width the width + */ + public void setWidth(float width) + { + this.setNumber(WIDTH, width); + } + + /** + * Sets the width of the element’s content rectangle in the + * inline-progression direction (Width). + * + * @param width the width + */ + public void setWidth(int width) + { + this.setNumber(WIDTH, width); + } + + /** + * Gets the height of the element’s content rectangle in the + * block-progression direction (Height). The default value is + * {@link #HEIGHT_AUTO}. + * + * @return the height (a Float or a String) + */ + public Object getHeight() + { + return this.getNumberOrName(HEIGHT, HEIGHT_AUTO); + } + + /** + * Sets the height of the element’s content rectangle in the + * block-progression direction (Height) to {@link #HEIGHT_AUTO}. + */ + public void setHeightAuto() + { + this.setName(HEIGHT, HEIGHT_AUTO); + } + + /** + * Sets the height of the element’s content rectangle in the + * block-progression direction (Height). + * + * @param height the height + */ + public void setHeight(float height) + { + this.setNumber(HEIGHT, height); + } + + /** + * Sets the height of the element’s content rectangle in the + * block-progression direction (Height). + * + * @param height the height + */ + public void setHeight(int height) + { + this.setNumber(HEIGHT, height); + } + + /** + * Gets the alignment, in the block-progression direction, of content within + * the table cell (BlockAlign). The default value is + * {@link #BLOCK_ALIGN_BEFORE}. + * + * @return the block alignment + */ + public String getBlockAlign() + { + return this.getName(BLOCK_ALIGN, BLOCK_ALIGN_BEFORE); + } + + /** + * Sets the alignment, in the block-progression direction, of content within + * the table cell (BlockAlign). The value should be one of: + *
        + *
      • {@link #BLOCK_ALIGN_BEFORE},
      • + *
      • {@link #BLOCK_ALIGN_MIDDLE},
      • + *
      • {@link #BLOCK_ALIGN_AFTER},
      • + *
      • {@link #BLOCK_ALIGN_JUSTIFY}.
      • + *
      + * + * @param blockAlign the block alignment + */ + public void setBlockAlign(String blockAlign) + { + this.setName(BLOCK_ALIGN, blockAlign); + } + + /** + * Gets the alignment, in the inline-progression direction, of content + * within the table cell (InlineAlign). The default value is + * {@link #INLINE_ALIGN_START}. + * + * @return the inline alignment + */ + public String getInlineAlign() + { + return this.getName(INLINE_ALIGN, INLINE_ALIGN_START); + } + + /** + * Sets the alignment, in the inline-progression direction, of content + * within the table cell (InlineAlign). The value should be one of + *
        + *
      • {@link #INLINE_ALIGN_START},
      • + *
      • {@link #INLINE_ALIGN_CENTER},
      • + *
      • {@link #INLINE_ALIGN_END}.
      • + *
      + * + * @param inlineAlign the inline alignment + */ + public void setInlineAlign(String inlineAlign) + { + this.setName(INLINE_ALIGN, inlineAlign); + } + + /** + * Gets the style of the border drawn on each edge of a table cell + * (TBorderStyle). + * + * @return + */ + public Object getTBorderStyle() + { + return this.getNameOrArrayOfName(T_BORDER_STYLE, BORDER_STYLE_NONE); + } + + /** + * Sets the same table border style for all four sides (TBorderStyle). The + * value should be one of: + *
        + *
      • {@link #BORDER_STYLE_NONE},
      • + *
      • {@link #BORDER_STYLE_HIDDEN},
      • + *
      • {@link #BORDER_STYLE_DOTTED},
      • + *
      • {@link #BORDER_STYLE_DASHED},
      • + *
      • {@link #BORDER_STYLE_SOLID},
      • + *
      • {@link #BORDER_STYLE_DOUBLE},
      • + *
      • {@link #BORDER_STYLE_GROOVE},
      • + *
      • {@link #BORDER_STYLE_RIDGE},
      • + *
      • {@link #BORDER_STYLE_INSET},
      • + *
      • {@link #BORDER_STYLE_OUTSET}.
      • + *
      + * + * @param tBorderStyle the table border style + */ + public void setAllTBorderStyles(String tBorderStyle) + { + this.setName(T_BORDER_STYLE, tBorderStyle); + } + + /** + * Sets the style of the border drawn on each edge of a table cell + * (TBorderStyle). The values should be of: + *
        + *
      • {@link #BORDER_STYLE_NONE},
      • + *
      • {@link #BORDER_STYLE_HIDDEN},
      • + *
      • {@link #BORDER_STYLE_DOTTED},
      • + *
      • {@link #BORDER_STYLE_DASHED},
      • + *
      • {@link #BORDER_STYLE_SOLID},
      • + *
      • {@link #BORDER_STYLE_DOUBLE},
      • + *
      • {@link #BORDER_STYLE_GROOVE},
      • + *
      • {@link #BORDER_STYLE_RIDGE},
      • + *
      • {@link #BORDER_STYLE_INSET},
      • + *
      • {@link #BORDER_STYLE_OUTSET}.
      • + *
      + * + * @param tBorderStyles + */ + public void setTBorderStyles(String[] tBorderStyles) + { + this.setArrayOfName(T_BORDER_STYLE, tBorderStyles); + } + + /** + * Gets the offset to account for the separation between the table cell’s + * content rectangle and the surrounding border (TPadding). The default + * value is 0. + * + * @return the table padding (a Float or an array of float) + */ + public Object getTPadding() + { + return this.getNumberOrArrayOfNumber(T_PADDING, 0.f); + } + + /** + * Sets the same table padding for all four sides (TPadding). + * + * @param tPadding the table padding + */ + public void setAllTPaddings(float tPadding) + { + this.setNumber(T_PADDING, tPadding); + } + + /** + * Sets the same table padding for all four sides (TPadding). + * + * @param tPadding the table padding + */ + public void setAllTPaddings(int tPadding) + { + this.setNumber(T_PADDING, tPadding); + } + + /** + * Sets the table paddings for four sides separately (TPadding). + * + * @param tPaddings the table paddings (an array of four floats) + */ + public void setTPaddings(float[] tPaddings) + { + this.setArrayOfNumber(T_PADDING, tPaddings); + } + + /** + * Gets the distance by which the element’s baseline shall be shifted + * relative to that of its parent element (BaselineShift). The default value + * is 0. + * + * @return the baseline shift + */ + public float getBaselineShift() + { + return this.getNumber(BASELINE_SHIFT, 0.f); + } + + /** + * Sets the distance by which the element’s baseline shall be shifted + * relative to that of its parent element (BaselineShift). + * + * @param baselineShift the baseline shift + */ + public void setBaselineShift(float baselineShift) + { + this.setNumber(BASELINE_SHIFT, baselineShift); + } + + /** + * Sets the distance by which the element’s baseline shall be shifted + * relative to that of its parent element (BaselineShift). + * + * @param baselineShift the baseline shift + */ + public void setBaselineShift(int baselineShift) + { + this.setNumber(BASELINE_SHIFT, baselineShift); + } + + /** + * Gets the element’s preferred height in the block-progression direction + * (LineHeight). The default value is {@link #LINE_HEIGHT_NORMAL}. + * + * @return the line height (a Float or a String) + */ + public Object getLineHeight() + { + return this.getNumberOrName(LINE_HEIGHT, LINE_HEIGHT_NORMAL); + } + + /** + * Sets the element’s preferred height in the block-progression direction + * (LineHeight) to {@link #LINE_HEIGHT_NORMAL}. + */ + public void setLineHeightNormal() + { + this.setName(LINE_HEIGHT, LINE_HEIGHT_NORMAL); + } + + /** + * Sets the element’s preferred height in the block-progression direction + * (LineHeight) to {@link #LINE_HEIGHT_AUTO}. + */ + public void setLineHeightAuto() + { + this.setName(LINE_HEIGHT, LINE_HEIGHT_AUTO); + } + + /** + * Sets the element’s preferred height in the block-progression direction + * (LineHeight). + * + * @param lineHeight the line height + */ + public void setLineHeight(float lineHeight) + { + this.setNumber(LINE_HEIGHT, lineHeight); + } + + /** + * Sets the element’s preferred height in the block-progression direction + * (LineHeight). + * + * @param lineHeight the line height + */ + public void setLineHeight(int lineHeight) + { + this.setNumber(LINE_HEIGHT, lineHeight); + } + + /** + * Gets the colour to be used for drawing text decorations + * (TextDecorationColor). + * + * @return the text decoration colour + */ + public PDGamma getTextDecorationColor() + { + return this.getColor(TEXT_DECORATION_COLOR); + } + + /** + * Sets the colour to be used for drawing text decorations + * (TextDecorationColor). + * + * @param textDecorationColor the text decoration colour + */ + public void setTextDecorationColor(PDGamma textDecorationColor) + { + this.setColor(TEXT_DECORATION_COLOR, textDecorationColor); + } + + /** + * Gets the thickness of each line drawn as part of the text decoration + * (TextDecorationThickness). + * + * @return the text decoration thickness + */ + public float getTextDecorationThickness() + { + return this.getNumber(TEXT_DECORATION_THICKNESS); + } + + /** + * Sets the thickness of each line drawn as part of the text decoration + * (TextDecorationThickness). + * + * @param textDecorationThickness the text decoration thickness + */ + public void setTextDecorationThickness(float textDecorationThickness) + { + this.setNumber(TEXT_DECORATION_THICKNESS, textDecorationThickness); + } + + /** + * Sets the thickness of each line drawn as part of the text decoration + * (TextDecorationThickness). + * + * @param textDecorationThickness the text decoration thickness + */ + public void setTextDecorationThickness(int textDecorationThickness) + { + this.setNumber(TEXT_DECORATION_THICKNESS, textDecorationThickness); + } + + /** + * Gets the type of text decoration (TextDecorationType). The default value + * is {@link #TEXT_DECORATION_TYPE_NONE}. + * + * @return the type of text decoration + */ + public String getTextDecorationType() + { + return this.getName(TEXT_DECORATION_TYPE, TEXT_DECORATION_TYPE_NONE); + } + + /** + * Sets the type of text decoration (TextDecorationType). The value should + * be one of: + *
        + *
      • {@link #TEXT_DECORATION_TYPE_NONE},
      • + *
      • {@link #TEXT_DECORATION_TYPE_UNDERLINE},
      • + *
      • {@link #TEXT_DECORATION_TYPE_OVERLINE},
      • + *
      • {@link #TEXT_DECORATION_TYPE_LINE_THROUGH}.
      • + *
      + * + * @param textDecorationType the type of text decoration + */ + public void setTextDecorationType(String textDecorationType) + { + this.setName(TEXT_DECORATION_TYPE, textDecorationType); + } + + /** + * Gets the justification of the lines within a ruby assembly (RubyAlign). + * The default value is {@link #RUBY_ALIGN_DISTRIBUTE}. + * + * @return the ruby alignment + */ + public String getRubyAlign() + { + return this.getName(RUBY_ALIGN, RUBY_ALIGN_DISTRIBUTE); + } + + /** + * Sets the justification of the lines within a ruby assembly (RubyAlign). + * The value should be one of: + *
        + *
      • {@link #RUBY_ALIGN_START},
      • + *
      • {@link #RUBY_ALIGN_CENTER},
      • + *
      • {@link #RUBY_ALIGN_END},
      • + *
      • {@link #RUBY_ALIGN_JUSTIFY},
      • + *
      • {@link #RUBY_ALIGN_DISTRIBUTE},
      • + *
      + * + * @param rubyAlign the ruby alignment + */ + public void setRubyAlign(String rubyAlign) + { + this.setName(RUBY_ALIGN, rubyAlign); + } + + /** + * Gets the placement of the RT structure element relative to the RB element + * in a ruby assembly (RubyPosition). The default value is + * {@link #RUBY_POSITION_BEFORE}. + * + * @return the ruby position + */ + public String getRubyPosition() + { + return this.getName(RUBY_POSITION, RUBY_POSITION_BEFORE); + } + + /** + * Sets the placement of the RT structure element relative to the RB element + * in a ruby assembly (RubyPosition). The value should be one of: + *
        + *
      • {@link #RUBY_POSITION_BEFORE},
      • + *
      • {@link #RUBY_POSITION_AFTER},
      • + *
      • {@link #RUBY_POSITION_WARICHU},
      • + *
      • {@link #RUBY_POSITION_INLINE}.
      • + *
      + * + * @param rubyPosition the ruby position + */ + public void setRubyPosition(String rubyPosition) + { + this.setName(RUBY_POSITION, rubyPosition); + } + + /** + * Gets the orientation of glyphs when the inline-progression direction is + * top to bottom or bottom to top (GlyphOrientationVertical). The default + * value is {@link #GLYPH_ORIENTATION_VERTICAL_AUTO}. + * + * @return the vertical glyph orientation + */ + public String getGlyphOrientationVertical() + { + return this.getName(GLYPH_ORIENTATION_VERTICAL, + GLYPH_ORIENTATION_VERTICAL_AUTO); + } + + /** + * Sets the orientation of glyphs when the inline-progression direction is + * top to bottom or bottom to top (GlyphOrientationVertical). The value + * should be one of: + *
        + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_AUTO},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_MINUS_180_DEGREES},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_MINUS_90_DEGREES},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_ZERO_DEGREES},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_90_DEGREES},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_180_DEGREES},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_270_DEGREES},
      • + *
      • {@link #GLYPH_ORIENTATION_VERTICAL_360_DEGREES}.
      • + *
      + * + * @param glyphOrientationVertical the vertical glyph orientation + */ + public void setGlyphOrientationVertical(String glyphOrientationVertical) + { + this.setName(GLYPH_ORIENTATION_VERTICAL, glyphOrientationVertical); + } + + /** + * Gets the number of columns in the content of the grouping element + * (ColumnCount). The default value is 1. + * + * @return the column count + */ + public int getColumnCount() + { + return this.getInteger(COLUMN_COUNT, 1); + } + + /** + * Sets the number of columns in the content of the grouping element + * (ColumnCount). + * + * @param columnCount the column count + */ + public void setColumnCount(int columnCount) + { + this.setInteger(COLUMN_COUNT, columnCount); + } + + /** + * Gets the desired space between adjacent columns in the inline-progression + * direction (ColumnGap). + * + * @return the column gap (FLoat or array of floats) + */ + public Object getColumnGap() + { + return this.getNumberOrArrayOfNumber(COLUMN_GAP, UNSPECIFIED); + } + + /** + * Sets the desired space between all columns in the inline-progression + * direction (ColumnGap). + * + * @param columnGap the column gap + */ + public void setColumnGap(float columnGap) + { + this.setNumber(COLUMN_GAP, columnGap); + } + + /** + * Sets the desired space between all columns in the inline-progression + * direction (ColumnGap). + * + * @param columnGap the column gap + */ + public void setColumnGap(int columnGap) + { + this.setNumber(COLUMN_GAP, columnGap); + } + + /** + * Sets the desired space between adjacent columns in the inline-progression + * direction (ColumnGap), the first element specifying the space between the + * first and second columns, the second specifying the space between the + * second and third columns, and so on. + * + * @param columnGaps the column gaps + */ + public void setColumnGaps(float[] columnGaps) + { + this.setArrayOfNumber(COLUMN_GAP, columnGaps); + } + + /** + * Gets the desired width of the columns, measured in default user space + * units in the inline-progression direction (ColumnWidths). + * + * @return the column widths (Float or array of floats) + */ + public Object getColumnWidths() + { + return this.getNumberOrArrayOfNumber(COLUMN_WIDTHS, UNSPECIFIED); + } + + /** + * Sets the same column width for all columns (ColumnWidths). + * + * @param columnWidth the column width + */ + public void setAllColumnWidths(float columnWidth) + { + this.setNumber(COLUMN_WIDTHS, columnWidth); + } + + /** + * Sets the same column width for all columns (ColumnWidths). + * + * @param columnWidth the column width + */ + public void setAllColumnWidths(int columnWidth) + { + this.setNumber(COLUMN_WIDTHS, columnWidth); + } + + /** + * Sets the column widths for the columns separately (ColumnWidths). + * + * @param columnWidths the column widths + */ + public void setColumnWidths(float[] columnWidths) + { + this.setArrayOfNumber(COLUMN_WIDTHS, columnWidths); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder().append(super.toString()); + if (this.isSpecified(PLACEMENT)) + { + sb.append(", Placement=").append(this.getPlacement()); + } + if (this.isSpecified(WRITING_MODE)) + { + sb.append(", WritingMode=").append(this.getWritingMode()); + } + if (this.isSpecified(BACKGROUND_COLOR)) + { + sb.append(", BackgroundColor=").append(this.getBackgroundColor()); + } + if (this.isSpecified(BORDER_COLOR)) + { + sb.append(", BorderColor=").append(this.getBorderColors()); + } + if (this.isSpecified(BORDER_STYLE)) + { + Object borderStyle = this.getBorderStyle(); + sb.append(", BorderStyle="); + if (borderStyle instanceof String[]) + { + sb.append(arrayToString((String[]) borderStyle)); + } + else + { + sb.append(borderStyle); + } + } + if (this.isSpecified(BORDER_THICKNESS)) + { + Object borderThickness = this.getBorderThickness(); + sb.append(", BorderThickness="); + if (borderThickness instanceof float[]) + { + sb.append(arrayToString((float[]) borderThickness)); + } + else + { + sb.append(String.valueOf((Float) borderThickness)); + } + } + if (this.isSpecified(PADDING)) + { + Object padding = this.getPadding(); + sb.append(", Padding="); + if (padding instanceof float[]) + { + sb.append(arrayToString((float[]) padding)); + } + else + { + sb.append(String.valueOf((Float) padding)); + } + } + if (this.isSpecified(COLOR)) + { + sb.append(", Color=").append(this.getColor()); + } + if (this.isSpecified(SPACE_BEFORE)) + { + sb.append(", SpaceBefore=") + .append(String.valueOf(this.getSpaceBefore())); + } + if (this.isSpecified(SPACE_AFTER)) + { + sb.append(", SpaceAfter=") + .append(String.valueOf(this.getSpaceAfter())); + } + if (this.isSpecified(START_INDENT)) + { + sb.append(", StartIndent=") + .append(String.valueOf(this.getStartIndent())); + } + if (this.isSpecified(END_INDENT)) + { + sb.append(", EndIndent=") + .append(String.valueOf(this.getEndIndent())); + } + if (this.isSpecified(TEXT_INDENT)) + { + sb.append(", TextIndent=") + .append(String.valueOf(this.getTextIndent())); + } + if (this.isSpecified(TEXT_ALIGN)) + { + sb.append(", TextAlign=").append(this.getTextAlign()); + } + if (this.isSpecified(BBOX)) + { + sb.append(", BBox=").append(this.getBBox()); + } + if (this.isSpecified(WIDTH)) + { + Object width = this.getWidth(); + sb.append(", Width="); + if (width instanceof Float) + { + sb.append(String.valueOf((Float) width)); + } + else + { + sb.append(width); + } + } + if (this.isSpecified(HEIGHT)) + { + Object height = this.getHeight(); + sb.append(", Height="); + if (height instanceof Float) + { + sb.append(String.valueOf((Float) height)); + } + else + { + sb.append(height); + } + } + if (this.isSpecified(BLOCK_ALIGN)) + { + sb.append(", BlockAlign=").append(this.getBlockAlign()); + } + if (this.isSpecified(INLINE_ALIGN)) + { + sb.append(", InlineAlign=").append(this.getInlineAlign()); + } + if (this.isSpecified(T_BORDER_STYLE)) + { + Object tBorderStyle = this.getTBorderStyle(); + sb.append(", TBorderStyle="); + if (tBorderStyle instanceof String[]) + { + sb.append(arrayToString((String[]) tBorderStyle)); + } + else + { + sb.append(tBorderStyle); + } + } + if (this.isSpecified(T_PADDING)) + { + Object tPadding = this.getTPadding(); + sb.append(", TPadding="); + if (tPadding instanceof float[]) + { + sb.append(arrayToString((float[]) tPadding)); + } + else + { + sb.append(String.valueOf((Float) tPadding)); + } + } + if (this.isSpecified(BASELINE_SHIFT)) + { + sb.append(", BaselineShift=") + .append(String.valueOf(this.getBaselineShift())); + } + if (this.isSpecified(LINE_HEIGHT)) + { + Object lineHeight = this.getLineHeight(); + sb.append(", LineHeight="); + if (lineHeight instanceof Float) + { + sb.append(String.valueOf((Float) lineHeight)); + } + else + { + sb.append(lineHeight); + } + } + if (this.isSpecified(TEXT_DECORATION_COLOR)) + { + sb.append(", TextDecorationColor=") + .append(this.getTextDecorationColor()); + } + if (this.isSpecified(TEXT_DECORATION_THICKNESS)) + { + sb.append(", TextDecorationThickness=") + .append(String.valueOf(this.getTextDecorationThickness())); + } + if (this.isSpecified(TEXT_DECORATION_TYPE)) + { + sb.append(", TextDecorationType=") + .append(this.getTextDecorationType()); + } + if (this.isSpecified(RUBY_ALIGN)) + { + sb.append(", RubyAlign=").append(this.getRubyAlign()); + } + if (this.isSpecified(RUBY_POSITION)) + { + sb.append(", RubyPosition=").append(this.getRubyPosition()); + } + if (this.isSpecified(GLYPH_ORIENTATION_VERTICAL)) + { + sb.append(", GlyphOrientationVertical=") + .append(this.getGlyphOrientationVertical()); + } + if (this.isSpecified(COLUMN_COUNT)) + { + sb.append(", ColumnCount=") + .append(String.valueOf(this.getColumnCount())); + } + if (this.isSpecified(COLUMN_GAP)) + { + Object columnGap = this.getColumnGap(); + sb.append(", ColumnGap="); + if (columnGap instanceof float[]) + { + sb.append(arrayToString((float[]) columnGap)); + } + else + { + sb.append(String.valueOf((Float) columnGap)); + } + } + if (this.isSpecified(COLUMN_WIDTHS)) + { + Object columnWidth = this.getColumnWidths(); + sb.append(", ColumnWidths="); + if (columnWidth instanceof float[]) + { + sb.append(arrayToString((float[]) columnWidth)); + } + else + { + sb.append(String.valueOf((Float) columnWidth)); + } + } + return sb.toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDListAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDListAttributeObject.java new file mode 100644 index 000000000..7d864a235 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDListAttributeObject.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSDictionary; + +/** + * A List attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDListAttributeObject extends PDStandardAttributeObject +{ + + /** + * standard attribute owner: List + */ + public static final String OWNER_LIST = "List"; + + protected static final String LIST_NUMBERING = "ListNumbering"; + + /** + * ListNumbering: Circle: Open circular bullet + */ + public static final String LIST_NUMBERING_CIRCLE = "Circle"; + /** + * ListNumbering: Decimal: Decimal arabic numerals (1–9, 10–99, …) + */ + public static final String LIST_NUMBERING_DECIMAL = "Decimal"; + /** + * ListNumbering: Disc: Solid circular bullet + */ + public static final String LIST_NUMBERING_DISC = "Disc"; + /** + * ListNumbering: LowerAlpha: Lowercase letters (a, b, c, …) + */ + public static final String LIST_NUMBERING_LOWER_ALPHA = "LowerAlpha"; + /** + * ListNumbering: LowerRoman: Lowercase roman numerals (i, ii, iii, iv, …) + */ + public static final String LIST_NUMBERING_LOWER_ROMAN = "LowerRoman"; + /** + * ListNumbering: None: No autonumbering; Lbl elements (if present) contain arbitrary text + * not subject to any numbering scheme + */ + public static final String LIST_NUMBERING_NONE = "None"; + /** + * ListNumbering: Square: Solid square bullet + */ + public static final String LIST_NUMBERING_SQUARE = "Square"; + /** + * ListNumbering: UpperAlpha: Uppercase letters (A, B, C, …) + */ + public static final String LIST_NUMBERING_UPPER_ALPHA = "UpperAlpha"; + /** + * ListNumbering: UpperRoman: Uppercase roman numerals (I, II, III, IV, …) + */ + public static final String LIST_NUMBERING_UPPER_ROMAN = "UpperRoman"; + + + /** + * Default constructor. + */ + public PDListAttributeObject() + { + this.setOwner(OWNER_LIST); + } + + /** + * Creates a new List attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDListAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the list numbering (ListNumbering). The default value is + * {@link #LIST_NUMBERING_NONE}. + * + * @return the list numbering + */ + public String getListNumbering() + { + return this.getName(LIST_NUMBERING, LIST_NUMBERING_NONE); + } + + /** + * Sets the list numbering (ListNumbering). The value shall be one of the + * following: + *
        + *
      • {@link #LIST_NUMBERING_NONE},
      • + *
      • {@link #LIST_NUMBERING_DISC},
      • + *
      • {@link #LIST_NUMBERING_CIRCLE},
      • + *
      • {@link #LIST_NUMBERING_SQUARE},
      • + *
      • {@link #LIST_NUMBERING_DECIMAL},
      • + *
      • {@link #LIST_NUMBERING_UPPER_ROMAN},
      • + *
      • {@link #LIST_NUMBERING_LOWER_ROMAN},
      • + *
      • {@link #LIST_NUMBERING_UPPER_ALPHA},
      • + *
      • {@link #LIST_NUMBERING_LOWER_ALPHA}.
      • + *
      + * + * @param listNumbering the list numbering + */ + public void setListNumbering(String listNumbering) + { + this.setName(LIST_NUMBERING, listNumbering); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder().append(super.toString()); + if (this.isSpecified(LIST_NUMBERING)) + { + sb.append(", ListNumbering=").append(this.getListNumbering()); + } + return sb.toString(); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDPrintFieldAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDPrintFieldAttributeObject.java new file mode 100644 index 000000000..53c990790 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDPrintFieldAttributeObject.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSDictionary; + +/** + * A PrintField attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDPrintFieldAttributeObject extends PDStandardAttributeObject +{ + + /** + * standard attribute owner: PrintField + */ + public static final String OWNER_PRINT_FIELD = "PrintField"; + + private static final String ROLE = "Role"; + private static final String CHECKED = "checked"; + private static final String DESC = "Desc"; + + /** + * role: rb: Radio button + */ + public static final String ROLE_RB = "rb"; + /** + * role: cb: Check box + */ + public static final String ROLE_CB = "cb"; + /** + * role: pb: Push button + */ + public static final String ROLE_PB = "pb"; + /** + * role: tv: Text-value field + */ + public static final String ROLE_TV = "tv"; + /** + * checked state: on + */ + public static final String CHECKED_STATE_ON = "on"; + /** + * checked state: off + */ + public static final String CHECKED_STATE_OFF = "off"; + /** + * checked state: neutral + */ + public static final String CHECKED_STATE_NEUTRAL = "neutral"; + + + /** + * Default constructor. + */ + public PDPrintFieldAttributeObject() + { + this.setOwner(OWNER_PRINT_FIELD); + } + + /** + * Creates a new PrintField attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDPrintFieldAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the role. + * + * @return the role + */ + public String getRole() + { + return this.getName(ROLE); + } + + /** + * Sets the role. The value of Role shall be one of the following: + *
        + *
      • {@link #ROLE_RB},
      • + *
      • {@link #ROLE_CB},
      • + *
      • {@link #ROLE_PB},
      • + *
      • {@link #ROLE_TV}.
      • + *
      + * + * @param role the role + */ + public void setRole(String role) + { + this.setName(ROLE, role); + } + + /** + * Gets the checked state. The default value is {@link #CHECKED_STATE_OFF}. + * + * @return the checked state + */ + public String getCheckedState() + { + return this.getName(CHECKED, CHECKED_STATE_OFF); + } + + /** + * Sets the checked state. The value shall be one of: + *
        + *
      • {@link #CHECKED_STATE_ON},
      • + *
      • {@link #CHECKED_STATE_OFF} (default), or
      • + *
      • {@link #CHECKED_STATE_NEUTRAL}.
      • + *
      + * + * @param checkedState the checked state + */ + public void setCheckedState(String checkedState) + { + this.setName(CHECKED, checkedState); + } + + /** + * Gets the alternate name of the field (Desc). + * + * @return the alternate name of the field + */ + public String getAlternateName() + { + return this.getString(DESC); + } + + /** + * Sets the alternate name of the field (Desc). + * + * @param alternateName the alternate name of the field + */ + public void setAlternateName(String alternateName) + { + this.setString(DESC, alternateName); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder().append(super.toString()); + if (this.isSpecified(ROLE)) + { + sb.append(", Role=").append(this.getRole()); + } + if (this.isSpecified(CHECKED)) + { + sb.append(", Checked=").append(this.getCheckedState()); + } + if (this.isSpecified(DESC)) + { + sb.append(", Desc=").append(this.getAlternateName()); + } + return sb.toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDStandardAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDStandardAttributeObject.java new file mode 100644 index 000000000..912cf7526 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDStandardAttributeObject.java @@ -0,0 +1,449 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDAttributeObject; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; + +/** + * A standard attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public abstract class PDStandardAttributeObject extends PDAttributeObject +{ + + /** + * Default constructor. + */ + public PDStandardAttributeObject() + { + } + + /** + * Creates a new standard attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDStandardAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Is the attribute with the given name specified in this attribute object? + * + * @param name the attribute name + * @return true if the attribute is specified, + * false otherwise + */ + public boolean isSpecified(String name) + { + return this.getCOSDictionary().getDictionaryObject(name) != null; + } + + + /** + * Gets a string attribute value. + * + * @param name the attribute name + * @return the string attribute value + */ + protected String getString(String name) + { + return this.getCOSDictionary().getString(name); + } + + /** + * Sets a string attribute value. + * + * @param name the attribute name + * @param value the string attribute value + */ + protected void setString(String name, String value) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setString(name, value); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Gets an array of strings. + * + * @param name the attribute name + * @return the array of strings + */ + protected String[] getArrayOfString(String name) + { + COSBase v = this.getCOSDictionary().getDictionaryObject(name); + if (v instanceof COSArray) + { + COSArray array = (COSArray) v; + String[] strings = new String[array.size()]; + for (int i = 0; i < array.size(); i++) + { + strings[i] = ((COSName) array.getObject(i)).getName(); + } + return strings; + } + return null; + } + + /** + * Sets an array of strings. + * + * @param name the attribute name + * @param values the array of strings + */ + protected void setArrayOfString(String name, String[] values) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + COSArray array = new COSArray(); + for (int i = 0; i < values.length; i++) + { + array.add(new COSString(values[i])); + } + this.getCOSDictionary().setItem(name, array); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Gets a name value. + * + * @param name the attribute name + * @return the name value + */ + protected String getName(String name) + { + return this.getCOSDictionary().getNameAsString(name); + } + + /** + * Gets a name value. + * + * @param name the attribute name + * @param defaultValue the default value + * @return the name value + */ + protected String getName(String name, String defaultValue) + { + return this.getCOSDictionary().getNameAsString(name, defaultValue); + } + + /** + * Gets a name value or array of name values. + * + * @param name the attribute name + * @param defaultValue the default value + * @return a String or array of Strings + */ + protected Object getNameOrArrayOfName(String name, String defaultValue) + { + COSBase v = this.getCOSDictionary().getDictionaryObject(name); + if (v instanceof COSArray) + { + COSArray array = (COSArray) v; + String[] names = new String[array.size()]; + for (int i = 0; i < array.size(); i++) + { + COSBase item = array.getObject(i); + if (item instanceof COSName) + { + names[i] = ((COSName) item).getName(); + } + } + return names; + } + if (v instanceof COSName) + { + return ((COSName) v).getName(); + } + return defaultValue; + } + + /** + * Sets a name value. + * + * @param name the attribute name + * @param value the name value + */ + protected void setName(String name, String value) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setName(name, value); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Sets an array of name values. + * + * @param name the attribute name + * @param values the array of name values + */ + protected void setArrayOfName(String name, String[] values) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + COSArray array = new COSArray(); + for (int i = 0; i < values.length; i++) + { + array.add(COSName.getPDFName(values[i])); + } + this.getCOSDictionary().setItem(name, array); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Gets a number or a name value. + * + * @param name the attribute name + * @param defaultValue the default name + * @return a Float or a String + */ + protected Object getNumberOrName(String name, String defaultValue) + { + COSBase value = this.getCOSDictionary().getDictionaryObject(name); + if (value instanceof COSNumber) + { + return ((COSNumber) value).floatValue(); + } + if (value instanceof COSName) + { + return ((COSName) value).getName(); + } + return defaultValue; + } + + /** + * Gets an integer. + * + * @param name the attribute name + * @param defaultValue the default value + * @return the integer + */ + protected int getInteger(String name, int defaultValue) + { + return this.getCOSDictionary().getInt(name, defaultValue); + } + + /** + * Sets an integer. + * + * @param name the attribute name + * @param value the integer + */ + protected void setInteger(String name, int value) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setInt(name, value); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Gets a number value. + * + * @param name the attribute name + * @param defaultValue the default value + * @return the number value + */ + protected float getNumber(String name, float defaultValue) + { + return this.getCOSDictionary().getFloat(name, defaultValue); + } + + /** + * Gets a number value. + * + * @param name the attribute name + * @return the number value + */ + protected float getNumber(String name) + { + return this.getCOSDictionary().getFloat(name); + } + + /** + * An "unspecified" default float value. + */ + protected static final float UNSPECIFIED = -1.f; + + /** + * Gets a number or an array of numbers. + * + * @param name the attribute name + * @param defaultValue the default value + * @return a Float or an array of floats + */ + protected Object getNumberOrArrayOfNumber(String name, float defaultValue) + { + COSBase v = this.getCOSDictionary().getDictionaryObject(name); + if (v instanceof COSArray) + { + COSArray array = (COSArray) v; + float[] values = new float[array.size()]; + for (int i = 0; i < array.size(); i++) + { + COSBase item = array.getObject(i); + if (item instanceof COSNumber) + { + values[i] = ((COSNumber) item).floatValue(); + } + } + return values; + } + if (v instanceof COSNumber) + { + return ((COSNumber) v).floatValue(); + } + if (defaultValue == UNSPECIFIED) + { + return null; + } + return defaultValue; + } + + /** + * Sets a float number. + * + * @param name the attribute name + * @param value the float number + */ + protected void setNumber(String name, float value) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setFloat(name, value); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Sets an integer number. + * + * @param name the attribute name + * @param value the integer number + */ + protected void setNumber(String name, int value) + { + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setInt(name, value); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Sets an array of float numbers. + * + * @param name the attribute name + * @param values the float numbers + */ + protected void setArrayOfNumber(String name, float[] values) + { + COSArray array = new COSArray(); + for (int i = 0; i < values.length; i++) + { + array.add(new COSFloat(values[i])); + } + COSBase oldBase = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setItem(name, array); + COSBase newBase = this.getCOSDictionary().getDictionaryObject(name); + this.potentiallyNotifyChanged(oldBase, newBase); + } + + /** + * Gets a colour. + * + * @param name the attribute name + * @return the colour + */ + protected PDGamma getColor(String name) + { + COSArray c = (COSArray) this.getCOSDictionary().getDictionaryObject(name); + if (c != null) + { + return new PDGamma(c); + } + return null; + } + + /** + * Gets a single colour or four colours. + * + * @param name the attribute name + * @return the single ({@link PDGamma}) or a ({@link PDFourColours}) + */ + protected Object getColorOrFourColors(String name) + { + COSArray array = + (COSArray) this.getCOSDictionary().getDictionaryObject(name); + if (array == null) + { + return null; + } + if (array.size() == 3) + { + // only one colour + return new PDGamma(array); + } + else if (array.size() == 4) + { + return new PDFourColours(array); + } + return null; + } + + /** + * Sets a colour. + * + * @param name the attribute name + * @param value the colour + */ + protected void setColor(String name, PDGamma value) + { + COSBase oldValue = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setItem(name, value); + COSBase newValue = value == null ? null : value.getCOSObject(); + this.potentiallyNotifyChanged(oldValue, newValue); + } + + /** + * Sets four colours. + * + * @param name the attribute name + * @param value the four colours + */ + protected void setFourColors(String name, PDFourColours value) + { + COSBase oldValue = this.getCOSDictionary().getDictionaryObject(name); + this.getCOSDictionary().setItem(name, value); + COSBase newValue = value == null ? null : value.getCOSObject(); + this.potentiallyNotifyChanged(oldValue, newValue); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDTableAttributeObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDTableAttributeObject.java new file mode 100644 index 000000000..8132a64bc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/PDTableAttributeObject.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; + +/** + * A Table attribute object. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class PDTableAttributeObject extends PDStandardAttributeObject +{ + + /** + * standard attribute owner: Table + */ + public static final String OWNER_TABLE = "Table"; + + protected static final String ROW_SPAN = "RowSpan"; + protected static final String COL_SPAN = "ColSpan"; + protected static final String HEADERS = "Headers"; + protected static final String SCOPE = "Scope"; + protected static final String SUMMARY = "Summary"; + + /** + * Scope: Both + */ + public static final String SCOPE_BOTH = "Both"; + /** + * Scope: Column + */ + public static final String SCOPE_COLUMN = "Column"; + /** + * Scope: Row + */ + public static final String SCOPE_ROW = "Row"; + + + /** + * Default constructor. + */ + public PDTableAttributeObject() + { + this.setOwner(OWNER_TABLE); + } + + /** + * Creates a new Table attribute object with a given dictionary. + * + * @param dictionary the dictionary + */ + public PDTableAttributeObject(COSDictionary dictionary) + { + super(dictionary); + } + + + /** + * Gets the number of rows in the enclosing table that shall be spanned by + * the cell (RowSpan). The default value is 1. + * + * @return the row span + */ + public int getRowSpan() + { + return this.getInteger(ROW_SPAN, 1); + } + + /** + * Sets the number of rows in the enclosing table that shall be spanned by + * the cell (RowSpan). + * + * @param rowSpan the row span + */ + public void setRowSpan(int rowSpan) + { + this.setInteger(ROW_SPAN, rowSpan); + } + + /** + * Gets the number of columns in the enclosing table that shall be spanned + * by the cell (ColSpan). The default value is 1. + * + * @return the column span + */ + public int getColSpan() + { + return this.getInteger(COL_SPAN, 1); + } + + /** + * Sets the number of columns in the enclosing table that shall be spanned + * by the cell (ColSpan). + * + * @param colSpan the column span + */ + public void setColSpan(int colSpan) + { + this.setInteger(COL_SPAN, colSpan); + } + + /** + * Gets the headers (Headers). An array of byte strings, where each string + * shall be the element identifier (see the + * {@link PDStructureElement#getElementIdentifier()}) for a TH structure + * element that shall be used as a header associated with this cell. + * + * @return the headers. + */ + public String[] getHeaders() + { + return this.getArrayOfString(HEADERS); + } + + /** + * Sets the headers (Headers). An array of byte strings, where each string + * shall be the element identifier (see the + * {@link PDStructureElement#getElementIdentifier()}) for a TH structure + * element that shall be used as a header associated with this cell. + * + * @param headers the headers + */ + public void setHeaders(String[] headers) + { + this.setArrayOfString(HEADERS, headers); + } + + /** + * Gets the scope (Scope). It shall reflect whether the header cell applies + * to the rest of the cells in the row that contains it, the column that + * contains it, or both the row and the column that contain it. + * + * @return the scope + */ + public String getScope() + { + return this.getName(SCOPE); + } + + /** + * Sets the scope (Scope). It shall reflect whether the header cell applies + * to the rest of the cells in the row that contains it, the column that + * contains it, or both the row and the column that contain it. The value + * shall be one of the following: + *
        + *
      • {@link #SCOPE_ROW},
      • + *
      • {@link #SCOPE_COLUMN}, or
      • + *
      • {@link #SCOPE_BOTH}.
      • + *
      + * + * @param scope the scope + */ + public void setScope(String scope) + { + this.setName(SCOPE, scope); + } + + /** + * Gets the summary of the table’s purpose and structure. + * + * @return the summary + */ + public String getSummary() + { + return this.getString(SUMMARY); + } + + /** + * Sets the summary of the table’s purpose and structure. + * + * @param summary the summary + */ + public void setSummary(String summary) + { + this.setString(SUMMARY, summary); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder().append(super.toString()); + if (this.isSpecified(ROW_SPAN)) + { + sb.append(", RowSpan=").append(String.valueOf(this.getRowSpan())); + } + if (this.isSpecified(COL_SPAN)) + { + sb.append(", ColSpan=").append(String.valueOf(this.getColSpan())); + } + if (this.isSpecified(HEADERS)) + { + sb.append(", Headers=").append(arrayToString(this.getHeaders())); + } + if (this.isSpecified(SCOPE)) + { + sb.append(", Scope=").append(this.getScope()); + } + if (this.isSpecified(SUMMARY)) + { + sb.append(", Summary=").append(this.getSummary()); + } + return sb.toString(); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/StandardStructureTypes.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/StandardStructureTypes.java new file mode 100644 index 000000000..095341c0c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/StandardStructureTypes.java @@ -0,0 +1,320 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * The standard structure types. + * + * @author Johannes Koch + * @version $Revision: $ + */ +public class StandardStructureTypes +{ + + private StandardStructureTypes() + { + } + + + // Grouping Elements + /** + * Document + */ + public static final String DOCUMENT = "Document"; + + /** + * Part + */ + public static final String PART = "Part"; + + /** + * Art + */ + public static final String ART = "Art"; + + /** + * Sect + */ + public static final String SECT = "Sect"; + + /** + * Div + */ + public static final String DIV = "Div"; + + /** + * BlockQuote + */ + public static final String BLOCK_QUOTE = "BlockQuote"; + + /** + * Caption + */ + public static final String CAPTION = "Caption"; + + /** + * TOC + */ + public static final String TOC = "TOC"; + + /** + * TOCI + */ + public static final String TOCI = "TOCI"; + + /** + * Index + */ + public static final String INDEX = "Index"; + + /** + * NonStruct + */ + public static final String NON_STRUCT = "NonStruct"; + + /** + * Private + */ + public static final String PRIVATE = "Private"; + + + // Block-Level Structure Elements + /** + * P + */ + public static final String P = "P"; + + /** + * H + */ + public static final String H = "H"; + + /** + * H1 + */ + public static final String H1 = "H1"; + + /** + * H2 + */ + public static final String H2 = "H2"; + + /** + * H3 + */ + public static final String H3 = "H3"; + + /** + * H4 + */ + public static final String H4 = "H4"; + + /** + * H5 + */ + public static final String H5 = "H5"; + + /** + * H6 + */ + public static final String H6 = "H6"; + + /** + * L + */ + public static final String L = "L"; + + /** + * LI + */ + public static final String LI = "LI"; + + /** + * Lbl + */ + public static final String LBL = "Lbl"; + + /** + * LBody + */ + public static final String L_BODY = "LBody"; + + /** + * Table + */ + public static final String TABLE = "Table"; + + /** + * TR + */ + public static final String TR = "TR"; + + /** + * TH + */ + public static final String TH = "TH"; + + /** + * TD + */ + public static final String TD = "TD"; + + /** + * THead + */ + public static final String T_HEAD = "THead"; + + /** + * TBody + */ + public static final String T_BODY = "TBody"; + + /** + * TFoot + */ + public static final String T_FOOT = "TFoot"; + + + // Inline-Level Structure Elements + /** + * Span + */ + public static final String SPAN = "Span"; + + /** + * Quote + */ + public static final String QUOTE = "Quote"; + + /** + * Note + */ + public static final String NOTE = "Note"; + + /** + * Reference + */ + public static final String REFERENCE = "Reference"; + + /** + * BibEntry + */ + public static final String BIB_ENTRY = "BibEntry"; + + /** + * Code + */ + public static final String CODE = "Code"; + + /** + * Link + */ + public static final String LINK = "Link"; + + /** + * Annot + */ + public static final String ANNOT = "Annot"; + + /** + * Ruby + */ + public static final String RUBY = "Ruby"; + + /** + * RB + */ + public static final String RB = "RB"; + + /** + * RT + */ + public static final String RT = "RT"; + + /** + * RP + */ + public static final String RP = "RP"; + + /** + * Warichu + */ + public static final String WARICHU = "Warichu"; + + /** + * WT + */ + public static final String WT = "WT"; + + /** + * WP + */ + public static final String WP = "WP"; + + + // Illustration Elements + /** + * Figure + */ + public static final String Figure = "Figure"; + + /** + * Formula + */ + public static final String FORMULA = "Formula"; + + /** + * Form + */ + public static final String FORM = "Form"; + + /** + * All standard structure types. + */ + public static List types = new ArrayList(); + + static + { + Field[] fields = StandardStructureTypes.class.getFields(); + for (int i = 0; i < fields.length; i++) + { + if (Modifier.isFinal(fields[i].getModifiers())) + { + try + { + types.add(fields[i].get(null).toString()); + } + catch (IllegalArgumentException e) + { + e.printStackTrace(); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + } + Collections.sort(types); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/package.html new file mode 100644 index 000000000..8448608da --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/documentinterchange/taggedpdf/package.html @@ -0,0 +1,26 @@ + + + + + + + +The tagged PDF package provides a mechanism for incorporating "tags" (standard +structure types and attributes) into a PDF file. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/PDPageContentStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/PDPageContentStream.java new file mode 100644 index 000000000..f69ffc139 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/PDPageContentStream.java @@ -0,0 +1,1355 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.edit; + +import java.awt.Color; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.COSStreamArray; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceN; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; +import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased; +import org.apache.pdfbox.pdmodel.graphics.color.PDPattern; +import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.apache.pdfbox.util.MapUtil; + + +/** + * 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 + * @version $Revision: 1.19 $ + */ +public class PDPageContentStream +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDPageContentStream.class); + + private PDPage page; + private OutputStream output; + private boolean inTextMode = false; + private Map fontMappings; + private Map xobjectMappings; + private PDResources resources; + private Map fonts; + private Map xobjects; + + private PDColorSpace currentStrokingColorSpace = new PDDeviceGray(); + private PDColorSpace currentNonStrokingColorSpace = new PDDeviceGray(); + + //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 SET_TEXT_MATRIX = "Tm\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 RECTANGLE = "re\n"; + private static final String FILL_NON_ZERO = "f\n"; + private static final String FILL_EVEN_ODD = "f*\n"; + private static final String LINE_TO = "l\n"; + private static final String MOVE_TO = "m\n"; + private static final String CLOSE_STROKE = "s\n"; + private static final String STROKE = "S\n"; + private static final String LINE_WIDTH = "w\n"; + private static final String CLOSE_SUBPATH = "h\n"; + private static final String CLIP_PATH_NON_ZERO = "W\n"; + private static final String CLIP_PATH_EVEN_ODD = "W*\n"; + private static final String NOP = "n\n"; + private static final String BEZIER_312 = "c\n"; + private static final String BEZIER_32 = "v\n"; + private static final String BEZIER_313 = "y\n"; + + private static final String MP = "MP\n"; + private static final String DP = "DP\n"; + private static final String BMC = "BMC\n"; + private static final String BDC = "BDC\n"; + private static final String EMC = "EMC\n"; + + private static final String SET_STROKING_COLORSPACE = "CS\n"; + private static final String SET_NON_STROKING_COLORSPACE = "cs\n"; + + private static final String SET_STROKING_COLOR_SIMPLE="SC\n"; + private static final String SET_STROKING_COLOR_COMPLEX="SCN\n"; + private static final String SET_NON_STROKING_COLOR_SIMPLE="sc\n"; + private static final String SET_NON_STROKING_COLOR_COMPLEX="scn\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 + { + this(document,sourcePage,appendContent,compress,false); + } + /** + * 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. + * @param resetContext Tell if the graphic context should be reseted. + * @throws IOException If there is an error writing to the page contents. + */ + public PDPageContentStream( PDDocument document, PDPage sourcePage, boolean appendContent, boolean compress, boolean resetContext ) + throws IOException + { + + page = sourcePage; + resources = page.getResources(); + if( resources == null ) + { + resources = new PDResources(); + page.setResources( resources ); + } + + //Fonts including reverse lookup + fonts = resources.getFonts(); + fontMappings = reverseMap(fonts, PDFont.class); + + //XObjects including reverse lookup + xobjects = resources.getXObjects(); + xobjectMappings = reverseMap(xobjects, PDXObject.class); + + // Get the pdstream from the source page instead of creating a new one + PDStream contents = sourcePage.getContents(); + boolean hasContent = contents != null; + + // If request specifies the need to append to the document + if(appendContent && hasContent) + { + + // 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 ); + } + + if (resetContext) + { + // create a new stream to encapsulate the existing stream + PDStream saveGraphics = new PDStream( document ); + output = saveGraphics.createOutputStream(); + // save the initial/unmodified graphics context + saveGraphicsState(); + close(); + if( compress ) + { + List filters = new ArrayList(); + filters.add( COSName.FLATE_DECODE ); + saveGraphics.setFilters( filters ); + } + // insert the new stream at the beginning + compoundStream.insertCOSStream(saveGraphics); + } + + // Sets the compoundStream as page contents + sourcePage.setContents( new PDStream(compoundStream) ); + output = contentsToAppend.createOutputStream(); + if (resetContext) + { + // restore the initial/unmodified graphics context + restoreGraphicsState(); + } + } + else + { + if (hasContent) + { + log.warn("You are overwriting an existing content, you should use the append mode"); + } + 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 ); + } + + private Map reverseMap(Map map, Class keyClass) + { + Map reversed = new java.util.HashMap(); + for (Object o : map.entrySet()) + { + Map.Entry entry = (Map.Entry)o; + reversed.put(keyClass.cast(entry.getValue()), (String)entry.getKey()); + } + return reversed; + } + + /** + * 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 = fontMappings.get( font ); + if( fontMapping == null ) + { + fontMapping = MapUtil.getNextUniqueKey( fonts, "F" ); + fontMappings.put( font, fontMapping ); + fonts.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, float x, float y ) throws IOException + { + drawXObject( image, x, y, image.getWidth(), image.getHeight() ); + } + + /** + * Draw an xobject(form or image) at the x,y coordinates and a certain width and height. + * + * @param xobject The xobject 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 drawXObject( PDXObject xobject, float x, float y, float width, float height ) throws IOException + { + AffineTransform transform = new AffineTransform(width, 0, 0, height, x, y); + drawXObject(xobject, transform); + } + + /** + * Draw an xobject(form or image) using the given {@link AffineTransform} to position + * the xobject. + * + * @param xobject The xobject to draw. + * @param transform the transformation matrix + * @throws IOException If there is an error writing to the stream. + */ + public void drawXObject( PDXObject xobject, AffineTransform transform ) throws IOException + { + String xObjectPrefix = null; + if( xobject instanceof PDXObjectImage ) + { + xObjectPrefix = "Im"; + } + else + { + xObjectPrefix = "Form"; + } + + String objMapping = xobjectMappings.get( xobject ); + if( objMapping == null ) + { + objMapping = MapUtil.getNextUniqueKey( xobjects, xObjectPrefix ); + xobjectMappings.put( xobject, objMapping ); + xobjects.put( objMapping, xobject ); + } + saveGraphicsState(); + appendRawCommands( SPACE ); + concatenate2CTM(transform); + appendRawCommands( SPACE ); + appendRawCommands( "/" ); + appendRawCommands( objMapping ); + appendRawCommands( SPACE ); + appendRawCommands( XOBJECT_DO ); + restoreGraphicsState(); + } + + + /** + * The Td operator. + * A current text matrix will be replaced with a new one (1 0 0 1 x y). + * @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 ); + } + + /** + * The Tm operator. Sets the text matrix to the given values. + * A current text matrix will be replaced with the new one. + * @param a The a value of the matrix. + * @param b The b value of the matrix. + * @param c The c value of the matrix. + * @param d The d value of the matrix. + * @param e The e value of the matrix. + * @param f The f value of the matrix. + * @throws IOException If there is an error writing to the stream. + */ + public void setTextMatrix( double a, double b, double c, double d, double e, double f ) throws IOException + { + if( !inTextMode ) + { + throw new IOException( "Error: must call beginText() before setTextMatrix"); + } + appendRawCommands( formatDecimal.format( a ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( b ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( c ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( e ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( f ) ); + appendRawCommands( SPACE ); + appendRawCommands( SET_TEXT_MATRIX ); + } + + /** + * The Tm operator. Sets the text matrix to the given values. + * A current text matrix will be replaced with the new one. + * @param matrix the transformation matrix + * @throws IOException If there is an error writing to the stream. + */ + public void setTextMatrix(AffineTransform matrix) throws IOException + { + appendMatrix(matrix); + appendRawCommands(SET_TEXT_MATRIX); + } + + /** + * The Tm operator. Sets the text matrix to the given scaling and translation values. + * A current text matrix will be replaced with the new one. + * @param sx The scaling factor in x-direction. + * @param sy The scaling factor in y-direction. + * @param tx The translation value in x-direction. + * @param ty The translation value in y-direction. + * @throws IOException If there is an error writing to the stream. + */ + public void setTextScaling( double sx, double sy, double tx, double ty ) throws IOException + { + setTextMatrix(sx, 0, 0, sy, tx, ty); + } + + /** + * The Tm operator. Sets the text matrix to the given translation values. + * A current text matrix will be replaced with the new one. + * @param tx The translation value in x-direction. + * @param ty The translation value in y-direction. + * @throws IOException If there is an error writing to the stream. + */ + public void setTextTranslation( double tx, double ty ) throws IOException + { + setTextMatrix(1, 0, 0, 1, tx, ty); + } + + /** + * The Tm operator. Sets the text matrix to the given rotation and translation values. + * A current text matrix will be replaced with the new one. + * @param angle The angle used for the counterclockwise rotation in radians. + * @param tx The translation value in x-direction. + * @param ty The translation value in y-direction. + * @throws IOException If there is an error writing to the stream. + */ + public void setTextRotation( double angle, double tx, double ty ) throws IOException + { + double angleCos = Math.cos(angle); + double angleSin = Math.sin(angle); + setTextMatrix( angleCos, angleSin, -angleSin, angleCos, tx, ty); + } + + /** + * The Cm operator. Concatenates the current transformation matrix with the given values. + * @param a The a value of the matrix. + * @param b The b value of the matrix. + * @param c The c value of the matrix. + * @param d The d value of the matrix. + * @param e The e value of the matrix. + * @param f The f value of the matrix. + * @throws IOException If there is an error writing to the stream. + */ + public void concatenate2CTM( double a, double b, double c, double d, double e, double f ) throws IOException + { + appendRawCommands( formatDecimal.format( a ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( b ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( c ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( e ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( f ) ); + appendRawCommands( SPACE ); + appendRawCommands( CONCATENATE_MATRIX ); + } + + /** + * The Cm operator. Concatenates the current transformation matrix with the given + * {@link AffineTransform}. + * @param at the transformation matrix + * @throws IOException If there is an error writing to the stream. + */ + public void concatenate2CTM(AffineTransform at) throws IOException + { + appendMatrix(at); + appendRawCommands(CONCATENATE_MATRIX); + } + + /** + * 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 space. This will add the colorspace to the PDResources + * if necessary. + * + * @param colorSpace The colorspace to write. + * @throws IOException If there is an error writing the colorspace. + */ + public void setStrokingColorSpace( PDColorSpace colorSpace ) throws IOException + { + currentStrokingColorSpace = colorSpace; + writeColorSpace( colorSpace ); + appendRawCommands( SET_STROKING_COLORSPACE ); + } + + /** + * Set the stroking color space. This will add the colorspace to the PDResources + * if necessary. + * + * @param colorSpace The colorspace to write. + * @throws IOException If there is an error writing the colorspace. + */ + public void setNonStrokingColorSpace( PDColorSpace colorSpace ) throws IOException + { + currentNonStrokingColorSpace = colorSpace; + writeColorSpace( colorSpace ); + appendRawCommands( SET_NON_STROKING_COLORSPACE ); + } + + private void writeColorSpace( PDColorSpace colorSpace ) throws IOException + { + COSName key = null; + if( colorSpace instanceof PDDeviceGray || + colorSpace instanceof PDDeviceRGB || + colorSpace instanceof PDDeviceCMYK ) + { + key = COSName.getPDFName( colorSpace.getName() ); + } + else + { + COSDictionary colorSpaces = + (COSDictionary)resources.getCOSDictionary().getDictionaryObject(COSName.COLORSPACE); + if( colorSpaces == null ) + { + colorSpaces = new COSDictionary(); + resources.getCOSDictionary().setItem( COSName.COLORSPACE, colorSpaces ); + } + key = colorSpaces.getKeyForValue( colorSpace.getCOSObject() ); + + if( key == null ) + { + int counter = 0; + String csName = "CS"; + while( colorSpaces.containsValue( csName + counter ) ) + { + counter++; + } + key = COSName.getPDFName( csName + counter ); + colorSpaces.setItem( key, colorSpace ); + } + } + key.writePDF( output ); + appendRawCommands( SPACE ); + } + + /** + * Set the color components of current stroking colorspace. + * + * @param components The components to set for the current color. + * @throws IOException If there is an error while writing to the stream. + */ + public void setStrokingColor( float[] components ) throws IOException + { + for( int i=0; i< components.length; i++ ) + { + appendRawCommands( formatDecimal.format( components[i] ) ); + appendRawCommands( SPACE ); + } + if( currentStrokingColorSpace instanceof PDSeparation || + currentStrokingColorSpace instanceof PDPattern || + currentStrokingColorSpace instanceof PDDeviceN || + currentStrokingColorSpace instanceof PDICCBased ) + { + appendRawCommands( SET_STROKING_COLOR_COMPLEX ); + } + else + { + appendRawCommands( SET_STROKING_COLOR_SIMPLE ); + } + } + + /** + * 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 color components of current non stroking colorspace. + * + * @param components The components to set for the current color. + * @throws IOException If there is an error while writing to the stream. + */ + public void setNonStrokingColor( float[] components ) throws IOException + { + for( int i=0; i< components.length; i++ ) + { + appendRawCommands( formatDecimal.format( components[i] ) ); + appendRawCommands( SPACE ); + } + if( currentNonStrokingColorSpace instanceof PDSeparation || + currentNonStrokingColorSpace instanceof PDPattern || + currentNonStrokingColorSpace instanceof PDDeviceN || + currentNonStrokingColorSpace instanceof PDICCBased ) + { + appendRawCommands( SET_NON_STROKING_COLOR_COMPLEX ); + } + else + { + appendRawCommands( SET_NON_STROKING_COLOR_SIMPLE ); + } + } + + /** + * 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 ); + } + + /** + * Add a rectangle to the current path. + * + * @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 addRect( 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( RECTANGLE ); + } + + /** + * 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 + { + addRect(x, y, width, height); + fill(PathIterator.WIND_NON_ZERO); + } + + /** + * Append a cubic Bézier curve to the current path. The curve extends from the current + * point to the point (x3 , y3 ), using (x1 , y1 ) and (x2 , y2 ) as the Bézier control points + * @param x1 x coordinate of the point 1 + * @param y1 y coordinate of the point 1 + * @param x2 x coordinate of the point 2 + * @param y2 y coordinate of the point 2 + * @param x3 x coordinate of the point 3 + * @param y3 y coordinate of the point 3 + * @throws IOException If there is an error while adding the . + */ + public void addBezier312(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException + { + appendRawCommands( formatDecimal.format( x1) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y1) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( x2) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y2) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( x3) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y3) ); + appendRawCommands( SPACE ); + appendRawCommands( BEZIER_312 ); + } + + /** + * Append a cubic Bézier curve to the current path. The curve extends from the current + * point to the point (x3 , y3 ), using the current point and (x2 , y2 ) as the Bézier control points + * @param x2 x coordinate of the point 2 + * @param y2 y coordinate of the point 2 + * @param x3 x coordinate of the point 3 + * @param y3 y coordinate of the point 3 + * @throws IOException If there is an error while adding the . + */ + public void addBezier32(float x2, float y2, float x3, float y3) throws IOException + { + appendRawCommands( formatDecimal.format( x2) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y2) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( x3) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y3) ); + appendRawCommands( SPACE ); + appendRawCommands( BEZIER_32 ); + } + + /** + * Append a cubic Bézier curve to the current path. The curve extends from the current + * point to the point (x3 , y3 ), using (x1 , y1 ) and (x3 , y3 ) as the Bézier control points + * @param x1 x coordinate of the point 1 + * @param y1 y coordinate of the point 1 + * @param x3 x coordinate of the point 3 + * @param y3 y coordinate of the point 3 + * @throws IOException If there is an error while adding the . + */ + public void addBezier31(float x1, float y1, float x3, float y3) throws IOException + { + appendRawCommands( formatDecimal.format( x1) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y1) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( x3) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y3) ); + appendRawCommands( SPACE ); + appendRawCommands( BEZIER_313 ); + } + + + /** + * Add a line to the given coordinate. + * + * @param x The x coordinate. + * @param y The y coordinate. + * @throws IOException If there is an error while adding the line. + */ + public void moveTo( float x, float y) throws IOException + { + // moveTo + appendRawCommands( formatDecimal.format( x) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y) ); + appendRawCommands( SPACE ); + appendRawCommands( MOVE_TO ); + } + + /** + * Add a move to the given coordinate. + * + * @param x The x coordinate. + * @param y The y coordinate. + * @throws IOException If there is an error while adding the line. + */ + public void lineTo( float x, float y) throws IOException + { + // moveTo + appendRawCommands( formatDecimal.format( x) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y) ); + appendRawCommands( SPACE ); + appendRawCommands( LINE_TO ); + } + /** + * add a line to the current path. + * + * @param xStart The start x coordinate. + * @param yStart The start y coordinate. + * @param xEnd The end x coordinate. + * @param yEnd The end y coordinate. + * @throws IOException If there is an error while adding the line. + */ + public void addLine( float xStart, float yStart, float xEnd, float yEnd ) throws IOException + { + // moveTo + moveTo(xStart, yStart); + // lineTo + lineTo(xEnd, yEnd); + } + + /** + * Draw a line on the page using the current non stroking color and the current line width. + * + * @param xStart The start x coordinate. + * @param yStart The start y coordinate. + * @param xEnd The end x coordinate. + * @param yEnd The end y coordinate. + * @throws IOException If there is an error while drawing on the screen. + */ + public void drawLine( float xStart, float yStart, float xEnd, float yEnd ) throws IOException + { + addLine(xStart, yStart, xEnd, yEnd); + // stroke + stroke(); + } + + /** + * Add a polygon to the current path. + * @param x x coordinate of each points + * @param y y coordinate of each points + * @throws IOException If there is an error while drawing on the screen. + */ + public void addPolygon(float[] x, float[] y) throws IOException + { + if (x.length != y.length) + { + throw new IOException( "Error: some points are missing coordinate" ); + } + for (int i = 0; i < x.length; i++) + { + if (i == 0) + { + moveTo(x[i], y[i]); + } + else + { + lineTo(x[i], y[i]); + } + } + closeSubPath(); + } + + /** + * Draw a polygon on the page using the current non stroking color. + * @param x x coordinate of each points + * @param y y coordinate of each points + * @throws IOException If there is an error while drawing on the screen. + */ + public void drawPolygon(float[] x, float[] y) throws IOException + { + addPolygon(x, y); + stroke(); + } + + /** + * Draw and fill a polygon on the page using the current non stroking color. + * @param x x coordinate of each points + * @param y y coordinate of each points + * @throws IOException If there is an error while drawing on the screen. + */ + public void fillPolygon(float[] x, float[] y) throws IOException + { + addPolygon(x, y); + fill(PathIterator.WIND_NON_ZERO); + } + + /** + * Stroke the path. + */ + public void stroke() throws IOException + { + appendRawCommands( STROKE ); + } + + /** + * Close and stroke the path. + */ + public void closeAndStroke() throws IOException + { + appendRawCommands( CLOSE_STROKE ); + } + + /** + * Fill the path. + */ + public void fill(int windingRule) throws IOException + { + if (windingRule == PathIterator.WIND_NON_ZERO) + { + appendRawCommands( FILL_NON_ZERO ); + } + else if (windingRule == PathIterator.WIND_EVEN_ODD) + { + appendRawCommands( FILL_EVEN_ODD ); + } + else + { + throw new IOException( "Error: unknown value for winding rule" ); + } + + } + + /** + * Close subpath. + */ + public void closeSubPath() throws IOException + { + appendRawCommands( CLOSE_SUBPATH ); + } + + /** + * Clip path. + */ + public void clipPath(int windingRule) throws IOException + { + if (windingRule == PathIterator.WIND_NON_ZERO) + { + appendRawCommands( CLIP_PATH_NON_ZERO ); + appendRawCommands( NOP ); + } + else if (windingRule == PathIterator.WIND_EVEN_ODD) + { + appendRawCommands( CLIP_PATH_EVEN_ODD ); + appendRawCommands( NOP ); + } + else + { + throw new IOException( "Error: unknown value for winding rule" ); + } + } + + /** + * Set linewidth to the given value. + * + * @param lineWidth The width which is used for drwaing. + * @throws IOException If there is an error while drawing on the screen. + */ + public void setLineWidth(float lineWidth) throws IOException + { + appendRawCommands( formatDecimal.format( lineWidth ) ); + appendRawCommands( SPACE ); + appendRawCommands( LINE_WIDTH ); + } + + /** + * Begin a marked content sequence. + * @param tag the tag + * @throws IOException if an I/O error occurs + */ + public void beginMarkedContentSequence(COSName tag) throws IOException + { + appendCOSName(tag); + appendRawCommands(SPACE); + appendRawCommands(BMC); + } + + /** + * Begin a marked content sequence with a reference to an entry in the page resources' + * Properties dictionary. + * @param tag the tag + * @param propsName the properties reference + * @throws IOException if an I/O error occurs + */ + public void beginMarkedContentSequence(COSName tag, COSName propsName) throws IOException + { + appendCOSName(tag); + appendRawCommands(SPACE); + appendCOSName(propsName); + appendRawCommands(SPACE); + appendRawCommands(BDC); + } + + /** + * End a marked content sequence. + * @throws IOException if an I/O error occurs + */ + public void endMarkedContentSequence() throws IOException + { + appendRawCommands(EMC); + } + + /** + * q operator. Saves the current graphics state. + * @throws IOException If an error occurs while writing to the stream. + */ + public void saveGraphicsState() throws IOException + { + appendRawCommands( SAVE_GRAPHICS_STATE); + } + + /** + * Q operator. Restores the current graphics state. + * @throws IOException If an error occurs while writing to the stream. + */ + public void restoreGraphicsState() throws IOException + { + appendRawCommands( RESTORE_GRAPHICS_STATE ); + } + + /** + * 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 ); + } + + /** + * This will append a {@link COSName} to the content stream. + * @param name the name + * @throws IOException If an error occurs while writing to the stream. + */ + public void appendCOSName(COSName name) throws IOException + { + name.writePDF(output); + } + + private void appendMatrix(AffineTransform transform) throws IOException + { + double[] values = new double[6]; + transform.getMatrix(values); + for (double v : values) + { + appendRawCommands(formatDecimal.format(v)); + appendRawCommands(SPACE); + } + } + + /** + * 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(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/package.html new file mode 100644 index 000000000..3a74c841b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/edit/package.html @@ -0,0 +1,25 @@ + + + + + + + +The PDModel edit package will be used to store classes for creating page content. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/AccessPermission.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/AccessPermission.java new file mode 100644 index 000000000..0c4b299b2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/AccessPermission.java @@ -0,0 +1,422 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +/** + * This class represents the access permissions to a document. + * These permissions are specified in the PDF format specifications, they include: + *
        + *
      • print the document
      • + *
      • modify the content of the document
      • + *
      • copy or extract content of the document
      • + *
      • add or modify annotations
      • + *
      • fill in interactive form fields
      • + *
      • extract text and graphics for accessibility to visually impaired people
      • + *
      • assemble the document
      • + *
      • print in degraded quality
      • + *
      + * + * This class can be used to protect a document by assigning access permissions to recipients. + * In this case, it must be used with a specific ProtectionPolicy. + * + * + * When a document is decrypted, it has a currentAccessPermission property which is the access permissions + * granted to the user who decrypted the document. + * + * @see ProtectionPolicy + * @see org.apache.pdfbox.pdmodel.PDDocument#getCurrentAccessPermission() + * + * @author Ben Litchfield + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.4 $ + */ + +public class AccessPermission +{ + + private 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; + + private int bytes = DEFAULT_PERMISSIONS; + + private boolean readOnly = false; + + /** + * Create a new access permission object. + * By default, all permissions are granted. + */ + public AccessPermission() + { + bytes = DEFAULT_PERMISSIONS; + } + + /** + * Create a new access permission object from a byte array. + * Bytes are ordered most significant byte first. + * + * @param b the bytes as defined in PDF specs + */ + + public AccessPermission(byte[] b) + { + bytes = 0; + bytes |= b[0] & 0xFF; + bytes <<= 8; + bytes |= b[1] & 0xFF; + bytes <<= 8; + bytes |= b[2] & 0xFF; + bytes <<= 8; + bytes |= b[3] & 0xFF; + } + + /** + * Creates a new access permission object from a single integer. + * + * @param permissions The permission bits. + */ + public AccessPermission( int permissions ) + { + bytes = permissions; + } + + private boolean isPermissionBitOn( int bit ) + { + return (bytes & (1 << (bit-1))) != 0; + } + + private boolean setPermissionBit( int bit, boolean value ) + { + int permissions = bytes; + if( value ) + { + permissions = permissions | (1 << (bit-1)); + } + else + { + permissions = permissions & (0xFFFFFFFF ^ (1 << (bit-1))); + } + bytes = permissions; + + return (bytes & (1 << (bit-1))) != 0; + } + + + + + /** + * This will tell if the access permission corresponds to owner + * access permission (no restriction). + * + * @return true if the access permission does not restrict the use of the document + */ + public boolean isOwnerPermission() + { + return (this.canAssembleDocument() + && this.canExtractContent() + && this.canExtractForAccessibility() + && this.canFillInForm() + && this.canModify() + && this.canModifyAnnotations() + && this.canPrint() + && this.canPrintDegraded() + ); + } + + /** + * returns an access permission object for a document owner. + * + * @return A standard owner access permission set. + */ + + public static AccessPermission getOwnerAccessPermission() + { + AccessPermission ret = new AccessPermission(); + ret.setCanAssembleDocument(true); + ret.setCanExtractContent(true); + ret.setCanExtractForAccessibility(true); + ret.setCanFillInForm(true); + ret.setCanModify(true); + ret.setCanModifyAnnotations(true); + ret.setCanPrint(true); + ret.setCanPrintDegraded(true); + return ret; + } + + /** + * This returns an integer representing the access permissions. + * This integer can be used for public key encryption. This format + * is not documented in the PDF specifications but is necessary for compatibility + * with Adobe Acrobat and Adobe Reader. + * + * @return the integer representing access permissions + */ + + public int getPermissionBytesForPublicKey() + { + setPermissionBit(1, true); + setPermissionBit(7, false); + setPermissionBit(8, false); + for(int i=13; i<=32; i++) + { + setPermissionBit(i, false); + } + return bytes; + } + + /** + * The returns an integer representing the access permissions. + * This integer can be used for standard PDF encryption as specified + * in the PDF specifications. + * + * @return the integer representing the access permissions + */ + public int getPermissionBytes() + { + return bytes; + } + + /** + * 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. + * This method will have no effect if the object is in read only mode + * + * @param allowPrinting A boolean determining if the user can print. + */ + public void setCanPrint( boolean allowPrinting ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowModifications A boolean determining if the user can modify the document. + */ + public void setCanModify( boolean allowModifications ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowExtraction A boolean determining if the user can extract content + * from the document. + */ + public void setCanExtractContent( boolean allowExtraction ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowAnnotationModification A boolean determining if the user can modify annotations. + */ + public void setCanModifyAnnotations( boolean allowAnnotationModification ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowFillingInForm A boolean determining if the user can fill in interactive forms. + */ + public void setCanFillInForm( boolean allowFillingInForm ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowExtraction A boolean determining if the user can extract content + * from the document. + */ + public void setCanExtractForAccessibility( boolean allowExtraction ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowAssembly A boolean determining if the user can assemble the document. + */ + public void setCanAssembleDocument( boolean allowAssembly ) + { + if(!readOnly) + { + 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. + * This method will have no effect if the object is in read only mode + * + * @param allowAssembly A boolean determining if the user can print the + * document in a degraded format. + */ + public void setCanPrintDegraded( boolean allowAssembly ) + { + if(!readOnly) + { + setPermissionBit( DEGRADED_PRINT_BIT, allowAssembly ); + } + } + + /** + * Locks the access permission read only (ie, the setters will have no effects). + * After that, the object cannot be unlocked. + * This method is used for the currentAccessPermssion of a document to avoid + * users to change access permission. + */ + public void setReadOnly() + { + readOnly = true; + } + + /** + * This will tell if the object has been set as read only. + * + * @return true if the object is in read only mode. + */ + + public boolean isReadOnly() + { + return readOnly; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/BadSecurityHandlerException.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/BadSecurityHandlerException.java new file mode 100644 index 000000000..d2fa6cd2a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/BadSecurityHandlerException.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +/** + * This exception can be thrown by the SecurityHandlersManager class when + * a document required an unimplemented security handler to be opened. + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * @version $Revision: 1.2 $ + */ + +public class BadSecurityHandlerException extends Exception +{ + /** + * Default Constructor. + */ + public BadSecurityHandlerException() + { + super(); + } + + /** + * Constructor. + * + * @param e A sub exception. + */ + public BadSecurityHandlerException(Exception e) + { + super(e); + } + + /** + * Constructor. + * + * @param msg Message describing exception. + */ + public BadSecurityHandlerException(String msg) + { + super(msg); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/DecryptionMaterial.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/DecryptionMaterial.java new file mode 100644 index 000000000..877d1153a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/DecryptionMaterial.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +/** + * This class represents data required to decrypt PDF documents. This can + * be a password for standard security or a X509 certificate with a private + * key for public key security. + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * @version $Revision: 1.2 $ + */ +public abstract class DecryptionMaterial +{ +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDCryptFilterDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDCryptFilterDictionary.java new file mode 100644 index 000000000..4054434d3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDCryptFilterDictionary.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This class is a specialized view of the crypt filter dictionary of a PDF document. + * It contains a low level dictionary (COSDictionary) and provides the methods to + * manage its fields. + * + * + * @version $Revision: 1.0 $ + */ +public class PDCryptFilterDictionary +{ + + /** + * COS crypt filter dictionary. + */ + protected COSDictionary cryptFilterDictionary = null; + + /** + * creates a new empty crypt filter dictionary. + */ + public PDCryptFilterDictionary() + { + cryptFilterDictionary = new COSDictionary(); + } + + /** + * creates a new crypt filter dictionary from the low level dictionary provided. + * @param d the low level dictionary that will be managed by the newly created object + */ + public PDCryptFilterDictionary(COSDictionary d) + { + cryptFilterDictionary = d; + } + + /** + * This will get the dictionary associated with this crypt filter dictionary. + * + * @return The COS dictionary that this object wraps. + */ + public COSDictionary getCOSDictionary() + { + return cryptFilterDictionary; + } + + /** + * This will set the number of bits to use for the crypt filter algorithm. + * + * @param length The new key length. + */ + public void setLength(int length) + { + cryptFilterDictionary.setInt(COSName.LENGTH, length); + } + + /** + * This will return the Length entry of the crypt filter dictionary.

      + * The length in bits for the crypt filter algorithm. This will return a multiple of 8. + * + * @return The length in bits for the encryption algorithm + */ + public int getLength() + { + return cryptFilterDictionary.getInt( COSName.LENGTH, 40 ); + } + + /** + * This will set the crypt filter method. + * Allowed values are: NONE, V2, AESV2 + * + * @param cfm name of the crypt filter method. + * + * @throws IOException If there is an error setting the data. + */ + public void setCryptFilterMethod(COSName cfm) throws IOException + { + cryptFilterDictionary.setItem( COSName.CFM, cfm ); + } + + /** + * This will return the crypt filter method. + * Allowed values are: NONE, V2, AESV2 + * + * @return the name of the crypt filter method. + * + * @throws IOException If there is an error accessing the data. + */ + public COSName getCryptFilterMethod() throws IOException + { + return (COSName)cryptFilterDictionary.getDictionaryObject( COSName.CFM ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java new file mode 100644 index 000000000..3ab9b0a10 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDEncryptionDictionary.java @@ -0,0 +1,433 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +/** + * This class is a specialized view of the encryption dictionary of a PDF document. + * It contains a low level dictionary (COSDictionary) and provides the methods to + * manage its fields. + * + * The available fields are the ones who are involved by standard security handler + * and public key security handler. + * + * @author Ben Litchfield + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.7 $ + */ +public 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; + + /** + * COS encryption dictionary. + */ + protected COSDictionary encryptionDictionary = null; + + /** + * creates a new empty encryption dictionary. + */ + public PDEncryptionDictionary() + { + encryptionDictionary = new COSDictionary(); + } + + /** + * creates a new encryption dictionary from the low level dictionary provided. + * @param d the low level dictionary that will be managed by the newly created object + */ + public PDEncryptionDictionary(COSDictionary d) + { + encryptionDictionary = d; + } + + /** + * This will get the dictionary associated with this encryption dictionary. + * + * @return The COS dictionary that this object wraps. + */ + public COSDictionary getCOSDictionary() + { + return encryptionDictionary; + } + + /** + * Sets the filter entry of the encryption dictionary. + * + * @param filter The filter name. + */ + public void setFilter(String filter) + { + encryptionDictionary.setItem( COSName.FILTER, COSName.getPDFName( filter ) ); + } + + /** + * Get the name of the filter. + * + * @return The filter name contained in this encryption dictionary. + */ + public String getFilter() + { + return encryptionDictionary.getNameAsString( COSName.FILTER ); + } + + /** + * Get the name of the subfilter. + * + * @return The subfilter name contained in this encryption dictionary. + */ + public String getSubFilter() + { + return encryptionDictionary.getNameAsString( COSName.SUB_FILTER ); + } + + /** + * Set the subfilter entry of the encryption dictionary. + * + * @param subfilter The value of the subfilter field. + */ + public void setSubFilter(String subfilter) + { + encryptionDictionary.setName( COSName.SUB_FILTER, subfilter ); + } + + /** + * This will set the V entry of the encryption dictionary.

      + * See PDF Reference 1.4 Table 3.13.

      + * Note: This value is used to decrypt the pdf document. If you change this when + * the document is encrypted then decryption will fail!. + * + * @param version The new encryption version. + */ + public void setVersion(int version) + { + encryptionDictionary.setInt( COSName.V, version ); + } + + /** + * This will return the V entry of the encryption dictionary.

      + * See PDF Reference 1.4 Table 3.13. + * + * @return The encryption version to use. + */ + public int getVersion() + { + return encryptionDictionary.getInt( COSName.V, 0 ); + } + + /** + * 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.setInt(COSName.LENGTH, length); + } + + /** + * This will return the Length entry of the encryption dictionary.

      + * The length in bits for the encryption algorithm. This will return a multiple of 8. + * + * @return The length in bits for the encryption algorithm + */ + public int getLength() + { + return encryptionDictionary.getInt( COSName.LENGTH, 40 ); + } + + /** + * This will set the R entry of the encryption dictionary.

      + * See PDF Reference 1.4 Table 3.14.

      + * + * Note: This value is used to decrypt the pdf document. If you change this when + * the document is encrypted then decryption will fail!. + * + * @param revision The new encryption version. + */ + public void setRevision(int revision) + { + encryptionDictionary.setInt( COSName.R, revision ); + } + + /** + * This will return the R entry of the encryption dictionary.

      + * See PDF Reference 1.4 Table 3.14. + * + * @return The encryption revision to use. + */ + public int getRevision() + { + return encryptionDictionary.getInt( COSName.R, DEFAULT_VERSION ); + } + + /** + * 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.O, owner ); + } + + /** + * This will get the O entry in the standard encryption dictionary. + * + * @return A 32 byte array or null if there is no owner key. + * + * @throws IOException If there is an error accessing the data. + */ + public byte[] getOwnerKey() throws IOException + { + byte[] o = null; + COSString owner = (COSString)encryptionDictionary.getDictionaryObject( COSName.O ); + if( owner != null ) + { + o = owner.getBytes(); + } + return o; + } + + /** + * 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.U, user ); + } + + /** + * This will get the U entry in the standard encryption dictionary. + * + * @return A 32 byte array or null if there is no user key. + * + * @throws IOException If there is an error accessing the data. + */ + public byte[] getUserKey() throws IOException + { + byte[] u = null; + COSString user = (COSString)encryptionDictionary.getDictionaryObject( COSName.U ); + if( user != null ) + { + u = user.getBytes(); + } + return u; + } + + /** + * This will set the permissions bit mask. + * + * @param permissions The new permissions bit mask + */ + public void setPermissions(int permissions) + { + encryptionDictionary.setInt( COSName.P, permissions ); + } + + /** + * This will get the permissions bit mask. + * + * @return The permissions bit mask. + */ + public int getPermissions() + { + return encryptionDictionary.getInt( COSName.P, 0 ); + } + + /** + * Will get the EncryptMetaData dictionary info. + * + * @return true if EncryptMetaData is explicitly set to false (the default is true) + */ + public boolean isEncryptMetaData() + { + // default is true (see 7.6.3.2 Standard Encryption Dictionary PDF 32000-1:2008) + boolean encryptMetaData = true; + + COSBase value = encryptionDictionary.getDictionaryObject(COSName.ENCRYPT_META_DATA); + + if (value instanceof COSBoolean) { + encryptMetaData = ((COSBoolean)value).getValue(); + } + + return encryptMetaData; + } + + /** + * This will set the Recipients field of the dictionary. This field contains an array + * of string. + * @param recipients the array of bytes arrays to put in the Recipients field. + * @throws IOException If there is an error setting the data. + */ + public void setRecipients(byte[][] recipients) throws IOException + { + COSArray array = new COSArray(); + for(int i=0; iBen Litchfield + * @version $Revision: 1.7 $ + * @deprecated Made deprecated by the new security layer of PDFBox. Use SecurityHandlers instead. + */ + +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; + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDStandardEncryption.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDStandardEncryption.java new file mode 100644 index 000000000..0bce59967 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PDStandardEncryption.java @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.encryption; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.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 + * @version $Revision: 1.7 $ + * @deprecated Made deprecated by the new security layer of PDFBox. Use SecurityHandlers instead. + */ +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.

      + * 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.

      + * See PDF Reference 1.4 Table 3.14.

      + * + * Note: This value is used to decrypt the pdf document. If you change this when + * the document is encrypted then decryption will fail!. + * + * @param revision The new encryption version. + */ + public void setRevision( int revision ) + { + encryptionDictionary.setInt( COSName.getPDFName( "R" ), 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.setInt( COSName.getPDFName( "P" ), 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/ProtectionPolicy.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/ProtectionPolicy.java new file mode 100644 index 000000000..813f53e60 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/ProtectionPolicy.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +/** + * This class represents the protection policy to apply to a document. + * + * Objects implementing this abstract class can be passed to the protect method of PDDocument + * to protect a document. + * + * @see org.apache.pdfbox.pdmodel.PDDocument#protect(ProtectionPolicy) + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * @version $Revision: 1.3 $ + */ +public abstract class ProtectionPolicy +{ + + private static final int DEFAULT_KEY_LENGTH = 40; + + private int encryptionKeyLength = DEFAULT_KEY_LENGTH; + + /** + * set the length in (bits) of the secret key that will be + * used to encrypt document data. + * The default value is 40 bits, which provides a low security level + * but is compatible with old versions of Acrobat Reader. + * + * @param l the length in bits (must be 40 or 128) + */ + public void setEncryptionKeyLength(int l) + { + if(l!=40 && l!=128) + { + throw new RuntimeException("Invalid key length '" + l + "' value must be 40 or 128!"); + } + encryptionKeyLength = l; + } + + /** + * Get the length of the secrete key that will be used to encrypt + * document data. + * + * @return The length (in bits) of the encryption key. + */ + public int getEncryptionKeyLength() + { + return encryptionKeyLength; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyDecryptionMaterial.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyDecryptionMaterial.java new file mode 100644 index 000000000..b254ab79d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyDecryptionMaterial.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; + +/** + * This class holds necessary information to decrypt a PDF document + * protected by the public key security handler. + * + * To decrypt such a document, we need: + *
        + *
      • a valid X509 certificate which correspond to one of the recipient of the document
      • + *
      • the private key corresponding to this certificate + *
      • the password to decrypt the private key if necessary
      • + *
      + * + * Objects of this class can be used with the openProtection method of PDDocument. + * + * The following example shows how to decrypt a document using a PKCS#12 certificate + * (typically files with a pfx extension). + * + *
      + * PDDocument doc = PDDocument.load(document_path);
      + * KeyStore ks = KeyStore.getInstance("PKCS12");
      + * ks.load(new FileInputStream(certificate_path), password.toCharArray());
      + * PublicKeyDecryptionMaterial dm = new PublicKeyDecryptionMaterial(ks, null, password);
      + * doc.openProtection(dm);
      + * 
      + * + * In this code sample certificate_path contains the path to the PKCS#12 certificate. + * + * @see org.apache.pdfbox.pdmodel.PDDocument#openProtection(DecryptionMaterial) + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * @version $Revision: 1.2 $ + */ + +public class PublicKeyDecryptionMaterial extends DecryptionMaterial +{ + private String password = null; + private KeyStore keyStore = null; + private String alias = null; + + /** + * Create a new public key decryption material. + * + * @param keystore The keystore were the private key and the certificate are + * @param a The alias of the private key and the certificate. + * If the keystore contains only 1 entry, this parameter can be left null. + * @param pwd The password to extract the private key from the keystore. + */ + + public PublicKeyDecryptionMaterial(KeyStore keystore, String a, String pwd) + { + keyStore = keystore; + alias = a; + password = pwd; + } + + + /** + * Returns the certificate contained in the keystore. + * + * @return The certificate that will be used to try to open the document. + * + * @throws KeyStoreException If there is an error accessing the certificate. + */ + + public X509Certificate getCertificate() throws KeyStoreException + { + if(keyStore.size() == 1) + { + Enumeration aliases = keyStore.aliases(); + String keyStoreAlias = (String)aliases.nextElement(); + return (X509Certificate)keyStore.getCertificate(keyStoreAlias); + } + else + { + if(keyStore.containsAlias(alias)) + { + return (X509Certificate)keyStore.getCertificate(alias); + } + throw new KeyStoreException("the keystore does not contain the given alias"); + } + } + + /** + * Returns the password given by the user and that will be used + * to open the private key. + * + * @return The password. + */ + public String getPassword() + { + return password; + } + + /** + * returns The private key that will be used to open the document protection. + * @return The private key. + * @throws KeyStoreException If there is an error accessing the key. + */ + public Key getPrivateKey() throws KeyStoreException + { + try + { + if(keyStore.size() == 1) + { + Enumeration aliases = keyStore.aliases(); + String keyStoreAlias = (String)aliases.nextElement(); + return keyStore.getKey(keyStoreAlias, password.toCharArray()); + } + else + { + if(keyStore.containsAlias(alias)) + { + return keyStore.getKey(alias, password.toCharArray()); + } + throw new KeyStoreException("the keystore does not contain the given alias"); + } + } + catch(UnrecoverableKeyException ex) + { + throw new KeyStoreException("the private key is not recoverable"); + } + catch(NoSuchAlgorithmException ex) + { + throw new KeyStoreException("the algorithm necessary to recover the key is not available"); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyProtectionPolicy.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyProtectionPolicy.java new file mode 100644 index 000000000..3be8481eb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyProtectionPolicy.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * This class represents the protection policy to use to protect + * a document with the public key security handler as described + * in the PDF specification 1.6 p104. + * + * PDF documents are encrypted so that they can be decrypted by + * one or more recipients. Each recipient have its own access permission. + * + * The following code sample shows how to protect a document using + * the public key security handler. In this code sample, doc is + * a PDDocument object. + * + *
      + * PublicKeyProtectionPolicy policy = new PublicKeyProtectionPolicy();
      + * PublicKeyRecipient recip = new PublicKeyRecipient();
      + * AccessPermission ap = new AccessPermission();
      + * ap.setCanModify(false);
      + * recip.setPermission(ap);
      + *
      + * // load the recipient's certificate
      + * InputStream inStream = new FileInputStream(certificate_path);
      + * CertificateFactory cf = CertificateFactory.getInstance("X.509");
      + * X509Certificate certificate = (X509Certificate)cf.generateCertificate(inStream);
      + * inStream.close();
      + *
      + * recip.setX509(certificate); // set the recipient's certificate
      + * policy.addRecipient(recip);
      + * policy.setEncryptionKeyLength(128); // the document will be encrypted with 128 bits secret key
      + * doc.protect(policy);
      + * doc.save(out);
      + * 
      + * + * + * @see org.apache.pdfbox.pdmodel.PDDocument#protect(ProtectionPolicy) + * @see AccessPermission + * @see PublicKeyRecipient + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.2 $ + */ +public class PublicKeyProtectionPolicy extends ProtectionPolicy +{ + + /** + * The list of recipients. + */ + private ArrayList recipients = null; + + /** + * The X509 certificate used to decrypt the current document. + */ + private X509Certificate decryptionCertificate; + + /** + * Constructor for encryption. Just creates an empty recipients list. + */ + public PublicKeyProtectionPolicy() + { + recipients = new ArrayList(); + } + + /** + * Adds a new recipient to the recipients list. + * + * @param r A new recipient. + */ + public void addRecipient(PublicKeyRecipient r) + { + recipients.add(r); + } + + /** + * Removes a recipient from the recipients list. + * + * @param r The recipient to remove. + * + * @return true If a recipient was found and removed. + */ + public boolean removeRecipient(PublicKeyRecipient r) + { + return recipients.remove(r); + } + + /** + * Returns an iterator to browse the list of recipients. Object + * found in this iterator are PublicKeyRecipient. + * + * @return The recipients list iterator. + */ + public Iterator getRecipientsIterator() + { + return recipients.iterator(); + } + + /** + * Getter of the property decryptionCertificate. + * + * @return Returns the decryptionCertificate. + */ + public X509Certificate getDecryptionCertificate() + { + return decryptionCertificate; + } + + /** + * Setter of the property decryptionCertificate. + * + * @param aDecryptionCertificate The decryption certificate to set. + */ + public void setDecryptionCertificate(X509Certificate aDecryptionCertificate) + { + this.decryptionCertificate = aDecryptionCertificate; + } + + /** + * Returns the number of recipients. + * + * @return The number of recipients. + */ + public int getRecipientsNumber() + { + return recipients.size(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyRecipient.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyRecipient.java new file mode 100644 index 000000000..893419a54 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeyRecipient.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.encryption; + +import java.security.cert.X509Certificate; + +/** + * Represents a recipient in the public key protection policy. + * + * @see PublicKeyProtectionPolicy + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.2 $ + */ +public class PublicKeyRecipient +{ + private X509Certificate x509; + + private AccessPermission permission; + + /** + * Returns the X509 certificate of the recipient. + * + * @return The X509 certificate + */ + public X509Certificate getX509() + { + return x509; + } + + /** + * Set the X509 certificate of the recipient. + * + * @param aX509 The X509 certificate + */ + public void setX509(X509Certificate aX509) + { + this.x509 = aX509; + } + + /** + * Returns the access permission granted to the recipient. + * + * @return The access permission object. + */ + public AccessPermission getPermission() + { + return permission; + } + + /** + * Set the access permission granted to the recipient. + * + * @param permissions The permission to set. + */ + public void setPermission(AccessPermission permissions) + { + this.permission = permissions; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java new file mode 100644 index 000000000..975c2637c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/PublicKeySecurityHandler.java @@ -0,0 +1,407 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.AlgorithmParameterGenerator; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Iterator; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DEROutputStream; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.asn1.cms.EncryptedContentInfo; +import org.bouncycastle.asn1.cms.EnvelopedData; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.cms.KeyTransRecipientInfo; +import org.bouncycastle.asn1.cms.RecipientIdentifier; +import org.bouncycastle.asn1.cms.RecipientInfo; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.TBSCertificateStructure; +import org.bouncycastle.cms.CMSEnvelopedData; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.RecipientInformation; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * This class implements the public key security handler + * described in the PDF specification. + * + * @see PDF Spec 1.6 p104 + * + * @see PublicKeyProtectionPolicy to see how to protect document with this security handler. + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * @version $Revision: 1.3 $ + */ +public class PublicKeySecurityHandler extends SecurityHandler +{ + + /** + * The filter name. + */ + public static final String FILTER = "Adobe.PubSec"; + + private static final String SUBFILTER = "adbe.pkcs7.s4"; + + private PublicKeyProtectionPolicy policy = null; + + /** + * Constructor. + */ + public PublicKeySecurityHandler() + { + } + + /** + * Constructor used for encryption. + * + * @param p The protection policy. + */ + public PublicKeySecurityHandler(PublicKeyProtectionPolicy p) + { + policy = p; + this.keyLength = policy.getEncryptionKeyLength(); + } + + /** + * Decrypt the document. + * + * @param doc The document to decrypt. + * @param decryptionMaterial The data used to decrypt the document. + * + * @throws CryptographyException If there is an error during decryption. + * @throws IOException If there is an error accessing data. + */ + public void decryptDocument(PDDocument doc, DecryptionMaterial decryptionMaterial) + throws CryptographyException, IOException + { + this.document = doc; + + PDEncryptionDictionary dictionary = doc.getEncryptionDictionary(); + + if(dictionary.getLength() != 0) + { + this.keyLength = dictionary.getLength(); + } + + if(!(decryptionMaterial instanceof PublicKeyDecryptionMaterial)) + { + throw new CryptographyException( + "Provided decryption material is not compatible with the document"); + } + + PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial)decryptionMaterial; + + try + { + boolean foundRecipient = false; + + // the decrypted content of the enveloped data that match + // the certificate in the decryption material provided + byte[] envelopedData = null; + + // the bytes of each recipient in the recipients array + byte[][] recipientFieldsBytes = new byte[dictionary.getRecipientsLength()][]; + + int recipientFieldsLength = 0; + + for(int i=0; i>> 8); + byte three = (byte)(permission >>> 16); + byte four = (byte)(permission >>> 24); + + System.arraycopy(seed, 0, pkcs7input, 0, 20); // put this seed in the pkcs7 input + + pkcs7input[20] = four; + pkcs7input[21] = three; + pkcs7input[22] = two; + pkcs7input[23] = one; + + DERObject obj = createDERForRecipient(pkcs7input, certificate); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + DEROutputStream k = new DEROutputStream(baos); + + k.writeObject(obj); + + recipientsField[i] = baos.toByteArray(); + + i++; + } + + dictionary.setRecipients(recipientsField); + + int sha1InputLength = seed.length; + + for(int j=0; jBen Litchfield + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.4 $ + */ + +public abstract class SecurityHandler +{ + + /* ------------------------------------------------ + * CONSTANTS + -------------------------------------------------- */ + + private static final int DEFAULT_KEY_LENGTH = 40; + + /* + * See 7.6.2, page 58, PDF 32000-1:2008 + */ + private final static byte[] AES_SALT = {(byte) 0x73, (byte) 0x41, (byte) 0x6c, (byte) 0x54}; + + /** + * The value of V field of the Encryption dictionary. + */ + protected int version; + + /** + * The length of the secret key used to encrypt the document. + */ + protected int keyLength = DEFAULT_KEY_LENGTH; + + /** + * The encryption key that will used to encrypt / decrypt. + */ + protected byte[] encryptionKey; + + /** + * The document whose security is handled by this security handler. + */ + + protected PDDocument document; + + /** + * The RC4 implementation used for cryptographic functions. + */ + protected ARCFour rc4 = new ARCFour(); + + private Set objects = new HashSet(); + + private Set potentialSignatures = new HashSet(); + + /* + * If true, AES will be used + */ + private boolean aes; + + /** + * The access permission granted to the current user for the document. These + * permissions are computed during decryption and are in read only mode. + */ + + protected AccessPermission currentAccessPermission = null; + + /** + * Prepare the document for encryption. + * + * @param doc The document that will be encrypted. + * + * @throws CryptographyException If there is an error while preparing. + * @throws IOException If there is an error with the document. + */ + public abstract void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException; + + /** + * Prepare the document for decryption. + * + * @param doc The document to decrypt. + * @param mat Information required to decrypt the document. + * @throws CryptographyException If there is an error while preparing. + * @throws IOException If there is an error with the document. + */ + public abstract void decryptDocument(PDDocument doc, DecryptionMaterial mat) + throws CryptographyException, IOException; + + + /** + * This method must be called by an implementation of this class to really proceed + * to decryption. + * + * @throws IOException If there is an error in the decryption. + * @throws CryptographyException If there is an error in the decryption. + */ + protected void proceedDecryption() throws IOException, CryptographyException + { + + COSDictionary trailer = document.getDocument().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 allObjects = document.getDocument().getObjects(); + Iterator objectIter = allObjects.iterator(); + while( objectIter.hasNext() ) + { + decryptObject( objectIter.next() ); + } + document.setEncryptionDictionary( null ); + } + + private void addDictionaryAndSubDictionary( Set set, COSDictionary dic ) + { + set.add( dic ); + COSArray kids = (COSArray)dic.getDictionaryObject( COSName.KIDS ); + for( int i=0; kids != null && i> 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" ); + md.update(newKey); + if (aes) { + md.update(AES_SALT); + } + digestedKey = md.digest(); + } + 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 ); + + if (aes) + { + byte[] iv = new byte[16]; + + data.read(iv); + + try { + Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + SecretKey aesKey = new SecretKeySpec(finalKey, "AES"); + + IvParameterSpec ips = new IvParameterSpec(iv); + + decryptCipher.init(decrypt ? Cipher.DECRYPT_MODE : Cipher.ENCRYPT_MODE, aesKey, ips); + + CipherInputStream cipherStream = new CipherInputStream(data, decryptCipher); + + try { + byte buffer[] = new byte[4096]; + long count = 0L; + for(int n = 0; -1 != (n = cipherStream.read(buffer));) + { + output.write(buffer, 0, n); + count += n; + } + } + finally { + cipherStream.close(); + } + } + catch (InvalidKeyException e) { + throw new WrappedIOException(e); + } + catch (InvalidAlgorithmParameterException e) { + throw new WrappedIOException(e); + } + catch (NoSuchAlgorithmException e) { + throw new WrappedIOException(e); + } + catch (NoSuchPaddingException e) { + throw new WrappedIOException(e); + } + } + else { + rc4.setKey( finalKey ); + rc4.write( data, output ); + } + + output.flush(); + } + + /** + * 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( COSBase 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(); + encryptData( objNum, + genNum, + encryptedStream, + stream.createFilteredStream(), + true /* decrypt */); + } + + /** + * This will encrypt a stream, but not the dictionary as the dictionary is + * encrypted by visitFromString() in COSWriter and we don't want to encrypt + * it twice. + * + * @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 encryptStream( COSStream stream, long objNum, long genNum ) + throws CryptographyException, IOException + { + InputStream encryptedStream = stream.getFilteredStream(); + encryptData( objNum, + genNum, + encryptedStream, + stream.createFilteredStream(), + false /* encrypt */); + } + + /** + * 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 + { + for( Map.Entry entry : dictionary.entrySet() ) + { + //if we are a signature dictionary and contain a Contents entry then + //we don't decrypt it. + if( !(entry.getKey().getName().equals( "Contents" ) && + entry.getValue() instanceof COSString && + potentialSignatures.contains( dictionary ))) + { + decrypt( entry.getValue(), 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(); + encryptData( objNum, genNum, data, buffer, true /* decrypt */ ); + 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; ikeyLength. + * @return Returns the keyLength. + * @uml.property name="keyLength" + */ + public int getKeyLength() + { + return keyLength; + } + + /** + * Setter of the property keyLength. + * + * @param keyLen The keyLength to set. + */ + public void setKeyLength(int keyLen) + { + this.keyLength = keyLen; + } + + /** + * Returns the access permissions that were computed during document decryption. + * The returned object is in read only mode. + * + * @return the access permissions or null if the document was not decrypted. + */ + public AccessPermission getCurrentAccessPermission() + { + return currentAccessPermission; + } + + /* + * True if AES is used for encryption and decryption. + */ + public boolean isAES() { + return aes; + } + + /* + * Set to true if AES for encryption and decryption should be used. + */ + public void setAES(boolean aes) { + this.aes = aes; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/SecurityHandlersManager.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/SecurityHandlersManager.java new file mode 100644 index 000000000..abec7f9c6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/SecurityHandlersManager.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + +import java.lang.reflect.Constructor; +import java.security.Security; +import java.util.Hashtable; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * This class manages security handlers for the application. It follows the singleton pattern. + * To be usable, security managers must be registered in it. Security managers are retrieved by + * the application when necessary. + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.3 $ + * + */ +public class SecurityHandlersManager +{ + + /** + * The unique instance of this manager. + */ + private static SecurityHandlersManager instance; + + /** + * hashtable used to index handlers regarding their name. + * Basically this will be used when opening an encrypted + * document to find the appropriate security handler to handle + * security features of the document. + */ + private Hashtable handlerNames = null; + + /** + * Hashtable used to index handlers regarding the class of + * protection policy they use. Basically this will be used when + * encrypting a document. + */ + private Hashtable handlerPolicyClasses = null; + + /** + * private constructor. + */ + private SecurityHandlersManager() + { + handlerNames = new Hashtable(); + handlerPolicyClasses = new Hashtable(); + try + { + this.registerHandler( + StandardSecurityHandler.FILTER, + StandardSecurityHandler.class, + StandardProtectionPolicy.class); + this.registerHandler( + PublicKeySecurityHandler.FILTER, + PublicKeySecurityHandler.class, + PublicKeyProtectionPolicy.class); + } + catch(Exception e) + { + System.err.println("SecurityHandlersManager strange error with builtin handlers: " + e.getMessage()); + System.exit(1); + } + } + + /** + * register a security handler. + * + * If the security handler was already registered an exception is thrown. + * If another handler was previously registered for the same filter name or + * for the same policy name, an exception is thrown + * + * @param filterName The name of the filter. + * @param securityHandlerClass Security Handler class to register. + * @param protectionPolicyClass Protection Policy class to register. + * + * @throws BadSecurityHandlerException If there is an error registering the security handler. + */ + public void registerHandler(String filterName, Class securityHandlerClass, Class protectionPolicyClass) + throws BadSecurityHandlerException + { + if(handlerNames.contains(securityHandlerClass) || handlerPolicyClasses.contains(securityHandlerClass)) + { + throw new BadSecurityHandlerException("the following security handler was already registered: " + + securityHandlerClass.getName()); + } + + if(SecurityHandler.class.isAssignableFrom(securityHandlerClass)) + { + try + { + if(handlerNames.containsKey(filterName)) + { + throw new BadSecurityHandlerException("a security handler was already registered " + + "for the filter name " + filterName); + } + if(handlerPolicyClasses.containsKey(protectionPolicyClass)) + { + throw new BadSecurityHandlerException("a security handler was already registered " + + "for the policy class " + protectionPolicyClass.getName()); + } + + handlerNames.put(filterName, securityHandlerClass); + handlerPolicyClasses.put(protectionPolicyClass, securityHandlerClass); + } + catch(Exception e) + { + throw new BadSecurityHandlerException(e); + } + } + else + { + throw new BadSecurityHandlerException("The class is not a super class of SecurityHandler"); + } + } + + + /** + * Get the singleton instance. + * + * @return The SecurityHandlersManager. + */ + public static SecurityHandlersManager getInstance() + { + if(instance == null) + { + instance = new SecurityHandlersManager(); + } + Security.addProvider(new BouncyCastleProvider()); + + return instance; + } + + /** + * Get the security handler for the protection policy. + * + * @param policy The policy to get the security handler for. + * + * @return The appropriate security handler. + * + * @throws BadSecurityHandlerException If it is unable to create a SecurityHandler. + */ + public SecurityHandler getSecurityHandler(ProtectionPolicy policy) throws BadSecurityHandlerException + { + + Object found = handlerPolicyClasses.get(policy.getClass()); + if(found == null) + { + throw new BadSecurityHandlerException( + "Cannot find an appropriate security handler for " + policy.getClass().getName()); + } + Class handlerclass = (Class) found; + Class[] argsClasses = {policy.getClass()}; + Object[] args = {policy}; + try + { + Constructor c = handlerclass.getDeclaredConstructor(argsClasses); + SecurityHandler handler = (SecurityHandler)c.newInstance(args); + return handler; + } + catch(Exception e) + { + e.printStackTrace(); + throw new BadSecurityHandlerException( + "problem while trying to instanciate the security handler "+ + handlerclass.getName() + ": " + e.getMessage()); + } + } + + + + /** + * Retrieve the appropriate SecurityHandler for a the given filter name. + * The filter name is an entry of the encryption dictionary of an encrypted document. + * + * @param filterName The filter name. + * + * @return The appropriate SecurityHandler if it exists. + * + * @throws BadSecurityHandlerException If the security handler does not exist. + */ + public SecurityHandler getSecurityHandler(String filterName) throws BadSecurityHandlerException + { + Object found = handlerNames.get(filterName); + if(found == null) + { + throw new BadSecurityHandlerException("Cannot find an appropriate security handler for " + filterName); + } + Class handlerclass = (Class) found; + Class[] argsClasses = {}; + Object[] args = {}; + try + { + Constructor c = handlerclass.getDeclaredConstructor(argsClasses); + SecurityHandler handler = (SecurityHandler)c.newInstance(args); + return handler; + } + catch(Exception e) + { + e.printStackTrace(); + throw new BadSecurityHandlerException( + "problem while trying to instanciate the security handler "+ + handlerclass.getName() + ": " + e.getMessage()); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardDecryptionMaterial.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardDecryptionMaterial.java new file mode 100644 index 000000000..5d082f19e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardDecryptionMaterial.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.encryption; + + +/** + * + * Represents the necessary information to decrypt a document protected by + * the standard security handler (password protection). + * + * This is only composed of a password. + * + * The following example shows how to decrypt a document protected with + * the standard security handler: + * + *
      + *  PDDocument doc = PDDocument.load(in);
      + *  StandardDecryptionMaterial dm = new StandardDecryptionMaterial("password");
      + *  doc.openProtection(dm);
      + *  
      + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.2 $ + */ + +public class StandardDecryptionMaterial extends DecryptionMaterial +{ + + private String password = null; + + /** + * Create a new standard decryption material with the given password. + * + * @param pwd The password. + */ + public StandardDecryptionMaterial(String pwd) + { + password = pwd; + } + + /** + * Returns the password. + * + * @return The password used to decrypt the document. + */ + public String getPassword() + { + return password; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardProtectionPolicy.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardProtectionPolicy.java new file mode 100644 index 000000000..a63b39f7a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardProtectionPolicy.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.encryption; + +/** + * This class represents the protection policy to add to a document + * for password-based protection. + * + * The following example shows how to protect a PDF document with password. + * In this example, the document will be protected so that someone opening + * the document with the user password user_pwd will not be + * able to modify the document. + * + *
      + * AccessPermission ap = new AccessPermission();
      + * ap.setCanModify(false);
      + * StandardProtectionPolicy policy = new StandardProtectionPolicy(owner_pwd, user_pwd, ap);
      + * doc.protect(policy);
      + * 
      + * + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * @version $Revision: 1.3 $ + */ +public class StandardProtectionPolicy extends ProtectionPolicy +{ + + private AccessPermission permissions; + + private String ownerPassword = ""; + + private String userPassword = ""; + + + /** + * Creates an new instance of the standard protection policy + * in order to protect a PDF document with passwords. + * + * @param ownerPass The owner's password. + * @param userPass The users's password. + * @param perms The access permissions given to the user. + */ + public StandardProtectionPolicy(String ownerPass, String userPass, AccessPermission perms) + { + this.permissions = perms; + this.userPassword = userPass; + this.ownerPassword = ownerPass; + } + + /** + * Getter of the property permissions. + * + * @return Returns the permissions. + */ + public AccessPermission getPermissions() + { + return permissions; + } + + /** + * Setter of the property permissions. + * + * @param perms The permissions to set. + */ + public void setPermissions(AccessPermission perms) + { + this.permissions = perms; + } + + /** + * Getter of the property ownerPassword. + * + * @return Returns the ownerPassword. + */ + public String getOwnerPassword() + { + return ownerPassword; + } + + /** + * Setter of the property ownerPassword. + * + * @param ownerPass The ownerPassword to set. + */ + public void setOwnerPassword(String ownerPass) + { + this.ownerPassword = ownerPass; + } + + /** + * Getter of the property userPassword. + * + * @return Returns the userPassword. + */ + public String getUserPassword() + { + return userPassword; + } + + /** + * Setter of the property userPassword. + * + * @param userPass The userPassword to set. + */ + public void setUserPassword(String userPass) + { + this.userPassword = userPass; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java new file mode 100644 index 000000000..70bd0ceca --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/encryption/StandardSecurityHandler.java @@ -0,0 +1,900 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.encryption; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.encryption.ARCFour; +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * + * The class implements the standard security handler as decribed + * in the PDF specifications. This security handler protects document + * with password. + * + * @see StandardProtectionPolicy to see how to protect document with this security handler. + * + * @author Ben Litchfield + * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr) + * + * @version $Revision: 1.5 $ + */ + +public class StandardSecurityHandler extends SecurityHandler +{ + /** + * Type of security handler. + */ + public static final String FILTER = "Standard"; + + private static final int DEFAULT_VERSION = 1; + + private static final int DEFAULT_REVISION = 3; + + private int revision = DEFAULT_REVISION; + + private StandardProtectionPolicy policy; + + private ARCFour rc4 = new ARCFour(); + + /** + * Protection policy class for this handler. + */ + public static final Class PROTECTION_POLICY_CLASS = StandardProtectionPolicy.class; + + /** + * Standard padding for encryption. + */ + 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 + }; + + /** + * Constructor. + */ + public StandardSecurityHandler() + { + } + + /** + * Constructor used for encryption. + * + * @param p The protection policy. + */ + public StandardSecurityHandler(StandardProtectionPolicy p) + { + policy = p; + keyLength = policy.getEncryptionKeyLength(); + } + + + /** + * Computes the version number of the StandardSecurityHandler + * regarding the encryption key length. + * See PDF Spec 1.6 p 93 + * + * @return The computed cersion number. + */ + private int computeVersionNumber() + { + if(keyLength == 40) + { + return DEFAULT_VERSION; + } + return 2; + } + + /** + * Computes the revision version of the StandardSecurityHandler to + * use regarding the version number and the permissions bits set. + * See PDF Spec 1.6 p98 + * + * @return The computed revision number. + */ + private int computeRevisionNumber() + { + if(version == 2 + && !policy.getPermissions().canFillInForm() + && !policy.getPermissions().canExtractForAccessibility() + && !policy.getPermissions().canPrintDegraded() ) + { + return 2; + } + return 3; + } + + /** + * Decrypt the document. + * + * @param doc The document to be decrypted. + * @param decryptionMaterial Information used to decrypt the document. + * + * @throws IOException If there is an error accessing data. + * @throws CryptographyException If there is an error with decryption. + */ + public void decryptDocument(PDDocument doc, DecryptionMaterial decryptionMaterial) + throws CryptographyException, IOException + { + document = doc; + + PDEncryptionDictionary dictionary = document.getEncryptionDictionary(); + if(!(decryptionMaterial instanceof StandardDecryptionMaterial)) + { + throw new CryptographyException("Provided decryption material is not compatible with the document"); + } + + StandardDecryptionMaterial material = (StandardDecryptionMaterial)decryptionMaterial; + + String password = material.getPassword(); + if(password == null) + { + password = ""; + } + + int dicPermissions = dictionary.getPermissions(); + int dicRevision = dictionary.getRevision(); + int dicLength = dictionary.getLength()/8; + + //some documents may have not document id, see + //test\encryption\encrypted_doc_no_id.pdf + COSArray documentIDArray = document.getDocument().getDocumentID(); + byte[] documentIDBytes = null; + if( documentIDArray != null && documentIDArray.size() >= 1 ) + { + COSString id = (COSString)documentIDArray.getObject( 0 ); + documentIDBytes = id.getBytes(); + } + else + { + documentIDBytes = new byte[0]; + } + + // we need to know whether the meta data was encrypted for password calculation + boolean encryptMetadata = dictionary.isEncryptMetaData(); + + byte[] u = dictionary.getUserKey(); + byte[] o = dictionary.getOwnerKey(); + + boolean isUserPassword = + isUserPassword( + password.getBytes("ISO-8859-1"), + u, + o, + dicPermissions, + documentIDBytes, + dicRevision, + dicLength, + encryptMetadata); + boolean isOwnerPassword = + isOwnerPassword( + password.getBytes("ISO-8859-1"), + u, + o, + dicPermissions, + documentIDBytes, + dicRevision, + dicLength, + encryptMetadata); + + if( isUserPassword ) + { + currentAccessPermission = new AccessPermission( dicPermissions ); + encryptionKey = + computeEncryptedKey( + password.getBytes("ISO-8859-1"), + o, + dicPermissions, + documentIDBytes, + dicRevision, + dicLength, + encryptMetadata ); + } + else if( isOwnerPassword ) + { + currentAccessPermission = AccessPermission.getOwnerAccessPermission(); + byte[] computedUserPassword = getUserPassword(password.getBytes("ISO-8859-1"),o,dicRevision,dicLength ); + encryptionKey = + computeEncryptedKey( + computedUserPassword, + o, + dicPermissions, + documentIDBytes, + dicRevision, + dicLength, + encryptMetadata); + } + else + { + throw new CryptographyException( + "Error: The supplied password does not match either the owner or user password in the document." ); + } + + // detect whether AES encryption is used. This assumes that the encryption algo is + // stored in the PDCryptFilterDictionary + PDCryptFilterDictionary stdCryptFilterDictionary = doc.getEncryptionDictionary(). + getStdCryptFilterDictionary(); + + if (stdCryptFilterDictionary != null) + { + COSName cryptFilterMethod = stdCryptFilterDictionary.getCryptFilterMethod(); + + if (cryptFilterMethod != null) { + setAES("AESV2".equalsIgnoreCase(cryptFilterMethod.getName())); + } + } + + this.proceedDecryption(); + } + + /** + * Prepare document for encryption. + * + * @param doc The documeent to encrypt. + * + * @throws IOException If there is an error accessing data. + * @throws CryptographyException If there is an error with decryption. + */ + public void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException + { + document = doc; + PDEncryptionDictionary encryptionDictionary = document.getEncryptionDictionary(); + if(encryptionDictionary == null) + { + encryptionDictionary = new PDEncryptionDictionary(); + } + version = computeVersionNumber(); + revision = computeRevisionNumber(); + encryptionDictionary.setFilter(FILTER); + encryptionDictionary.setVersion(version); + encryptionDictionary.setRevision(revision); + encryptionDictionary.setLength(keyLength); + + String ownerPassword = policy.getOwnerPassword(); + String userPassword = policy.getUserPassword(); + if( ownerPassword == null ) + { + ownerPassword = ""; + } + if( userPassword == null ) + { + userPassword = ""; + } + + int permissionInt = policy.getPermissions().getPermissionBytes(); + + encryptionDictionary.setPermissions(permissionInt); + + int length = keyLength/8; + + COSArray idArray = document.getDocument().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("ISO-8859-1") ); + md.update( userPassword.getBytes("ISO-8859-1") ); + md.update( document.getDocument().toString().getBytes() ); + byte[] id = md.digest( this.toString().getBytes("ISO-8859-1") ); + COSString idString = new COSString(); + idString.append( id ); + idArray.add( idString ); + idArray.add( idString ); + document.getDocument().setDocumentID( idArray ); + } + catch( NoSuchAlgorithmException e ) + { + throw new CryptographyException( e ); + } + catch(IOException e ) + { + throw new CryptographyException( e ); + } + } + + COSString id = (COSString)idArray.getObject( 0 ); + + byte[] o = computeOwnerPassword( + ownerPassword.getBytes("ISO-8859-1"), + userPassword.getBytes("ISO-8859-1"), revision, length); + + byte[] u = computeUserPassword( + userPassword.getBytes("ISO-8859-1"), + o, permissionInt, id.getBytes(), revision, length, true); + + encryptionKey = computeEncryptedKey( + userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length, true); + + encryptionDictionary.setOwnerKey(o); + encryptionDictionary.setUserKey(u); + + document.setEncryptionDictionary( encryptionDictionary ); + document.getDocument().setEncryptionDictionary(encryptionDictionary.getCOSDictionary()); + + } + + /** + * Check for owner password. + * + * @param ownerPassword The owner password. + * @param u The u entry of the encryption dictionary. + * @param o The o entry of the encryption dictionary. + * @param permissions The set of permissions on the document. + * @param id The document id. + * @param encRevision The encryption algorithm revision. + * @param length The encryption key length. + * + * @return True If the ownerPassword param is the owner password. + * + * @throws CryptographyException If there is an error during encryption. + * @throws IOException If there is an error accessing data. + */ + public final boolean isOwnerPassword( + byte[] ownerPassword, + byte[] u, + byte[] o, + int permissions, + byte[] id, + int encRevision, + int length, + boolean encryptMetadata) + throws CryptographyException, IOException + { + byte[] userPassword = getUserPassword( ownerPassword, o, encRevision, length ); + return isUserPassword( userPassword, u, o, permissions, id, encRevision, length, encryptMetadata ); + } + + /** + * Get the user password based on the owner password. + * + * @param ownerPassword The plaintext owner password. + * @param o The o entry of the encryption dictionary. + * @param encRevision The encryption revision number. + * @param length The key length. + * + * @return The u entry of the encryption dictionary. + * + * @throws CryptographyException If there is an error generating the user password. + * @throws IOException If there is an error accessing data while generating the user password. + */ + public final byte[] getUserPassword( + byte[] ownerPassword, + byte[] o, + int encRevision, + 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( encRevision == 3 || encRevision == 4 ) + { + for( int i=0; i<50; i++ ) + { + md.reset(); + md.update( digest ); + digest = md.digest(); + } + } + if( encRevision == 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( encRevision == 2 ) + { + rc4.setKey( rc4Key ); + rc4.write( o, result ); + } + else if( encRevision == 3 || encRevision == 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 ); + } + } + + /** + * Compute the encryption key. + * + * @param password The password to compute the encrypted key. + * @param o The o entry of the encryption dictionary. + * @param permissions The permissions for the document. + * @param id The document id. + * @param encRevision The revision of the encryption algorithm. + * @param length The length of the encryption key. + * + * @return The encrypted key bytes. + * + * @throws CryptographyException If there is an error with encryption. + */ + public final byte[] computeEncryptedKey( + byte[] password, + byte[] o, + int permissions, + byte[] id, + int encRevision, + int length, + boolean encryptMetadata) + 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 ); + + //(Security handlers of revision 4 or greater) If document metadata is not being encrypted, + //pass 4 bytes with the value 0xFFFFFFFF to the MD5 hash function. + //see 7.6.3.3 Algorithm 2 Step f of PDF 32000-1:2008 + if( encRevision == 4 && !encryptMetadata) + { + md.update(new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}); + } + + byte[] digest = md.digest(); + + //step 6 + if( encRevision == 3 || encRevision == 4) + { + for( int i=0; i<50; i++ ) + { + md.reset(); + md.update( digest, 0, length ); + digest = md.digest(); + } + } + + //step 7 + if( encRevision == 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 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 encRevision 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 encRevision, + int length, + boolean encryptMetadata) + throws CryptographyException, IOException + { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + //STEP 1 + byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, encRevision, length, encryptMetadata ); + + if( encRevision == 2 ) + { + //STEP 2 + rc4.setKey( encryptionKey ); + rc4.write( ENCRYPT_PADDING, result ); + } + else if( encRevision == 3 || encRevision == 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(); + } + + /** + * Compute the owner entry in the encryption dictionary. + * + * @param ownerPassword The plaintext owner password. + * @param userPassword The plaintext user password. + * @param encRevision The revision number of the encryption algorithm. + * @param length The length of the encryption key. + * + * @return The o entry of the encryption dictionary. + * + * @throws CryptographyException If there is an error with encryption. + * @throws IOException If there is an error accessing data. + */ + public final byte[] computeOwnerPassword( + byte[] ownerPassword, + byte[] userPassword, + int encRevision, + 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( encRevision == 3 || encRevision == 4) + { + for( int i=0; i<50; i++ ) + { + md.reset(); + md.update( digest, 0, length ); + digest = md.digest(); + } + } + if( encRevision == 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( encRevision == 3 || encRevision == 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; + } + + /** + * Check if a plaintext password is the user password. + * + * @param password The plaintext password. + * @param u The u entry of the encryption dictionary. + * @param o The o entry of the encryption dictionary. + * @param permissions The permissions set in the the PDF. + * @param id The document id used for encryption. + * @param encRevision The revision of the encryption algorithm. + * @param length The length of the encryption key. + * + * @return true If the plaintext password is the user password. + * + * @throws CryptographyException If there is an error during encryption. + * @throws IOException If there is an error accessing data. + */ + public final boolean isUserPassword( + byte[] password, + byte[] u, + byte[] o, + int permissions, + byte[] id, + int encRevision, + int length, + boolean encryptMetadata) + throws CryptographyException, IOException + { + boolean matches = false; + //STEP 1 + byte[] computedValue = computeUserPassword( password, o, permissions, id, encRevision, length, + encryptMetadata ); + if( encRevision == 2 ) + { + //STEP 2 + matches = arraysEqual( u, computedValue ); + } + else if( encRevision == 3 || encRevision == 4 ) + { + //STEP 2 + matches = arraysEqual( u, computedValue, 16 ); + } + else + { + throw new IOException( "Unknown Encryption Revision " + encRevision ); + } + return matches; + } + + /** + * Check if a plaintext password is the user password. + * + * @param password The plaintext password. + * @param u The u entry of the encryption dictionary. + * @param o The o entry of the encryption dictionary. + * @param permissions The permissions set in the the PDF. + * @param id The document id used for encryption. + * @param encRevision The revision of the encryption algorithm. + * @param length The length of the encryption key. + * + * @return true If the plaintext password is the user password. + * + * @throws CryptographyException If there is an error during encryption. + * @throws IOException If there is an error accessing data. + */ + public final boolean isUserPassword( + String password, + byte[] u, + byte[] o, + int permissions, + byte[] id, + int encRevision, + int length, + boolean encryptMetadata ) + throws CryptographyException, IOException + { + return isUserPassword(password.getBytes("ISO-8859-1"), + u,o,permissions, id, encRevision, length, encryptMetadata); + } + + /** + * Check for owner password. + * + * @param password The owner password. + * @param u The u entry of the encryption dictionary. + * @param o The o entry of the encryption dictionary. + * @param permissions The set of permissions on the document. + * @param id The document id. + * @param encRevision The encryption algorithm revision. + * @param length The encryption key length. + * + * @return True If the ownerPassword param is the owner password. + * + * @throws CryptographyException If there is an error during encryption. + * @throws IOException If there is an error accessing data. + */ + public final boolean isOwnerPassword( + String password, + byte[] u, + byte[] o, + int permissions, + byte[] id, + int encRevision, + int length, + boolean encryptMetadata) + throws CryptographyException, IOException + { + return isOwnerPassword(password.getBytes("ISO-8859-1"), + u,o,permissions, id, encRevision, length, encryptMetadata); + } + + private static final boolean arraysEqual( byte[] first, byte[] second, int count ) + { + boolean equal = first.length >= count && second.length >= count; + for( int i=0; i + + + + + + +The encryption package will handle the PDF document security handlers and the functionality of pluggable security handlers. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotation.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotation.java new file mode 100644 index 000000000..72f344384 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotation.java @@ -0,0 +1,608 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.awt.Color; +import java.io.IOException; +import java.util.Calendar; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.util.BitFlagHelper; +import org.apache.pdfbox.util.DateConverter; +import org.w3c.dom.Element; + +/** + * This represents an FDF annotation that is part of the FDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public abstract class FDFAnnotation implements COSObjectable +{ + /** + * Annotation dictionary. + */ + protected COSDictionary annot; + + /** + * Default constructor. + */ + public FDFAnnotation() + { + annot = new COSDictionary(); + annot.setName( "Type", "Annot" ); + } + + /** + * Constructor. + * + * @param a The FDF annotation. + */ + public FDFAnnotation( COSDictionary a ) + { + annot = a; + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting data from the element. + */ + public FDFAnnotation( Element element ) throws IOException + { + this(); + + String page = element.getAttribute( "page" ); + if( page != null ) + { + setPage( Integer.parseInt( page ) ); + } + + String color = element.getAttribute( "color" ); + if( color != null ) + { + if( color.length() == 7 && color.charAt( 0 ) == '#' ) + { + int colorValue = Integer.parseInt(color.substring(1,7), 16); + setColor( new Color(colorValue) ); + } + } + + setDate( element.getAttribute( "date" ) ); + + String flags = element.getAttribute( "flags" ); + if( flags != null ) + { + String[] flagTokens = flags.split( "," ); + for( int i=0; i< flagTokens.length; i++ ) + { + if( flagTokens[i].equals( "invisible" ) ) + { + setInvisible( true ); + } + else if( flagTokens[i].equals( "hidden" ) ) + { + setHidden( true ); + } + else if( flagTokens[i].equals( "print" ) ) + { + setPrinted( true ); + } + else if( flagTokens[i].equals( "nozoom" ) ) + { + setNoZoom( true ); + } + else if( flagTokens[i].equals( "norotate" ) ) + { + setNoRotate( true ); + } + else if( flagTokens[i].equals( "noview" ) ) + { + setNoView( true ); + } + else if( flagTokens[i].equals( "readonly" ) ) + { + setReadOnly( true ); + } + else if( flagTokens[i].equals( "locked" ) ) + { + setLocked( true ); + } + else if( flagTokens[i].equals( "togglenoview" ) ) + { + setToggleNoView( true ); + } + } + + } + + setName( element.getAttribute( "name" ) ); + + String rect = element.getAttribute( "rect" ); + if( rect != null ) + { + String[] rectValues = rect.split( "," ); + float[] values = new float[ rectValues.length ]; + for( int i=0; i=3 ) + { + retval = new Color( rgb[0], rgb[1], rgb[2] ); + } + } + return retval; + } + + /** + * Set the annotation color. + * + * @param c The annotation color. + */ + public void setColor( Color c ) + { + COSArray color = null; + if( c != null ) + { + float[] colors = c.getRGBColorComponents( null ); + color = new COSArray(); + color.setFloatArray( colors ); + } + annot.setItem( "color", color ); + } + + /** + * Modification date. + * + * @return The date as a string. + */ + public String getDate() + { + return annot.getString( "date" ); + } + + /** + * The annotation modification date. + * + * @param date The date to store in the FDF annotation. + */ + public void setDate( String date ) + { + annot.setString( "date", date ); + } + + /** + * Get the invisible flag. + * + * @return The invisible flag. + */ + public boolean isInvisible() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_INVISIBLE ); + } + + /** + * Set the invisible flag. + * + * @param invisible The new invisible flag. + */ + public void setInvisible( boolean invisible ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_INVISIBLE, invisible ); + } + + /** + * Get the hidden flag. + * + * @return The hidden flag. + */ + public boolean isHidden() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_HIDDEN ); + } + + /** + * Set the hidden flag. + * + * @param hidden The new hidden flag. + */ + public void setHidden( boolean hidden ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_HIDDEN, hidden ); + } + + /** + * Get the printed flag. + * + * @return The printed flag. + */ + public boolean isPrinted() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_PRINTED ); + } + + /** + * Set the printed flag. + * + * @param printed The new printed flag. + */ + public void setPrinted( boolean printed ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_PRINTED, printed ); + } + + /** + * Get the noZoom flag. + * + * @return The noZoom flag. + */ + public boolean isNoZoom() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_NO_ZOOM ); + } + + /** + * Set the noZoom flag. + * + * @param noZoom The new noZoom flag. + */ + public void setNoZoom( boolean noZoom ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_NO_ZOOM, noZoom ); + } + + /** + * Get the noRotate flag. + * + * @return The noRotate flag. + */ + public boolean isNoRotate() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_NO_ROTATE ); + } + + /** + * Set the noRotate flag. + * + * @param noRotate The new noRotate flag. + */ + public void setNoRotate( boolean noRotate ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_NO_ROTATE, noRotate ); + } + + /** + * Get the noView flag. + * + * @return The noView flag. + */ + public boolean isNoView() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_NO_VIEW ); + } + + /** + * Set the noView flag. + * + * @param noView The new noView flag. + */ + public void setNoView( boolean noView ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_NO_VIEW, noView ); + } + + /** + * Get the readOnly flag. + * + * @return The readOnly flag. + */ + public boolean isReadOnly() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_READ_ONLY ); + } + + /** + * Set the readOnly flag. + * + * @param readOnly The new readOnly flag. + */ + public void setReadOnly( boolean readOnly ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_READ_ONLY, readOnly ); + } + + /** + * Get the locked flag. + * + * @return The locked flag. + */ + public boolean isLocked() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_LOCKED ); + } + + /** + * Set the locked flag. + * + * @param locked The new locked flag. + */ + public void setLocked( boolean locked ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_LOCKED, locked ); + } + + /** + * Get the toggleNoView flag. + * + * @return The toggleNoView flag. + */ + public boolean isToggleNoView() + { + return BitFlagHelper.getFlag( annot, COSName.F, PDAnnotation.FLAG_TOGGLE_NO_VIEW ); + } + + /** + * Set the toggleNoView flag. + * + * @param toggleNoView The new toggleNoView flag. + */ + public void setToggleNoView( boolean toggleNoView ) + { + BitFlagHelper.setFlag( annot, COSName.F, PDAnnotation.FLAG_TOGGLE_NO_VIEW, toggleNoView ); + } + + /** + * Set a unique name for an annotation. + * + * @param name The unique annotation name. + */ + public void setName( String name ) + { + annot.setString( COSName.NM, name ); + } + + /** + * Get the annotation name. + * + * @return The unique name of the annotation. + */ + public String getName() + { + return annot.getString( COSName.NM ); + } + + /** + * Set the rectangle associated with this annotation. + * + * @param rectangle The annotation rectangle. + */ + public void setRectangle( PDRectangle rectangle ) + { + annot.setItem( COSName.RECT, rectangle ); + } + + /** + * The rectangle associated with this annotation. + * + * @return The annotation rectangle. + */ + public PDRectangle getRectangle() + { + PDRectangle retval = null; + COSArray rectArray = (COSArray)annot.getDictionaryObject( COSName.RECT ); + if( rectArray != null ) + { + retval = new PDRectangle( rectArray ); + } + + return retval; + } + + /** + * Set a unique title for an annotation. + * + * @param title The annotation title. + */ + public void setTitle( String title ) + { + annot.setString( COSName.T, title ); + } + + /** + * Get the annotation title. + * + * @return The title of the annotation. + */ + public String getTitle() + { + return annot.getString( COSName.T ); + } + + /** + * The annotation create date. + * + * @return The date of the creation of the annotation date + * + * @throws IOException If there is an error converting the string to a Calendar object. + */ + public Calendar getCreationDate() throws IOException + { + return annot.getDate( COSName.CREATION_DATE ); + } + + /** + * Set the creation date. + * + * @param date The date the annotation was created. + */ + public void setCreationDate( Calendar date ) + { + annot.setDate( COSName.CREATION_DATE, date ); + } + + /** + * Set the annotation opacity. + * + * @param opacity The new opacity value. + */ + public void setOpacity( float opacity ) + { + annot.setFloat( COSName.CA, opacity ); + } + + /** + * Get the opacity value. + * + * @return The opacity of the annotation. + */ + public float getOpacity() + { + return annot.getFloat( COSName.CA, 1f ); + + } + + /** + * A short description of the annotation. + * + * @param subject The annotation subject. + */ + public void setSubject( String subject ) + { + annot.setString( COSName.SUBJ, subject ); + } + + /** + * Get the description of the annotation. + * + * @return The subject of the annotation. + */ + public String getSubject() + { + return annot.getString( COSName.SUBJ ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCaret.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCaret.java new file mode 100644 index 000000000..9b51f6f4b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCaret.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Caret FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationCaret extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Caret"; + + /** + * Default constructor. + */ + public FDFAnnotationCaret() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationCaret( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationCaret( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCircle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCircle.java new file mode 100644 index 000000000..322ecdc68 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationCircle.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Circle FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationCircle extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Circle"; + + /** + * Default constructor. + */ + public FDFAnnotationCircle() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationCircle( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationCircle( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFileAttachment.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFileAttachment.java new file mode 100644 index 000000000..d6ecf68a8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFileAttachment.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a FileAttachment FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationFileAttachment extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="FileAttachment"; + + /** + * Default constructor. + */ + public FDFAnnotationFileAttachment() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationFileAttachment( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationFileAttachment( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFreeText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFreeText.java new file mode 100644 index 000000000..d56446e34 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationFreeText.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a FreeText FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationFreeText extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="FreeText"; + + /** + * Default constructor. + */ + public FDFAnnotationFreeText() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationFreeText( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationFreeText( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationHighlight.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationHighlight.java new file mode 100644 index 000000000..f3a4b3c54 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationHighlight.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Highlight FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationHighlight extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Highlight"; + + /** + * Default constructor. + */ + public FDFAnnotationHighlight() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationHighlight( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationHighlight( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationInk.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationInk.java new file mode 100644 index 000000000..abbcaaac3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationInk.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Ink FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationInk extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Ink"; + + /** + * Default constructor. + */ + public FDFAnnotationInk() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationInk( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationInk( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationLine.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationLine.java new file mode 100644 index 000000000..22bbbea88 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationLine.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Line FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationLine extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Line"; + + /** + * Default constructor. + */ + public FDFAnnotationLine() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationLine( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationLine( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolygon.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolygon.java new file mode 100644 index 000000000..62df32701 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolygon.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Polygon FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationPolygon extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Polygon"; + + /** + * Default constructor. + */ + public FDFAnnotationPolygon() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationPolygon( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationPolygon( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolyline.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolyline.java new file mode 100644 index 000000000..93a87e3d8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationPolyline.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Polyline FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationPolyline extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Polyline"; + + /** + * Default constructor. + */ + public FDFAnnotationPolyline() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationPolyline( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationPolyline( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSound.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSound.java new file mode 100644 index 000000000..bc275140b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSound.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Sound FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationSound extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Sound"; + + /** + * Default constructor. + */ + public FDFAnnotationSound() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationSound( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationSound( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquare.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquare.java new file mode 100644 index 000000000..120458749 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquare.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Square FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationSquare extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Square"; + + /** + * Default constructor. + */ + public FDFAnnotationSquare() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationSquare( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationSquare( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquiggly.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquiggly.java new file mode 100644 index 000000000..9c679ce39 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationSquiggly.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Squiggly FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationSquiggly extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Squiggly"; + + /** + * Default constructor. + */ + public FDFAnnotationSquiggly() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationSquiggly( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationSquiggly( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStamp.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStamp.java new file mode 100644 index 000000000..659631e06 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStamp.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Stamp FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationStamp extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Stamp"; + + /** + * Default constructor. + */ + public FDFAnnotationStamp() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationStamp( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationStamp( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStrikeOut.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStrikeOut.java new file mode 100644 index 000000000..1aab2b602 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationStrikeOut.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a StrikeOut FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationStrikeOut extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="StrikeOut"; + + /** + * Default constructor. + */ + public FDFAnnotationStrikeOut() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationStrikeOut( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationStrikeOut( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationText.java new file mode 100644 index 000000000..63d7157a8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationText.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Text FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationText extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Text"; + + /** + * Default constructor. + */ + public FDFAnnotationText() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationText( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationText( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationUnderline.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationUnderline.java new file mode 100644 index 000000000..dc2dbf92f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFAnnotationUnderline.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.w3c.dom.Element; + +/** + * This represents a Underline FDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FDFAnnotationUnderline extends FDFAnnotation +{ + /** + * COS Model value for SubType entry. + */ + public static final String SUBTYPE ="Underline"; + + /** + * Default constructor. + */ + public FDFAnnotationUnderline() + { + super(); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } + + /** + * Constructor. + * + * @param a An existing FDF Annotation. + */ + public FDFAnnotationUnderline( COSDictionary a ) + { + super( a ); + } + + /** + * Constructor. + * + * @param element An XFDF element. + * + * @throws IOException If there is an error extracting information from the element. + */ + public FDFAnnotationUnderline( Element element ) throws IOException + { + super( element ); + annot.setName( COSName.SUBTYPE, SUBTYPE ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFCatalog.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFCatalog.java new file mode 100644 index 000000000..ac6516112 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFCatalog.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +import org.apache.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 + * @version $Revision: 1.3 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFDictionary.java new file mode 100644 index 000000000..9484aa369 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFDictionary.java @@ -0,0 +1,480 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification; +import org.apache.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 + * @version $Revision: 1.9 $ + */ +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\n" ); + } + COSArray ids = this.getID(); + if( ids != null ) + { + COSString original = (COSString)ids.getObject( 0 ); + COSString modified = (COSString)ids.getObject( 1 ); + output.write( "\n"); + } + List fields = getFields(); + if( fields != null && fields.size() > 0 ) + { + output.write( "\n" ); + for( int i=0; i\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. + * + * @throws IOException If there is an error creating the file spec. + */ + public PDFileSpecification getFile() throws IOException + { + 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; iBen Litchfield + * @version $Revision: 1.6 $ + */ +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( "\n" ); + output.write( "\n" ); + + getCatalog().writeXML( output ); + + output.write( "\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(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFField.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFField.java new file mode 100644 index 000000000..3bd42f4dc --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFField.java @@ -0,0 +1,749 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.io.IOException; +import java.io.Writer; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.PDTextStream; + +import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; +import org.apache.pdfbox.pdmodel.interactive.action.PDAdditionalActions; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; + +import org.apache.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 + * @version $Revision: 1.5 $ + */ +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 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( "\n"); + Object value = getValue(); + if( value != null ) + { + output.write( "" + value + "\n" ); + } + PDTextStream rt = getRichText(); + if( rt != null ) + { + output.write( "" + rt.getAsString() + "\n" ); + } + List kids = getKids(); + if( kids != null ) + { + for( int i=0; i\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( COSName.KIDS ); + List retval = null; + if( kids != null ) + { + List actuals = new ArrayList(); + for( int i=0; i kids ) + { + field.setItem( COSName.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
      + * Address.State
      + * Address.City
      + * + * @return The partial field name. + */ + public String getPartialFieldName() + { + return field.getString( COSName.T ); + } + + /** + * This will set the partial field name. + * + * @param partial The partial field name. + */ + public void setPartialFieldName( String partial ) + { + field.setString( COSName.T, partial ); + } + + /** + * This will set the value for the field. This will return type will either be
      + * String : Checkboxes, Radio Button
      + * 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( COSName.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( COSName.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( COSName.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 = COSInteger.get( ff ); + } + field.setItem( COSName.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.setInt( COSName.FF, 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( COSName.SET_FF ); + 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 = COSInteger.get( ff ); + } + field.setItem( COSName.SET_FF, 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.setInt( COSName.SET_FF, 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( COSName.CLR_FF ); + 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 = COSInteger.get( ff ); + } + field.setItem( COSName.CLR_FF, 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.setInt( COSName.CLR_FF, 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 = COSInteger.get( f ); + } + field.setItem( COSName.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.setInt( COSName.F, 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( COSName.SET_F ); + 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 = COSInteger.get( ff ); + } + field.setItem( COSName.SET_F, 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.setInt( COSName.SET_F, 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( COSName.CLR_F ); + 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 = COSInteger.get( ff ); + } + field.setItem( COSName.CLR_F, 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.setInt( COSName.CLR_F, 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( COSName.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( COSName.AP, ap ); + } + + /** + * This will get named page references.. + * + * @return The named page references. + */ + public FDFNamedPageReference getAppearanceStreamReference() + { + FDFNamedPageReference retval = null; + COSDictionary ref = (COSDictionary)field.getDictionaryObject( COSName.AP_REF ); + 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( COSName.AP_REF, 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( COSName.OPT ); + if( array != null ) + { + List objects = new ArrayList(); + for( int i=0; iBen Litchfield + * @version $Revision: 1.3 $ + */ +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.
      + * + * 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFJavaScript.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFJavaScript.java new file mode 100644 index 000000000..b8774ea3d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFJavaScript.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.PDTextStream; +import org.apache.pdfbox.pdmodel.common.PDNamedTextStream; + +/** + * This represents an FDF JavaScript dictionary that is part of the FDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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() + { + COSArray array = (COSArray)js.getDictionaryObject( "Doc" ); + List namedStreams = new ArrayList(); + if( array == null ) + { + array = new COSArray(); + js.setItem( "Doc", array ); + } + for( int i=0; iBen Litchfield + * @version $Revision: 1.3 $ + */ +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. + * + * @throws IOException If there is an error creating the file spec. + */ + public PDFileSpecification getFileSpecification() throws IOException + { + return PDFileSpecification.createFS( ref.getDictionaryObject( "F" ) ); + } + + /** + * 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFOptionElement.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFOptionElement.java new file mode 100644 index 000000000..b6a862c2b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFOptionElement.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSString; + +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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 ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFPage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFPage.java new file mode 100644 index 000000000..6db0a95cf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFPage.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This represents an FDF page that is part of the FDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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; iBen Litchfield + * @version $Revision: 1.2 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFTemplate.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFTemplate.java new file mode 100644 index 000000000..99eeee06c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/fdf/FDFTemplate.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.fdf; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This represents an FDF template that is part of the FDF page. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 + + + + + + +The fdf package will handle all of the logic used for FDF objects inside of the PDF/FDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/FontManager.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/FontManager.java new file mode 100644 index 000000000..178043a22 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/FontManager.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Properties; + +import org.apache.pdfbox.util.ResourceLoader; + +/** + * This class is used as font manager. + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ + +public class FontManager +{ + + // HashMap with all known fonts + private static HashMap envFonts = new HashMap(); + // the standard font + private static String standardFont = null; + private static Properties fontMapping = new Properties(); + + static { + try + { + ResourceLoader.loadProperties( + "org/apache/pdfbox/resources/FontMapping.properties", + fontMapping ); + } + catch( IOException io ) + { + io.printStackTrace(); + throw new RuntimeException( "Error loading font mapping" ); + } + loadFonts(); + loadFontMapping(); + loadBasefontMapping(); + setStandardFont(); + } + + private FontManager() + { + } + /** + * Get the standard font from the environment, usually Arial or Times New Roman. + * + * @return The standard font + * + */ + public static java.awt.Font getStandardFont() + { + if (standardFont != null) + { + return getAwtFont(standardFont); + } + return null; + } + + /** + * Get the font for the given fontname. + * + * @param font The name of the font. + * + * @return The font we are looking for or a similar font or null if nothing is found. + * + */ + public static java.awt.Font getAwtFont(String font) + { + String fontname = normalizeFontname(font); + if (envFonts.containsKey(fontname)) + { + return (java.awt.Font)envFonts.get(fontname); + } + return null; + } + + /** + * Load all available fonts from the environment. + */ + private static void loadFonts() + { + java.awt.Font[] allFonts = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + int numberOfFonts = allFonts.length; + for (int i=0;i -1) + { + normalizedFontname = normalizedFontname.substring(normalizedFontname.indexOf("+")+1); + } + // normalize all kinds of fonttypes. There are several possible version which have to be normalized + // e.g. Arial,Bold Arial-BoldMT Helevtica-oblique ... + boolean isBold = normalizedFontname.indexOf("bold") > -1; + boolean isItalic = normalizedFontname.indexOf("italic") > -1 || normalizedFontname.indexOf("oblique") > -1; + normalizedFontname = normalizedFontname.toLowerCase().replaceAll("bold" , "") + .replaceAll("italic" , "").replaceAll("oblique" , ""); + if (isBold) + { + normalizedFontname += "bold"; + } + if (isItalic) + { + normalizedFontname += "italic"; + } + return normalizedFontname; + } + + + /** + * Add a font-mapping. + * + * @param font The name of the font. + * + * @param mappedName The name of the mapped font. + * + */ + private static boolean addFontMapping(String font, String mappedName) + { + String fontname = normalizeFontname(font); + // is there already a font mapping ? + if (envFonts.containsKey(fontname)) + { + return false; + } + String mappedFontname = normalizeFontname(mappedName); + // is the mapped font available ? + if (!envFonts.containsKey(mappedFontname)) + { + return false; + } + envFonts.put(fontname, envFonts.get(mappedFontname)); + return true; + } + + /** + * Load the mapping for the well knwon font-substitutions. + * + */ + private static void loadFontMapping() + { + boolean addedMapping = true; + // There could be some recursive mappings in the fontmapping, so that we have to + // read the list until no more additional mapping is added to it + while (addedMapping) + { + int counter = 0; + Enumeration keys = fontMapping.keys(); + while (keys.hasMoreElements()) + { + String key = (String)keys.nextElement(); + if (addFontMapping(key,(String)fontMapping.get(key))) + { + counter++; + } + } + if (counter == 0) + { + addedMapping = false; + } + } + } + + /** + * Mapping for the basefonts. + */ + private static void loadBasefontMapping() + { + addFontMapping("Times-Roman","TimesNewRoman"); + addFontMapping("Times-Bold","TimesNewRoman,Bold"); + addFontMapping("Times-Italic","TimesNewRoman,Italic"); + addFontMapping("Times-BoldItalic","TimesNewRoman,Bold,Italic"); + addFontMapping("Helvetica-Oblique","Helvetica,Italic"); + addFontMapping("Helvetica-BoldOblique","Helvetica,Bold,Italic"); + addFontMapping("Courier-Oblique","Courier,Italic"); + addFontMapping("Courier-BoldOblique","Courier,Bold,Italic"); + } + + /** + * Try to determine if the font has both a BOLD and an ITALIC-type. + * + * @param name The font. + * + * @return font has BOLD and ITALIC-type or not + */ + private static boolean isBoldItalic(java.awt.Font font) + { + return isBold(font) && isItalic(font); + } + + /** + * Try to determine if the font has a BOLD-type. + * + * @param name The font. + * + * @return font has BOLD-type or not + */ + private static boolean isBold(java.awt.Font font) + { + String name = font.getName().toLowerCase(); + if (name.indexOf("bold") > -1) + { + return true; + } + String psname = font.getPSName().toLowerCase(); + if (psname.indexOf("bold") > -1) + { + return true; + } + return false; + } + + /** + * Try to determine if the font has an ITALIC-type. + * + * @param name The font. + * + * @return font has ITALIC-type or not + */ + private static boolean isItalic(java.awt.Font font) + { + String name = font.getName().toLowerCase(); + // oblique is the same as italic + if (name.indexOf("italic") > -1 || name.indexOf("oblique") > -1) + { + return true; + } + String psname = font.getPSName().toLowerCase(); + if (psname.indexOf("italic") > -1 || psname.indexOf("oblique") > -1) + { + return true; + } + return false; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFont.java new file mode 100644 index 000000000..087d16888 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFont.java @@ -0,0 +1,357 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.encoding.conversion.CMapSubstitution; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.util.ResourceLoader; + +/** + * This is implementation for the CIDFontType0/CIDFontType2 Fonts. + * + * @author Ben Litchfield + * @version $Revision: 1.11 $ + */ +public abstract class PDCIDFont extends PDSimpleFont +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDCIDFont.class); + + private Map widthCache = null; + private long defaultWidth = 0; + + /** + * Constructor. + */ + public PDCIDFont() + { + super(); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDCIDFont( COSDictionary fontDictionary ) + { + super( fontDictionary ); + extractWidths(); + } + + /** + * 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( "getFontBoundingBox(): 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() + { + if (defaultWidth == 0) + { + COSNumber number = (COSNumber)font.getDictionaryObject( COSName.DW); + if( number != null ) + { + defaultWidth = number.intValue(); + } + else + { + defaultWidth = 1000; + } + } + return defaultWidth; + } + + /** + * This will set the default width for the glyphs of this font. + * + * @param dw The default width. + */ + public void setDefaultWidth( long dw ) + { + defaultWidth = dw; + font.setLong( COSName.DW, 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 = getDefaultWidth(); + int code = getCodeFromArray( c, offset, length ); + + Float widthFloat = widthCache.get( code ); + if( widthFloat != null ) + { + retval = widthFloat.floatValue(); + } + return retval; + } + + private void extractWidths() + { + if (widthCache == null) + { + widthCache = new HashMap(); + COSArray widths = (COSArray)font.getDictionaryObject( COSName.W ); + if( widths != null ) + { + int size = widths.size(); + int counter = 0; + while (counter < size) + { + COSNumber firstCode = (COSNumber)widths.getObject( counter++ ); + COSBase next = widths.getObject( counter++ ); + if( next instanceof COSArray ) + { + COSArray array = (COSArray)next; + int startRange = firstCode.intValue(); + int arraySize = array.size(); + for (int i=0; i 0 ) + { + totalWidths += rangeWidth.floatValue(); + characterCount += 1; + } + } + } + } + float average = totalWidths / characterCount; + if( average <= 0 ) + { + average = defaultWidth; + } + return average; + } + + /** + * {@inheritDoc} + */ + public float getFontWidth( int charCode ) + { + float width = -1; + if (widthCache.containsKey(charCode)) + { + width = widthCache.get(charCode); + } + return width; + } + + /** + * Extract the CIDSystemInfo. + * @return the CIDSystemInfo as String + */ + private String getCIDSystemInfo() + { + String cidSystemInfo = null; + COSDictionary cidsysteminfo = (COSDictionary)font.getDictionaryObject(COSName.CIDSYSTEMINFO); + if (cidsysteminfo != null) + { + String ordering = cidsysteminfo.getString(COSName.ORDERING); + String registry = cidsysteminfo.getString(COSName.REGISTRY); + int supplement = cidsysteminfo.getInt(COSName.SUPPLEMENT); + cidSystemInfo = registry + "-" + ordering+ "-" + supplement; + } + return cidSystemInfo; + } + + @Override + protected void determineEncoding() + { + String cidSystemInfo = getCIDSystemInfo(); + if (cidSystemInfo != null) + { + if (cidSystemInfo.contains("Identity")) + { + cidSystemInfo = "Identity-H"; + } + else if (cidSystemInfo.startsWith("Adobe-UCS-")) + { + cidSystemInfo = "Adobe-Identity-UCS"; + } + else + { + cidSystemInfo = cidSystemInfo.substring(0,cidSystemInfo.lastIndexOf("-"))+"-UCS2"; + } + cmap = cmapObjects.get( cidSystemInfo ); + if (cmap == null) + { + String resourceName = resourceRootCMAP + cidSystemInfo; + try { + parseCmap( resourceRootCMAP, ResourceLoader.loadResource( resourceName ) ); + if( cmap == null) + { + log.error("Error: Could not parse predefined CMAP file for '" + cidSystemInfo + "'" ); + } + } + catch(IOException exception) + { + log.error("Error: Could not find predefined CMAP file for '" + cidSystemInfo + "'" ); + } + } + } + else + { + super.determineEncoding(); + } + } + + @Override + public String encode(byte[] c, int offset, int length) throws IOException + { + String result = null; + if (cmap != null) + { + result = cmapEncoding(getCodeFromArray( c, offset, length ), length, true); + } + else + { + result = super.encode(c, offset, length); + } + return result; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType0Font.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType0Font.java new file mode 100644 index 000000000..f413f4ae5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType0Font.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.awt.Font; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is implementation of the CIDFontType0 Font. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class PDCIDFontType0Font extends PDCIDFont +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDCIDFontType0Font.class); + + /** + * Constructor. + */ + public PDCIDFontType0Font() + { + super(); + font.setItem( COSName.SUBTYPE, COSName.CID_FONT_TYPE0 ); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDCIDFontType0Font( COSDictionary fontDictionary ) + { + super( fontDictionary ); + } + + /** + * {@inheritDoc} + */ + public Font getawtFont() throws IOException + { + Font awtFont = null; + PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary)getFontDescriptor(); + if( fd.getFontFile3() != null ) + { + // create a font with the embedded data + PDType1CFont type1CFont = new PDType1CFont( super.font ); + awtFont = type1CFont.getawtFont(); + if (awtFont == null) + { + awtFont = FontManager.getAwtFont(fd.getFontName()); + if (awtFont != null) + { + log.info("Using font "+awtFont.getName()+ " instead"); + } + } + } + return awtFont; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java new file mode 100644 index 000000000..5928f60f9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDCIDFontType2Font.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * This is implementation of the CIDFontType2 Font. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class PDCIDFontType2Font extends PDCIDFont +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDCIDFontType2Font.class); + + /** + * Constructor. + */ + public PDCIDFontType2Font() + { + super(); + font.setItem( COSName.SUBTYPE, COSName.CID_FONT_TYPE2 ); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDCIDFontType2Font( COSDictionary fontDictionary ) + { + super( fontDictionary ); + } + + /** + * {@inheritDoc} + */ + public Font getawtFont() throws IOException + { + Font awtFont = null; + PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary)getFontDescriptor(); + PDStream ff2Stream = fd.getFontFile2(); + if( ff2Stream != null ) + { + try + { + // create a font with the embedded data + awtFont = Font.createFont( Font.TRUETYPE_FONT, ff2Stream.createInputStream() ); + } + catch( FontFormatException f ) + { + log.info("Can't read the embedded font " + fd.getFontName() ); + } + if (awtFont == null) + { + awtFont = FontManager.getAwtFont(fd.getFontName()); + if (awtFont != null) + { + log.info("Using font "+awtFont.getName()+ " instead"); + } + } + } + // TODO FontFile3 + return awtFont; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFont.java new file mode 100644 index 000000000..15b405b2f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFont.java @@ -0,0 +1,843 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import org.apache.fontbox.afm.AFMParser; +import org.apache.fontbox.afm.FontMetric; +import org.apache.fontbox.cmap.CMapParser; +import org.apache.fontbox.cmap.CMap; + +import org.apache.pdfbox.encoding.Encoding; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDMatrix; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +import org.apache.pdfbox.util.ResourceLoader; + +import java.awt.Graphics; +import java.awt.geom.AffineTransform; + +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This is the base class for all PDF fonts. + * + * @author Ben Litchfield + * @version $Revision: 1.46 $ + */ +public abstract class PDFont implements COSObjectable +{ + + /** + * 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; + + /** + * The descriptor of the font + */ + private PDFontDescriptor fontDescriptor = null; + + /** + * The font matrix + */ + protected PDMatrix fontMatrix = 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. + */ + protected CMap cmap = null; + + private boolean hasToUnicode = false; + + protected static Map cmapObjects = + Collections.synchronizedMap( new HashMap() ); + + /** + * A list a floats representing the widths + */ + private List widths = null; + + /** + * The static map of the default Adobe font metrics. + */ + private static final Map afmObjects = + Collections.unmodifiableMap( getAdobeFontMetrics() ); + + // TODO move the Map to PDType1Font as these are the 14 Standard fonts + // which are definitely Type 1 fonts + private static Map getAdobeFontMetrics() + { + Map metrics = new HashMap(); + addAdobeFontMetric( metrics, "Courier-Bold" ); + addAdobeFontMetric( metrics, "Courier-BoldOblique" ); + addAdobeFontMetric( metrics, "Courier" ); + addAdobeFontMetric( metrics, "Courier-Oblique" ); + addAdobeFontMetric( metrics, "Helvetica" ); + addAdobeFontMetric( metrics, "Helvetica-Bold" ); + addAdobeFontMetric( metrics, "Helvetica-BoldOblique" ); + addAdobeFontMetric( metrics, "Helvetica-Oblique" ); + addAdobeFontMetric( metrics, "Symbol" ); + addAdobeFontMetric( metrics, "Times-Bold" ); + addAdobeFontMetric( metrics, "Times-BoldItalic" ); + addAdobeFontMetric( metrics, "Times-Italic" ); + addAdobeFontMetric( metrics, "Times-Roman" ); + addAdobeFontMetric( metrics, "ZapfDingbats" ); + return metrics; + } + + protected final static String resourceRootCMAP = "org/apache/pdfbox/resources/cmap/"; + private final static String resourceRootAFM = "org/apache/pdfbox/resources/afm/"; + + private static void addAdobeFontMetric( + Map metrics, String name ) + { + try + { + String resource = resourceRootAFM + name + ".afm"; + InputStream afmStream = ResourceLoader.loadResource( resource ); + if( afmStream != null ) + { + try + { + AFMParser parser = new AFMParser( afmStream ); + parser.parse(); + metrics.put( name, parser.getResult() ); + } + finally + { + afmStream.close(); + } + } + } + catch (Exception e) + { + // ignore + } + } + + /** + * 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 extraction. THIS METHOD WILL BE MOVED OR REMOVED + * TO ANOTHER LOCATION IN A FUTURE VERSION OF PDFBOX. + */ + public static void clearResources() + { + 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; + determineEncoding(); + } + + /** + * This will get the font descriptor for this font. + * + * @return The font descriptor for this font. + * + */ + public PDFontDescriptor getFontDescriptor() + { + if(fontDescriptor == null) + { + COSDictionary fd = (COSDictionary)font.getDictionaryObject( COSName.FONT_DESC ); + if (fd != null) + { + fontDescriptor = new PDFontDescriptorDictionary( fd ); + } + else + { + FontMetric afm = getAFM(); + if( afm != null ) + { + fontDescriptor = new PDFontDescriptorAFM( afm ); + } + } + } + return fontDescriptor; + } + + /** + * 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.FONT_DESC, dic ); + this.fontDescriptor = fontDescriptor; + } + + /** + * Determines the encoding for the font. + * This method as to be overwritten, as there are different + * possibilities to define a mapping. + */ + protected abstract void determineEncoding(); + + /** + * {@inheritDoc} + */ + 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 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 getFontHeight( 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("ISO-8859-1"); + float totalWidth = 0; + for( int i=0; i -1) + { + name = name.substring(name.indexOf("+")+1); + } + + } + else if ( baseFont instanceof COSString ) + { + COSString string = (COSString)baseFont; + name = string.getString(); + } + if ( name != null ) + { + afm = afmObjects.get( name ); + } + } + return afm; + } + + private FontMetric afm = null; + + private COSBase encoding = null; + /** + * cache the {@link COSName#ENCODING} object from + * the font's dictionary since it is called so often. + *

      + * Use this method instead of + *

      +     *   font.getDictionaryObject(COSName.ENCODING);
      +     * 
      + * @return + */ + protected COSBase getEncoding() + { + if (encoding == null) + { + encoding = font.getDictionaryObject( COSName.ENCODING ); + } + return encoding; + } + + /** + * Set the encoding object from the fonts dictionary. + * @param encoding the given encoding. + */ + protected void setEncoding(COSBase encoding) + { + font.setItem( COSName.ENCODING, encoding ); + this.encoding = encoding; + } + + /** + * Encode the given value using the CMap of the font. + * + * @param code the code to encode. + * @param length the byte length of the given code. + * @param isCIDFont indicates that the used font is a CID font. + * + * @return The value of the encoded character. + */ + protected String cmapEncoding( int code, int length, boolean isCIDFont ) throws IOException + { + String retval = null; + if (cmap != null) + { + retval = cmap.lookup(code, length); + if (retval == null && isCIDFont) + { + retval = cmap.lookupCID(code); + } + } + return retval; + } + /** + * 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; + int code = getCodeFromArray( c, offset, length ); + if( cmap != null ) + { + retval = cmapEncoding(code, length, false); + } + + // there is no cmap but probably an encoding with a suitable mapping + if( retval == null ) + { + Encoding encoding = getFontEncoding(); + if( encoding != null ) + { + retval = encoding.getCharacter( code ); + } + if( retval == null && (cmap == null || length == 2)) + { + 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++ ) + { + try + { + SINGLE_CHAR_STRING[i] = new String( new byte[] {(byte)i}, "ISO-8859-1" ); + } + catch (UnsupportedEncodingException e) + { + // Nothing should happen here + e.printStackTrace(); + } + for( int j=0; j<256; j++ ) + { + try + { + DOUBLE_CHAR_STRING[i][j] = new String( new byte[] {(byte)i, (byte)j}, "UTF-16BE" ); + } + catch (UnsupportedEncodingException e) + { + // Nothing should happen here + e.printStackTrace(); + } + } + } + } + + 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; + } + + protected void parseCmap( String cmapRoot, InputStream cmapStream) + { + if( cmapStream != null ) + { + CMapParser parser = new CMapParser(); + try + { + cmap = parser.parse( cmapRoot, cmapStream ); + // limit the cache to external CMaps + if (cmapRoot != null) + { + cmapObjects.put( cmap.getName(), cmap ); + } + } + catch (IOException exception) {} + } + } + + /** + * The will set the encoding for this font. + * + * @param enc The font encoding. + */ + public void setFontEncoding( Encoding enc ) + { + fontEncoding = enc; + } + + /** + * This will get or create the encoder. + * + * @return The encoding to use. + */ + public Encoding getFontEncoding() + { + 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 ); + } + + // Memorized values to avoid repeated dictionary lookups + private String subtype = null; + private boolean type1Font; + private boolean trueTypeFont; + private boolean typeFont; + + /** + * This will get the subtype of font, Type1, Type3, ... + * + * @return The type of font that this is. + */ + public String getSubType() + { + if (subtype == null) + { + subtype = font.getNameAsString( COSName.SUBTYPE ); + type1Font = "Type1".equals(subtype); + trueTypeFont = "TrueType".equals(subtype); + typeFont = type1Font || "Type0".equals(subtype) || trueTypeFont; + } + return subtype; + } + + /** + * Determines if the font is a type 1 font. + * @return returns true if the font is a type 1 font + */ + protected boolean isType1Font() + { + getSubType(); + return type1Font; + } + + private boolean isTrueTypeFont() + { + getSubType(); + return trueTypeFont; + } + + private boolean isTypeFont() + { + getSubType(); + return typeFont; + } + + /** + * 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() + { + if (widths == null) + { + COSArray array = (COSArray)font.getDictionaryObject( COSName.WIDTHS ); + if (array != null) + { + widths = COSArrayList.convertFloatCOSArrayToList( array ); + } + } + return widths; + } + + /** + * Set the widths of the characters code. + * + * @param widths The widths of the character codes. + */ + public void setWidths( List widths ) + { + this.widths = widths; + font.setItem( COSName.WIDTHS, COSArrayList.converterToCOSArray( this.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() + { + if (fontMatrix == null) + { + COSArray array = (COSArray)font.getDictionaryObject( COSName.FONT_MATRIX ); + if( array == null ) + { + array = new COSArray(); + array.add( new COSFloat( 0.001f ) ); + array.add( COSInteger.ZERO ); + array.add( COSInteger.ZERO ); + array.add( new COSFloat( 0.001f ) ); + array.add( COSInteger.ZERO ); + array.add( COSInteger.ZERO ); + } + fontMatrix = new PDMatrix(array); + } + return fontMatrix; + } + + /** + * This will get the fonts bounding box. + * + * @return The fonts bounding box. + * + * @throws IOException If there is an error getting the bounding box. + */ + public abstract PDRectangle getFontBoundingBox() throws IOException; + + /** + * {@inheritDoc} + */ + public boolean equals( Object other ) + { + return other instanceof PDFont && ((PDFont)other).getCOSObject() == this.getCOSObject(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return this.getCOSObject().hashCode(); + } + + /** + * Determines the width of the given character. + * @param charCode the code of the given character + * @return the width of the character + */ + public float getFontWidth( int charCode ) + { + float width = -1; + int firstChar = getFirstChar(); + int lastChar = getLastChar(); + if (charCode >= firstChar && charCode <= lastChar) + { + List widths = getWidths(); + // maybe the font doesn't provide any widths + if (widths != null) + { + width = widths.get(charCode-firstChar).floatValue(); + } + } + else + { + PDFontDescriptor fd = getFontDescriptor(); + if (fd instanceof PDFontDescriptorDictionary) + { + width = fd.getMissingWidth(); + } + } + return width; + } + + /** + * Determines if a font as a ToUnicode entry. + * @return true if the font has a ToUnicode entry + */ + protected boolean hasToUnicode() + { + return hasToUnicode; + } + + /** + * Sets hasToUnicode to the given value. + * @param hasToUnicode the given value for hasToUnicode + */ + protected void setHasToUnicode(boolean hasToUnicode) + { + this.hasToUnicode = hasToUnicode; + } + + public COSString createString(String text) throws IOException + { + return new COSString(text); + + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptor.java new file mode 100644 index 000000000..ae6e7b439 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptor.java @@ -0,0 +1,531 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.io.IOException; + +import org.apache.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.2 $ + */ +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 = 4; + /** + * A font descriptor flag. See PDF Reference for description. + */ + private static final int FLAG_SCRIPT = 8; + /** + * A font descriptor flag. See PDF Reference for description. + */ + private static final int FLAG_NON_SYMBOLIC = 32; + /** + * A font descriptor flag. See PDF Reference for description. + */ + private static final int FLAG_ITALIC = 64; + /** + * A font descriptor flag. See PDF Reference for description. + */ + private static final int FLAG_ALL_CAP = 65536; + /** + * A font descriptor flag. See PDF Reference for description. + */ + private static final int FLAG_SMALL_CAP = 131072; + /** + * A font descriptor flag. See PDF Reference for description. + */ + private static final int FLAG_FORCE_BOLD = 262144; + + + /** + * 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() & bit) != 0; + } + + private void setFlagBit( int bit, boolean value ) + { + int flags = getFlags(); + if( value ) + { + flags = flags | bit; + } + else + { + flags = flags & (0xFFFFFFFF ^ bit); + } + 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 ); + + /** + * This will get the missing width for the font. + * + * @return The missing width value. + */ + public abstract float getMissingWidth(); + + /** + * This will set the missing width for the font. + * + * @param charSet The new missing width for the font. + */ + public abstract void setMissingWidth( float missingWidth ); + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java new file mode 100644 index 000000000..b5f0a6a7e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorAFM.java @@ -0,0 +1,432 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.io.IOException; + +import org.apache.fontbox.afm.FontMetric; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +import org.apache.fontbox.util.BoundingBox; + +/** + * This class represents the font descriptor when the font information + * is coming from an AFM file. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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" ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java new file mode 100644 index 000000000..5125922de --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontDescriptorDictionary.java @@ -0,0 +1,605 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.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.4 $ + */ +public class PDFontDescriptorDictionary extends PDFontDescriptor implements COSObjectable +{ + private COSDictionary dic; + private float xHeight = Float.NEGATIVE_INFINITY; + private float capHeight = Float.NEGATIVE_INFINITY; + private int flags = -1; + + /** + * Constructor. + */ + public PDFontDescriptorDictionary() + { + dic = new COSDictionary(); + dic.setItem( COSName.TYPE, COSName.FONT_DESC ); + } + + /** + * 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; + } + + /** + * Convert this standard java object to a COS object. + * + * @return The cos object that matches this Java object. + */ + public COSBase getCOSObject() + { + return dic; + } + + /** + * Get the font name. + * + * @return The name of the font. + */ + public String getFontName() + { + String retval = null; + COSName name = (COSName)dic.getDictionaryObject( COSName.FONT_NAME ); + 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.FONT_NAME, name ); + } + + /** + * A string representing the preferred font family. + * + * @return The font family. + */ + public String getFontFamily() + { + String retval = null; + COSString name = (COSString)dic.getDictionaryObject( COSName.FONT_FAMILY ); + 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.FONT_FAMILY, 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( COSName.FONT_WEIGHT,0 ); + } + + /** + * Set the weight of the font. + * + * @param fontWeight The new weight of the font. + */ + public void setFontWeight( float fontWeight ) + { + dic.setFloat( COSName.FONT_WEIGHT, 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.FONT_STRETCH ); + 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.FONT_STRETCH, name ); + } + + /** + * This will get the font flags. + * + * @return The font flags. + */ + public int getFlags() + { + if (flags == -1) + { + flags = dic.getInt( COSName.FLAGS, 0 ); + } + return flags; + } + + /** + * This will set the font flags. + * + * @param flags The new font flags. + */ + public void setFlags( int flags ) + { + dic.setInt( COSName.FLAGS, flags ); + this.flags = flags; + } + + /** + * This will get the fonts bounding box. + * + * @return The fonts bounding box. + */ + public PDRectangle getFontBoundingBox() + { + COSArray rect = (COSArray)dic.getDictionaryObject( COSName.FONT_BBOX ); + 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.FONT_BBOX, array ); + } + + /** + * This will get the italic angle for the font. + * + * @return The italic angle. + */ + public float getItalicAngle() + { + return dic.getFloat( COSName.ITALIC_ANGLE, 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( COSName.ITALIC_ANGLE, angle ); + } + + /** + * This will get the ascent for the font. + * + * @return The ascent. + */ + public float getAscent() + { + return dic.getFloat( COSName.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( COSName.ASCENT, ascent ); + } + + /** + * This will get the descent for the font. + * + * @return The descent. + */ + public float getDescent() + { + return dic.getFloat( COSName.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( COSName.DESCENT, descent ); + } + + /** + * This will get the leading for the font. + * + * @return The leading. + */ + public float getLeading() + { + return dic.getFloat( COSName.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( COSName.LEADING, leading ); + } + + /** + * This will get the CapHeight for the font. + * + * @return The cap height. + */ + public float getCapHeight() + { + if(capHeight==Float.NEGATIVE_INFINITY) + { + /* We observed a negative value being returned with + * the Scheherazade font. PDFBOX-429 was logged for this. + * We are not sure if returning the absolute value + * is the correct fix, but it seems to work. */ + capHeight = java.lang.Math.abs(dic.getFloat( COSName.CAP_HEIGHT, 0 )); + } + return capHeight; + } + + + /** + * 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( COSName.CAP_HEIGHT, capHeight ); + this.capHeight = capHeight; + } + + /** + * This will get the x height for the font. + * + * @return The x height. + */ + public float getXHeight() + { + if(xHeight==Float.NEGATIVE_INFINITY) + { + /* We observed a negative value being returned with + * the Scheherazade font. PDFBOX-429 was logged for this. + * We are not sure if returning the absolute value + * is the correct fix, but it seems to work. */ + xHeight = java.lang.Math.abs(dic.getFloat( COSName.XHEIGHT, 0 )); + } + return xHeight; + } + + /** + * 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( COSName.XHEIGHT, xHeight ); + this.xHeight = xHeight; + } + + /** + * This will get the stemV for the font. + * + * @return The stem v value. + */ + public float getStemV() + { + return dic.getFloat( COSName.STEM_V, 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( COSName.STEM_V, stemV ); + } + + /** + * This will get the stemH for the font. + * + * @return The stem h value. + */ + public float getStemH() + { + return dic.getFloat( COSName.STEM_H, 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( COSName.STEM_H, stemH ); + } + + /** + * This will get the average width for the font. + * + * @return The average width value. + */ + public float getAverageWidth() + { + return dic.getFloat( COSName.AVG_WIDTH, 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( COSName.AVG_WIDTH, averageWidth ); + } + + /** + * This will get the max width for the font. + * + * @return The max width value. + */ + public float getMaxWidth() + { + return dic.getFloat( COSName.MAX_WIDTH, 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( COSName.MAX_WIDTH, maxWidth ); + } + + /** + * This will get the missing width for the font. + * + * @return The missing width value. + */ + public float getMissingWidth() + { + return dic.getFloat( COSName.MISSING_WIDTH, 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( COSName.MISSING_WIDTH, 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.CHAR_SET ); + 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.CHAR_SET, 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( COSName.FONT_FILE ); + 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( COSName.FONT_FILE, 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( COSName.FONT_FILE2 ); + 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( COSName.FONT_FILE2, 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( COSName.FONT_FILE3 ); + 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( COSName.FONT_FILE3, stream ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontFactory.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontFactory.java new file mode 100644 index 000000000..cff4dfcde --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDFontFactory.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.io.IOException; +import java.util.Map; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This will create the correct type of font based on information in the dictionary. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public class PDFontFactory +{ + /** + * private constructor, should only use static methods in this class. + */ + private PDFontFactory() + { + } + + /** + * Logger instance. + */ + private static final Log log = LogFactory.getLog(PDFontFactory.class); + + /** + * This will create the correct font based on information in the dictionary. + * + * @param dic The populated dictionary. + * + * @param fontCache A Map to cache already created fonts + * + * @return The corrent implementation for the font. + * + * @throws IOException If the dictionary is not valid. + */ + public static PDFont createFont(COSDictionary dic, Map fontCache) throws IOException + { + PDFont retval = null; + if (fontCache != null) + { + String fontKey = dic.getNameAsString(COSName.BASE_FONT) + dic.getNameAsString(COSName.NAME) + + dic.getNameAsString(COSName.SUBTYPE); + if (dic.getItem(COSName.ENCODING) != null) + { + fontKey += dic.getItem(COSName.ENCODING).toString(); + } + if (fontCache.containsKey(fontKey)) + { + retval = (PDFont)fontCache.get(fontKey); + } + else + { + retval = PDFontFactory.createFont( dic ); + fontCache.put(fontKey, retval); + } + } + else + { + retval = PDFontFactory.createFont( dic ); + } + return retval; + } + + /** + * 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.TYPE1) ) + { + retval = new PDType1Font( dic ); + } + else if( subType.equals( COSName.MM_TYPE1 ) ) + { + retval = new PDMMType1Font( dic ); + } + else if( subType.equals( COSName.TRUE_TYPE ) ) + { + retval = new PDTrueTypeFont( dic ); + } + else if( subType.equals( COSName.TYPE3 ) ) + { + retval = new PDType3Font( dic ); + } + else if( subType.equals( COSName.TYPE0 ) ) + { + retval = new PDType0Font( dic ); + } + else if( subType.equals( COSName.CID_FONT_TYPE0 ) ) + { + retval = new PDCIDFontType0Font( dic ); + } + else if( subType.equals( COSName.CID_FONT_TYPE2 ) ) + { + retval = new PDCIDFontType2Font( dic ); + } + else + { + log.warn("Substituting TrueType for unknown font subtype=" + dic.getDictionaryObject( COSName.SUBTYPE ).toString()); + //throw new IOException( "Unknown font subtype=" + subType ); + retval = new PDTrueTypeFont( dic ); + } + return retval; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDMMType1Font.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDMMType1Font.java new file mode 100644 index 000000000..f99b30cb7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDMMType1Font.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is implementation of the Multiple Master Type1 Font. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class PDMMType1Font extends PDSimpleFont +{ + /** + * Constructor. + */ + public PDMMType1Font() + { + super(); + font.setItem( COSName.SUBTYPE, COSName.MM_TYPE1 ); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDMMType1Font( COSDictionary fontDictionary ) + { + super( fontDictionary ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDSimpleFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDSimpleFont.java new file mode 100644 index 000000000..99f2e247d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDSimpleFont.java @@ -0,0 +1,450 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.awt.geom.Point2D; +import java.awt.geom.NoninvertibleTransformException; +import java.io.IOException; + +import java.util.HashMap; + +import org.apache.fontbox.afm.FontMetric; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.encoding.DictionaryEncoding; +import org.apache.pdfbox.encoding.Encoding; +import org.apache.pdfbox.encoding.EncodingManager; +import org.apache.pdfbox.encoding.conversion.CMapSubstitution; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.util.ResourceLoader; + +/** + * This class contains implementation details of the simple pdf fonts. + * + * @author Ben Litchfield + * @version $Revision: 1.18 $ + */ +public abstract class PDSimpleFont extends PDFont +{ + private final HashMap mFontSizes = + new HashMap(128); + + private float avgFontWidth = 0.0f; + + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDSimpleFont.class); + + /** + * Constructor. + */ + public PDSimpleFont() + { + super(); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDSimpleFont( COSDictionary fontDictionary ) + { + super( fontDictionary ); + } + + /** + * Looks up, creates, returns the AWT Font. + */ + public Font getawtFont() throws IOException + { + log.error("Not yet implemented:" + getClass().getName() ); + return null; + } + + /** + * {@inheritDoc} + */ + public void drawString( String string, Graphics g, float fontSize, + AffineTransform at, float x, float y ) throws IOException + { + Font _awtFont = getawtFont(); + + // mdavis - fix fontmanager.so/dll on sun.font.FileFont.getGlyphImage + // for font with bad cmaps? + // Type1 fonts are not affected as they don't have cmaps + if (!isType1Font() && _awtFont.canDisplayUpTo(string) != -1) { + log.warn("Changing font on <" + string + "> from <" + + _awtFont.getName() + "> to the default font"); + _awtFont = Font.decode(null); + } + + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + writeFont(g2d, at, _awtFont, x, y, string); + } + + /** + * This will get the font height 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 getFontHeight( byte[] c, int offset, int length ) throws IOException + { + float retval = 0; + int code = getCodeFromArray( c, offset, length ); + FontMetric metric = getAFM(); + if( metric != null ) + { + Encoding encoding = getFontEncoding(); + String characterName = encoding.getName( code ); + retval = metric.getCharacterHeight( characterName ); + } + else + { + PDFontDescriptor desc = getFontDescriptor(); + if( desc != null ) + { + float xHeight = desc.getXHeight(); + float capHeight = desc.getCapHeight(); + if( xHeight != 0f && capHeight != 0 ) + { + //do an average of these two. Can we do better??? + retval = (xHeight + capHeight)/2f; + } + else if( xHeight != 0 ) + { + retval = xHeight; + } + else if( capHeight != 0 ) + { + retval = capHeight; + } + else + { + retval = 0; + } + //hmm, not sure if this is 100% correct + //but gives a height, Should we add Descent as well?? + if( retval == 0 ) + { + retval = desc.getAscent(); + } + } + } + return retval; + } + + /** + * 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 + { + int code = getCodeFromArray( c, offset, length ); + Float fontWidth = mFontSizes.get(code); + if (fontWidth == null) + { + fontWidth = getFontWidth(code); + if (fontWidth == -1) + { + //hmm should this be in PDType1Font?? + fontWidth = getFontWidthFromAFMFile( code ); + } + mFontSizes.put(code, fontWidth); + } + 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; + + //AJW + if (avgFontWidth != 0.0f) + { + average = avgFontWidth; + } + else + { + float totalWidth = 0.0f; + float characterCount = 0.0f; + COSArray widths = (COSArray)font.getDictionaryObject( COSName.WIDTHS ); + if( widths != null ) + { + for( int i=0; i 0 ) + { + totalWidth += fontWidth.floatValue(); + characterCount += 1; + } + } + } + + if( totalWidth > 0 ) + { + average = totalWidth / characterCount; + } + else + { + average = getAverageFontWidthFromAFMFile(); + } + avgFontWidth = average; + } + return average; + } + + + /** + * This will get the ToUnicode object. + * + * @return The ToUnicode object. + */ + public COSBase getToUnicode() + { + return font.getDictionaryObject( COSName.TO_UNICODE ); + } + + /** + * This will set the ToUnicode object. + * + * @param unicode The unicode object. + */ + public void setToUnicode( COSBase unicode ) + { + font.setItem( COSName.TO_UNICODE, 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(); + } + + /** + * This will draw a string on a canvas using the font. + * + * @param g2d The graphics to draw onto. + * @param at The transformation matrix with all infos for scaling and shearing of the font. + * @param awtFont The font to draw. + * @param x The x coordinate to draw at. + * @param y The y coordinate to draw at. + * @param string The string to draw. + * + */ + protected void writeFont(final Graphics2D g2d, final AffineTransform at, final Font awtFont, + final float x, final float y, final String string) + { + // check if we have a rotation + if (!at.isIdentity()) + { + try + { + AffineTransform atInv = at.createInverse(); + // do only apply the size of the transform, rotation will be realized by rotating the graphics, + // otherwise the hp printers will not render the font + g2d.setFont(awtFont.deriveFont(1f)); + // apply the inverse transformation to the graphics, which should be the same as applying the + // transformation itself to the text + g2d.transform(at); + // translate the coordinates + Point2D.Float newXy = new Point2D.Float(x,y); + atInv.transform(new Point2D.Float( x, y), newXy); + g2d.drawString( string, (float)newXy.getX(), (float)newXy.getY() ); + // restore the original transformation + g2d.transform(atInv); + } + catch (NoninvertibleTransformException e) + { + log.error("Error in "+getClass().getName()+".writeFont",e); + } + } + else + { + g2d.setFont( awtFont.deriveFont( at ) ); + g2d.drawString( string, x, y ); + } + } + + /** + * {@inheritDoc} + */ + protected void determineEncoding() + { + String cmapName = null; + COSName encodingName = null; + COSBase encoding = getEncoding(); + Encoding fontEncoding = null; + if (encoding != null) + { + if (encoding instanceof COSName) + { + if (cmap == null) + { + encodingName = (COSName)encoding; + cmap = cmapObjects.get( encodingName.getName() ); + if (cmap == null) + { + cmapName = encodingName.getName(); + } + } + if (cmap == null && cmapName != null) + { + try + { + fontEncoding = + EncodingManager.INSTANCE.getEncoding(encodingName); + } + catch(IOException exception) + { + log.debug("Debug: Could not find encoding for " + encodingName ); + } + } + } + else if (encoding instanceof COSDictionary) + { + try + { + fontEncoding = new DictionaryEncoding((COSDictionary)encoding); + } + catch(IOException exception) + { + log.error("Error: Could not create the DictionaryEncoding" ); + } + } + else if(encoding instanceof COSStream ) + { + if (cmap == null) + { + COSStream encodingStream = (COSStream)encoding; + try + { + parseCmap( null, encodingStream.getUnfilteredStream() ); + } + catch(IOException exception) + { + log.error("Error: Could not parse the embedded CMAP" ); + } + } + } + } + setFontEncoding(fontEncoding); + extractToUnicodeEncoding(); + + if (cmap == null && cmapName != null) + { + String resourceName = resourceRootCMAP + cmapName; + try { + parseCmap( resourceRootCMAP, ResourceLoader.loadResource( resourceName ) ); + if( cmap == null && encodingName == null) + { + log.error("Error: Could not parse predefined CMAP file for '" + cmapName + "'" ); + } + } + catch(IOException exception) + { + log.error("Error: Could not find predefined CMAP file for '" + cmapName + "'" ); + } + } + } + + private void extractToUnicodeEncoding() + { + COSName encodingName = null; + String cmapName = null; + COSBase toUnicode = getToUnicode(); + if( toUnicode != null ) + { + setHasToUnicode(true); + if ( toUnicode instanceof COSStream ) + { + try { + parseCmap(null, ((COSStream)toUnicode).getUnfilteredStream()); + } + catch(IOException exception) + { + log.error("Error: Could not load embedded CMAP" ); + } + } + else if ( toUnicode instanceof COSName) + { + encodingName = (COSName)toUnicode; + cmap = cmapObjects.get( encodingName.getName() ); + if (cmap == null) + { + cmapName = encodingName.getName(); + String resourceName = resourceRootCMAP + cmapName; + try { + parseCmap( resourceRootCMAP, ResourceLoader.loadResource( resourceName )); + } + catch(IOException exception) + { + log.error("Error: Could not find predefined CMAP file for '" + cmapName + "'" ); + } + if( cmap == null) + { + log.error("Error: Could not parse predefined CMAP file for '" + cmapName + "'" ); + } + } + } + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java new file mode 100644 index 000000000..e9d6bc6a3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java @@ -0,0 +1,485 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fontbox.ttf.CMAPEncodingEntry; +import org.apache.fontbox.ttf.CMAPTable; +import org.apache.fontbox.ttf.GlyphData; +import org.apache.fontbox.ttf.GlyphTable; +import org.apache.fontbox.ttf.HeaderTable; +import org.apache.fontbox.ttf.HorizontalHeaderTable; +import org.apache.fontbox.ttf.HorizontalMetricsTable; +import org.apache.fontbox.ttf.NamingTable; +import org.apache.fontbox.ttf.NameRecord; +import org.apache.fontbox.ttf.OS2WindowsMetricsTable; +import org.apache.fontbox.ttf.PostScriptTable; +import org.apache.fontbox.ttf.TTFParser; +import org.apache.fontbox.ttf.TrueTypeFont; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.encoding.WinAnsiEncoding; +import org.apache.pdfbox.util.ResourceLoader; + +import java.awt.Font; +import java.awt.FontFormatException; + +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 + * @version $Revision: 1.17 $ + */ +public class PDTrueTypeFont extends PDSimpleFont +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDTrueTypeFont.class); + + /** + * This is the key to a property in the 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( + "org/apache/pdfbox/resources/PDFBox_External_Fonts.properties", + externalFonts ); + } + catch( IOException io ) + { + throw new RuntimeException( "Error loading font resources", io ); + } + } + + + /** + * 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 ) throws IOException + { + super( fontDictionary ); + ensureFontDescriptor(); + } + + /** + * 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 embedded into a document. + * + * @param doc The PDF document that will hold the embedded font. + * @param file a ttf file. + * @return a PDTrueTypeFont instance. + * @throws IOException If there is an error loading the data. + */ + public static PDTrueTypeFont loadTTF( PDDocument doc, File file ) throws IOException + { + return loadTTF( doc, new FileInputStream( file ) ); + } + + /** + * This will load a TTF to be embedded into a document. + * + * @param doc The PDF document that will hold the embedded font. + * @param stream a ttf input stream. + * @return a PDTrueTypeFont instance. + * @throws IOException If there is an error loading the data. + */ + public static PDTrueTypeFont loadTTF( PDDocument doc, InputStream stream ) throws IOException + { + PDTrueTypeFont retval = new PDTrueTypeFont(); + PDFontDescriptorDictionary fd = new PDFontDescriptorDictionary(); + retval.setFontDescriptor( fd ); + PDStream fontStream = new PDStream(doc, stream, false ); + fontStream.getStream().setInt( COSName.LENGTH1, fontStream.getByteArray().length ); + fontStream.addCompression(); + fd.setFontFile2( fontStream ); + // As the stream was close within the PDStream constructor, we have to recreate it + stream = fontStream.createInputStream(); + try + { + retval.loadDescriptorDictionary(fd, stream); + } + finally + { + stream.close(); + } + //only support winansi encoding right now, should really + //just use Identity-H with unicode mapping + retval.setFontEncoding( new WinAnsiEncoding() ); + retval.setEncoding(COSName.WIN_ANSI_ENCODING); + return retval; + } + + private void ensureFontDescriptor() throws IOException + { + if( getFontDescriptor() == null ) + { + PDFontDescriptorDictionary fdd = new PDFontDescriptorDictionary(); + setFontDescriptor(fdd); + InputStream ttfData = getExternalTTFData(); + if( ttfData != null ) + { + try + { + loadDescriptorDictionary(fdd, ttfData); + } + finally + { + ttfData.close(); + } + } + } + } + + private void loadDescriptorDictionary(PDFontDescriptorDictionary fd, InputStream ttfData) throws IOException + { + TrueTypeFont ttf = null; + try + { + TTFParser parser = new TTFParser(); + ttf = parser.parseTTF( ttfData ); + NamingTable naming = ttf.getNaming(); + List records = naming.getNameRecords(); + for( int i=0; i 0 ); + fd.setItalicAngle( ps.getItalicAngle() ); + + String[] names = ps.getGlyphNames(); + if( names != null ) + { + for( int i=0; i widths = new ArrayList(maxWidths); + float zero = 250; + for( int i=0; i= 0 + && widths.get( glyphToCCode[i]-firstChar) == zero ) + { + widths.set( glyphToCCode[i]-firstChar, widthValues[i]*scaling ); + } + } + setWidths( widths ); + setFirstChar( firstChar ); + setLastChar( firstChar + widths.size()-1 ); + + } + finally + { + if( ttf != null ) + { + ttf.close(); + } + } + } + + /** + * {@inheritDoc} + */ + public Font getawtFont() throws IOException + { + PDFontDescriptorDictionary fd = (PDFontDescriptorDictionary)getFontDescriptor(); + if( awtFont == null ) + { + PDStream ff2Stream = fd.getFontFile2(); + if( ff2Stream != null ) + { + try + { + // create a font with the embedded data + awtFont = Font.createFont( Font.TRUETYPE_FONT, ff2Stream.createInputStream() ); + } + catch( FontFormatException f ) + { + log.info("Can't read the embedded font " + fd.getFontName() ); + } + if (awtFont == null) + { + awtFont = FontManager.getAwtFont(fd.getFontName()); + if (awtFont != null) + { + log.info("Using font "+awtFont.getName()+ " instead"); + } + } + } + else + { + // check if the font is part of our environment + awtFont = FontManager.getAwtFont(fd.getFontName()); + if (awtFont == null) + { + log.info("Can't find the specified font " + fd.getFontName() ); + // check if there is a font mapping for an external font file + TrueTypeFont ttf = getExternalFontFile2( fd ); + if( ttf != null ) + { + try + { + awtFont = Font.createFont( Font.TRUETYPE_FONT, ttf.getOriginalData() ); + } + catch( FontFormatException f ) + { + log.info("Can't read the external fontfile " + fd.getFontName() ); + } + } + } + } + if (awtFont == null) + { + // we can't find anything, so we have to use the standard font + awtFont = FontManager.getStandardFont(); + log.info("Using font "+awtFont.getName()+ " instead"); + } + } + return awtFont; + } + + private InputStream getExternalTTFData() throws IOException + { + String ttfResource = externalFonts.getProperty( UNKNOWN_FONT ); + String baseFont = getBaseFont(); + if( baseFont != null && externalFonts.containsKey(baseFont) ) + { + ttfResource = externalFonts.getProperty( baseFont ); + } + return (ttfResource != null ? ResourceLoader.loadResource(ttfResource) : null); + } + + /** + * 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 TrueTypeFont getExternalFontFile2(PDFontDescriptorDictionary fd) + throws IOException + { + TrueTypeFont 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 ) + { + retval = (TrueTypeFont)loadedExternalFonts.get( baseFont ); + if( retval == null ) + { + TTFParser ttfParser = new TTFParser(); + InputStream fontStream = ResourceLoader.loadResource( fontResource ); + if( fontStream == null ) + { + throw new IOException( "Error missing font resource '" + externalFonts.get(baseFont) + "'" ); + } + retval = ttfParser.parseTTF( fontStream ); + loadedExternalFonts.put( baseFont, retval ); + } + } + } + + return retval; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType0Font.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType0Font.java new file mode 100644 index 000000000..aaf685111 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType0Font.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.awt.Font; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +/** + * This is implementation of the Type0 Font. + * See PDFBOX-605 + * for the related improvement issue. + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +public class PDType0Font extends PDSimpleFont +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDType0Font.class); + + private COSArray descendantFontArray; + private PDFont descendentFont; + private COSDictionary descendantFontDictionary; + private Font awtFont; + /** + * Constructor. + */ + public PDType0Font() + { + super(); + font.setItem( COSName.SUBTYPE, COSName.TYPE0 ); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDType0Font( COSDictionary fontDictionary ) + { + super( fontDictionary ); + descendantFontDictionary = (COSDictionary)getDescendantFonts().getObject( 0 ); + if (descendantFontDictionary != null) + { + try + { + descendentFont = PDFontFactory.createFont( descendantFontDictionary ); + } + catch (IOException exception) + { + log.error("Error while creating the descendant font!"); + } + } + } + + /** + * {@inheritDoc} + */ + public Font getawtFont() throws IOException + { + if (awtFont == null) + { + if (descendentFont != null) + { + awtFont = ((PDSimpleFont)descendentFont).getawtFont(); + } + if (awtFont == null) + { + awtFont = FontManager.getStandardFont(); + log.info("Using font "+awtFont.getName()+ " instead of "+descendentFont.getFontDescriptor().getFontName()); + } + } + return awtFont; + } + + /** + * This will get the fonts bounding box. + * + * @return The fonts bounding 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 + { + return descendentFont.getFontWidth( c, offset, length ); + } + + /** + * This will get the font height for a character. + * + * @param c The character code to get the height 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 getFontHeight( byte[] c, int offset, int length ) throws IOException + { + return descendentFont.getFontHeight( 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 + { + return descendentFont.getAverageFontWidth(); + } + + private COSArray getDescendantFonts() + { + if (descendantFontArray == null) + { + descendantFontArray = (COSArray)font.getDictionaryObject( COSName.DESCENDANT_FONTS ); + } + return descendantFontArray; + } + + /** + * {@inheritDoc} + */ + public float getFontWidth( int charCode ) + { + return descendentFont.getFontWidth(charCode); + } + + @Override + public String encode(byte[] c, int offset, int length) throws IOException + { + if (hasToUnicode()) + { + return super.encode(c, offset, length); + } + else + { + int result = cmap.lookupCID(c, offset, length); + if (result != -1) + { + return descendentFont.cmapEncoding(result, 2, true); + } + return null; + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1AfmPfbFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1AfmPfbFont.java new file mode 100644 index 000000000..4acfaf200 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1AfmPfbFont.java @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.apache.fontbox.afm.AFMParser; +import org.apache.fontbox.afm.CharMetric; +import org.apache.fontbox.afm.FontMetric; + +import org.apache.fontbox.pfb.PfbParser; + +import org.apache.pdfbox.encoding.AFMEncoding; +import org.apache.pdfbox.encoding.DictionaryEncoding; +import org.apache.pdfbox.encoding.Encoding; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; + +/** + * This is implementation of the Type1 Font + * with a afm and a pfb file. + * + * @author Michael Niedermair + * @version $Revision: 1.5 $ + */ +public class PDType1AfmPfbFont extends PDType1Font +{ + /** + * the buffersize. + */ + private static final int BUFFERSIZE = 0xffff; + + /** + * 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 + { + + PDFontDescriptorDictionary 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(); + setFontEncoding(afmToDictionary(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(); + Encoding encoding = getFontEncoding(); + int maxWidths = 256; + List widths = new ArrayList(maxWidths); + float zero = 250; + Iterator iter = listmetric.iterator(); + for( int i=0; i 0) + { + firstchar = Math.min(firstchar, n); + lastchar = Math.max(lastchar, n); + if (m.getWx() > 0) + { + float width = m.getWx(); + widths.set(n,new Float(width)); + // germandbls has 2 character codes !! Don't ask me why ..... + // StandardEncoding = 0373 = 251 + // WinANSIEncoding = 0337 = 223 + if (m.getName().equals("germandbls") && n != 223) + { + widths.set(0337,new Float(width)); + } + } + } + else + { + // my AFMPFB-Fonts has no character-codes for german umlauts + // so that I've to add them here by hand + if (m.getName().equals("adieresis")) + { + widths.set(0344,(Float)widths.get(encoding.getCode("a"))); + } + else if (m.getName().equals("odieresis")) + { + widths.set(0366,(Float)widths.get(encoding.getCode("o"))); + } + else if (m.getName().equals("udieresis")) + { + widths.set(0374,(Float)widths.get(encoding.getCode("u"))); + } + else if (m.getName().equals("Adieresis")) + { + widths.set(0304,(Float)widths.get(encoding.getCode("A"))); + } + else if (m.getName().equals("Odieresis")) + { + widths.set(0326,(Float)widths.get(encoding.getCode("O"))); + } + else if (m.getName().equals("Udieresis")) + { + widths.set(0334,(Float)widths.get(encoding.getCode("U"))); + } + } + } + setFirstChar(0); + setLastChar(255); + setWidths(widths); + } + + /* + * This will generate a Encoding from the AFM-Encoding, because the AFM-Enconding isn't exported to the pdf + * and consequently the StandardEncoding is used so that any special character is missing + * I've copied the code from the pdfbox-forum posted by V0JT4 and made some additions concerning german umlauts + * see also https://sourceforge.net/forum/message.php?msg_id=4705274 + */ + private DictionaryEncoding afmToDictionary(AFMEncoding encoding) throws java.io.IOException + { + COSArray array = new COSArray(); + array.add(COSInteger.ZERO); + for (int i = 0; i < 256; i++) + { + array.add(COSName.getPDFName(encoding.getName(i))); + } + // my AFMPFB-Fonts has no character-codes for german umlauts + // so that I've to add them here by hand + array.set( 0337+1, COSName.getPDFName("germandbls")); + array.set( 0344+1, COSName.getPDFName("adieresis")); + array.set( 0366+1, COSName.getPDFName("odieresis")); + array.set( 0374+1, COSName.getPDFName("udieresis")); + array.set( 0304+1, COSName.getPDFName("Adieresis")); + array.set( 0326+1, COSName.getPDFName("Odieresis")); + array.set( 0334+1, COSName.getPDFName("Udieresis")); + + COSDictionary dictionary = new COSDictionary(); + dictionary.setItem(COSName.NAME, COSName.ENCODING); + dictionary.setItem(COSName.DIFFERENCES, array); + dictionary.setItem(COSName.BASE_ENCODING, COSName.STANDARD_ENCODING); + return new DictionaryEncoding(dictionary); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1CFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1CFont.java new file mode 100644 index 000000000..67c8690a9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1CFont.java @@ -0,0 +1,608 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.pdmodel.font; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fontbox.afm.AFMParser; +import org.apache.fontbox.afm.FontMetric; +import org.apache.fontbox.cff.AFMFormatter; +import org.apache.fontbox.cff.charset.CFFCharset; +import org.apache.fontbox.cff.encoding.CFFEncoding; +import org.apache.fontbox.cff.CFFFont; +import org.apache.fontbox.cff.CFFParser; +import org.apache.fontbox.cff.Type1FontFormatter; +import org.apache.fontbox.util.BoundingBox; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.encoding.Encoding; +import org.apache.pdfbox.encoding.EncodingManager; +import org.apache.pdfbox.exceptions.WrappedIOException; +import org.apache.pdfbox.pdmodel.common.PDMatrix; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * This class represents a CFF/Type2 Font (aka Type1C Font). + * @author Villu Ruusmann + * @version $Revision: 10.0$ + */ +public class PDType1CFont extends PDSimpleFont +{ + private CFFFont cffFont = null; + + private Map codeToName = new HashMap(); + + private Map codeToCharacter = new HashMap(); + + private Map characterToCode = new HashMap(); + + private FontMetric fontMetric = null; + + private Font awtFont = null; + + private Map glyphWidths = new HashMap(); + + private Map glyphHeights = new HashMap(); + + private Float avgWidth = null; + + private PDRectangle fontBBox = null; + + private static final Log log = LogFactory.getLog(PDType1CFont.class); + + private static final byte[] SPACE_BYTES = {(byte)32}; + + private COSDictionary fontDict = null; + + /** + * Constructor. + * @param fontDictionary the corresponding dictionary + */ + public PDType1CFont( COSDictionary fontDictionary ) throws IOException + { + super( fontDictionary ); + fontDict = fontDictionary; + load(); + } + + /** + * {@inheritDoc} + */ + public String encode( byte[] bytes, int offset, int length ) throws IOException + { + String character = getCharacter(bytes, offset, length); + if( character == null ) + { + log.debug("No character for code " + (bytes[offset] & 0xff) + " in " + this.cffFont.getName()); + return null; + } + + return character; + } + + private String getCharacter( byte[] bytes, int offset, int length ) + { + if (length > 2) + { + return null; + } + int code = bytes[offset] & 0xff; + if (length == 2) + { + code = code * 256 + bytes[offset+1] & 0xff; + } + return (String)this.codeToCharacter.get(code); + } + + /** + * {@inheritDoc} + */ + public float getFontWidth( byte[] bytes, int offset, int length ) throws IOException + { + String name = getName(bytes, offset, length); + if( name == null && !Arrays.equals(SPACE_BYTES, bytes) ) + { + log.debug("No name for code " + (bytes[offset] & 0xff) + " in " + this.cffFont.getName()); + + return 0; + } + + Float width = (Float)this.glyphWidths.get(name); + if( width == null ) + { + width = Float.valueOf(getFontMetric().getCharacterWidth(name)); + this.glyphWidths.put(name, width); + } + + return width.floatValue(); + } + + /** + * {@inheritDoc} + */ + public float getFontHeight( byte[] bytes, int offset, int length ) throws IOException + { + String name = getName(bytes, offset, length); + if( name == null ) + { + log.debug("No name for code " + (bytes[offset] & 0xff) + " in " + this.cffFont.getName()); + + return 0; + } + + Float height = (Float)this.glyphHeights.get(name); + if( height == null ) + { + height = Float.valueOf(getFontMetric().getCharacterHeight(name)); + this.glyphHeights.put(name, height); + } + + return height.floatValue(); + } + + private String getName( byte[] bytes, int offset, int length ) + { + if (length > 2) + { + return null; + } + + int code = bytes[offset] & 0xff; + if (length == 2) + { + code = code * 256 + bytes[offset+1] & 0xff; + } + + return (String)this.codeToName.get(code); + } + + /** + * {@inheritDoc} + */ + public float getStringWidth( String string ) throws IOException + { + float width = 0; + + for( int i = 0; i < string.length(); i++ ) + { + String character = string.substring(i, i + 1); + + Integer code = getCode(character); + if( code == null ) + { + log.debug("No code for character " + character); + + return 0; + } + + width += getFontWidth(new byte[]{(byte)code.intValue()}, 0, 1); + } + + return width; + } + + private Integer getCode( String character ) + { + return (Integer)this.characterToCode.get(character); + } + + + /** + * {@inheritDoc} + */ + public float getAverageFontWidth() throws IOException + { + if( this.avgWidth == null ) + { + this.avgWidth = Float.valueOf(getFontMetric().getAverageCharacterWidth()); + } + + return this.avgWidth.floatValue(); + } + + /** + * {@inheritDoc} + */ + public PDRectangle getFontBoundingBox() throws IOException + { + if( this.fontBBox == null ) + { + this.fontBBox = new PDRectangle(getFontMetric().getFontBBox()); + } + + return this.fontBBox; + } + + /** + * {@inheritDoc} + */ + public PDMatrix getFontMatrix() + { + if( fontMatrix == null ) + { + List numbers = (List)this.cffFont.getProperty("FontMatrix"); + if( numbers != null && numbers.size() == 6 ) + { + COSArray array = new COSArray(); + for(Number number : numbers) + { + array.add(new COSFloat(number.floatValue())); + } + fontMatrix = new PDMatrix(array); + } + else + { + super.getFontMatrix(); + } + } + return fontMatrix; + } + + /** + * {@inheritDoc} + */ + public Font getawtFont() throws IOException + { + if (awtFont == null) + { + this.awtFont = prepareAwtFont(this.cffFont); + } + return awtFont; + } + + private FontMetric getFontMetric() + { + if (fontMetric == null) + { + try + { + fontMetric = prepareFontMetric(cffFont); + } + catch (IOException exception) + { + log.error("An error occured while extracting the font metrics!", exception); + } + } + return fontMetric; + } + + private void load() throws IOException + { + byte[] cffBytes = loadBytes(); + + CFFParser cffParser = new CFFParser(); + List fonts = cffParser.parse(cffBytes); + + this.cffFont = (CFFFont)fonts.get(0); + + CFFEncoding encoding = this.cffFont.getEncoding(); + PDFEncoding pdfEncoding = new PDFEncoding(encoding); + + CFFCharset charset = this.cffFont.getCharset(); + PDFCharset pdfCharset = new PDFCharset(charset); + + Map charStringsDict = this.cffFont.getCharStringsDict(); + Map pdfCharStringsDict = new LinkedHashMap(); + pdfCharStringsDict.put(".notdef", charStringsDict.get(".notdef")); + + Map codeToNameMap = new LinkedHashMap(); + + Collection mappings = this.cffFont.getMappings(); + for( Iterator it = mappings.iterator(); it.hasNext();) + { + CFFFont.Mapping mapping = it.next(); + Integer code = Integer.valueOf(mapping.getCode()); + String name = mapping.getName(); + codeToNameMap.put(code, name); + } + + Set knownNames = new HashSet(codeToNameMap.values()); + + Map codeToNameOverride = loadOverride(); + for( Iterator> it = (codeToNameOverride.entrySet()).iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + Integer code = (Integer)entry.getKey(); + String name = (String)entry.getValue(); + if(knownNames.contains(name)) + { + codeToNameMap.put(code, name); + } + } + + Map nameToCharacter; + try + { + // TODO remove access by reflection + Field nameToCharacterField = Encoding.class.getDeclaredField("NAME_TO_CHARACTER"); + nameToCharacterField.setAccessible(true); + nameToCharacter = (Map)nameToCharacterField.get(null); + } + catch( Exception e ) + { + throw new RuntimeException(e); + } + + for( Iterator> it = (codeToNameMap.entrySet()).iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + Integer code = (Integer)entry.getKey(); + String name = (String)entry.getValue(); + String uniName = "uni"; + String character = (String)nameToCharacter.get(name); + if( character != null ) + { + for( int j = 0; j < character.length(); j++ ) + { + uniName += hexString(character.charAt(j), 4); + } + } + else + { + uniName += hexString(code.intValue(), 4); + character = String.valueOf((char)code.intValue()); + } + pdfEncoding.register(code.intValue(), code.intValue()); + pdfCharset.register(code.intValue(), uniName); + this.codeToName.put(code, uniName); + this.codeToCharacter.put(code, character); + this.characterToCode.put(character, code); + pdfCharStringsDict.put(uniName, charStringsDict.get(name)); + } + + this.cffFont.setEncoding(pdfEncoding); + this.cffFont.setCharset(pdfCharset); + charStringsDict.clear(); + charStringsDict.putAll(pdfCharStringsDict); + Number defaultWidthX = (Number)this.cffFont.getProperty("defaultWidthX"); + this.glyphWidths.put(null, Float.valueOf(defaultWidthX.floatValue())); + } + + private byte[] loadBytes() throws IOException + { + PDFontDescriptor fd = getFontDescriptor(); + if( fd != null && fd instanceof PDFontDescriptorDictionary) + { + PDStream ff3Stream = ((PDFontDescriptorDictionary)fd).getFontFile3(); + if( ff3Stream != null ) + { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + InputStream is = ff3Stream.createInputStream(); + try + { + byte[] buf = new byte[512]; + while(true) + { + int count = is.read(buf); + if( count < 0 ) + { + break; + } + os.write(buf, 0, count); + } + } + finally + { + is.close(); + } + + return os.toByteArray(); + } + } + + throw new IOException(); + } + + private Map loadOverride() throws IOException + { + Map result = new LinkedHashMap(); + COSBase encoding = fontDict.getDictionaryObject(COSName.ENCODING); + if( encoding instanceof COSName ) + { + COSName name = (COSName)encoding; + result.putAll(loadEncoding(name)); + } + else if( encoding instanceof COSDictionary ) + { + COSDictionary encodingDic = (COSDictionary)encoding; + COSName baseName = (COSName)encodingDic.getDictionaryObject(COSName.BASE_ENCODING); + if( baseName != null ) + { + result.putAll(loadEncoding(baseName)); + } + COSArray differences = (COSArray)encodingDic.getDictionaryObject(COSName.DIFFERENCES); + if( differences != null ) + { + result.putAll(loadDifferences(differences)); + } + } + + return result; + } + + private Map loadEncoding(COSName name) throws IOException + { + Map result = new LinkedHashMap(); + Encoding encoding = EncodingManager.INSTANCE.getEncoding(name); + for( Iterator> it = (encoding.getCodeToNameMap().entrySet()).iterator(); + it.hasNext();) + { + Map.Entry entry = it.next(); + result.put(entry.getKey(), (entry.getValue())); + } + + return result; + } + + private Map loadDifferences(COSArray differences) + { + Map result = new LinkedHashMap(); + Integer code = null; + for( int i = 0; i < differences.size(); i++) + { + COSBase element = differences.get(i); + if( element instanceof COSNumber ) + { + COSNumber number = (COSNumber)element; + code = Integer.valueOf(number.intValue()); + } + else + { + if( element instanceof COSName ) + { + COSName name = (COSName)element; + result.put(code, name.getName()); + code = Integer.valueOf(code.intValue() + 1); + } + } + } + return result; + } + + + private static String hexString( int code, int length ) + { + String string = Integer.toHexString(code); + while(string.length() < length) + { + string = ("0" + string); + } + + return string; + } + + private FontMetric prepareFontMetric( CFFFont font ) throws IOException + { + byte[] afmBytes = AFMFormatter.format(font); + + InputStream is = new ByteArrayInputStream(afmBytes); + try + { + AFMParser afmParser = new AFMParser(is); + afmParser.parse(); + + FontMetric result = afmParser.getResult(); + + // Replace default FontBBox value with a newly computed one + BoundingBox bounds = result.getFontBBox(); + List numbers = Arrays.asList( + Integer.valueOf((int)bounds.getLowerLeftX()), + Integer.valueOf((int)bounds.getLowerLeftY()), + Integer.valueOf((int)bounds.getUpperRightX()), + Integer.valueOf((int)bounds.getUpperRightY()) + ); + font.addValueToTopDict("FontBBox", numbers); + + return result; + } + finally + { + is.close(); + } + } + + private static Font prepareAwtFont( CFFFont font ) throws IOException + { + byte[] type1Bytes = Type1FontFormatter.format(font); + + InputStream is = new ByteArrayInputStream(type1Bytes); + try + { + return Font.createFont(Font.TYPE1_FONT, is); + } + catch( FontFormatException ffe ) + { + throw new WrappedIOException(ffe); + } + finally + { + is.close(); + } + } + + /** + * This class represents a PDFEncoding. + * + */ + private static class PDFEncoding extends CFFEncoding + { + + private PDFEncoding( CFFEncoding parent ) + { + Iterator parentEntries = parent.getEntries().iterator(); + while(parentEntries.hasNext()) + { + addEntry(parentEntries.next()); + } + } + + public boolean isFontSpecific() + { + return true; + } + + } + + /** + * This class represents a PDFCharset. + * + */ + private static class PDFCharset extends CFFCharset + { + private PDFCharset( CFFCharset parent ) + { + Iterator parentEntries = parent.getEntries().iterator(); + while(parentEntries.hasNext()) + { + addEntry(parentEntries.next()); + } + } + + public boolean isFontSpecific() + { + return true; + } + + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1Font.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1Font.java new file mode 100644 index 000000000..b6c757ea8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType1Font.java @@ -0,0 +1,432 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fontbox.afm.FontMetric; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.encoding.AFMEncoding; +import org.apache.pdfbox.encoding.Encoding; +import org.apache.pdfbox.encoding.EncodingManager; +import org.apache.pdfbox.encoding.Type1Encoding; +import org.apache.pdfbox.encoding.WinAnsiEncoding; +import org.apache.pdfbox.pdmodel.common.PDMatrix; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * This is implementation of the Type1 Font. + * + * @author Ben Litchfield + * @version $Revision: 1.11 $ + */ +public class PDType1Font extends PDSimpleFont +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDType1Font.class); + + private PDType1CFont type1CFont = null; + /** + * 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.TYPE1 ); + } + + /** + * Constructor. + * + * @param fontDictionary The font dictionary according to the PDF specification. + */ + public PDType1Font( COSDictionary fontDictionary ) + { + super( fontDictionary ); + PDFontDescriptor fd = getFontDescriptor(); + if (fd != null && fd instanceof PDFontDescriptorDictionary) + { + // a Type1 font may contain a Type1C font + PDStream fontFile3 = ((PDFontDescriptorDictionary)fd).getFontFile3(); + if (fontFile3 != null) + { + try + { + type1CFont = new PDType1CFont( super.font ); + } + catch (IOException exception) + { + log.info("Can't read the embedded type1C font " + fd.getFontName() ); + } + } + } + } + + /** + * Constructor. + * + * @param baseFont The base font for this font. + */ + public PDType1Font( String baseFont ) + { + this(); + setBaseFont( baseFont ); + setFontEncoding(new WinAnsiEncoding()); + setEncoding(COSName.WIN_ANSI_ENCODING); + } + + /** + * 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] ); + } + + /** + * {@inheritDoc} + */ + public Font getawtFont() throws IOException + { + if( awtFont == null ) + { + if (type1CFont != null) + { + awtFont = type1CFont.getawtFont(); + } + else + { + String baseFont = getBaseFont(); + PDFontDescriptor fd = getFontDescriptor(); + if (fd != null && fd instanceof PDFontDescriptorDictionary) + { + PDFontDescriptorDictionary fdDictionary = (PDFontDescriptorDictionary)fd; + if( fdDictionary.getFontFile() != null ) + { + try + { + // create a type1 font with the embedded data + awtFont = Font.createFont( Font.TYPE1_FONT, fdDictionary.getFontFile().createInputStream() ); + } + catch (FontFormatException e) + { + log.info("Can't read the embedded type1 font " + fd.getFontName() ); + } + } + if (awtFont == null) + { + // check if the font is part of our environment + awtFont = FontManager.getAwtFont(fd.getFontName()); + if (awtFont == null) + { + log.info("Can't find the specified font " + fd.getFontName() ); + } + } + } + else + { + // check if the font is part of our environment + awtFont = FontManager.getAwtFont(baseFont); + if (awtFont == null) + { + log.info("Can't find the specified basefont " + baseFont ); + } + } + } + if (awtFont == null) + { + // we can't find anything, so we have to use the standard font + awtFont = FontManager.getStandardFont(); + log.info("Using font "+awtFont.getName()+ " instead"); + } + } + return awtFont; + } + + protected void determineEncoding() + { + super.determineEncoding(); + Encoding fontEncoding = getFontEncoding(); + if(fontEncoding == null) + { + FontMetric metric = getAFM(); + if (metric != null) + { + fontEncoding = new AFMEncoding( metric ); + } + setFontEncoding(fontEncoding); + } + getEncodingFromFont(getFontEncoding() == null); + } + + /** + * Tries to get the encoding for the type1 font. + * + */ + private void getEncodingFromFont(boolean extractEncoding) + { + // This whole section of code needs to be replaced with an actual type1 font parser!! + // Get the font program from the embedded type font. + PDFontDescriptor fontDescriptor = getFontDescriptor(); + if( fontDescriptor != null && fontDescriptor instanceof PDFontDescriptorDictionary) + { + PDStream fontFile = ((PDFontDescriptorDictionary)fontDescriptor).getFontFile(); + if( fontFile != null ) + { + BufferedReader in = null; + try + { + in = new BufferedReader(new InputStreamReader(fontFile.createInputStream())); + + // this section parses the font program stream searching for a /Encoding entry + // if it contains an array of values a Type1Encoding will be returned + // if it encoding contains an encoding name the corresponding Encoding will be returned + String line = ""; + Type1Encoding encoding = null; + while( (line = in.readLine()) != null) + { + if (extractEncoding) + { + if (line.startsWith("currentdict end")) { + if (encoding != null) + setFontEncoding(encoding); + break; + } + if (line.startsWith("/Encoding")) + { + if(line.contains("array")) + { + StringTokenizer st = new StringTokenizer(line); + // ignore the first token + st.nextElement(); + int arraySize = Integer.parseInt(st.nextToken()); + encoding = new Type1Encoding(arraySize); + } + // if there is already an encoding, we don't need to + // assign another one + else if (getFontEncoding() == null) + { + StringTokenizer st = new StringTokenizer(line); + // ignore the first token + st.nextElement(); + String type1Encoding = st.nextToken(); + setFontEncoding( + EncodingManager.INSTANCE.getEncoding( + COSName.getPDFName(type1Encoding))); + break; + } + } + else if (line.startsWith("dup")) { + StringTokenizer st = new StringTokenizer(line.replaceAll("/"," /")); + // ignore the first token + st.nextElement(); + int index = Integer.parseInt(st.nextToken()); + String name = st.nextToken(); + if(encoding == null) + log.warn("Unable to get character encoding. Encoding defintion found without /Encoding line."); + else + encoding.addCharacterEncoding(index, name.replace("/", "")); + } + } + // according to the pdf reference, all font matrices should be same, except for type 3 fonts. + // but obviously there are some type1 fonts with different matrix values, see pdf sample + // attached to PDFBOX-935 + if (line.startsWith("/FontMatrix")) + { + String matrixValues = line.substring(line.indexOf("[")+1,line.lastIndexOf("]")); + StringTokenizer st = new StringTokenizer(matrixValues); + COSArray array = new COSArray(); + if (st.countTokens() >= 6) + { + try + { + for (int i=0;i<6;i++) + { + COSFloat floatValue = new COSFloat(Float.parseFloat(st.nextToken())); + array.add(floatValue); + } + fontMatrix = new PDMatrix(array); + } + catch (NumberFormatException exception) + { + log.error("Can't read the fontmatrix from embedded font file!"); + } + } + } + } + } + catch(IOException exception) + { + log.error("Error: Could not extract the encoding from the embedded type1 font."); + } + finally + { + if (in != null) + { + try + { + in.close(); + } + catch(IOException exception) + { + log.error("An error occurs while closing the stream used to read the embedded type1 font."); + } + } + } + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String encode(byte[] c, int offset, int length) throws IOException + { + if (type1CFont != null && getFontEncoding() == null) + { + return type1CFont.encode(c, offset, length); + } + else + { + return super.encode(c, offset, length); + } + } + + /** + * {@inheritDoc} + */ + @Override + public PDMatrix getFontMatrix() + { + if (type1CFont != null) + { + return type1CFont.getFontMatrix(); + } + else + { + return super.getFontMatrix(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType3Font.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType3Font.java new file mode 100644 index 000000000..cdbd3a706 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/PDType3Font.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.common.PDMatrix; + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.geom.AffineTransform; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; + +/** + * This is implementation of the Type3 Font. + * + * @author Ben Litchfield + * @version $Revision: 1.8 $ + */ +public class PDType3Font extends PDSimpleFont +{ + //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.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.CHAR_PROCS ); + COSStream stream = (COSStream)charProcs.getDictionaryObject( COSName.getPDFName( "" + character ) ); + if( stream != null ) + { + Type3StreamParser parser = new Type3StreamParser(); + retval = parser.createImage( stream ); + images.put( c, retval ); + } + else + { + //stream should not be null!! + } + } + return retval; + + } + + /** + * {@inheritDoc} + */ + public void drawString( String string, Graphics g, float fontSize, AffineTransform at, float x, float y ) + throws IOException + { + //if( string.equals( "V" )|| string.equals( "o" ) ) + { + for(int i=0; i 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/Type3StreamParser.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/Type3StreamParser.java new file mode 100644 index 000000000..25cf5f2b1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/Type3StreamParser.java @@ -0,0 +1,594 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.font; + +import java.awt.Image; + +import java.io.IOException; + +import java.util.List; + +import org.apache.fontbox.util.BoundingBox; + +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.graphics.xobject.PDInlinedImage; + +import org.apache.pdfbox.util.ImageParameters; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.PDFStreamEngine; + +/** + * This class will handle creating an image for a type 3 glyph. + * + * @author Ben Litchfield + * @version $Revision: 1.10 $ + */ +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( "" ); + 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( "" ); + 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( "" ); + } + else if( operation.equals( "J" ) ) + { + //Set the line cap style in the graphics state + //System.out.println( "" ); + } + 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( "" ); + } + else if( operation.equals( "M" ) ) + { + //System.out.println( "" ); + } + 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( "" ); + } + 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( "" ); + } + else if( operation.equals( "SC" ) ) + { + //set color for stroking operations + //System.out.println( "" ); + } + 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(""); + } + //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(""); + } + 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(""); + } + 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(""); + } + 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(""); + } + + //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(""); + } + } + else if( operation.equals( "TJ" ) ) + { + Matrix td = new Matrix(); + + COSArray array = (COSArray)arguments.get( 0 ); + for( int i=0; i" ); + } + 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(""); + } + } + 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(""); + } + } + 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(""); + } + + 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( "" ); + } + else if( operation.equals( "Ts" ) ) + { + //Set text rise + //System.out.println( "" ); + } + else if( operation.equals( "Tw" ) ) + { + //set word spacing + COSNumber wordSpacing = (COSNumber)arguments.get( 0 ); + if (log.isDebugEnabled()) + { + log.debug(""); + } + 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( "" ); + } + else if( operation.equals( "W" ) ) + { + //Set clipping path using nonzero winding number rule + //System.out.println( "" ); + } + 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() ); + }*/ + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/package.html new file mode 100644 index 000000000..aa9081214 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/font/package.html @@ -0,0 +1,25 @@ + + + + + + + +Classes to deal with font functionality in a PDF Document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java new file mode 100644 index 000000000..7238dff25 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDExtendedGraphicsState.java @@ -0,0 +1,564 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +import java.io.IOException; + + +/** + * 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 + * @version $Revision: 1.5 $ + */ +public class PDExtendedGraphicsState implements COSObjectable +{ + /** + * 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.EXT_G_STATE ); + } + + /** + * 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 + { + for( COSName key : graphicsState.keySet() ) + { + if( key.equals( COSName.LW ) ) + { + gs.setLineWidth( getLineWidth().doubleValue() ); + } + else if( key.equals( COSName.LC ) ) + { + gs.setLineCap( getLineCapStyle() ); + } + else if( key.equals( COSName.LJ ) ) + { + gs.setLineJoin( getLineJoinStyle() ); + } + else if( key.equals( COSName.ML ) ) + { + gs.setMiterLimit( getMiterLimit().doubleValue() ); + } + else if( key.equals( COSName.D ) ) + { + gs.setLineDashPattern( getLineDashPattern() ); + } + else if( key.equals( COSName.RI ) ) + { + gs.setRenderingIntent( getRenderingIntent() ); + } + else if( key.equals( COSName.OPM ) ) + { + gs.setOverprintMode( getOverprintMode().doubleValue() ); + } + else if( key.equals( COSName.FONT ) ) + { + PDFontSetting setting = getFontSetting(); + gs.getTextState().setFont( setting.getFont() ); + gs.getTextState().setFontSize( setting.getFontSize() ); + } + else if( key.equals( COSName.FL ) ) + { + gs.setFlatness( getFlatnessTolerance().floatValue() ); + } + else if( key.equals( COSName.SM ) ) + { + gs.setSmoothness( getSmoothnessTolerance().floatValue() ); + } + else if( key.equals( COSName.SA ) ) + { + gs.setStrokeAdjustment( getAutomaticStrokeAdjustment() ); + } + else if( key.equals( COSName.CA ) ) + { + gs.setAlphaConstants( getStrokingAlpaConstant().floatValue() ); + } + else if( key.equals( COSName.CA_NS ) ) + { + gs.setNonStrokeAlphaConstants(getNonStrokingAlpaConstant().floatValue() ); + } + else if( key.equals( COSName.AIS ) ) + { + gs.setAlphaSource( getAlphaSourceFlag() ); + } + else if( key.equals( COSName.TK ) ) + { + gs.getTextState().setKnockoutFlag( getTextKnockoutFlag() ); + } + } + } + + /** + * 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( COSName.LW ); + } + + /** + * This will set the line width. + * + * @param width The line width for the object. + */ + public void setLineWidth( Float width ) + { + setFloatItem( COSName.LW, width ); + } + + /** + * This will get the line cap style. + * + * @return null or the LC value of the dictionary. + */ + public int getLineCapStyle() + { + return graphicsState.getInt( COSName.LC ); + } + + /** + * This will set the line cap style for the graphics state. + * + * @param style The new line cap style to set. + */ + public void setLineCapStyle( int style ) + { + graphicsState.setInt( COSName.LC, style ); + } + + /** + * This will get the line join style. + * + * @return null or the LJ value in the dictionary. + */ + public int getLineJoinStyle() + { + return graphicsState.getInt( COSName.LJ ); + } + + /** + * This will set the line join style. + * + * @param style The new line join style. + */ + public void setLineJoinStyle( int style ) + { + graphicsState.setInt( COSName.LJ, style ); + } + + + /** + * This will get the miter limit. + * + * @return null or the ML value in the dictionary. + */ + public Float getMiterLimit() + { + return getFloatItem( COSName.ML ); + } + + /** + * This will set the miter limit for the graphics state. + * + * @param miterLimit The new miter limit value + */ + public void setMiterLimit( Float miterLimit ) + { + setFloatItem( COSName.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( COSName.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( COSName.D, dashPattern.getCOSObject() ); + } + + /** + * This will get the rendering intent. + * + * @return null or the RI value in the dictionary. + */ + public String getRenderingIntent() + { + return graphicsState.getNameAsString( "RI" ); + } + + /** + * This will set the rendering intent for the graphics state. + * + * @param ri The new rendering intent + */ + public void setRenderingIntent( String ri ) + { + graphicsState.setName( "RI", ri ); + } + + /** + * This will get the overprint control. + * + * @return The overprint control or null if one has not been set. + */ + public boolean getStrokingOverprintControl() + { + return graphicsState.getBoolean( COSName.OP, false ); + } + + /** + * This will get the overprint control(OP). + * + * @param op The overprint control. + */ + public void setStrokingOverprintControl( boolean op ) + { + graphicsState.setBoolean( COSName.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() + { + return graphicsState.getBoolean( COSName.OP_NS, getStrokingOverprintControl() ); + } + + /** + * This will get the overprint control(OP). + * + * @param op The overprint control. + */ + public void setNonStrokingOverprintControl( boolean op ) + { + graphicsState.setBoolean( COSName.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( COSName.OPM ); + } + + /** + * This will get the overprint mode(OPM). + * + * @param overprintMode The overprint mode + */ + public void setOverprintMode( Float overprintMode ) + { + setFloatItem( COSName.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( COSName.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( COSName.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( COSName.FL ); + } + + /** + * This will get the flatness tolerance. + * + * @param flatness The new flatness tolerance + */ + public void setFlatnessTolerance( Float flatness ) + { + setFloatItem( COSName.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( COSName.SM ); + } + + /** + * This will get the smoothness tolerance. + * + * @param smoothness The new smoothness tolerance + */ + public void setSmoothnessTolerance( Float smoothness ) + { + setFloatItem( COSName.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 graphicsState.getBoolean( COSName.SA,false ); + } + + /** + * This will get the automatic stroke adjustment flag. + * + * @param sa The new automatic stroke adjustment flag. + */ + public void setAutomaticStrokeAdjustment( boolean sa ) + { + graphicsState.setBoolean( COSName.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( COSName.CA ); + } + + /** + * This will get the stroking alpha constant. + * + * @param alpha The new stroking alpha constant. + */ + public void setStrokingAlphaConstant( Float alpha ) + { + setFloatItem( COSName.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( COSName.CA_NS ); + } + + /** + * This will get the non stroking alpha constant. + * + * @param alpha The new non stroking alpha constant. + */ + public void setNonStrokingAlphaConstant( Float alpha ) + { + setFloatItem( COSName.CA_NS, alpha ); + } + + /** + * This will get the alpha source flag. + * + * @return The alpha source flag. + */ + public boolean getAlphaSourceFlag() + { + return graphicsState.getBoolean( COSName.AIS, false ); + } + + /** + * This will get the alpha source flag. + * + * @param alpha The alpha source flag. + */ + public void setAlphaSourceFlag( boolean alpha ) + { + graphicsState.setBoolean( COSName.AIS, alpha ); + } + + /** + * This will get the text knockout flag. + * + * @return The text knockout flag. + */ + public boolean getTextKnockoutFlag() + { + return graphicsState.getBoolean( COSName.TK,true ); + } + + /** + * This will get the text knockout flag. + * + * @param tk The text knockout flag. + */ + public void setTextKnockoutFlag( boolean tk ) + { + graphicsState.setBoolean( COSName.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() ) ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDFontSetting.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDFontSetting.java new file mode 100644 index 000000000..77c6e1c4b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDFontSetting.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.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 + * @version $Revision: 1.4 $ + */ +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; + } + + /** + * {@inheritDoc} + */ + 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 ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDGraphicsState.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDGraphicsState.java new file mode 100644 index 000000000..508ac555c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDGraphicsState.java @@ -0,0 +1,507 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics; + +import java.awt.*; +import java.awt.geom.GeneralPath; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.text.PDTextState; +import org.apache.pdfbox.util.Matrix; + +/** + * This class will hold the current state of the graphics parameters when executing a + * content stream. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +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 PDColorState strokingColor = new PDColorState(); + private PDColorState nonStrokingColor = new PDColorState(); + 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 = 1.0; + private double nonStrokingAlphaConstants = 1.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; + + private GeneralPath currentClippingPath; + + /** + * Default constructor. + */ + public PDGraphicsState() + { + } + + /** + * Constructor with a given pagesize to initialize the clipping path. + * @param page the size of the page + */ + public PDGraphicsState(PDRectangle page) + { + currentClippingPath = new GeneralPath(new Rectangle(page.createDimension())); + if (page.getLowerLeftX() != 0 || page.getLowerLeftY() != 0) + { + //Compensate for offset + this.currentTransformationMatrix = this.currentTransformationMatrix.multiply( + Matrix.getTranslatingInstance(-page.getLowerLeftX(), -page.getLowerLeftY())); + } + } + + /** + * 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 stroke alpha constants property. + * + * @return The value of the stroke alpha constants parameter. + */ + public double getAlphaConstants() + { + return alphaConstants; + } + + /** + * set the value of the stroke alpha constants property. + * + * @param value The value of the stroke alpha constants parameter. + */ + public void setAlphaConstants(double value) + { + alphaConstants = value; + } + + /** + * Get the value of the non-stroke alpha constants property. + * + * @return The value of the non-stroke alpha constants parameter. + */ + public double getNonStrokeAlphaConstants() + { + return nonStrokingAlphaConstants; + } + + /** + * set the value of the non-stroke alpha constants property. + * + * @param value The value of the non-stroke alpha constants parameter. + */ + public void setNonStrokeAlphaConstants(double value) + { + nonStrokingAlphaConstants = value; + } + + /** + * get the value of the stroke alpha source property. + * + * @return The value of the stroke 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; + } + + /** + * {@inheritDoc} + */ + public Object clone() + { + PDGraphicsState clone = null; + try + { + clone = (PDGraphicsState)super.clone(); + clone.setTextState( (PDTextState)textState.clone() ); + clone.setCurrentTransformationMatrix( currentTransformationMatrix.copy() ); + clone.strokingColor = (PDColorState)strokingColor.clone(); + clone.nonStrokingColor = ((PDColorState)nonStrokingColor.clone()); + if( lineDashPattern != null ) + { + clone.setLineDashPattern( (PDLineDashPattern)lineDashPattern.clone() ); + } + if (currentClippingPath != null) + { + clone.setCurrentClippingPath((GeneralPath)currentClippingPath.clone()); + } + } + catch( CloneNotSupportedException e ) + { + e.printStackTrace(); + } + return clone; + } + + /** + * Returns the stroking color state. + * + * @return stroking color state + */ + public PDColorState getStrokingColor() + { + return strokingColor; + } + + /** + * Returns the non-stroking color state. + * + * @return non-stroking color state + */ + public PDColorState getNonStrokingColor() + { + return nonStrokingColor; + } + + /** + * This will set the current clipping path. + * + * @param pCurrentClippingPath The current clipping path. + * + */ + public void setCurrentClippingPath(Shape pCurrentClippingPath) + { + if (pCurrentClippingPath != null) + { + if (pCurrentClippingPath instanceof GeneralPath) + { + currentClippingPath = (GeneralPath)pCurrentClippingPath; + } + else + { + currentClippingPath = new GeneralPath(); + currentClippingPath.append(pCurrentClippingPath,false); + } + } + else + { + currentClippingPath = null; + } + } + + /** + * This will get the current clipping path. + * + * @return The current clipping path. + */ + public Shape getCurrentClippingPath() + { + return currentClippingPath; + } + + public Composite getStrokeJavaComposite() { + + return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) alphaConstants); + } + + public Composite getNonStrokeJavaComposite() { + + return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) nonStrokingAlphaConstants); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDLineDashPattern.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDLineDashPattern.java new file mode 100644 index 000000000..a43807794 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDLineDashPattern.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.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 + * @version $Revision: 1.7 $ + */ +public class PDLineDashPattern implements COSObjectable, Cloneable +{ + 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( COSInteger.ZERO ); + } + + /** + * Constructs a line dash pattern from an existing array. + * + * @param ldp The existing line dash pattern. + */ + public PDLineDashPattern( COSArray ldp ) + { + lineDashPattern = ldp; + } + + /** + * Constructs a line dash pattern from an existing array. + * + * @param ldp The existing line dash pattern. + * @param phase The phase for the line dash pattern. + */ + public PDLineDashPattern( COSArray ldp, int phase ) + { + lineDashPattern = new COSArray(); + lineDashPattern.add( ldp ); + lineDashPattern.add( COSInteger.get( phase ) ); + } + + /** + * {@inheritDoc} + */ + public Object clone() + { + PDLineDashPattern pattern = null; + try + { + pattern = (PDLineDashPattern)super.clone(); + pattern.setDashPattern(getDashPattern()); + pattern.setPhaseStart(getPhaseStart()); + } + catch(CloneNotSupportedException exception) + { + exception.printStackTrace(); + } + return pattern; + } + + /** + * {@inheritDoc} + */ + 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, 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 ) ); + } + + /** + * Checks if the dashPattern is empty or all values equals 0. + * + * @return true if the dashPattern is empty or all values equals 0 + */ + public boolean isDashPatternEmpty() + { + float[] dashPattern = getCOSDashPattern().toFloatArray(); + boolean dashPatternEmpty = true; + if (dashPattern != null) + { + int arraySize = dashPattern.length; + for(int i=0;i 0) + { + dashPatternEmpty = false; + break; + } + } + } + return dashPatternEmpty; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDShading.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDShading.java new file mode 100644 index 000000000..091b2b7d0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/PDShading.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSBoolean; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.common.function.PDFunction; + + + +import java.io.IOException; + +/** + * This class represents a Shading Pattern color space. + * See section 4.6.3 of the PDF 1.7 specification. + * + * @author Daniel wilson + * @version $Revision: 1.0 $ + */ +public class PDShading implements COSObjectable +{ + private COSDictionary DictShading; + private COSName shadingname; + private COSArray domain = null; + private COSArray extend = null; + private PDFunction function = null; + private PDColorSpace colorspace = null; + + /** + * The name of this object. + */ + public static final String NAME = "Shading"; + + /** + * Default constructor. + */ + public PDShading() + { + DictShading = new COSDictionary(); + //DictShading.add( COSName.getPDFName( NAME ) ); + } + + /** + * Constructor. + * + * @param shading The shading dictionary. + */ + public PDShading(COSName name, COSDictionary shading) + { + DictShading = shading; + shadingname = name; + } + + /** + * This will return the name of the object. + * + * @return The name of the object. + */ + 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 COSName.SHADING; + } + + /** + * This will return the name of this particular shading dictionary + * + * @return The name of the shading dictionary + */ + public COSName getShadingName() + { + return shadingname; + } + + /** + * This will return the ShadingType -- an integer between 1 and 7 that specifies the gradient type. + * Required in all Shading Dictionaries. + * + * @return The Shading Type + */ + public int getShadingType() + { + return DictShading.getInt(COSName.SHADING_TYPE); + } + + /** + * This will return the Color Space. + * Required in all Shading Dictionaries. + * + * @return The Color Space of the shading dictionary + */ + public PDColorSpace getColorSpace() throws IOException + { + if (colorspace == null) + { + colorspace = PDColorSpaceFactory.createColorSpace(DictShading.getDictionaryObject(COSName.COLORSPACE)); + } + return colorspace; + } + + /** + * This will return a boolean flag indicating whether to antialias the shading pattern. + * + * @return The antialias flag, defaulting to False + */ + public boolean getAntiAlias() + { + return DictShading.getBoolean(COSName.ANTI_ALIAS,false); + } + + /** + * Returns the coordinate array used by several of the gradient types. Interpretation depends on the ShadingType. + * + * @return The coordinate array. + */ + public COSArray getCoords() + { + return (COSArray)(DictShading.getDictionaryObject(COSName.COORDS)); + } + + /** + * Returns the function used by several of the gradient types. Interpretation depends on the ShadingType. + * + * @return The gradient function. + */ + public PDFunction getFunction() throws IOException + { + if (function == null) + { + function = PDFunction.create(DictShading.getDictionaryObject(COSName.FUNCTION)); + } + return function; + } + + /** + * Returns the Domain array used by several of the gradient types. Interpretation depends on the ShadingType. + * + * @return The Domain array. + */ + public COSArray getDomain() + { + if (domain == null) + { + domain = (COSArray)(DictShading.getDictionaryObject(COSName.DOMAIN)); + // use default values + if (domain == null) + { + domain = new COSArray(); + domain.add(new COSFloat(0.0f)); + domain.add(new COSFloat(1.0f)); + } + } + return domain; + } + + /** + * Returns the Extend array used by several of the gradient types. Interpretation depends on the ShadingType. + * Default is {false, false}. + * + * @return The Extend array. + */ + public COSArray getExtend() + { + if (extend == null) + { + extend = (COSArray)(DictShading.getDictionaryObject(COSName.EXTEND)); + // use default values + if (extend == null) + { + extend = new COSArray(); + extend.add(COSBoolean.FALSE); + extend.add(COSBoolean.FALSE); + } + } + return extend; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + String sColorSpace; + String sFunction; + try + { + sColorSpace = getColorSpace().toString(); + }catch (IOException e) + { + sColorSpace = "Failure retrieving ColorSpace: " + e.toString(); + } + try + { + sFunction = getFunction().toString(); + }catch(IOException e) + { + sFunction = "n/a"; + } + + + String s = "Shading " + shadingname + "\n" + + "\tShadingType: " + getShadingType() + "\n" + + "\tColorSpace: " + sColorSpace + "\n" + + "\tAntiAlias: " + getAntiAlias() + "\n" + + "\tCoords: " + (getCoords() != null ? getCoords().toString() : "") + "\n" + + "\tDomain: " + getDomain().toString() + "\n" + + "\tFunction: " + sFunction + "\n" + + "\tExtend: " + getExtend().toString() + "\n" + + "\tRaw Value:\n" + + DictShading.toString(); + + return s; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCMYK.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCMYK.java new file mode 100644 index 000000000..24cc6994a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCMYK.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import java.awt.color.ColorSpace; + +/** + * This class represents a CMYK color space. + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class ColorSpaceCMYK extends ColorSpace +{ + /** + * IDfor serialization. + */ + private static final long serialVersionUID = -6362864473145799405L; + + /** + * Constructor. + */ + public ColorSpaceCMYK() + { + super(ColorSpace.TYPE_CMYK,4); + } + + /** + * Converts colorvalues from RGB-colorspace to CIEXYZ-colorspace. + * @param rgbvalue RGB colorvalues to be converted. + * @return Returns converted colorvalues. + */ + private float[] fromRGBtoCIEXYZ(float[] rgbvalue) + { + ColorSpace colorspaceRGB = ColorSpace.getInstance(CS_sRGB); + return colorspaceRGB.toCIEXYZ(rgbvalue); + } + + /** + * Converts colorvalues from CIEXYZ-colorspace to RGB-colorspace. + * @param rgbvalue CIEXYZ colorvalues to be converted. + * @return Returns converted colorvalues. + */ + private float[] fromCIEXYZtoRGB(float[] xyzvalue) + { + ColorSpace colorspaceXYZ = ColorSpace.getInstance(CS_CIEXYZ); + return colorspaceXYZ.toRGB(xyzvalue); + } + + /** + * {@inheritDoc} + */ + public float[] fromCIEXYZ(float[] colorvalue) + { + if (colorvalue != null && colorvalue.length == 3) + { + // We have to convert from XYV to RGB to CMYK + return fromRGB(fromCIEXYZtoRGB(colorvalue)); + } + return null; + } + + /** + * {@inheritDoc} + */ + public float[] fromRGB(float[] rgbvalue) + { + if (rgbvalue != null && rgbvalue.length == 3) + { + // First of all we have to convert from RGB to CMY + float c = 1 - rgbvalue[0]; + float m = 1 - rgbvalue[1]; + float y = 1 - rgbvalue[2]; + // Now we have to convert from CMY to CMYK + float varK = 1; + float[] cmyk = new float[4]; + if ( c < varK ) + { + varK = c; + } + if ( m < varK ) + { + varK = m; + } + if ( y < varK ) + { + varK = y; + } + if ( varK == 1 ) + { + cmyk[0] = cmyk[1] = cmyk[2] = 0; + } + else + { + cmyk[0] = ( c - varK ) / ( 1 - varK ); + cmyk[1] = ( m - varK ) / ( 1 - varK ); + cmyk[2] = ( y - varK ) / ( 1 - varK ); + } + cmyk[3] = varK; + return cmyk; + } + return null; + } + + /** + * {@inheritDoc} + */ + public float[] toCIEXYZ(float[] colorvalue) + { + if (colorvalue != null && colorvalue.length == 4) + { + // We have to convert from CMYK to RGB to XYV + return fromRGBtoCIEXYZ(toRGB(colorvalue)); + } + return null; + } + + /** + * {@inheritDoc} + */ + public float[] toRGB(float[] colorvalue) + { + if (colorvalue != null && colorvalue.length == 4) + { + // First of all we have to convert from CMYK to CMY + float k = colorvalue[3]; + float c = ( colorvalue[0] * ( 1 - k ) + k ); + float m = ( colorvalue[1] * ( 1 - k ) + k ); + float y = ( colorvalue[2] * ( 1 - k ) + k ); + // Now we have to convert from CMY to RGB + return new float[] { 1 - c, 1 - m, 1 - y }; + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCalRGB.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCalRGB.java new file mode 100644 index 000000000..a497ff84e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/ColorSpaceCalRGB.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import java.awt.color.ColorSpace; + +import org.apache.pdfbox.pdmodel.common.PDMatrix; + +/** + * This class represents a CalRGB color space. + * + * In the first place this implementation is needed to support CalRGB. + * To keep it simple, the CalRGB colorspace is treated similar to a DeviceRGB colorspace. + * There is no conversion including the gamma, whitepoint, blackpoint or matrix values yet. + * This should be suitable for displaying and simple printings. + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class ColorSpaceCalRGB extends ColorSpace +{ + private PDGamma gamma = null; + private PDTristimulus whitepoint = null; + private PDTristimulus blackpoint = null; + private PDMatrix matrix = null; + + /** + * ID for serialization. + */ + private static final long serialVersionUID = -6362864473145799405L; + + /** + * Constructor. + */ + public ColorSpaceCalRGB() + { + super(ColorSpace.TYPE_3CLR,3); + } + + /** + * Constructor. + * @param gammaValue Gamma + * @param whitept Whitepoint + * @param blackpt Blackpoint + * @param linearMatrix Matrix value + */ + public ColorSpaceCalRGB(PDGamma gammaValue, PDTristimulus whitept, PDTristimulus blackpt, PDMatrix linearMatrix) + { + this(); + this.gamma = gammaValue; + this.whitepoint = whitept; + this.blackpoint = blackpt; + this.matrix = linearMatrix; + } + + /** + * Converts colorvalues from RGB-colorspace to CIEXYZ-colorspace. + * @param rgbvalue RGB colorvalues to be converted. + * @return Returns converted colorvalues. + */ + private float[] fromRGBtoCIEXYZ(float[] rgbvalue) + { + ColorSpace colorspaceRGB = ColorSpace.getInstance(CS_sRGB); + return colorspaceRGB.toCIEXYZ(rgbvalue); + } + + /** + * Converts colorvalues from CIEXYZ-colorspace to RGB-colorspace. + * @param rgbvalue CIEXYZ colorvalues to be converted. + * @return Returns converted colorvalues. + */ + private float[] fromCIEXYZtoRGB(float[] xyzvalue) + { + ColorSpace colorspaceXYZ = ColorSpace.getInstance(CS_CIEXYZ); + return colorspaceXYZ.toRGB(xyzvalue); + } + + /** + * {@inheritDoc} + */ + public float[] fromCIEXYZ(float[] colorvalue) + { + if (colorvalue != null && colorvalue.length == 3) + { + // We have to convert from XYV to RGB + return fromCIEXYZtoRGB(colorvalue); + } + return null; + } + + /** + * {@inheritDoc} + */ + public float[] fromRGB(float[] rgbvalue) + { + if (rgbvalue != null && rgbvalue.length == 3) + { + return rgbvalue; + } + return null; + } + + /** + * {@inheritDoc} + */ + public float[] toCIEXYZ(float[] colorvalue) + { + if (colorvalue != null && colorvalue.length == 4) + { + // We have to convert from RGB to XYV + return fromRGBtoCIEXYZ(toRGB(colorvalue)); + } + return null; + } + + /** + * {@inheritDoc} + */ + public float[] toRGB(float[] colorvalue) + { + if (colorvalue != null && colorvalue.length == 3) + { + return colorvalue; + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalGray.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalGray.java new file mode 100644 index 000000000..92cba17a9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalGray.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.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 + * @version $Revision: 1.5 $ + */ +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.CALGRAY ); + 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. + */ + protected 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.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.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() + { + COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.WHITE_POINT ); + if( wp == null ) + { + wp = new COSArray(); + wp.add( new COSFloat( 1.0f ) ); + wp.add( new COSFloat( 1.0f ) ); + wp.add( new COSFloat( 1.0f ) ); + dictionary.setItem( COSName.WHITE_POINT, 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.WHITE_POINT, 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() + { + COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.BLACK_POINT ); + if( bp == null ) + { + bp = new COSArray(); + bp.add( new COSFloat( 0.0f ) ); + bp.add( new COSFloat( 0.0f ) ); + bp.add( new COSFloat( 0.0f ) ); + dictionary.setItem( COSName.BLACK_POINT, 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.BLACK_POINT, bpArray ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java new file mode 100644 index 000000000..df1fb237b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDCalRGB.java @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; + +import org.apache.pdfbox.pdmodel.common.PDMatrix; + +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 a Cal RGB color space. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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.CALRGB ); + 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. + */ + protected ColorSpace createColorSpace() + { + return new ColorSpaceCalRGB(getGamma(),getWhitepoint(),getBlackPoint(),getLinearInterpretation()); + } + + /** + * 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[] nBits = {bpc, bpc, bpc}; + return new ComponentColorModel( getJavaColorSpace(), + nBits, + false, + false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE); + } + + /** + * 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() + { + COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.WHITE_POINT ); + if( wp == null ) + { + wp = new COSArray(); + wp.add( new COSFloat( 1.0f ) ); + wp.add( new COSFloat( 1.0f ) ); + wp.add( new COSFloat( 1.0f ) ); + dictionary.setItem( COSName.WHITE_POINT, 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.WHITE_POINT, 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() + { + COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.BLACK_POINT ); + if( bp == null ) + { + bp = new COSArray(); + bp.add( new COSFloat( 0.0f ) ); + bp.add( new COSFloat( 0.0f ) ); + bp.add( new COSFloat( 0.0f ) ); + dictionary.setItem( COSName.BLACK_POINT, 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.BLACK_POINT, 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.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.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 = value.getCOSArray(); + } + dictionary.setItem( COSName.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.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.MATRIX, matrixArray ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java new file mode 100644 index 000000000..3dc75c2a8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpace.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSArray; + +import org.apache.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 + * @version $Revision: 1.5 $ + */ +public abstract class PDColorSpace implements COSObjectable +{ + /** + * array for the given parameters. + */ + protected COSArray array; + + /** + * Cached Java AWT color space. + * + * @see #getJavaColorSpace() + */ + private ColorSpace colorSpace = null; + + /** + * 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() ); + } + + /** + * Returns the Java AWT color space for this instance. + * + * @return Java AWT color space + * @throws IOException if the color space can not be created + */ + public ColorSpace getJavaColorSpace() throws IOException { + if (colorSpace == null) { + colorSpace = createColorSpace(); + } + return colorSpace; + } + + /** + * 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. + */ + protected 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; + + /* + Don't just tell me its color type -- tell me its contents! + */ + /** + * {@inheritDoc} + */ + public String toString() + { + return getName() + "{ " + (array==null? "" : array.toString() ) + " }"; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java new file mode 100644 index 000000000..0cca0079f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDColorSpaceFactory.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; + +/** + * This class represents a color space in a pdf document. + * + * @author Ben Litchfield + * @version $Revision: 1.11 $ + */ +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 + { + return createColorSpace( colorSpace, null ); + } + + /** + * This will create the correct color space given the name. + * + * @param colorSpace The color space object. + * @param colorSpaces The ColorSpace dictionary from the current resources, if any. + * + * @return The color space. + * + * @throws IOException If the color space name is unknown. + */ + public static PDColorSpace createColorSpace( COSBase colorSpace, Map colorSpaces ) throws IOException + { + PDColorSpace retval = null; + if( colorSpace instanceof COSName ) + { + retval = createColorSpace( ((COSName)colorSpace).getName(), colorSpaces ); + } + else if( colorSpace instanceof COSArray ) + { + COSArray array = (COSArray)colorSpace; + String name = ((COSName)array.getObject( 0 )).getName(); + if( name.equals( PDCalGray.NAME ) ) + { + retval = new PDCalGray( array ); + } + else if( name.equals( PDDeviceRGB.NAME ) ) + { + retval = PDDeviceRGB.INSTANCE; + } + else if( name.equals( PDDeviceGray.NAME ) ) + { + retval = new PDDeviceGray(); + } + else if( name.equals( PDDeviceCMYK.NAME ) ) + { + retval = PDDeviceCMYK.INSTANCE; + } + else if( name.equals( PDCalRGB.NAME ) ) + { + retval = new PDCalRGB( array ); + } + else if( name.equals( PDDeviceN.NAME ) ) + { + retval = new PDDeviceN( array ); + } + else if( name.equals( PDIndexed.NAME ) || + name.equals( PDIndexed.ABBREVIATED_NAME )) + { + retval = new PDIndexed( array ); + } + else if( name.equals( PDLab.NAME ) ) + { + retval = new PDLab( array ); + } + else if( name.equals( PDSeparation.NAME ) ) + { + retval = new PDSeparation( array ); + } + else if( name.equals( PDICCBased.NAME ) ) + { + retval = new PDICCBased( array ); + } + else if( name.equals( PDPattern.NAME ) ) + { + retval = new PDPattern( array ); + } + else + { + throw new IOException( "Unknown colorspace array type:" + name ); + } + } + 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 + { + return createColorSpace(colorSpaceName, null); + } + + /** + * This will create the correct color space given the name. + * + * @param colorSpaceName The name of the colorspace. + * @param colorSpaces The ColorSpace dictionary from the current resources, if any. + * + * @return The color space. + * + * @throws IOException If the color space name is unknown. + */ + public static PDColorSpace createColorSpace( String colorSpaceName, Map colorSpaces ) 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( colorSpaces != null && colorSpaces.get( colorSpaceName ) != null ) + { + cs = (PDColorSpace)colorSpaces.get( colorSpaceName ); + } + 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; iBen Litchfield + * @version $Revision: 1.7 $ + */ +public class PDColorState implements Cloneable +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDColorState.class); + + /** + * The default color that can be set to replace all colors in + * {@link ICC_ColorSpace ICC color spaces}. + * + * @see #setIccOverrideColor(Color) + */ + private static volatile Color iccOverrideColor = + Color.getColor("org.apache.pdfbox.ICC_override_color"); + + /** + * Sets the default color to replace all colors in + * {@link ICC_ColorSpace ICC color spaces}. This will work around + * a potential JVM crash caused by broken native ICC color manipulation + * code in the Sun class libraries. + *

      + * The default override can be specified by setting the color code in + * org.apache.pdfbox.ICC_override_color system property + * (see {@link Color#getColor(String)}. If this system property is not + * specified, then the override is not enabled unless this method is + * explicitly called. + * + * @param color ICC override color, + * or null to disable the override + * @see PDFBOX-511 + * @since Apache PDFBox 0.8.1 + */ + public static void setIccOverrideColor(Color color) { + iccOverrideColor = color; + } + + private PDColorSpace colorSpace = new PDDeviceGray(); + private COSArray colorSpaceValue = new COSArray(); + + /** + * Cached Java AWT color based on the current color space and value. + * The value is cleared whenever the color space or value is set. + * + * @see #getJavaColor() + */ + private Color color = null; + + /** + * Default constructor. + * + */ + public PDColorState() + { + setColorSpaceValue( new float[] {0}); + } + + /** + * {@inheritDoc} + */ + public Object clone() + { + PDColorState retval = new PDColorState(); + retval.colorSpace = this.colorSpace; + retval.colorSpaceValue.clear(); + retval.colorSpaceValue.addAll( this.colorSpaceValue ); + return retval; + } + + /** + * Returns the Java AWT color based on the current color space and value. + * + * @return current Java AWT color + * @throws IOException if the current color can not be created + */ + public Color getJavaColor() throws IOException { + if (color == null) { + color = createColor(); + } + return color; + } + + /** + * Create the current color from the colorspace and values. + * @return The current awt color. + * @throws IOException If there is an error creating the color. + */ + private Color createColor() throws IOException + { + float[] components = colorSpaceValue.toFloatArray(); + try + { + if( colorSpace.getName().equals(PDDeviceRGB.NAME) && components.length == 3 ) + { + //for some reason, when using RGB and the RGB colorspace + //the new Color doesn't maintain exactly the same values + //I think some color conversion needs to take place first + //for now we will just make rgb a special case. + return new Color( components[0], components[1], components[2] ); + } + else + { + if (components.length == 1) + { + if (colorSpace.getName().equals(PDSeparation.NAME)) + { + //Use that component as a single-integer RGB value + return new Color((int)components[0]); + } + if (colorSpace.getName().equals(PDDeviceGray.NAME)) + { + // Handling DeviceGray as a special case as with JVM 1.5.0_15 + // and maybe others printing on Windows fails with an + // ArrayIndexOutOfBoundsException when selecting colors + // and strokes e.g. sun.awt.windows.WPrinterJob.setTextColor + return new Color(components[0],components[0],components[0]); + } + } + Color override = iccOverrideColor; + ColorSpace cs = colorSpace.getJavaColorSpace(); + if (cs instanceof ICC_ColorSpace && override != null) + { + log.warn( + "Using an ICC override color to avoid a potential" + + " JVM crash (see PDFBOX-511)"); + return override; + } + else + { + return new Color( cs, components, 1f ); + } + } + } + // Catch IOExceptions from PDColorSpace.getJavaColorSpace(), but + // possibly also IllegalArgumentExceptions or other RuntimeExceptions + // from the potentially complex color management code. + catch (Exception e) + { + Color cGuess; + String sMsg = "Unable to create the color instance " + + Arrays.toString(components) + " in color space " + + colorSpace + "; guessing color ... "; + try + { + switch(components.length) + { + case 1://Use that component as a single-integer RGB value + cGuess = new Color((int)components[0]); + sMsg += "\nInterpretating as single-integer RGB"; + break; + case 3: //RGB + cGuess = new Color(components[0],components[1],components[2]); + sMsg += "\nInterpretating as RGB"; + break; + case 4: //CMYK + //do a rough conversion to RGB as I'm not getting the CMYK to work. + //http://www.codeproject.com/KB/applications/xcmyk.aspx + float R, G, B, K; + K = components[3]; + + R = components[0] * (1f - K) + K; + G = components[1] * (1f - K) + K; + B = components[2] * (1f - K) + K; + + R = (1f - R); + G = (1f - G); + B = (1f - B); + + cGuess = new Color( R,G,B ); + sMsg += "\nInterpretating as CMYK"; + break; + default: + + sMsg += "\nUnable to guess using " + components.length + " components; using black instead"; + cGuess = Color.BLACK; + } + } + catch (Exception e2) + { + sMsg += "\nColor interpolation failed; using black instead\n"; + sMsg += e2.toString(); + cGuess = Color.BLACK; + } + log.warn(sMsg, e); + return cGuess; + } + } + + /** + * Constructor with an existing color set. Default colorspace is PDDeviceGray. + * + * @param csValues The color space values. + */ + public PDColorState( 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; + // Clear color cache + color = null; + } + + /** + * 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 ); + // Clear color cache + color = null; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java new file mode 100644 index 000000000..a45c5a31a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceCMYK.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import java.awt.color.ColorSpace; +import java.awt.image.ColorModel; +import java.io.IOException; + +import java.awt.Transparency; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; + + +/** + * This class represents a CMYK color space. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +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 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. + */ + protected ColorSpace createColorSpace() + { + return new ColorSpaceCMYK(); + } + + /** + * 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, bpc }; + ComponentColorModel componentColorModel = + new ComponentColorModel( getJavaColorSpace(), + nbBits, + false, + false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE ); + return componentColorModel; + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceGray.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceGray.java new file mode 100644 index 000000000..77d58ca00 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceGray.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.6 $ + */ +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. + */ + protected ColorSpace createColorSpace() + { + 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; + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceN.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceN.java new file mode 100644 index 000000000..1f8af69e7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceN.java @@ -0,0 +1,288 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import java.awt.color.ColorSpace; +import java.awt.image.ColorModel; + +import java.io.IOException; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNull; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.function.PDFunction; +import org.apache.pdfbox.pdmodel.common.function.PDFunctionType4; + +/** + * This class represents a DeviceN color space. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class PDDeviceN extends PDColorSpace +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDDeviceN.class); + + /** + * The name of this color space. + */ + public static final String NAME = "DeviceN"; + + private COSArray array; + + /** + * Constructor. + */ + public PDDeviceN() + { + array = new COSArray(); + array.add( COSName.DEVICEN ); + 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. + */ + protected ColorSpace createColorSpace() throws IOException + { + try + { + PDColorSpace alt = getAlternateColorSpace(); + return alt.getJavaColorSpace(); + } + catch (IOException ioexception) + { + log.error(ioexception, ioexception); + throw ioexception; + } + catch (Exception exception) + { + log.error(exception, exception); + throw new IOException("Failed to Create ColorSpace"); + } + } + + /** + * 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 + { + log.info("About to create ColorModel for " + getAlternateColorSpace().toString()); + return getAlternateColorSpace().createColorModel(bpc); + } + + /** + * 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. + * + * @return The tint transform function. + * + * @throws IOException if there is an error creating the function. + */ + public PDFunction getTintTransform() throws IOException + { + return PDFunction.create( array.getObject( 3 ) ); + } + + /** + * This will set the tint transform function. + * + * @param tint The tint transform function. + */ + public void setTintTransform( PDFunction 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() ); + } + } + + /** + * Returns the components of the color in the alternate colorspace for the given tint value. + * @return COSArray with the color components + * @throws IOException If the tint function is not supported + */ + public COSArray calculateColorValues(List tintValues) throws IOException + { + PDFunction tintTransform = getTintTransform(); + if(tintTransform instanceof PDFunctionType4) + { + log.warn("Unsupported tint transformation type: "+tintTransform.getClass().getName() + + " in "+getClass().getName()+".getColorValues()" + + " using color black instead."); + int numberOfComponents = getAlternateColorSpace().getNumberOfComponents(); + // To get black as color: + // 0.0f is used for the single value(s) if the colorspace is gray or RGB based + // 1.0f is used for the single value if the colorspace is CMYK based + float colorValue = numberOfComponents == 4 ? 1.0f : 0.0f; + COSArray retval = new COSArray(); + for (int i=0;iBen Litchfield + * @version $Revision: 1.2 $ + */ +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.COLORANTS ); + if( colorants == null ) + { + colorants = new COSDictionary(); + dictionary.setItem( COSName.COLORANTS, colorants ); + } + for( COSName name : colorants.keySet() ) + { + 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.COLORANTS, colorantDict ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java new file mode 100644 index 000000000..3b435f6e3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDDeviceRGB.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.8 $ + */ +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. + */ + protected ColorSpace createColorSpace() + { + 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( getJavaColorSpace(), + nbBits, + false, + false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE ); + + return componentColorModel; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDGamma.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDGamma.java new file mode 100644 index 000000000..66798fcb7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDGamma.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * A gamma array, or collection of three floating point parameters used for + * color operations. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDICCBased.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDICCBased.java new file mode 100644 index 000000000..be9d15756 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDICCBased.java @@ -0,0 +1,349 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.PDRange; +import org.apache.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 + * @version $Revision: 1.6 $ + */ +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.ICCBASED ); + 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. + */ + protected 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, bpc }; //added 4th bpc to handle CMYK + ComponentColorModel componentColorModel = + new ComponentColorModel( getJavaColorSpace(), + 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.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().setInt( COSName.N, 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.ALTERNATE ); + COSArray alternateArray = null; + if( alternate == null ) + { + alternateArray = new COSArray(); + int numComponents = getNumberOfComponents(); + COSName csName = null; + if( numComponents == 1 ) + { + csName = COSName.DEVICEGRAY; + } + else if( numComponents == 3 ) + { + csName = COSName.DEVICERGB; + } + else if( numComponents == 4 ) + { + csName = COSName.DEVICECMYK; + } + else + { + throw new IOException( "Unknown colorspace number of components:" + numComponents ); + } + alternateArray.add( 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; iBen Litchfield + * @version $Revision: 1.4 $ + */ +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; + + /** + * The lookup data as byte array. + */ + private byte[] lookupData; + + /** + * Constructor, default DeviceRGB, hival 255. + */ + public PDIndexed() + { + array = new COSArray(); + array.add( COSName.INDEXED ); + array.add( COSName.DEVICERGB ); + array.add( COSInteger.get( 255 ) ); + array.add( org.apache.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. + */ + protected 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(); + PDColorSpace baseColorSpace = getBaseColorSpace(); + ColorModel cm = null; + if( baseColorSpace instanceof PDDeviceRGB ) + { + cm = new IndexColorModel(bpc, size+1, index,0,false); + } + else + { + ColorModel baseColorModel = baseColorSpace.createColorModel(bpc); + if( baseColorModel.getTransferType() != DataBuffer.TYPE_BYTE ) + { + throw new IOException( "Not implemented" ); + } + byte[] r = new byte[size+1]; + byte[] g = new byte[size+1]; + byte[] b = new byte[size+1]; + byte[] a = baseColorModel.hasAlpha() ? new byte[size+1] : null; + byte[] inData = new byte[baseColorModel.getNumComponents()]; + for( int i = 0; i <= size; i++ ) + { + System.arraycopy(index, i * inData.length, inData, 0, inData.length); + r[i] = (byte)baseColorModel.getRed(inData); + g[i] = (byte)baseColorModel.getGreen(inData); + b[i] = (byte)baseColorModel.getBlue(inData); + if(a != null) + { + a[i] = (byte)baseColorModel.getAlpha(inData); + } + } + cm = a == null ? new IndexColorModel(bpc, size+1, r, g, b) : new IndexColorModel(bpc, size+1, r, g, b, a); + } + 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 + { + COSBase base = array.getObject( 1 ); + return PDColorSpaceFactory.createColorSpace( base ); + } + + /** + * 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, high ); + } + + /** + * This will perform a lookup into the color lookup table. + * + * @param lookupIndex The zero-based index into the table, should not exceed the high value. + * @param componentNumber The component number, probably 1,2,3,3. + * + * @return The value that was from the lookup table. + * + * @throws IOException If there is an error looking up the color. + */ + public int lookupColor( int lookupIndex, int componentNumber ) throws IOException + { + PDColorSpace baseColor = getBaseColorSpace(); + byte[] data = getLookupData(); + int numberOfComponents = baseColor.getNumberOfComponents(); + return (data[lookupIndex*numberOfComponents + componentNumber]+256)%256; + } + + /** + * Get the lookup data table. + * + * @return a byte array containing the the lookup data. + * @throws IOException if an error occurs. + */ + public byte[] getLookupData() throws IOException + { + if ( lookupData == null) + { + COSBase lookupTable = array.getObject( 3 ); + if( lookupTable instanceof COSString ) + { + lookupData = ((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 ); + } + lookupData = output.toByteArray(); + } + else if( lookupTable == null ) + { + lookupData = new byte[0]; + } + else + { + throw new IOException( "Error: Unknown type for lookup table " + lookupTable ); + } + } + return lookupData; + } + + /** + * This will set a color in the color lookup table. + * + * @param lookupIndex The zero-based index into the table, should not exceed the high value. + * @param componentNumber The component number, probably 1,2,3,3. + * @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 lookupIndex, int componentNumber, int color ) throws IOException + { + PDColorSpace baseColor = getBaseColorSpace(); + int numberOfComponents = baseColor.getNumberOfComponents(); + byte[] data = getLookupData(); + data[lookupIndex*numberOfComponents + componentNumber] = (byte)color; + COSString string = new COSString( data ); + array.set( 3, string ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDLab.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDLab.java new file mode 100644 index 000000000..a914f72ba --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDLab.java @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; + +import org.apache.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 + * @version $Revision: 1.4 $ + */ +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.LAB ); + 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. + */ + protected 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() + { + COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.WHITE_POINT ); + if( wp == null ) + { + wp = new COSArray(); + wp.add( new COSFloat( 1.0f ) ); + wp.add( new COSFloat( 1.0f ) ); + wp.add( new COSFloat( 1.0f ) ); + dictionary.setItem( COSName.WHITE_POINT, 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.WHITE_POINT, 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() + { + COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.BLACK_POINT ); + if( bp == null ) + { + bp = new COSArray(); + bp.add( new COSFloat( 0.0f ) ); + bp.add( new COSFloat( 0.0f ) ); + bp.add( new COSFloat( 0.0f ) ); + dictionary.setItem( COSName.BLACK_POINT, 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.BLACK_POINT, bpArray ); + } + + private COSArray getRangeArray() + { + COSArray range = (COSArray)dictionary.getDictionaryObject( COSName.RANGE ); + if( range == null ) + { + range = new COSArray(); + dictionary.setItem( COSName.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.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, 1 ); + } + + /** + * 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.RANGE, rangeArray ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDPattern.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDPattern.java new file mode 100644 index 000000000..55f6ba834 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDPattern.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.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 + * @version $Revision: 1.4 $ + */ +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.PATTERN ); + } + + /** + * 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. + */ + protected 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" ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java new file mode 100644 index 000000000..79e8dff80 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.color; + +import java.awt.color.ColorSpace; +import java.awt.image.ColorModel; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.function.PDFunction; +import org.apache.pdfbox.pdmodel.common.function.PDFunctionType4; + +/** + * This class represents a Separation color space. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class PDSeparation extends PDColorSpace +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDSeparation.class); + + /** + * The name of this color space. + */ + public static final String NAME = "Separation"; + + + /** + * Constructor. + */ + public PDSeparation() + { + array = new COSArray(); + array.add( COSName.SEPARATION ); + 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 getAlternateColorSpace().getNumberOfComponents(); + } + + /** + * 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. + */ + protected ColorSpace createColorSpace() throws IOException + { + try + { + PDColorSpace alt = getAlternateColorSpace(); + return alt.getJavaColorSpace(); + } + catch (IOException ioexception) + { + log.error(ioexception, ioexception); + + throw ioexception; + } + catch (Exception exception) + { + log.error(exception, exception); + throw new IOException("Failed to Create ColorSpace"); + } + } + + /** + * 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 + { + log.info("About to create ColorModel for " + getAlternateColorSpace().toString()); + return getAlternateColorSpace().createColorModel(bpc); + } + + /** + * 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 ); + PDColorSpace cs = PDColorSpaceFactory.createColorSpace( alternate ); + return cs; + } + + /** + * 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. + * + * @return The tint transform function. + * + * @throws IOException If there is an error creating the PDFunction + */ + public PDFunction getTintTransform() throws IOException + { + return PDFunction.create( array.getObject( 3 ) ); + } + + /** + * This will set the tint transform function. + * + * @param tint The tint transform function. + */ + public void setTintTransform( PDFunction tint ) + { + array.set( 3, tint ); + } + + /** + * Returns the components of the color in the alternate colorspace for the given tint value. + * @return COSArray with the color components + * @throws IOException If the tint function is not supported + */ + public COSArray calculateColorValues(COSBase tintValue) throws IOException + { + PDFunction tintTransform = getTintTransform(); + if(tintTransform instanceof PDFunctionType4) + { + log.warn("Unsupported tint transformation type: "+tintTransform.getClass().getName() + + " in "+getClass().getName()+".getColorValues()" + + " using color black instead."); + int numberOfComponents = getAlternateColorSpace().getNumberOfComponents(); + // To get black as color: + // 0.0f is used for the single value(s) if the colorspace is gray or RGB based + // 1.0f is used for the single value if the colorspace is CMYK based + float colorValue = numberOfComponents == 4 ? 1.0f : 0.0f; + COSArray retval = new COSArray(); + for (int i=0;iBen Litchfield + * @version $Revision: 1.2 $ + */ +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 + + + + + + +This package deals with colors that are stored in a PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java new file mode 100644 index 000000000..30b45dc99 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.optionalcontent; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents an optional content group (OCG). + * + * @since PDF 1.5 + * @version $Revision$ + */ +public class PDOptionalContentGroup implements COSObjectable +{ + + private COSDictionary ocg; + + /** + * Creates a new optional content group (OCG). + * @param name the name of the content group + */ + public PDOptionalContentGroup(String name) + { + this.ocg = new COSDictionary(); + this.ocg.setItem(COSName.TYPE, COSName.OCG); + setName(name); + } + + /** + * Creates a new instance based on a given {@link COSDictionary}. + * @param dict the dictionary + */ + public PDOptionalContentGroup(COSDictionary dict) + { + if (!dict.getItem(COSName.TYPE).equals(COSName.OCG)) + { + throw new IllegalArgumentException( + "Provided dictionary is not of type '" + COSName.OCG + "'"); + } + this.ocg = dict; + } + + /** {@inheritDoc} */ + public COSBase getCOSObject() + { + return this.ocg; + } + + /** + * Returns the name of the optional content group. + * @return the name + */ + public String getName() + { + return this.ocg.getString(COSName.NAME); + } + + /** + * Sets the name of the optional content group. + * @param name the name + */ + public void setName(String name) + { + this.ocg.setString(COSName.NAME, name); + } + + //TODO Add support for "Intent" and "Usage" + + /** {@inheritDoc} */ + @Override + public String toString() + { + return super.toString() + " (" + getName() + ")"; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java new file mode 100644 index 000000000..c10e0878f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.optionalcontent; + +import java.util.Collection; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents the optional content properties dictionary. + * + * @since PDF 1.5 + * @version $Revision$ + */ +public class PDOptionalContentProperties implements COSObjectable +{ + + /** + * Enumeration for the BaseState dictionary entry on the "D" dictionary. + */ + public static enum BaseState + { + + /** The "ON" value. */ + ON(COSName.ON), + /** The "OFF" value. */ + OFF(COSName.OFF), + /** The "Unchanged" value. */ + UNCHANGED(COSName.UNCHANGED); + + private COSName name; + + private BaseState(COSName value) + { + this.name = value; + } + + /** + * Returns the PDF name for the state. + * @return the name of the state + */ + public COSName getName() + { + return this.name; + } + + /** + * Returns the base state represented by the given {@link COSName}. + * @param state the state name + * @return the state enum value + */ + public static BaseState valueOf(COSName state) + { + if (state == null) + { + return BaseState.ON; + } + return BaseState.valueOf(state.getName().toUpperCase()); + } + + } + + private COSDictionary dict; + + /** + * Creates a new optional content properties dictionary. + */ + public PDOptionalContentProperties() + { + this.dict = new COSDictionary(); + this.dict.setItem(COSName.OCGS, new COSArray()); + this.dict.setItem(COSName.D, new COSDictionary()); + } + + /** + * Creates a new instance based on a given {@link COSDictionary}. + * @param props the dictionary + */ + public PDOptionalContentProperties(COSDictionary props) + { + this.dict = props; + } + + /** {@inheritDoc} */ + public COSBase getCOSObject() + { + return this.dict; + } + + private COSArray getOCGs() + { + COSArray ocgs = (COSArray)this.dict.getItem(COSName.OCGS); + if (ocgs == null) + { + ocgs = new COSArray(); + this.dict.setItem(COSName.OCGS, ocgs); //OCGs is required + } + return ocgs; + } + + private COSDictionary getD() + { + COSDictionary d = (COSDictionary)this.dict.getDictionaryObject(COSName.D); + if (d == null) + { + d = new COSDictionary(); + this.dict.setItem(COSName.D, d); //D is required + } + return d; + } + + /** + * Returns the optional content group of the given name. + * @param name the group name + * @return the optional content group or null, if there is no such group + */ + public PDOptionalContentGroup getGroup(String name) + { + COSArray ocgs = getOCGs(); + for (COSBase o : ocgs) + { + COSDictionary ocg = toDictionary(o); + String groupName = ocg.getString(COSName.NAME); + if (groupName.equals(name)) + { + return new PDOptionalContentGroup(ocg); + } + } + return null; + } + + /** + * Adds an optional content group (OCG). + * @param ocg the optional content group + */ + public void addGroup(PDOptionalContentGroup ocg) + { + COSArray ocgs = getOCGs(); + ocgs.add(ocg.getCOSObject()); + + //By default, add new group to the "Order" entry so it appears in the user interface + COSArray order = (COSArray)getD().getDictionaryObject(COSName.ORDER); + if (order == null) + { + order = new COSArray(); + getD().setItem(COSName.ORDER, order); + } + order.add(ocg); + } + + /** + * Returns the collection of all optional content groups. + * @return the optional content groups + */ + public Collection getOptionalContentGroups() + { + Collection coll = new java.util.ArrayList(); + COSArray ocgs = getOCGs(); + for (COSBase base : ocgs) + { + COSObject obj = (COSObject)base; //Children must be indirect references + coll.add(new PDOptionalContentGroup((COSDictionary)obj.getObject())); + } + return coll; + } + + /** + * Returns the base state for optional content groups. + * @return the base state + */ + public BaseState getBaseState() + { + COSDictionary d = getD(); + COSName name = (COSName)d.getItem(COSName.BASE_STATE); + return BaseState.valueOf(name); + } + + /** + * Sets the base state for optional content groups. + * @param state the base state + */ + public void setBaseState(BaseState state) + { + COSDictionary d = getD(); + d.setItem(COSName.BASE_STATE, state.getName()); + } + + /** + * Lists all optional content group names. + * @return an array of all names + */ + public String[] getGroupNames() + { + COSArray ocgs = (COSArray)dict.getDictionaryObject(COSName.OCGS); + int size = ocgs.size(); + String[] groups = new String[size]; + for (int i = 0; i < size; i++) + { + COSBase obj = (COSBase)ocgs.get(i); + COSDictionary ocg = toDictionary(obj); + groups[i] = ocg.getString(COSName.NAME); + } + return groups; + } + + /** + * Indicates whether a particular optional content group is found in the PDF file. + * @param groupName the group name + * @return true if the group exists, false otherwise + */ + public boolean hasGroup(String groupName) + { + String[] layers = getGroupNames(); + for (String layer : layers) + { + if (layer.equals(groupName)) + { + return true; + } + } + return false; + } + + /** + * Indicates whether an optional content group is enabled. + * @param groupName the group name + * @return true if the group is enabled + */ + public boolean isGroupEnabled(String groupName) + { + //TODO handle Optional Content Configuration Dictionaries, + //i.e. OCProperties/Configs + + COSDictionary d = getD(); + COSArray on = (COSArray)d.getDictionaryObject(COSName.ON); + if (on != null) + { + for (COSBase o : on) + { + COSDictionary group = toDictionary(o); + String name = group.getString(COSName.NAME); + if (name.equals(groupName)) + { + return true; + } + } + } + + COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF); + if (off != null) + { + for (COSBase o : off) + { + COSDictionary group = toDictionary(o); + String name = group.getString(COSName.NAME); + if (name.equals(groupName)) + { + return false; + } + } + } + + BaseState baseState = getBaseState(); + boolean enabled = !baseState.equals(BaseState.OFF); + //TODO What to do with BaseState.Unchanged? + return enabled; + } + + private COSDictionary toDictionary(COSBase o) + { + if (o instanceof COSObject) + { + return (COSDictionary)((COSObject)o).getObject(); + } + else + { + return (COSDictionary)o; + } + } + + /** + * Enables or disables an optional content group. + * @param groupName the group name + * @param enable true to enable, false to disable + * @return true if the group already had an on or off setting, false otherwise + */ + public boolean setGroupEnabled(String groupName, boolean enable) + { + COSDictionary d = getD(); + COSArray on = (COSArray)d.getDictionaryObject(COSName.ON); + if (on == null) + { + on = new COSArray(); + d.setItem(COSName.ON, on); + } + COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF); + if (off == null) + { + off = new COSArray(); + d.setItem(COSName.OFF, off); + } + + boolean found = false; + for (COSBase o : on) + { + COSDictionary group = toDictionary(o); + String name = group.getString(COSName.NAME); + if (!enable && name.equals(groupName)) + { + //disable group + on.remove(group); + off.add(group); + found = true; + break; + } + } + for (COSBase o : off) + { + COSDictionary group = toDictionary(o); + String name = group.getString(COSName.NAME); + if (enable && name.equals(groupName)) + { + //enable group + off.remove(group); + on.add(group); + found = true; + break; + } + } + + if (!found) + { + PDOptionalContentGroup ocg = getGroup(groupName); + if (enable) + { + on.add(ocg.getCOSObject()); + } + else + { + off.add(ocg.getCOSObject()); + } + } + return found; + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/package.html new file mode 100644 index 000000000..bfb012154 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/package.html @@ -0,0 +1,25 @@ + + + + + + + +The PDModel graphics package deals with graphics states, operations, and parameters within the PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Average.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Average.java new file mode 100644 index 000000000..a22f7b4f2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Average.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.predictor; + +/** + * We can use raw on the right hand side of + * the decoding formula because it is already decoded. + * + * average(i,j) = raw(i,j) + (raw(i-1,j)+raw(i,j-1)/2 + * + * decoding + * + * raw(i,j) = avarage(i,j) - (raw(i-1,j)+raw(i,j-1)/2 + * + * @author xylifyx@yahoo.co.uk + * @version $Revision: 1.3 $ + */ +public class Average extends PredictorAlgorithm +{ + /** + * Not an optimal version, but close to the def. + * + * {@inheritDoc} + */ + 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)); + } + } + + /** + * {@inheritDoc} + */ + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/None.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/None.java new file mode 100644 index 000000000..757f57045 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/None.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.predictor; + +/** + * The none algorithm. + * + * None(i,j) = Raw(i,j) + * + * Raw(i,j) = None(i,j) + * + * @author xylifyx@yahoo.co.uk + * @version $Revision: 1.3 $ + */ +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); + } + + + + /** + * {@inheritDoc} + */ + 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]; + } + } + + /** + * {@inheritDoc} + */ + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Optimum.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Optimum.java new file mode 100644 index 000000000..145f3d039 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Optimum.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.1 $ + */ +public class Optimum extends PredictorAlgorithm +{ + /** + * {@inheritDoc} + */ + 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()); + } + } + + /** + * {@inheritDoc} + */ + public void encodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset, + int destDy, int destOffset) + { + throw new UnsupportedOperationException("encodeLine"); + } + + /** + * {@inheritDoc} + */ + public void decodeLine(byte[] src, byte[] dest, int srcDy, int srcOffset, + int destDy, int destOffset) + { + throw new UnsupportedOperationException("decodeLine"); + } + + /** + * {@inheritDoc} + */ + 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() }; + + /** + * {@inheritDoc} + */ + public void setBpp(int bpp) + { + super.setBpp(bpp); + for (int i = 0; i < filter.length; i++) + { + filter[i].setBpp(bpp); + } + } + /** + * {@inheritDoc} + */ + public void setHeight(int height) + { + super.setHeight(height); + for (int i = 0; i < filter.length; i++) + { + filter[i].setHeight(height); + } + } + + /** + * {@inheritDoc} + */ + public void setWidth(int width) + { + super.setWidth(width); + for (int i = 0; i < filter.length; i++) + { + filter[i].setWidth(width); + } + } + + /** + * {@inheritDoc} + */ + 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); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Paeth.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Paeth.java new file mode 100644 index 000000000..f161fa73a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Paeth.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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: + * + * Paeth(i,j) = Raw(i,j) - PaethPredictor(Raw(i-1,j), Raw(i,j-1), Raw(i-1,j-1)) + * + * To decode the Paeth filter + * + * Raw(i,j) = Paeth(i,j) - PaethPredictor(Raw(i-1,j), Raw(i,j-1), Raw(i-1,j-1)) + * + * @author xylifyx@yahoo.co.uk + * @version $Revision: 1.3 $ + */ +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; + } + } + + /** + * {@inheritDoc} + */ + 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))); + } + } + + /** + * {@inheritDoc} + */ + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java new file mode 100644 index 000000000..c90eca4dd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/PredictorAlgorithm.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.4 $ + * @see PNG Filters + */ +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 + *

        + *
      • 1 No prediction (the default value) + *
      • 2 TIFF Predictor 2 + *
      • 10 PNG prediction (on encoding, PNG None on all rows) + *
      • 11 PNG prediction (on encoding, PNG Sub on all rows) + *
      • 12 PNG prediction (on encoding, PNG Up on all rows) + *
      • 13 PNG prediction (on encoding, PNG Average on all rows) + *
      • 14 PNG prediction (on encoding, PNG Paeth on all rows) + *
      • 15 PNG prediction (on encoding, PNG optimum) + *
      + * + * @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 Optimum(); + break; + default: + filter = new None(); + } + return filter; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Sub.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Sub.java new file mode 100644 index 000000000..dc401ac09 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Sub.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.predictor; + +/** + * The sub algorithm. + * + * Sub(i,j) = Raw(i,j) - Raw(i-1,j) + * + * Raw(i,j) = Sub(i,j) + Raw(i-1,j) + * + * @author xylifyx@yahoo.co.uk + * @version $Revision: 1.3 $ + */ +public class Sub extends PredictorAlgorithm +{ + /** + * {@inheritDoc} + */ + 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]); + } + } + + /** + * {@inheritDoc} + */ + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Up.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Up.java new file mode 100644 index 000000000..57588c9b0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/Up.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.predictor; + +/** + * The up algorithm. + * + * Up(i,j) = Raw(i,j) - Raw(i,j-1) + * + * Raw(i,j) = Up(i,j) + Raw(i,j-1) + * + * @author xylifyx@yahoo.co.uk + * @version $Revision: 1.3 $ + */ +public class Up extends PredictorAlgorithm +{ + /** + * {@inheritDoc} + */ + 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]); + } + } + } + + /** + * {@inheritDoc} + */ + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/package.html new file mode 100644 index 000000000..bc8ea6d1e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/predictor/package.html @@ -0,0 +1,26 @@ + + + + + + + +The predictor package contains code for different PNG predictor algorithms that +are present in PDF documents. These classes are used internally by PDFBox. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/CompositeImage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/CompositeImage.java new file mode 100644 index 000000000..73ca9cbbb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/CompositeImage.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.xobject; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import org.apache.pdfbox.cos.COSArray; + + +/** + * This class is responsible for combining a base image with an SMask-based transparency + * image to form a composite image. + * See section 11.5 of the pdf specification for details on Soft Masks. + *

      + * Briefly however, an Smask is a supplementary greyscale image whose RGB-values define + * a transparency mask which, when combined appropriately with the base image, + * allows per-pixel transparency to be applied. + *

      + * Note that Smasks are not required for any image and if the smask is not present + * in the pdf file, the image will have no transparent pixels. + * + * @author Neil McErlean + */ +public class CompositeImage +{ + private BufferedImage baseImage; + private BufferedImage smaskImage; + + /** + * Standard constructor. + * @param baseImage the base Image. + * @param smaskImage the transparency image. + * + */ + public CompositeImage(BufferedImage baseImage, BufferedImage smaskImage) + { + this.baseImage = baseImage; + this.smaskImage = smaskImage; + } + + /** + * This method applies the specified transparency mask to a given image and returns a new BufferedImage + * whose alpha values are computed from the transparency mask (smask) image. + */ + public BufferedImage createMaskedImage(COSArray decodeArray) throws IOException + { + // The decode array should only be [0 1] or [1 0]. See PDF spec. + // [0 1] means the smask's RGB values give transparency. Default: see PDF spec section 8.9.5.1 + // [1 0] means the smask's RGB values give opacity. + + boolean isOpaque = false; + if (decodeArray != null) + { + isOpaque = decodeArray.getInt(0) > decodeArray.getInt(1); + } + + final int baseImageWidth = baseImage.getWidth(); + final int baseImageHeight = baseImage.getHeight(); + + BufferedImage result = new BufferedImage(baseImageWidth, baseImageHeight, BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < baseImageWidth; x++) + { + for (int y = 0; y < baseImageHeight; y++) + { + int rgb = baseImage.getRGB(x, y); + int alpha = smaskImage.getRGB(x, y); + + // The smask image defines a transparency mask but it has no alpha values itself, instead + // using the greyscale values to indicate transparency. + // 0xAARRGGBB + + // We need to remove any alpha value in the main image. + int rgbOnly = 0x00FFFFFF & rgb; + + // We need to use one of the rgb values as the new alpha value for the main image. + // It seems the mask is greyscale, so it shouldn't matter whether we use R, G or B + // as the indicator of transparency. + if (isOpaque) + { + alpha = ~alpha; + } + int alphaOnly = alpha << 24; + + result.setRGB(x, y, rgbOnly | alphaOnly); + } + } + return result; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDCcitt.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDCcitt.java new file mode 100644 index 000000000..aa48963bf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDCcitt.java @@ -0,0 +1,706 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.xobject; + +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.io.RandomAccess; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; + +/** + * An image class for CCITT Fax. + * + * @author Ben Litchfield + * @author paul king + * @version $Revision: 1.6 $ + */ +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, RandomAccess 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.XOBJECT ); + dic.setItem( COSName.DECODE_PARMS, decodeParms); + + setBitsPerComponent( 1 ); + setColorSpace( new PDDeviceGray() ); + setWidth( decodeParms.getInt(COSName.COLUMNS) ); + setHeight( decodeParms.getInt(COSName.ROWS) ); + + } + + /** + * Returns an image of the CCITT Fax, or null if TIFFs are not supported. (Requires additional JAI Image filters ) + * + * {@inheritDoc} + */ + public BufferedImage getRGBImage() throws IOException + { + COSStream stream = getCOSStream(); + COSBase decodeP = stream.getDictionaryObject(COSName.DECODE_PARMS); + COSDictionary decodeParms = null; + if (decodeP instanceof COSDictionary) + { + decodeParms = (COSDictionary)decodeP; + } + else if (decodeP instanceof COSArray) + { + decodeParms = (COSDictionary)((COSArray)decodeP).get(0); + } + int cols = decodeParms.getInt(COSName.COLUMNS, 1728); + int rows = decodeParms.getInt(COSName.ROWS, 0); + int height = stream.getInt(COSName.HEIGHT, 0); + if (rows > 0 && height > 0) + { + // ensure that rows doesn't contain implausible data, see PDFBOX-771 + rows = Math.min(rows, height); + } + else + { + // at least one of the values has to have a valid value + rows = Math.max(rows, height); + } + boolean blackIsOne = decodeParms.getBoolean(COSName.BLACK_IS_1, false); + + byte[] map; + if (blackIsOne) + { + map = new byte[] {(byte)0x00, (byte)0xff}; + } + else + { + map = new byte[] {(byte)0xff}; + } + ColorModel cm = new IndexColorModel(1, map.length, map, map, map, Transparency.OPAQUE); + WritableRaster raster = cm.createCompatibleWritableRaster( cols, rows ); + DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer(); + byte[] bufferData = buffer.getData(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int bytesRead; + byte[] data = new byte[16384]; + InputStream unfilteredStream = stream.getUnfilteredStream(); + while ((bytesRead = unfilteredStream.read(data, 0, data.length)) != -1) + { + baos.write(data, 0, bytesRead); + } + baos.flush(); + System.arraycopy( baos.toByteArray(), 0,bufferData, 0, + (baos.size() < bufferData.length ? baos.size() : bufferData.length) ); + return new BufferedImage(cm, raster, false, null); + } + + /** + * This writes a tiff to out. + * + * {@inheritDoc} + */ + public void write2OutputStream(OutputStream out) throws IOException + { + // We should use another format than TIFF to get rid of the TiffWrapper + 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(RandomAccess 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(COSName.COLUMNS,val); + break; + } + case 257: + { + parms.setInt(COSName.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(COSName.BLACK_IS_1, 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(COSName.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, RandomAccess raf) throws IOException + { + if (endianess == 'I') + { + return raf.read() | (raf.read() << 8); + } + return (raf.read() << 8) | raf.read(); + } + + private int readlong(char endianess, RandomAccess 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 + + private TiffWrapper(InputStream rawstream, COSDictionary options) + { + buildHeader(options); + currentOffset=0; + datastream = rawstream; + } + + // Implement basic methods from InputStream + /** + * {@inheritDoc} + */ + public boolean markSupported() + { + return false; + } + /** + * {@inheritDoc} + */ + public void reset() throws IOException + { + throw new IOException("reset not supported"); + } + + /** + * For simple read, take a byte from the tiffheader array or pass through. + * + * {@inheritDoc} + */ + 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. + * + * {@inheritDoc} + */ + 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. + * + * {@inheritDoc} + */ + 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. + * + * {@inheritDoc} + */ + 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 + + int ifdSize = 10 + (12 * numOfTags ) + 4; + tiffheader = new byte[ifdSize + 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 = ifdSize; + + // 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 + + COSArray decode = getDecode(); + // we have to invert the b/w-values, + // if the Decode array exists and consists of (1,0) + if (decode != null && decode.getInt(0) == 1) + { + blackis1 = 1; + } + COSBase dicOrArrayParms = options.getDictionaryObject(COSName.DECODE_PARMS); + COSDictionary decodeParms = null; + if( dicOrArrayParms instanceof COSDictionary ) + { + decodeParms = (COSDictionary)dicOrArrayParms; + } + else + { + COSArray parmsArray = (COSArray)dicOrArrayParms; + if( parmsArray.size() == 1 ) + { + decodeParms = (COSDictionary)parmsArray.getObject( 0 ); + } + else + { + //else find the first dictionary with Row/Column info and use that. + for( int i=0; i 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(COSName.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, tiffheader.length); // Offset to start of image data - updated below + addTag(279, options.getInt(COSName.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 + int len = value.length() + 1; + tiffheader[offset+4]=(byte)(len & 0xff); + 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); + try { + System.arraycopy(value.getBytes("US-ASCII"), 0, + tiffheader, additionalOffset, value.length()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Incompatible VM without US-ASCII encoding", e); + } + additionalOffset += len; + } + + 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); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java new file mode 100644 index 000000000..008bfbc0e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDInlinedImage.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 java.util.Map; + +import org.apache.pdfbox.filter.Filter; +import org.apache.pdfbox.filter.FilterManager; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.util.ImageParameters; + +/** + * This class represents an inlined image. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +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 + { + return createImage( null ); + } + + /** + * This will take the inlined image information and create a java.awt.Image from + * it. + * + * @param colorSpaces The ColorSpace dictionary from the current resources, if any. + * + * @return The image that this object represents. + * + * @throws IOException If there is an error creating the image. + */ + public BufferedImage createImage( Map colorSpaces ) 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( colorSpaces ); + ColorModel colorModel = null; + if(pcs != null) + { + colorModel = + pcs.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; i DCT_FILTERS = new ArrayList(); + + private static final float DEFAULT_COMPRESSION_LEVEL = 0.75f; + + 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.XOBJECT ); + + BufferedImage image = getRGBImage(); + if (image != null) + { + setBitsPerComponent( 8 ); + setColorSpace( PDDeviceRGB.INSTANCE ); + setHeight( image.getHeight() ); + setWidth( image.getWidth() ); + } + + } + + /** + * Construct from a buffered image. + * The default compression level of 0.75 will be used. + * + * @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); + createImageStream(doc, bi, DEFAULT_COMPRESSION_LEVEL); + } + + /** + * 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 + * @param compressionQuality The quality level which is used to compress the image + * @throws IOException If there is an error processing the jpeg data. + */ + public PDJpeg( PDDocument doc, BufferedImage bi, float compressionQuality ) throws IOException + { + super( new PDStream( doc ), JPG); + createImageStream(doc, bi, compressionQuality); + } + + private void createImageStream(PDDocument doc, BufferedImage bi, float compressionQuality) throws IOException + { + BufferedImage alpha = null; + if (bi.getColorModel().hasAlpha()) + { + alpha = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = alpha.createGraphics(); + g.setColor(Color.BLACK); + g.drawRect(0, 0, bi.getWidth(), bi.getHeight()); + g.setColor(Color.WHITE); + g.drawImage(bi, 0, 0, null); + int alphaHeight = alpha.getHeight(); + int alphaWidth = alpha.getWidth(); + int whiteRGB = (Color.WHITE).getRGB(); + for(int y = 0; y < alphaHeight; y++) + { + for(int x = 0; x < alphaWidth; x++) + { + Color color = new Color(alpha.getRGB(x, y)); + if(color.getRed() != 0 && color.getGreen() != 0 && color.getBlue() != 0) + { + alpha.setRGB(x, y, whiteRGB); + } + } + } + BufferedImage image = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_USHORT_565_RGB); + g = image.createGraphics(); + g.drawImage(bi, 0, 0, null); + bi = image; + } + + java.io.OutputStream os = getCOSStream().createFilteredStream(); + try + { + ImageWriter writer = null; + Iterator iter = ImageIO.getImageWritersByFormatName(JPG); + if (iter.hasNext()) + { + writer = iter.next(); + } + ImageOutputStream ios = ImageIO.createImageOutputStream(os); + writer.setOutput(ios); + + // Set the compression quality + JPEGImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault()); + iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + iwparam.setCompressionQuality(compressionQuality); + + // Write the image + writer.write(null, new IIOImage(bi, null, null), iwparam); + + writer.dispose(); + + COSDictionary dic = getCOSStream(); + dic.setItem( COSName.FILTER, COSName.DCT_DECODE ); + dic.setItem( COSName.SUBTYPE, COSName.IMAGE); + dic.setItem( COSName.TYPE, COSName.XOBJECT ); + PDXObjectImage alphaPdImage = null; + if(alpha != null) + { + alphaPdImage = new PDJpeg(doc, alpha, compressionQuality); + dic.setItem(COSName.SMASK, alphaPdImage); + } + setBitsPerComponent( 8 ); + if (bi.getColorModel().getNumComponents() == 3) + { + setColorSpace( PDDeviceRGB.INSTANCE ); + } + else + { + if (bi.getColorModel().getNumComponents() == 1) + { + setColorSpace( new PDDeviceGray() ); + } + else + { + throw new IllegalStateException(); + } + } + 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. ) + * {@inheritDoc} + */ + public BufferedImage getRGBImage() throws IOException + { //TODO PKOCH + BufferedImage bi = null; + boolean readError = false; + try + { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + write2OutputStream(os); + os.close(); + byte[] img = os.toByteArray(); + + // 1. try to read jpeg image + try + { + bi = ImageIO.read(new ByteArrayInputStream(img)); + } + catch (IIOException iioe) + { + // cannot read jpeg + readError = true; + } + catch (Exception ignore) + { + } + + // 2. try to read jpeg again. some jpegs have some strange header containing + // "Adobe " at some place. so just replace the header with a valid jpeg header. + // TODO : not sure if it works for all cases + if (bi == null && readError) + { + byte[] newImage = replaceHeader(img); + + ByteArrayInputStream bai = new ByteArrayInputStream(newImage); + + bi = ImageIO.read(bai); + } + } + finally + { + } + + // If there is a 'soft mask' image then we use that as a transparency mask. + PDXObjectImage smask = getSMaskImage(); + if (smask != null) + { + BufferedImage smaskBI = smask.getRGBImage(); + + COSArray decodeArray = smask.getDecode(); + CompositeImage compositeImage = new CompositeImage(bi, smaskBI); + BufferedImage rgbImage = compositeImage.createMaskedImage(decodeArray); + + return rgbImage; + } + else + { + // But if there is no soft mask, use the unaltered image. + return bi; + } + } + + /** + * This writes the JPeg to out. + * {@inheritDoc} + */ + 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 ); + } + } + + /** + * Returns the given file as byte array. + * @param file File to be read + * @return given file as byte array + * @throws IOException if somethin went wrong during reading the file + */ + public static byte[] getBytesFromFile(File file) throws IOException + { + InputStream is = new FileInputStream(file); + long length = file.length(); + + if (length > Integer.MAX_VALUE) + { + // File is too large + throw new IOException("File is tooo large"); + } + + // Create the byte array to hold the data + byte[] bytes = new byte[(int)length]; + + // Read in the bytes + int offset = 0; + int numRead = 0; + + while (offset < bytes.length + && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) + { + offset += numRead; + } + + // Ensure all the bytes have been read in + if (offset < bytes.length) + { + throw new IOException("Could not completely read file "+file.getName()); + } + is.close(); + return bytes; + } + + private int getHeaderEndPos(byte[] image) + { + for (int i = 0; i < image.length; i++) + { + byte b = image[i]; + if (b == (byte) 0xDB) + { + // TODO : check for ff db + return i -2; + } + } + return 0; + } + + private byte[] replaceHeader(byte[] image) + { + // get end position of wrong header respectively startposition of "real jpeg data" + int pos = getHeaderEndPos(image); + + // simple correct header + byte[] header = new byte[]{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xE0, (byte) 0x00, + (byte) 0x10, (byte) 0x4A, (byte) 0x46, (byte) 0x49, (byte) 0x46, (byte) 0x00, (byte) 0x01, + (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x60, (byte) 0x00, (byte) 0x60, (byte) 0x00, (byte) 0x00}; + + // concat + byte[] newImage = new byte[image.length - pos + header.length - 1]; + System.arraycopy(header, 0, newImage, 0, header.length); + System.arraycopy(image, pos + 1, newImage, header.length, image.length - pos - 1); + + return newImage; + } +} + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java new file mode 100644 index 000000000..69b01fbd9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDPixelMap.java @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.xobject; + +import java.awt.Transparency; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.io.OutputStream; + +import javax.imageio.ImageIO; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.PDStream; + +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; +import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased; +import org.apache.pdfbox.pdmodel.graphics.color.PDIndexed; + + + +/** + * This class contains a PixelMap Image. + * @author Ben Litchfield + * @author mathiak + * @version $Revision: 1.10 $ + */ +public class PDPixelMap extends PDXObjectImage +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDPixelMap.class); + + 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. + * + * @return {@inheritDoc} + * + * @throws IOException {@inheritDoc} + */ + public BufferedImage getRGBImage() throws IOException + { + if( image != null ) + { + return image; + } + + try + { + int width = getWidth(); + int height = getHeight(); + int bpc = getBitsPerComponent(); + + byte[] array = getPDStream().getByteArray(); + if (array.length == 0) + { + log.error("Something went wrong ... the pixelmap doesn't contain any data."); + return null; + } + // Get the ColorModel right + PDColorSpace colorspace = getColorSpace(); + if (colorspace == null) + { + log.error("getColorSpace() returned NULL. Predictor = " + getPredictor()); + return null; + } + + ColorModel cm = null; + if (colorspace instanceof PDIndexed) + { + PDIndexed csIndexed = (PDIndexed)colorspace; + ColorModel baseColorModel = csIndexed.getBaseColorSpace().createColorModel(bpc); + int size = csIndexed.getHighValue(); + byte[] index = csIndexed.getLookupData(); + boolean hasAlpha = baseColorModel.hasAlpha(); + COSArray maskArray = getMask(); + if( baseColorModel.getTransferType() != DataBuffer.TYPE_BYTE ) + { + throw new IOException( "Not implemented" ); + } + byte[] r = new byte[size+1]; + byte[] g = new byte[size+1]; + byte[] b = new byte[size+1]; + byte[] a = hasAlpha ? new byte[size+1] : null; + byte[] inData = new byte[baseColorModel.getNumComponents()]; + for( int i = 0; i <= size; i++ ) + { + System.arraycopy(index, i * inData.length, inData, 0, inData.length); + r[i] = (byte)baseColorModel.getRed(inData); + g[i] = (byte)baseColorModel.getGreen(inData); + b[i] = (byte)baseColorModel.getBlue(inData); + if( hasAlpha ) + { + a[i] = (byte)baseColorModel.getAlpha(inData); + } + } + if (hasAlpha) + { + cm = new IndexColorModel(bpc, size+1, r, g, b, a); + } + else + { + if (maskArray != null) + { + cm = new IndexColorModel(bpc, size+1, r, g, b, maskArray.getInt(0)); + } + else + { + cm = new IndexColorModel(bpc, size+1, r, g, b); + } + } + } + else if (bpc == 1) + { + byte[] map = null; + if (colorspace instanceof PDDeviceGray) + { + COSArray decode = getDecode(); + // we have to invert the b/w-values, + // if the Decode array exists and consists of (1,0) + if (decode != null && decode.getInt(0) == 1) + { + map = new byte[] {(byte)0xff}; + } + else + { + map = new byte[] {(byte)0x00, (byte)0xff}; + } + } + else if (colorspace instanceof PDICCBased) + { + if ( ((PDICCBased)colorspace).getNumberOfComponents() == 1) + { + map = new byte[] {(byte)0xff}; + } + else + { + map = new byte[] {(byte)0x00, (byte)0xff}; + } + } + else + { + map = new byte[] {(byte)0x00, (byte)0xff}; + } + cm = new IndexColorModel(bpc, map.length, map, map, map, Transparency.OPAQUE); + } + else + { + if (colorspace instanceof PDICCBased) + { + if (((PDICCBased)colorspace).getNumberOfComponents() == 1) + { + byte[] map = new byte[] {(byte)0xff}; + cm = new IndexColorModel(bpc, 1, map, map, map, Transparency.OPAQUE); + } + else + { + cm = colorspace.createColorModel( bpc ); + } + } + else + { + cm = colorspace.createColorModel( bpc ); + } + } + + log.debug("ColorModel: " + cm.toString()); + WritableRaster raster = cm.createCompatibleWritableRaster( width, height ); + DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer(); + byte[] bufferData = buffer.getData(); + + System.arraycopy( array, 0,bufferData, 0, + (array.length + *

    • 1 No prediction (the default value) + *
    • 2 TIFF Predictor 2 + *
    • 10 PNG prediction (on encoding, PNG None on all rows) + *
    • 11 PNG prediction (on encoding, PNG Sub on all rows) + *
    • 12 PNG prediction (on encoding, PNG Up on all rows) + *
    • 13 PNG prediction (on encoding, PNG Average on all rows) + *
    • 14 PNG prediction (on encoding, PNG Path on all rows) + *
    • 15 PNG prediction (on encoding, PNG optimum) + *
    + * + * Default value: 1. + * + * @return predictor algorithm code + */ + public int getPredictor() + { + COSDictionary decodeParms = getDecodeParams(); + if (decodeParms != null) + { + int i = decodeParms.getInt(COSName.PREDICTOR); + if (i != -1) + { + return i; + } + } + return 1; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObject.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObject.java new file mode 100644 index 000000000..9e31bca14 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObject.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.xobject; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDMetadata; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * The base class for all XObjects in the PDF document. + * + * @author Ben Litchfield + * @author mathiak + * @author Marcel Kammer + * @version $Revision: 1.14 $ + */ +public abstract class PDXObject implements COSObjectable +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDXObject.class); + + private PDStream xobject; + + /** + * Standard constructor. + * + * @param xobj The XObject dictionary. + */ + public PDXObject(COSStream xobj) + { + xobject = new PDStream( xobj ); + xobject.getStream().setName( COSName.TYPE, "XObject" ); + } + + /** + * Standard constuctor. + * + * @param xobj The XObject dictionary. + */ + public PDXObject(PDStream xobj) + { + xobject = xobj; + xobject.getStream().setName( COSName.TYPE, "XObject" ); + } + + /** + * 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. + * + * {@inheritDoc} + */ + 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( COSName.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 + { + log.warn( "Skipping unknown XObject subtype '" + subtype + "'" ); + } + } + 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( COSName.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( COSName.METADATA, meta ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java new file mode 100644 index 000000000..c89283840 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.xobject; + +import java.awt.geom.AffineTransform; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.util.Matrix; + +/** + * A form xobject. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +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 ); + getCOSStream().setName( COSName.SUBTYPE, SUB_TYPE ); + } + + /** + * Standard constuctor. + * + * @param formStream The XObject is passed as a COSStream. + */ + public PDXObjectForm(COSStream formStream) + { + super( formStream ); + getCOSStream().setName( COSName.SUBTYPE, SUB_TYPE ); + } + + /** + * 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 ); + } + + /** + * An array of four numbers in the form coordinate system (see + * below), giving the coordinates of the left, bottom, right, and top edges, + * respectively, of the form XObject's bounding box. These boundaries are used + * to clip the form XObject and to determine its size for caching. + * + * @return The BBox of the form. + */ + public PDRectangle getBBox() + { + PDRectangle retval = null; + COSArray array = (COSArray)getCOSStream().getDictionaryObject( COSName.BBOX ); + if( array != null ) + { + retval = new PDRectangle( array ); + } + return retval; + } + + /** + * This will set the BBox (bounding box) for this form. + * + * @param bbox The new BBox for this form. + */ + public void setBBox(PDRectangle bbox) + { + if( bbox == null ) + { + getCOSStream().removeItem( COSName.BBOX ); + } + else + { + getCOSStream().setItem( COSName.BBOX, bbox.getCOSArray() ); + } + } + + /** + * This will get the optional Matrix of an XObjectForm. + * It maps the form space into the user space + * @return the form matrix + */ + public Matrix getMatrix() + { + Matrix retval = null; + COSArray array = (COSArray)getCOSStream().getDictionaryObject( COSName.MATRIX ); + if( array != null ) + { + retval = new Matrix(); + retval.setValue(0, 0, ((COSNumber) array.get(0)).floatValue()); + retval.setValue(0, 1, ((COSNumber) array.get(1)).floatValue()); + retval.setValue(1, 0, ((COSNumber) array.get(2)).floatValue()); + retval.setValue(1, 1, ((COSNumber) array.get(3)).floatValue()); + retval.setValue(2, 0, ((COSNumber) array.get(4)).floatValue()); + retval.setValue(2, 1, ((COSNumber) array.get(5)).floatValue()); + } + return retval; + } + + /** + * Sets the optional Matrix entry for the form XObject. + * @param transform the transformation matrix + */ + public void setMatrix(AffineTransform transform) + { + COSArray matrix = new COSArray(); + double[] values = new double[6]; + transform.getMatrix(values); + for (double v : values) + { + matrix.add(new COSFloat((float)v)); + } + getCOSStream().setItem(COSName.MATRIX, matrix); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java new file mode 100644 index 000000000..c43b9f6c1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectImage.java @@ -0,0 +1,370 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.xobject; + +import java.awt.image.BufferedImage; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.File; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; +import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState; + +/** + * The prototype for all PDImages. + * + * @author Ben Litchfield + * @author mathiak + * @version $Revision: 1.9 $ + */ +public abstract class PDXObjectImage extends PDXObject +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDXObjectImage.class); + + /** + * The XObject subtype. + */ + public static final String SUB_TYPE = "Image"; + + /** + * This contains the suffix used when writing to file. + */ + private String suffix; + + private PDGraphicsState graphicsState; + + /** + * 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; + + /** + * Returns a PDXObjectImage of the SMask image, if there is one. + * See section 11.5 of the pdf specification for details on Soft Masks. + * + * @return the PDXObjectImage of the SMask if there is one, else null. + * @throws IOException if an I/O error occurs creating an XObject + */ + public PDXObjectImage getSMaskImage() throws IOException + { + COSStream cosStream = getPDStream().getStream(); + COSBase smask = cosStream.getDictionaryObject(COSName.SMASK); + + if (smask == null) + { + return null; + } + else + { + return (PDXObjectImage)PDXObject.createXObject(smask); + } + } + + /** + * 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(); + } + } + } + + /** + * Writes the image to a file with the filename + an appropriate +suffix, like "Image.jpg". + * The suffix is automatically set by the + * @param file the file + * @throws IOException When somethings wrong with the corresponding +file. + */ + public void write2file(File file) throws IOException + { + FileOutputStream out = null; + try + { + out = new FileOutputStream(file); + 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( COSName.HEIGHT, -1 ); + } + + /** + * Set the height of the image. + * + * @param height The height of the image. + */ + public void setHeight( int height ) + { + getCOSStream().setInt( COSName.HEIGHT, height ); + } + + /** + * Get the width of the image. + * + * @return The width of the image. + */ + public int getWidth() + { + return getCOSStream().getInt( COSName.WIDTH, -1 ); + } + + /** + * Set the width of the image. + * + * @param width The width of the image. + */ + public void setWidth( int width ) + { + getCOSStream().setInt( COSName.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 ); + if (retval == null) + { + log.info("About to return NULL from createColorSpace branch"); + } + } + 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(); + if (retval == null) + { + log.info("About to return NULL from CCITT branch"); + } + } + else if( COSName.JBIG2_DECODE.equals( filter ) ) + { + retval = new PDDeviceGray(); + if (retval == null) + { + log.info("About to return NULL from JBIG2 branch"); + } + } + else if (getImageMask()) + { + //Stencil Mask branch. Section 4.8.5 of the reference, page 350 in version 1.7. + retval = graphicsState.getNonStrokingColor().getColorSpace(); + log.info("Stencil Mask branch returning " + retval.toString()); + //throw new IOException("Trace the Stencil Mask!!!!"); + + } + else + { + log.info("About to return NULL from unhandled branch." + + " filter = " + filter); + } + } + 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.COLORSPACE, base ); + } + + /** + * This will get the suffix for this image type, jpg/png. + * + * @return The image suffix. + */ + public String getSuffix() + { + return suffix; + } + + /** + * Get the ImageMask flag. Used in Stencil Masking. Section 4.8.5 of the spec. + * + * @return The ImageMask flag. This is optional and defaults to False, so if it does not exist, we return False + */ + public boolean getImageMask() + { + return getCOSStream().getBoolean( COSName.IMAGE_MASK, false ); + } + + /** + * Allow the Invoke operator to set the graphics state so that, + * in the case of an Image Mask, we can get to the current nonstroking colorspace. + * @param newGS The new graphicstate + */ + public void setGraphicsState(PDGraphicsState newGS) + { + graphicsState = newGS; + } + + /** + * Returns the Decode Array of an XObjectImage. + * @return the decode array + */ + public COSArray getDecode() + { + COSBase decode = getCOSStream().getDictionaryObject( COSName.DECODE ); + if (decode != null && decode instanceof COSArray) + { + return (COSArray)decode; + } + return null; + } + + /** + * Returns the optional mask of a XObjectImage if there is one. + * + * @return The mask as COSArray otherwise null. + */ + public COSArray getMask() + { + COSBase mask = getCOSStream().getDictionaryObject(COSName.MASK); + if (mask != null) + { + return (COSArray)mask; + } + return null; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/package.html new file mode 100644 index 000000000..7dd05638f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/graphics/xobject/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package deals with images that are stored in a PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDActionFactory.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDActionFactory.java new file mode 100644 index 000000000..9ee54655c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDActionFactory.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action; + +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionGoTo; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionJavaScript; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionLaunch; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionRemoteGoTo; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionURI; + +/** + * This class will take a dictionary and determine which type of action to create. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +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; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java new file mode 100644 index 000000000..205836fce --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAdditionalActions.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +/** + * This represents a dictionary of actions that occur due to events. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java new file mode 100644 index 000000000..148e5a57d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDAnnotationAdditionalActions.java @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +/** + * This class represents an annotation's dictionary of actions + * that occur due to events. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.2 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java new file mode 100644 index 000000000..b53a5007f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDDocumentCatalogAdditionalActions.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +/** + * This class represents a document catalog's dictionary of actions + * that occur due to events. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.2 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java new file mode 100644 index 000000000..e9025d8c1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDFormFieldAdditionalActions.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +/** + * This class represents a form field's dictionary of actions + * that occur due to events. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.2 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java new file mode 100644 index 000000000..1432829e0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/PDPageAdditionalActions.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +/** + * This class represents a page object's dictionary of actions + * that occur due to events. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.2 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/package.html new file mode 100644 index 000000000..12075f57d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package represents actions that can be performed in a PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDAction.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDAction.java new file mode 100644 index 000000000..7da2537b3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDAction.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.PDDestinationOrAction; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; + +/** + * This represents an action that can be executed in a PDF document. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.3 $ + */ +public abstract class PDAction implements PDDestinationOrAction +{ + /** + * 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, COSName.getPDFName( "Next" )); + } + else if( next instanceof COSArray ) + { + COSArray array = (COSArray)next; + List actions = new ArrayList(); + for( int i=0; iBen Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.2 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java new file mode 100644 index 000000000..e28411193 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionJavaScript.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.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") ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java new file mode 100644 index 000000000..9b2943c88 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionLaunch.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification; + +/** + * This represents a launch action that can be executed in a PDF document. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.5 $ + */ +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. + * + * @throws IOException If there is an error creating the file spec. + */ + public PDFileSpecification getFile() throws IOException + { + 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java new file mode 100644 index 000000000..6d5b0e24f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionRemoteGoTo.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification; + +/** + * This represents a remote go-to action that can be executed in a PDF document. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.4 $ + */ +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. + * + * @throws IOException If there is an error creating the file spec. + */ + public PDFileSpecification getFile() throws IOException + { + 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionURI.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionURI.java new file mode 100644 index 000000000..bdbc12a25 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDActionURI.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This represents a URI action that can be executed in a PDF document. + * + * @author Ben Litchfield + * @author Panagiotis Toumasis (ptoumasis@mail.gr) + * @version $Revision: 1.3 $ + */ +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 this.action.getBoolean("IsMap", false); + } + + /** + * This will specify whether to track the mouse position when the URI is resolved. + * + * @param value The flag value. + */ + public void setTrackMousePosition( boolean value ) + { + this.action.setBoolean("IsMap", value); + } + + // TODO this must go into PDURIDictionary + /** + * 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 <BASE>, as described + * in the HTML 4.01 Specification. + * + * @return The URI entry of the specific URI dictionary. + * @deprecated use {@link PDURIDictionary#getBase()} instead + */ + public String getBase() + { + return action.getString( "Base" ); + } + + // TODO this must go into PDURIDictionary + /** + * 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 <BASE>, as described + * in the HTML 4.01 Specification. + * + * @param base The the base URI to be used. + * @deprecated use {@link PDURIDictionary#setBase(String)} instead + */ + public void setBase( String base ) + { + action.setString( "Base", base ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDURIDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDURIDictionary.java new file mode 100644 index 000000000..b54729498 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDURIDictionary.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This is the implementation of an URI dictionary. + * + * @version $Revision: 1.0 $ + * + */ +public class PDURIDictionary implements COSObjectable +{ + + private COSDictionary uriDictionary; + + /** + * Constructor. + * + */ + public PDURIDictionary() + { + this.uriDictionary = new COSDictionary(); + } + + /** + * Constructor. + * + * @param dictionary the corresponding dictionary + */ + public PDURIDictionary(COSDictionary dictionary) + { + this.uriDictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.uriDictionary; + } + + /** + * Returns the corresponding dictionary. + * @return the dictionary + */ + public COSDictionary getDictionary() + { + return this.uriDictionary; + } + + /** + * 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 <BASE>, as described + * in the HTML 4.01 Specification. + * + * @return The URI entry of the specific URI dictionary. + */ + public String getBase() + { + return this.getDictionary().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 <BASE>, as described + * in the HTML 4.01 Specification. + * + * @param base The the base URI to be used. + */ + public void setBase(String base) + { + this.getDictionary().setString("Base", base); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java new file mode 100644 index 000000000..486e6ebf5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/PDWindowsLaunchParams.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.action.type; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * Launch paramaters for the windows OS. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/package.html new file mode 100644 index 000000000..30831eba2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/action/type/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains all of the available PDF action types. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java new file mode 100644 index 000000000..476314389 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java @@ -0,0 +1,644 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; +import org.apache.pdfbox.util.BitFlagHelper; +import org.apache.pdfbox.cos.COSBase; + +/** + * This class represents a PDF annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.16 $ + */ +public abstract class PDAnnotation implements COSObjectable +{ + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDAnnotation.class); + + /** + * 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. + */ + // TODO not yet implemented: + // Movie, Screen, PrinterMark, TrapNet, Watermark, 3D, Redact + public static PDAnnotation createAnnotation( COSBase base ) throws IOException + { + PDAnnotation annot = null; + if( base instanceof COSDictionary ) + { + COSDictionary annotDic = (COSDictionary)base; + String subtype = annotDic.getNameAsString( COSName.SUBTYPE ); + if( PDAnnotationFileAttachment.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationFileAttachment( annotDic ); + } + else if( PDAnnotationLine.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationLine( annotDic ); + } + else if( PDAnnotationLink.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationLink(annotDic); + } + else if( PDAnnotationPopup.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationPopup(annotDic); + } + else if( PDAnnotationRubberStamp.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationRubberStamp(annotDic); + } + else if( PDAnnotationSquareCircle.SUB_TYPE_SQUARE.equals(subtype) || + PDAnnotationSquareCircle.SUB_TYPE_CIRCLE.equals(subtype) ) + { + annot = new PDAnnotationSquareCircle( annotDic ); + } + else if( PDAnnotationText.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationText( annotDic); + } + else if( PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT.equals(subtype) || + PDAnnotationTextMarkup.SUB_TYPE_UNDERLINE.equals(subtype) || + PDAnnotationTextMarkup.SUB_TYPE_SQUIGGLY.equals(subtype) || + PDAnnotationTextMarkup.SUB_TYPE_STRIKEOUT.equals(subtype) ) + { + annot = new PDAnnotationTextMarkup( annotDic ); + } + else if( PDAnnotationLink.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationLink( annotDic ); + } + else if( PDAnnotationWidget.SUB_TYPE.equals(subtype) ) + { + annot = new PDAnnotationWidget( annotDic ); + } + else if( PDAnnotationMarkup.SUB_TYPE_FREETEXT.equals(subtype) || + PDAnnotationMarkup.SUB_TYPE_POLYGON.equals(subtype) || + PDAnnotationMarkup.SUB_TYPE_POLYLINE.equals(subtype) || + PDAnnotationMarkup.SUB_TYPE_CARET.equals(subtype) || + PDAnnotationMarkup.SUB_TYPE_INK.equals(subtype) || + PDAnnotationMarkup.SUB_TYPE_SOUND.equals(subtype) ) + { + annot = new PDAnnotationMarkup( annotDic ); + } + else + { + annot = new PDAnnotationUnknown( annotDic ); + log.debug("Unknown or unsupported annotation subtype "+subtype); + } + } + else + { + throw new IOException( "Error: Unknown annotation type " + base ); + } + + return annot; + } + + /** + * Constructor. + */ + public PDAnnotation() + { + dictionary = new COSDictionary(); + dictionary.setItem( COSName.TYPE, COSName.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.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.RECT, rectangle.getCOSArray() ); + } + + /** + * This will get the flags for this field. + * + * @return flags The set of flags. + */ + public int getAnnotationFlags() + { + return getDictionary().getInt( COSName.F, 0 ); + } + + /** + * This will set the flags for this field. + * + * @param flags The new flags. + */ + public void setAnnotationFlags( int flags ) + { + getDictionary().setInt( COSName.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.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.AS ); + } + else + { + getDictionary().setItem( COSName.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.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.AP, ap ); + } + + /** + * Get the invisible flag. + * + * @return The invisible flag. + */ + public boolean isInvisible() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_INVISIBLE ); + } + + /** + * Set the invisible flag. + * + * @param invisible The new invisible flag. + */ + public void setInvisible( boolean invisible ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_INVISIBLE, invisible ); + } + + /** + * Get the hidden flag. + * + * @return The hidden flag. + */ + public boolean isHidden() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_HIDDEN ); + } + + /** + * Set the hidden flag. + * + * @param hidden The new hidden flag. + */ + public void setHidden( boolean hidden ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_HIDDEN, hidden ); + } + + /** + * Get the printed flag. + * + * @return The printed flag. + */ + public boolean isPrinted() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_PRINTED ); + } + + /** + * Set the printed flag. + * + * @param printed The new printed flag. + */ + public void setPrinted( boolean printed ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_PRINTED, printed ); + } + + /** + * Get the noZoom flag. + * + * @return The noZoom flag. + */ + public boolean isNoZoom() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_NO_ZOOM ); + } + + /** + * Set the noZoom flag. + * + * @param noZoom The new noZoom flag. + */ + public void setNoZoom( boolean noZoom ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_NO_ZOOM, noZoom ); + } + + /** + * Get the noRotate flag. + * + * @return The noRotate flag. + */ + public boolean isNoRotate() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_NO_ROTATE ); + } + + /** + * Set the noRotate flag. + * + * @param noRotate The new noRotate flag. + */ + public void setNoRotate( boolean noRotate ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_NO_ROTATE, noRotate ); + } + + /** + * Get the noView flag. + * + * @return The noView flag. + */ + public boolean isNoView() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_NO_VIEW ); + } + + /** + * Set the noView flag. + * + * @param noView The new noView flag. + */ + public void setNoView( boolean noView ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_NO_VIEW, noView ); + } + + /** + * Get the readOnly flag. + * + * @return The readOnly flag. + */ + public boolean isReadOnly() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_READ_ONLY ); + } + + /** + * Set the readOnly flag. + * + * @param readOnly The new readOnly flag. + */ + public void setReadOnly( boolean readOnly ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_READ_ONLY, readOnly ); + } + + /** + * Get the locked flag. + * + * @return The locked flag. + */ + public boolean isLocked() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_LOCKED ); + } + + /** + * Set the locked flag. + * + * @param locked The new locked flag. + */ + public void setLocked( boolean locked ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_LOCKED, locked ); + } + + /** + * Get the toggleNoView flag. + * + * @return The toggleNoView flag. + */ + public boolean isToggleNoView() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.F, FLAG_TOGGLE_NO_VIEW ); + } + + /** + * Set the toggleNoView flag. + * + * @param toggleNoView The new toggleNoView flag. + */ + public void setToggleNoView( boolean toggleNoView ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.F, FLAG_TOGGLE_NO_VIEW, toggleNoView ); + } + + /** + * 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); + } + + /** + * This will retrieve the date and time the annotation was modified. + * + * @return the modified date/time (often in date format, but can be an arbitary string). + */ + public String getModifiedDate() + { + return getDictionary().getString( COSName.M ); + } + + /** + * This will set the the date and time the annotation was modified. + * + * @param m + * the date and time the annotation was created. + */ + public void setModifiedDate( String m ) + { + getDictionary().setString( COSName.M, m ); + } + + /** + * This will get the name, a string intended to uniquely identify each annotation + * within a page. Not to be confused with some annotations Name entry which + * impact the default image drawn for them. + * + * @return The identifying name for the Annotation. + */ + public String getAnnotationName() + { + return getDictionary().getString( COSName.NM ); + } + + /** + * This will set the name, a string intended to uniquely identify each annotation + * within a page. Not to be confused with some annotations Name entry which + * impact the default image drawn for them. + * + * @param nm The identifying name for the annotation. + */ + public void setAnnotationName( String nm ) + { + getDictionary().setString( COSName.NM, nm ); + } + + /** + * This will set the colour used in drawing various elements. + * As of PDF 1.6 these are : Background of icon when closed + * Title bar of popup window + * Border of a link annotation + * + * Colour is in DeviceRGB colourspace + * + * @param c + * colour in the DeviceRGB colourspace + * + */ + public void setColour( PDGamma c ) + { + getDictionary().setItem( COSName.C, c ); + } + + /** + * This will retrieve the colour used in drawing various elements. + * As of PDF 1.6 these are : Background of icon when closed + * Title bar of popup window + * Border of a link annotation + * + * Colour is in DeviceRGB colourspace + * + * @return PDGamma object representing the colour + * + */ + public PDGamma getColour() + { + COSArray c = (COSArray) getDictionary().getItem(COSName.C ); + if (c != null) + { + return new PDGamma( c ); + } + else + { + return null; + } + } + + /** + * This will retrieve the subtype of the annotation. + * + * @return the subtype + */ + public String getSubtype() + { + return this.getDictionary().getNameAsString(COSName.SUBTYPE); + } + + /** + * This will set the corresponding page for this annotation. + * + * @param page is the corresponding page + */ + public void setPage(PDPage page) + { + this.getDictionary().setItem(COSName.P, page); + } + + /** + * This will retrieve the corresponding page of this annotation. + * + * @return the corresponding page + */ + public PDPage getPage() + { + COSDictionary p = (COSDictionary) this.getDictionary().getDictionaryObject(COSName.P); + if (p != null) + { + return new PDPage(p); + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationFileAttachment.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationFileAttachment.java new file mode 100644 index 000000000..ce0f7e9d6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationFileAttachment.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification; + +/** + * This is the class that represents a file attachement. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class PDAnnotationFileAttachment extends PDAnnotationMarkup +{ + /** + * See get/setAttachmentName. + */ + public static final String ATTACHMENT_NAME_PUSH_PIN = "PushPin"; + /** + * See get/setAttachmentName. + */ + public static final String ATTACHMENT_NAME_GRAPH = "Graph"; + /** + * See get/setAttachmentName. + */ + public static final String ATTACHMENT_NAME_PAPERCLIP = "Paperclip"; + /** + * See get/setAttachmentName. + */ + public static final String ATTACHMENT_NAME_TAG = "Tag"; + + /** + * The type of annotation. + */ + public static final String SUB_TYPE = "FileAttachment"; + + /** + * Constructor. + */ + public PDAnnotationFileAttachment() + { + super(); + getDictionary().setItem( COSName.SUBTYPE, COSName.getPDFName( SUB_TYPE ) ); + } + + /** + * Creates a Link annotation from a COSDictionary, expected to be + * a correct object definition. + * + * @param field the PDF objet to represent as a field. + */ + public PDAnnotationFileAttachment(COSDictionary field) + { + super( field ); + } + + /** + * Return the attached file. + * + * @return The attached file. + * + * @throws IOException If there is an error creating the file spec. + */ + public PDFileSpecification getFile() throws IOException + { + return PDFileSpecification.createFS( getDictionary().getDictionaryObject( "FS" ) ); + } + + /** + * Set the attached file. + * + * @param file The file that is attached. + */ + public void setFile( PDFileSpecification file ) + { + getDictionary().setItem( "FS", file ); + } + + /** + * This is the name used to draw the type of attachment. + * See the ATTACHMENT_NAME_XXX constants. + * + * @return The name that describes the visual cue for the attachment. + */ + public String getAttachmentName() + { + return getDictionary().getNameAsString( "Name", ATTACHMENT_NAME_PUSH_PIN ); + } + + /** + * Set the name used to draw the attachement icon. + * See the ATTACHMENT_NAME_XXX constants. + * + * @param name The name of the visual icon to draw. + */ + public void setAttachementName( String name ) + { + getDictionary().setName( "Name", name ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLine.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLine.java new file mode 100644 index 000000000..f16924db2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLine.java @@ -0,0 +1,493 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; + +/** + * This is the class that represents a line annotation. + * Introduced in PDF 1.3 specification + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDAnnotationLine extends PDAnnotationMarkup +{ + + + /* + * The various values for intent (get/setIT, see the PDF 1.6 reference Table + * 8.22 + */ + + /** + * Constant for annotation intent of Arrow. + */ + public static final String IT_LINE_ARROW = "LineArrow"; + + /** + * Constant for annotation intent of a dimension line. + */ + public static final String IT_LINE_DIMENSION = "LineDimension"; + + /* + * The various values for line ending styles, see the PDF 1.6 reference + * Table 8.23 + */ + + /** + * Constant for a square line ending. + */ + public static final String LE_SQUARE = "Square"; + + /** + * Constant for a circle line ending. + */ + public static final String LE_CIRCLE = "Circle"; + + /** + * Constant for a diamond line ending. + */ + public static final String LE_DIAMOND = "Diamond"; + + /** + * Constant for a open arrow line ending. + */ + public static final String LE_OPEN_ARROW = "OpenArrow"; + + /** + * Constant for a closed arrow line ending. + */ + public static final String LE_CLOSED_ARROW = "ClosedArrow"; + + /** + * Constant for no line ending. + */ + public static final String LE_NONE = "None"; + + /** + * Constant for a butt line ending. + */ + public static final String LE_BUTT = "Butt"; + + /** + * Constant for a reversed open arrow line ending. + */ + public static final String LE_R_OPEN_ARROW = "ROpenArrow"; + + /** + * Constant for a revered closed arrow line ending. + */ + public static final String LE_R_CLOSED_ARROW = "RClosedArrow"; + + /** + * Constant for a slash line ending. + */ + public static final String LE_SLASH = "Slash"; + + /** + * The type of annotation. + */ + public static final String SUB_TYPE = "Line"; + + /** + * Constructor. + */ + public PDAnnotationLine() + { + super(); + getDictionary().setItem( COSName.SUBTYPE, COSName.getPDFName( SUB_TYPE ) ); + // Dictionary value L is mandatory, fill in with arbitary value + setLine( new float[] { 0, 0, 0, 0 } ); + + } + + /** + * Creates a Line annotation from a COSDictionary, expected to be a correct + * object definition. + * + * @param field + * the PDF object to represent as a field. + */ + public PDAnnotationLine( COSDictionary field ) + { + super( field ); + } + + /** + * This will set start and end coordinates of the line (or leader line if LL + * entry is set). + * + * @param l + * array of 4 floats [x1, y1, x2, y2] line start and end points + * in default user space. + */ + public void setLine( float[] l ) + { + COSArray newL = new COSArray(); + newL.setFloatArray( l ); + getDictionary().setItem( "L", newL ); + } + + /** + * This will retrieve the start and end coordinates of the line (or leader + * line if LL entry is set). + * + * @return array of floats [x1, y1, x2, y2] line start and end points in + * default user space. + */ + public float[] getLine() + { + COSArray l = (COSArray) getDictionary().getDictionaryObject( "L" ); + return l.toFloatArray(); + } + + /** + * This will set the line ending style for the start point, + * see the LE_ constants for the possible values. + * + * @param style The new style. + */ + public void setStartPointEndingStyle( String style ) + { + if( style == null ) + { + style = LE_NONE; + } + COSArray array = (COSArray)getDictionary().getDictionaryObject( "LE" ); + if( array == null ) + { + array = new COSArray(); + array.add( COSName.getPDFName( style ) ); + array.add( COSName.getPDFName( LE_NONE ) ); + getDictionary().setItem( "LE", array ); + } + else + { + array.setName( 0, style ); + } + } + + /** + * This will retrieve the line ending style for the start point, + * possible values shown in the LE_ constants section. + * + * @return The ending style for the start point. + */ + public String getStartPointEndingStyle() + { + String retval = LE_NONE; + COSArray array = (COSArray)getDictionary().getDictionaryObject( "LE" ); + if( array != null ) + { + retval = array.getName( 0 ); + } + + return retval; + } + + /** + * This will set the line ending style for the end point, + * see the LE_ constants for the possible values. + * + * @param style The new style. + */ + public void setEndPointEndingStyle( String style ) + { + if( style == null ) + { + style = LE_NONE; + } + COSArray array = (COSArray)getDictionary().getDictionaryObject( "LE" ); + if( array == null ) + { + array = new COSArray(); + array.add( COSName.getPDFName( LE_NONE ) ); + array.add( COSName.getPDFName( style ) ); + getDictionary().setItem( "LE", array ); + } + else + { + array.setName( 1, style ); + } + } + + /** + * This will retrieve the line ending style for the end point, + * possible values shown in the LE_ constants section. + * + * @return The ending style for the end point. + */ + public String getEndPointEndingStyle() + { + String retval = LE_NONE; + COSArray array = (COSArray)getDictionary().getDictionaryObject( "LE" ); + if( array != null ) + { + retval = array.getName( 1 ); + } + + return retval; + } + + /** + * This will set interior colour of the line endings defined in the LE + * entry. Colour is in DeviceRGB colourspace. + * + * @param ic + * colour in the DeviceRGB colourspace. + * + */ + public void setInteriorColour( PDGamma ic ) + { + getDictionary().setItem( "IC", ic ); + } + + /** + * This will retrieve the interior colour of the line endings defined in the + * LE entry. Colour is in DeviceRGB colourspace. + * + * + * @return PDGamma object representing the colour. + * + */ + public PDGamma getInteriorColour() + { + + COSArray ic = (COSArray) getDictionary().getDictionaryObject( "IC" ); + if (ic != null) + { + return new PDGamma( ic ); + } + else + { + return null; + } + } + + /** + * This will set if the contents are shown as a caption to the line. + * + * @param cap + * Boolean value. + */ + public void setCaption( boolean cap ) + { + getDictionary().setBoolean( "Cap", cap ); + } + + /** + * This will retrieve if the contents are shown as a caption or not. + * + * @return boolean if the content is shown as a caption. + */ + public boolean getCaption() + { + return getDictionary().getBoolean( "Cap", false ); + } + + /** + * This will set the border style dictionary, specifying the width and dash + * pattern used in drawing the line. + * + * @param bs the border style dictionary to set. + * + */ + public void setBorderStyle( PDBorderStyleDictionary bs ) + { + this.getDictionary().setItem( "BS", bs); + } + + /** + * This will retrieve the border style dictionary, specifying the width and + * dash pattern used in drawing the line. + * + * @return the border style dictionary. + */ + public PDBorderStyleDictionary getBorderStyle() + { + COSDictionary bs = (COSDictionary) this.getDictionary().getItem( + COSName.getPDFName( "BS" ) ); + if (bs != null) + { + return new PDBorderStyleDictionary( bs ); + } + else + { + return null; + } + } + + /** + * This will retrieve the length of the leader line. + * + * @return the length of the leader line + */ + public float getLeaderLineLength() + { + return this.getDictionary().getFloat("LL"); + } + + /** + * This will set the length of the leader line. + * + * @param leaderLineLength length of the leader line + */ + public void setLeaderLineLength(float leaderLineLength) + { + this.getDictionary().setFloat("LL", leaderLineLength); + } + + /** + * This will retrieve the length of the leader line extensions. + * + * @return the length of the leader line extensions + */ + public float getLeaderLineExtensionLength() + { + return this.getDictionary().getFloat("LLE"); + } + + /** + * This will set the length of the leader line extensions. + * + * @param leaderLineExtensionLength length of the leader line extensions + */ + public void setLeaderLineExtensionLength(float leaderLineExtensionLength) + { + this.getDictionary().setFloat("LLE", leaderLineExtensionLength); + } + + /** + * This will retrieve the length of the leader line offset. + * + * @return the length of the leader line offset + */ + public float getLeaderLineOffsetLength() + { + return this.getDictionary().getFloat("LLO"); + } + + /** + * This will set the length of the leader line offset. + * + * @param leaderLineOffsetLength length of the leader line offset + */ + public void setLeaderLineOffsetLength(float leaderLineOffsetLength) + { + this.getDictionary().setFloat("LLO", leaderLineOffsetLength); + } + + /** + * This will retrieve the caption positioning. + * + * @return the caption positioning + */ + public String getCaptionPositioning() + { + return this.getDictionary().getString("CP"); + } + + /** + * This will set the caption positioning. + * Allowed values are: "Inline" and "Top" + * + * @param captionPositioning caption positioning + */ + public void setCaptionPositioning(String captionPositioning) + { + this.getDictionary().setString("CP", captionPositioning); + } + + /** + * This will set the horizontal offset of the caption. + * + * @param offset the horizontal offset of the caption + */ + public void setCaptionHorizontalOffset( float offset ) + { + COSArray array = (COSArray)this.getDictionary().getDictionaryObject( "CO" ); + if( array == null ) + { + array = new COSArray(); + array.setFloatArray(new float[] {offset, 0.f}); + this.getDictionary().setItem( "CO", array ); + } + else + { + array.set(0, new COSFloat(offset) ); + } + } + + /** + * This will retrieve the horizontal offset of the caption. + * + * @return the the horizontal offset of the caption + */ + public float getCaptionHorizontalOffset() + { + float retval = 0.f; + COSArray array = (COSArray)this.getDictionary().getDictionaryObject( "CO" ); + if( array != null ) + { + retval = array.toFloatArray()[0]; + } + + return retval; + } + + /** + * This will set the vertical offset of the caption. + * + * @param offset vertical offset of the caption + */ + public void setCaptionVerticalOffset( float offset ) + { + COSArray array = (COSArray)this.getDictionary().getDictionaryObject( "CO" ); + if( array == null ) + { + array = new COSArray(); + array.setFloatArray(new float[] {0.f, offset}); + this.getDictionary().setItem( "CO", array ); + } + else + { + array.set(1, new COSFloat(offset) ); + } + } + + /** + * This will retrieve the vertical offset of the caption. + * + * @return the vertical offset of the caption + */ + public float getCaptionVerticalOffset() + { + float retval = 0.f; + COSArray array = (COSArray)this.getDictionary().getDictionaryObject( "CO" ); + if( array != null ) + { + retval = array.toFloatArray()[1]; + } + return retval; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLink.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLink.java new file mode 100644 index 000000000..1b64bc49b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationLink.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; + +/** + * This is the class that represents a link annotation. + * + * @author Ben Litchfield + * @author Paul King + * @version $Revision: 1.3 $ + */ +public class PDAnnotationLink extends PDAnnotation +{ + + + /** + * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. + */ + public static final String HIGHLIGHT_MODE_NONE = "N"; + /** + * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. + */ + public static final String HIGHLIGHT_MODE_INVERT = "I"; + /** + * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. + */ + public static final String HIGHLIGHT_MODE_OUTLINE = "O"; + /** + * Constant values of the Text as defined in the PDF 1.6 reference Table 8.19. + */ + public static final String HIGHLIGHT_MODE_PUSH = "P"; + + + /** + * The type of annotation. + */ + public static final String SUB_TYPE = "Link"; + + /** + * Constructor. + */ + public PDAnnotationLink() + { + super(); + getDictionary().setItem( COSName.SUBTYPE, COSName.getPDFName( SUB_TYPE ) ); + } + + /** + * Creates a Link annotation from a COSDictionary, expected to be + * a correct object definition. + * + * @param field the PDF objet to represent as a field. + */ + public PDAnnotationLink(COSDictionary field) + { + super( field ); + } + + /** + * Get the action to be performed when this annotation is to be activated. + * + * @return The action to be performed when this annotation is activated. + * + * TODO not all annotations have an A entry + */ + public PDAction getAction() + { + COSDictionary action = (COSDictionary) + this.getDictionary().getDictionaryObject( COSName.A ); + return PDActionFactory.createAction( action ); + } + + /** + * Set the annotation action. + * As of PDF 1.6 this is only used for Widget Annotations + * @param action The annotation action. + * TODO not all annotations have an A entry + */ + public void setAction( PDAction action ) + { + this.getDictionary().setItem( COSName.A, action ); + } + + /** + * This will set the border style dictionary, specifying the width and dash + * pattern used in drawing the line. + * + * @param bs the border style dictionary to set. + * TODO not all annotations may have a BS entry + * + */ + public void setBorderStyle( PDBorderStyleDictionary bs ) + { + this.getDictionary().setItem( "BS", bs); + } + + /** + * This will retrieve the border style dictionary, specifying the width and + * dash pattern used in drawing the line. + * + * @return the border style dictionary. + * TODO not all annotations may have a BS entry + */ + public PDBorderStyleDictionary getBorderStyle() + { + COSDictionary bs = (COSDictionary) this.getDictionary().getItem( + COSName.getPDFName( "BS" ) ); + if (bs != null) + { + return new PDBorderStyleDictionary( bs ); + } + else + { + return null; + } + } + + /** + * Get the destination to be displayed when the annotation is activated. Either + * this or the A should be set but not both. + * + * @return The destination for this annotation. + * + * @throws IOException If there is an error creating the destination. + */ + public PDDestination getDestination() throws IOException + { + COSBase base = getDictionary().getDictionaryObject( COSName.DEST ); + PDDestination retval = PDDestination.create( base ); + + return retval; + } + + /** + * The new destination value. + * + * @param dest The updated destination. + */ + public void setDestination( PDDestination dest ) + { + getDictionary().setItem( COSName.DEST, dest ); + } + + /** + * Set the highlight mode for when the mouse is depressed. + * See the HIGHLIGHT_MODE_XXX constants. + * + * @return The string representation of the highlight mode. + */ + public String getHighlightMode() + { + return getDictionary().getNameAsString( COSName.H, HIGHLIGHT_MODE_INVERT ); + } + + /** + * Set the highlight mode. See the HIGHLIGHT_MODE_XXX constants. + * + * @param mode The new highlight mode. + */ + public void setHighlightMode( String mode ) + { + getDictionary().setName( COSName.H, mode ); + } + + /** + * This will set the previous URI action, in case it + * needs to be retrieved at later date. + * + * @param pa The previous URI. + */ + public void setPreviousURI( PDActionURI pa ) + { + getDictionary().setItem( "PA", pa ); + } + + /** + * This will set the previous URI action, in case it's + * needed. + * + * @return The previous URI. + */ + public PDActionURI getPreviousURI() + { + COSDictionary pa = (COSDictionary) getDictionary().getDictionaryObject("PA"); + if ( pa != null ) + { + return new PDActionURI( pa ); + } + else + { + return null; + } + } + + /** + * This will set the set of quadpoints which encompass the areas of this + * annotation which will activate. + * + * @param quadPoints + * an array representing the set of area covered. + */ + public void setQuadPoints( float[] quadPoints ) + { + COSArray newQuadPoints = new COSArray(); + newQuadPoints.setFloatArray( quadPoints ); + getDictionary().setItem( "QuadPoints", newQuadPoints ); + } + + /** + * This will retrieve the set of quadpoints which encompass the areas of + * this annotation which will activate. + * + * @return An array of floats representing the quad points. + */ + public float[] getQuadPoints() + { + COSArray quadPoints = (COSArray) getDictionary().getDictionaryObject( "QuadPoints" ); + if (quadPoints != null) + { + return quadPoints.toFloatArray(); + } + else + { + return null; // Should never happen as this is a required item + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationMarkup.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationMarkup.java new file mode 100644 index 000000000..80cc5a9af --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationMarkup.java @@ -0,0 +1,343 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.PDTextStream; +import org.apache.pdfbox.cos.COSBase; + +import java.io.IOException; + +import java.util.Calendar; + +/** + * This class represents the additonal fields of a Markup type Annotation. See + * section 12.5.6 of ISO32000-1:2008 (starting with page 390) for details on + * annotation types. + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDAnnotationMarkup extends PDAnnotation +{ + /** + * Constant for a FreeText type of annotation. + */ + public static final String SUB_TYPE_FREETEXT = "FreeText"; + /** + * Constant for an Polygon type of annotation. + */ + public static final String SUB_TYPE_POLYGON = "Polygon"; + /** + * Constant for an PolyLine type of annotation. + */ + public static final String SUB_TYPE_POLYLINE = "PolyLine"; + /** + * Constant for an Caret type of annotation. + */ + public static final String SUB_TYPE_CARET = "Caret"; + /** + * Constant for an Ink type of annotation. + */ + public static final String SUB_TYPE_INK = "Ink"; + /** + * Constant for an Sound type of annotation. + */ + public static final String SUB_TYPE_SOUND = "Sound"; + + /* + * The various values of the reply type as defined in the PDF 1.7 reference + * Table 170 + */ + + /** + * Constant for an annotation reply type. + */ + public static final String RT_REPLY = "R"; + + /** + * Constant for an annotation reply type. + */ + public static final String RT_GROUP = "Group"; + + /** + * Constructor. + */ + public PDAnnotationMarkup() + { + super(); + } + + /** + * Constructor. + * + * @param dict + * The annotations dictionary. + */ + public PDAnnotationMarkup( COSDictionary dict ) + { + super( dict ); + } + + /** + * Retrieve the string used as the title of the popup window shown when open + * and active (by convention this identifies who added the annotation). + * + * @return The title of the popup. + */ + public String getTitlePopup() + { + return getDictionary().getString( "T" ); + } + + /** + * Set the string used as the title of the popup window shown when open and + * active (by convention this identifies who added the annotation). + * + * @param t + * The title of the popup. + */ + public void setTitlePopup( String t ) + { + getDictionary().setString( "T", t ); + } + + /** + * This will retrieve the popup annotation used for entering/editing the + * text for this annotation. + * + * @return the popup annotation. + */ + public PDAnnotationPopup getPopup() + { + COSDictionary popup = (COSDictionary) getDictionary().getDictionaryObject( "Popup" ); + if (popup != null) + { + return new PDAnnotationPopup( popup ); + } + else + { + return null; + } + } + + /** + * This will set the popup annotation used for entering/editing the text for + * this annotation. + * + * @param popup + * the popup annotation. + */ + public void setPopup( PDAnnotationPopup popup ) + { + getDictionary().setItem( "Popup", popup ); + } + + /** + * This will retrieve the constant opacity value used when rendering the + * annotation (excluing any popup). + * + * @return the constant opacity value. + */ + public float getConstantOpacity() + { + return getDictionary().getFloat( "CA", 1 ); + } + + /** + * This will set the constant opacity value used when rendering the + * annotation (excluing any popup). + * + * @param ca + * the constant opacity value. + */ + public void setConstantOpacity( float ca ) + { + getDictionary().setFloat( "CA", ca ); + } + + /** + * This will retrieve the rich text stream which is displayed in the popup + * window. + * + * @return the rich text stream. + */ + public PDTextStream getRichContents() + { + COSBase rc = getDictionary().getDictionaryObject( "RC" ); + if (rc != null) + { + return PDTextStream.createTextStream( rc ); + } + else + { + return null; + } + } + + /** + * This will set the rich text stream which is displayed in the popup window. + * + * @param rc + * the rich text stream. + */ + public void setRichContents( PDTextStream rc ) + { + getDictionary().setItem( "RC", rc); + } + + /** + * This will retrieve the date and time the annotation was created. + * + * @return the creation date/time. + * @throws IOException + * if there is a format problem when converting the date. + */ + public Calendar getCreationDate() throws IOException + { + return getDictionary().getDate( "CreationDate" ); + } + + /** + * This will set the the date and time the annotation was created. + * + * @param creationDate + * the date and time the annotation was created. + */ + public void setCreationDate( Calendar creationDate ) + { + getDictionary().setDate( "CreationDate", creationDate ); + } + + /** + * This will retrieve the annotation to which this one is "In Reply To" the + * actual relationship is specified by the RT entry. + * + * @return the other annotation. + * @throws IOException + * if there is an error with the annotation. + */ + public PDAnnotation getInReplyTo() throws IOException + { + COSBase irt = getDictionary().getDictionaryObject( "IRT" ); + return PDAnnotation.createAnnotation( irt ); + } + + /** + * This will set the annotation to which this one is "In Reply To" the + * actual relationship is specified by the RT entry. + * + * @param irt the annotation this one is "In Reply To". + */ + public void setInReplyTo( PDAnnotation irt ) + { + getDictionary().setItem( "IRT", irt ); + } + + /** + * This will retrieve the short description of the subject of the annotation. + * + * @return the subject. + */ + public String getSubject() + { + return getDictionary().getString( "Subj" ); + } + + /** + * This will set the short description of the subject of the annotation. + * + * @param subj short description of the subject. + */ + public void setSubject( String subj ) + { + getDictionary().setString( "Subj", subj ); + } + + /** + * This will retrieve the Reply Type (relationship) with the annotation in + * the IRT entry See the RT_* constants for the available values. + * + * @return the relationship. + */ + public String getReplyType() + { + return getDictionary().getNameAsString( "RT", RT_REPLY ); + } + + /** + * This will set the Reply Type (relationship) with the annotation in the + * IRT entry See the RT_* constants for the available values. + * + * @param rt the reply type. + */ + public void setReplyType( String rt ) + { + getDictionary().setName( "RT", rt ); + } + + /** + * This will retrieve the intent of the annotation The values and meanings + * are specific to the actual annotation See the IT_* constants for the + * annotation classes. + * + * @return the intent + */ + public String getIntent() + { + return getDictionary().getNameAsString( "IT" ); + } + + /** + * This will set the intent of the annotation The values and meanings are + * specific to the actual annotation See the IT_* constants for the + * annotation classes. + * + * @param it the intent + */ + public void setIntent( String it ) + { + getDictionary().setName( "IT", it ); + } + + /** + * This will return the external data dictionary. + * + * @return the external data dictionary + */ + public PDExternalDataDictionary getExternalData() + { + COSBase exData = this.getDictionary().getDictionaryObject("ExData"); + if (exData instanceof COSDictionary) + { + return new PDExternalDataDictionary((COSDictionary) exData); + } + return null; + } + + /** + * This will set the external data dictionary. + * + * @param externalData the external data dictionary + */ + public void setExternalData(PDExternalDataDictionary externalData) + { + this.getDictionary().setItem("ExData", externalData); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationPopup.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationPopup.java new file mode 100644 index 000000000..2f5359ef5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationPopup.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is the class that represents a popup annotation. + * Introduced in PDF 1.3 specification + * + * @author Paul King + * @version $Revision: 1.2 $ + */ +public class PDAnnotationPopup extends PDAnnotation +{ + + /** + * The type of annotation. + */ + public static final String SUB_TYPE = "Popup"; + + /** + * Constructor. + */ + public PDAnnotationPopup() + { + super(); + getDictionary() + .setItem( COSName.SUBTYPE, COSName.getPDFName( SUB_TYPE ) ); + } + + /** + * Creates a popup annotation from a COSDictionary, expected to be a correct + * object definition. + * + * @param field + * the PDF objet to represent as a field. + */ + public PDAnnotationPopup( COSDictionary field ) + { + super( field ); + } + + /** + * This will set inital state of the annotation, open or closed. + * + * @param open + * Boolean value, true = open false = closed. + */ + public void setOpen( boolean open ) + { + getDictionary().setBoolean( "Open" , open ); + } + + /** + * This will retrieve the initial state of the annotation, open Or closed + * (default closed). + * + * @return The initial state, true = open false = closed. + */ + public boolean getOpen() + { + return getDictionary().getBoolean( "Open" , false ); + } + + /** + * This will set the markup annotation which this popup relates to. + * + * @param annot + * the markup annotation. + */ + public void setParent( PDAnnotationMarkup annot ) + { + getDictionary().setItem( COSName.PARENT, annot.getDictionary() ); + } + + /** + * This will retrieve the markup annotation which this popup relates to. + * + * @return The parent markup annotation. + */ + public PDAnnotationMarkup getParent() + { + PDAnnotationMarkup am = null; + try + { + am = (PDAnnotationMarkup) + PDAnnotation.createAnnotation( getDictionary().getDictionaryObject( "Parent", "P" ) ); + } + catch (IOException ioe) + { + // Couldn't construct the annotation, so return null i.e. do nothing + } + return am; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java new file mode 100644 index 000000000..9eb87bd51 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationRubberStamp.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is the class that represents a rubber stamp annotation. + * Introduced in PDF 1.3 specification + * + * @author Paul King + * @version $Revision: 1.2 $ + */ +public class PDAnnotationRubberStamp extends PDAnnotationMarkup +{ + + /* + * 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); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationSquareCircle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationSquareCircle.java new file mode 100644 index 000000000..3cbc9ec1e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationSquareCircle.java @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +/** + * This is the class that represents a rectangular or eliptical annotation + * Introduced in PDF 1.3 specification . + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDAnnotationSquareCircle extends PDAnnotationMarkup +{ + + /** + * Constant for a Rectangular type of annotation. + */ + public static final String SUB_TYPE_SQUARE = "Square"; + /** + * Constant for an Eliptical type of annotation. + */ + public static final String SUB_TYPE_CIRCLE = "Circle"; + + /** + * Creates a Circle or Square annotation of the specified sub type. + * + * @param subType the subtype the annotation represents. + */ + public PDAnnotationSquareCircle( String subType ) + { + super(); + setSubtype( subType ); + } + + /** + * Creates a Line annotation from a COSDictionary, expected to be a correct + * object definition. + * + * @param field + * the PDF objet to represent as a field. + */ + public PDAnnotationSquareCircle( COSDictionary field ) + { + super( field ); + } + + + /** + * This will set interior colour of the drawn area + * Colour is in DeviceRGB colourspace. + * + * @param ic + * colour in the DeviceRGB colourspace. + * + */ + public void setInteriorColour( PDGamma ic ) + { + getDictionary().setItem( "IC", ic ); + } + + /** + * This will retrieve the interior colour of the drawn area + * Colour is in DeviceRGB colourspace. + * + * + * @return PDGamma object representing the colour. + * + */ + public PDGamma getInteriorColour() + { + + COSArray ic = (COSArray) getDictionary().getItem( + COSName.getPDFName( "IC" ) ); + if (ic != null) + { + return new PDGamma( ic ); + } + else + { + return null; + } + } + + + /** + * This will set the border effect dictionary, specifying effects to be applied + * when drawing the line. + * + * @param be The border effect dictionary to set. + * + */ + public void setBorderEffect( PDBorderEffectDictionary be ) + { + getDictionary().setItem( "BE", be ); + } + + /** + * This will retrieve the border effect dictionary, specifying effects to be + * applied used in drawing the line. + * + * @return The border effect dictionary + */ + public PDBorderEffectDictionary getBorderEffect() + { + COSDictionary be = (COSDictionary) getDictionary().getDictionaryObject( "BE" ); + if (be != null) + { + return new PDBorderEffectDictionary( be ); + } + else + { + return null; + } + } + + /** + * This will set the rectangle difference rectangle. Giving the difference + * between the annotations rectangle and where the drawing occurs. + * (To take account of any effects applied through the BE entry forexample) + * + * @param rd the rectangle difference + * + */ + public void setRectDifference( PDRectangle rd ) + { + getDictionary().setItem( "RD", rd ); + } + + /** + * This will get the rectangle difference rectangle. Giving the difference + * between the annotations rectangle and where the drawing occurs. + * (To take account of any effects applied through the BE entry forexample) + * + * @return the rectangle difference + */ + public PDRectangle getRectDifference() + { + COSArray rd = (COSArray) getDictionary().getDictionaryObject( "RD" ); + if (rd != null) + { + return new PDRectangle( rd ); + } + else + { + return null; + } + } + + /** + * This will set the sub type (and hence appearance, AP taking precedence) For + * this annotation. See the SUB_TYPE_XXX constants for valid values. + * + * @param subType The subtype of the annotation + */ + public void setSubtype( String subType ) + { + getDictionary().setName( COSName.SUBTYPE, subType ); + } + + /** + * This will retrieve the sub type (and hence appearance, AP taking precedence) + * For this annotation. + * + * @return The subtype of this annotation, see the SUB_TYPE_XXX constants. + */ + public String getSubtype() + { + return getDictionary().getNameAsString( COSName.SUBTYPE); + } + + /** + * This will set the border style dictionary, specifying the width and dash + * pattern used in drawing the line. + * + * @param bs the border style dictionary to set. + * TODO not all annotations may have a BS entry + * + */ + public void setBorderStyle( PDBorderStyleDictionary bs ) + { + this.getDictionary().setItem( "BS", bs); + } + + /** + * This will retrieve the border style dictionary, specifying the width and + * dash pattern used in drawing the line. + * + * @return the border style dictionary. + * TODO not all annotations may have a BS entry + */ + public PDBorderStyleDictionary getBorderStyle() + { + COSDictionary bs = (COSDictionary) this.getDictionary().getItem( + COSName.getPDFName( "BS" ) ); + if (bs != null) + { + return new PDBorderStyleDictionary( bs ); + } + else + { + return null; + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationText.java new file mode 100644 index 000000000..0894cba1a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationText.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This is the class that represents a text annotation. + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDAnnotationText extends PDAnnotationMarkup +{ + + /* + * The various values of the Text as defined in the PDF 1.7 reference Table + * 172 + */ + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_COMMENT = "Comment"; + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_KEY = "Key"; + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_NOTE = "Note"; + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_HELP = "Help"; + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_NEW_PARAGRAPH = "NewParagraph"; + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_PARAGRAPH = "Paragraph"; + + /** + * Constant for the name of a text annotation. + */ + public static final String NAME_INSERT = "Insert"; + + /** + * The type of annotation. + */ + public static final String SUB_TYPE = "Text"; + + /** + * Constructor. + */ + public PDAnnotationText() + { + super(); + getDictionary() + .setItem( COSName.SUBTYPE, COSName.getPDFName( SUB_TYPE ) ); + } + + /** + * Creates a Text annotation from a COSDictionary, expected to be a correct + * object definition. + * + * @param field + * the PDF object to represent as a field. + */ + public PDAnnotationText( COSDictionary field ) + { + super( field ); + } + + /** + * This will set initial state of the annotation, open or closed. + * + * @param open + * Boolean value, true = open false = closed + */ + public void setOpen( boolean open ) + { + getDictionary().setBoolean( COSName.getPDFName( "Open" ), open ); + } + + /** + * This will retrieve the initial state of the annotation, open Or closed + * (default closed). + * + * @return The initial state, true = open false = closed + */ + public boolean getOpen() + { + return getDictionary().getBoolean( COSName.getPDFName( "Open" ), false ); + } + + /** + * 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 annotation + */ + 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 NOTE. + * + * @return The name of this annotation, see the NAME_XXX constants. + */ + public String getName() + { + return getDictionary().getNameAsString( COSName.NAME, NAME_NOTE ); + } + + /** + * This will retrieve the annotation state. + * + * @return the annotation state + */ + public String getState() + { + return this.getDictionary().getString("State"); + } + + /** + * This will set the annotation state. + * + * @param state the annotation state + */ + public void setState(String state) + { + this.getDictionary().setString("State", state); + } + + /** + * This will retrieve the annotation state model. + * + * @return the annotation state model + */ + public String getStateModel() + { + return this.getDictionary().getString("StateModel"); + } + + /** + * This will set the annotation state model. + * Allowed values are "Marked" and "Review" + * + * @param stateModel the annotation state model + */ + public void setStateModel(String stateModel) + { + this.getDictionary().setString("StateModel", stateModel); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationTextMarkup.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationTextMarkup.java new file mode 100644 index 000000000..5351df799 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationTextMarkup.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSName; + +/** + * This is the abstract class that represents a text markup annotation + * Introduced in PDF 1.3 specification, except Squiggly lines in 1.4. + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDAnnotationTextMarkup extends PDAnnotationMarkup +{ + + /** + * The types of annotation. + */ + public static final String SUB_TYPE_HIGHLIGHT = "Highlight"; + /** + * The types of annotation. + */ + public static final String SUB_TYPE_UNDERLINE = "Underline"; + /** + * The types of annotation. + */ + public static final String SUB_TYPE_SQUIGGLY = "Squiggly"; + /** + * The types of annotation. + */ + public static final String SUB_TYPE_STRIKEOUT = "StrikeOut"; + + + private PDAnnotationTextMarkup() + { + // Must be constructed with a subType or dictionary parameter + } + + /** + * Creates a TextMarkup annotation of the specified sub type. + * + * @param subType the subtype the annotation represents + */ + public PDAnnotationTextMarkup(String subType) + { + super(); + setSubtype( subType ); + + // Quad points are required, set and empty array + setQuadPoints( new float[0] ); + } + + /** + * Creates a TextMarkup annotation from a COSDictionary, expected to be a + * correct object definition. + * + * @param field the PDF objet to represent as a field. + */ + public PDAnnotationTextMarkup( COSDictionary field ) + { + super( field ); + } + + /** + * This will set the set of quadpoints which encompass the areas of this + * annotation. + * + * @param quadPoints + * an array representing the set of area covered + */ + public void setQuadPoints( float[] quadPoints ) + { + COSArray newQuadPoints = new COSArray(); + newQuadPoints.setFloatArray( quadPoints ); + getDictionary().setItem( "QuadPoints", newQuadPoints ); + } + + /** + * This will retrieve the set of quadpoints which encompass the areas of + * this annotation. + * + * @return An array of floats representing the quad points. + */ + public float[] getQuadPoints() + { + COSArray quadPoints = (COSArray) getDictionary().getDictionaryObject( "QuadPoints" ); + if (quadPoints != null) + { + return quadPoints.toFloatArray(); + } + else + { + return null; // Should never happen as this is a required item + } + } + + /** + * This will set the sub type (and hence appearance, AP taking precedence) For + * this annotation. See the SUB_TYPE_XXX constants for valid values. + * + * @param subType The subtype of the annotation + */ + public void setSubtype( String subType ) + { + getDictionary().setName( COSName.SUBTYPE, subType ); + } + + /** + * This will retrieve the sub type (and hence appearance, AP taking precedence) + * For this annotation. + * + * @return The subtype of this annotation, see the SUB_TYPE_XXX constants. + */ + public String getSubtype() + { + return getDictionary().getNameAsString( COSName.SUBTYPE); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java new file mode 100644 index 000000000..db5b5dc2b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationUnknown.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java new file mode 100644 index 000000000..69aeafbe6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotationWidget.java @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; +import org.apache.pdfbox.pdmodel.interactive.action.PDAnnotationAdditionalActions; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; + +/** + * This is the class that represents a widget. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class PDAnnotationWidget extends PDAnnotation +{ + /** + * The type of annotation. + */ + public static final String SUB_TYPE = "Widget"; + + + /** + * Constructor. + */ + public PDAnnotationWidget() + { + super(); + getDictionary().setName( COSName.SUBTYPE, SUB_TYPE); + } + + + /** + * 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 ); + } + + /** + * Returns the highlighting mode. Default value: I + *
    + *
    N
    + *
    (None) No highlighting.
    + *
    I
    + *
    (Invert) Invert the contents of the annotation rectangle.
    + *
    O
    + *
    (Outline) Invert the annotation's border.
    + *
    P
    + *
    (Push) Display the annotation's down appearance, if any. If no + * down appearance is defined, the contents of the annotation rectangle + * shall be offset to appear as if it were pushed below the surface of + * the page
    + *
    T
    + *
    (Toggle) Same as P (which is preferred).
    + *
    + * + * @return the highlighting mode + */ + public String getHighlightingMode() + { + return this.getDictionary().getNameAsString(COSName.H, "I"); + } + + /** + * Sets the highlighting mode. + *
    + *
    N
    + *
    (None) No highlighting.
    + *
    I
    + *
    (Invert) Invert the contents of the annotation rectangle.
    + *
    O
    + *
    (Outline) Invert the annotation's border.
    + *
    P
    + *
    (Push) Display the annotation's down appearance, if any. If no + * down appearance is defined, the contents of the annotation rectangle + * shall be offset to appear as if it were pushed below the surface of + * the page
    + *
    T
    + *
    (Toggle) Same as P (which is preferred).
    + *
    + * + * @param highlightingMode the highlighting mode + * the defined values + */ + public void setHighlightingMode(String highlightingMode) + { + if ((highlightingMode == null) + || "N".equals(highlightingMode) || "I".equals(highlightingMode) + || "O".equals(highlightingMode) || "P".equals(highlightingMode) + || "T".equals(highlightingMode)) + { + this.getDictionary().setName(COSName.H, highlightingMode); + } + else + { + throw new IllegalArgumentException( "Valid values for highlighting mode are " + + "'N', 'N', 'O', 'P' or 'T'" ); + } + } + + /** + * Returns the appearance characteristics dictionary. + * + * @return the appearance characteristics dictionary + */ + public PDAppearanceCharacteristicsDictionary getAppearanceCharacteristics() + { + COSBase mk = this.getDictionary().getDictionaryObject(COSName.getPDFName("MK")); + if (mk instanceof COSDictionary) + { + return new PDAppearanceCharacteristicsDictionary((COSDictionary) mk); + } + return null; + } + + /** + * Sets the appearance characteristics dictionary. + * + * @param appearanceCharacteristics the appearance characteristics dictionary + */ + public void setAppearanceCharacteristics(PDAppearanceCharacteristicsDictionary appearanceCharacteristics) + { + this.getDictionary().setItem("MK", appearanceCharacteristics); + } + + /** + * Get the action to be performed when this annotation is to be activated. + * + * @return The action to be performed when this annotation is activated. + */ + public PDAction getAction() + { + COSDictionary action = (COSDictionary) + this.getDictionary().getDictionaryObject( COSName.A ); + return PDActionFactory.createAction( action ); + } + + /** + * Set the annotation action. + * As of PDF 1.6 this is only used for Widget Annotations + * @param action The annotation action. + */ + public void setAction( PDAction action ) + { + this.getDictionary().setItem( COSName.A, action ); + } + + /** + * Get the additional actions for this field. This will return null + * if there are no additional actions for this field. + * As of PDF 1.6 this is only used for Widget Annotations. + * + * @return The actions of the field. + */ + public PDAnnotationAdditionalActions getActions() + { + COSDictionary aa = (COSDictionary)this.getDictionary().getDictionaryObject( "AA" ); + PDAnnotationAdditionalActions retval = null; + if( aa != null ) + { + retval = new PDAnnotationAdditionalActions( aa ); + } + return retval; + } + + /** + * Set the actions of the field. + * + * @param actions The field actions. + */ + public void setActions( PDAnnotationAdditionalActions actions ) + { + this.getDictionary().setItem( "AA", actions ); + } + + /** + * This will set the border style dictionary, specifying the width and dash + * pattern used in drawing the line. + * + * @param bs the border style dictionary to set. + * + */ + public void setBorderStyle( PDBorderStyleDictionary bs ) + { + this.getDictionary().setItem( "BS", bs); + } + + /** + * This will retrieve the border style dictionary, specifying the width and + * dash pattern used in drawing the line. + * + * @return the border style dictionary. + */ + public PDBorderStyleDictionary getBorderStyle() + { + COSDictionary bs = (COSDictionary) this.getDictionary().getItem( + COSName.getPDFName( "BS" ) ); + if (bs != null) + { + return new PDBorderStyleDictionary( bs ); + } + else + { + return null; + } + } + + // TODO where to get acroForm from? +// public PDField getParent() throws IOException +// { +// COSBase parent = this.getDictionary().getDictionaryObject(COSName.PARENT); +// if (parent instanceof COSDictionary) +// { +// PDAcroForm acroForm = null; +// return PDFieldFactory.createField(acroForm, (COSDictionary) parent); +// } +// return null; +// } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceCharacteristicsDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceCharacteristicsDictionary.java new file mode 100644 index 000000000..3dc1e5545 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceCharacteristicsDictionary.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm; + +/** + * This class represents an appearance characteristics dictionary. + * + * @version $Revision: 1.0 $ + * + */ +public class PDAppearanceCharacteristicsDictionary implements COSObjectable +{ + + private COSDictionary dictionary; + + /** + * Constructor. + * + * @param dict dictionary + */ + public PDAppearanceCharacteristicsDictionary(COSDictionary dict) + { + this.dictionary = dict; + } + + + /** + * returns the dictionary. + * @return the dictionary + */ + public COSDictionary getDictionary() + { + return this.dictionary; + } + + /** + * {@inheritDoc} + * + */ + public COSBase getCOSObject() + { + return this.dictionary; + } + + /** + * This will retrieve the rotation of the annotation widget. + * It must be a multiple of 90. Default is 0 + * @return the rotation + */ + public int getRotation() + { + return this.getDictionary().getInt(COSName.R, 0); + } + + /** + * This will set the rotation. + * + * @param rotation the rotation as a multiple of 90 + */ + public void setRotation(int rotation) + { + this.getDictionary().setInt(COSName.R, rotation); + } + + /** + * This will retrieve the border color. + * + * @return the border color. + */ + public PDGamma getBorderColour() + { + COSBase c = this.getDictionary().getItem(COSName.getPDFName("BC")); + if (c instanceof COSArray) + { + return new PDGamma((COSArray) c); + } + return null; + } + + /** + * This will set the border color. + * + * @param c the border color + */ + public void setBorderColour(PDGamma c) + { + this.getDictionary().setItem("BC", c); + } + + /** + * This will retrieve the background color. + * + * @return the background color. + */ + public PDGamma getBackground() + { + COSBase c = this.getDictionary().getItem(COSName.getPDFName("BG")); + if (c instanceof COSArray) + { + return new PDGamma((COSArray) c); + } + return null; + } + + /** + * This will set the background color. + * + * @param c the background color + */ + public void setBackground(PDGamma c) + { + this.getDictionary().setItem("BG", c); + } + + /** + * This will retrieve the normal caption. + * + * @return the normal caption. + */ + public String getNormalCaption() + { + return this.getDictionary().getString("CA"); + } + + /** + * This will set the normal caption. + * + * @param caption the normal caption + */ + public void setNormalCaption(String caption) + { + this.getDictionary().setString("CA", caption); + } + + /** + * This will retrieve the rollover caption. + * + * @return the rollover caption. + */ + public String getRolloverCaption() + { + return this.getDictionary().getString("RC"); + } + + /** + * This will set the rollover caption. + * + * @param caption the rollover caption + */ + public void setRolloverCaption(String caption) + { + this.getDictionary().setString("RC", caption); + } + + /** + * This will retrieve the alternate caption. + * + * @return the alternate caption. + */ + public String getAlternateCaption() + { + return this.getDictionary().getString("AC"); + } + + /** + * This will set the alternate caption. + * + * @param caption the alternate caption + */ + public void setAlternateCaption(String caption) + { + this.getDictionary().setString("AC", caption); + } + + /** + * This will retrieve the normal icon. + * + * @return the normal icon. + */ + public PDXObjectForm getNormalIcon() + { + COSBase i = this.getDictionary().getDictionaryObject("I"); + if (i instanceof COSStream) + { + return new PDXObjectForm((COSStream) i); + } + return null; + } + + /** + * This will retrieve the rollover icon. + * + * @return the rollover icon + */ + public PDXObjectForm getRolloverIcon() + { + COSBase i = this.getDictionary().getDictionaryObject("RI"); + if (i instanceof COSStream) + { + return new PDXObjectForm((COSStream) i); + } + return null; + } + + /** + * This will retrieve the alternate icon. + * + * @return the alternate icon. + */ + public PDXObjectForm getAlternateIcon() + { + COSBase i = this.getDictionary().getDictionaryObject("IX"); + if (i instanceof COSStream) + { + return new PDXObjectForm((COSStream) i); + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java new file mode 100644 index 000000000..e3d029a08 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceDictionary.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.COSDictionaryMap; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class represents a PDF /AP entry the appearance dictionary. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 == null ) + { + return null; + } + else 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 ); + for( COSName asName : map.keySet() ) + { + 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 ) + { + COSStream aux = (COSStream) ap; + ap = new COSDictionary(); + ((COSDictionary)ap).setItem(COSName.getPDFName( "default" ), aux ); + } + COSDictionary map = (COSDictionary)ap; + Map actuals = new HashMap(); + retval = new COSDictionaryMap( actuals, map ); + for( COSName asName : map.keySet() ) + { + 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 ) + { + COSStream aux = (COSStream) ap; + ap = new COSDictionary(); + ((COSDictionary)ap).setItem(COSName.getPDFName( "default" ), aux ); + } + COSDictionary map = (COSDictionary)ap; + Map actuals = + new HashMap(); + retval = new COSDictionaryMap( actuals, map ); + for( COSName asName : map.keySet() ) + { + 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 ) ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java new file mode 100644 index 000000000..30154ab02 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceStream.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +import org.apache.pdfbox.pdmodel.PDResources; + + +/** + * This class represents an appearance for an annotation. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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; + } + + /** + * {@inheritDoc} + */ + 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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderEffectDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderEffectDictionary.java new file mode 100644 index 000000000..4f51baace --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderEffectDictionary.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents a PDF /BE entry the border effect dictionary. + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDBorderEffectDictionary implements COSObjectable +{ + + /* + * The various values of the effect applied to the border as defined in the + * PDF 1.6 reference Table 8.14 + */ + + /** + * Constant for the name for no effect. + */ + public static final String STYLE_SOLID = "S"; + + /** + * Constant for the name of a cloudy effect. + */ + public static final String STYLE_CLOUDY = "C"; + + private COSDictionary dictionary; + + /** + * Constructor. + */ + public PDBorderEffectDictionary() + { + dictionary = new COSDictionary(); + } + + /** + * Constructor. + * + * @param dict + * a border style dictionary. + */ + public PDBorderEffectDictionary( 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 set the intensity of the applied effect. + * + * @param i + * the intensity of the effect values 0 to 2 + */ + public void setIntensity( float i ) + { + getDictionary().setFloat( "I", i ); + } + + /** + * This will retrieve the intensity of the applied effect. + * + * @return the intensity value 0 to 2 + */ + public float getIntensity() + { + return getDictionary().getFloat( "I", 0 ); + } + + /** + * This will set the border effect, see the STYLE_* constants for valid values. + * + * @param s + * the border effect to use + */ + public void setStyle( String s ) + { + getDictionary().setName( "S", s ); + } + + /** + * This will retrieve the border effect, see the STYLE_* constants for valid + * values. + * + * @return the effect of the border + */ + public String getStyle() + { + return getDictionary().getNameAsString( "S", STYLE_SOLID ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderStyleDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderStyleDictionary.java new file mode 100644 index 000000000..1425e6546 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDBorderStyleDictionary.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSInteger; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.PDLineDashPattern; + +/** + * This class represents a PDF /BS entry the border style dictionary. + * + * @author Paul King + * @version $Revision: 1.1 $ + */ +public class PDBorderStyleDictionary implements COSObjectable +{ + + /* + * The various values of the style for the border as defined in the PDF 1.6 + * reference Table 8.13 + */ + + /** + * Constant for the name of a solid style. + */ + public static final String STYLE_SOLID = "S"; + + /** + * Constant for the name of a dashed style. + */ + public static final String STYLE_DASHED = "D"; + + /** + * Constant for the name of a beveled style. + */ + public static final String STYLE_BEVELED = "B"; + + /** + * Constant for the name of a inset style. + */ + public static final String STYLE_INSET = "I"; + + /** + * Constant for the name of a underline style. + */ + public static final String STYLE_UNDERLINE = "U"; + + private COSDictionary dictionary; + + /** + * Constructor. + */ + public PDBorderStyleDictionary() + { + dictionary = new COSDictionary(); + } + + /** + * Constructor. + * + * @param dict + * a border style dictionary. + */ + public PDBorderStyleDictionary( 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 set the border width in points, 0 = no border. + * + * @param w + * float the width in points + */ + public void setWidth( float w ) + { + getDictionary().setFloat( "W", w ); + } + + /** + * This will retrieve the border width in points, 0 = no border. + * + * @return flaot the width of the border in points + */ + public float getWidth() + { + return getDictionary().getFloat( "W", 1 ); + } + + /** + * This will set the border style, see the STYLE_* constants for valid values. + * + * @param s + * the border style to use + */ + public void setStyle( String s ) + { + getDictionary().setName( "S", s ); + } + + /** + * This will retrieve the border style, see the STYLE_* constants for valid + * values. + * + * @return the style of the border + */ + public String getStyle() + { + return getDictionary().getNameAsString( "S", STYLE_SOLID ); + } + + /** + * This will set the dash style used for drawing the border. + * + * @param d + * the dash style to use + */ + public void setDashStyle( PDLineDashPattern d ) + { + COSArray array = null; + if( d != null ) + { + array = d.getCOSDashPattern(); + } + getDictionary().setItem( "D", array ); + } + + /** + * This will retrieve the dash style used for drawing the border. + * + * @return the dash style of the border + */ + public PDLineDashPattern getDashStyle() + { + COSArray d = (COSArray) getDictionary().getDictionaryObject( "D" ); + if (d == null) + { + d = new COSArray(); + d.add( COSInteger.THREE ); + getDictionary().setItem( "D", d ); + } + return new PDLineDashPattern( d, 0 ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDExternalDataDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDExternalDataDictionary.java new file mode 100644 index 000000000..066f9ed37 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/PDExternalDataDictionary.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.annotation; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents an external data dictionary. + * + * @version $Revision: 1.0 $ + * + */ +public class PDExternalDataDictionary implements COSObjectable +{ + + private COSDictionary dataDictionary; + + /** + * Constructor. + */ + public PDExternalDataDictionary() + { + this.dataDictionary = new COSDictionary(); + this.dataDictionary.setName(COSName.TYPE, "ExData"); + } + + /** + * Constructor. + * + * @param dictionary Dictionary + */ + public PDExternalDataDictionary(COSDictionary dictionary) + { + this.dataDictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.dataDictionary; + } + + /** + * returns the dictionary. + * + * @return the dictionary + */ + public COSDictionary getDictionary() + { + return this.dataDictionary; + } + + /** + * returns the type of the external data dictionary. + * It must be "ExData", if present + * @return the type of the external data dictionary + */ + public String getType() + { + return this.getDictionary().getNameAsString(COSName.TYPE, "ExData"); + } + + /** + * returns the subtype of the external data dictionary. + * @return the subtype of the external data dictionary + */ + public String getSubtype() + { + return this.getDictionary().getNameAsString(COSName.SUBTYPE); + } + + /** + * This will set the subtype of the external data dictionary. + * @param subtype the subtype of the external data dictionary + */ + public void setSubtype(String subtype) + { + this.getDictionary().setName(COSName.SUBTYPE, subtype); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/package.html new file mode 100644 index 000000000..ab9660fdf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/annotation/package.html @@ -0,0 +1,25 @@ + + + + + + + +The annotation package contains classes that work with PDF annotation elements. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java new file mode 100644 index 000000000..2822d14d5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java @@ -0,0 +1,330 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.digitalsignature; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Calendar; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdfwriter.COSFilterInputStream; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This represents a digital signature that can be attached to a document. + * + * @author Ben Litchfield + * @author Thomas Chojecki + * @version $Revision: 1.2 $ + */ +public class PDSignature implements COSObjectable +{ + + private COSDictionary dictionary; + + /** + * A signature filter value. + */ + public static final COSName FILTER_ADOBE_PPKLITE = COSName.ADOBE_PPKLITE; + + /** + * A signature filter value. + */ + public static final COSName FILTER_ENTRUST_PPKEF = COSName.ENTRUST_PPKEF; + + /** + * A signature filter value. + */ + public static final COSName FILTER_CICI_SIGNIT = COSName.CICI_SIGNIT; + + /** + * A signature filter value. + */ + public static final COSName FILTER_VERISIGN_PPKVS = COSName.VERISIGN_PPKVS; + + /** + * A signature subfilter value. + */ + public static final COSName SUBFILTER_ADBE_X509_RSA_SHA1 = COSName.ADBE_X509_RSA_SHA1; + + /** + * A signature subfilter value. + */ + public static final COSName SUBFILTER_ADBE_PKCS7_DETACHED = COSName.ADBE_PKCS7_DETACHED; + + /** + * A signature subfilter value. + */ + public static final COSName SUBFILTER_ETSI_CADES_DETACHED = COSName.getPDFName("ETSI.CAdES.detached"); + + /** + * A signature subfilter value. + */ + public static final COSName SUBFILTER_ADBE_PKCS7_SHA1 = COSName.ADBE_PKCS7_SHA1; + + /** + * Default constructor. + */ + public PDSignature() + { + dictionary = new COSDictionary(); + dictionary.setItem(COSName.TYPE, COSName.SIG); + } + + /** + * Constructor. + * + * @param dict The signature dictionary. + */ + public PDSignature(COSDictionary dict) + { + dictionary = dict; + } + + /** + * Convert this standard java object to a COS object. + * + * @return The cos object that matches this Java object. + */ + public COSBase getCOSObject() + { + return getDictionary(); + } + + /** + * Convert this standard java object to a COS dictionary. + * + * @return The COS dictionary that matches this Java object. + */ + public COSDictionary getDictionary() + { + return dictionary; + } + + public void setFilter(COSName filter) + { + dictionary.setItem(COSName.FILTER, filter); + } + + /** + * Set a subfilter that specify the signature that should be used. + * + * @param subfilter the subfilter that shall be used. + */ + public void setSubFilter(COSName subfilter) + { + dictionary.setItem(COSName.SUBFILTER, subfilter); + } + + public void setName(String name) + { + dictionary.setString(COSName.NAME, name); + } + + public void setLocation(String location) + { + dictionary.setString(COSName.LOCATION, location); + } + + public void setReason(String reason) + { + dictionary.setString(COSName.REASON, reason); + } + + public void setSignDate(Calendar cal) + { + dictionary.setDate("M", cal); + } + + public String getFilter() + { + return ((COSName)dictionary.getItem(COSName.FILTER)).getName(); + } + + public String getSubFilter() + { + return ((COSName)dictionary.getItem(COSName.SUBFILTER)).getName(); + } + + public String getName() + { + return dictionary.getString(COSName.NAME); + } + + public String getLocation() + { + return dictionary.getString(COSName.LOCATION); + } + + public String getReason() + { + return dictionary.getString(COSName.REASON); + } + + public Calendar getSignDate() + { + try + { + return dictionary.getDate("M"); + } + catch (IOException e) + { + return null; + } + } + + public void setByteRange(int[] range) + { + if (range.length!=4) + return; + + COSArray ary = new COSArray(); + for ( int i : range ) + { + ary.add(COSInteger.get(i)); + } + + dictionary.setItem("ByteRange", ary); + } + + /** + * Read out the byterange from the file + * + * @return a integer array with the byterange + */ + public int[] getByteRange() + { + COSArray byteRange = (COSArray)dictionary.getDictionaryObject("ByteRange"); + int[] ary = new int[byteRange.size()]; + for (int i = 0; i and ) + else if(buffer[c-1]==0x3E || buffer[c-1]==0x29) + byteOS.write(buffer, 0, c-1); + else + byteOS.write(buffer, 0, c); + } + fis.close(); + + return COSString.createFromHexString(byteOS.toString()).getBytes(); + } + + public void setContents(byte[] bytes) + { + COSString string = new COSString(bytes); + string.setForceHexForm(true); + dictionary.setItem("Contents", string); + } + + /** + * Will return the signed content of the document. + * + * @param pdfFile The signed pdf file as InputStream + * @return a byte array containing only the signed part of the content + * @throws IOException if the pdfFile can't be read + */ + public byte[] getSignedContent(InputStream pdfFile) throws IOException + { + COSFilterInputStream fis=null; + + try + { + fis = new COSFilterInputStream(pdfFile,getByteRange()); + return fis.toByteArray(); + } + finally + { + if (fis != null) + fis.close(); + } + } + + /** + * Will return the signed content of the document. + * + * @param pdfFile The signed pdf file as byte array + * @return a byte array containing only the signed part of the content + * @throws IOException if the pdfFile can't be read + */ + public byte[] getSignedContent(byte[] pdfFile) throws IOException + { + COSFilterInputStream fis=null; + + try + { + fis = new COSFilterInputStream(pdfFile,getByteRange()); + return fis.toByteArray(); + } + finally + { + if (fis != null) + fis.close(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureInterface.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureInterface.java new file mode 100644 index 000000000..c264399a9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureInterface.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.digitalsignature; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.exceptions.SignatureException; + +/** + * Providing an interface for accessing necessary functions for signing a pdf document. + * + * @author Thomas Chojecki + * @version $ + */ +public interface SignatureInterface +{ + + /** + * Creates a cms signature for the given content + * + * @param content is the content as a (Filter)InputStream + * @return signature as a byte array + */ + public byte[] sign (InputStream content) throws SignatureException, IOException; + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java new file mode 100644 index 000000000..918ad53ef --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.digitalsignature; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.pdfparser.VisualSignatureParser; + + +public class SignatureOptions +{ + private COSDocument visualSignature; + + private int preferedSignatureSize; + + private int pageNo; + + /** + * Set the page number. + * + * @param pageNo the page number + * + */ + public void setPage(int pageNo) + { + this.pageNo = pageNo; + } + + /** + * Get the page number. + * + * @return the page number + */ + public int getPage() + { + return pageNo; + } + + /** + * Reads the visual signature from the given input stream. + * + * @param is the input stream containing the visual signature + * + * @throws IOException when something went wrong during parsing + */ + public void setVisualSignature(InputStream is) throws IOException + { + VisualSignatureParser visParser = new VisualSignatureParser(is); + visParser.parse(); + visualSignature = visParser.getDocument(); + } + + /** + * Get the visual signature. + * + * @return the visual signature + */ + public COSDocument getVisualSignature() + { + return visualSignature; + } + + /** + * Get the preferred size of the signature. + * + * @return the preferred size + */ + public int getPreferedSignatureSize() + { + return preferedSignatureSize; + } + + /** + * Set the preferred size of the signature. + * + * @param size the size of the signature + */ + public void setPreferedSignatureSize(int size) + { + if (size > 0) + { + preferedSignatureSize = size; + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/package.html new file mode 100644 index 000000000..0c8ed05cd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/digitalsignature/package.html @@ -0,0 +1,25 @@ + + + + + + + +The digitial signature library will manage signatures that are stored in the PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java new file mode 100644 index 000000000..086a642f2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDDestination.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.common.PDDestinationOrAction; + +/** + * This represents a destination in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.6 $ + */ +public abstract class PDDestination implements PDDestinationOrAction +{ + + /** + * 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java new file mode 100644 index 000000000..d421b3509 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDNamedDestination.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +/** + * This represents a destination to a page by referencing it with a name. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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("ISO-8859-1") ); + } + else if( dest == null ) + { + namedDestination = null; + } + else + { + namedDestination = new COSString( dest ); + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java new file mode 100644 index 000000000..aed658310 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageDestination.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageNode; + +/** + * This represents a destination to a page, see subclasses for specific parameters. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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; + } + + /** + * Returns the page number for this destination, regardless of whether + * this is a page number or a reference to a page. + * + * @since Apache PDFBox 1.0.0 + * @see PDOutlineItem + * @return page number, or -1 if the destination type is unknown + */ + public int findPageNumber() { + int retval = -1; + if( array.size() > 0 ) + { + COSBase page = array.getObject( 0 ); + if( page instanceof COSNumber ) + { + retval = ((COSNumber)page).intValue(); + } + else if (page instanceof COSDictionary) + { + COSBase parent = page; + while (((COSDictionary) parent).getDictionaryObject("Parent", "P") != null) { + parent = ((COSDictionary) parent).getDictionaryObject( "Parent", "P" ); + } + // now parent is the pages node + PDPageNode pages = new PDPageNode((COSDictionary) parent); + List allPages = new ArrayList(); + pages.getAllKids(allPages); + retval = allPages.indexOf(new PDPage((COSDictionary) page)) + 1; + } + } + 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java new file mode 100644 index 000000000..8544a05c8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitDestination.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java new file mode 100644 index 000000000..549a186f2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitHeightDestination.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java new file mode 100644 index 000000000..cba5ccf83 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitRectangleDestination.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java new file mode 100644 index 000000000..0085f9d48 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageFitWidthDestination.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java new file mode 100644 index 000000000..876281400 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/PDPageXYZDestination.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.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 + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html new file mode 100644 index 000000000..4a2f5b7d1 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/destination/package.html @@ -0,0 +1,25 @@ + + + + + + + +The destination package allows destinations into a pdf document to be specified. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java new file mode 100644 index 000000000..74c92ea1d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline; + +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This represents an outline in a pdf document. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java new file mode 100644 index 000000000..a15c63023 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java @@ -0,0 +1,418 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline; + +import java.awt.Color; +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.exceptions.OutlineNotLocalException; +import org.apache.pdfbox.pdmodel.PDDestinationNameTreeNode; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction; +import org.apache.pdfbox.pdmodel.interactive.action.type.PDActionGoTo; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination; +import org.apache.pdfbox.util.BitFlagHelper; + +/** + * This represents an outline in a pdf document. + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +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 ); + } + + /** + * {@inheritDoc} + */ + public PDOutlineNode getParent() + { + return super.getParent(); + } + + /** + * 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( COSName.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( COSName.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( COSName.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( COSName.NEXT, outlineNode ); + } + + /** + * Get the title of this node. + * + * @return The title of this node. + */ + public String getTitle() + { + return node.getString( COSName.TITLE ); + } + + /** + * Set the title for this node. + * + * @param title The new title for this node. + */ + public void setTitle( String title ) + { + node.setString( COSName.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( COSName.DEST ) ); + } + + /** + * Set the page destination for this node. + * + * @param dest The new page destination for this node. + */ + public void setDestination( PDDestination dest ) + { + node.setItem( COSName.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( COSName.A ) ); + } + + /** + * Set the action for this node. + * + * @param action The new action for this node. + */ + public void setAction( PDAction action ) + { + node.setItem( COSName.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( COSName.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 setStructuredElement( PDStructureElement structureElement ) + { + node.setItem( COSName.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 PDColorState getTextColor() + { + PDColorState retval = null; + COSArray csValues = (COSArray)node.getDictionaryObject( COSName.C ); + if( csValues == null ) + { + csValues = new COSArray(); + csValues.growToSize( 3, new COSFloat( 0 ) ); + node.setItem( COSName.C, csValues ); + } + retval = new PDColorState(csValues); + retval.setColorSpace( PDDeviceRGB.INSTANCE ); + return retval; + } + + /** + * Set the text color for this node. The colorspace must be a PDDeviceRGB. + * + * @param textColor The text color for this node. + */ + public void setTextColor( PDColorState textColor ) + { + node.setItem( COSName.C, textColor.getCOSColorSpaceValue() ); + } + + /** + * Set the text color for this node. The colorspace must be a PDDeviceRGB. + * + * @param textColor The text color for this node. + */ + public void setTextColor( Color textColor ) + { + COSArray array = new COSArray(); + array.add( new COSFloat( textColor.getRed()/255f)); + array.add( new COSFloat( textColor.getGreen()/255f)); + array.add( new COSFloat( textColor.getBlue()/255f)); + node.setItem( COSName.C, array ); + } + + /** + * A flag telling if the text should be italic. + * + * @return The italic flag. + */ + public boolean isItalic() + { + return BitFlagHelper.getFlag( node, COSName.F, ITALIC_FLAG ); + } + + /** + * Set the italic property of the text. + * + * @param italic The new italic flag. + */ + public void setItalic( boolean italic ) + { + BitFlagHelper.setFlag( node, COSName.F, ITALIC_FLAG, italic ); + } + + /** + * A flag telling if the text should be bold. + * + * @return The bold flag. + */ + public boolean isBold() + { + return BitFlagHelper.getFlag( node, COSName.F, BOLD_FLAG ); + } + + /** + * Set the bold property of the text. + * + * @param bold The new bold flag. + */ + public void setBold( boolean bold ) + { + BitFlagHelper.setFlag( node, COSName.F, BOLD_FLAG, bold ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java new file mode 100644 index 000000000..eed8df1af --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This represents an node in an outline in a pdf document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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. + */ + protected PDOutlineNode getParent() + { + PDOutlineNode retval = null; + COSDictionary parent = (COSDictionary)node.getDictionaryObject( "Parent", "P" ); + if( parent != null ) + { + if( parent.getDictionaryObject( "Parent", "P" ) == 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 ); + } + + PDOutlineItem lastNode = outlineNode; + while(lastNode.getNextSibling() != null) + { + lastNode = lastNode.getNextSibling(); + } + setLastChild( lastNode ); + } + + /** + * 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/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html new file mode 100644 index 000000000..cc338123f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/package.html @@ -0,0 +1,25 @@ + + + + + + + +The outline package allows for a PDF outline(bookmarks) to be created. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/package.html new file mode 100644 index 000000000..697069398 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/documentnavigation/package.html @@ -0,0 +1,25 @@ + + + + + + + +A package to allow access to document level navigation within a PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java new file mode 100644 index 000000000..87a9cf2ce --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDResources; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.fdf.FDFDictionary; +import org.apache.pdfbox.pdmodel.fdf.FDFDocument; +import org.apache.pdfbox.pdmodel.fdf.FDFCatalog; +import org.apache.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 + * @version $Revision: 1.14 $ + */ +public class PDAcroForm implements COSObjectable +{ + 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 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 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.apache.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 + { + List retval = null; + COSArray fields = + (COSArray) acroForm.getDictionaryObject( + COSName.getPDFName("Fields")); + + if( fields != null ) + { + List actuals = 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 ) + { + actuals.add(field); + } + } + } + retval = new COSArrayList( actuals, fields ); + } + return retval; + } + + /** + * Set the fields that are part of this AcroForm. + * + * @param fields The fields that are part of this form. + */ + public void setFields( List fields ) + { + acroForm.setItem( "Fields", COSArrayList.converterToCOSArray( fields )); + } + + /** + * 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 + { + String[] nameSubSection = name.split( "\\." ); + 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 ) || + fieldName.getString().equals( nameSubSection[0] ) ) + { + PDField root = PDFieldFactory.createField( this, element ); + + if( nameSubSection.length > 1 ) + { + PDField kid = root.findKid( nameSubSection, 1 ); + if( kid != null ) + { + retval = kid; + } + else + { + retval = root; + } + } + else + { + retval = root; + } + } + } + } + } + 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 ); + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return acroForm; + } + + /** + * Get the XFA resource, the XFA resource is only used for PDF 1.5+ forms. + * + * @return The xfa resource or null if it does not exist. + */ + public PDXFA getXFA() + { + PDXFA xfa = null; + COSBase base = acroForm.getDictionaryObject( "XFA" ); + if( base != null ) + { + xfa = new PDXFA( base ); + } + return xfa; + } + + /** + * Set the XFA resource, this is only used for PDF 1.5+ forms. + * + * @param xfa The xfa resource. + */ + public void setXFA( PDXFA xfa ) + { + acroForm.setItem( "XFA", xfa ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java new file mode 100644 index 000000000..fdc9502cf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java @@ -0,0 +1,641 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.cos.COSString; + +import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdfwriter.ContentStreamWriter; + +import org.apache.pdfbox.pdmodel.PDResources; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDFontDescriptor; +import org.apache.pdfbox.pdmodel.font.PDSimpleFont; + +import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget; + +import org.apache.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.apache.pdfbox.examlpes.fdf.ImportFDF. So Ben, here is the + * solution. + * + * @author sug + * @author Ben Litchfield + * @version $Revision: 1.20 $ + */ +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( COSName.KIDS ); + if( kids != null && kids.size() > 0 ) + { + COSDictionary firstKid = (COSDictionary)kids.getObject( 0 ); + dap = (COSString)firstKid.getDictionaryObject( COSName.DA ); + } + if( dap == null ) + { + dap = (COSString) acroForm.getDictionary().getDictionaryObject(COSName.DA); + } + } + return dap; + } + + private int getQ() + { + int q = parent.getQ(); + if( parent.getDictionary().getDictionaryObject( COSName.Q ) == null ) + { + COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( COSName.KIDS ); + if( kids != null && kids.size() > 0 ) + { + COSDictionary firstKid = (COSDictionary)kids.getObject( 0 ); + COSNumber qNum = (COSNumber)firstKid.getDictionaryObject( COSName.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() ) + { + COSObjectable next = widgetIter.next(); + PDField field = null; + PDAnnotationWidget widget = null; + if( next instanceof PDField ) + { + field = (PDField)next; + widget = field.getWidget(); + } + else + { + widget = (PDAnnotationWidget)next; + } + PDFormFieldAdditionalActions actions = null; + if( field != null ) + { + actions = field.getActions(); + } + if( actions != null && + actions.getF() != null && + widget.getDictionary().getDictionaryObject( COSName.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("ISO-8859-1") ); + insertGeneratedAppearance( widget, output, pdFont, tokens, appearanceStream ); + output.write( " EMC".getBytes("ISO-8859-1") ); + 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 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("ISO-8859-1") ), 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(line.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 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(); + } + } + + float widthBasedFontSize = Float.MAX_VALUE; + + 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 )/1000.f; + float availableWidth = getAvailableWidth(boundingBox, getLineWidth(tokens)); + widthBasedFontSize = 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 = Math.min((availHeight/height), widthBasedFontSize); + } + 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDCheckbox.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDCheckbox.java new file mode 100644 index 000000000..836fcd0ee --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDCheckbox.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +import java.io.IOException; + +/** + * A class for handling the PDF field as a checkbox. + * + * @author Ben Litchfield + * @author sug + * @version $Revision: 1.11 $ + */ +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 ) + { + for( COSName name : ((COSDictionary)n).keySet() ) + { + 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); + } + + /** + * {@inheritDoc} + */ + 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 ) + { + for( COSName key :((COSDictionary)n).keySet() ) + { + if( !key.equals( OFF_VALUE) ) + { + retval = 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" ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDChoiceButton.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDChoiceButton.java new file mode 100644 index 000000000..e59383e72 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDChoiceButton.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; + +import org.apache.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.apache.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 fdfKids = fdfField.getKids(); + List pdKids = getKids(); + for( int i=0; fdfKids != null && i 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( COSName.PARENT, COSName.P ); + 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 find one of the child elements. The name array are the components + * of the name to search down the tree of names. The nameIndex is where to + * start in that array. This method is called recursively until it finds + * the end point based on the name array. + * + * @param name An array that picks the path to the field. + * @param nameIndex The index into the array. + * @return The field at the endpoint or null if none is found. + * @throws IOException If there is an error creating the field. + */ + public PDField findKid( String[] name, int nameIndex ) throws IOException + { + PDField retval = null; + COSArray kids = (COSArray)getDictionary().getDictionaryObject( COSName.KIDS ); + if( kids != null ) + { + for (int i = 0; retval == null && i < kids.size(); i++) + { + COSDictionary kidDictionary = (COSDictionary)kids.getObject(i); + if( name[nameIndex].equals( kidDictionary.getString( "T" ) ) ) + { + retval = PDFieldFactory.createField( acroForm, kidDictionary ); + if( name.length > nameIndex+1 ) + { + retval = retval.findKid( name, nameIndex+1 ); + } + } + } + } + return retval; + } + + /** + * 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( COSName.PARENT, COSName.P ); + if( kidDictionary.getDictionaryObject( COSName.FT ) != null || + (parent != null && parent.getDictionaryObject( COSName.FT ) != null ) ) + { + kidsList.add( PDFieldFactory.createField( acroForm, kidDictionary )); + } + else if( "Widget".equals( kidDictionary.getNameAsString( COSName.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. + */ + @Override + public String toString() + { + return "" + getDictionary().getDictionaryObject( COSName.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( COSName.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( COSName.AA, actions ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java new file mode 100644 index 000000000..047bf1df7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.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 PDSignatureField( 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()); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDPushButton.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDPushButton.java new file mode 100644 index 000000000..2d9493d85 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDPushButton.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.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.apache.pdfbox.pdmodel.field.PDField#COSField(org.apache.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" ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDRadioCollection.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDRadioCollection.java new file mode 100644 index 000000000..da51389fa --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDRadioCollection.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.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.13 $ + */ +public class PDRadioCollection extends PDChoiceButton +{ + /** + * A Ff flag. + */ + public static final int FLAG_RADIOS_IN_UNISON = 1 << 25; + + /** + * @param theAcroForm The acroForm for this field. + * @param field The field that makes up the radio collection. + * + * {@inheritDoc} + */ + public PDRadioCollection( PDAcroForm theAcroForm, COSDictionary field) + { + super(theAcroForm,field); + } + + /** + * From the PDF Spec
    + * 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(), COSName.FF, FLAG_RADIOS_IN_UNISON, radiosInUnison ); + } + + /** + * + * @return true If the flag is set for radios in unison. + */ + public boolean isRadiosInUnison() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.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. + * + * {@inheritDoc} + */ + public void setValue(String value) throws IOException + { + getDictionary().setString( COSName.V, value ); + List kids = getKids(); + for (int i = 0; i < kids.size(); i++) + { + PDField field = (PDField)kids.get(i); + if ( field instanceof PDCheckbox ) + { + PDCheckbox btn = (PDCheckbox)field; + 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++) + { + PDField kid = (PDField)kids.get(i); + if ( kid instanceof PDCheckbox ) + { + PDCheckbox btn = (PDCheckbox)kid; + if( btn.isChecked() ) + { + retval = btn.getOnValue(); + } + } + } + if( retval == null ) + { + retval = getDictionary().getNameAsString( COSName.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. + */ + @SuppressWarnings("unchecked") + 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignature.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignature.java new file mode 100644 index 000000000..f63a898b0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignature.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSDictionary; + +import java.io.IOException; + +/** + * A class for handling the PDF field as a signature. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class PDSignature extends PDField +{ + + /** + * @see PDField#PDField(PDAcroForm,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 PDField#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 PDField#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"; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignatureField.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignatureField.java new file mode 100644 index 000000000..92abaac4a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDSignatureField.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A class for handling the PDF field as a signature. + * + * @author Ben Litchfield + * @author Thomas Chojecki + * @version $Revision: 1.5 $ + */ +public class PDSignatureField extends PDField +{ + + /** + * @see PDField#PDField(PDAcroForm,COSDictionary) + * + * @param theAcroForm The acroForm for this field. + * @param field The dictionary for the signature. + * @throws IOException If there is an error while resolving partital name for the signature field + */ + public PDSignatureField( PDAcroForm theAcroForm, COSDictionary field) throws IOException + { + super(theAcroForm,field); + // dirty hack to avoid npe caused through getWidget() method + getDictionary().setName( COSName.TYPE, "Annot" ); + getDictionary().setName( COSName.SUBTYPE, PDAnnotationWidget.SUB_TYPE); + } + + /** + * @see PDField#PDField(PDAcroForm) + * + * @param theAcroForm The acroForm for this field. + * @throws IOException If there is an error while resolving partial name for the signature field + */ + public PDSignatureField( PDAcroForm theAcroForm) throws IOException + { + super( theAcroForm ); + getDictionary().setName("FT", "Sig"); + getWidget().setLocked(true); + getWidget().setPrinted(true); + setPartialName(generatePartialName()); + getDictionary().setName( COSName.TYPE, "Annot" ); + getDictionary().setName( COSName.SUBTYPE, PDAnnotationWidget.SUB_TYPE); + } + + /** + * Generate a unique name for the signature + * @return + * @throws IOException + */ + private String generatePartialName() throws IOException + { + PDAcroForm acroForm = getAcroForm(); + List fields = acroForm.getFields(); + + String fieldName = "Signature"; + int i = 1; + + Set sigNames = new HashSet(); + + for ( Object object : fields ) + { + if(object instanceof PDSignatureField) + { + sigNames.add(((PDSignatureField)object).getPartialName()); + } + } + + while(sigNames.contains(fieldName+i)) + ++i; + + return fieldName+i; + } + + /** + * @see PDField#setValue(java.lang.String) + * + * @param value The new value for the field. + * + * @throws IOException If there is an error creating the appearance stream. + * @deprecated use setSignature(PDSignature) instead + */ + @Override + @Deprecated + public void setValue(String value) throws IOException + { + throw new RuntimeException( "Can't set signature as String, use setSignature(PDSignature) instead" ); + } + + /** + * @see PDField#setValue(java.lang.String) + * + * @return The string value of this field. + * + * @throws IOException If there is an error creating the appearance stream. + * @deprecated use getSignature() instead + */ + @Override + @Deprecated + public String getValue() throws IOException + { + throw new RuntimeException( "Can't get signature as String, use getSignature() instead." ); + } + + /** + * Return a string rep of this object. + * + * @return A string rep of this object. + */ + @Override + public String toString() + { + return "PDSignature"; + } + + /** + * Add a signature dictionary to the signature field + * + * @param value is the PDSignature + */ + public void setSignature(PDSignature value) + { + getDictionary().setItem("V", value); + } + + /** + * Get the signature dictionary + * + * @return the signature dictionary + * + */ + public PDSignature getSignature() + { + COSBase dictionary = getDictionary().getDictionaryObject(COSName.V); + if (dictionary == null) + return null; + return new PDSignature((COSDictionary)dictionary); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDTextbox.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDTextbox.java new file mode 100644 index 000000000..46ab18b98 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDTextbox.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.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.apache.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); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDUnknownField.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDUnknownField.java new file mode 100644 index 000000000..d05945b5c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDUnknownField.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import java.io.IOException; + +import org.apache.pdfbox.cos.COSDictionary; + +/** + * This class represents a form field with an unknown type. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class PDUnknownField extends PDField +{ + /** + * @see org.apache.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); + } + + /** + * {@inheritDoc} + */ + public void setValue(String value) throws IOException + { + //do nothing + } + + /** + * {@inheritDoc} + */ + public String getValue() throws IOException + { + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java new file mode 100644 index 000000000..7f4274f45 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.util.BitFlagHelper; + +import java.io.IOException; + +/** + * A class for handling PDF fields that display text. + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +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.apache.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.DA); + } + + /** + * @see org.apache.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.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( COSName.V ); + } + + /** + * @return true if the field is multiline + */ + public boolean isMultiline() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.FF, FLAG_MULTILINE ); + } + + /** + * Set the multiline bit. + * + * @param multiline The value for the multiline. + */ + public void setMultiline( boolean multiline ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.FF, FLAG_MULTILINE, multiline ); + } + + /** + * @return true if the field is a password field. + */ + public boolean isPassword() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.FF, FLAG_PASSWORD ); + } + + /** + * Set the password bit. + * + * @param password The value for the password. + */ + public void setPassword( boolean password ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.FF, FLAG_PASSWORD, password ); + } + + /** + * @return true if the field is a file select field. + */ + public boolean isFileSelect() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.FF, FLAG_FILE_SELECT ); + } + + /** + * Set the file select bit. + * + * @param fileSelect The value for the fileSelect. + */ + public void setFileSelect( boolean fileSelect ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.FF, FLAG_FILE_SELECT, fileSelect ); + } + + /** + * @return true if the field is not suppose to spell check. + */ + public boolean doNotSpellCheck() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.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(), COSName.FF, FLAG_DO_NOT_SPELL_CHECK, doNotSpellCheck ); + } + + /** + * @return true if the field is not suppose to scroll. + */ + public boolean doNotScroll() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.FF, FLAG_DO_NOT_SCROLL ); + } + + /** + * Set the doNotScroll bit. + * + * @param doNotScroll The value for the doNotScroll. + */ + public void setDoNotScroll( boolean doNotScroll ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.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(), COSName.FF, FLAG_COMB ); + } + + /** + * Set the comb bit. + * + * @param comb The value for the comb. + */ + public void setComb( boolean comb ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.FF, FLAG_COMB, comb ); + } + + /** + * @return true if the field is a rich text field. + */ + public boolean isRichText() + { + return BitFlagHelper.getFlag( getDictionary(), COSName.FF, FLAG_RICH_TEXT ); + } + + /** + * Set the richText bit. + * + * @param richText The value for the richText. + */ + public void setRichText( boolean richText ) + { + BitFlagHelper.setFlag( getDictionary(), COSName.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)
    + * 1 - Centered
    + * 2 - Right
    + * Please see the QUADDING_CONSTANTS. + * + * @return The justification of the text strings. + */ + public int getQ() + { + int retval = 0; + COSNumber number = (COSNumber)getDictionary().getDictionaryObject( COSName.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().setInt( COSName.Q, q ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDXFA.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDXFA.java new file mode 100644 index 000000000..2264bc741 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/PDXFA.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.form; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents an XML Forms Architecture Data packet. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class PDXFA implements COSObjectable +{ + private COSBase xfa; + + /** + * Constructor. + * + * @param xfaBase The xfa resource. + */ + public PDXFA( COSBase xfaBase ) + { + xfa = xfaBase; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return xfa; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/package.html new file mode 100644 index 000000000..a641b4f1a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/form/package.html @@ -0,0 +1,25 @@ + + + + + + + +The interactive package contains classes that deal with interactive annotations such as textfields and buttons. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDMeasureDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDMeasureDictionary.java new file mode 100644 index 000000000..d9432961f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDMeasureDictionary.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.measurement; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents a measure dictionary. + * + * @version $Revision: 1.0 $ + * + */ +public class PDMeasureDictionary implements COSObjectable +{ + /** + * The type of the dictionary. + */ + public static final String TYPE = "Measure"; + + private COSDictionary measureDictionary; + + /** + * Constructor. + */ + protected PDMeasureDictionary() + { + this.measureDictionary = new COSDictionary(); + this.getDictionary().setName(COSName.TYPE, TYPE); + } + + /** + * Constructor. + * + * @param dictionary the corresponding dictionary + */ + public PDMeasureDictionary(COSDictionary dictionary) + { + this.measureDictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.measureDictionary; + } + + /** + * This will return the corresponding dictionary. + * + * @return the measure dictionary + */ + public COSDictionary getDictionary() + { + return this.measureDictionary; + } + + /** + * This will return the type of the measure dictionary. + * It must be "Measure" + * + * @return the type + */ + public String getType() + { + return TYPE; + } + + /** + * returns the subtype of the measure dictionary. + * @return the subtype of the measure data dictionary + */ + + public String getSubtype() + { + return this.getDictionary().getNameAsString(COSName.SUBTYPE, + PDRectlinearMeasureDictionary.SUBTYPE); + } + + /** + * This will set the subtype of the measure dictionary. + * @param subtype the subtype of the measure dictionary + */ + protected void setSubtype(String subtype) + { + this.getDictionary().setName(COSName.SUBTYPE, subtype); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDNumberFormatDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDNumberFormatDictionary.java new file mode 100644 index 000000000..f0eb01093 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDNumberFormatDictionary.java @@ -0,0 +1,333 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.measurement; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This class represents a number format dictionary. + * + * @version $Revision: 1.0$ + * + */ +public class PDNumberFormatDictionary implements COSObjectable +{ + + /** + * The type of the dictionary. + */ + public static final String TYPE = "NumberFormat"; + + /** + * Constant indicating that the label specified by U is a suffix to the value. + */ + public static final String LABEL_SUFFIX_TO_VALUE = "S"; + /** + * Constant indicating that the label specified by U is a postfix to the value. + */ + public static final String LABEL_PREFIX_TO_VALUE = "P"; + + /** + * Constant for showing a fractional value as decimal to the precision specified by the D entry. + */ + public static final String FRACTIONAL_DISPLAY_DECIMAL = "D"; + /** + * Constant for showing a fractional value as a fraction with denominator specified by the D entry. + */ + public static final String FRACTIONAL_DISPLAY_FRACTION = "F"; + /** + * Constant for showing a fractional value without fractional part; round to the nearest whole unit. + */ + public static final String FRACTIONAL_DISPLAY_ROUND = "R"; + /** + * Constant for showing a fractional value without fractional part; truncate to achieve whole units. + */ + public static final String FRACTIONAL_DISPLAY_TRUNCATE = "T"; + + private COSDictionary numberFormatDictionary; + + /** + * Constructor. + */ + public PDNumberFormatDictionary() + { + this.numberFormatDictionary = new COSDictionary(); + this.numberFormatDictionary.setName(COSName.TYPE, TYPE); + } + + /** + * Constructor. + * + * @param dictionary the corresponding dictionary + */ + public PDNumberFormatDictionary(COSDictionary dictionary) + { + this.numberFormatDictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.numberFormatDictionary; + } + + /** + * This will return the dictionary. + * + * @return the number format dictionary + */ + public COSDictionary getDictionary() + { + return this.numberFormatDictionary; + } + + /** + * This will return the type of the number format dictionary. + * It must be "NumberFormat" + * + * @return the type + */ + public String getType() + { + return TYPE; + } + + /** + * This will return the label for the units. + * + * @return the label for the units + */ + public String getUnits() + { + return this.getDictionary().getString("U"); + } + + /** + * This will set the label for the units. + * + * @param units the label for the units + */ + public void setUnits(String units) + { + this.getDictionary().setString("U", units); + } + + /** + * This will return the conversion factor. + * + * @return the conversion factor + */ + public float getConversionFactor() + { + return this.getDictionary().getFloat("C"); + } + + /** + * This will set the conversion factor. + * + * @param conversionFactor the conversion factor + */ + public void setConversionFactor(float conversionFactor) + { + this.getDictionary().setFloat("C", conversionFactor); + } + + /** + * This will return the value for the manner to display a fractional value. + * + * @return the manner to display a fractional value + */ + public String getFractionalDisplay() + { + return this.getDictionary().getString("F", FRACTIONAL_DISPLAY_DECIMAL); + } + + /** + * This will set the value for the manner to display a fractional value. + * Allowed values are "D", "F", "R" and "T" + * @param fractionalDisplay the manner to display a fractional value + */ + public void setFractionalDisplay(String fractionalDisplay) + { + if ((fractionalDisplay == null) + || FRACTIONAL_DISPLAY_DECIMAL.equals(fractionalDisplay) + || FRACTIONAL_DISPLAY_FRACTION.equals(fractionalDisplay) + || FRACTIONAL_DISPLAY_ROUND.equals(fractionalDisplay) + || FRACTIONAL_DISPLAY_TRUNCATE.equals(fractionalDisplay)) + { + this.getDictionary().setString("F", fractionalDisplay); + } + else + { + throw new IllegalArgumentException("Value must be \"D\", \"F\", \"R\", or \"T\", (or null)."); + } + } + + /** + * This will return the precision or denominator of a fractional amount. + * + * @return the precision or denominator + */ + public int getDenominator() + { + return this.getDictionary().getInt("D"); + } + + /** + * This will set the precision or denominator of a fractional amount. + * + * @param denominator the precision or denominator + */ + public void setDenominator(int denominator) + { + this.getDictionary().setInt("D", denominator); + } + + /** + * This will return the value indication if the denominator of the fractional value is reduced/truncated . + * + * @return fd + */ + public boolean isFD() + { + return this.getDictionary().getBoolean("FD", false); + } + + /** + * This will set the value indication if the denominator of the fractional value is reduced/truncated . + * The denominator may not be reduced/truncated if true + * @param fd fd + */ + public void setFD(boolean fd) + { + this.getDictionary().setBoolean("FD", fd); + } + + /** + * This will return the text to be used between orders of thousands in display of numerical values. + * + * @return thousands separator + */ + public String getThousandsSeparator() + { + return this.getDictionary().getString("RT", ","); + } + + /** + * This will set the text to be used between orders of thousands in display of numerical values. + * + * @param thousandsSeparator thousands separator + */ + public void setThousandsSeparator(String thousandsSeparator) + { + this.getDictionary().setString("RT", thousandsSeparator); + } + + /** + * This will return the text to be used as the decimal point in displaying numerical values. + * + * @return decimal separator + */ + public String getDecimalSeparator() + { + return this.getDictionary().getString("RD", "."); + } + + /** + * This will set the text to be used as the decimal point in displaying numerical values. + * + * @param decimalSeparator decimal separator + */ + public void setDecimalSeparator(String decimalSeparator) + { + this.getDictionary().setString("RD", decimalSeparator); + } + + /** + * This will return the text to be concatenated to the left of the label specified by U. + * @return label prefix + */ + public String getLabelPrefixString() + { + return this.getDictionary().getString("PS", " "); + } + + /** + * This will set the text to be concatenated to the left of the label specified by U. + * @param labelPrefixString label prefix + */ + public void setLabelPrefixString(String labelPrefixString) + { + this.getDictionary().setString("PS", labelPrefixString); + } + + /** + * This will return the text to be concatenated after the label specified by U. + * + * @return label suffix + */ + public String getLabelSuffixString() + { + return this.getDictionary().getString("SS", " "); + } + + /** + * This will set the text to be concatenated after the label specified by U. + * + * @param labelSuffixString label suffix + */ + public void setLabelSuffixString(String labelSuffixString) + { + this.getDictionary().setString("SS", labelSuffixString); + } + + /** + * This will return a value indicating the ordering of the label specified by U to the calculated unit value. + * + * @return label position + */ + public String getLabelPositionToValue() + { + return this.getDictionary().getString("O", LABEL_SUFFIX_TO_VALUE); + } + + /** + * This will set the value indicating the ordering of the label specified by U to the calculated unit value. + * Possible values are "S" and "P" + * + * @param labelPositionToValue label position + */ + public void setLabelPositionToValue(String labelPositionToValue) + { + if ((labelPositionToValue == null) + || LABEL_PREFIX_TO_VALUE.equals(labelPositionToValue) + || LABEL_SUFFIX_TO_VALUE.equals(labelPositionToValue)) + { + this.getDictionary().setString("O", labelPositionToValue); + } + else + { + throw new IllegalArgumentException("Value must be \"S\", or \"P\" (or null)."); + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDRectlinearMeasureDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDRectlinearMeasureDictionary.java new file mode 100644 index 000000000..1c6ecfbfe --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDRectlinearMeasureDictionary.java @@ -0,0 +1,344 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.measurement; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This class represents a rectlinear measure dictionary. + * + * @version $Revision: 1.0 $ + * + */ +public class PDRectlinearMeasureDictionary extends PDMeasureDictionary +{ + + /** + * The subtype of the rectlinear measure dictionary. + */ + public static final String SUBTYPE = "RL"; + + /** + * Constructor. + */ + public PDRectlinearMeasureDictionary() + { + this.setSubtype(SUBTYPE); + } + + /** + * Constructor. + * + * @param dictionary the corresponding dictionary + */ + public PDRectlinearMeasureDictionary(COSDictionary dictionary) + { + super(dictionary); + } + + /** + * This will return the scale ration. + * + * @return the scale ratio. + */ + public String getScaleRatio() + { + return this.getDictionary().getString(COSName.R); + } + + /** + * This will set the scale ration. + * + * @param scaleRatio the scale ratio. + */ + public void setScaleRatio(String scaleRatio) + { + this.getDictionary().setString(COSName.R, scaleRatio); + } + + /** + * This will return the changes along the x-axis. + * + * @return changes along the x-axis + */ + public PDNumberFormatDictionary[] getChangeXs() + { + COSArray x = (COSArray)this.getDictionary().getDictionaryObject("X"); + if (x != null) + { + PDNumberFormatDictionary[] retval = + new PDNumberFormatDictionary[x.size()]; + for (int i = 0; i < x.size(); i++) + { + COSDictionary dic = (COSDictionary) x.get(i); + retval[i] = new PDNumberFormatDictionary(dic); + } + return retval; + } + return null; + } + + /** + * This will set the changes along the x-axis. + * + * @param changeXs changes along the x-axis + */ + public void setChangeXs(PDNumberFormatDictionary[] changeXs) + { + COSArray array = new COSArray(); + for (int i = 0; i < changeXs.length; i++) + { + array.add(changeXs[i]); + } + this.getDictionary().setItem("X", array); + } + + /** + * This will return the changes along the y-axis. + * + * @return changes along the y-axis + */ + public PDNumberFormatDictionary[] getChangeYs() + { + COSArray y = (COSArray)this.getDictionary().getDictionaryObject("Y"); + if (y != null) + { + PDNumberFormatDictionary[] retval = + new PDNumberFormatDictionary[y.size()]; + for (int i = 0; i < y.size(); i++) + { + COSDictionary dic = (COSDictionary) y.get(i); + retval[i] = new PDNumberFormatDictionary(dic); + } + return retval; + } + return null; + } + + /** + * This will set the changes along the y-axis. + * + * @param changeYs changes along the y-axis + */ + public void setChangeYs(PDNumberFormatDictionary[] changeYs) + { + COSArray array = new COSArray(); + for (int i = 0; i < changeYs.length; i++) + { + array.add(changeYs[i]); + } + this.getDictionary().setItem("Y", array); + } + + /** + * This will return the distances. + * + * @return distances + */ + public PDNumberFormatDictionary[] getDistances() + { + COSArray d = (COSArray)this.getDictionary().getDictionaryObject("D"); + if (d != null) + { + PDNumberFormatDictionary[] retval = + new PDNumberFormatDictionary[d.size()]; + for (int i = 0; i < d.size(); i++) + { + COSDictionary dic = (COSDictionary) d.get(i); + retval[i] = new PDNumberFormatDictionary(dic); + } + return retval; + } + return null; + } + + /** + * This will set the distances. + * + * @param distances distances + */ + public void setDistances(PDNumberFormatDictionary[] distances) + { + COSArray array = new COSArray(); + for (int i = 0; i < distances.length; i++) + { + array.add(distances[i]); + } + this.getDictionary().setItem("D", array); + } + + /** + * This will return the areas. + * + * @return areas + */ + public PDNumberFormatDictionary[] getAreas() + { + COSArray a = (COSArray)this.getDictionary().getDictionaryObject(COSName.A); + if (a != null) + { + PDNumberFormatDictionary[] retval = + new PDNumberFormatDictionary[a.size()]; + for (int i = 0; i < a.size(); i++) + { + COSDictionary dic = (COSDictionary) a.get(i); + retval[i] = new PDNumberFormatDictionary(dic); + } + return retval; + } + return null; + } + + /** + * This will set the areas. + * + * @param areas areas + */ + public void setAreas(PDNumberFormatDictionary[] areas) + { + COSArray array = new COSArray(); + for (int i = 0; i < areas.length; i++) + { + array.add(areas[i]); + } + this.getDictionary().setItem(COSName.A, array); + } + + /** + * This will return the angles. + * + * @return angles + */ + public PDNumberFormatDictionary[] getAngles() + { + COSArray t = (COSArray)this.getDictionary().getDictionaryObject("T"); + if (t != null) + { + PDNumberFormatDictionary[] retval = + new PDNumberFormatDictionary[t.size()]; + for (int i = 0; i < t.size(); i++) + { + COSDictionary dic = (COSDictionary) t.get(i); + retval[i] = new PDNumberFormatDictionary(dic); + } + return retval; + } + return null; + } + + /** + * This will set the angles. + * + * @param angles angles + */ + public void setAngles(PDNumberFormatDictionary[] angles) + { + COSArray array = new COSArray(); + for (int i = 0; i < angles.length; i++) + { + array.add(angles[i]); + } + this.getDictionary().setItem("T", array); + } + + /** + * This will return the sloaps of a line. + * + * @return the sloaps of a line + */ + public PDNumberFormatDictionary[] getLineSloaps() + { + COSArray s = (COSArray)this.getDictionary().getDictionaryObject("S"); + if (s != null) + { + PDNumberFormatDictionary[] retval = + new PDNumberFormatDictionary[s.size()]; + for (int i = 0; i < s.size(); i++) + { + COSDictionary dic = (COSDictionary) s.get(i); + retval[i] = new PDNumberFormatDictionary(dic); + } + return retval; + } + return null; + } + + /** + * This will set the sloaps of a line. + * + * @param lineSloaps the sloaps of a line + */ + public void setLineSloaps(PDNumberFormatDictionary[] lineSloaps) + { + COSArray array = new COSArray(); + for (int i = 0; i < lineSloaps.length; i++) + { + array.add(lineSloaps[i]); + } + this.getDictionary().setItem("S", array); + } + + /** + * This will return the origin of the coordinate system. + * + * @return the origin + */ + public float[] getCoordSystemOrigin() + { + COSArray o = (COSArray)this.getDictionary().getDictionaryObject("O"); + if (o != null) + { + return o.toFloatArray(); + } + return null; + } + + /** + * This will set the origin of the coordinate system. + * + * @param coordSystemOrigin the origin + */ + public void setCoordSystemOrigin(float[] coordSystemOrigin) + { + COSArray array = new COSArray(); + array.setFloatArray(coordSystemOrigin); + this.getDictionary().setItem("O", array); + } + + /** + * This will return the CYX factor. + * + * @return CYX factor + */ + public float getCYX() + { + return this.getDictionary().getFloat("CYX"); + } + + /** + * This will set the CYX factor. + * + * @param cyx CYX factor + */ + public void setCYX(float cyx) + { + this.getDictionary().setFloat("CYX", cyx); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDViewportDictionary.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDViewportDictionary.java new file mode 100644 index 000000000..6243128d8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/PDViewportDictionary.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.measurement; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +/** + * This class represents a viewport dictionary. + * + * @version $Revision: 1.0 $ + * + */ +public class PDViewportDictionary implements COSObjectable +{ + + /** + * The type of this annotation. + */ + public static final String TYPE = "Viewport"; + + private COSDictionary viewportDictionary; + + /** + * Constructor. + */ + public PDViewportDictionary() + { + this.viewportDictionary = new COSDictionary(); + } + + /** + * Constructor. + * + * @param dictionary the dictionary + */ + public PDViewportDictionary(COSDictionary dictionary) + { + this.viewportDictionary = dictionary; + } + + /** + * {@inheritDoc} + */ + public COSBase getCOSObject() + { + return this.viewportDictionary; + } + + /** + * This will return the corresponding dictionary. + * + * @return the viewport dictionary + */ + public COSDictionary getDictionary() + { + return this.viewportDictionary; + } + + /** + * Returns the type of the viewport dictionary. + * It must be "Viewport" + * @return the type of the external data dictionary + */ + + public String getType() + { + return TYPE; + } + + /** + * This will retrieve the rectangle specifying the location of the viewport. + * + * @return the location + */ + public PDRectangle getBBox() + { + COSArray bbox = (COSArray)this.getDictionary().getDictionaryObject("BBox"); + if (bbox != null) + { + return new PDRectangle(bbox); + } + return null; + } + + /** + * This will set the rectangle specifying the location of the viewport. + * + * @param rectangle the rectangle specifying the location. + */ + public void setBBox(PDRectangle rectangle) + { + this.getDictionary().setItem("BBox", rectangle); + } + + /** + * This will retrieve the name of the viewport. + * + * @return the name of the viewport + */ + public String getName() + { + return this.getDictionary().getNameAsString(COSName.NAME); + } + + /** + * This will set the name of the viewport. + * + * @param name the name of the viewport + */ + public void setName(String name) + { + this.getDictionary().setName(COSName.NAME, name); + } + + /** + * This will retrieve the measure dictionary. + * + * @return the measure dictionary + */ + public PDMeasureDictionary getMeasure() + { + COSDictionary measure = (COSDictionary)this.getDictionary().getDictionaryObject("Measure"); + if (measure != null) + { + return new PDMeasureDictionary(measure); + } + return null; + } + + /** + * This will set the measure dictionary. + * + * @param measure the measure dictionary + */ + public void setMeasure(PDMeasureDictionary measure) + { + this.getDictionary().setItem("Measure", measure); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/package.html new file mode 100644 index 000000000..1b91e603e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/measurement/package.html @@ -0,0 +1,25 @@ + + + + + + + +The measurement package contains classes that work with elements specifying measure properties. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java new file mode 100644 index 000000000..bd126c814 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThread.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.pagenavigation; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.PDDocumentInformation; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This a single thread in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java new file mode 100644 index 000000000..9f736f2ee --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/PDThreadBead.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.pagenavigation; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +/** + * This a single bead in a thread in a PDF document. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/package.html new file mode 100644 index 000000000..d04b221bd --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/pagenavigation/package.html @@ -0,0 +1,25 @@ + + + + + + + +A package to allow provide access to PDF page navigation functionality. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java new file mode 100644 index 000000000..614afb0ef --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/PDViewerPreferences.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.interactive.viewerpreferences; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; + +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * This is the document viewing preferences. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/package.html new file mode 100644 index 000000000..392b03dd4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/interactive/viewerpreferences/package.html @@ -0,0 +1,25 @@ + + + + + + + +A package to allow access to document viewing preferences. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java new file mode 100644 index 000000000..2f34cc819 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.markedcontent; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup; + +/** + * This class represents a property list used for the marked content feature to map a resource name + * to a dictionary. + * + * @since PDF 1.2 + * @version $Revision$ + */ +public class PDPropertyList implements COSObjectable +{ + + private COSDictionary props; + + /** + * Creates a new property list. + */ + public PDPropertyList() + { + this.props = new COSDictionary(); + } + + /** + * Creates a new instance based on a given {@link COSDictionary}. + * @param dict the dictionary + */ + public PDPropertyList(COSDictionary dict) + { + this.props = dict; + } + + /** {@inheritDoc} */ + public COSBase getCOSObject() + { + return this.props; + } + + /** + * Returns the optional content group belonging to the given resource name. + * @param name the resource name + * @return the optional content group or null if the group was not found + */ + public PDOptionalContentGroup getOptionalContentGroup(COSName name) + { + COSDictionary dict = (COSDictionary)props.getDictionaryObject(name); + if (dict != null) + { + if (COSName.OCG.equals(dict.getItem(COSName.TYPE))) + { + return new PDOptionalContentGroup(dict); + } + } + return null; + } + + /** + * Puts a mapping from a resource name to an optional content group. + * @param name the resource name + * @param ocg the optional content group + */ + public void putMapping(COSName name, PDOptionalContentGroup ocg) + { + putMapping(name, (COSDictionary)ocg.getCOSObject()); + } + + private void putMapping(COSName name, COSDictionary dict) + { + props.setItem(name, dict); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/package.html new file mode 100644 index 000000000..1c9c15ce5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/package.html @@ -0,0 +1,25 @@ + + + + + + + +The PDModel package represents a high level API for creating and manipulating PDF documents. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/PDTextState.java b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/PDTextState.java new file mode 100644 index 000000000..35c2c19da --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/PDTextState.java @@ -0,0 +1,272 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; + +/** + * This class will hold the current state of the text parameters when executing a + * content stream. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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; + } + + /** + * {@inheritDoc} + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ignore) + { + //ignore + } + return null; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/package.html new file mode 100644 index 000000000..3a31cb033 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/pdmodel/text/package.html @@ -0,0 +1,25 @@ + + + + + + + +The PDModel text package deals with text states, operations, and parameters within the PDF document. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSHEXTable.java b/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSHEXTable.java new file mode 100644 index 000000000..1ee918e9c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSHEXTable.java @@ -0,0 +1,557 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.persistence.util; + +import org.apache.pdfbox.util.StringUtil; + +/** + * helper type for faster mapping of bytes to their hex equivalent. + * + * @author Michael Traut + * @version $Revision: 1.4 $ + */ +public final class COSHEXTable +{ + private COSHEXTable() + { + } + /** + * ASCII byte values for the hex strings. + */ + public static final byte[][] TABLE = + { + StringUtil.getBytes("00"), + StringUtil.getBytes("01"), + StringUtil.getBytes("02"), + StringUtil.getBytes("03"), + StringUtil.getBytes("04"), + StringUtil.getBytes("05"), + StringUtil.getBytes("06"), + StringUtil.getBytes("07"), + StringUtil.getBytes("08"), + StringUtil.getBytes("09"), + StringUtil.getBytes("0A"), + StringUtil.getBytes("0B"), + StringUtil.getBytes("0C"), + StringUtil.getBytes("0D"), + StringUtil.getBytes("0E"), + StringUtil.getBytes("0F"), + StringUtil.getBytes("10"), + StringUtil.getBytes("11"), + StringUtil.getBytes("12"), + StringUtil.getBytes("13"), + StringUtil.getBytes("14"), + StringUtil.getBytes("15"), + StringUtil.getBytes("16"), + StringUtil.getBytes("17"), + StringUtil.getBytes("18"), + StringUtil.getBytes("19"), + StringUtil.getBytes("1A"), + StringUtil.getBytes("1B"), + StringUtil.getBytes("1C"), + StringUtil.getBytes("1D"), + StringUtil.getBytes("1E"), + StringUtil.getBytes("1F"), + StringUtil.getBytes("20"), + StringUtil.getBytes("21"), + StringUtil.getBytes("22"), + StringUtil.getBytes("23"), + StringUtil.getBytes("24"), + StringUtil.getBytes("25"), + StringUtil.getBytes("26"), + StringUtil.getBytes("27"), + StringUtil.getBytes("28"), + StringUtil.getBytes("29"), + StringUtil.getBytes("2A"), + StringUtil.getBytes("2B"), + StringUtil.getBytes("2C"), + StringUtil.getBytes("2D"), + StringUtil.getBytes("2E"), + StringUtil.getBytes("2F"), + StringUtil.getBytes("30"), + StringUtil.getBytes("31"), + StringUtil.getBytes("32"), + StringUtil.getBytes("33"), + StringUtil.getBytes("34"), + StringUtil.getBytes("35"), + StringUtil.getBytes("36"), + StringUtil.getBytes("37"), + StringUtil.getBytes("38"), + StringUtil.getBytes("39"), + StringUtil.getBytes("3A"), + StringUtil.getBytes("3B"), + StringUtil.getBytes("3C"), + StringUtil.getBytes("3D"), + StringUtil.getBytes("3E"), + StringUtil.getBytes("3F"), + StringUtil.getBytes("40"), + StringUtil.getBytes("41"), + StringUtil.getBytes("42"), + StringUtil.getBytes("43"), + StringUtil.getBytes("44"), + StringUtil.getBytes("45"), + StringUtil.getBytes("46"), + StringUtil.getBytes("47"), + StringUtil.getBytes("48"), + StringUtil.getBytes("49"), + StringUtil.getBytes("4A"), + StringUtil.getBytes("4B"), + StringUtil.getBytes("4C"), + StringUtil.getBytes("4D"), + StringUtil.getBytes("4E"), + StringUtil.getBytes("4F"), + StringUtil.getBytes("50"), + StringUtil.getBytes("51"), + StringUtil.getBytes("52"), + StringUtil.getBytes("53"), + StringUtil.getBytes("54"), + StringUtil.getBytes("55"), + StringUtil.getBytes("56"), + StringUtil.getBytes("57"), + StringUtil.getBytes("58"), + StringUtil.getBytes("59"), + StringUtil.getBytes("5A"), + StringUtil.getBytes("5B"), + StringUtil.getBytes("5C"), + StringUtil.getBytes("5D"), + StringUtil.getBytes("5E"), + StringUtil.getBytes("5F"), + StringUtil.getBytes("60"), + StringUtil.getBytes("61"), + StringUtil.getBytes("62"), + StringUtil.getBytes("63"), + StringUtil.getBytes("64"), + StringUtil.getBytes("65"), + StringUtil.getBytes("66"), + StringUtil.getBytes("67"), + StringUtil.getBytes("68"), + StringUtil.getBytes("69"), + StringUtil.getBytes("6A"), + StringUtil.getBytes("6B"), + StringUtil.getBytes("6C"), + StringUtil.getBytes("6D"), + StringUtil.getBytes("6E"), + StringUtil.getBytes("6F"), + StringUtil.getBytes("70"), + StringUtil.getBytes("71"), + StringUtil.getBytes("72"), + StringUtil.getBytes("73"), + StringUtil.getBytes("74"), + StringUtil.getBytes("75"), + StringUtil.getBytes("76"), + StringUtil.getBytes("77"), + StringUtil.getBytes("78"), + StringUtil.getBytes("79"), + StringUtil.getBytes("7A"), + StringUtil.getBytes("7B"), + StringUtil.getBytes("7C"), + StringUtil.getBytes("7D"), + StringUtil.getBytes("7E"), + StringUtil.getBytes("7F"), + StringUtil.getBytes("80"), + StringUtil.getBytes("81"), + StringUtil.getBytes("82"), + StringUtil.getBytes("83"), + StringUtil.getBytes("84"), + StringUtil.getBytes("85"), + StringUtil.getBytes("86"), + StringUtil.getBytes("87"), + StringUtil.getBytes("88"), + StringUtil.getBytes("89"), + StringUtil.getBytes("8A"), + StringUtil.getBytes("8B"), + StringUtil.getBytes("8C"), + StringUtil.getBytes("8D"), + StringUtil.getBytes("8E"), + StringUtil.getBytes("8F"), + StringUtil.getBytes("90"), + StringUtil.getBytes("91"), + StringUtil.getBytes("92"), + StringUtil.getBytes("93"), + StringUtil.getBytes("94"), + StringUtil.getBytes("95"), + StringUtil.getBytes("96"), + StringUtil.getBytes("97"), + StringUtil.getBytes("98"), + StringUtil.getBytes("99"), + StringUtil.getBytes("9A"), + StringUtil.getBytes("9B"), + StringUtil.getBytes("9C"), + StringUtil.getBytes("9D"), + StringUtil.getBytes("9E"), + StringUtil.getBytes("9F"), + StringUtil.getBytes("A0"), + StringUtil.getBytes("A1"), + StringUtil.getBytes("A2"), + StringUtil.getBytes("A3"), + StringUtil.getBytes("A4"), + StringUtil.getBytes("A5"), + StringUtil.getBytes("A6"), + StringUtil.getBytes("A7"), + StringUtil.getBytes("A8"), + StringUtil.getBytes("A9"), + StringUtil.getBytes("AA"), + StringUtil.getBytes("AB"), + StringUtil.getBytes("AC"), + StringUtil.getBytes("AD"), + StringUtil.getBytes("AE"), + StringUtil.getBytes("AF"), + StringUtil.getBytes("B0"), + StringUtil.getBytes("B1"), + StringUtil.getBytes("B2"), + StringUtil.getBytes("B3"), + StringUtil.getBytes("B4"), + StringUtil.getBytes("B5"), + StringUtil.getBytes("B6"), + StringUtil.getBytes("B7"), + StringUtil.getBytes("B8"), + StringUtil.getBytes("B9"), + StringUtil.getBytes("BA"), + StringUtil.getBytes("BB"), + StringUtil.getBytes("BC"), + StringUtil.getBytes("BD"), + StringUtil.getBytes("BE"), + StringUtil.getBytes("BF"), + StringUtil.getBytes("C0"), + StringUtil.getBytes("C1"), + StringUtil.getBytes("C2"), + StringUtil.getBytes("C3"), + StringUtil.getBytes("C4"), + StringUtil.getBytes("C5"), + StringUtil.getBytes("C6"), + StringUtil.getBytes("C7"), + StringUtil.getBytes("C8"), + StringUtil.getBytes("C9"), + StringUtil.getBytes("CA"), + StringUtil.getBytes("CB"), + StringUtil.getBytes("CC"), + StringUtil.getBytes("CD"), + StringUtil.getBytes("CE"), + StringUtil.getBytes("CF"), + StringUtil.getBytes("D0"), + StringUtil.getBytes("D1"), + StringUtil.getBytes("D2"), + StringUtil.getBytes("D3"), + StringUtil.getBytes("D4"), + StringUtil.getBytes("D5"), + StringUtil.getBytes("D6"), + StringUtil.getBytes("D7"), + StringUtil.getBytes("D8"), + StringUtil.getBytes("D9"), + StringUtil.getBytes("DA"), + StringUtil.getBytes("DB"), + StringUtil.getBytes("DC"), + StringUtil.getBytes("DD"), + StringUtil.getBytes("DE"), + StringUtil.getBytes("DF"), + StringUtil.getBytes("E0"), + StringUtil.getBytes("E1"), + StringUtil.getBytes("E2"), + StringUtil.getBytes("E3"), + StringUtil.getBytes("E4"), + StringUtil.getBytes("E5"), + StringUtil.getBytes("E6"), + StringUtil.getBytes("E7"), + StringUtil.getBytes("E8"), + StringUtil.getBytes("E9"), + StringUtil.getBytes("EA"), + StringUtil.getBytes("EB"), + StringUtil.getBytes("EC"), + StringUtil.getBytes("ED"), + StringUtil.getBytes("EE"), + StringUtil.getBytes("EF"), + StringUtil.getBytes("F0"), + StringUtil.getBytes("F1"), + StringUtil.getBytes("F2"), + StringUtil.getBytes("F3"), + StringUtil.getBytes("F4"), + StringUtil.getBytes("F5"), + StringUtil.getBytes("F6"), + StringUtil.getBytes("F7"), + StringUtil.getBytes("F8"), + StringUtil.getBytes("F9"), + StringUtil.getBytes("FA"), + StringUtil.getBytes("FB"), + StringUtil.getBytes("FC"), + StringUtil.getBytes("FD"), + StringUtil.getBytes("FE"), + StringUtil.getBytes("FF") + }; + + /** + * 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" + }; +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSObjectKey.java b/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSObjectKey.java new file mode 100644 index 000000000..01e6ed074 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/COSObjectKey.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.persistence.util; + +import org.apache.pdfbox.cos.COSObject; + +/** + * Object representing the physical reference to an indirect pdf object. + * + * @author Michael Traut + * @version $Revision: 1.5 $ + */ +public class COSObjectKey implements Comparable +{ + 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); + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return "" + getNumber() + " " + getGeneration() + " R"; + } + + /** {@inheritDoc} */ + public int compareTo(COSObjectKey other) + { + if (getNumber() < other.getNumber()) + { + return -1; + } + else if (getNumber() > other.getNumber()) + { + return 1; + } + else + { + if (getGeneration() < other.getGeneration()) + { + return -1; + } + else if (getGeneration() > other.getGeneration()) + { + return 1; + } + else + { + return 0; + } + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/package.html new file mode 100644 index 000000000..d0f0d84f5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/persistence/util/package.html @@ -0,0 +1,25 @@ + + + + + + + +These are utilities used by the persistence layer. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/BitFlagHelper.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/BitFlagHelper.java new file mode 100644 index 000000000..3d7959af7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/BitFlagHelper.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; + +/** + * This class will be used for bit flag operations. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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. + * + * @deprecated use {@link #setFlag(COSDictionary, COSName, int, boolean)} using COSName constants instead + */ + public static final void setFlag( COSDictionary dic, String field, int bitFlag, boolean value ) + { + setFlag(dic, COSName.getPDFName(field), bitFlag, value); + } + + /** + * Sets the given boolean value at bitPos in the flags. + * + * @param dic The dictionary to set the value into. + * @param field The COSName 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 final void setFlag( COSDictionary dic, COSName 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' + * + * @deprecated use {@link #getFlag(COSDictionary, COSName, boolean)} using COSName constants instead + */ + public static final boolean getFlag(COSDictionary dic, String field, int bitFlag) + { + return getFlag(dic, COSName.getPDFName(field), bitFlag); + } + + /** + * Gets the boolean value from the flags at the given bit + * position. + * + * @param dic The dictionary to get the field from. + * @param field The COSName 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 final boolean getFlag(COSDictionary dic, COSName field, int bitFlag) + { + int ff = dic.getInt( field, 0 ); + return (ff & bitFlag) == bitFlag; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/DateConverter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/DateConverter.java new file mode 100644 index 000000000..17ec3908f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/DateConverter.java @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.Locale; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import org.apache.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 + * @version $Revision: 1.14 $ + */ +public class DateConverter +{ + //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", Locale.ENGLISH), + new SimpleDateFormat("EEEE, MMM dd, yyyy hh:mm:ss a", Locale.ENGLISH), + new SimpleDateFormat("MM/dd/yyyy hh:mm:ss", Locale.ENGLISH), + new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz", Locale.ENGLISH), + new SimpleDateFormat("EEEE, MMM dd, yyyy", Locale.ENGLISH), // Acrobat Distiller 1.0.2 for Macintosh + new SimpleDateFormat("EEEE MMM dd, yyyy HH:mm:ss", Locale.ENGLISH), // ECMP5 + new SimpleDateFormat("EEEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH), // GNU Ghostscript 7.0.7 + new SimpleDateFormat("EEEE, MMM dd, yyyy 'at' hh:mma", Locale.ENGLISH), // Acrobat Net Distiller 1.0 for Windows + new SimpleDateFormat("d/MM/yyyy hh:mm:ss", Locale.ENGLISH), // PDFBOX-164 + new SimpleDateFormat("dd/MM/yyyy hh:mm:ss", Locale.ENGLISH), // PDFBOX-170 + new SimpleDateFormat("EEEEEEEEEE, MMMMMMMMMMMM dd, yyyy", Locale.ENGLISH), // PDFBOX-465 + new SimpleDateFormat("dd MMM yyyy hh:mm:ss", Locale.ENGLISH), // for 26 May 2000 11:25:00 + new SimpleDateFormat("dd MMM yyyy hh:mm", Locale.ENGLISH), // for 26 May 2000 11:25 + new SimpleDateFormat("M/dd/yyyy hh:mm:ss", Locale.ENGLISH), + new SimpleDateFormat("MM/d/yyyy hh:mm:ss", Locale.ENGLISH), + new SimpleDateFormat("M/dd/yyyy", Locale.ENGLISH), + new SimpleDateFormat("MM/d/yyyy", Locale.ENGLISH), + new SimpleDateFormat("M/d/yyyy hh:mm:ss", Locale.ENGLISH), + new SimpleDateFormat("M/d/yyyy", Locale.ENGLISH), + new SimpleDateFormat("M/d/yy hh:mm:ss", Locale.ENGLISH), + new SimpleDateFormat("M/d/yy", Locale.ENGLISH), + new SimpleDateFormat("yyyymmdd hh:mm:ss Z"), // + new SimpleDateFormat("yyyymmdd hh:mm:ss"), // + new SimpleDateFormat("yyyymmdd'+00''00'''"), // + new SimpleDateFormat("yyyymmdd'+01''00'''"), // + new SimpleDateFormat("yyyymmdd'+02''00'''"), // + new SimpleDateFormat("yyyymmdd'+03''00'''"), // + new SimpleDateFormat("yyyymmdd'+04''00'''"), // + new SimpleDateFormat("yyyymmdd'+05''00'''"), // + new SimpleDateFormat("yyyymmdd'+06''00'''"), // + new SimpleDateFormat("yyyymmdd'+07''00'''"), // + new SimpleDateFormat("yyyymmdd'+08''00'''"), // + new SimpleDateFormat("yyyymmdd'+09''00'''"), // + new SimpleDateFormat("yyyymmdd'+10''00'''"), // + new SimpleDateFormat("yyyymmdd'+11''00'''"), // + new SimpleDateFormat("yyyymmdd'+12''00'''"), // + new SimpleDateFormat("yyyymmdd'-01''00'''"), // + new SimpleDateFormat("yyyymmdd'-02''00'''"), // + new SimpleDateFormat("yyyymmdd'-03''00'''"), // + new SimpleDateFormat("yyyymmdd'-04''00'''"), // + new SimpleDateFormat("yyyymmdd'-05''00'''"), // + new SimpleDateFormat("yyyymmdd'-06''00'''"), // + new SimpleDateFormat("yyyymmdd'-07''00'''"), // + new SimpleDateFormat("yyyymmdd'-08''00'''"), // + new SimpleDateFormat("yyyymmdd'-09''00'''"), // + new SimpleDateFormat("yyyymmdd'-10''00'''"), // + new SimpleDateFormat("yyyymmdd'-11''00'''"), // + new SimpleDateFormat("yyyymmdd'-12''00'''"), // + new SimpleDateFormat("yyyymmdd"), // for 20090401+0200 + }; + + 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:" ); + // PDFBOX-402 , SimpleDateFormat is not thread safe, created it when you use it. + buffer.append( new SimpleDateFormat( "yyyyMMddHHmmss" , Locale.ENGLISH).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 && date.trim().length() > 0 ) + { + //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 ) ); + } + + 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 ) + { + if( sign == '+' ) + { + //parseInt cannot handle the + sign + hours = Integer.parseInt( date.substring( 15, 17 ) ); + } + else if (sign == '-') + { + hours = -Integer.parseInt(date.substring(15,17)); + } + else + { + hours = -Integer.parseInt( date.substring( 14, 16 ) ); + } + } + if( date.length() > 20 ) + { + minutes = Integer.parseInt( date.substring( 18, 20 ) ); + } + zone = new SimpleTimeZone( hours*60*60*1000 + minutes*60*1000, "Unknown" ); + } + } + if( zone != null ) + { + retval = new GregorianCalendar( zone ); + } + else + { + retval = new GregorianCalendar(); + } + + retval.set(year, month-1, day, hour, minute, second ); + // PDFBOX-598: PDF dates are only accurate up to a second + retval.set(Calendar.MILLISECOND, 0); + } + catch( NumberFormatException e ) + { + for( int i=0; retval == null && iBen Litchfield + * @version $Revision: 1.3 $ + */ +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/fluidbook/tools/fwstk/src/apache/pdfbox/util/ExtensionFileFilter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/ExtensionFileFilter.java new file mode 100644 index 000000000..48bc5d55c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/ExtensionFileFilter.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + + +/** + * A FileFilter that will only accept files of a certain extension. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class ExtensionFileFilter extends FileFilter +{ + private String[] extensions = null; + private String desc; + + /** + * Constructor. + * + * @param ext A list of filename extensions, ie new String[] { "PDF"}. + * @param description A description of the files. + */ + public ExtensionFileFilter( String[] ext, String description ) + { + extensions = ext; + desc = description; + } + + /** + * {@inheritDoc} + */ + public boolean accept(File pathname) + { + if (pathname.isDirectory()) + { + return true; + } + boolean acceptable = false; + String name = pathname.getName().toUpperCase(); + for( int i=0; !acceptable && iBrian Carrier + * @version $Revision: 1.0 $ + */ +public class ICU4JImpl +{ + Bidi bidi; + + /** + * Constructor. + */ + public ICU4JImpl() + { + bidi = new Bidi(); + + /* We do not use bidi.setInverse() because that uses + * Bidi.REORDER_INVERSE_NUMBERS_AS_L, which caused problems + * in some test files. For example, a file had a line of: + * 0 1 / ARABIC + * and the 0 and 1 were reversed in the end result. + * REORDER_INVERSE_LIKE_DIRECT is the inverse Bidi mode + * that more closely reflects the Unicode spec. + */ + bidi.setReorderingMode(Bidi.REORDER_INVERSE_LIKE_DIRECT); + } + + /** + * Takes a line of text in presentation order and converts it to logical order. + * @see TextNormalize.makeLineLogicalOrder(String, boolean) + * + * @param str String to convert + * @param isRtlDominant RTL (right-to-left) will be the dominant text direction + * @return The converted string + */ + public String makeLineLogicalOrder(String str, boolean isRtlDominant) + { + bidi.setPara(str, isRtlDominant?Bidi.RTL:Bidi.LTR, null); + + /* Set the mirror flag so that parentheses and other mirror symbols + * are properly reversed, when needed. With this removed, lines + * such as (CBA) in the PDF file will come out like )ABC( in logical + * order. + */ + return bidi.writeReordered(Bidi.DO_MIRRORING); + } + + /** + * Normalize presentation forms of characters to the separate parts. + * @see TextNormalize.normalizePres(String) + * + * @param str String to normalize + * @return Normalized form + */ + public String normalizePres(String str) + { + StringBuilder builder = null; + int p = 0; + int q = 0; + int strLength = str.length(); + for (; q < strLength; q++) + { + // We only normalize if the codepoint is in a given range. + // Otherwise, NFKC converts too many things that would cause + // confusion. For example, it converts the micro symbol in + // extended Latin to the value in the Greek script. We normalize + // the Unicode Alphabetic and Arabic A&B Presentation forms. + char c = str.charAt(q); + if ((0xFB00 <= c && c <= 0xFDFF) || (0xFE70 <= c && c <= 0xFEFF)) + { + if (builder == null) { + builder = new StringBuilder(strLength * 2); + } + builder.append(str.substring(p, q)); + // Some fonts map U+FDF2 differently than the Unicode spec. + // They add an extra U+0627 character to compensate. + // This removes the extra character for those fonts. + if(c == 0xFDF2 && q > 0 && (str.charAt(q-1) == 0x0627 || str.charAt(q-1) == 0xFE8D)) + { + builder.append("\u0644\u0644\u0647"); + } + else + { + // Trim because some decompositions have an extra space, + // such as U+FC5E + builder.append( + Normalizer.normalize(c, Normalizer.NFKC).trim()); + } + p = q + 1; + } + } + if (builder == null) { + return str; + } else { + builder.append(str.substring(p, q)); + return builder.toString(); + } + } + + /** + * Decomposes Diacritic characters to their combining forms. + * + * @param str String to be Normalized + * @return A Normalized String + */ + public String normalizeDiac(String str) + { + StringBuilder retStr = new StringBuilder(); + int strLength = str.length(); + for (int i = 0; i < strLength; i++) + { + char c = str.charAt(i); + if(Character.getType(c) == Character.NON_SPACING_MARK + || Character.getType(c) == Character.MODIFIER_SYMBOL + || Character.getType(c) == Character.MODIFIER_LETTER) + { + /* + * Trim because some decompositions have an extra space, such as + * U+00B4 + */ + retStr.append(Normalizer.normalize(c, Normalizer.NFKC).trim()); + } + else + { + retStr.append(str.charAt(i)); + } + } + return retStr.toString(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/ImageParameters.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/ImageParameters.java new file mode 100644 index 000000000..4b23c6204 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/ImageParameters.java @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; + +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * This contains all of the image parameters for in inlined image. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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( COSName abbreviatedName, COSName name ) + { + COSBase retval = dictionary.getDictionaryObject( abbreviatedName ); + if( retval == null ) + { + retval = dictionary.getDictionaryObject( name ); + } + return retval; + } + + private int getNumberOrNegativeOne( COSName abbreviatedName, COSName 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( COSName.BPC, COSName.BITS_PER_COMPONENT ); + } + + /** + * Set the number of bits per component. + * + * @param bpc The number of bits per component. + */ + public void setBitsPerComponent( int bpc ) + { + dictionary.setInt( COSName.BPC, 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 + { + return getColorSpace( null ); + } + + /** + * This will get the color space or null if none exists. + * + * @param colorSpaces The ColorSpace dictionary from the current resources, if any. + * + * @return The color space for this image. + * + * @throws IOException If there is an error getting the colorspace. + */ + public PDColorSpace getColorSpace( Map colorSpaces ) throws IOException + { + COSBase cs = getCOSObject( COSName.CS, COSName.COLORSPACE ); + PDColorSpace retval = null; + if( cs != null ) + { + retval = PDColorSpaceFactory.createColorSpace( cs, colorSpaces ); + } + 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.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( COSName.H, COSName.HEIGHT ); + } + + /** + * Set the height of the image. + * + * @param h The height of the image. + */ + public void setHeight( int h ) + { + dictionary.setInt( COSName.H, h ); + } + + /** + * The width of this image. This will return -1 if one has not + * been set. + * + * @return The width. + */ + public int getWidth() + { + return getNumberOrNegativeOne( COSName.W, COSName.WIDTH ); + } + + /** + * Set the width of the image. + * + * @param w The width of the image. + */ + public void setWidth( int w ) + { + dictionary.setInt( COSName.W, 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, COSName.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/fluidbook/tools/fwstk/src/apache/pdfbox/util/LayerUtility.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/LayerUtility.java new file mode 100644 index 000000000..af6a68a6a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/LayerUtility.java @@ -0,0 +1,320 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.awt.geom.AffineTransform; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +import org.apache.fontbox.util.BoundingBox; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup; +import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm; +import org.apache.pdfbox.pdmodel.markedcontent.PDPropertyList; + +/** + * This class allows to import pages as Form XObjects into a PDF file and use them to create + * layers (optional content groups). + * + * @version $Revision$ + */ +public class LayerUtility +{ + private static final boolean DEBUG = true; + + private PDDocument targetDoc; + private PDFCloneUtility cloner; + + /** + * Creates a new instance. + * @param document the PDF document to modify + */ + public LayerUtility(PDDocument document) + { + this.targetDoc = document; + this.cloner = new PDFCloneUtility(document); + } + + /** + * Returns the PDF document we work on. + * @return the PDF document + */ + public PDDocument getDocument() + { + return this.targetDoc; + } + + /** + * Some applications may not wrap their page content in a save/restore (q/Q) pair which can + * lead to problems with coordinate system transformations when content is appended. This + * method lets you add a q/Q pair around the existing page's content. + * @param page the page + * @throws IOException if an I/O error occurs + */ + public void wrapInSaveRestore(PDPage page) throws IOException + { + COSDictionary saveGraphicsStateDic = new COSDictionary(); + COSStream saveGraphicsStateStream = new COSStream(saveGraphicsStateDic, + getDocument().getDocument().getScratchFile()); + OutputStream saveStream = saveGraphicsStateStream.createUnfilteredStream(); + saveStream.write("q\n".getBytes("ISO-8859-1")); + saveStream.flush(); + + COSStream restoreGraphicsStateStream = new COSStream(saveGraphicsStateDic, + getDocument().getDocument().getScratchFile()); + OutputStream restoreStream = restoreGraphicsStateStream.createUnfilteredStream(); + restoreStream.write("Q\n".getBytes("ISO-8859-1")); + restoreStream.flush(); + + //Wrap the existing page's content in a save/restore pair (q/Q) to have a controlled + //environment to add additional content. + COSDictionary pageDictionary = page.getCOSDictionary(); + COSBase contents = pageDictionary.getDictionaryObject(COSName.CONTENTS); + if (contents instanceof COSStream) + { + COSStream contentsStream = (COSStream)contents; + + COSArray array = new COSArray(); + array.add(saveGraphicsStateStream); + array.add(contentsStream); + array.add(restoreGraphicsStateStream); + + pageDictionary.setItem(COSName.CONTENTS, array); + } + else if( contents instanceof COSArray ) + { + COSArray contentsArray = (COSArray)contents; + + contentsArray.add(0, saveGraphicsStateStream); + contentsArray.add(restoreGraphicsStateStream); + } + else + { + throw new IOException("Contents are unknown type: " + contents.getClass().getName()); + } + } + + /** + * Imports a page from some PDF file as a Form XObject so it can be placed on another page + * in the target document. + * @param sourceDoc the source PDF document that contains the page to be copied + * @param pageNumber the page number of the page to be copied + * @return a Form XObject containing the original page's content + * @throws IOException if an I/O error occurs + */ + public PDXObjectForm importPageAsForm(PDDocument sourceDoc, int pageNumber) throws IOException + { + PDPage page = (PDPage)sourceDoc.getDocumentCatalog().getAllPages().get(pageNumber); + return importPageAsForm(sourceDoc, page); + } + + private static final Set PAGE_TO_FORM_FILTER = new java.util.HashSet( + Arrays.asList(new String[] {"Group", "LastModified", "Metadata"})); + + /** + * Imports a page from some PDF file as a Form XObject so it can be placed on another page + * in the target document. + * @param sourceDoc the source PDF document that contains the page to be copied + * @param page the page in the source PDF document to be copied + * @return a Form XObject containing the original page's content + * @throws IOException if an I/O error occurs + */ + public PDXObjectForm importPageAsForm(PDDocument sourceDoc, PDPage page) throws IOException + { + COSStream pageStream = (COSStream)page.getContents().getCOSObject(); + PDStream newStream = new PDStream(targetDoc, + pageStream.getUnfilteredStream(), false); + PDXObjectForm form = new PDXObjectForm(newStream); + + //Copy resources + PDResources pageRes = page.findResources(); + PDResources formRes = new PDResources(); + cloner.cloneMerge(pageRes, formRes); + form.setResources(formRes); + + //Transfer some values from page to form + transferDict(page.getCOSDictionary(), form.getCOSStream(), PAGE_TO_FORM_FILTER, true); + + Matrix matrix = form.getMatrix(); + AffineTransform at = matrix != null ? matrix.createAffineTransform() : new AffineTransform(); + PDRectangle mediaBox = page.findMediaBox(); + PDRectangle cropBox = page.findCropBox(); + PDRectangle viewBox = (cropBox != null ? cropBox : mediaBox); + + //Handle the /Rotation entry on the page dict + int rotation = getNormalizedRotation(page); + + //Transform to FOP's user space + //at.scale(1 / viewBox.getWidth(), 1 / viewBox.getHeight()); + at.translate(mediaBox.getLowerLeftX() - viewBox.getLowerLeftX(), + mediaBox.getLowerLeftY() - viewBox.getLowerLeftY()); + switch (rotation) + { + case 90: + at.scale(viewBox.getWidth() / viewBox.getHeight(), viewBox.getHeight() / viewBox.getWidth()); + at.translate(0, viewBox.getWidth()); + at.rotate(-Math.PI / 2.0); + break; + case 180: + at.translate(viewBox.getWidth(), viewBox.getHeight()); + at.rotate(-Math.PI); + break; + case 270: + at.scale(viewBox.getWidth() / viewBox.getHeight(), viewBox.getHeight() / viewBox.getWidth()); + at.translate(viewBox.getHeight(), 0); + at.rotate(-Math.PI * 1.5); + default: + //no additional transformations necessary + } + //Compensate for Crop Boxes not starting at 0,0 + at.translate(-viewBox.getLowerLeftX(), -viewBox.getLowerLeftY()); + if (!at.isIdentity()) + { + form.setMatrix(at); + } + + BoundingBox bbox = new BoundingBox(); + bbox.setLowerLeftX(viewBox.getLowerLeftX()); + bbox.setLowerLeftY(viewBox.getLowerLeftY()); + bbox.setUpperRightX(viewBox.getUpperRightX()); + bbox.setUpperRightY(viewBox.getUpperRightY()); + form.setBBox(new PDRectangle(bbox)); + + return form; + } + + /** + * Places the given form over the existing content of the indicated page (like an overlay). + * The form is enveloped in a marked content section to indicate that it's part of an + * optional content group (OCG), here used as a layer. This optional group is returned and + * can be enabled and disabled through methods on {@link PDOptionalContentProperties}. + * @param targetPage the target page + * @param form the form to place + * @param transform the transformation matrix that controls the placement + * @param layerName the name for the layer/OCG to produce + * @return the optional content group that was generated for the form usage + * @throws IOException if an I/O error occurs + */ + public PDOptionalContentGroup appendFormAsLayer(PDPage targetPage, + PDXObjectForm form, AffineTransform transform, + String layerName) throws IOException + { + PDDocumentCatalog catalog = targetDoc.getDocumentCatalog(); + PDOptionalContentProperties ocprops = catalog.getOCProperties(); + if (ocprops == null) + { + ocprops = new PDOptionalContentProperties(); + catalog.setOCProperties(ocprops); + } + if (ocprops.hasGroup(layerName)) + { + throw new IllegalArgumentException("Optional group (layer) already exists: " + layerName); + } + + PDOptionalContentGroup layer = new PDOptionalContentGroup(layerName); + ocprops.addGroup(layer); + + PDResources resources = targetPage.findResources(); + PDPropertyList props = resources.getProperties(); + if (props == null) + { + props = new PDPropertyList(); + resources.setProperties(props); + } + + //Find first free resource name with the pattern "MC" + int index = 0; + PDOptionalContentGroup ocg; + COSName resourceName; + do + { + resourceName = COSName.getPDFName("MC" + index); + ocg = props.getOptionalContentGroup(resourceName); + index++; + } while (ocg != null); + //Put mapping for our new layer/OCG + props.putMapping(resourceName, layer); + + PDPageContentStream contentStream = new PDPageContentStream( + targetDoc, targetPage, true, !DEBUG); + contentStream.beginMarkedContentSequence(COSName.OC, resourceName); + contentStream.drawXObject(form, transform); + contentStream.endMarkedContentSequence(); + contentStream.close(); + + return layer; + } + + private void transferDict(COSDictionary orgDict, COSDictionary targetDict, + Set filter, boolean inclusive) throws IOException + { + for (Map.Entry entry : orgDict.entrySet()) + { + COSName key = entry.getKey(); + if (inclusive && !filter.contains(key.getName())) + { + continue; + } + else if (!inclusive && filter.contains(key.getName())) + { + continue; + } + targetDict.setItem(key, + cloner.cloneForNewDocument(entry.getValue())); + } + } + + private static int getNormalizedRotation(PDPage page) + { + //Handle the /Rotation entry on the page dict + int rotation = page.findRotation(); + while (rotation >= 360) + { + rotation -= 360; + } + if (rotation < 0) + { + rotation = 0; + } + switch (rotation) + { + case 90: + case 180: + case 270: + return rotation; + default: + return 0; + } + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/MapUtil.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/MapUtil.java new file mode 100644 index 000000000..dfec85e1c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/MapUtil.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.util.Map; + + +/** + * This class with handle some simple Map operations. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class MapUtil +{ + private MapUtil() + { + //utility class + } + + /** + * Generate a unique key for the map based on a prefix. + * + * @param map The map to look for existing keys. + * @param prefix The prefix to use when generating the key. + * @return The new unique key that does not currently exist in the map. + */ + public static final String getNextUniqueKey( Map map, String prefix ) + { + int counter = 0; + while( map.get( prefix+counter ) != null ) + { + counter++; + } + return prefix+counter; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/Matrix.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/Matrix.java new file mode 100644 index 000000000..c9d6fdd8f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/Matrix.java @@ -0,0 +1,415 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.awt.geom.AffineTransform; + +/** + * This class will be used for matrix manipulation. + * + * @author Ben Litchfield + * @version $Revision: 1.14 $ + */ +public class Matrix implements Cloneable +{ + static final float[] DEFAULT_SINGLE = + { + 1,0,0, + 0,1,0, + 0,0,1 + }; + + private float[] single; + + /** + * Constructor. + */ + public Matrix() + { + single = new float[DEFAULT_SINGLE.length]; + reset(); + } + + /** + * This method resets the numbers in this Matrix to the original values, which are + * the values that a newly constructed Matrix would have. + */ + public void reset() + { + System.arraycopy(DEFAULT_SINGLE, 0, single, 0, DEFAULT_SINGLE.length); + } + + /** + * 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 ) + { + return this.multiply(b, new Matrix()); + } + + /** + * This method multiplies this Matrix with the specified other Matrix, storing the product in the specified + * result Matrix. By reusing Matrix instances like this, multiplication chains can be executed without having + * to create many temporary Matrix objects. + *

    + * It is allowed to have (other == this) or (result == this) or indeed (other == result) but if this is done, + * the backing float[] matrix values may be copied in order to ensure a correct product. + * + * @param other the second operand Matrix in the multiplication + * @param result the Matrix instance into which the result should be stored. If result is null, a new Matrix + * instance is created. + * @return the product of the two matrices. + */ + public Matrix multiply( Matrix other, Matrix result ) + { + if (result == null) + { + result = new Matrix(); + } + + if (other != null && other.single != null) + { + // the operands + float[] thisOperand = this.single; + float[] otherOperand = other.single; + + // We're multiplying 2 sets of floats together to produce a third, but we allow + // any of these float[] instances to be the same objects. + // There is the possibility then to overwrite one of the operands with result values + // and therefore corrupt the result. + + // If either of these operands are the same float[] instance as the result, then + // they need to be copied. + + if (this == result) + { + final float[] thisOrigVals = new float[this.single.length]; + System.arraycopy(this.single, 0, thisOrigVals, 0, this.single.length); + + thisOperand = thisOrigVals; + } + if (other == result) + { + final float[] otherOrigVals = new float[other.single.length]; + System.arraycopy(other.single, 0, otherOrigVals, 0, other.single.length); + + otherOperand = otherOrigVals; + } + + result.single[0] = thisOperand[0] * otherOperand[0] + + thisOperand[1] * otherOperand[3] + + thisOperand[2] * otherOperand[6]; + result.single[1] = thisOperand[0] * otherOperand[1] + + thisOperand[1] * otherOperand[4] + + thisOperand[2] * otherOperand[7]; + result.single[2] = thisOperand[0] * otherOperand[2] + + thisOperand[1] * otherOperand[5] + + thisOperand[2] * otherOperand[8]; + result.single[3] = thisOperand[3] * otherOperand[0] + + thisOperand[4] * otherOperand[3] + + thisOperand[5] * otherOperand[6]; + result.single[4] = thisOperand[3] * otherOperand[1] + + thisOperand[4] * otherOperand[4] + + thisOperand[5] * otherOperand[7]; + result.single[5] = thisOperand[3] * otherOperand[2] + + thisOperand[4] * otherOperand[5] + + thisOperand[5] * otherOperand[8]; + result.single[6] = thisOperand[6] * otherOperand[0] + + thisOperand[7] * otherOperand[3] + + thisOperand[8] * otherOperand[6]; + result.single[7] = thisOperand[6] * otherOperand[1] + + thisOperand[7] * otherOperand[4] + + thisOperand[8] * otherOperand[7]; + result.single[8] = thisOperand[6] * otherOperand[2] + + thisOperand[7] * otherOperand[5] + + thisOperand[8] * otherOperand[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]; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFCloneUtility.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFCloneUtility.java new file mode 100644 index 000000000..fcbcdc340 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFCloneUtility.java @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * Utility class used to clone PDF objects. It keeps track of objects it has already cloned. + * + * @version $Revision$ + */ +public class PDFCloneUtility +{ + + private PDDocument destination; + private Map clonedVersion = new HashMap(); + + /** + * Creates a new instance for the given target document. + * @param dest the destination PDF document that will receive the clones + */ + public PDFCloneUtility(PDDocument dest) + { + this.destination = dest; + } + + /** + * Returns the destination PDF document this cloner instance is set up for. + * @return the destination PDF document + */ + public PDDocument getDestination() + { + return this.destination; + } + + /** + * Deep-clones the given object for inclusion into a different PDF document identified by + * the destination parameter. + * @param base the initial object as the root of the deep-clone operation + * @return the cloned instance of the base object + * @throws IOException if an I/O error occurs + */ + public COSBase cloneForNewDocument( Object base ) throws IOException + { + if( base == null ) + { + return null; + } + COSBase retval = (COSBase)clonedVersion.get( base ); + if( retval != null ) + { + //we are done, it has already been converted. + } + else if( base instanceof List ) + { + COSArray array = new COSArray(); + List list = (List)base; + for( int i=0; i entry : originalStream.entrySet() ) + { + stream.getStream().setItem( + entry.getKey(), + cloneForNewDocument(entry.getValue())); + } + retval = stream.getStream(); + } + else if( base instanceof COSDictionary ) + { + COSDictionary dic = (COSDictionary)base; + retval = new COSDictionary(); + clonedVersion.put( base, retval ); + for( Map.Entry entry : dic.entrySet() ) + { + ((COSDictionary)retval).setItem( + entry.getKey(), + cloneForNewDocument(entry.getValue())); + } + } + else + { + retval = (COSBase)base; + } + clonedVersion.put( base, retval ); + return retval; + } + + + /** + * Merges two objects of the same type by deep-cloning its members. + *
    + * Base and target must be instances of the same class. + * @param base the base object to be cloned + * @param target the merge target + * @throws IOException if an I/O error occurs + */ + public void cloneMerge( COSObjectable base, COSObjectable target) throws IOException + { + if( base == null ) + { + return; + } + COSBase retval = (COSBase)clonedVersion.get( base ); + if( retval != null ) + { + return; + //we are done, it has already been converted. // ### Is that correct for cloneMerge??? + } + else if( base instanceof List ) + { + COSArray array = new COSArray(); + List list = (List)base; + for( int i = 0; i < list.size(); i++ ) + { + array.add( cloneForNewDocument( list.get( i ) ) ); + } + ((List)target).add(array); + } + else if( base instanceof COSObjectable && !(base instanceof COSBase) ) + { + cloneMerge(((COSObjectable)base).getCOSObject(), ((COSObjectable)target).getCOSObject() ); + clonedVersion.put( base, retval ); + } + else if( base instanceof COSObject ) + { + if(target instanceof COSObject) + { + cloneMerge(((COSObject) base).getObject(),((COSObject) target).getObject() ); + } + else if(target instanceof COSDictionary) + { + cloneMerge(((COSObject)base).getObject(), ((COSDictionary)target)); + } + clonedVersion.put( base, retval ); + } + else if( base instanceof COSArray ) + { + COSArray array = (COSArray)base; + for( int i=0; i entry : originalStream.entrySet() ) + { + stream.getStream().setItem( + entry.getKey(), + cloneForNewDocument(entry.getValue())); + } + retval = stream.getStream(); + target = retval; + } + else if( base instanceof COSDictionary ) + { + COSDictionary dic = (COSDictionary)base; + clonedVersion.put( base, retval ); + for( Map.Entry entry : dic.entrySet() ) + { + COSName key = entry.getKey(); + COSBase value = entry.getValue(); + if (((COSDictionary)target).getItem(key) != null) + { + cloneMerge(value, ((COSDictionary)target).getItem(key)); + } + else + { + ((COSDictionary)target).setItem( key, cloneForNewDocument(value)); + } + } + } + else + { + retval = (COSBase)base; + } + clonedVersion.put( base, retval ); + + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFHighlighter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFHighlighter.java new file mode 100644 index 000000000..576f47000 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFHighlighter.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + + +/** + * Highlighting of words in a PDF document with an XML file. + * + * @author slagraulet (slagraulet@cardiweb.com) + * @author Ben Litchfield + * @version $Revision: 1.7 $ + * + * @see + * Adobe Highlight File Format + */ +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; + private static final String ENCODING = "UTF-16"; + + /** + * Default constructor. + * + * @throws IOException If there is an error constructing this class. + */ + public PDFHighlighter() throws IOException + { + super(ENCODING); + 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("\n\n\n"); + textOS = new ByteArrayOutputStream(); + textWriter = new OutputStreamWriter( textOS, ENCODING); + writeText(pdDocument, textWriter); + highlighterOutput.write("\n\n"); + highlighterOutput.flush(); + } + + /** + * {@inheritDoc} + */ + protected void endPage( PDPage pdPage ) throws IOException + { + textWriter.flush(); + + String page = new String( textOS.toByteArray(), ENCODING ); + 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(" \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() + " 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); + }**/ +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFImageWriter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFImageWriter.java new file mode 100644 index 000000000..e741e5a78 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFImageWriter.java @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import javax.imageio.IIOException; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOInvalidTreeException; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageOutputStream; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * This class will take a PDF document and strip out all of the text and ignore the + * formatting and such. Please note; it is up to clients of this class to verify that + * a specific user has the correct permissions to extract text from the + * PDF document. + *

    + * Patterned after PDFTextStripper. + * + * @author Daniel Wilson + * @version $Revision: 1.1 $ + */ +public class PDFImageWriter extends PDFStreamEngine +{ + + /** + * Instantiate a new PDFImageWriter object. + */ + public PDFImageWriter() + { + } + + /** + * Instantiate a new PDFImageWriter object. Loading all of the operator mappings + * from the properties object that is passed in. + * + * @param props The properties containing the mapping of operators to PDFOperator + * classes. + * + * @throws IOException If there is an error reading the properties. + */ + public PDFImageWriter( Properties props ) throws IOException + { + super( props ); + } + + /** + * Converts a given page range of a PDF document to bitmap images. + * @param document the PDF document + * @param imageType the target format (ex. "png") + * @param password the password (needed if the PDF is encrypted) + * @param startPage the start page (1 is the first page) + * @param endPage the end page (set to Integer.MAX_VALUE for all pages) + * @param outputPrefix used to construct the filename for the individual images + * @return true if the images were produced, false if there was an error + * @throws IOException if an I/O error occurs + */ + public boolean writeImage(PDDocument document, String imageType, String password, + int startPage, int endPage, String outputPrefix) + throws IOException + { + int resolution; + try + { + resolution = Toolkit.getDefaultToolkit().getScreenResolution(); + } + catch( HeadlessException e ) + { + resolution = 96; + } + return writeImage(document, imageType, password, startPage, endPage, outputPrefix, + 8, resolution); + } + + /** + * Converts a given page range of a PDF document to bitmap images. + * @param document the PDF document + * @param imageFormat the target format (ex. "png") + * @param password the password (needed if the PDF is encrypted) + * @param startPage the start page (1 is the first page) + * @param endPage the end page (set to Integer.MAX_VALUE for all pages) + * @param outputPrefix used to construct the filename for the individual images + * @param imageType the image type (see {@link BufferedImage}.TYPE_*) + * @param resolution the resolution in dpi (dots per inch) + * @return true if the images were produced, false if there was an error + * @throws IOException if an I/O error occurs + */ + public boolean writeImage(PDDocument document, String imageFormat, String password, + int startPage, int endPage, String outputPrefix, int imageType, int resolution) + throws IOException + { + boolean bSuccess = true; + 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(imageType, resolution); + String fileName = outputPrefix + (i + 1) + "." + imageFormat; + System.out.println( "Writing: " + fileName ); + output = ImageIO.createImageOutputStream( new File( fileName ) ); + + boolean foundWriter = false; + Iterator writerIter = ImageIO.getImageWritersByFormatName( imageFormat ); + 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); + } + IIOMetadata meta = createMetadata( image, imageWriter, writerParams, resolution); + imageWriter.setOutput( output ); + imageWriter.write( null, new IIOImage( image, null, meta ), writerParams ); + foundWriter = true; + } + catch( IIOException io ) + { + throw new IOException( io.getMessage() ); + } + finally + { + if( imageWriter != null ) + { + imageWriter.dispose(); + } + } + } + if( !foundWriter ) + { + bSuccess = false; + } + } + finally + { + if( output != null ) + { + output.flush(); + output.close(); + } + } + } + return bSuccess; + } + + private IIOMetadata createMetadata(RenderedImage image, ImageWriter imageWriter, + ImageWriteParam writerParams, int resolution) + { + ImageTypeSpecifier type; + if (writerParams.getDestinationType() != null) + { + type = writerParams.getDestinationType(); + } + else + { + type = ImageTypeSpecifier.createFromRenderedImage( image ); + } + IIOMetadata meta = imageWriter.getDefaultImageMetadata( type, writerParams ); + return (addResolution(meta, resolution) ? meta : null); + } + + private static final String STANDARD_METADATA_FORMAT = "javax_imageio_1.0"; + + private boolean addResolution(IIOMetadata meta, int resolution) + { + if (meta.isStandardMetadataFormatSupported()) + { + IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(STANDARD_METADATA_FORMAT); + IIOMetadataNode dim = getChildNode(root, "Dimension"); + IIOMetadataNode child; + child = getChildNode(dim, "HorizontalPixelSize"); + if (child == null) + { + child = new IIOMetadataNode("HorizontalPixelSize"); + dim.appendChild(child); + } + child.setAttribute("value", + Double.toString(resolution / 25.4)); + child = getChildNode(dim, "VerticalPixelSize"); + if (child == null) + { + child = new IIOMetadataNode("VerticalPixelSize"); + dim.appendChild(child); + } + child.setAttribute("value", + Double.toString(resolution / 25.4)); + try + { + meta.mergeTree(STANDARD_METADATA_FORMAT, root); + } + catch (IIOInvalidTreeException e) + { + throw new RuntimeException("Cannot update image metadata: " + + e.getMessage()); + } + return true; + } + return false; + } + + private static IIOMetadataNode getChildNode(Node n, String name) + { + NodeList nodes = n.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) + { + Node child = nodes.item(i); + if (name.equals(child.getNodeName())) + { + return (IIOMetadataNode)child; + } + } + return null; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMarkedContentExtractor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMarkedContentExtractor.java new file mode 100644 index 000000000..42f68f98d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMarkedContentExtractor.java @@ -0,0 +1,269 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Stack; + +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDMarkedContent; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; + +/** + * This is an stream engine to extract the marked content of a pdf. + * @author koch + * @version $Revision$ + */ +public class PDFMarkedContentExtractor extends PDFStreamEngine +{ + private boolean suppressDuplicateOverlappingText = true; + private List markedContents = new ArrayList(); + private Stack currentMarkedContents = new Stack(); + + private Map> characterListMapping = + new HashMap>(); + + /** + * encoding that text will be written in (or null). + */ + protected String outputEncoding; + + /** + * The normalizer is used to remove text ligatures/presentation forms + * and to correct the direction of right to left text, such as Arabic and Hebrew. + */ + private TextNormalize normalize = null; + + /** + * Instantiate a new PDFTextStripper object. This object will load + * properties from PDFMarkedContentExtractor.properties and will not + * do anything special to convert the text to a more encoding-specific + * output. + * + * @throws IOException If there is an error loading the properties. + */ + public PDFMarkedContentExtractor() throws IOException + { + super( ResourceLoader.loadProperties( + "org/apache/pdfbox/resources/PDFMarkedContentExtractor.properties", true ) ); + this.outputEncoding = null; + this.normalize = new TextNormalize(this.outputEncoding); + } + + + /** + * Instantiate a new PDFTextStripper object. Loading all of the operator mappings + * from the properties object that is passed in. Does not convert the text + * to more encoding-specific output. + * + * @param props The properties containing the mapping of operators to PDFOperator + * classes. + * + * @throws IOException If there is an error reading the properties. + */ + public PDFMarkedContentExtractor( Properties props ) throws IOException + { + super( props ); + this.outputEncoding = null; + this.normalize = new TextNormalize(this.outputEncoding); + } + + /** + * Instantiate a new PDFTextStripper object. This object will load + * properties from PDFMarkedContentExtractor.properties and will apply + * encoding-specific conversions to the output text. + * + * @param encoding The encoding that the output will be written in. + * @throws IOException If there is an error reading the properties. + */ + public PDFMarkedContentExtractor( String encoding ) throws IOException + { + super( ResourceLoader.loadProperties( + "org/apache/pdfbox/resources/PDFMarkedContentExtractor.properties", true )); + this.outputEncoding = encoding; + this.normalize = new TextNormalize(this.outputEncoding); + } + + + /** + * 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; + } + + + public void beginMarkedContentSequence(COSName tag, COSDictionary properties) + { + PDMarkedContent markedContent = PDMarkedContent.create(tag, properties); + if (this.currentMarkedContents.isEmpty()) + { + this.markedContents.add(markedContent); + } + else + { + PDMarkedContent currentMarkedContent = + this.currentMarkedContents.peek(); + if (currentMarkedContent != null) + { + currentMarkedContent.addMarkedContent(markedContent); + } + } + this.currentMarkedContents.push(markedContent); + } + + public void endMarkedContentSequence() + { + if (!this.currentMarkedContents.isEmpty()) + { + this.currentMarkedContents.pop(); + } + } + + public void xobject(PDXObject xobject) + { + if (!this.currentMarkedContents.isEmpty()) + { + this.currentMarkedContents.peek().addXObject(xobject); + } + } + + + /** + * This will process a TextPosition object and add the + * text to the list of characters on a page. It takes care of + * overlapping text. + * + * @param text The text to process. + */ + protected void processTextPosition( TextPosition text ) + { + boolean showCharacter = true; + if( this.suppressDuplicateOverlappingText ) + { + showCharacter = false; + String textCharacter = text.getCharacter(); + float textX = text.getX(); + float textY = text.getY(); + List sameTextCharacters = this.characterListMapping.get( textCharacter ); + if( sameTextCharacters == null ) + { + sameTextCharacters = new ArrayList(); + this.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 textList = new ArrayList(); + + /* In the wild, some PDF encoded documents put diacritics (accents on + * top of characters) into a separate Tj element. When displaying them + * graphically, the two chunks get overlayed. With text output though, + * we need to do the overlay. This code recombines the diacritic with + * its associated character if the two are consecutive. + */ + if(textList.isEmpty()) + { + textList.add(text); + } + else + { + /* test if we overlap the previous entry. + * Note that we are making an assumption that we need to only look back + * one TextPosition to find what we are overlapping. + * This may not always be true. */ + TextPosition previousTextPosition = (TextPosition)textList.get(textList.size()-1); + if(text.isDiacritic() && previousTextPosition.contains(text)) + { + previousTextPosition.mergeDiacritic(text, this.normalize); + } + /* If the previous TextPosition was the diacritic, merge it into this + * one and remove it from the list. */ + else if(previousTextPosition.isDiacritic() && text.contains(previousTextPosition)) + { + text.mergeDiacritic(previousTextPosition, this.normalize); + textList.remove(textList.size()-1); + textList.add(text); + } + else + { + textList.add(text); + } + } + if (!this.currentMarkedContents.isEmpty()) + { + this.currentMarkedContents.peek().addText(text); + } + } + } + + + public List getMarkedContents() + { + return this.markedContents; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMergerUtility.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMergerUtility.java new file mode 100644 index 000000000..b3abd0c1d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFMergerUtility.java @@ -0,0 +1,442 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.File; +import java.io.FileInputStream; +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 org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDDocumentInformation; +import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.COSArrayList; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; +import org.apache.pdfbox.pdmodel.interactive.form.PDFieldFactory; + +/** + * This class will take a list of pdf documents and merge them, saving the result + * in a new document. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class PDFMergerUtility +{ + + private List sources; + private String destinationFileName; + private OutputStream destinationStream; + private boolean ignoreAcroFormErrors = false; + + /** + * Instantiate a new PDFMergerUtility. + */ + public PDFMergerUtility() + { + sources = new ArrayList(); + } + + /** + * Get the name of the destination file. + * @return Returns the destination. + */ + public String getDestinationFileName() + { + return destinationFileName; + } + + /** + * Set the name of the destination file. + * @param destination + * The destination to set. + */ + public void setDestinationFileName(String destination) + { + this.destinationFileName = destination; + } + + /** + * Get the destination OutputStream. + * @return Returns the destination OutputStream. + */ + public OutputStream getDestinationStream() + { + return destinationStream; + } + + /** + * Set the destination OutputStream. + * @param destination + * The destination to set. + */ + public void setDestinationStream(OutputStream destinationStream) + { + this.destinationStream = destinationStream; + } + + /** + * Add a source file to the list of files to merge. + * + * @param source Full path and file name of source document. + */ + public void addSource(String source) + { + try + { + sources.add(new FileInputStream(new File(source))); + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + /** + * Add a source file to the list of files to merge. + * + * @param source File representing source document + */ + public void addSource(File source) + { + try + { + sources.add(new FileInputStream(source)); + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + /** + * Add a source to the list of documents to merge. + * + * @param source InputStream representing source document + */ + public void addSource(InputStream source) + { + sources.add(source); + } + + /** + * Add a list of sources to the list of documents to merge. + * + * @param source List of InputStream objects representing source documents + */ + public void addSources(List sources) + { + this.sources.addAll(sources); + } + + /** + * Merge the list of source documents, saving the result in the destination file. + * + * @throws IOException If there is an error saving the document. + * @throws COSVisitorException If an error occurs while saving the destination file. + */ + public void mergeDocuments() throws IOException, COSVisitorException + { + PDDocument destination = null; + InputStream sourceFile; + PDDocument source; + if (sources != null && sources.size() > 0) + { + java.util.Vector tobeclosed = new java.util.Vector(); + + try + { + Iterator sit = sources.iterator(); + sourceFile = sit.next(); + destination = PDDocument.load(sourceFile); + + while (sit.hasNext()) + { + sourceFile = sit.next(); + source = PDDocument.load(sourceFile); + tobeclosed.add(source); + appendDocument(destination, source); + } + if(destinationStream == null) + { + destination.save(destinationFileName); + } + else + { + destination.save(destinationStream); + } + } + finally + { + if (destination != null) + { + destination.close(); + } + for (PDDocument doc : tobeclosed) + { + doc.close(); + } + } + } + } + + + /** + * append all pages from source to destination. + * + * @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: source PDF is encrypted, can't append encrypted PDF documents." ); + } + PDDocumentInformation destInfo = destination.getDocumentInformation(); + PDDocumentInformation srcInfo = source.getDocumentInformation(); + destInfo.getDictionary().mergeInto( srcInfo.getDictionary() ); + + PDDocumentCatalog destCatalog = destination.getDocumentCatalog(); + PDDocumentCatalog srcCatalog = source.getDocumentCatalog(); + + if( destCatalog.getOpenAction() == null ) + { + destCatalog.setOpenAction( srcCatalog.getOpenAction() ); + } + + PDFCloneUtility cloner = new PDFCloneUtility(destination); + + try + { + PDAcroForm destAcroForm = destCatalog.getAcroForm(); + PDAcroForm srcAcroForm = srcCatalog.getAcroForm(); + if( destAcroForm == null ) + { + cloner.cloneForNewDocument( srcAcroForm ); + destCatalog.setAcroForm( srcAcroForm ); + } + else + { + if( srcAcroForm != null ) + { + mergeAcroForm(cloner, destAcroForm, srcAcroForm); + } + } + } + catch(Exception e) + { + // if we are not ignoring exceptions, we'll re-throw this + if(!ignoreAcroFormErrors) + { + throw (IOException)e; + } + } + + COSArray destThreads = (COSArray)destCatalog.getCOSDictionary().getDictionaryObject( + COSName.THREADS); + COSArray srcThreads = (COSArray)cloner.cloneForNewDocument( + destCatalog.getCOSDictionary().getDictionaryObject( COSName.THREADS )); + if( destThreads == null ) + { + destCatalog.getCOSDictionary().setItem( COSName.THREADS, srcThreads ); + } + else + { + destThreads.addAll( srcThreads ); + } + + PDDocumentNameDictionary destNames = destCatalog.getNames(); + PDDocumentNameDictionary srcNames = srcCatalog.getNames(); + if( srcNames != null ) + { + if( destNames == null ) + { + destCatalog.getCOSDictionary().setItem( COSName.NAMES, + cloner.cloneForNewDocument( srcNames ) ); + } + else + { + cloner.cloneMerge(srcNames, destNames); + } + + } + + PDDocumentOutline destOutline = destCatalog.getDocumentOutline(); + PDDocumentOutline srcOutline = srcCatalog.getDocumentOutline(); + if( srcOutline != null ) + { + if( destOutline == null ) + { + PDDocumentOutline cloned = + new PDDocumentOutline( (COSDictionary)cloner.cloneForNewDocument( srcOutline ) ); + destCatalog.setDocumentOutline( cloned ); + } + else + { + PDOutlineItem first = srcOutline.getFirstChild(); + if(first != null) + { + PDOutlineItem clonedFirst = new PDOutlineItem( + (COSDictionary)cloner.cloneForNewDocument( first )); + destOutline.appendChild( clonedFirst ); + } + } + } + + String destPageMode = destCatalog.getPageMode(); + String srcPageMode = srcCatalog.getPageMode(); + if( destPageMode == null ) + { + destCatalog.setPageMode( srcPageMode ); + } + + COSDictionary destLabels = (COSDictionary)destCatalog.getCOSDictionary().getDictionaryObject( + COSName.PAGE_LABELS); + COSDictionary srcLabels = (COSDictionary)srcCatalog.getCOSDictionary().getDictionaryObject( + COSName.PAGE_LABELS); + if( srcLabels != null ) + { + int destPageCount = destination.getNumberOfPages(); + COSArray destNums = null; + if( destLabels == null ) + { + destLabels = new COSDictionary(); + destNums = new COSArray(); + destLabels.setItem( COSName.NUMS, destNums ); + destCatalog.getCOSDictionary().setItem( COSName.PAGE_LABELS, destLabels ); + } + else + { + destNums = (COSArray)destLabels.getDictionaryObject( COSName.NUMS ); + } + COSArray srcNums = (COSArray)srcLabels.getDictionaryObject( COSName.NUMS ); + if (srcNums != null) + { + for( int i=0; i pages = source.getDocumentCatalog().getAllPages(); + Iterator pageIter = pages.iterator(); + while( pageIter.hasNext() ) + { + PDPage page = pageIter.next(); + PDPage newPage = + new PDPage( (COSDictionary)cloner.cloneForNewDocument( page.getCOSDictionary() ) ); + newPage.setCropBox( page.findCropBox() ); + newPage.setMediaBox( page.findMediaBox() ); + newPage.setRotation( page.findRotation() ); + destination.addPage( newPage ); + } + } + + + private int nextFieldNum = 1; + + /** + * Merge the contents of the source form into the destination form + * for the destination file. + * + * @param cloner the object cloner for the destination document + * @param destAcroForm the destination form + * @param srcAcroForm the source form + * @throws IOException If an error occurs while adding the field. + */ + private void mergeAcroForm(PDFCloneUtility cloner, PDAcroForm destAcroForm, PDAcroForm srcAcroForm) + throws IOException + { + List destFields = destAcroForm.getFields(); + List srcFields = srcAcroForm.getFields(); + if( srcFields != null ) + { + if( destFields == null ) + { + destFields = new COSArrayList(); + destAcroForm.setFields( destFields ); + } + Iterator srcFieldsIterator = srcFields.iterator(); + while (srcFieldsIterator.hasNext()) + { + PDField srcField = (PDField)srcFieldsIterator.next(); + PDField destField = + PDFieldFactory.createField( + destAcroForm, + (COSDictionary)cloner.cloneForNewDocument(srcField.getDictionary() )); + // if the form already has a field with this name then we need to rename this field + // to prevent merge conflicts. + if ( destAcroForm.getField(destField.getFullyQualifiedName()) != null ) + { + destField.setPartialName("dummyFieldName"+(nextFieldNum++)); + } + destFields.add(destField); + } + } + } + + public boolean isIgnoreAcroFormErrors() + { + return ignoreAcroFormErrors; + } + + public void setIgnoreAcroFormErrors(boolean ignoreAcroFormErrors) + { + this.ignoreAcroFormErrors = ignoreAcroFormErrors; + } + + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFOperator.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFOperator.java new file mode 100644 index 000000000..d7246f360 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFOperator.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 + * @version $Revision: 1.14 $ + */ +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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFStreamEngine.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFStreamEngine.java new file mode 100644 index 000000000..7d5624fff --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFStreamEngine.java @@ -0,0 +1,713 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.exceptions.WrappedIOException; + +import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; + +import org.apache.pdfbox.pdmodel.common.PDMatrix; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType3Font; + +import org.apache.pdfbox.pdmodel.graphics.PDExtendedGraphicsState; +import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; + +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * 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 + * @version $Revision: 1.38 $ + */ +public class PDFStreamEngine +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(PDFStreamEngine.class); + + /** + * The PDF operators that are ignored by this engine. + */ + private final Set unsupportedOperators = new HashSet(); + + private static final byte[] SPACE_BYTES = { (byte)32 }; + + private PDGraphicsState graphicsState = null; + + private Matrix textMatrix = null; + private Matrix textLineMatrix = null; + private Stack graphicsStack = new Stack(); + + private Map operators = new HashMap(); + + private Stack streamResourcesStack = new Stack(); + + private PDPage page; + + private Map documentFontCache = new HashMap(); + + private int validCharCnt; + private int totalCharCnt; + + /** + * Flag to skip malformed or otherwise unparseable input where possible. + */ + private boolean forceParsing = false; + + /** + * This is a simple internal class used by the Stream engine to handle the + * resources stack. + */ + private static class StreamResources + { + private Map fonts; + private Map colorSpaces; + private Map xobjects; + private Map graphicsStates; + private PDResources resources; + + private StreamResources() + {}; + } + + /** + * Constructor. + */ + public PDFStreamEngine() + { + //default constructor + validCharCnt = 0; + totalCharCnt = 0; + + } + + /** + * Constructor with engine properties. The property keys are all + * PDF operators, the values are class names used to execute those + * operators. An empty value means that the operator will be silently + * ignored. + * + * @param properties The engine properties. + * + * @throws IOException If there is an error setting the engine properties. + */ + public PDFStreamEngine( Properties properties ) throws IOException + { + if( properties == null ) + { + throw new NullPointerException( "properties cannot be null" ); + } + Enumeration names = properties.propertyNames(); + for ( Object name : Collections.list( names ) ) + { + String operator = name.toString(); + String processorClassName = properties.getProperty( operator ); + if( "".equals( processorClassName ) ) + { + unsupportedOperators.add( operator ); + } + else + { + try + { + Class klass = Class.forName( processorClassName ); + OperatorProcessor processor = + (OperatorProcessor) klass.newInstance(); + registerOperatorProcessor( operator, processor ); + } + catch( Exception e ) + { + throw new WrappedIOException( + "OperatorProcessor class " + processorClassName + + " could not be instantiated", e ); + } + } + } + validCharCnt = 0; + totalCharCnt = 0; + } + + public boolean isForceParsing() { + return forceParsing; + } + + public void setForceParsing(boolean forceParsing) { + this.forceParsing = forceParsing; + } + + /** + * Register a custom operator processor with the engine. + * + * @param operator The operator as a string. + * @param op Processor instance. + */ + public void registerOperatorProcessor( String operator, OperatorProcessor op ) + { + op.setContext( this ); + operators.put( operator, op ); + } + + /** + * This method must be called between processing documents. The + * PDFStreamEngine caches information for the document between pages + * and this will release the cached information. This only needs + * to be called if processing a new document. + * + */ + public void resetEngine() + { + documentFontCache.clear(); + validCharCnt = 0; + totalCharCnt = 0; + } + + /** + * 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(aPage.findCropBox()); + textMatrix = null; + textLineMatrix = null; + graphicsStack.clear(); + streamResourcesStack.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( documentFontCache ); + sr.colorSpaces = resources.getColorSpaces(); + sr.xobjects = resources.getXObjects(); + sr.graphicsStates = resources.getGraphicsStates(); + sr.resources = resources; + + streamResourcesStack.push(sr); + try { + processSubStream(cosStream); + } finally { + streamResourcesStack.pop(); + } + } else { + processSubStream(cosStream); + } + } + + private void processSubStream(COSStream cosStream) throws IOException { + List arguments = new ArrayList(); + PDFStreamParser parser = new PDFStreamParser(cosStream, forceParsing); + try { + Iterator iter = parser.getTokenIterator(); + + while (iter.hasNext()) { + Object next = iter.next(); + if (log.isDebugEnabled()) { + log.debug("processing substream token: " + 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((COSBase) next); + } + } + } finally { + parser.close(); + } + } + + + /** + * A method provided as an event interface to allow a subclass to perform + * some specific functionality when text needs to be processed. + * + * @param text The text to be processed. + */ + protected void processTextPosition( TextPosition text ) + { + //subclasses can override to provide specific functionality. + } + + /** + * A method provided as an event interface to allow a subclass to perform + * some specific functionality on the string encoded by a glyph. + * + * @param str The string to be processed. + */ + protected String inspectFontEncoding( String str ) + { + return str; + } + + /** + * Process encoded text from the PDF Stream. + * You should override this method if you want to perform an action when + * encoded text is being processed. + * + * @param string The encoded text + * + * @throws IOException If there is an error processing the string + */ + public void processEncodedText( byte[] string ) throws IOException + { + /* Note on variable names. There are three different units being used + * in this code. Character sizes are given in glyph units, text locations + * are initially given in text units, and we want to save the data in + * display units. The variable names should end with Text or Disp to + * represent if the values are in text or disp units (no glyph units are saved). + */ + final float fontSizeText = graphicsState.getTextState().getFontSize(); + final float horizontalScalingText = graphicsState.getTextState().getHorizontalScalingPercent()/100f; + //float verticalScalingText = horizontalScaling;//not sure if this is right but what else to do??? + final float riseText = graphicsState.getTextState().getRise(); + final float wordSpacingText = graphicsState.getTextState().getWordSpacing(); + final float characterSpacingText = graphicsState.getTextState().getCharacterSpacing(); + + //We won't know the actual number of characters until + //we process the byte data(could be two bytes each) but + //it won't ever be more than string.length*2(there are some cases + //were a single byte will result in two output characters "fi" + + final PDFont font = graphicsState.getTextState().getFont(); + // all fonts are providing the width/height of a character in thousandths of a unit of text space + float fontMatrixXScaling = 1/1000f; + float fontMatrixYScaling = 1/1000f; + float glyphSpaceToTextSpaceFactor = 1/1000f; + // expect Type3 fonts, those are providing the width of a character in glyph space units + if (font instanceof PDType3Font) + { + PDMatrix fontMatrix = font.getFontMatrix(); + fontMatrixXScaling = fontMatrix.getValue(0, 0); + fontMatrixYScaling = fontMatrix.getValue(1, 1); + //This will typically be 1000 but in the case of a type3 font + //this might be a different number + glyphSpaceToTextSpaceFactor = 1f/fontMatrix.getValue( 0, 0 ); + } + float spaceWidthText=0; + try + { + // to avoid crash as described in PDFBOX-614 + // lets see what the space displacement should be + spaceWidthText = (font.getFontWidth( SPACE_BYTES, 0, 1 )*glyphSpaceToTextSpaceFactor); + } + catch (Throwable exception) + { + log.warn( exception, exception); + } + + if( spaceWidthText == 0 ) + { + spaceWidthText = (font.getAverageFontWidth()*glyphSpaceToTextSpaceFactor); + //The average space width appears to be higher than necessary + //so lets make it a little bit smaller. + spaceWidthText *= .80f; + } + + float maxVerticalDisplacementText = 0; + + Matrix textStateParameters = new Matrix(); + textStateParameters.setValue(0,0, fontSizeText*horizontalScalingText); + textStateParameters.setValue(1,1, fontSizeText); + textStateParameters.setValue(2,1, riseText); + + int pageRotation = page.findRotation(); + float pageHeight = page.findMediaBox().getHeight(); + float pageWidth = page.findMediaBox().getWidth(); + + Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); + Matrix textXctm = new Matrix(); + Matrix textMatrixEnd = new Matrix(); + Matrix td = new Matrix(); + Matrix tempMatrix = new Matrix(); + + int codeLength = 1; + for( int i=0; i arguments ) throws IOException + { + try + { + PDFOperator oper = PDFOperator.getOperator( operation ); + processOperator( oper, arguments ); + } + catch (IOException e) + { + log.warn(e, e); + } + } + + /** + * 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 + { + try + { + String operation = operator.getOperation(); + OperatorProcessor processor = (OperatorProcessor)operators.get( operation ); + if( processor != null ) + { + processor.setContext(this); + processor.process( operator, arguments ); + } + else + { + if (!unsupportedOperators.contains(operation)) + { + log.info("unsupported/disabled operation: " + operation); + unsupportedOperators.add(operation); + } + } + } + catch (Exception e) + { + log.warn(e, e); + } + } + + /** + * @return Returns the colorSpaces. + */ + public Map getColorSpaces() + { + return streamResourcesStack.peek().colorSpaces; + } + + /** + * @return Returns the colorSpaces. + */ + public Map getXObjects() + { + return streamResourcesStack.peek().xobjects; + } + + /** + * @param value The colorSpaces to set. + */ + public void setColorSpaces(Map value) + { + streamResourcesStack.peek().colorSpaces = value; + } + /** + * @return Returns the fonts. + */ + public Map getFonts() + { + return streamResourcesStack.peek().fonts; + } + /** + * @param value The fonts to set. + */ + public void setFonts(Map value) + { + 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 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 streamResourcesStack.peek().resources; + } + + /** + * Get the current page that is being processed. + * + * @return The page being processed. + */ + public PDPage getCurrentPage() + { + return page; + } + + /** + * Get the total number of valid characters in the doc + * that could be decoded in processEncodedText(). + * @return The number of valid characters. + */ + public int getValidCharCnt() + { + return validCharCnt; + } + + /** + * Get the total number of characters in the doc + * (including ones that could not be mapped). + * @return The number of characters. + */ + public int getTotalCharCnt() + { + return totalCharCnt; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFText2HTML.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFText2HTML.java new file mode 100644 index 000000000..42e45a539 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFText2HTML.java @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * 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.3 $ + */ +public class PDFText2HTML extends PDFTextStripper +{ + private static final int INITIAL_PDF_TO_HTML_BYTES = 8192; + + private boolean onFirstPage = true; + + /** + * Constructor. + * @param encoding The encoding to be used + * @throws IOException If there is an error during initialization. + */ + public PDFText2HTML(String encoding) throws IOException + { + super(encoding); + setLineSeparator(systemLineSeparator); + setParagraphStart("

    "); + setParagraphEnd("

    "+systemLineSeparator); + setPageStart("
    "); + setPageEnd("
    "+systemLineSeparator); + setArticleStart(systemLineSeparator); + setArticleEnd(systemLineSeparator); + } + + /** + * Write the header to the output document. Now also writes the tag defining + * the character encoding. + * + * @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("\n"); + buf.append(""); + buf.append("" + escape(getTitle()) + "\n"); + if(outputEncoding != null) + { + buf.append("\n"); + } + buf.append("\n"); + buf.append("\n"); + super.writeString(buf.toString()); + } + + /** + * {@inheritDoc} + */ + protected void writePage() throws IOException + { + if (onFirstPage) + { + writeHeader(); + onFirstPage = false; + } + super.writePage(); + } + + /** + * {@inheritDoc} + */ + public void endDocument(PDDocument pdf) throws IOException + { + super.writeString(""); + } + + /** + * This method will attempt to guess the title of the document using + * either the document properties or the first lines of text. + * + * @return returns the title. + */ + protected String getTitle() + { + String titleGuess = document.getDocumentInformation().getTitle(); + if(titleGuess != null && titleGuess.length() > 0) + { + return titleGuess; + } + else + { + Iterator> textIter = getCharactersByArticle().iterator(); + float lastFontSize = -1.0f; + + StringBuffer titleText = new StringBuffer(); + while (textIter.hasNext()) + { + Iterator textByArticle = textIter.next().iterator(); + while (textByArticle.hasNext()) + { + TextPosition position = textByArticle.next(); + + float currentFontSize = position.getFontSize(); + //If we're past 64 chars we will assume that we're past the title + //64 is arbitrary + if (currentFontSize != lastFontSize || titleText.length() > 64) + { + if (titleText.length() > 0) + { + return titleText.toString(); + } + lastFontSize = currentFontSize; + } + if (currentFontSize > 13.0f) + { // most body text is 12pt + titleText.append(position.getCharacter()); + } + } + } + } + return ""; + } + + + /** + * Write out the article separator (div tag) with proper text direction + * information. + * + * @param isltr true if direction of text is left to right + * @throws IOException + * If there is an error writing to the stream. + */ + protected void startArticle(boolean isltr) throws IOException + { + if (isltr) + { + super.writeString("
    "); + } + else + { + super.writeString("
    "); + } + } + + /** + * Write out the article separator. + * + * @throws IOException + * If there is an error writing to the stream. + */ + protected void endArticle() throws IOException + { + super.endArticle(); + super.writeString("
    "); + } + + /** + * Write a string to the output stream and escape some HTML characters. + * + * @param chars String to be written to the stream + * @throws IOException + * If there is an error writing to the stream. + */ + protected void writeString(String chars) throws IOException + { + super.writeString(escape(chars)); + } + + /** + * Escape some HTML characters. + * + * @param chars String to be escaped + * @return returns escaped String. + */ + private String escape(String chars) + { + StringBuilder builder = new StringBuilder(chars.length()); + for (int i = 0; i < chars.length(); i++) + { + char c = chars.charAt(i); + // write non-ASCII as named entities + if ((c < 32) || (c > 126)) + { + int charAsInt = c; + builder.append("&#").append(charAsInt).append(";"); + } + else + { + switch (c) + { + case 34: + builder.append("""); + break; + case 38: + builder.append("&"); + break; + case 60: + builder.append("<"); + break; + case 62: + builder.append(">"); + break; + default: + builder.append(String.valueOf(c)); + } + } + } + return builder.toString(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripper.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripper.java new file mode 100644 index 000000000..18f8af878 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripper.java @@ -0,0 +1,1882 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; +import java.util.regex.Pattern; + +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.exceptions.CryptographyException; +import org.apache.pdfbox.exceptions.InvalidPasswordException; +import org.apache.pdfbox.exceptions.WrappedIOException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.COSObjectable; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; +import org.apache.pdfbox.pdmodel.interactive.pagenavigation.PDThreadBead; +import org.apache.pdfbox.util.TextPosition; + + +/** + * This class will take a pdf document and strip out all of the text and ignore the + * formatting and such. Please note; it is up to clients of this class to verify that + * a specific user has the correct permissions to extract text from the + * PDF document. + * + * The basic flow of this process is that we get a document and use a series of + * processXXX() functions that work on smaller and smaller chunks of the page. + * Eventually, we fully process each page and then print it. + * + * @author Ben Litchfield + * @version $Revision: 1.70 $ + */ +public class PDFTextStripper extends PDFStreamEngine +{ + + private static final String thisClassName = PDFTextStripper.class.getSimpleName().toLowerCase(); + + private static float DEFAULT_INDENT_THRESHOLD = 2.0f; + private static float DEFAULT_DROP_THRESHOLD = 2.5f; + + //enable the ability to set the default indent/drop thresholds + //with -D system properties: + // pdftextstripper.indent + // pdftextstripper.drop + static + { + String prop = thisClassName+".indent"; + String s = System.getProperty(prop); + if(s!=null && s.length()>0) + { + try + { + float f = Float.parseFloat(s); + DEFAULT_INDENT_THRESHOLD = f; + } + catch(NumberFormatException nfe) + { + //ignore and use default + } + } + prop = thisClassName+".drop"; + s = System.getProperty(prop); + if(s!=null && s.length()>0) + { + try + { + float f = Float.parseFloat(s); + DEFAULT_DROP_THRESHOLD = f; + } + catch(NumberFormatException nfe){ + //ignore and use default + } + } + } + + /** + * The platforms line separator. + */ + protected final String systemLineSeparator = System.getProperty("line.separator"); + + private String lineSeparator = systemLineSeparator; + private String pageSeparator = systemLineSeparator; + private String wordSeparator = " "; + private String paragraphStart = ""; + private String paragraphEnd = ""; + private String pageStart = ""; + private String pageEnd = pageSeparator; + private String articleStart = ""; + private String articleEnd = ""; + + + 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 boolean suppressDuplicateOverlappingText = true; + private boolean shouldSeparateByBeads = true; + private boolean sortByPosition = false; + private boolean addMoreFormatting = false; + + private float indentThreshold = DEFAULT_INDENT_THRESHOLD; + private float dropThreshold = DEFAULT_DROP_THRESHOLD; + + // We will need to estimate where to add spaces. + // These are used to help guess. + private float spacingTolerance = .5f; + private float averageCharTolerance = .3f; + + 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>(); + + /** + * encoding that text will be written in (or null). + */ + protected String outputEncoding; + + /** + * The document to read. + */ + protected PDDocument document; + /** + * The stream to write the output to. + */ + protected Writer output; + + /** + * The normalizer is used to remove text ligatures/presentation forms + * and to correct the direction of right to left text, such as Arabic and Hebrew. + */ + private TextNormalize normalize = null; + + /** + * Instantiate a new PDFTextStripper object. This object will load + * properties from PDFTextStripper.properties and will not do + * anything special to convert the text to a more encoding-specific + * output. + * + * @throws IOException If there is an error loading the properties. + */ + public PDFTextStripper() throws IOException + { + super( ResourceLoader.loadProperties( + "org/apache/pdfbox/resources/PDFTextStripper.properties", true ) ); + this.outputEncoding = null; + normalize = new TextNormalize(this.outputEncoding); + } + + + /** + * Instantiate a new PDFTextStripper object. Loading all of the operator mappings + * from the properties object that is passed in. Does not convert the text + * to more encoding-specific output. + * + * @param props The properties containing the mapping of operators to PDFOperator + * classes. + * + * @throws IOException If there is an error reading the properties. + */ + public PDFTextStripper( Properties props ) throws IOException + { + super( props ); + this.outputEncoding = null; + normalize = new TextNormalize(this.outputEncoding); + } + /** + * Instantiate a new PDFTextStripper object. This object will load + * properties from PDFTextStripper.properties and will apply + * encoding-specific conversions to the output text. + * + * @param encoding The encoding that the output will be written in. + * @throws IOException If there is an error reading the properties. + */ + public PDFTextStripper( String encoding ) throws IOException + { + super( ResourceLoader.loadProperties( + "org/apache/pdfbox/resources/PDFTextStripper.properties", true )); + this.outputEncoding = encoding; + normalize = new TextNormalize(this.outputEncoding); + } + + /** + * This will return the text of a document. See writeText.
    + * 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 ); + } + + /** + * {@inheritDoc} + */ + public void resetEngine() + { + super.resetEngine(); + currentPageNo = 0; + } + + /** + * 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 + { + resetEngine(); + document = doc; + output = outputStream; + if (getAddMoreFormatting()) { + paragraphEnd = lineSeparator; + pageStart = lineSeparator; + articleStart = lineSeparator; + articleEnd = lineSeparator; + } + 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. + // + try + { + document.decrypt(""); + } + catch (CryptographyException e) + { + throw new WrappedIOException("Error decrypting document, details: ", e); + } + catch (InvalidPasswordException e) + { + throw new WrappedIOException("Error: document is encrypted", e); + } + } + + 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( 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(); + currentPageNo++; + if( contentStream != null ) + { + COSStream contents = contentStream.getStream(); + processPage( nextPage, contents ); + } + } + } + + 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 + { + 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)charactersByArticle.get( i )).clear(); + } + else + { + charactersByArticle.set( i, new ArrayList() ); + } + } + + characterListMapping.clear(); + processStream( page, page.findResources(), content ); + writePage(); + endPage( page ); + } + + } + + /** + * Start a new article, which is typically defined as a column + * on a single page (also referred to as a bead). This assumes + * that the primary direction of text is left to right. + * Default implementation is to do nothing. Subclasses + * may provide additional information. + * + * @throws IOException If there is any error writing to the stream. + */ + protected void startArticle() throws IOException + { + startArticle(true); + } + + /** + * Start a new article, which is typically defined as a column + * on a single page (also referred to as a bead). + * Default implementation is to do nothing. Subclasses + * may provide additional information. + * + * @param isltr true if primary direction of text is left to right. + * @throws IOException If there is any error writing to the stream. + */ + protected void startArticle(boolean isltr) throws IOException + { + output.write(getArticleStart()); + } + + /** + * End an article. Default implementation is to do nothing. Subclasses + * may provide additional information. + * + * @throws IOException If there is any error writing to the stream. + */ + protected void endArticle() throws IOException + { + output.write(getArticleEnd()); + } + + /** + * 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 + } + + private static final float ENDOFLASTTEXTX_RESET_VALUE = -1; + private static final float MAXYFORLINE_RESET_VALUE = -Float.MAX_VALUE; + private static final float EXPECTEDSTARTOFNEXTWORDX_RESET_VALUE = -Float.MAX_VALUE; + private static final float MAXHEIGHTFORLINE_RESET_VALUE = -1; + private static final float MINYTOPFORLINE_RESET_VALUE = Float.MAX_VALUE; + private static final float LASTWORDSPACING_RESET_VALUE = -1; + + /** + * This will print the text of the processed page to "output". + * It will estimate, based on the coordinates of the text, where + * newlines and word spacings should be placed. The text will be + * sorted only if that feature was enabled. + * + * @throws IOException If there is an error writing the text. + */ + protected void writePage() throws IOException + { + float maxYForLine = MAXYFORLINE_RESET_VALUE; + float minYTopForLine = MINYTOPFORLINE_RESET_VALUE; + float endOfLastTextX = ENDOFLASTTEXTX_RESET_VALUE; + float lastWordSpacing = LASTWORDSPACING_RESET_VALUE; + float maxHeightForLine = MAXHEIGHTFORLINE_RESET_VALUE; + PositionWrapper lastPosition = null; + PositionWrapper lastLineStartPosition = null; + + boolean startOfPage = true;//flag to indicate start of page + boolean startOfArticle = true; + if(charactersByArticle.size() > 0) { + writePageStart(); + } + + for( int i = 0; i < charactersByArticle.size(); i++) + { + List textList = charactersByArticle.get( i ); + if( getSortByPosition() ) + { + TextPositionComparator comparator = new TextPositionComparator(); + Collections.sort( textList, comparator ); + } + + Iterator textIter = textList.iterator(); + + /* Before we can display the text, we need to do some normalizing. + * Arabic and Hebrew text is right to left and is typically stored + * in its logical format, which means that the rightmost character is + * stored first, followed by the second character from the right etc. + * However, PDF stores the text in presentation form, which is left to + * right. We need to do some normalization to convert the PDF data to + * the proper logical output format. + * + * Note that if we did not sort the text, then the output of reversing the + * text is undefined and can sometimes produce worse output then not trying + * to reverse the order. Sorting should be done for these languages. + * */ + + /* First step is to determine if we have any right to left text, and + * if so, is it dominant. */ + int ltrCnt = 0; + int rtlCnt = 0; + + while( textIter.hasNext() ) + { + TextPosition position = (TextPosition)textIter.next(); + String stringValue = position.getCharacter(); + for (int a = 0; a < stringValue.length(); a++) + { + byte dir = Character.getDirectionality(stringValue.charAt(a)); + if ((dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT ) || + (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING) || + (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE )) + { + ltrCnt++; + } + else if ((dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT ) || + (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) || + (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING) || + (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE )) + { + rtlCnt++; + } + } + } + + // choose the dominant direction + boolean isRtlDominant = rtlCnt > ltrCnt; + + startArticle(!isRtlDominant); + startOfArticle = true; + // we will later use this to skip reordering + boolean hasRtl = rtlCnt > 0; + + /* Now cycle through to print the text. + * We queue up a line at a time before we print so that we can convert + * the line from presentation form to logical form (if needed). + */ + List line = new ArrayList(); + + textIter = textList.iterator(); // start from the beginning again + /* PDF files don't always store spaces. We will need to guess where we should add + * spaces based on the distances between TextPositions. Historically, this was done + * based on the size of the space character provided by the font. In general, this worked + * but there were cases where it did not work. Calculating the average character width + * and using that as a metric works better in some cases but fails in some cases where the + * spacing worked. So we use both. NOTE: Adobe reader also fails on some of these examples. + */ + //Keeps track of the previous average character width + float previousAveCharWidth = -1; + while( textIter.hasNext() ) + { + TextPosition position = (TextPosition)textIter.next(); + PositionWrapper current = new PositionWrapper(position); + String characterValue = position.getCharacter(); + + //Resets the average character width when we see a change in font + // or a change in the font size + if(lastPosition != null && ((position.getFont() != lastPosition.getTextPosition().getFont()) + || (position.getFontSize() != lastPosition.getTextPosition().getFontSize()))) + { + previousAveCharWidth = -1; + } + + float positionX; + float positionY; + float positionWidth; + float positionHeight; + + /* If we are sorting, then we need to use the text direction + * adjusted coordinates, because they were used in the sorting. */ + if (getSortByPosition()) + { + positionX = position.getXDirAdj(); + positionY = position.getYDirAdj(); + positionWidth = position.getWidthDirAdj(); + positionHeight = position.getHeightDir(); + } + else + { + positionX = position.getX(); + positionY = position.getY(); + positionWidth = position.getWidth(); + positionHeight = position.getHeight(); + } + + //The current amount of characters in a word + int wordCharCount = position.getIndividualWidths().length; + + /* Estimate the expected width of the space based on the + * space character with some margin. */ + float wordSpacing = position.getWidthOfSpace(); + float deltaSpace = 0; + if ((wordSpacing == 0) || (wordSpacing == Float.NaN)) + { + deltaSpace = Float.MAX_VALUE; + } + else + { + if( lastWordSpacing < 0 ) + { + deltaSpace = (wordSpacing * getSpacingTolerance()); + } + else + { + deltaSpace = (((wordSpacing+lastWordSpacing)/2f)* getSpacingTolerance()); + } + } + + /* Estimate the expected width of the space based on the + * average character width with some margin. This calculation does not + * make a true average (average of averages) but we found that it gave the + * best results after numerous experiments. Based on experiments we also found that + * .3 worked well. */ + float averageCharWidth = -1; + if(previousAveCharWidth < 0) + { + averageCharWidth = (positionWidth/wordCharCount); + } + else + { + averageCharWidth = (previousAveCharWidth + (positionWidth/wordCharCount))/2f; + } + float deltaCharWidth = (averageCharWidth * getAverageCharTolerance()); + + //Compares the values obtained by the average method and the wordSpacing method and picks + //the smaller number. + float expectedStartOfNextWordX = EXPECTEDSTARTOFNEXTWORDX_RESET_VALUE; + if(endOfLastTextX != ENDOFLASTTEXTX_RESET_VALUE) + { + if(deltaCharWidth > deltaSpace) + { + expectedStartOfNextWordX = endOfLastTextX + deltaSpace; + } + else + { + expectedStartOfNextWordX = endOfLastTextX + deltaCharWidth; + } + } + + if( lastPosition != null ) + { + if(startOfArticle){ + lastPosition.setArticleStart(); + startOfArticle = false; + } + // 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. + + /* XXX BC: In theory, this check should really check if the next char is in full range + * seen in this line. This is what I tried to do with minYTopForLine, but this caused a lot + * of regression test failures. So, I'm leaving it be for now. */ + if(!overlap(positionY, positionHeight, maxYForLine, maxHeightForLine)) + { + writeLine(normalize(line,isRtlDominant,hasRtl),isRtlDominant); + line.clear(); + + lastLineStartPosition = handleLineSeparation(current, lastPosition, lastLineStartPosition, maxHeightForLine); + + endOfLastTextX = ENDOFLASTTEXTX_RESET_VALUE; + expectedStartOfNextWordX = EXPECTEDSTARTOFNEXTWORDX_RESET_VALUE; + maxYForLine = MAXYFORLINE_RESET_VALUE; + maxHeightForLine = MAXHEIGHTFORLINE_RESET_VALUE; + minYTopForLine = MINYTOPFORLINE_RESET_VALUE; + } + + //Test if our TextPosition starts after a new word would be expected to start. + if (expectedStartOfNextWordX != EXPECTEDSTARTOFNEXTWORDX_RESET_VALUE && expectedStartOfNextWordX < positionX && + //only bother adding a space if the last character was not a space + lastPosition.getTextPosition().getCharacter() != null && + !lastPosition.getTextPosition().getCharacter().endsWith( " " ) ) + { + line.add(WordSeparator.getSeparator()); + } + } + + if (positionY >= maxYForLine) + { + maxYForLine = positionY; + } + + // 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 = positionX + positionWidth; + + // add it to the list + if (characterValue != null) + { + if(startOfPage && lastPosition==null){ + writeParagraphStart();//not sure this is correct for RTL? + } + line.add(position); + } + maxHeightForLine = Math.max( maxHeightForLine, positionHeight ); + minYTopForLine = Math.min(minYTopForLine,positionY - positionHeight); + lastPosition = current; + if(startOfPage){ + lastPosition.setParagraphStart(); + lastPosition.setLineStart(); + lastLineStartPosition = lastPosition; + startOfPage=false; + } + lastWordSpacing = wordSpacing; + previousAveCharWidth = averageCharWidth; + } + + // print the final line + if (line.size() > 0) + { + writeLine(normalize(line,isRtlDominant,hasRtl),isRtlDominant); + writeParagraphEnd(); + } + + endArticle(); + } + writePageEnd(); + } + + private boolean overlap( float y1, float height1, float y2, float height2 ) + { + return within( y1, y2, .1f) || (y2 <= y1 && y2 >= y1-height1) || + (y1 <= y2 && y1 >= y2-height2); + } + + /** + * Write the page separator value to the output stream. + * @throws IOException + * If there is a problem writing out the pageseparator to the document. + */ + protected void writePageSeperator() throws IOException + { + // RDD - newline at end of flush - required for end of page (so that the top + // of the next page starts on its own line. + // + output.write(getPageSeparator()); + output.flush(); + } + + /** + * Write the line separator value to the output stream. + * @throws IOException + * If there is a problem writing out the lineseparator to the document. + */ + protected void writeLineSeparator( ) throws IOException + { + output.write(getLineSeparator()); + } + + + /** + * Write the word separator value to the output stream. + * @throws IOException + * If there is a problem writing out the wordseparator to the document. + */ + protected void writeWordSeparator() throws IOException + { + output.write(getWordSeparator()); + } + + /** + * Write the string in TextPosition 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() ); + } + + /** + * Write a Java 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 writeString( String text ) throws IOException + { + output.write( text ); + } + + /** + * 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 process a TextPosition object and add the + * text to the list of characters on a page. It takes care of + * overlapping text. + * + * @param text The text to process. + */ + protected void processTextPosition( 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 textList = (List) charactersByArticle.get( articleDivisionIndex ); + + /* In the wild, some PDF encoded documents put diacritics (accents on + * top of characters) into a separate Tj element. When displaying them + * graphically, the two chunks get overlayed. With text output though, + * we need to do the overlay. This code recombines the diacritic with + * its associated character if the two are consecutive. + */ + if(textList.isEmpty()) + { + textList.add(text); + } + else + { + /* test if we overlap the previous entry. + * Note that we are making an assumption that we need to only look back + * one TextPosition to find what we are overlapping. + * This may not always be true. */ + TextPosition previousTextPosition = (TextPosition)textList.get(textList.size()-1); + if(text.isDiacritic() && previousTextPosition.contains(text)) + { + previousTextPosition.mergeDiacritic(text, normalize); + } + /* If the previous TextPosition was the diacritic, merge it into this + * one and remove it from the list. */ + else if(previousTextPosition.isDiacritic() && text.contains(previousTextPosition)) + { + text.mergeDiacritic(previousTextPosition, normalize); + textList.remove(textList.size()-1); + textList.add(text); + } + else + { + 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 getSuppressDuplicateOverlappingText() + { + 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 Vector> 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 getSeparateByBeads() + { + 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 add some more text formatting. + * @return true if some more text formatting will be added + */ + public boolean getAddMoreFormatting() + { + return addMoreFormatting; + } + + /** + * There will some additional text formatting be added if addMoreFormatting + * is set to true. Default is false. + * @param newAddMoreFormatting Tell PDFBox to add some more text formatting + */ + public void setAddMoreFormatting(boolean newAddMoreFormatting) + { + addMoreFormatting = newAddMoreFormatting; + } + + /** + * 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 getSortByPosition() + { + 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.
    + * The default is to not sort by position.
    + *
    + * A PDF writer could choose to write each character in a different order. By + * default PDFBox does not 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; + } + + /** + * Get the current space width-based tolerance value that is being used + * to estimate where spaces in text should be added. Note that the + * default value for this has been determined from trial and error. + * + * @return The current tolerance / scaling factor + */ + public float getSpacingTolerance() + { + return spacingTolerance; + } + + /** + * Set the space width-based tolerance value that is used + * to estimate where spaces in text should be added. Note that the + * default value for this has been determined from trial and error. + * Setting this value larger will reduce the number of spaces added. + * + * @param spacingToleranceValue tolerance / scaling factor to use + */ + public void setSpacingTolerance(float spacingToleranceValue) + { + this.spacingTolerance = spacingToleranceValue; + } + + /** + * Get the current character width-based tolerance value that is being used + * to estimate where spaces in text should be added. Note that the + * default value for this has been determined from trial and error. + * + * @return The current tolerance / scaling factor + */ + public float getAverageCharTolerance() + { + return averageCharTolerance; + } + + /** + * Set the character width-based tolerance value that is used + * to estimate where spaces in text should be added. Note that the + * default value for this has been determined from trial and error. + * Setting this value larger will reduce the number of spaces added. + * + * @param averageCharToleranceValue average tolerance / scaling factor to use + */ + public void setAverageCharTolerance(float averageCharToleranceValue) + { + this.averageCharTolerance = averageCharToleranceValue; + } + + + /** + * returns the multiple of whitespace character widths + * for the current text which the current + * line start can be indented from the previous line start + * beyond which the current line start is considered + * to be a paragraph start. + * @return the number of whitespace character widths to use + * when detecting paragraph indents. + */ + public float getIndentThreshold() + { + return indentThreshold; + } + + /** + * sets the multiple of whitespace character widths + * for the current text which the current + * line start can be indented from the previous line start + * beyond which the current line start is considered + * to be a paragraph start. The default value is 2.0. + * + * @param indentThreshold the number of whitespace character widths to use + * when detecting paragraph indents. + */ + public void setIndentThreshold(float indentThreshold) + { + this.indentThreshold = indentThreshold; + } + + /** + * the minimum whitespace, as a multiple + * of the max height of the current characters + * beyond which the current line start is considered + * to be a paragraph start. + * @return the character height multiple for + * max allowed whitespace between lines in + * the same paragraph. + */ + public float getDropThreshold() + { + return dropThreshold; + } + + /** + * sets the minimum whitespace, as a multiple + * of the max height of the current characters + * beyond which the current line start is considered + * to be a paragraph start. The default value is 2.5. + * + * @param dropThreshold the character height multiple for + * max allowed whitespace between lines in + * the same paragraph. + */ + public void setDropThreshold(float dropThreshold) + { + this.dropThreshold = dropThreshold; + } + + /** + * Returns the string which will be used at the beginning of a paragraph. + * @return the paragraph start string + */ + public String getParagraphStart() + { + return paragraphStart; + } + + /** + * Sets the string which will be used at the beginning of a paragraph. + * @param s the paragraph start string + */ + public void setParagraphStart(String s) + { + this.paragraphStart = s; + } + + /** + * Returns the string which will be used at the end of a paragraph. + * @return the paragraph end string + */ + public String getParagraphEnd() + { + return paragraphEnd; + } + + /** + * Sets the string which will be used at the end of a paragraph. + * @param s the paragraph end string + */ + public void setParagraphEnd(String s) + { + this.paragraphEnd = s; + } + + + /** + * Returns the string which will be used at the beginning of a page. + * @return the page start string + */ + public String getPageStart() + { + return pageStart; + } + + /** + * Sets the string which will be used at the beginning of a page. + * @param s the page start string + */ + public void setPageStart(String pageStart) + { + this.pageStart = pageStart; + } + + /** + * Returns the string which will be used at the end of a page. + * @return the page end string + */ + public String getPageEnd() + { + return pageEnd; + } + + /** + * Sets the string which will be used at the end of a page. + * @param s the page end string + */ + public void setPageEnd(String pageEnd) + { + this.pageEnd = pageEnd; + } + + /** + * Returns the string which will be used at the beginning of an article. + * @return the article start string + */ + public String getArticleStart() { + return articleStart; + } + + /** + * Sets the string which will be used at the beginning of an article. + * @param s the article start string + */ + public void setArticleStart(String articleStart) { + this.articleStart = articleStart; + } + + /** + * Returns the string which will be used at the end of an article. + * @return the article end string + */ + public String getArticleEnd(){ + return articleEnd; + } + + /** + * Sets the string which will be used at the end of an article. + * @param s the article end string + */ + public void setArticleEnd(String articleEnd){ + this.articleEnd = articleEnd; + } + + + /** + * Reverse characters of a compound Arabic glyph. + * When getSortByPosition() is true, inspect the sequence encoded + * by one glyph. If the glyph encodes two or more Arabic characters, + * reverse these characters from a logical order to a visual order. + * This ensures that the bidirectional algorithm that runs later will + * convert them back to a logical order. + * + * @param str a string obtained from font.encoding() + */ + public String inspectFontEncoding(String str) + { + if (!sortByPosition || str == null || str.length() < 2) + return str; + + for (int i = 0; i < str.length(); ++i) + { + if (Character.getDirectionality(str.charAt(i)) + != Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + return str; + } + + StringBuilder reversed = new StringBuilder(str.length()); + for (int i = str.length() - 1; i >= 0; --i) + { + reversed.append(str.charAt(i)); + } + return reversed.toString(); + } + + /** + * handles the line separator for a new line given + * the specified current and previous TextPositions. + * @param position the current text position + * @param lastPosition the previous text position + * @param lastLineStartPosition the last text position that followed a line + * separator. + * @param maxHeightForLine max height for positions since lastLineStartPosition + * @throws IOException + */ + protected PositionWrapper handleLineSeparation(PositionWrapper current, + PositionWrapper lastPosition, PositionWrapper lastLineStartPosition, float maxHeightForLine) + throws IOException { + current.setLineStart(); + isParagraphSeparation(current, lastPosition, lastLineStartPosition, maxHeightForLine); + lastLineStartPosition = current; + if (current.isParagraphStart()) { + if(lastPosition.isArticleStart()) { + writeParagraphStart(); + } else { + writeLineSeparator(); + writeParagraphSeparator(); + } + } else { + writeLineSeparator(); + } + return lastLineStartPosition; + } + + /** + * tests the relationship between the last text position, the current text + * position and the last text position that followed a line separator to + * decide if the gap represents a paragraph separation. This should + * only be called for consecutive text positions that first pass the + * line separation test. + *

    + * This base implementation tests to see if the lastLineStartPosition is + * null OR if the current vertical position has dropped below the last text + * vertical position by at least 2.5 times the current text height OR if the + * current horizontal position is indented by at least 2 times the current + * width of a space character.

    + *

    + * This also attempts to identify text that is indented under a hanging indent.

    + *

    + * This method sets the isParagraphStart and isHangingIndent flags on the current + * position object.

    + * + * @param position the current text position. This may have its isParagraphStart + * or isHangingIndent flags set upon return. + * @param lastPosition the previous text position (should not be null). + * @param lastLineStartPosition the last text position that followed a line + * separator. May be null. + * @param maxHeightForLine max height for text positions since lasLineStartPosition. + */ + protected void isParagraphSeparation(PositionWrapper position, + PositionWrapper lastPosition, PositionWrapper lastLineStartPosition, float maxHeightForLine){ + boolean result = false; + if(lastLineStartPosition == null) { + result = true; + }else{ + float yGap = Math.abs(position.getTextPosition().getYDirAdj()- + lastPosition.getTextPosition().getYDirAdj()); + float xGap = (position.getTextPosition().getXDirAdj()- + lastLineStartPosition.getTextPosition().getXDirAdj());//do we need to flip this for rtl? + if(yGap > (getDropThreshold()*maxHeightForLine)){ + result = true; + }else if(xGap > (getIndentThreshold()*position.getTextPosition().getWidthOfSpace())){ + //text is indented, but try to screen for hanging indent + if(!lastLineStartPosition.isParagraphStart()){ + result = true; + }else{ + position.setHangingIndent(); + } + }else if(xGap < -position.getTextPosition().getWidthOfSpace()){ + //text is left of previous line. Was it a hanging indent? + if(!lastLineStartPosition.isParagraphStart()){ + result = true; + } + }else if(Math.abs(xGap) < (0.25 * position.getTextPosition().getWidth())){ + //current horizontal position is within 1/4 a char of the last + //linestart. We'll treat them as lined up. + if(lastLineStartPosition.isHangingIndent()){ + position.setHangingIndent(); + }else if(lastLineStartPosition.isParagraphStart()){ + //check to see if the previous line looks like + //any of a number of standard list item formats + Pattern liPattern = matchListItemPattern(lastLineStartPosition); + if(liPattern!=null){ + Pattern currentPattern = matchListItemPattern(position); + if(liPattern == currentPattern){ + result = true; + } + } + } + } + } + if(result){ + position.setParagraphStart(); + } + } + + /** + * writes the paragraph separator string to the output. + * @throws IOException + */ + protected void writeParagraphSeparator()throws IOException{ + writeParagraphEnd(); + writeParagraphStart(); + } + + /** + * Write something (if defined) at the start of a paragraph. + * @throws IOException if something went wrong + */ + protected void writeParagraphStart() throws IOException{ + output.write(getParagraphStart()); + } + + /** + * Write something (if defined) at the end of a paragraph. + * @throws IOException if something went wrong + */ + protected void writeParagraphEnd() throws IOException{ + output.write(getParagraphEnd()); + } + + /** + * Write something (if defined) at the start of a page. + * @throws IOException if something went wrong + */ + protected void writePageStart()throws IOException{ + output.write(getPageStart()); + } + + /** + * Write something (if defined) at the end of a page. + * @throws IOException if something went wrong + */ + protected void writePageEnd()throws IOException{ + output.write(getPageEnd()); + } + + /** + * returns the list item Pattern object that matches + * the text at the specified PositionWrapper or null + * if the text does not match such a pattern. The list + * of Patterns tested against is given by the + * {@link #getListItemPatterns()} method. To add to + * the list, simply override that method (if sub-classing) + * or explicitly supply your own list using + * {@link #setListItemPatterns(List)}. + * @param pw + * @return + */ + protected Pattern matchListItemPattern(PositionWrapper pw) { + TextPosition tp = pw.getTextPosition(); + String txt = tp.getCharacter(); + Pattern p = matchPattern(txt,getListItemPatterns()); + return p; + } + + /** + * a list of regular expressions that match commonly used + * list item formats, i.e. bullets, numbers, letters, + * Roman numerals, etc. Not meant to be + * comprehensive. + */ + private static final String[] LIST_ITEM_EXPRESSIONS = { + "\\.", + "\\d+\\.", + "\\[\\d+\\]", + "\\d+\\)", + "[A-Z]\\.", + "[a-z]\\.", + "[A-Z]\\)", + "[a-z]\\)", + "[IVXL]+\\.", + "[ivxl]+\\.", + + }; + + private List liPatterns = null; + /** + * use to supply a different set of regular expression + * patterns for matching list item starts. + * + * @param patterns + */ + protected void setListItemPatterns(List patterns){ + liPatterns = patterns; + } + + + /** + * returns a list of regular expression Patterns representing + * different common list item formats. For example + * numbered items of form: + *
      + *
    1. some text
    2. + *
    3. more text
    4. + *
    + * or + *
      + *
    • some text
    • + *
    • more text
    • + *
    + * etc., all begin with some character pattern. The pattern "\\d+\." (matches "1.", "2.", ...) + * or "\[\\d+\]" (matches "[1]", "[2]", ...). + *

    + * This method returns a list of such regular expression Patterns. + * @return a list of Pattern objects. + */ + protected List getListItemPatterns(){ + if(liPatterns == null){ + liPatterns = new ArrayList(); + for(String expression : LIST_ITEM_EXPRESSIONS){ + Pattern p = Pattern.compile(expression); + liPatterns.add(p); + } + } + return liPatterns; + } + + /** + * iterates over the specified list of Patterns until + * it finds one that matches the specified string. Then + * returns the Pattern. + *

    + * Order of the supplied list of patterns is important as + * most common patterns should come first. Patterns + * should be strict in general, and all will be + * used with case sensitivity on. + *

    + * @param s + * @param patterns + * @return + */ + protected static final Pattern matchPattern(String s, List patterns){ + Pattern matchedPattern = null; + for(Pattern p : patterns){ + if(p.matcher(s).matches()){ + return p; + } + } + return matchedPattern; + } + + /** + * Write a list of string containing a whole line of a document. + * @param line a list with the words of the given line + * @param isRtlDominant determines if rtl or ltl is dominant + * @throws IOException if something went wrong + */ + private void writeLine(List line, boolean isRtlDominant)throws IOException{ + int numberOfStrings = line.size(); + if (isRtlDominant) { + for(int i=numberOfStrings-1; i>=0; i--){ + if (i < numberOfStrings-1) + writeWordSeparator(); + writeString(line.get(i)); + } + } + else { + for(int i=0; i normalize(List line, boolean isRtlDominant, boolean hasRtl){ + LinkedList normalized = new LinkedList(); + StringBuilder lineBuilder = new StringBuilder(); + for(TextPosition text : line){ + if (text instanceof WordSeparator) { + String lineStr = lineBuilder.toString(); + if (hasRtl) { + lineStr = normalize.makeLineLogicalOrder(lineStr,isRtlDominant); + } + lineStr = normalize.normalizePres(lineStr); + normalized.add(lineStr); + lineBuilder = new StringBuilder(); + } + else { + lineBuilder.append(text.getCharacter()); + } + } + if (lineBuilder.length() > 0) { + String lineStr = lineBuilder.toString(); + if (hasRtl) { + lineStr = normalize.makeLineLogicalOrder(lineStr,isRtlDominant); + } + lineStr = normalize.normalizePres(lineStr); + normalized.add(lineStr); + } + return normalized; + } + + /** + * internal marker class. Used as a place holder in + * a line of TextPositions. + * @author ME21969 + * + */ + private static final class WordSeparator extends TextPosition{ + private static final WordSeparator separator = new WordSeparator(); + + private WordSeparator(){ + } + + public static final WordSeparator getSeparator(){ + return separator; + } + + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripperByArea.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripperByArea.java new file mode 100644 index 000000000..409590e41 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PDFTextStripperByArea.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.awt.geom.Rectangle2D; +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.Properties; +import java.util.Vector; + +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDStream; + +/** + * This will extract text from a specified region in the PDF. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +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(); + setPageSeparator( "" ); + } + + + /** + * Instantiate a new PDFTextStripperArea object. Loading all of the operator + * mappings from the properties object that is passed in. Does not convert + * the text to more encoding-specific output. + * + * @param props + * The properties containing the mapping of operators to + * PDFOperator classes. + * + * @throws IOException + * If there is an error reading the properties. + */ + public PDFTextStripperByArea(Properties props) throws IOException + { + super(props); + setPageSeparator(""); + } + + /** + * Instantiate a new PDFTextStripperArea object. This object will load + * properties from PDFTextStripper.properties and will apply + * encoding-specific conversions to the output text. + * + * @param encoding + * The encoding that the output will be written in. + * @throws IOException + * If there is an error reading the properties. + */ + public PDFTextStripperByArea(String encoding) throws IOException + { + super(encoding); + setPageSeparator(""); + } + + /** + * 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, Rectangle2D 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 = 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() ) + { + setStartPage(getCurrentPageNo()); + setEndPage(getCurrentPageNo()); + //reset the stored text for the region so this class + //can be reused. + String regionName = 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 ); + } + } + + + /** + * {@inheritDoc} + */ + protected void processTextPosition( TextPosition text ) + { + Iterator regionIter = regionArea.keySet().iterator(); + while( regionIter.hasNext() ) + { + String region = regionIter.next(); + Rectangle2D rect = regionArea.get( region ); + if( rect.contains( text.getX(), text.getY() ) ) + { + charactersByArticle = (Vector)regionCharacterList.get( region ); + super.processTextPosition( text ); + } + } + } + + + /** + * This will print the processed page text to the output stream. + * + * @throws IOException If there is an error writing the text. + */ + protected void writePage() throws IOException + { + Iterator regionIter = regionArea.keySet().iterator(); + while( regionIter.hasNext() ) + { + String region = regionIter.next(); + charactersByArticle = (Vector)regionCharacterList.get( region ); + output = regionText.get( region ); + super.writePage(); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PageExtractor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PageExtractor.java new file mode 100644 index 000000000..9d759550d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PageExtractor.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pdfbox.util; + +import java.io.IOException; +import java.util.List; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; + +/** + * This class will extract one or more sequential pages and create a new document. + * @author Adam Nichols (adam@apache.org) + */ +public class PageExtractor { + protected PDDocument sourceDocument; + protected int startPage = 1; // first page to extract is page 1 (by default) + protected int endPage = 0; + + /** + * Creates a new instance of PageExtractor + * @param document The document to split. + */ + public PageExtractor(PDDocument sourceDocument) { + this.sourceDocument = sourceDocument; + endPage = sourceDocument.getNumberOfPages(); + } + + /** + * Creates a new instance of PageExtractor + * @param document The document to split. + * @param startPage The first page you want extracted (inclusive) + * @param endPage The last page you want extracted (inclusive) + */ + public PageExtractor(PDDocument sourceDocument, int startPage, int endPage) { + this(sourceDocument); + this.startPage = startPage; + this.endPage = endPage; + } + + /** + * This will take a document and extract the desired pages into a new + * document. Both startPage and endPage are included in the extracted + * document. If the endPage is greater than the number of pages in the + * source document, it will go to the end of the document. If startPage is + * less than 1, it'll start with page 1. If startPage is greater than + * endPage or greater than the number of pages in the source document, a + * blank document will be returned. + * + * @return The extracted document + * @throws IOException If there is an IOError + */ + public PDDocument extract() throws IOException { + PDDocument extractedDocument = new PDDocument(); + extractedDocument.setDocumentInformation(sourceDocument.getDocumentInformation()); + extractedDocument.getDocumentCatalog().setViewerPreferences( + sourceDocument.getDocumentCatalog().getViewerPreferences()); + + List pages = (List)sourceDocument.getDocumentCatalog().getAllPages(); + int pageCounter = 1; + for(PDPage page : pages) { + if(pageCounter >= startPage && pageCounter <= endPage) { + PDPage imported = extractedDocument.importPage(page); + imported.setCropBox(page.findCropBox()); + imported.setMediaBox(page.findMediaBox()); + imported.setResources(page.findResources()); + imported.setRotation(page.findRotation()); + } + pageCounter++; + } + + return extractedDocument; + } + + /** + * Gets the first page number to be extracted. + * @return the first page number which should be extracted + */ + public int getStartPage() { + return startPage; + } + + /** + * Sets the first page number to be extracted. + * @param startPage the first page number which should be extracted + */ + public void setStartPage(int startPage) { + this.startPage = startPage; + } + + /** + * Gets the last page number (inclusive) to be extracted. + * @return the last page number which should be extracted + */ + public int getEndPage() { + return endPage; + } + + /** + * Sets the last page number to be extracted. + * @param endPage the last page number which should be extracted + */ + public void setEndPage(int endPage) { + this.endPage = endPage; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/PositionWrapper.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PositionWrapper.java new file mode 100644 index 000000000..25dda9d10 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/PositionWrapper.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + + +/** + * wrapper of TextPosition that adds flags to track + * status as linestart and paragraph start positions. + *

    + * This is implemented as a wrapper since the TextPosition + * class doesn't provide complete access to its + * state fields to subclasses. Also, conceptually TextPosition is + * immutable while these flags need to be set post-creation so + * it makes sense to put these flags in this separate class. + *

    + * @author m.martinez@ll.mit.edu + * + */ +public class PositionWrapper +{ + + private boolean isLineStart = false; + private boolean isParagraphStart = false; + private boolean isPageBreak = false; + private boolean isHangingIndent = false; + private boolean isArticleStart = false; + + private TextPosition position = null; + + /** + * Returns the underlying TextPosition object. + * @return the text position + */ + protected TextPosition getTextPosition() + { + return position; + } + + + public boolean isLineStart() + { + return isLineStart; + } + + + /** + * Sets the isLineStart() flag to true. + */ + public void setLineStart() + { + this.isLineStart = true; + } + + + public boolean isParagraphStart() + { + return isParagraphStart; + } + + + /** + * sets the isParagraphStart() flag to true. + */ + public void setParagraphStart() + { + this.isParagraphStart = true; + } + + + public boolean isArticleStart() + { + return isArticleStart; + } + + + /** + * Sets the isArticleStart() flag to true. + */ + public void setArticleStart() + { + this.isArticleStart = true; + } + + + public boolean isPageBreak() + { + return isPageBreak; + } + + + /** + * Sets the isPageBreak() flag to true. + */ + public void setPageBreak() + { + this.isPageBreak = true; + } + + + public boolean isHangingIndent() + { + return isHangingIndent; + } + + + /** + * Sets the isHangingIndent() flag to true. + */ + public void setHangingIndent() + { + this.isHangingIndent = true; + } + + + /** + * Constructs a PositionWrapper around the specified TextPosition object. + * @param position the text position + */ + public PositionWrapper(TextPosition position) + { + this.position = position; + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/ResourceLoader.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/ResourceLoader.java new file mode 100644 index 000000000..f1dbf746f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/ResourceLoader.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.util.Properties; + +/** + * This class will handle loading resource files(AFM/CMAP). + * + * @author Ben Litchfield + * @version $Revision: 1.9 $ + */ +public class ResourceLoader +{ + + /** + * 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 + { + 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. + * @param failIfNotFound Throw an error message if the properties were not found. + * + * @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, boolean failIfNotFound ) throws IOException + { + Properties properties = null; + InputStream is = null; + try + { + is = loadResource( resourceName ); + if( is != null ) + { + properties = new Properties(); + properties.load( is ); + } + else + { + if( failIfNotFound ) + { + throw new IOException( "Error: could not find resource '" + resourceName + "' on classpath." ); + } + } + } + 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; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/Splitter.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/Splitter.java new file mode 100644 index 000000000..c1b2b86c8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/Splitter.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.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 + * @version $Revision: 1.7 $ + */ +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. + * + * protected void createNewDocumentIfNecessary() + * { + * if( isPrime( pageNumber ) ) + * { + * super.createNewDocumentIfNecessary(); + * } + * } + * + * + * @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() ); + // only the resources of the page will be copied + imported.setResources( page.getResources() ); + imported.setRotation( page.findRotation() ); + pageNumber++; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/StringUtil.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/StringUtil.java new file mode 100644 index 000000000..a781c56ca --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/StringUtil.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.io.UnsupportedEncodingException; + +public class StringUtil +{ + /** + * Converts a string to it ISO-8859-1 byte sequence + * + * It is an workaround for variable initialisations outside of functions. + */ + public static byte[] getBytes(String s) + { + try + { + return s.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e) + { + throw new RuntimeException("Unsupported Encoding", e); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextNormalize.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextNormalize.java new file mode 100644 index 000000000..661198d2a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextNormalize.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.util.HashMap; + +/** + * This class allows a caller to normalize text in various ways. + * It will load the ICU4J jar file if it is defined on the classpath. + * + * @author Brian Carrier + * @version $Revision: 1.0 $ + */ +public class TextNormalize +{ + private ICU4JImpl icu4j = null; + private static final HashMap DIACHASH = new HashMap(); + private String outputEncoding; + + /** + * + * @param encoding The Encoding that the text will eventually be written as (or null) + */ + public TextNormalize(String encoding) + { + findICU4J(); + populateDiacHash(); + this.outputEncoding = encoding; + } + + private void findICU4J() + { + // see if we can load the icu4j classes from the classpath + try + { + this.getClass().getClassLoader().loadClass("com.ibm.icu.text.Bidi"); + this.getClass().getClassLoader().loadClass("com.ibm.icu.text.Normalizer"); + icu4j = new ICU4JImpl(); + } + catch (ClassNotFoundException e) + { + icu4j = null; + } + } + /* + * Adds non-decomposing diacritics to the hash with their related + * combining character. These are values that the unicode spec claims + * are equivalent but are not mapped in the form NFKC normalization method. + * Determined by going through the Combining Diacritical Marks section of + * the Unicode spec and identifying which characters are not mapped to by + * the normalization. + */ + private void populateDiacHash() + { + DIACHASH.put(new Integer(0x0060), "\u0300"); + DIACHASH.put(new Integer(0x02CB), "\u0300"); + DIACHASH.put(new Integer(0x0027), "\u0301"); + DIACHASH.put(new Integer(0x02B9), "\u0301"); + DIACHASH.put(new Integer(0x02CA), "\u0301"); + DIACHASH.put(new Integer(0x005e), "\u0302"); + DIACHASH.put(new Integer(0x02C6), "\u0302"); + DIACHASH.put(new Integer(0x007E), "\u0303"); + DIACHASH.put(new Integer(0x02C9), "\u0304"); + DIACHASH.put(new Integer(0x00B0), "\u030A"); + DIACHASH.put(new Integer(0x02BA), "\u030B"); + DIACHASH.put(new Integer(0x02C7), "\u030C"); + DIACHASH.put(new Integer(0x02C8), "\u030D"); + DIACHASH.put(new Integer(0x0022), "\u030E"); + DIACHASH.put(new Integer(0x02BB), "\u0312"); + DIACHASH.put(new Integer(0x02BC), "\u0313"); + DIACHASH.put(new Integer(0x0486), "\u0313"); + DIACHASH.put(new Integer(0x055A), "\u0313"); + DIACHASH.put(new Integer(0x02BD), "\u0314"); + DIACHASH.put(new Integer(0x0485), "\u0314"); + DIACHASH.put(new Integer(0x0559), "\u0314"); + DIACHASH.put(new Integer(0x02D4), "\u031D"); + DIACHASH.put(new Integer(0x02D5), "\u031E"); + DIACHASH.put(new Integer(0x02D6), "\u031F"); + DIACHASH.put(new Integer(0x02D7), "\u0320"); + DIACHASH.put(new Integer(0x02B2), "\u0321"); + DIACHASH.put(new Integer(0x02CC), "\u0329"); + DIACHASH.put(new Integer(0x02B7), "\u032B"); + DIACHASH.put(new Integer(0x02CD), "\u0331"); + DIACHASH.put(new Integer(0x005F), "\u0332"); + DIACHASH.put(new Integer(0x204E), "\u0359"); + } + + /** + * Takes a line of text in presentation order and converts it to logical order. + * For most text other than Arabic and Hebrew, the presentation and logical + * orders are the same. However, for Arabic and Hebrew, they are different and + * if the text involves both RTL and LTR text then the Unicode BIDI algorithm + * must be used to determine how to map between them. + * + * @param str Presentation form of line to convert (i.e. left most char is first char) + * @param isRtlDominant true if the PAGE has a dominant right to left ordering + * @return Logical form of string (or original string if ICU4J library is not on classpath) + */ + public String makeLineLogicalOrder(String str, boolean isRtlDominant) + { + if (icu4j != null) + { + return icu4j.makeLineLogicalOrder(str, isRtlDominant); + } + else + { + return str; + } + } + + /** + * Normalize the presentation forms of characters in the string. + * For example, convert the single "fi" ligature to "f" and "i". + * + * @param str String to normalize + * @return Normalized string (or original string if ICU4J library is not on classpath) + */ + public String normalizePres(String str) + { + if (icu4j != null) + { + return icu4j.normalizePres(str); + } + else + { + return str; + } + } + + /** + * Normalize the diacritic, for example, + * convert non-combining diacritic characters to their combining + * counterparts. + * + * @param str String to normalize + * @return Normalized string (or original string if ICU4J library is not on classpath) + */ + public String normalizeDiac(String str) + { + /* + * Unicode contains special combining forms of the diacritic characters + * and we want to use these. + */ + if(outputEncoding != null && outputEncoding.toUpperCase().startsWith("UTF")) + { + Integer c = new Integer(str.charAt(0)); + // convert the characters not defined in the Unicode spec + if(DIACHASH.containsKey(c)) + { + return (String)DIACHASH.get(c); + } + else if (icu4j != null) + { + return icu4j.normalizeDiac(str); + } + else + { + return str; + } + } + else + { + return str; + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPosition.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPosition.java new file mode 100644 index 000000000..feceb2424 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPosition.java @@ -0,0 +1,747 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.font.PDFont; + +/** + * This represents a string and a position on the screen of those characters. + * + * @author Ben Litchfield + * @version $Revision: 1.12 $ + */ +public class TextPosition +{ + /* TextMatrix for the start of the text object. Coordinates + * are in display units and have not been adjusted. */ + private Matrix textPos; + + // ending X and Y coordinates in display units + private float endX; + private float endY; + + private float maxTextHeight; // maximum height of text, in display units + private int rot; // 0, 90, 180, 270 degrees of page rotation + private float x = Float.NEGATIVE_INFINITY; + private float y = Float.NEGATIVE_INFINITY; + private float pageHeight; + private float pageWidth; + private float[] widths; + private float widthOfSpace; // width of a space, in display units + private String str; + private PDFont font; + private float fontSize; + private int fontSizePt; + // TODO remove unused value + private float wordSpacing; // word spacing value, in display units + + /** + * Constructor. + */ + protected TextPosition() + { + + } + + /** + * Constructor. + * + * @param page Page that the text is located in + * @param textPositionSt TextMatrix for start of text (in display units) + * @param textPositionEnd TextMatrix for end of text (in display units) + * @param maxFontH Maximum height of text (in display units) + * @param individualWidths The width of each individual character. (in ? units) + * @param spaceWidth The width of the space character. (in display units) + * @param string The character to be displayed. + * @param currentFont The current for for this text position. + * @param fontSizeValue The new font size. + * @param fontSizeInPt The font size in pt units. + * @param ws The word spacing parameter (in display units) + */ + public TextPosition( + PDPage page, + Matrix textPositionSt, + Matrix textPositionEnd, + float maxFontH, + float[] individualWidths, + float spaceWidth, + String string, + PDFont currentFont, + float fontSizeValue, + int fontSizeInPt, + float ws + ) + { + this.textPos = textPositionSt; + + this.endX = textPositionEnd.getXPosition(); + this.endY = textPositionEnd.getYPosition(); + + this.rot = page.findRotation(); + // make sure it is 0 to 270 and no negative numbers + if(this.rot < 0) + { + rot += 360; + } + + this.maxTextHeight = maxFontH; + this.pageHeight = page.findMediaBox().getHeight(); + this.pageWidth = page.findMediaBox().getWidth(); + + this.widths = individualWidths; + this.widthOfSpace = spaceWidth; + this.str = string; + this.font = currentFont; + this.fontSize = fontSizeValue; + this.fontSizePt = fontSizeInPt; + this.wordSpacing = ws; + } + + /** + * Constructor. + * + * @param pageRotation rotation of the page that the text is located in + * @param pageWidth rotation of the page that the text is located in + * @param pageHeight rotation of the page that the text is located in + * @param textPositionSt TextMatrix for start of text (in display units) + * @param textPositionEnd TextMatrix for end of text (in display units) + * @param maxFontH Maximum height of text (in display units) + * @param individualWidth The width of the given character/string. (in ? units) + * @param spaceWidth The width of the space character. (in display units) + * @param string The character to be displayed. + * @param currentFont The current for for this text position. + * @param fontSizeValue The new font size. + * @param fontSizeInPt The font size in pt units. + * + * @deprecated Use {@link TextPosition(int, float, float, Matrix, float, float, float, float, float, String, PDFont, float, int)} instead. + */ + public TextPosition( + int pageRotation, + float pageWidth, + float pageHeight, + Matrix textPositionSt, + Matrix textPositionEnd, + float maxFontH, + float individualWidth, + float spaceWidth, + String string, + PDFont currentFont, + float fontSizeValue, + int fontSizeInPt + ) + { + this(pageRotation, pageWidth, pageHeight, textPositionSt, + textPositionEnd.getXPosition(), textPositionEnd.getYPosition(), + maxFontH, individualWidth, spaceWidth, string, currentFont, fontSizeValue, fontSizeInPt); + } + + /** + * Constructor. + * + * @param pageRotation rotation of the page that the text is located in + * @param pageWidth rotation of the page that the text is located in + * @param pageHeight rotation of the page that the text is located in + * @param textPositionSt TextMatrix for start of text (in display units) + * @param endX + * @param endY + * @param maxFontH Maximum height of text (in display units) + * @param individualWidth The width of the given character/string. (in ? units) + * @param spaceWidth The width of the space character. (in display units) + * @param string The character to be displayed. + * @param currentFont The current for for this text position. + * @param fontSizeValue The new font size. + * @param fontSizeInPt The font size in pt units. + */ + public TextPosition( + int pageRotation, + float pageWidth, + float pageHeight, + Matrix textPositionSt, + float endX, + float endY, + float maxFontH, + float individualWidth, + float spaceWidth, + String string, + PDFont currentFont, + float fontSizeValue, + int fontSizeInPt + ) + { + this.textPos = textPositionSt; + + this.endX = endX; + this.endY = endY; + + this.rot = pageRotation; + // make sure it is 0 to 270 and no negative numbers + if(this.rot < 0) + { + rot += 360; + } + + this.maxTextHeight = maxFontH; + this.pageHeight = pageHeight; + this.pageWidth = pageWidth; + + this.widths = new float[]{individualWidth}; + this.widthOfSpace = spaceWidth; + this.str = string; + this.font = currentFont; + this.fontSize = fontSizeValue; + this.fontSizePt = fontSizeInPt; + } + + /** + * Return the string of characters stored in this object. + * + * @return The string on the screen. + */ + public String getCharacter() + { + return str; + } + + /** + * Return the Matrix textPos stored in this object. + * + * @return The Matrix containing all infos of the starting textposition + */ + public Matrix getTextPos() + { + return textPos; + } + + /** + * Return the direction/orientation of the string in this object + * based on its text matrix. + * @return The direction of the text (0, 90, 180, or 270) + */ + public float getDir() + { + float a = textPos.getValue(0,0); + float b = textPos.getValue(0,1); + float c = textPos.getValue(1,0); + float d = textPos.getValue(1,1); + + // 12 0 left to right + // 0 12 + if ((a > 0) && (Math.abs(b) < d) && (Math.abs(c) < a) && (d > 0)) + { + return 0; + } + // -12 0 right to left (upside down) + // 0 -12 + else if ((a < 0) && (Math.abs(b) < Math.abs(d)) && (Math.abs(c) < Math.abs(a)) && (d < 0)) + { + return 180; + } + // 0 12 up + // -12 0 + else if ((Math.abs(a) < Math.abs(c)) && (b > 0) && (c < 0) && (Math.abs(d) < b)) + { + return 90; + } + // 0 -12 down + // 12 0 + else if ((Math.abs(a) < c) && (b < 0) && (c > 0) && (Math.abs(d) < Math.abs(b))) + { + return 270; + } + return 0; + } + + /** + * Return the X starting coordinate of the text, adjusted by + * the given rotation amount. The rotation adjusts where the 0,0 + * location is relative to the text. + * + * @param rotation Rotation to apply (0, 90, 180, or 270). 0 will perform no adjustments. + * @return X coordinate + */ + private float getXRot(float rotation) + { + if (rotation == 0) + { + return textPos.getValue(2,0); + } + else if (rotation == 90) + { + return textPos.getValue(2,1); + } + else if (rotation == 180) + { + return pageWidth - textPos.getValue(2,0); + } + else if (rotation == 270) + { + return pageHeight - textPos.getValue(2,1); + } + return 0; + } + + /** + * This will get the page rotation adjusted x position of the character. + * This is adjusted based on page rotation so that the upper + * left is 0,0. + * + * @return The x coordinate of the character. + */ + public float getX() + { + if (x == Float.NEGATIVE_INFINITY) + { + x = getXRot(rot); + } + return x; + } + + /** + * This will get the text direction adjusted x position of the character. + * This is adjusted based on text direction so that the first character + * in that direction is in the upper left at 0,0. + * + * @return The x coordinate of the text. + */ + public float getXDirAdj() + { + return getXRot(getDir()); + } + + /** + * This will get the y position of the character with 0,0 in lower left. + * This will be adjusted by the given rotation. + * @param rotation Rotation to apply to text to adjust the 0,0 location (0,90,180,270) + * + * @return The y coordinate of the text + */ + private float getYLowerLeftRot(float rotation) + { + if (rotation == 0) + { + return textPos.getValue(2,1); + } + else if (rotation == 90) + { + return pageWidth - textPos.getValue(2,0); + } + else if (rotation == 180) + { + return pageHeight - textPos.getValue(2,1); + } + else if (rotation == 270) + { + return textPos.getValue(2,0); + } + return 0; + } + + /** + * This will get the y position of the text, adjusted so that 0,0 is upper left and + * it is adjusted based on the page rotation. + * + * @return The adjusted y coordinate of the character. + */ + public float getY() + { + if (y == Float.NEGATIVE_INFINITY) + { + if ((rot == 0) || (rot == 180)) + { + y = pageHeight - getYLowerLeftRot(rot); + } + else + { + y = pageWidth - getYLowerLeftRot(rot); + } + } + return y; + } + + /** + * This will get the y position of the text, adjusted so that 0,0 is upper left and + * it is adjusted based on the text direction. + * + * @return The adjusted y coordinate of the character. + */ + public float getYDirAdj() + { + float dir = getDir(); + // some PDFBox code assumes that the 0,0 point is in upper left, not lower left + if ((dir == 0) || (dir == 180)) + { + return pageHeight - getYLowerLeftRot(dir); + } + else + { + return pageWidth - getYLowerLeftRot(dir); + } + } + + + + /** + * Get the length or width of the text, based on a given rotation. + * + * @param rotation Rotation that was used to determine coordinates (0,90,180,270) + * @return Width of text in display units + */ + private float getWidthRot(float rotation) + { + if ((rotation == 90) || (rotation == 270)) + { + return Math.abs(endY - textPos.getYPosition()); + } + else + { + return Math.abs(endX - textPos.getXPosition()); + } + } + + /** + * This will get the width of the string when page rotation adjusted coordinates are used. + * + * @return The width of the text in display units. + */ + public float getWidth() + { + return getWidthRot(rot); + } + + /** + * This will get the width of the string when text direction adjusted coordinates are used. + * + * @return The width of the text in display units. + */ + public float getWidthDirAdj() + { + return getWidthRot(getDir()); + } + + /** + * This will get the maximum height of all characters in this string. + * + * @return The maximum height of all characters in this string. + */ + public float getHeight() + { + return maxTextHeight; + } + + /** + * This will get the maximum height of all characters in this string. + * + * @return The maximum height of all characters in this string. + */ + public float getHeightDir() + { + // this is not really a rotation-dependent calculation, but this is defined for symmetry. + return maxTextHeight; + } + + /** + * 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 size in pt. + * To get this size we have to multiply the pdf-fontsize and the scaling from the textmatrix + * + * @return The font size in pt. + */ + public float getFontSizeInPt() + { + return fontSizePt; + } + + /** + * 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. + */ + @Deprecated + 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 textPos.getXScale(); + } + + /** + * @return Returns the yScale. + */ + public float getYScale() + { + return textPos.getYScale(); + } + + /** + * Get the widths of each individual character. + * + * @return An array that is the same length as the length of the string. + */ + public float[] getIndividualWidths() + { + return widths; + } + + /** + * Show the string data for this text position. + * + * @return A human readable form of this object. + */ + public String toString() + { + return getCharacter(); + } + + + /** + * Determine if this TextPosition logically contains + * another (i.e. they overlap and should be rendered on top + * of each other). + * @param tp2 The other TestPosition to compare against + * + * @return True if tp2 is contained in the bounding box of this text. + */ + public boolean contains( TextPosition tp2) + { + double thisXstart = getXDirAdj(); + double thisXend = getXDirAdj() + getWidthDirAdj(); + + double tp2Xstart = tp2.getXDirAdj(); + double tp2Xend = tp2.getXDirAdj() + tp2.getWidthDirAdj(); + + /* + * No X overlap at all so return as soon as possible. + */ + if(tp2Xend <= thisXstart || tp2Xstart >= thisXend) + { + return false; + } + /* + * No Y overlap at all so return as soon as possible. + * Note: 0.0 is in the upper left and y-coordinate is + * top of TextPosition + */ + if((tp2.getYDirAdj() + tp2.getHeightDir() < getYDirAdj()) || + (tp2.getYDirAdj() > getYDirAdj() + getHeightDir())) + { + return false; + } + /* We're going to calculate the percentage of overlap. If its less + * than a 15% x-coordinate overlap then we'll return false because its negligible. + * .15 was determined by trial and error in the regression test files. + */ + else if((tp2Xstart > thisXstart) && (tp2Xend > thisXend)) + { + double overlap = thisXend - tp2Xstart; + double overlapPercent = overlap/getWidthDirAdj(); + return (overlapPercent > .15); + } + else if((tp2Xstart < thisXstart) && (tp2Xend < thisXend)) + { + double overlap = tp2Xend - thisXstart; + double overlapPercent = overlap/getWidthDirAdj(); + return (overlapPercent > .15); + } + return true; + } + + /** + * Merge a single character TextPosition into the current object. + * This is to be used only for cases where we have a diacritic that + * overlaps an existing TextPosition. In a graphical display, we could + * overlay them, but for text extraction we need to merge them. Use the + * contains() method to test if two objects overlap. + * + * @param diacritic TextPosition to merge into the current TextPosition. + * @param normalize Instance of TextNormalize class to be used to normalize diacritic + */ + public void mergeDiacritic(TextPosition diacritic, TextNormalize normalize) + { + if (diacritic.getCharacter().length() > 1) + { + return; + } + + float diacXStart = diacritic.getXDirAdj(); + float diacXEnd = diacXStart + diacritic.widths[0]; + + float currCharXStart = getXDirAdj(); + + int strLen = str.length(); + boolean wasAdded = false; + + for (int i = 0; i < strLen && !wasAdded; i++) + { + float currCharXEnd = currCharXStart + widths[i]; + + /* + * This is the case where there is an overlap of the diacritic character with + * the current character and the previous character. If no previous character, + * just append the diacritic after the current one. + */ + if(diacXStart < currCharXStart && diacXEnd <= currCharXEnd) + { + if(i == 0) + { + insertDiacritic(i, diacritic, normalize); + } + else + { + float distanceOverlapping1 = diacXEnd - currCharXStart; + float percentage1 = distanceOverlapping1/widths[i]; + + float distanceOverlapping2 = currCharXStart - diacXStart; + float percentage2 = distanceOverlapping2/widths[i-1]; + + if(percentage1 >= percentage2) + { + insertDiacritic(i, diacritic, normalize); + } + else + { + insertDiacritic(i-1, diacritic, normalize); + } + } + wasAdded = true; + } + //diacritic completely covers this character and therefore we assume that + //this is the character the diacritic belongs to + else if(diacXStart < currCharXStart && diacXEnd > currCharXEnd) + { + insertDiacritic(i, diacritic, normalize); + wasAdded = true; + } + //Otherwise, The diacritic modifies this character because its completely + //contained by the character width + else if(diacXStart >= currCharXStart && diacXEnd <= currCharXEnd) + { + insertDiacritic(i, diacritic, normalize); + wasAdded = true; + } + /* + * Last character in the TextPosition so we add diacritic to the end + */ + else if(diacXStart >= currCharXStart && diacXEnd > currCharXEnd && i == (strLen - 1)) + { + insertDiacritic(i, diacritic, normalize); + wasAdded = true; + } + /* + * Couldn't find anything useful so we go to the next character in the + * TextPosition + */ + currCharXStart += widths[i]; + } + } + /** + * Inserts the diacritic TextPosition to the str of this TextPosition + * and updates the widths array to include the extra character width. + * @param i current character + * @param diacritic The diacritic TextPosition + * @param normalize Instance of TextNormalize class to be used to normalize diacritic + */ + private void insertDiacritic(int i, TextPosition diacritic, TextNormalize normalize) + { + /* we add the diacritic to the right or left of the character + * depending on the direction of the character. Note that this + * is only required because the text is currently stored in + * presentation order and not in logical order. + */ + int dir = Character.getDirectionality(str.charAt(i)); + StringBuffer buf = new StringBuffer(); + + buf.append(str.substring(0,i)); + + float[] widths2 = new float[widths.length+1]; + System.arraycopy(widths, 0, widths2, 0, i); + + if ((dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT) + || (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + || (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING) + || (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE)) + { + buf.append(normalize.normalizeDiac(diacritic.getCharacter())); + widths2[i] = 0; + buf.append(str.charAt(i)); + widths2[i+1] = widths[i]; + } + else + { + buf.append(str.charAt(i)); + widths2[i] = widths[i]; + buf.append(normalize.normalizeDiac(diacritic.getCharacter())); + widths2[i+1] = 0; + } + + // Get the rest of the string + buf.append(str.substring(i+1, str.length())); + System.arraycopy(widths, i+1, widths2, i+2, widths.length-i-1); + + str = buf.toString(); + widths = widths2; + } + + /** + * + * @return True if the current character is a diacritic char. + */ + public boolean isDiacritic() + { + String cText = this.getCharacter(); + return (cText.length() == 1 && (Character.getType(cText.charAt(0)) == Character.NON_SPACING_MARK + || Character.getType(cText.charAt(0)) == Character.MODIFIER_SYMBOL + || Character.getType(cText.charAt(0)) == Character.MODIFIER_LETTER)); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPositionComparator.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPositionComparator.java new file mode 100644 index 000000000..e9bc7e581 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/TextPositionComparator.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util; + +import java.util.Comparator; + +/** + * This class is a comparator for TextPosition operators. It handles + * pages with text in different directions by grouping the text based + * on direction and sorting in that direction. This allows continuous text + * in a given direction to be more easily grouped together. + * + * @author Ben Litchfield + * @version $Revision: 1.7 $ + */ +public class TextPositionComparator implements Comparator +{ + /** + * {@inheritDoc} + */ + public int compare(Object o1, Object o2) + { + int retval = 0; + TextPosition pos1 = (TextPosition)o1; + TextPosition pos2 = (TextPosition)o2; + + /* Only compare text that is in the same direction. */ + if (pos1.getDir() < pos2.getDir()) + { + return -1; + } + else if (pos1.getDir() > pos2.getDir()) + { + return 1; + } + + // Get the text direction adjusted coordinates + float x1 = pos1.getXDirAdj(); + float x2 = pos2.getXDirAdj(); + + float pos1YBottom = pos1.getYDirAdj(); + float pos2YBottom = pos2.getYDirAdj(); + // note that the coordinates have been adjusted so 0,0 is in upper left + float pos1YTop = pos1YBottom - pos1.getHeightDir(); + float pos2YTop = pos2YBottom - pos2.getHeightDir(); + + float yDifference = Math.abs( pos1YBottom-pos2YBottom); + //we will do a simple tolerance comparison. + if( yDifference < .1 || + (pos2YBottom >= pos1YTop && pos2YBottom <= pos1YBottom) || + (pos1YBottom >= pos2YTop && pos1YBottom <= pos2YBottom)) + { + if( x1 < x2 ) + { + retval = -1; + } + else if( x1 > x2 ) + { + retval = 1; + } + else + { + retval = 0; + } + } + else if( pos1YBottom < pos2YBottom ) + { + retval = -1; + } + else + { + return 1; + } + return retval; + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/XMLUtil.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/XMLUtil.java new file mode 100644 index 000000000..708ce9a12 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/XMLUtil.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.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 Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 arguments) + throws IOException + { + COSName tag = null; + for (COSBase argument : arguments) + { + if (argument instanceof COSName) + { + tag = (COSName) argument; + } + } + if (this.context instanceof PDFMarkedContentExtractor) + { + ((PDFMarkedContentExtractor) this.context).beginMarkedContentSequence(tag, null); + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginMarkedContentSequenceWithProperties.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginMarkedContentSequenceWithProperties.java new file mode 100644 index 000000000..4357e1418 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginMarkedContentSequenceWithProperties.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.util.PDFMarkedContentExtractor; +import org.apache.pdfbox.util.PDFOperator; +/** + * BDC : Begins a marked-content sequence with property list. + * + * @author koch + * @version $Revision$ + */ +public class BeginMarkedContentSequenceWithProperties extends OperatorProcessor +{ + + /** + * {@inheritDoc} + */ + public void process(PDFOperator operator, List arguments) + throws IOException + { + COSName tag = null; + COSDictionary properties = null; + for (COSBase argument : arguments) + { + if (argument instanceof COSName) + { + tag = (COSName) argument; + } + else if (argument instanceof COSDictionary) + { + properties = (COSDictionary) argument; + } + } + if (this.context instanceof PDFMarkedContentExtractor) + { + ((PDFMarkedContentExtractor) this.context).beginMarkedContentSequence(tag, properties); + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginText.java new file mode 100644 index 000000000..6ccd7a064 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/BeginText.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Ben Litchfield + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ +public class BeginText extends OperatorProcessor +{ + + /** + * process : BT : Begin text object. + * @param operator The operator that is being executed. + * @param arguments List + */ + public void process(PDFOperator operator, List arguments) + { + context.setTextMatrix( new Matrix()); + context.setTextLineMatrix( new Matrix() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/CloseAndStrokePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/CloseAndStrokePath.java new file mode 100644 index 000000000..b19113b82 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/CloseAndStrokePath.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class CloseAndStrokePath extends OperatorProcessor +{ + + /** + * s close and 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 + { + context.processOperator( "h", arguments ); + context.processOperator( "S", arguments ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Concatenate.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Concatenate.java new file mode 100644 index 000000000..b77c503e8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Concatenate.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; +import java.io.IOException; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ + +public class Concatenate extends OperatorProcessor +{ + + /** + * process : cm : Concatenate matrix to current transformation matrix. + * @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 + { + + //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); + + 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()); + + //this line has changed + context.getGraphicsState().setCurrentTransformationMatrix( + newMatrix.multiply( context.getGraphicsState().getCurrentTransformationMatrix() ) ); + + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndMarkedContentSequence.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndMarkedContentSequence.java new file mode 100644 index 000000000..992095ca9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndMarkedContentSequence.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFMarkedContentExtractor; +import org.apache.pdfbox.util.PDFOperator; + +/** + * EMC : Ends a marked-content sequence begun by BMC or BDC. + * @author koch + * @version $Revision: $ + */ +public class EndMarkedContentSequence extends OperatorProcessor +{ + + /** + * {@inheritDoc} + */ + public void process(PDFOperator operator, List arguments) + throws IOException + { + if (this.context instanceof PDFMarkedContentExtractor) + { + ((PDFMarkedContentExtractor) this.context).endMarkedContentSequence(); + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndText.java new file mode 100644 index 000000000..1814c04a0 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/EndText.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ +public class EndText extends OperatorProcessor +{ + + /** + * process : ET : End text object. + * @param operator The operator that is being executed. + * @param arguments List + */ + public void process(PDFOperator operator, List arguments) + { + context.setTextMatrix( null); + context.setTextLineMatrix( null); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GRestore.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GRestore.java new file mode 100644 index 000000000..d921afff4 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GRestore.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ +public class GRestore extends OperatorProcessor +{ + /** + * process : Q : Restore graphics state. + * @param operator The operator that is being executed. + * @param arguments List + */ + public void process(PDFOperator operator, List arguments) + { + context.setGraphicsState( (PDGraphicsState)context.getGraphicsStack().pop() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GSave.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GSave.java new file mode 100644 index 000000000..45ee141a2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/GSave.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ + +public class GSave extends OperatorProcessor +{ + /** + * process : q : Save graphics state. + * @param operator The operator that is being executed. + * @param arguments List + */ + public void process(PDFOperator operator, List arguments) + { + context.getGraphicsStack().push( (PDGraphicsState)context.getGraphicsState().clone() ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Invoke.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Invoke.java new file mode 100644 index 000000000..06c1054eb --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/Invoke.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm; +import org.apache.pdfbox.util.PDFMarkedContentExtractor; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Invoke named XObject. + * + * @author Ben Litchfield + * @author Mario Ivankovits + * + * @version $Revision: 1.9 $ + */ +public class Invoke extends OperatorProcessor +{ + //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 ); + + Map xobjects = context.getXObjects(); + PDXObject xobject = (PDXObject) xobjects.get(name.getName()); + if (this.context instanceof PDFMarkedContentExtractor) + { + ((PDFMarkedContentExtractor) this.context).xobject(xobject); + } + + 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 ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveAndShow.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveAndShow.java new file mode 100644 index 000000000..37469cb28 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveAndShow.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ +public class MoveAndShow extends OperatorProcessor +{ + /** + * ' 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 + // + + context.processOperator("T*", null); + context.processOperator("Tj", arguments); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveText.java new file mode 100644 index 000000000..b4676fbbf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveText.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ +public class MoveText extends OperatorProcessor +{ + + /** + * 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 ); + Matrix td = new Matrix(); + td.setValue( 2, 0, x.floatValue() ); + td.setValue( 2, 1, y.floatValue() ); + context.setTextLineMatrix( td.multiply( context.getTextLineMatrix() ) ); + context.setTextMatrix( context.getTextLineMatrix().copy() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveTextSetLeading.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveTextSetLeading.java new file mode 100644 index 000000000..6ead2d36b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/MoveTextSetLeading.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ +public class MoveTextSetLeading extends OperatorProcessor +{ + + /** + * 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 ); + + ArrayList args = new ArrayList(); + args.add(new COSFloat(-1*y.floatValue())); + context.processOperator("TL", args); + context.processOperator("Td", arguments); + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/NextLine.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/NextLine.java new file mode 100644 index 000000000..cf77a0aab --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/NextLine.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSFloat; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ +public class NextLine extends OperatorProcessor +{ + /** + * 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 + { + //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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/OperatorProcessor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/OperatorProcessor.java new file mode 100644 index 000000000..503b86d3d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/OperatorProcessor.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.PDFStreamEngine; +import java.util.List; +import java.io.IOException; + +/** + * @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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetCharSpacing.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetCharSpacing.java new file mode 100644 index 000000000..cfcfe96e2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetCharSpacing.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ +public class SetCharSpacing extends OperatorProcessor +{ + /** + * 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; + context.getGraphicsState().getTextState().setCharacterSpacing( characterSpacing.floatValue() ); + } + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetGraphicsStateParameters.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetGraphicsStateParameters.java new file mode 100644 index 000000000..efb5fb8a8 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetGraphicsStateParameters.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.graphics.PDExtendedGraphicsState; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ + +public class SetGraphicsStateParameters extends OperatorProcessor +{ + /** + * 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 ); + PDExtendedGraphicsState gs = (PDExtendedGraphicsState)context.getGraphicsStates().get( graphicsName.getName() ); + gs.copyIntoGraphicsState( context.getGraphicsState() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetHorizontalTextScaling.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetHorizontalTextScaling.java new file mode 100644 index 000000000..03d4e7e58 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetHorizontalTextScaling.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ + +public class SetHorizontalTextScaling extends OperatorProcessor +{ + /** + * 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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineCapStyle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineCapStyle.java new file mode 100644 index 000000000..855ab8b84 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineCapStyle.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class SetLineDashPattern extends OperatorProcessor +{ + + /** + * Set the line dash pattern. + * @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 + { + COSArray dashArray = (COSArray)arguments.get( 0 ); + int dashPhase = ((COSNumber)arguments.get( 1 )).intValue(); + PDLineDashPattern lineDash = new PDLineDashPattern( dashArray, dashPhase ); + context.getGraphicsState().setLineDashPattern( lineDash ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineJoinStyle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineJoinStyle.java new file mode 100644 index 000000000..88964f2ca --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineJoinStyle.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author
    Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ + +public class SetLineMiterLimit extends OperatorProcessor +{ + /** + * w Set miter limit. + * @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 miterLimit = (COSNumber)arguments.get( 0 ); + context.getGraphicsState().setMiterLimit( miterLimit.doubleValue() ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineWidth.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineWidth.java new file mode 100644 index 000000000..f193ae82c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetLineWidth.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ + +public class SetLineWidth extends OperatorProcessor +{ + /** + * 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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMatrix.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMatrix.java new file mode 100644 index 000000000..5580ae063 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMatrix.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ + +public class SetMatrix extends OperatorProcessor +{ + + /** + * 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 ); + + 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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMoveAndShow.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMoveAndShow.java new file mode 100644 index 000000000..269266174 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetMoveAndShow.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.6 $ + */ + +public class SetMoveAndShow extends OperatorProcessor +{ + /** + * " 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 + // + context.processOperator("Tw", arguments.subList(0,1)); + context.processOperator("Tc", arguments.subList(1,2)); + context.processOperator("'", arguments.subList(2,3)); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingCMYKColor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingCMYKColor.java new file mode 100644 index 000000000..bb39cf133 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingCMYKColor.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; +import org.apache.pdfbox.util.PDFOperator; + +/** + *

    Set the non stroking color space.

    + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class SetNonStrokingCMYKColor extends OperatorProcessor +{ + /** + * 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 + { + PDColorSpace cs = PDDeviceCMYK.INSTANCE; + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + colorInstance.setColorSpace( cs ); + float[] values = new float[4]; + for( int i=0; iSet the non stroking color space.

    + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class SetNonStrokingCalRGBColor 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 + { + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + float[] values = new float[3]; + for( int i=0; iSet the non stroking color space.

    + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class SetNonStrokingColor extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(SetNonStrokingColor.class); + + /** + * sc,scn 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 colorSpace = context.getGraphicsState().getNonStrokingColor().getColorSpace(); + if (colorSpace != null) + { + OperatorProcessor newOperator = null; + if (colorSpace instanceof PDDeviceGray) + { + newOperator = new SetNonStrokingGrayColor(); + } + else if (colorSpace instanceof PDDeviceRGB) + { + newOperator = new SetNonStrokingRGBColor(); + } + else if (colorSpace instanceof PDDeviceCMYK) + { + newOperator = new SetNonStrokingCMYKColor(); + } + else if (colorSpace instanceof PDICCBased) + { + newOperator = new SetNonStrokingICCBasedColor(); + } + else if (colorSpace instanceof PDCalRGB) + { + newOperator = new SetNonStrokingCalRGBColor(); + } + else if (colorSpace instanceof PDSeparation) + { + newOperator = new SetNonStrokingSeparation(); + } + else if (colorSpace instanceof PDDeviceN) + { + newOperator = new SetNonStrokingDeviceN(); + } + + if (newOperator != null) + { + newOperator.setContext(getContext()); + newOperator.process(operator, arguments); + } + else + { + log.warn("Not supported colorspace "+colorSpace.getName() + + " within operator "+operator.getOperation()); + } + } + else + { + log.warn("Colorspace not found in "+getClass().getName()+".process!!"); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingColorSpace.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingColorSpace.java new file mode 100644 index 000000000..0d91e5dd3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingColorSpace.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; +import org.apache.pdfbox.util.PDFOperator; + +/** + *

    Set the non stroking color space.

    + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +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 = PDColorSpaceFactory.createColorSpace( name, context.getColorSpaces() ); + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + colorInstance.setColorSpace( cs ); + int numComponents = cs.getNumberOfComponents(); + float[] values = EMPTY_FLOAT_ARRAY; + if( numComponents >= 0 ) + { + values = new float[numComponents]; + for( int i=0; i arguments) throws IOException + { + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + PDColorSpace colorSpace = colorInstance.getColorSpace(); + + if (colorSpace != null) + { + List argList = arguments; + OperatorProcessor newOperator = null; + + if (colorSpace instanceof PDDeviceN) { + PDDeviceN sep = (PDDeviceN) colorSpace; + colorSpace = sep.getAlternateColorSpace(); + argList = sep.calculateColorValues(arguments).toList(); + } + + if (colorSpace instanceof PDDeviceGray) + { + newOperator = new SetNonStrokingGrayColor(); + } + else if (colorSpace instanceof PDDeviceRGB) + { + newOperator = new SetNonStrokingRGBColor(); + } + else if (colorSpace instanceof PDDeviceCMYK) + { + newOperator = new SetNonStrokingCMYKColor(); + } + else if (colorSpace instanceof PDICCBased) + { + newOperator = new SetNonStrokingICCBasedColor(); + } + else if (colorSpace instanceof PDCalRGB) + { + newOperator = new SetNonStrokingCalRGBColor(); + } + + if (newOperator != null) + { + colorInstance.setColorSpace(colorSpace); + newOperator.setContext(getContext()); + newOperator.process(operator, argList); + } + else + { + log.warn("Not supported colorspace "+colorSpace.getName() + + " within operator "+operator.getOperation()); + } + } + + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingGrayColor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingGrayColor.java new file mode 100644 index 000000000..eef473915 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingGrayColor.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; +import org.apache.pdfbox.util.PDFOperator; + +/** + *

    Set the non stroking color space.

    + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class SetNonStrokingGrayColor 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 = new PDDeviceGray(); + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + colorInstance.setColorSpace( cs ); + float[] values = new float[1]; + if( arguments.size() >= 1 ) + { + values[0] = ((COSNumber)arguments.get( 0 )).floatValue(); + } + else + { + throw new IOException( "Error: Expected at least one argument when setting non stroking gray color"); + } + colorInstance.setColorSpaceValue( values ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingICCBasedColor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingICCBasedColor.java new file mode 100644 index 000000000..23848ec16 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetNonStrokingICCBasedColor.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class SetNonStrokingICCBasedColor extends OperatorProcessor +{ + /** + * scn 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 + { + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + PDColorSpace cs = colorInstance.getColorSpace(); + int numberOfComponents = cs.getNumberOfComponents(); + float[] values = new float[numberOfComponents]; + for( int i=0; iSet the non stroking color space.

    + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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; + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + colorInstance.setColorSpace( cs ); + float[] values = new float[3]; + for( int i=0; iDaniel Wilson + * @version $Revision: 1.0 $ + */ +public class SetNonStrokingSeparation extends OperatorProcessor +{ + + /** + * scn 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 + { + PDColorState colorInstance = context.getGraphicsState().getNonStrokingColor(); + PDColorSpace colorSpace = colorInstance.getColorSpace(); + + if (colorSpace != null) + { + PDSeparation sep = (PDSeparation) colorSpace; + colorSpace = sep.getAlternateColorSpace(); + COSArray values = sep.calculateColorValues(arguments.get(0)); + colorInstance.setColorSpaceValue(values.toFloatArray()); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingCMYKColor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingCMYKColor.java new file mode 100644 index 000000000..ac10b7242 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingCMYKColor.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; +import org.apache.pdfbox.util.PDFOperator; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class SetStrokingCMYKColor extends OperatorProcessor +{ + /** + * K 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 + { + PDColorState color = context.getGraphicsState().getStrokingColor(); + color.setColorSpace( PDDeviceCMYK.INSTANCE ); + float[] values = new float[4]; + for( int i=0; iStructural modification of the PDFEngine class : + * the long sequence of conditions in processOperator is replaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class SetStrokingCalRGBColor 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 + { + PDColorState color = context.getGraphicsState().getStrokingColor(); + float[] values = new float[3]; + for( int i=0; iSet the stroking color space.

    + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class SetStrokingColor extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(SetStrokingColor.class); + + /** + * SC,SCN 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 colorSpace = context.getGraphicsState().getStrokingColor().getColorSpace(); + if (colorSpace != null) + { + OperatorProcessor newOperator = null; + if (colorSpace instanceof PDDeviceGray) + { + newOperator = new SetStrokingGrayColor(); + } + else if (colorSpace instanceof PDDeviceRGB) + { + newOperator = new SetStrokingRGBColor(); + } + else if (colorSpace instanceof PDDeviceCMYK) + { + newOperator = new SetStrokingCMYKColor(); + } + else if (colorSpace instanceof PDICCBased) + { + newOperator = new SetStrokingICCBasedColor(); + } + else if (colorSpace instanceof PDCalRGB) + { + newOperator = new SetStrokingCalRGBColor(); + } + else if (colorSpace instanceof PDSeparation) + { + newOperator = new SetStrokingSeparation(); + } + else if (colorSpace instanceof PDDeviceN) + { + newOperator = new SetStrokingDeviceN(); + } + + if (newOperator != null) + { + newOperator.setContext(getContext()); + newOperator.process(operator, arguments); + } + else + { + log.info("Not supported colorspace "+colorSpace.getName() + + " within operator "+operator.getOperation()); + } + } + else + { + log.warn("Colorspace not found in "+getClass().getName()+".process!!"); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingColorSpace.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingColorSpace.java new file mode 100644 index 000000000..5ee752c6d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingColorSpace.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; +import org.apache.pdfbox.util.PDFOperator; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ + +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 = PDColorSpaceFactory.createColorSpace( name, context.getColorSpaces() ); + PDColorState color = context.getGraphicsState().getStrokingColor(); + color.setColorSpace( cs ); + int numComponents = cs.getNumberOfComponents(); + float[] values = EMPTY_FLOAT_ARRAY; + if( numComponents >= 0 ) + { + values = new float[numComponents]; + for( int i=0; i arguments) throws IOException + { + PDColorState color = context.getGraphicsState().getStrokingColor(); + PDColorSpace colorSpace = color.getColorSpace(); + + if (colorSpace != null) + { + OperatorProcessor newOperator = null; + List argList = arguments; + + if (colorSpace instanceof PDDeviceN) + { + PDDeviceN sep = (PDDeviceN) colorSpace; + colorSpace = sep.getAlternateColorSpace(); + argList = sep.calculateColorValues(arguments).toList(); + } + + if (colorSpace instanceof PDDeviceGray) + { + newOperator = new SetStrokingGrayColor(); + } + else if (colorSpace instanceof PDDeviceRGB) + { + newOperator = new SetStrokingRGBColor(); + } + else if (colorSpace instanceof PDDeviceCMYK) + { + newOperator = new SetStrokingCMYKColor(); + } + else if (colorSpace instanceof PDICCBased) + { + newOperator = new SetStrokingICCBasedColor(); + } + else if (colorSpace instanceof PDCalRGB) + { + newOperator = new SetStrokingCalRGBColor(); + } + + if (newOperator != null) + { + color.setColorSpace(colorSpace); + newOperator.setContext(getContext()); + newOperator.process(operator, argList); + } + else + { + log.warn("Not supported colorspace "+colorSpace.getName() + + " within operator "+operator.getOperation()); + } + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingGrayColor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingGrayColor.java new file mode 100644 index 000000000..63c8528d5 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingGrayColor.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; +import org.apache.pdfbox.util.PDFOperator; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class SetStrokingGrayColor 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 + { + PDColorState color = context.getGraphicsState().getStrokingColor(); + color.setColorSpace( new PDDeviceGray() ); + float[] values = new float[1]; + if( arguments.size() >= 1 ) + { + values[0] = ((COSNumber)arguments.get( 0 )).floatValue(); + } + else + { + throw new IOException( "Error: Expected at least one argument when setting non stroking gray color"); + } + color.setColorSpaceValue( values ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingICCBasedColor.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingICCBasedColor.java new file mode 100644 index 000000000..0f29bb87a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetStrokingICCBasedColor.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorState; +import org.apache.pdfbox.util.PDFOperator; + +/** + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class SetStrokingICCBasedColor extends OperatorProcessor +{ + /** + * scn 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 + { + PDColorState color = context.getGraphicsState().getStrokingColor(); + PDColorSpace cs = color.getColorSpace(); + int numberOfComponents = cs.getNumberOfComponents(); + float[] values = new float[numberOfComponents]; + for( int i=0; iStructal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 + { + PDColorState color = context.getGraphicsState().getStrokingColor(); + color.setColorSpace( PDDeviceRGB.INSTANCE ); + float[] values = new float[3]; + for( int i=0; iDaniel Wilson + * @version $Revision: 1.0 $ + */ +public class SetStrokingSeparation extends OperatorProcessor +{ + + /** + * scn 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 + { + PDColorState colorInstance = context.getGraphicsState().getStrokingColor(); + PDColorSpace colorSpace = colorInstance.getColorSpace(); + + if (colorSpace != null) + { + PDSeparation sep = (PDSeparation) colorSpace; + colorSpace = sep.getAlternateColorSpace(); + COSArray values = sep.calculateColorValues(arguments.get(0)); + colorInstance.setColorSpaceValue(values.toFloatArray()); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextFont.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextFont.java new file mode 100644 index 000000000..00f3a42d2 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextFont.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.5 $ + */ + +public class SetTextFont extends OperatorProcessor +{ + /** + * 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 ); + + 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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextLeading.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextLeading.java new file mode 100644 index 000000000..f658eb9ea --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextLeading.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ + +public class SetTextLeading extends OperatorProcessor +{ + /** + * 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() ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRenderingMode.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRenderingMode.java new file mode 100644 index 000000000..0dd0233c9 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRenderingMode.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ + +public class SetTextRenderingMode extends OperatorProcessor +{ + /** + * 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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRise.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRise.java new file mode 100644 index 000000000..3446adaff --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetTextRise.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + *

    Structal modification of the PDFEngine class : + * the long sequence of conditions in processOperator is remplaced by + * this strategy pattern.

    + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ + +public class SetTextRise extends OperatorProcessor +{ + /** + * 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/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetWordSpacing.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetWordSpacing.java new file mode 100644 index 000000000..2cfcebcb3 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/SetWordSpacing.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.util.PDFOperator; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ + +public class SetWordSpacing extends OperatorProcessor +{ + /** + * 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 ); + context.getGraphicsState().getTextState().setWordSpacing( wordSpacing.floatValue() ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowText.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowText.java new file mode 100644 index 000000000..edc5eac3e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowText.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.util.PDFOperator; + +import java.io.IOException; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.4 $ + */ + +public class ShowText extends OperatorProcessor +{ + + /** + * 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.processEncodedText( string.getBytes() ); + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowTextGlyph.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowTextGlyph.java new file mode 100644 index 000000000..6f0dbd2e7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/ShowTextGlyph.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator; + +import java.util.List; + +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import java.io.IOException; +import org.apache.pdfbox.cos.COSString; + +/** + * @author Huault : huault@free.fr + * @version $Revision: 1.6 $ + */ + +public class ShowTextGlyph extends OperatorProcessor +{ + /** + * 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 ); + int arraySize = array.size(); + float fontsize = context.getGraphicsState().getTextState().getFontSize(); + float horizontalScaling = context.getGraphicsState().getTextState().getHorizontalScalingPercent()/100; + for( int i=0; i + + + + + + +This package contains implementations of all of the PDF operators. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java new file mode 100644 index 000000000..c7f5d9295 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/AppendRectangleToPath.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +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 ); + + double x1 = x.doubleValue(); + double y1 = y.doubleValue(); + // create a pair of coordinates for the transformation + double x2 = w.doubleValue()+x1; + double y2 = h.doubleValue()+y1; + + Point2D startCoords = drawer.transformedPoint(x1,y1); + Point2D endCoords = drawer.transformedPoint(x2,y2); + + float width = (float)(endCoords.getX()-startCoords.getX()); + float height = (float)(endCoords.getY()-startCoords.getY()); + float xStart = (float)startCoords.getX(); + float yStart = (float)startCoords.getY(); + + // To ensure that the path is created in the right direction, + // we have to create it by combining single lines instead of + // creating a simple rectangle + GeneralPath path = drawer.getLinePath(); + path.moveTo(xStart, yStart); + path.lineTo(xStart+width, yStart); + path.lineTo(xStart+width, yStart+height); + path.lineTo(xStart, yStart+height); + path.lineTo(xStart, yStart); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/BeginInlineImage.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/BeginInlineImage.java new file mode 100644 index 000000000..c41637e31 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/BeginInlineImage.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDInlinedImage; +import org.apache.pdfbox.util.ImageParameters; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class BeginInlineImage extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(BeginInlineImage.class); + + /** + * 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; + PDPage page = drawer.getPage(); + //begin inline image object + ImageParameters params = operator.getImageParameters(); + PDInlinedImage image = new PDInlinedImage(); + image.setImageParameters( params ); + image.setImageData( operator.getImageData() ); + BufferedImage awtImage = image.createImage( context.getColorSpaces() ); + + if (awtImage == null) + { + log.warn("BeginInlineImage.process(): createImage returned NULL"); + return; + } + int imageWidth = awtImage.getWidth(); + int imageHeight = awtImage.getHeight(); + double pageHeight = drawer.getPageSize().getHeight(); + + Matrix ctm = drawer.getGraphicsState().getCurrentTransformationMatrix(); + int pageRotation = page.findRotation(); + + AffineTransform ctmAT = ctm.createAffineTransform(); + ctmAT.scale(1f/imageWidth, 1f/imageHeight); + Matrix rotationMatrix = new Matrix(); + rotationMatrix.setFromAffineTransform( ctmAT ); + // calculate the inverse rotation angle + // scaleX = m00 = cos + // shearX = m01 = -sin + // tan = sin/cos + double angle = Math.atan(ctmAT.getShearX()/ctmAT.getScaleX()); + Matrix translationMatrix = null; + if (pageRotation == 0 || pageRotation == 180) + { + translationMatrix = Matrix.getTranslatingInstance((float)(Math.sin(angle)*ctm.getXScale()), (float)(pageHeight-2*ctm.getYPosition()-Math.cos(angle)*ctm.getYScale())); + } + else if (pageRotation == 90 || pageRotation == 270) + { + translationMatrix = Matrix.getTranslatingInstance((float)(Math.sin(angle)*ctm.getYScale()), (float)(pageHeight-2*ctm.getYPosition())); + } + rotationMatrix = rotationMatrix.multiply(translationMatrix); + rotationMatrix.setValue(0, 1, (-1)*rotationMatrix.getValue(0, 1)); + rotationMatrix.setValue(1, 0, (-1)*rotationMatrix.getValue(1, 0)); + AffineTransform at = new AffineTransform( + rotationMatrix.getValue(0,0),rotationMatrix.getValue(0,1), + rotationMatrix.getValue(1,0), rotationMatrix.getValue( 1, 1), + rotationMatrix.getValue(2,0),rotationMatrix.getValue(2,1) + ); + drawer.drawImage(awtImage, at); + } +} \ No newline at end of file diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipEvenOddRule.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipEvenOddRule.java new file mode 100644 index 000000000..63e14c827 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipEvenOddRule.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Daniel Wilson + * @version $Revision: 1.1 $ + */ +public class ClipEvenOddRule extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(ClipEvenOddRule.class); + + /** + * process : W* : set clipping path using even odd rule. + * @param operator The operator that is being executed. + * @param arguments List + * + * @throws IOException if there is an error during execution. + */ + public void process(PDFOperator operator, List arguments) throws IOException + { + + try + { + PageDrawer drawer = (PageDrawer)context; + drawer.setClippingPath(GeneralPath.WIND_EVEN_ODD); + } + catch (Exception e) + { + log.warn(e, e); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipNonZeroRule.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipNonZeroRule.java new file mode 100644 index 000000000..649fb4ba6 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClipNonZeroRule.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Daniel Wilson + * @version $Revision: 1.1 $ + */ +public class ClipNonZeroRule extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(ClipNonZeroRule.class); + + /** + * process : W : Set the clipping path using non zero winding rule. + * @param operator The operator that is being executed. + * @param arguments List + * + * @throws IOException If there is an error during the processing. + */ + public void process(PDFOperator operator, List arguments) throws IOException + { + + try + { + PageDrawer drawer = (PageDrawer)context; + drawer.setClippingPath(GeneralPath.WIND_NON_ZERO); + } + catch (Exception e) + { + log.warn(e, e); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillEvenOddAndStrokePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillEvenOddAndStrokePath.java new file mode 100644 index 000000000..a3bc43d63 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillEvenOddAndStrokePath.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class CloseFillEvenOddAndStrokePath extends org.apache.pdfbox.util.operator.OperatorProcessor +{ + + /** + * fill and 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 + { + context.processOperator( "h", arguments ); + + context.processOperator( "f*", arguments ); + + context.processOperator( "S", arguments ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillNonZeroAndStrokePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillNonZeroAndStrokePath.java new file mode 100644 index 000000000..d1e097428 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CloseFillNonZeroAndStrokePath.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class CloseFillNonZeroAndStrokePath extends OperatorProcessor +{ + + /** + * fill and 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 + { + context.processOperator( "h", arguments ); + + context.processOperator( "f", arguments ); + + context.processOperator( "S", arguments ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClosePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClosePath.java new file mode 100644 index 000000000..1b9cfaa4d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/ClosePath.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class ClosePath extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(ClosePath.class); + + /** + * process : h : Close path. + * @param operator The operator that is being executed. + * @param arguments List + * + * @throws IOException if something went wrong during logging + */ + public void process(PDFOperator operator, List arguments) throws IOException + { + PageDrawer drawer = (PageDrawer)context; + try + { + drawer.getLinePath().closePath(); + } + catch( Throwable t ) + { + log.warn(t, t); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveTo.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveTo.java new file mode 100644 index 000000000..286c5f93c --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveTo.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; +import java.awt.geom.Point2D; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + + Point2D point1 = drawer.transformedPoint(x1.doubleValue(), y1.doubleValue()); + Point2D point2 = drawer.transformedPoint(x2.doubleValue(), y2.doubleValue()); + Point2D point3 = drawer.transformedPoint(x3.doubleValue(), y3.doubleValue()); + + drawer.getLinePath().curveTo((float)point1.getX(), (float)point1.getY(), + (float)point2.getX(), (float)point2.getY(), (float)point3.getX(), (float)point3.getY()); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java new file mode 100644 index 000000000..522e0f76a --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateFinalPoint.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; +import java.awt.geom.Point2D; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + + Point2D point1 = drawer.transformedPoint(x1.doubleValue(), y1.doubleValue()); + Point2D point3 = drawer.transformedPoint(x3.doubleValue(), y3.doubleValue()); + + drawer.getLinePath().curveTo((float)point1.getX(), (float)point1.getY(), + (float)point3.getX(), (float)point3.getY(), (float)point3.getX(), (float)point3.getY()); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java new file mode 100644 index 000000000..84844f70d --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/CurveToReplicateInitialPoint.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + GeneralPath path = drawer.getLinePath(); + Point2D currentPoint = path.getCurrentPoint(); + + Point2D point2 = drawer.transformedPoint(x2.doubleValue(), y2.doubleValue()); + Point2D point3 = drawer.transformedPoint(x3.doubleValue(), y3.doubleValue()); + + drawer.getLinePath().curveTo((float)currentPoint.getX(), (float)currentPoint.getY(), + (float)point2.getX(), (float)point2.getY(), (float)point3.getX(), (float)point3.getY()); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/EndPath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/EndPath.java new file mode 100644 index 000000000..20919c26f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/EndPath.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class EndPath extends OperatorProcessor +{ + + + /** + * process : n : End 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().reset(); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddAndStrokePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddAndStrokePath.java new file mode 100644 index 000000000..abaeb474b --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddAndStrokePath.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; + +import java.awt.geom.GeneralPath; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Andreas Lehmkühler + * @version $Revision: 101 $ + */ +public class FillEvenOddAndStrokePath extends org.apache.pdfbox.util.operator.OperatorProcessor +{ + + /** + * fill and 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; + GeneralPath currentPath = (GeneralPath)drawer.getLinePath().clone(); + + context.processOperator( "f*", arguments ); + drawer.setLinePath( currentPath ); + + context.processOperator( "S", arguments ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java new file mode 100644 index 000000000..222c85753 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillEvenOddRule.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class FillEvenOddRule extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(FillEvenOddRule.class); + + /** + * process : f* : fill path using even odd rule. + * @param operator The operator that is being executed. + * @param arguments List + * + * @throws IOException if there is an error during execution. + */ + public void process(PDFOperator operator, List arguments) throws IOException + { + try + { + ///dwilson refactoring + PageDrawer drawer = (PageDrawer)context; + drawer.fillPath(GeneralPath.WIND_EVEN_ODD); + } + catch (Exception e) + { + log.warn(e, e); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroAndStrokePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroAndStrokePath.java new file mode 100644 index 000000000..e6692339e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroAndStrokePath.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; + +import java.awt.geom.GeneralPath; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.1 $ + */ +public class FillNonZeroAndStrokePath extends org.apache.pdfbox.util.operator.OperatorProcessor +{ + + /** + * fill and 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; + GeneralPath currentPath = (GeneralPath)drawer.getLinePath().clone(); + + context.processOperator( "f", arguments ); + drawer.setLinePath( currentPath ); + + context.processOperator( "S", arguments ); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java new file mode 100644 index 000000000..9d52c11c7 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/FillNonZeroRule.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.5 $ + */ +public class FillNonZeroRule extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(FillNonZeroRule.class); + + /** + * process : F/f : fill path using non zero winding rule. + * @param operator The operator that is being executed. + * @param arguments List + * + * @throws IOException If there is an error during the processing. + */ + public void process(PDFOperator operator, List arguments) throws IOException + { + + try + { + ///dwilson refactoring + PageDrawer drawer = (PageDrawer)context; + drawer.fillPath(GeneralPath.WIND_NON_ZERO); + } + catch (Exception e) + { + log.warn(e, e); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/Invoke.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/Invoke.java new file mode 100644 index 000000000..0fcd3bc63 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/Invoke.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +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.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.4 $ + */ +public class Invoke extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(Invoke.class); + + /** + * process : Do : Paint the specified XObject (section 4.7). + * @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(); + COSName objectName = (COSName)arguments.get( 0 ); + Map xobjects = drawer.getResources().getXObjects(); + PDXObject xobject = (PDXObject)xobjects.get( objectName.getName() ); + if ( xobject == null ) + { + log.warn("Can't find the XObject for '"+objectName.getName()+"'"); + } + else if( xobject instanceof PDXObjectImage ) + { + PDXObjectImage image = (PDXObjectImage)xobject; + try + { + image.setGraphicsState(drawer.getGraphicsState()); + BufferedImage awtImage = image.getRGBImage(); + if (awtImage == null) + { + log.warn("getRGBImage returned NULL"); + return;//TODO PKOCH + } + int imageWidth = awtImage.getWidth(); + int imageHeight = awtImage.getHeight(); + double pageHeight = drawer.getPageSize().getHeight(); + + log.debug("imageWidth: " + imageWidth + "\t\timageHeight: " + imageHeight); + + Matrix ctm = drawer.getGraphicsState().getCurrentTransformationMatrix(); + float yScaling = ctm.getYScale(); + float angle = (float)Math.acos(ctm.getValue(0, 0)/ctm.getXScale()); + if (ctm.getValue(0, 1) < 0 && ctm.getValue(1, 0) > 0) + angle = (-1)*angle; + ctm.setValue(2, 1, (float)(pageHeight - ctm.getYPosition() - Math.cos(angle)*yScaling)); + ctm.setValue(2, 0, (float)(ctm.getXPosition() - Math.sin(angle)*yScaling)); + // because of the moved 0,0-reference, we have to shear in the opposite direction + ctm.setValue(0, 1, (-1)*ctm.getValue(0, 1)); + ctm.setValue(1, 0, (-1)*ctm.getValue(1, 0)); + AffineTransform ctmAT = ctm.createAffineTransform(); + ctmAT.scale(1f/imageWidth, 1f/imageHeight); + drawer.drawImage( awtImage, ctmAT ); + } + catch( Exception e ) + { + e.printStackTrace(); + log.error(e, e); + } + } + else if(xobject instanceof PDXObjectForm) + { + // save the graphics state + context.getGraphicsStack().push( (PDGraphicsState)context.getGraphicsState().clone() ); + + PDXObjectForm form = (PDXObjectForm)xobject; + COSStream invoke = (COSStream)form.getCOSObject(); + PDResources pdResources = form.getResources(); + if(pdResources == null) + { + pdResources = page.findResources(); + } + // if there is an optional form matrix, we have to + // map the form space to the user space + Matrix matrix = form.getMatrix(); + if (matrix != null) + { + Matrix xobjectCTM = matrix.multiply( context.getGraphicsState().getCurrentTransformationMatrix()); + context.getGraphicsState().setCurrentTransformationMatrix(xobjectCTM); + } + getContext().processSubStream( page, pdResources, invoke ); + + // restore the graphics state + context.setGraphicsState( (PDGraphicsState)context.getGraphicsStack().pop() ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/LineTo.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/LineTo.java new file mode 100644 index 000000000..475354142 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/LineTo.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; +import java.awt.geom.Point2D; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +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 ); + + Point2D pos = drawer.transformedPoint(x.doubleValue(), y.doubleValue()); + drawer.getLinePath().lineTo((float)pos.getX(), (float)pos.getY()); + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/MoveTo.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/MoveTo.java new file mode 100644 index 000000000..46a2c4955 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/MoveTo.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.Point2D; +import java.util.List; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class MoveTo extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(MoveTo.class); + + /** + * process : m : Begin new subpath. + * @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 + { + try + { + PageDrawer drawer = (PageDrawer)context; + COSNumber x = (COSNumber)arguments.get( 0 ); + COSNumber y = (COSNumber)arguments.get( 1 ); + Point2D pos = drawer.transformedPoint(x.doubleValue(), y.doubleValue()); + drawer.getLinePath().moveTo((float)pos.getX(), (float)pos.getY()); + } + catch (Exception exception) + { + log.warn( exception, exception); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SHFill.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SHFill.java new file mode 100644 index 000000000..b431c8b34 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SHFill.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +/** + * Implementation of sh operator for page drawer. + * See section 4.6.3 of the PDF 1.7 specification. + * + * @author Daniel Wilson + * @version $Revision: 1.0 $ + */ +public class SHFill extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(SHFill.class); + + /** + * process : sh : shade fill the path or clipping area. + * @param operator The operator that is being executed. + * @param arguments List + * + * @throws IOException if there is an error during execution. + */ + public void process(PDFOperator operator, List arguments) throws IOException + { + try + { + PageDrawer drawer = (PageDrawer)context; + drawer.SHFill((COSName)(arguments.get(0))); + + } + catch (Exception e) + { + log.warn(e, e); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineCapStyle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineCapStyle.java new file mode 100644 index 000000000..bee3b1b42 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineCapStyle.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class SetLineDashPattern extends org.apache.pdfbox.util.operator.SetLineDashPattern +{ + + /** + * Set the line dash pattern. + * @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 ); + PDLineDashPattern lineDashPattern = context.getGraphicsState().getLineDashPattern(); + PageDrawer drawer = (PageDrawer)context; + BasicStroke stroke = (BasicStroke)drawer.getStroke(); + if (stroke == null) + { + if (lineDashPattern.isDashPatternEmpty()) + { + drawer.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f) ); + } + else + { + drawer.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, + lineDashPattern.getCOSDashPattern().toFloatArray(), lineDashPattern.getPhaseStart()) ); + } + } + else + { + if (lineDashPattern.isDashPatternEmpty()) + { + drawer.setStroke( new BasicStroke(stroke.getLineWidth(), stroke.getEndCap(), + stroke.getLineJoin(), stroke.getMiterLimit()) ); + } + else + { + drawer.setStroke( new BasicStroke(stroke.getLineWidth(), stroke.getEndCap(), stroke.getLineJoin(), + stroke.getMiterLimit(), lineDashPattern.getCOSDashPattern().toFloatArray(), + lineDashPattern.getPhaseStart()) ); + } + } + } + +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineJoinStyle.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineJoinStyle.java new file mode 100644 index 000000000..ad22605ed --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineJoinStyle.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Andreas Lehmkühler + * @version $Revision: 1.0 $ + */ +public class SetLineMiterLimit extends org.apache.pdfbox.util.operator.SetLineMiterLimit +{ + + /** + * Set the line dash pattern. + * @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 miterLimit = (float)context.getGraphicsState().getMiterLimit(); + PageDrawer drawer = (PageDrawer)context; + BasicStroke stroke = (BasicStroke)drawer.getStroke(); + if (stroke == null) + { + drawer.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, + miterLimit, null, 0.0f)); + } + else + { + drawer.setStroke( new BasicStroke(stroke.getLineWidth(), stroke.getEndCap(), stroke.getLineJoin(), + miterLimit, null, 0.0f)); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineWidth.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineWidth.java new file mode 100644 index 000000000..f606dc567 --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/SetLineWidth.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.PDFOperator; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.2 $ + */ +public class SetLineWidth extends org.apache.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 drawer = (PageDrawer)context; + BasicStroke stroke = (BasicStroke)drawer.getStroke(); + if (stroke == null) + { + drawer.setStroke( new BasicStroke( lineWidth ) ); + } + else + { + drawer.setStroke( new BasicStroke(lineWidth, stroke.getEndCap(), stroke.getLineJoin(), + stroke.getMiterLimit(), stroke.getDashArray(), stroke.getDashPhase()) ); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/StrokePath.java b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/StrokePath.java new file mode 100644 index 000000000..359fa2b7f --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/StrokePath.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.util.operator.pagedrawer; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.pdfviewer.PageDrawer; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.operator.OperatorProcessor; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.io.IOException; + +/** + * Implementation of content stream operator for page drawer. + * + * @author Ben Litchfield + * @version $Revision: 1.3 $ + */ +public class StrokePath extends OperatorProcessor +{ + + /** + * Log instance. + */ + private static final Log log = LogFactory.getLog(StrokePath.class); + + /** + * 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 + { + ///dwilson 3/19/07 refactor + try + { + PageDrawer drawer = (PageDrawer)context; + + float lineWidth = (float)context.getGraphicsState().getLineWidth(); + Matrix ctm = context.getGraphicsState().getCurrentTransformationMatrix(); + if ( ctm != null && ctm.getXScale() > 0) + { + lineWidth = lineWidth * ctm.getXScale(); + } + + BasicStroke stroke = (BasicStroke)drawer.getStroke(); + if (stroke == null) + { + drawer.setStroke( new BasicStroke( lineWidth ) ); + } + else + { + drawer.setStroke( new BasicStroke(lineWidth, stroke.getEndCap(), stroke.getLineJoin(), + stroke.getMiterLimit(), stroke.getDashArray(), stroke.getDashPhase()) ); + } + drawer.strokePath(); + } + catch (Exception exception) + { + log.warn(exception, exception); + } + } +} diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/package.html new file mode 100644 index 000000000..1d0c2e29e --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/operator/pagedrawer/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains implementations of all of the PDF operators. + + diff --git a/fluidbook/tools/fwstk/src/apache/pdfbox/util/package.html b/fluidbook/tools/fwstk/src/apache/pdfbox/util/package.html new file mode 100644 index 000000000..6431cdfdf --- /dev/null +++ b/fluidbook/tools/fwstk/src/apache/pdfbox/util/package.html @@ -0,0 +1,25 @@ + + + + + + + +This package contains utility classes that are used by the PDFBox project. + + -- 2.39.5