From 28999738a9a392a9e7a3e44e6f5d4410dd66a637 Mon Sep 17 00:00:00 2001
From: Javen O'Neal
Date: Wed, 8 Feb 2017 07:47:30 +0000
Subject: [PATCH] Convert all *.java files to use native end of line
character(s) $ dos2unix `find -name *.java` $ svn propset svn:eol-style
native `find -name *.java`
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1782114 13f79535-47bb-0310-9956-ffa450edef68
---
.../usermodel/examples/EmbeddedObjects.java | 134 +-
.../hssf/usermodel/examples/InCellLists.java | 1106 ++---
.../poi/ss/examples/AddDimensionedImage.java | 2050 ++++-----
.../apache/poi/ss/examples/AligningCells.java | 148 +-
.../poi/ss/examples/CellStyleDetails.java | 192 +-
.../poi/ss/examples/ConditionalFormats.java | 1280 +++---
.../poi/ss/examples/ExcelComparator.java | 1226 ++---
.../poi/ss/examples/LinkedDropDownLists.java | 422 +-
.../formula/SettingExternalFunction.java | 190 +-
.../poi/xslf/usermodel/DataExtraction.java | 190 +-
.../xslf/usermodel/MergePresentations.java | 108 +-
.../poi/xslf/usermodel/PieChartDemo.java | 330 +-
.../apache/poi/xslf/usermodel/Tutorial1.java | 148 +-
.../apache/poi/xslf/usermodel/Tutorial2.java | 170 +-
.../apache/poi/xslf/usermodel/Tutorial3.java | 102 +-
.../apache/poi/xslf/usermodel/Tutorial4.java | 188 +-
.../apache/poi/xslf/usermodel/Tutorial5.java | 102 +-
.../apache/poi/xslf/usermodel/Tutorial6.java | 122 +-
.../apache/poi/xslf/usermodel/Tutorial7.java | 180 +-
.../xssf/usermodel/examples/CellComments.java | 180 +-
.../usermodel/examples/CustomXMLMapping.java | 90 +-
.../usermodel/examples/EmbeddedObjects.java | 132 +-
.../usermodel/examples/HyperlinkExample.java | 14 +-
.../xssf/usermodel/examples/LineChart.java | 186 +-
.../usermodel/examples/UpdateEmbeddedDoc.java | 402 +-
.../poi/ss/excelant/ExcelAntHandlerTask.java | 152 +-
.../apache/poi/ss/excelant/ExcelAntSet.java | 96 +-
.../ss/excelant/IExcelAntWorkbookHandler.java | 84 +-
.../util/ExcelAntWorkbookUtilFactory.java | 120 +-
.../apache/poi/ss/excelant/BuildFileTest.java | 1208 ++---
.../apache/poi/ss/excelant/TestBuildFile.java | 238 +-
.../poi/stress/AbstractFileHandler.java | 284 +-
.../apache/poi/stress/SlideShowHandler.java | 250 +-
.../org/apache/poi/ddf/EscherColorRef.java | 592 +--
.../org/apache/poi/ddf/EscherPictBlip.java | 42 +-
.../apache/poi/hssf/dev/BiffDrawingToXml.java | 328 +-
.../poi/hssf/record/LbsDataSubRecord.java | 840 ++--
.../hssf/record/RecordFactoryInputStream.java | 16 +-
.../record/cont/ContinuableRecordInput.java | 264 +-
.../poi/hssf/usermodel/DummyGraphics2d.java | 1664 +++----
.../poi/hssf/usermodel/HSSFCombobox.java | 202 +-
.../poi/hssf/usermodel/HSSFHyperlink.java | 70 +-
.../poi/hssf/usermodel/HSSFShapeFactory.java | 284 +-
.../apache/poi/poifs/crypt/ChainingMode.java | 64 +-
.../poifs/crypt/ChunkedCipherInputStream.java | 560 +--
.../crypt/ChunkedCipherOutputStream.java | 600 +--
.../poi/poifs/crypt/CipherAlgorithm.java | 154 +-
.../poi/poifs/crypt/CipherProvider.java | 80 +-
.../poi/poifs/crypt/CryptoFunctions.java | 1152 ++---
.../poi/poifs/crypt/DataSpaceMapUtils.java | 740 +--
.../poifs/crypt/EncryptionInfoBuilder.java | 66 +-
.../poi/poifs/crypt/EncryptionMode.java | 104 +-
.../org/apache/poi/poifs/crypt/Encryptor.java | 208 +-
.../apache/poi/poifs/crypt/HashAlgorithm.java | 148 +-
.../crypt/binaryrc4/BinaryRC4Decryptor.java | 332 +-
.../binaryrc4/BinaryRC4EncryptionHeader.java | 102 +-
.../BinaryRC4EncryptionInfoBuilder.java | 118 +-
.../BinaryRC4EncryptionVerifier.java | 178 +-
.../crypt/binaryrc4/BinaryRC4Encryptor.java | 320 +-
.../crypt/cryptoapi/CryptoAPIDecryptor.java | 510 +--
.../cryptoapi/CryptoAPIEncryptionHeader.java | 136 +-
.../CryptoAPIEncryptionInfoBuilder.java | 148 +-
.../CryptoAPIEncryptionVerifier.java | 116 +-
.../crypt/cryptoapi/CryptoAPIEncryptor.java | 554 +--
.../crypt/standard/EncryptionRecord.java | 46 +-
.../standard/StandardEncryptionHeader.java | 260 +-
.../StandardEncryptionInfoBuilder.java | 192 +-
.../crypt/standard/StandardEncryptor.java | 470 +-
.../poi/poifs/filesystem/Ole10Native.java | 802 ++--
.../poi/poifs/macros/VBAMacroExtractor.java | 180 +-
.../poi/sl/draw/BitmapImageRenderer.java | 612 +--
.../org/apache/poi/sl/draw/DrawAutoShape.java | 54 +-
.../apache/poi/sl/draw/DrawBackground.java | 138 +-
.../poi/sl/draw/DrawConnectorShape.java | 52 +-
.../org/apache/poi/sl/draw/DrawFactory.java | 476 +-
.../apache/poi/sl/draw/DrawFontManager.java | 112 +-
.../apache/poi/sl/draw/DrawFreeformShape.java | 122 +-
.../poi/sl/draw/DrawGraphicalFrame.java | 80 +-
.../apache/poi/sl/draw/DrawGroupShape.java | 150 +-
.../apache/poi/sl/draw/DrawMasterSheet.java | 106 +-
.../poi/sl/draw/DrawNotImplemented.java | 70 +-
.../org/apache/poi/sl/draw/DrawNothing.java | 94 +-
.../org/apache/poi/sl/draw/DrawPaint.java | 1048 ++---
.../apache/poi/sl/draw/DrawPictureShape.java | 408 +-
.../org/apache/poi/sl/draw/DrawShape.java | 482 +-
.../org/apache/poi/sl/draw/DrawSheet.java | 194 +-
.../apache/poi/sl/draw/DrawSimpleShape.java | 892 ++--
.../org/apache/poi/sl/draw/DrawSlide.java | 88 +-
.../apache/poi/sl/draw/DrawTableShape.java | 514 +--
.../org/apache/poi/sl/draw/DrawTextBox.java | 52 +-
.../apache/poi/sl/draw/DrawTextFragment.java | 210 +-
.../apache/poi/sl/draw/DrawTextParagraph.java | 1462 +++---
.../org/apache/poi/sl/draw/DrawTextShape.java | 454 +-
src/java/org/apache/poi/sl/draw/Drawable.java | 322 +-
.../org/apache/poi/sl/draw/ImageRenderer.java | 258 +-
.../apache/poi/sl/draw/PathGradientPaint.java | 376 +-
.../org/apache/poi/sl/draw/SLGraphics.java | 3692 +++++++--------
.../poi/sl/draw/binding/CTAdjPoint2D.java | 218 +-
.../sl/draw/binding/CTAdjustHandleList.java | 198 +-
.../apache/poi/sl/draw/binding/CTAngle.java | 140 +-
.../apache/poi/sl/draw/binding/CTColor.java | 474 +-
.../poi/sl/draw/binding/CTColorMRU.java | 212 +-
.../draw/binding/CTComplementTransform.java | 92 +-
.../poi/sl/draw/binding/CTConnection.java | 190 +-
.../poi/sl/draw/binding/CTConnectionSite.java | 228 +-
.../sl/draw/binding/CTConnectionSiteList.java | 186 +-
.../sl/draw/binding/CTCustomGeometry2D.java | 484 +-
.../draw/binding/CTEmbeddedWAVAudioFile.java | 304 +-
.../sl/draw/binding/CTFixedPercentage.java | 140 +-
.../poi/sl/draw/binding/CTGammaTransform.java | 92 +-
.../poi/sl/draw/binding/CTGeomGuide.java | 224 +-
.../poi/sl/draw/binding/CTGeomGuideList.java | 186 +-
.../poi/sl/draw/binding/CTGeomRect.java | 342 +-
.../sl/draw/binding/CTGrayscaleTransform.java | 92 +-
.../sl/draw/binding/CTGroupTransform2D.java | 592 +--
.../poi/sl/draw/binding/CTHslColor.java | 442 +-
.../poi/sl/draw/binding/CTHyperlink.java | 806 ++--
.../draw/binding/CTInverseGammaTransform.java | 92 +-
.../sl/draw/binding/CTInverseTransform.java | 92 +-
.../sl/draw/binding/CTOfficeArtExtension.java | 244 +-
.../binding/CTOfficeArtExtensionList.java | 186 +-
.../apache/poi/sl/draw/binding/CTPath2D.java | 606 +--
.../poi/sl/draw/binding/CTPath2DArcTo.java | 342 +-
.../poi/sl/draw/binding/CTPath2DClose.java | 92 +-
.../draw/binding/CTPath2DCubicBezierTo.java | 186 +-
.../poi/sl/draw/binding/CTPath2DLineTo.java | 164 +-
.../poi/sl/draw/binding/CTPath2DList.java | 186 +-
.../poi/sl/draw/binding/CTPath2DMoveTo.java | 164 +-
.../sl/draw/binding/CTPath2DQuadBezierTo.java | 186 +-
.../poi/sl/draw/binding/CTPercentage.java | 140 +-
.../apache/poi/sl/draw/binding/CTPoint2D.java | 186 +-
.../apache/poi/sl/draw/binding/CTPoint3D.java | 232 +-
.../sl/draw/binding/CTPolarAdjustHandle.java | 546 +--
.../sl/draw/binding/CTPositiveFixedAngle.java | 140 +-
.../binding/CTPositiveFixedPercentage.java | 140 +-
.../sl/draw/binding/CTPositivePercentage.java | 140 +-
.../poi/sl/draw/binding/CTPositiveSize2D.java | 186 +-
.../poi/sl/draw/binding/CTPresetColor.java | 366 +-
.../sl/draw/binding/CTPresetGeometry2D.java | 228 +-
.../sl/draw/binding/CTPresetTextShape.java | 228 +-
.../apache/poi/sl/draw/binding/CTRatio.java | 186 +-
.../poi/sl/draw/binding/CTRelativeRect.java | 406 +-
.../poi/sl/draw/binding/CTSRgbColor.java | 372 +-
.../poi/sl/draw/binding/CTScRgbColor.java | 442 +-
.../apache/poi/sl/draw/binding/CTScale2D.java | 228 +-
.../poi/sl/draw/binding/CTSchemeColor.java | 366 +-
.../poi/sl/draw/binding/CTSphereCoords.java | 232 +-
.../poi/sl/draw/binding/CTSystemColor.java | 438 +-
.../poi/sl/draw/binding/CTTransform2D.java | 464 +-
.../poi/sl/draw/binding/CTVector3D.java | 232 +-
.../poi/sl/draw/binding/CTXYAdjustHandle.java | 546 +--
.../poi/sl/draw/binding/ObjectFactory.java | 4046 ++++++++---------
.../poi/sl/draw/binding/STBlackWhiteMode.java | 298 +-
.../poi/sl/draw/binding/STPathFillMode.java | 218 +-
.../poi/sl/draw/binding/STPresetColorVal.java | 2362 +++++-----
.../poi/sl/draw/binding/STRectAlignment.java | 266 +-
.../poi/sl/draw/binding/STSchemeColorVal.java | 394 +-
.../poi/sl/draw/binding/STShapeType.java | 3114 ++++++-------
.../poi/sl/draw/binding/STTextShapeType.java | 778 ++--
.../poi/sl/usermodel/AutoNumberingScheme.java | 576 +--
.../apache/poi/sl/usermodel/ColorStyle.java | 218 +-
.../poi/sl/usermodel/ConnectorShape.java | 50 +-
.../apache/poi/sl/usermodel/FillStyle.java | 44 +-
.../poi/sl/usermodel/FreeformShape.java | 90 +-
.../poi/sl/usermodel/GraphicalFrame.java | 58 +-
.../org/apache/poi/sl/usermodel/Insets2D.java | 280 +-
.../poi/sl/usermodel/LineDecoration.java | 226 +-
.../poi/sl/usermodel/PlaceableShape.java | 184 +-
.../apache/poi/sl/usermodel/Placeholder.java | 310 +-
.../apache/poi/sl/usermodel/PresetColor.java | 558 +--
.../org/apache/poi/sl/usermodel/Shadow.java | 106 +-
.../poi/sl/usermodel/SlideShowFactory.java | 530 +--
.../apache/poi/sl/usermodel/TableCell.java | 228 +-
.../apache/poi/sl/usermodel/TableShape.java | 160 +-
.../poi/sl/usermodel/TextParagraph.java | 752 +--
.../apache/poi/sl/usermodel/TextShape.java | 514 +--
.../poi/sl/usermodel/VerticalAlignment.java | 148 +-
.../poi/ss/format/CellNumberPartHandler.java | 328 +-
.../poi/ss/format/CellNumberStringMod.java | 210 +-
.../apache/poi/ss/formula/EvaluationCell.java | 12 +-
.../apache/poi/ss/formula/SharedFormula.java | 196 +-
.../poi/ss/formula/functions/Address.java | 206 +-
.../ss/formula/functions/BaseNumberUtils.java | 156 +-
.../poi/ss/formula/functions/Bin2Dec.java | 248 +-
.../apache/poi/ss/formula/functions/Code.java | 108 +-
.../poi/ss/formula/functions/Complex.java | 300 +-
.../poi/ss/formula/functions/Dec2Bin.java | 262 +-
.../poi/ss/formula/functions/Dec2Hex.java | 276 +-
.../poi/ss/formula/functions/Delta.java | 168 +-
.../poi/ss/formula/functions/EOMonth.java | 164 +-
.../poi/ss/formula/functions/FactDouble.java | 172 +-
.../poi/ss/formula/functions/Hex2Dec.java | 132 +-
.../poi/ss/formula/functions/ImReal.java | 196 +-
.../poi/ss/formula/functions/Imaginary.java | 218 +-
.../apache/poi/ss/formula/functions/Mirr.java | 218 +-
.../poi/ss/formula/functions/Oct2Dec.java | 128 +-
.../poi/ss/formula/functions/Quotient.java | 146 +-
.../apache/poi/ss/formula/functions/Rate.java | 246 +-
.../apache/poi/ss/formula/functions/Rept.java | 148 +-
.../poi/ss/formula/functions/Roman.java | 300 +-
.../poi/ss/formula/functions/WeekNum.java | 178 +-
.../apache/poi/ss/usermodel/AutoFilter.java | 158 +-
.../poi/ss/usermodel/BorderFormatting.java | 516 +--
.../ss/usermodel/ColorScaleFormatting.java | 132 +-
.../poi/ss/usermodel/ComparisonOperator.java | 140 +-
.../poi/ss/usermodel/ConditionType.java | 184 +-
.../ss/usermodel/ConditionalFormatting.java | 234 +-
.../usermodel/ConditionalFormattingRule.java | 244 +-
.../ConditionalFormattingThreshold.java | 220 +-
.../poi/ss/usermodel/DataBarFormatting.java | 144 +-
.../poi/ss/usermodel/FontFormatting.java | 302 +-
.../poi/ss/usermodel/FractionFormat.java | 368 +-
.../usermodel/IconMultiStateFormatting.java | 254 +-
.../poi/ss/usermodel/PatternFormatting.java | 156 +-
.../usermodel/SheetConditionalFormatting.java | 390 +-
.../poi/ss/usermodel/charts/AxisTickMark.java | 60 +-
.../poi/ss/usermodel/charts/ChartSeries.java | 110 +-
.../poi/ss/usermodel/charts/LayoutMode.java | 70 +-
.../poi/ss/usermodel/charts/LayoutTarget.java | 78 +-
.../ss/usermodel/charts/LineChartData.java | 82 +-
.../ss/usermodel/charts/LineChartSeries.java | 76 +-
.../poi/ss/usermodel/charts/ManualLayout.java | 306 +-
.../charts/ManuallyPositionable.java | 72 +-
.../poi/ss/usermodel/charts/TitleType.java | 56 +-
.../poi/ss/util/DateFormatConverter.java | 866 ++--
.../apache/poi/ss/util/ExpandedDouble.java | 196 +-
.../org/apache/poi/ss/util/ImageUtils.java | 570 +--
.../apache/poi/ss/util/NormalisedDecimal.java | 542 +--
.../org/apache/poi/util/Configurator.java | 72 +-
src/java/org/apache/poi/util/JvmBugs.java | 110 +-
src/java/org/apache/poi/util/LocaleUtil.java | 282 +-
src/java/org/apache/poi/util/PngUtils.java | 106 +-
.../apache/poi/util/SuppressForbidden.java | 66 +-
src/java/org/apache/poi/util/Units.java | 260 +-
.../java/org/apache/poi/POIXMLTypeLoader.java | 348 +-
.../org/apache/poi/dev/OOXMLPrettyPrint.java | 286 +-
.../poi/openxml4j/util/ZipSecureFile.java | 618 +--
.../crypt/agile/AgileEncryptionHeader.java | 268 +-
.../agile/AgileEncryptionInfoBuilder.java | 218 +-
.../poi/poifs/crypt/agile/AgileEncryptor.java | 920 ++--
.../dsig/CertificateSecurityException.java | 76 +-
.../poi/poifs/crypt/dsig/DigestInfo.java | 112 +-
.../ExpiredCertificateSecurityException.java | 76 +-
.../poifs/crypt/dsig/KeyInfoKeySelector.java | 206 +-
.../crypt/dsig/OOXMLURIDereferencer.java | 260 +-
.../RevokedCertificateSecurityException.java | 76 +-
.../poi/poifs/crypt/dsig/SignatureConfig.java | 1794 ++++----
.../crypt/dsig/SignatureMarshalListener.java | 182 +-
.../TrustCertificateSecurityException.java | 76 +-
.../dsig/facets/EnvelopedSignatureFacet.java | 120 +-
.../dsig/facets/KeyInfoSignatureFacet.java | 294 +-
.../dsig/facets/OOXMLSignatureFacet.java | 948 ++--
.../dsig/facets/Office2010SignatureFacet.java | 156 +-
.../crypt/dsig/facets/SignatureFacet.java | 378 +-
.../dsig/facets/XAdESSignatureFacet.java | 570 +--
.../dsig/facets/XAdESXLSignatureFacet.java | 778 ++--
.../RelationshipTransformService.java | 496 +-
.../crypt/dsig/services/RevocationData.java | 264 +-
.../dsig/services/RevocationDataService.java | 94 +-
.../dsig/services/SignaturePolicyService.java | 128 +-
.../dsig/services/TSPTimeStampService.java | 520 +--
.../crypt/dsig/services/TimeStampService.java | 108 +-
.../services/TimeStampServiceValidator.java | 102 +-
.../apache/poi/util/IdentifierManager.java | 532 +--
.../java/org/apache/poi/util/XmlSort.java | 174 +-
.../xslf/model/CharacterPropertyFetcher.java | 78 +-
.../xslf/model/ParagraphPropertyFetcher.java | 102 +-
.../poi/xslf/model/PropertyFetcher.java | 96 +-
.../xslf/model/TextBodyPropertyFetcher.java | 102 +-
.../poi/xslf/usermodel/XSLFAutoShape.java | 216 +-
.../poi/xslf/usermodel/XSLFBackground.java | 224 +-
.../apache/poi/xslf/usermodel/XSLFColor.java | 968 ++--
.../xslf/usermodel/XSLFConnectorShape.java | 154 +-
.../poi/xslf/usermodel/XSLFDrawing.java | 226 +-
.../poi/xslf/usermodel/XSLFFreeformShape.java | 418 +-
.../poi/xslf/usermodel/XSLFGraphicFrame.java | 436 +-
.../poi/xslf/usermodel/XSLFGroupShape.java | 756 +--
.../poi/xslf/usermodel/XSLFHyperlink.java | 332 +-
.../poi/xslf/usermodel/XSLFLineBreak.java | 66 +-
.../poi/xslf/usermodel/XSLFMetroShape.java | 124 +-
.../poi/xslf/usermodel/XSLFPictureShape.java | 462 +-
.../apache/poi/xslf/usermodel/XSLFShadow.java | 212 +-
.../apache/poi/xslf/usermodel/XSLFShape.java | 1122 ++---
.../xslf/usermodel/XSLFSlideShowFactory.java | 200 +-
.../apache/poi/xslf/usermodel/XSLFTable.java | 700 +--
.../poi/xslf/usermodel/XSLFTableCell.java | 1546 +++----
.../poi/xslf/usermodel/XSLFTableRow.java | 212 +-
.../poi/xslf/usermodel/XSLFTableStyle.java | 168 +-
.../poi/xslf/usermodel/XSLFTextBox.java | 120 +-
.../poi/xslf/usermodel/XSLFTextRun.java | 1258 ++---
.../org/apache/poi/xslf/util/PPTX2PNG.java | 428 +-
.../xssf/streaming/GZIPSheetDataWriter.java | 136 +-
.../poi/xssf/streaming/SheetDataWriter.java | 874 ++--
.../xssf/usermodel/XSSFBorderFormatting.java | 734 +--
.../usermodel/XSSFColorScaleFormatting.java | 184 +-
.../usermodel/XSSFConditionalFormatting.java | 262 +-
.../XSSFConditionalFormattingRule.java | 722 +--
.../XSSFConditionalFormattingThreshold.java | 154 +-
.../xssf/usermodel/XSSFDataBarFormatting.java | 166 +-
.../xssf/usermodel/XSSFFontFormatting.java | 448 +-
.../XSSFIconMultiStateFormatting.java | 168 +-
.../xssf/usermodel/XSSFPatternFormatting.java | 216 +-
.../XSSFSheetConditionalFormatting.java | 590 +--
.../charts/AbstractXSSFChartSeries.java | 158 +-
.../usermodel/charts/XSSFLineChartData.java | 252 +-
.../usermodel/charts/XSSFManualLayout.java | 494 +-
.../poi/xwpf/usermodel/AbstractXWPFSDT.java | 214 +-
.../poi/xwpf/usermodel/BodyElementType.java | 22 +-
.../apache/poi/xwpf/usermodel/Borders.java | 76 +-
.../apache/poi/xwpf/usermodel/BreakClear.java | 86 +-
.../apache/poi/xwpf/usermodel/BreakType.java | 82 +-
.../apache/poi/xwpf/usermodel/Document.java | 78 +-
.../org/apache/poi/xwpf/usermodel/IBody.java | 180 +-
.../poi/xwpf/usermodel/IBodyElement.java | 30 +-
.../org/apache/poi/xwpf/usermodel/ICell.java | 20 +-
.../poi/xwpf/usermodel/ISDTContent.java | 34 +-
.../poi/xwpf/usermodel/LineSpacingRule.java | 72 +-
.../xwpf/usermodel/ParagraphAlignment.java | 56 +-
.../xwpf/usermodel/PositionInParagraph.java | 88 +-
.../org/apache/poi/xwpf/usermodel/TOC.java | 164 +-
.../poi/xwpf/usermodel/TextAlignment.java | 102 +-
.../poi/xwpf/usermodel/TextSegement.java | 162 +-
.../poi/xwpf/usermodel/UnderlinePatterns.java | 72 +-
.../poi/xwpf/usermodel/VerticalAlign.java | 72 +-
.../poi/xwpf/usermodel/XWPFAbstractNum.java | 80 +-
.../usermodel/XWPFDefaultParagraphStyle.java | 30 +-
.../xwpf/usermodel/XWPFDefaultRunStyle.java | 30 +-
.../poi/xwpf/usermodel/XWPFFieldRun.java | 44 +-
.../poi/xwpf/usermodel/XWPFFootnote.java | 754 +--
.../poi/xwpf/usermodel/XWPFFootnotes.java | 166 +-
.../poi/xwpf/usermodel/XWPFHyperlinkRun.java | 84 +-
.../poi/xwpf/usermodel/XWPFLatentStyles.java | 60 +-
.../apache/poi/xwpf/usermodel/XWPFNum.java | 84 +-
.../poi/xwpf/usermodel/XWPFNumbering.java | 436 +-
.../poi/xwpf/usermodel/XWPFPicture.java | 82 +-
.../apache/poi/xwpf/usermodel/XWPFSDT.java | 54 +-
.../poi/xwpf/usermodel/XWPFSDTCell.java | 50 +-
.../poi/xwpf/usermodel/XWPFSDTContent.java | 190 +-
.../xwpf/usermodel/XWPFSDTContentCell.java | 232 +-
.../apache/poi/xwpf/usermodel/XWPFStyle.java | 276 +-
.../apache/poi/xwpf/usermodel/XWPFStyles.java | 380 +-
.../poi/xwpf/usermodel/XWPFTableCell.java | 1036 ++---
.../poi/xwpf/usermodel/XWPFTableRow.java | 348 +-
.../poi/openxml4j/opc/ZipFileAssert.java | 278 +-
.../poi/poifs/crypt/AllPOIFSCryptoTests.java | 70 +-
.../apache/poi/poifs/crypt/PkiTestUtils.java | 634 +--
.../crypt/TestAgileEncryptionParameters.java | 218 +-
.../crypt/TestCertificateEncryption.java | 394 +-
.../apache/poi/poifs/crypt/TestDecryptor.java | 368 +-
.../poi/poifs/crypt/TestEncryptionInfo.java | 130 +-
.../apache/poi/poifs/crypt/TestEncryptor.java | 1052 ++---
.../poi/poifs/crypt/TestSignatureInfo.java | 1448 +++---
.../org/apache/poi/sl/TestHeadersFooters.java | 206 +-
.../org/apache/poi/sl/TestTable.java | 326 +-
.../poi/util/TestIdentifierManager.java | 258 +-
.../apache/poi/xslf/XSLFTestDataSamples.java | 144 +-
.../poi/xslf/usermodel/TestPPTX2PNG.java | 206 +-
.../poi/xslf/usermodel/TestXSLFAutoShape.java | 620 +--
.../poi/xslf/usermodel/TestXSLFChart.java | 262 +-
.../poi/xslf/usermodel/TestXSLFColor.java | 366 +-
.../usermodel/TestXSLFConnectorShape.java | 316 +-
.../xslf/usermodel/TestXSLFFreeformShape.java | 108 +-
.../xslf/usermodel/TestXSLFGroupShape.java | 214 +-
.../poi/xslf/usermodel/TestXSLFHyperlink.java | 358 +-
.../poi/xslf/usermodel/TestXSLFNotes.java | 212 +-
.../xslf/usermodel/TestXSLFPictureShape.java | 420 +-
.../poi/xslf/usermodel/TestXSLFShape.java | 214 +-
.../usermodel/TestXSLFShapeContainer.java | 126 +-
.../poi/xslf/usermodel/TestXSLFSheet.java | 148 +-
.../xslf/usermodel/TestXSLFSimpleShape.java | 770 ++--
.../poi/xslf/usermodel/TestXSLFSlide.java | 392 +-
.../poi/xslf/usermodel/TestXSLFSlideShow.java | 298 +-
.../poi/xslf/usermodel/TestXSLFTable.java | 434 +-
.../xslf/usermodel/TestXSLFTableStyles.java | 74 +-
.../poi/xslf/usermodel/TestXSLFTextBox.java | 178 +-
.../xslf/usermodel/TestXSLFTextParagraph.java | 788 ++--
.../poi/xslf/usermodel/TestXSLFTextRun.java | 160 +-
.../poi/xslf/usermodel/TestXSLFTextShape.java | 1886 ++++----
.../poi/xslf/usermodel/TestXSLFTheme.java | 342 +-
.../apache/poi/xssf/TestXSSFCloneSheet.java | 186 +-
.../poi/xssf/model/TestThemesTable.java | 526 +--
.../streaming/TestSXSSFDataValidation.java | 124 +-
.../poi/xssf/usermodel/TestUnfixedBugs.java | 800 ++--
.../TestXSSFConditionalFormatting.java | 118 +-
.../charts/TestXSSFLineChartData.java | 152 +-
.../charts/TestXSSFManualLayout.java | 240 +-
.../org/apache/poi/xwpf/AllXWPFTests.java | 40 +-
.../poi/xwpf/TestAllExtendedProperties.java | 158 +-
.../TestPackageCorePropertiesGetKeywords.java | 56 +-
.../org/apache/poi/xwpf/TestXWPFBugs.java | 252 +-
.../apache/poi/xwpf/XWPFTestDataSamples.java | 106 +-
.../poi/xwpf/model/TestXWPFDecorators.java | 114 +-
.../apache/poi/hslf/model/HSLFMetroShape.java | 178 +-
.../textproperties/TabStopPropCollection.java | 246 +-
.../textproperties/WrapFlagsTextProp.java | 60 +-
.../poi/hslf/record/EscherPlaceholder.java | 150 +-
.../apache/poi/hslf/record/ExObjRefAtom.java | 188 +-
.../record/HSLFEscherClientDataRecord.java | 240 +-
.../hslf/record/HSLFEscherRecordFactory.java | 134 +-
.../poi/hslf/record/SSSlideInfoAtom.java | 578 +--
.../poi/hslf/record/TextSpecInfoRun.java | 692 +--
.../hslf/usermodel/HSLFConnectorShape.java | 130 +-
.../hslf/usermodel/HSLFShapeContainer.java | 98 +-
.../hslf/usermodel/HSLFSlideShowFactory.java | 84 +-
.../poi/hslf/usermodel/HSLFTextParagraph.java | 3298 +++++++-------
.../poi/hwmf/draw/HwmfDrawProperties.java | 692 +--
.../apache/poi/hwmf/draw/HwmfGraphics.java | 888 ++--
.../poi/hwmf/draw/HwmfSLImageRenderer.java | 248 +-
.../poi/hwmf/record/HwmfBinaryRasterOp.java | 224 +-
.../apache/poi/hwmf/record/HwmfBitmap16.java | 176 +-
.../apache/poi/hwmf/record/HwmfBitmapDib.java | 836 ++--
.../poi/hwmf/record/HwmfBrushStyle.java | 160 +-
.../apache/poi/hwmf/record/HwmfColorRef.java | 144 +-
.../org/apache/poi/hwmf/record/HwmfDraw.java | 1396 +++---
.../apache/poi/hwmf/record/HwmfEscape.java | 424 +-
.../org/apache/poi/hwmf/record/HwmfFill.java | 2020 ++++----
.../org/apache/poi/hwmf/record/HwmfFont.java | 1190 ++---
.../poi/hwmf/record/HwmfHatchStyle.java | 94 +-
.../apache/poi/hwmf/record/HwmfHeader.java | 154 +-
.../apache/poi/hwmf/record/HwmfMapMode.java | 226 +-
.../org/apache/poi/hwmf/record/HwmfMisc.java | 1160 ++---
.../poi/hwmf/record/HwmfObjectTableEntry.java | 56 +-
.../apache/poi/hwmf/record/HwmfPalette.java | 650 +--
.../apache/poi/hwmf/record/HwmfPenStyle.java | 378 +-
.../poi/hwmf/record/HwmfPlaceableHeader.java | 180 +-
.../apache/poi/hwmf/record/HwmfRecord.java | 86 +-
.../poi/hwmf/record/HwmfRecordType.java | 224 +-
.../poi/hwmf/record/HwmfTernaryRasterOp.java | 796 ++--
.../org/apache/poi/hwmf/record/HwmfText.java | 1122 ++---
.../apache/poi/hwmf/record/HwmfWindowing.java | 1420 +++---
.../poi/hwmf/usermodel/HwmfPicture.java | 344 +-
.../poi/hwpf/model/CharIndexTranslator.java | 188 +-
.../src/org/apache/poi/hwpf/model/FFData.java | 460 +-
.../org/apache/poi/hwpf/model/FFDataBase.java | 98 +-
.../poi/hwpf/model/NilPICFAndBinData.java | 158 +-
.../src/org/apache/poi/hwpf/model/Xstz.java | 174 +-
.../poi/hwpf/model/types/BKFAbstractType.java | 384 +-
.../model/types/FFDataBaseAbstractType.java | 856 ++--
.../poi/hwpf/model/types/FRDAbstractType.java | 182 +-
.../hwpf/model/types/FSPAAbstractType.java | 822 ++--
.../hwpf/model/types/FibBaseAbstractType.java | 1674 +++----
.../model/types/FibRgLw95AbstractType.java | 936 ++--
.../model/types/FibRgLw97AbstractType.java | 1372 +++---
.../model/types/FibRgW97AbstractType.java | 830 ++--
.../hwpf/model/types/GrfhicAbstractType.java | 614 +--
.../hwpf/model/types/HRESIAbstractType.java | 272 +-
.../poi/hwpf/model/types/LFOAbstractType.java | 580 +--
.../model/types/LFOLVLBaseAbstractType.java | 580 +--
.../hwpf/model/types/LSTFAbstractType.java | 758 +--
.../poi/hwpf/model/types/TLPAbstractType.java | 584 +--
.../org/apache/poi/hmef/TestBugs.java | 138 +-
.../hslf/record/TestDocumentEncryption.java | 390 +-
.../poi/hslf/usermodel/TestFontRendering.java | 242 +-
.../poi/hsmf/TestMessageSubmissionChunk.java | 116 +-
.../TestMessageSubmissionChunkY2KRead.java | 134 +-
.../org/apache/poi/hwmf/TestHwmfParsing.java | 502 +-
.../apache/poi/hwpf/HWPFTestDataSamples.java | 342 +-
.../org/apache/poi/hwpf/model/PlfLfoTest.java | 110 +-
.../org/apache/poi/hwpf/sprm/TestSprms.java | 200 +-
.../poi/hwpf/usermodel/TestBug47563.java | 170 +-
.../poi/hwpf/usermodel/TestBug49820.java | 124 +-
.../poi/hwpf/usermodel/TestBug50075.java | 82 +-
.../poi/hwpf/usermodel/TestRangeSymbols.java | 94 +-
.../org/apache/poi/POIDataSamples.java | 562 +--
.../poi/hssf/model/TestDrawingAggregate.java | 1892 ++++----
.../hssf/model/TestEscherRecordFactory.java | 198 +-
.../apache/poi/hssf/model/TestHSSFAnchor.java | 856 ++--
.../hssf/usermodel/TestEmbeddedObjects.java | 124 +-
.../poi/hssf/usermodel/TestHSSFHyperlink.java | 26 +-
.../poi/hssf/usermodel/TestPatriarch.java | 114 +-
.../poi/hssf/usermodel/TestPolygon.java | 552 +--
.../poi/hssf/usermodel/TestShapeGroup.java | 530 +--
.../apache/poi/hssf/usermodel/TestText.java | 428 +-
.../poi/poifs/crypt/TestXorEncryption.java | 150 +-
.../org/apache/poi/sl/TestCommonSL.java | 76 +-
.../poi/ss/formula/TestMissingWorkbook.java | 266 +-
.../functions/AllSpreadsheetBasedTests.java | 96 +-
.../BaseTestFunctionsFromSpreadsheet.java | 478 +-
.../functions/RefEvalImplementation.java | 136 +-
.../poi/ss/formula/functions/TestAddress.java | 158 +-
.../poi/ss/formula/functions/TestClean.java | 132 +-
.../poi/ss/formula/functions/TestCode.java | 118 +-
.../TestCodeFunctionsFromSpreadsheet.java | 62 +-
.../poi/ss/formula/functions/TestComplex.java | 136 +-
.../TestComplexFunctionsFromSpreadsheet.java | 62 +-
.../TestDeltaFunctionsFromSpreadsheet.java | 62 +-
.../poi/ss/formula/functions/TestEOMonth.java | 304 +-
...estFactDoubleFunctionsFromSpreadsheet.java | 64 +-
.../TestImRealFunctionsFromSpreadsheet.java | 64 +-
...TestImaginaryFunctionsFromSpreadsheet.java | 64 +-
.../TestIndirectFunctionFromSpreadsheet.java | 72 +-
.../functions/TestLogicalFunction.java | 214 +-
.../TestMatchFunctionsFromSpreadsheet.java | 72 +-
.../poi/ss/formula/functions/TestMirr.java | 336 +-
.../ss/formula/functions/TestQuotient.java | 126 +-
.../TestQuotientFunctionsFromSpreadsheet.java | 64 +-
.../TestReptFunctionsFromSpreadsheet.java | 62 +-
.../TestRomanFunctionsFromSpreadsheet.java | 64 +-
.../TestWeekNumFunctionsFromSpreadsheet.java | 64 +-
...stWeekNumFunctionsFromSpreadsheet2013.java | 66 +-
.../BaseTestConditionalFormatting.java | 2870 ++++++------
.../apache/poi/ss/util/BaseTestCellUtil.java | 832 ++--
502 files changed, 89558 insertions(+), 89558 deletions(-)
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/EmbeddedObjects.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/EmbeddedObjects.java
index 57df51c1af..4f0a707b2f 100644
--- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/EmbeddedObjects.java
+++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/EmbeddedObjects.java
@@ -1,67 +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.poi.hssf.usermodel.examples;
-
-import java.io.Closeable;
-import java.io.FileInputStream;
-
-import org.apache.poi.hslf.usermodel.HSLFSlideShow;
-import org.apache.poi.hssf.usermodel.HSSFObjectData;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hwpf.HWPFDocument;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.Entry;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-/**
- * Demonstrates how you can extract embedded data from a .xls file
- */
-public class EmbeddedObjects {
- @SuppressWarnings("unused")
- public static void main(String[] args) throws Exception {
- POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(args[0]));
- HSSFWorkbook workbook = new HSSFWorkbook(fs);
- for (HSSFObjectData obj : workbook.getAllEmbeddedObjects()) {
- //the OLE2 Class Name of the object
- String oleName = obj.getOLE2ClassName();
- DirectoryNode dn = (obj.hasDirectoryEntry()) ? (DirectoryNode) obj.getDirectory() : null;
- Closeable document = null;
- if (oleName.equals("Worksheet")) {
- document = new HSSFWorkbook(dn, fs, false);
- } else if (oleName.equals("Document")) {
- document = new HWPFDocument(dn);
- } else if (oleName.equals("Presentation")) {
- document = new HSLFSlideShow(dn);
- } else {
- if(dn != null){
- // The DirectoryEntry is a DocumentNode. Examine its entries to find out what it is
- for (Entry entry : dn) {
- String name = entry.getName();
- }
- } else {
- // There is no DirectoryEntry
- // Recover the object's data from the HSSFObjectData instance.
- byte[] objectData = obj.getObjectData();
- }
- }
- if (document != null) {
- document.close();
- }
- }
- workbook.close();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.hssf.usermodel.examples;
+
+import java.io.Closeable;
+import java.io.FileInputStream;
+
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hssf.usermodel.HSSFObjectData;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * Demonstrates how you can extract embedded data from a .xls file
+ */
+public class EmbeddedObjects {
+ @SuppressWarnings("unused")
+ public static void main(String[] args) throws Exception {
+ POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(args[0]));
+ HSSFWorkbook workbook = new HSSFWorkbook(fs);
+ for (HSSFObjectData obj : workbook.getAllEmbeddedObjects()) {
+ //the OLE2 Class Name of the object
+ String oleName = obj.getOLE2ClassName();
+ DirectoryNode dn = (obj.hasDirectoryEntry()) ? (DirectoryNode) obj.getDirectory() : null;
+ Closeable document = null;
+ if (oleName.equals("Worksheet")) {
+ document = new HSSFWorkbook(dn, fs, false);
+ } else if (oleName.equals("Document")) {
+ document = new HWPFDocument(dn);
+ } else if (oleName.equals("Presentation")) {
+ document = new HSLFSlideShow(dn);
+ } else {
+ if(dn != null){
+ // The DirectoryEntry is a DocumentNode. Examine its entries to find out what it is
+ for (Entry entry : dn) {
+ String name = entry.getName();
+ }
+ } else {
+ // There is no DirectoryEntry
+ // Recover the object's data from the HSSFObjectData instance.
+ byte[] objectData = obj.getObjectData();
+ }
+ }
+ if (document != null) {
+ document.close();
+ }
+ }
+ workbook.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/InCellLists.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/InCellLists.java
index e1d4cbcbf8..b250441110 100644
--- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/InCellLists.java
+++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/InCellLists.java
@@ -1,553 +1,553 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-
-package org.apache.poi.hssf.usermodel.examples;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFRow;
-import org.apache.poi.hssf.usermodel.HSSFCell;
-import org.apache.poi.hssf.usermodel.HSSFCellStyle;
-import org.apache.poi.hssf.usermodel.HSSFDataFormat;
-import org.apache.poi.hssf.usermodel.HSSFRichTextString;
-
-/**
- * This class contains code that demonstrates how to insert plain, numbered
- * and bulleted lists into an Excel spreadsheet cell.
- *
- * Look at the code contained in the demonstrateMethodCalls() method. It calls
- * other methods that create plain, numbered and bulleted single and
- * multi-level lists. The demonstrateMethodCalls() method appears at the top
- * of the class definition.
- *
- * Though different methods are provided to construct single and multi-level
- * plain, numbered and bulleted lists, close examination will reveal that they
- * are not strictly necessary. If the inputs to the listInCell() and
- * multilLevelListInCell() methods are constructed to include the bullet
- * character or the item numbers then these methods alone may be sufficient.
- *
- * @author Mark Beardsley [msb at apache.org]
- */
-public class InCellLists {
-
- // This character looks like a solid, black, loser case letter 'o'
- // positioned up from the base line of the text.
- private static final char BULLET_CHARACTER = '\u2022';
-
- // The tab character - \t - cannot be used to create a tab space
- // within a cell as it is rendered as a square. Therefore, four
- // spaces are used to simulate that character.
- private static final String TAB = " ";
-
- /**
- * Call each of the list creation methods.
- *
- * @param outputFilename A String that encapsulates the name of and path to
- * the Excel spreadsheet file this code will create.
- */
- public void demonstrateMethodCalls(String outputFilename) throws IOException {
- HSSFWorkbook workbook = new HSSFWorkbook();
- try {
- HSSFSheet sheet = workbook.createSheet("In Cell Lists");
- HSSFRow row = sheet.createRow(0);
-
- // Create a cell at A1 and insert a single, bulleted, item into
- // that cell.
- HSSFCell cell = row.createCell(0);
- this.bulletedItemInCell(workbook, "List Item", cell);
-
- // Create a cell at A2 and insert a plain list - that is one
- // whose items are neither bulleted or numbered - into that cell.
- row = sheet.createRow(1);
- cell = row.createCell(0);
- ArrayList listItems = new ArrayList();
- listItems.add("List Item One.");
- listItems.add("List Item Two.");
- listItems.add("List Item Three.");
- listItems.add("List Item Four.");
- this.listInCell(workbook, listItems, cell);
- // The row height and cell width are set here to ensure that the
- // list may be seen.
- row.setHeight((short)1100);
- sheet.setColumnWidth(0, 9500);
-
- // Create a cell at A3 and insert a numbered list into that cell.
- // Note that a couple of items have been added to the listItems
- // ArrayList
- row = sheet.createRow(2);
- cell = row.createCell(0);
- listItems.add("List Item Five.");
- listItems.add("List Item Six.");
- this.numberedListInCell(workbook, listItems, cell, 1, 2);
- row.setHeight((short)1550);
-
- // Create a cell at A4 and insert a numbered list into that cell.
- // Note that a couple of items have been added to the listItems
- // ArrayList
- row = sheet.createRow(3);
- cell = row.createCell(0);
- listItems.add("List Item Seven.");
- listItems.add("List Item Eight.");
- listItems.add("List Item Nine.");
- listItems.add("List Item Ten.");
- this.bulletedListInCell(workbook, listItems, cell);
- row.setHeight((short)2550);
-
- // Insert a plain, multi-level list into cell A5. Note that
- // the major difference here is that the list items are passed as
- // an ArrayList of MultiLevelListItems. Note that an ArrayList
- // of instances of an inner class was used here in preference to
- // a Hashtable or HashMap as the ArrayList will preserve the
- // ordering of the items added to it; the first item added will
- // be the first item recovered and the last item added, the last
- // item recovered. Alternatively, a LinkedHashMap could be used
- // to preserve order.
- row = sheet.createRow(4);
- cell = row.createCell(0);
- ArrayList multiLevelListItems = new ArrayList();
- listItems = new ArrayList();
- listItems.add("ML List Item One - Sub Item One.");
- listItems.add("ML List Item One - Sub Item Two.");
- listItems.add("ML List Item One - Sub Item Three.");
- listItems.add("ML List Item One - Sub Item Four.");
- multiLevelListItems.add(new MultiLevelListItem("List Item One.", listItems));
- // Passing either null or an empty ArrayList will signal that
- // there are no lower level items associated with the top level
- // item
- multiLevelListItems.add(new MultiLevelListItem("List Item Two.", null));
- multiLevelListItems.add(new MultiLevelListItem("List Item Three.", null));
- listItems = new ArrayList();
- listItems.add("ML List Item Four - Sub Item One.");
- listItems.add("ML List Item Four - Sub Item Two.");
- listItems.add("ML List Item Four - Sub Item Three.");
- multiLevelListItems.add(new MultiLevelListItem("List Item Four.", listItems));
- this.multiLevelListInCell(workbook, multiLevelListItems, cell);
- row.setHeight((short)2800);
-
- // Insert a numbered multi-level list into cell A6. Note that the
- // same ArrayList as constructed for the above plain multi-level
- // list example will be re-used
- row = sheet.createRow(5);
- cell = row.createCell(0);
- this.multiLevelNumberedListInCell(workbook, multiLevelListItems,
- cell, 1, 1, 1, 2);
- row.setHeight((short)2800);
-
- // Insert a numbered multi-level list into cell A7. Note that the
- // same ArrayList as constructed for the plain multi-level list
- // example will be re-used
- row = sheet.createRow(6);
- cell = row.createCell(0);
- this.multiLevelBulletedListInCell(workbook, multiLevelListItems, cell);
- row.setHeight((short)2800);
-
- // Save the completed workbook
- FileOutputStream fos = new FileOutputStream(new File(outputFilename));
- try {
- workbook.write(fos);
- } finally {
- fos.close();
- }
- }
- catch(FileNotFoundException fnfEx) {
- System.out.println("Caught a: " + fnfEx.getClass().getName());
- System.out.println("Message: " + fnfEx.getMessage());
- System.out.println("Stacktrace follows...........");
- fnfEx.printStackTrace(System.out);
- }
- catch(IOException ioEx) {
- System.out.println("Caught a: " + ioEx.getClass().getName());
- System.out.println("Message: " + ioEx.getMessage());
- System.out.println("Stacktrace follows...........");
- ioEx.printStackTrace(System.out);
- }
- finally {
- workbook.close();
- }
- }
-
- /**
- * Inserts a single bulleted item into a cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param listItem An instance of the String class encapsulating the
- * items text.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list item
- * will be written.
- */
- public void bulletedItemInCell(HSSFWorkbook workbook, String listItem, HSSFCell cell) {
- // A format String must be built to ensure that the contents of the
- // cell appear as a bulleted item.
- HSSFDataFormat format = workbook.createDataFormat();
- String formatString = InCellLists.BULLET_CHARACTER + " @";
- int formatIndex = format.getFormat(formatString);
-
- // Construct an HSSFCellStyle and set it's data formt to use the
- // object created above.
- HSSFCellStyle bulletStyle = workbook.createCellStyle();
- bulletStyle.setDataFormat((short)formatIndex);
-
- // Set the cells contents and style.
- cell.setCellValue(new HSSFRichTextString(listItem));
- cell.setCellStyle(bulletStyle);
- }
-
- /**
- * Inserts a list of plain items - that is items that are neither
- * numbered or bulleted - into a single cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param listItems An ArrayList whose elements encapsulate the text for
- * the list's items.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list
- * will be written.
- */
- public void listInCell(HSSFWorkbook workbook, ArrayList listItems, HSSFCell cell) {
- StringBuilder buffer = new StringBuilder();
- HSSFCellStyle wrapStyle = workbook.createCellStyle();
- wrapStyle.setWrapText(true);
- for(String listItem : listItems) {
- buffer.append(listItem);
- buffer.append("\n");
- }
- // The StringBuffer's contents are the source for the contents
- // of the cell.
- cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
- cell.setCellStyle(wrapStyle);
- }
-
- /**
- * Inserts a numbered list into a single cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param listItems An ArrayList whose elements encapsulate the text for
- * the lists items.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list
- * will be written.
- * @param startingValue A primitive int containing the number for the first
- * item in the list.
- * @param increment A primitive int containing the value that should be used
- * to calculate subsequent item numbers.
- */
- public void numberedListInCell(HSSFWorkbook workbook,
- ArrayList listItems,
- HSSFCell cell,
- int startingValue,
- int increment) {
- StringBuilder buffer = new StringBuilder();
- int itemNumber = startingValue;
- // Note that again, an HSSFCellStye object is required and that
- // it's wrap text property should be set to 'true'
- HSSFCellStyle wrapStyle = workbook.createCellStyle();
- wrapStyle.setWrapText(true);
- // Note that the basic method is identical to the listInCell() method
- // with one difference; a number prefixed to the items text.
- for(String listItem : listItems) {
- buffer.append(itemNumber).append(". ");
- buffer.append(listItem);
- buffer.append("\n");
- itemNumber += increment;
- }
- // The StringBuffer's contents are the source for the contents
- // of the cell.
- cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
- cell.setCellStyle(wrapStyle);
- }
-
- /**
- * Insert a bulleted list into a cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param listItems An ArrayList whose elements encapsulate the text for
- * the lists items.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list
- * will be written.
- */
- public void bulletedListInCell(HSSFWorkbook workbook,
- ArrayList listItems,
- HSSFCell cell) {
- StringBuilder buffer = new StringBuilder();
- // Note that again, an HSSFCellStye object is required and that
- // it's wrap text property should be set to 'true'
- HSSFCellStyle wrapStyle = workbook.createCellStyle();
- wrapStyle.setWrapText(true);
- // Note that the basic method is identical to the listInCell() method
- // with one difference; the bullet character prefixed to the items text.
- for(String listItem : listItems) {
- buffer.append(InCellLists.BULLET_CHARACTER + " ");
- buffer.append(listItem);
- buffer.append("\n");
- }
- // The StringBuffer's contents are the source for the contents
- // of the cell.
- cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
- cell.setCellStyle(wrapStyle);
- }
-
- /**
- * Insert a multi-level list into a cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param multiLevelListItems An ArrayList whose elements contain instances
- * of the MultiLevelListItem class. Each element
- * encapsulates the text for the high level item
- * along with an ArrayList. Each element of this
- * ArrayList encapsulates the text for a lower
- * level item.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list
- * will be written.
- */
- public void multiLevelListInCell(HSSFWorkbook workbook,
- ArrayList multiLevelListItems,
- HSSFCell cell) {
- StringBuilder buffer = new StringBuilder();
- // Note that again, an HSSFCellStye object is required and that
- // it's wrap text property should be set to 'true'
- HSSFCellStyle wrapStyle = workbook.createCellStyle();
- wrapStyle.setWrapText(true);
- // Step through the ArrayList of MultilLevelListItem instances.
- for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
- // For each element in the ArrayList, get the text for the high
- // level list item......
- buffer.append(multiLevelListItem.getItemText());
- buffer.append("\n");
- // and then an ArrayList whose elements encapsulate the text
- // for the lower level list items.
- ArrayList lowerLevelItems = multiLevelListItem.getLowerLevelItems();
- if(!(lowerLevelItems == null) && !(lowerLevelItems.isEmpty())) {
- for(String item : lowerLevelItems) {
- buffer.append(InCellLists.TAB);
- buffer.append(item);
- buffer.append("\n");
- }
- }
- }
- // The StringBuffer's contents are the source for the contents
- // of the cell.
- cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
- cell.setCellStyle(wrapStyle);
- }
-
- /**
- * Insert a multi-level list into a cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param multiLevelListItems An ArrayList whose elements contain instances
- * of the MultiLevelListItem class. Each element
- * encapsulates the text for the high level item
- * along with an ArrayList. Each element of this
- * ArrayList encapsulates the text for a lower
- * level item.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list
- * will be written.
- * @param highLevelStartingValue A primitive int containing the number
- * for the first high level item in the list.
- * @param highLevelIncrement A primitive int containing the value that
- * should be used to calculate the number of
- * subsequent high level item.
- * @param lowLevelStartingValue A primitive int will containing the number
- * for the first low level item associated
- * with a high level item.
- * @param lowLevelIncrement A primitive int containing the value that
- * should be used to calculate the number of
- * subsequent low level item.
- */
- public void multiLevelNumberedListInCell(HSSFWorkbook workbook,
- ArrayList multiLevelListItems,
- HSSFCell cell,
- int highLevelStartingValue,
- int highLevelIncrement,
- int lowLevelStartingValue,
- int lowLevelIncrement) {
- StringBuilder buffer = new StringBuilder();
- int highLevelItemNumber = highLevelStartingValue;
- // Note that again, an HSSFCellStye object is required and that
- // it's wrap text property should be set to 'true'
- HSSFCellStyle wrapStyle = workbook.createCellStyle();
- wrapStyle.setWrapText(true);
- // Step through the ArrayList of MultilLevelListItem instances.
- for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
- // For each element in the ArrayList, get the text for the high
- // level list item......
- buffer.append(highLevelItemNumber);
- buffer.append(". ");
- buffer.append(multiLevelListItem.getItemText());
- buffer.append("\n");
- // and then an ArrayList whose elements encapsulate the text
- // for the lower level list items.
- ArrayList lowerLevelItems = multiLevelListItem.getLowerLevelItems();
- if(!(lowerLevelItems == null) && !(lowerLevelItems.isEmpty())) {
- int lowLevelItemNumber = lowLevelStartingValue;
- for(String item : lowerLevelItems) {
- buffer.append(InCellLists.TAB);
- buffer.append(highLevelItemNumber);
- buffer.append(".");
- buffer.append(lowLevelItemNumber);
- buffer.append(" ");
- buffer.append(item);
- buffer.append("\n");
- lowLevelItemNumber += lowLevelIncrement;
- }
- }
- highLevelItemNumber += highLevelIncrement;
- }
- // The StringBuffer's contents are the source for the contents
- // of the cell.
- cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
- cell.setCellStyle(wrapStyle);
- }
-
- /**
- * Insert a bulleted multi-level list into a cell.
- *
- * @param workbook A reference to the HSSFWorkbook that 'contains' the
- * cell.
- * @param multiLevelListItems An ArrayList whose elements contain instances
- * of the MultiLevelListItem class. Each element
- * encapsulates the text for the high level item
- * along with an ArrayList. Each element of this
- * ArrayList encapsulates the text for a lower
- * level item.
- * @param cell An instance of the HSSFCell class that encapsulates a
- * reference to the spreadsheet cell into which the list
- * will be written.
- */
- public void multiLevelBulletedListInCell(HSSFWorkbook workbook,
- ArrayList multiLevelListItems,
- HSSFCell cell) {
- StringBuilder buffer = new StringBuilder();
- // Note that again, an HSSFCellStye object is required and that
- // it's wrap text property should be set to 'true'
- HSSFCellStyle wrapStyle = workbook.createCellStyle();
- wrapStyle.setWrapText(true);
- // Step through the ArrayList of MultilLevelListItem instances.
- for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
- // For each element in the ArrayList, get the text for the high
- // level list item......
- buffer.append(InCellLists.BULLET_CHARACTER);
- buffer.append(" ");
- buffer.append(multiLevelListItem.getItemText());
- buffer.append("\n");
- // and then an ArrayList whose elements encapsulate the text
- // for the lower level list items.
- ArrayList lowerLevelItems = multiLevelListItem.getLowerLevelItems();
- if(!(lowerLevelItems == null) && !(lowerLevelItems.isEmpty())) {
- for(String item : lowerLevelItems) {
- buffer.append(InCellLists.TAB);
- buffer.append(InCellLists.BULLET_CHARACTER);
- buffer.append(" ");
- buffer.append(item);
- buffer.append("\n");
- }
- }
- }
- // The StringBuffer's contents are the source for the contents
- // of the cell.
- cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
- cell.setCellStyle(wrapStyle);
- }
-
- /**
- * The main entry point to the program. Demonstrates how to call the method
- * that will create an Excel workbook containing many different sorts of
- * lists.
- *
- * @param args the command line arguments.
- */
- public static void main(String[] args) throws IOException {
- new InCellLists().demonstrateMethodCalls("Latest In Cell List.xls");
- }
-
- /**
- * An instance of this inner class models an item or element in a
- * multi-level list. Each multi-level list item consists of the text for the
- * high level items and an ArrayList containing the text for each of the
- * associated lower level items. When written into a cell, each multi-level
- * list item will have this general appearance.
- *
- * Item One
- * Sub Item One.
- * Sub Item Two.
- * Item Two
- * Sub Item One.
- * Sub Item Two.
- * etc.
- *
- * It would be quite possible to modify this class to model much more
- * complex list structures descending through two, three or even more
- * levels.
- */
- public final class MultiLevelListItem {
-
- private String itemText = null;
- private ArrayList lowerLevelItems = null;
-
- /**
- * Create a new instance of the MultiLevelListItem class using the
- * following parameters.
- *
- * @param itemText A String that encapsulates the text for the high
- * level list item.
- * @param lowerLevelItems An ArrayList whose elements encapsulate the
- * text for the associated lower level list
- * items.
- */
- public MultiLevelListItem(String itemText, ArrayList lowerLevelItems) {
- this.itemText = itemText;
- this.lowerLevelItems = lowerLevelItems;
- }
-
- /**
- * Get the text for the high level list item.
- *
- * @return A String that encapsulates the text for the high level list
- * item.
- */
- public String getItemText() {
- return(this.itemText);
- }
-
- /**
- * Get the text for the associated lower level list items.
- *
- * @return An ArrayList whose elements each encapsulate the text for a
- * single associated lower level list item.
- */
- public ArrayList getLowerLevelItems() {
- return(this.lowerLevelItems);
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.hssf.usermodel.examples;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+
+/**
+ * This class contains code that demonstrates how to insert plain, numbered
+ * and bulleted lists into an Excel spreadsheet cell.
+ *
+ * Look at the code contained in the demonstrateMethodCalls() method. It calls
+ * other methods that create plain, numbered and bulleted single and
+ * multi-level lists. The demonstrateMethodCalls() method appears at the top
+ * of the class definition.
+ *
+ * Though different methods are provided to construct single and multi-level
+ * plain, numbered and bulleted lists, close examination will reveal that they
+ * are not strictly necessary. If the inputs to the listInCell() and
+ * multilLevelListInCell() methods are constructed to include the bullet
+ * character or the item numbers then these methods alone may be sufficient.
+ *
+ * @author Mark Beardsley [msb at apache.org]
+ */
+public class InCellLists {
+
+ // This character looks like a solid, black, loser case letter 'o'
+ // positioned up from the base line of the text.
+ private static final char BULLET_CHARACTER = '\u2022';
+
+ // The tab character - \t - cannot be used to create a tab space
+ // within a cell as it is rendered as a square. Therefore, four
+ // spaces are used to simulate that character.
+ private static final String TAB = " ";
+
+ /**
+ * Call each of the list creation methods.
+ *
+ * @param outputFilename A String that encapsulates the name of and path to
+ * the Excel spreadsheet file this code will create.
+ */
+ public void demonstrateMethodCalls(String outputFilename) throws IOException {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ try {
+ HSSFSheet sheet = workbook.createSheet("In Cell Lists");
+ HSSFRow row = sheet.createRow(0);
+
+ // Create a cell at A1 and insert a single, bulleted, item into
+ // that cell.
+ HSSFCell cell = row.createCell(0);
+ this.bulletedItemInCell(workbook, "List Item", cell);
+
+ // Create a cell at A2 and insert a plain list - that is one
+ // whose items are neither bulleted or numbered - into that cell.
+ row = sheet.createRow(1);
+ cell = row.createCell(0);
+ ArrayList listItems = new ArrayList();
+ listItems.add("List Item One.");
+ listItems.add("List Item Two.");
+ listItems.add("List Item Three.");
+ listItems.add("List Item Four.");
+ this.listInCell(workbook, listItems, cell);
+ // The row height and cell width are set here to ensure that the
+ // list may be seen.
+ row.setHeight((short)1100);
+ sheet.setColumnWidth(0, 9500);
+
+ // Create a cell at A3 and insert a numbered list into that cell.
+ // Note that a couple of items have been added to the listItems
+ // ArrayList
+ row = sheet.createRow(2);
+ cell = row.createCell(0);
+ listItems.add("List Item Five.");
+ listItems.add("List Item Six.");
+ this.numberedListInCell(workbook, listItems, cell, 1, 2);
+ row.setHeight((short)1550);
+
+ // Create a cell at A4 and insert a numbered list into that cell.
+ // Note that a couple of items have been added to the listItems
+ // ArrayList
+ row = sheet.createRow(3);
+ cell = row.createCell(0);
+ listItems.add("List Item Seven.");
+ listItems.add("List Item Eight.");
+ listItems.add("List Item Nine.");
+ listItems.add("List Item Ten.");
+ this.bulletedListInCell(workbook, listItems, cell);
+ row.setHeight((short)2550);
+
+ // Insert a plain, multi-level list into cell A5. Note that
+ // the major difference here is that the list items are passed as
+ // an ArrayList of MultiLevelListItems. Note that an ArrayList
+ // of instances of an inner class was used here in preference to
+ // a Hashtable or HashMap as the ArrayList will preserve the
+ // ordering of the items added to it; the first item added will
+ // be the first item recovered and the last item added, the last
+ // item recovered. Alternatively, a LinkedHashMap could be used
+ // to preserve order.
+ row = sheet.createRow(4);
+ cell = row.createCell(0);
+ ArrayList multiLevelListItems = new ArrayList();
+ listItems = new ArrayList();
+ listItems.add("ML List Item One - Sub Item One.");
+ listItems.add("ML List Item One - Sub Item Two.");
+ listItems.add("ML List Item One - Sub Item Three.");
+ listItems.add("ML List Item One - Sub Item Four.");
+ multiLevelListItems.add(new MultiLevelListItem("List Item One.", listItems));
+ // Passing either null or an empty ArrayList will signal that
+ // there are no lower level items associated with the top level
+ // item
+ multiLevelListItems.add(new MultiLevelListItem("List Item Two.", null));
+ multiLevelListItems.add(new MultiLevelListItem("List Item Three.", null));
+ listItems = new ArrayList();
+ listItems.add("ML List Item Four - Sub Item One.");
+ listItems.add("ML List Item Four - Sub Item Two.");
+ listItems.add("ML List Item Four - Sub Item Three.");
+ multiLevelListItems.add(new MultiLevelListItem("List Item Four.", listItems));
+ this.multiLevelListInCell(workbook, multiLevelListItems, cell);
+ row.setHeight((short)2800);
+
+ // Insert a numbered multi-level list into cell A6. Note that the
+ // same ArrayList as constructed for the above plain multi-level
+ // list example will be re-used
+ row = sheet.createRow(5);
+ cell = row.createCell(0);
+ this.multiLevelNumberedListInCell(workbook, multiLevelListItems,
+ cell, 1, 1, 1, 2);
+ row.setHeight((short)2800);
+
+ // Insert a numbered multi-level list into cell A7. Note that the
+ // same ArrayList as constructed for the plain multi-level list
+ // example will be re-used
+ row = sheet.createRow(6);
+ cell = row.createCell(0);
+ this.multiLevelBulletedListInCell(workbook, multiLevelListItems, cell);
+ row.setHeight((short)2800);
+
+ // Save the completed workbook
+ FileOutputStream fos = new FileOutputStream(new File(outputFilename));
+ try {
+ workbook.write(fos);
+ } finally {
+ fos.close();
+ }
+ }
+ catch(FileNotFoundException fnfEx) {
+ System.out.println("Caught a: " + fnfEx.getClass().getName());
+ System.out.println("Message: " + fnfEx.getMessage());
+ System.out.println("Stacktrace follows...........");
+ fnfEx.printStackTrace(System.out);
+ }
+ catch(IOException ioEx) {
+ System.out.println("Caught a: " + ioEx.getClass().getName());
+ System.out.println("Message: " + ioEx.getMessage());
+ System.out.println("Stacktrace follows...........");
+ ioEx.printStackTrace(System.out);
+ }
+ finally {
+ workbook.close();
+ }
+ }
+
+ /**
+ * Inserts a single bulleted item into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItem An instance of the String class encapsulating the
+ * items text.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list item
+ * will be written.
+ */
+ public void bulletedItemInCell(HSSFWorkbook workbook, String listItem, HSSFCell cell) {
+ // A format String must be built to ensure that the contents of the
+ // cell appear as a bulleted item.
+ HSSFDataFormat format = workbook.createDataFormat();
+ String formatString = InCellLists.BULLET_CHARACTER + " @";
+ int formatIndex = format.getFormat(formatString);
+
+ // Construct an HSSFCellStyle and set it's data formt to use the
+ // object created above.
+ HSSFCellStyle bulletStyle = workbook.createCellStyle();
+ bulletStyle.setDataFormat((short)formatIndex);
+
+ // Set the cells contents and style.
+ cell.setCellValue(new HSSFRichTextString(listItem));
+ cell.setCellStyle(bulletStyle);
+ }
+
+ /**
+ * Inserts a list of plain items - that is items that are neither
+ * numbered or bulleted - into a single cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItems An ArrayList whose elements encapsulate the text for
+ * the list's items.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void listInCell(HSSFWorkbook workbook, ArrayList listItems, HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ for(String listItem : listItems) {
+ buffer.append(listItem);
+ buffer.append("\n");
+ }
+ // The StringBuffer's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Inserts a numbered list into a single cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItems An ArrayList whose elements encapsulate the text for
+ * the lists items.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ * @param startingValue A primitive int containing the number for the first
+ * item in the list.
+ * @param increment A primitive int containing the value that should be used
+ * to calculate subsequent item numbers.
+ */
+ public void numberedListInCell(HSSFWorkbook workbook,
+ ArrayList listItems,
+ HSSFCell cell,
+ int startingValue,
+ int increment) {
+ StringBuilder buffer = new StringBuilder();
+ int itemNumber = startingValue;
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Note that the basic method is identical to the listInCell() method
+ // with one difference; a number prefixed to the items text.
+ for(String listItem : listItems) {
+ buffer.append(itemNumber).append(". ");
+ buffer.append(listItem);
+ buffer.append("\n");
+ itemNumber += increment;
+ }
+ // The StringBuffer's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a bulleted list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItems An ArrayList whose elements encapsulate the text for
+ * the lists items.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void bulletedListInCell(HSSFWorkbook workbook,
+ ArrayList listItems,
+ HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Note that the basic method is identical to the listInCell() method
+ // with one difference; the bullet character prefixed to the items text.
+ for(String listItem : listItems) {
+ buffer.append(InCellLists.BULLET_CHARACTER + " ");
+ buffer.append(listItem);
+ buffer.append("\n");
+ }
+ // The StringBuffer's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a multi-level list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param multiLevelListItems An ArrayList whose elements contain instances
+ * of the MultiLevelListItem class. Each element
+ * encapsulates the text for the high level item
+ * along with an ArrayList. Each element of this
+ * ArrayList encapsulates the text for a lower
+ * level item.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void multiLevelListInCell(HSSFWorkbook workbook,
+ ArrayList multiLevelListItems,
+ HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Step through the ArrayList of MultilLevelListItem instances.
+ for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
+ // For each element in the ArrayList, get the text for the high
+ // level list item......
+ buffer.append(multiLevelListItem.getItemText());
+ buffer.append("\n");
+ // and then an ArrayList whose elements encapsulate the text
+ // for the lower level list items.
+ ArrayList lowerLevelItems = multiLevelListItem.getLowerLevelItems();
+ if(!(lowerLevelItems == null) && !(lowerLevelItems.isEmpty())) {
+ for(String item : lowerLevelItems) {
+ buffer.append(InCellLists.TAB);
+ buffer.append(item);
+ buffer.append("\n");
+ }
+ }
+ }
+ // The StringBuffer's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a multi-level list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param multiLevelListItems An ArrayList whose elements contain instances
+ * of the MultiLevelListItem class. Each element
+ * encapsulates the text for the high level item
+ * along with an ArrayList. Each element of this
+ * ArrayList encapsulates the text for a lower
+ * level item.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ * @param highLevelStartingValue A primitive int containing the number
+ * for the first high level item in the list.
+ * @param highLevelIncrement A primitive int containing the value that
+ * should be used to calculate the number of
+ * subsequent high level item.
+ * @param lowLevelStartingValue A primitive int will containing the number
+ * for the first low level item associated
+ * with a high level item.
+ * @param lowLevelIncrement A primitive int containing the value that
+ * should be used to calculate the number of
+ * subsequent low level item.
+ */
+ public void multiLevelNumberedListInCell(HSSFWorkbook workbook,
+ ArrayList multiLevelListItems,
+ HSSFCell cell,
+ int highLevelStartingValue,
+ int highLevelIncrement,
+ int lowLevelStartingValue,
+ int lowLevelIncrement) {
+ StringBuilder buffer = new StringBuilder();
+ int highLevelItemNumber = highLevelStartingValue;
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Step through the ArrayList of MultilLevelListItem instances.
+ for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
+ // For each element in the ArrayList, get the text for the high
+ // level list item......
+ buffer.append(highLevelItemNumber);
+ buffer.append(". ");
+ buffer.append(multiLevelListItem.getItemText());
+ buffer.append("\n");
+ // and then an ArrayList whose elements encapsulate the text
+ // for the lower level list items.
+ ArrayList lowerLevelItems = multiLevelListItem.getLowerLevelItems();
+ if(!(lowerLevelItems == null) && !(lowerLevelItems.isEmpty())) {
+ int lowLevelItemNumber = lowLevelStartingValue;
+ for(String item : lowerLevelItems) {
+ buffer.append(InCellLists.TAB);
+ buffer.append(highLevelItemNumber);
+ buffer.append(".");
+ buffer.append(lowLevelItemNumber);
+ buffer.append(" ");
+ buffer.append(item);
+ buffer.append("\n");
+ lowLevelItemNumber += lowLevelIncrement;
+ }
+ }
+ highLevelItemNumber += highLevelIncrement;
+ }
+ // The StringBuffer's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a bulleted multi-level list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param multiLevelListItems An ArrayList whose elements contain instances
+ * of the MultiLevelListItem class. Each element
+ * encapsulates the text for the high level item
+ * along with an ArrayList. Each element of this
+ * ArrayList encapsulates the text for a lower
+ * level item.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void multiLevelBulletedListInCell(HSSFWorkbook workbook,
+ ArrayList multiLevelListItems,
+ HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Step through the ArrayList of MultilLevelListItem instances.
+ for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
+ // For each element in the ArrayList, get the text for the high
+ // level list item......
+ buffer.append(InCellLists.BULLET_CHARACTER);
+ buffer.append(" ");
+ buffer.append(multiLevelListItem.getItemText());
+ buffer.append("\n");
+ // and then an ArrayList whose elements encapsulate the text
+ // for the lower level list items.
+ ArrayList lowerLevelItems = multiLevelListItem.getLowerLevelItems();
+ if(!(lowerLevelItems == null) && !(lowerLevelItems.isEmpty())) {
+ for(String item : lowerLevelItems) {
+ buffer.append(InCellLists.TAB);
+ buffer.append(InCellLists.BULLET_CHARACTER);
+ buffer.append(" ");
+ buffer.append(item);
+ buffer.append("\n");
+ }
+ }
+ }
+ // The StringBuffer's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * The main entry point to the program. Demonstrates how to call the method
+ * that will create an Excel workbook containing many different sorts of
+ * lists.
+ *
+ * @param args the command line arguments.
+ */
+ public static void main(String[] args) throws IOException {
+ new InCellLists().demonstrateMethodCalls("Latest In Cell List.xls");
+ }
+
+ /**
+ * An instance of this inner class models an item or element in a
+ * multi-level list. Each multi-level list item consists of the text for the
+ * high level items and an ArrayList containing the text for each of the
+ * associated lower level items. When written into a cell, each multi-level
+ * list item will have this general appearance.
+ *
+ * Item One
+ * Sub Item One.
+ * Sub Item Two.
+ * Item Two
+ * Sub Item One.
+ * Sub Item Two.
+ * etc.
+ *
+ * It would be quite possible to modify this class to model much more
+ * complex list structures descending through two, three or even more
+ * levels.
+ */
+ public final class MultiLevelListItem {
+
+ private String itemText = null;
+ private ArrayList lowerLevelItems = null;
+
+ /**
+ * Create a new instance of the MultiLevelListItem class using the
+ * following parameters.
+ *
+ * @param itemText A String that encapsulates the text for the high
+ * level list item.
+ * @param lowerLevelItems An ArrayList whose elements encapsulate the
+ * text for the associated lower level list
+ * items.
+ */
+ public MultiLevelListItem(String itemText, ArrayList lowerLevelItems) {
+ this.itemText = itemText;
+ this.lowerLevelItems = lowerLevelItems;
+ }
+
+ /**
+ * Get the text for the high level list item.
+ *
+ * @return A String that encapsulates the text for the high level list
+ * item.
+ */
+ public String getItemText() {
+ return(this.itemText);
+ }
+
+ /**
+ * Get the text for the associated lower level list items.
+ *
+ * @return An ArrayList whose elements each encapsulate the text for a
+ * single associated lower level list item.
+ */
+ public ArrayList getLowerLevelItems() {
+ return(this.lowerLevelItems);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java b/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java
index 227216df0d..4e644cda8b 100644
--- a/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java
+++ b/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java
@@ -1,1025 +1,1025 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-
-package org.apache.poi.ss.examples;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Locale;
-
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.ss.usermodel.ClientAnchor;
-import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
-import org.apache.poi.ss.usermodel.Drawing;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.util.IOUtils;
-
-
-/**
- * Demonstrates how to add an image to a worksheet and set that images size
- * to a specific number of millimetres irrespective of the width of the columns
- * or height of the rows. Overridden methods are provided so that the location
- * of the image - the cells row and column coordinates that define the top
- * left hand corners of the image - can be identified either in the familiar
- * Excel manner - A1 for instance - or using POI's methodology of a column and
- * row index where 0, 0 would indicate cell A1.
- *
- * The best way to make use of these techniques is to delay adding the image to
- * the sheet until all other work has been completed. That way, the sizes of
- * all rows and columns will have been adjusted - assuming that step was
- * necessary. Even though the anchors type is set to prevent the image moving
- * or re-sizing, this setting does not have any effect until the sheet is being
- * viewed using the Excel application.
- *
- * The key to the process is the ClientAnchor class. It defines methods that allow
- * us to define the location of an image by specifying the following;
- *
- * * How far - in terms of coordinate positions - the image should be inset
- * from the left hand border of a cell.
- * * How far - in terms of coordinate positions - the image should be inset
- * from the from the top of the cell.
- * * How far - in terms of coordinate positions - the right hand edge of
- * the image should protrude into a cell (measured from the cells left hand
- * edge to the images right hand edge).
- * * How far - in terms of coordinate positions - the bottom edge of the
- * image should protrude into a row (measured from the cells top edge to
- * the images bottom edge).
- * * The index of the column that contains the cell whose top left hand
- * corner should be aligned with the top left hand corner of the image.
- * * The index of the row that contains the cell whose top left hand corner
- * should be aligned with the images top left hand corner.
- * * The index of the column that contains the cell whose top left hand
- * corner should be aligned with the images bottom right hand corner
- * * The index number of the row that contains the cell whose top left
- * hand corner should be aligned with the images bottom right hand corner.
- *
- * It can be used to add an image into cell A1, for example, in the following
- * manner;
- *
- * ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
- *
- * anchor.setDx1(0);
- * anchor.setDy1(0);
- * anchor.setDx2(0);
- * anchor.setDy2(0);
- * anchor.setCol1(0);
- * anchor.setRow1(0);
- * anchor.setCol2(1);
- * anchor.setRow2(1);
- *
- * Taken together, the first four methods define the locations of the top left
- * and bottom right hand corners of the image if you imagine that the image is
- * represented by a simple rectangle. The setDx1() and setDy1() methods locate
- * the top left hand corner of the image while setDx2() and and Dy2() locate the
- * bottom right hand corner of the image. An individual image can be inserted
- * into a single cell or is can lie across many cells and the latter four methods
- * are used to define just where the image should be positioned. They do this by
- * again by identifying where the top left and bottom right hand corners of the
- * image should be located but this time in terms of the indexes of the cells
- * in which those corners should be located. The setCol1() and setRow1() methods
- * together identify the cell that should contain the top left hand corner of
- * the image while setCol2() and setRow2() do the same for the images bottom
- * right hand corner.
- *
- * Knowing that, it is possible to look again at the example above and to see
- * that the top left hand corner of the image will be located in cell A1 (0, 0)
- * and it will be aligned with the very top left hand corner of the cell. Likewise,
- * the bottom right hand corner of the image will be located in cell B2 (1, 1) and
- * it will again be aligned with the top left hand corner of the cell. This has the
- * effect of making the image seem to occupy the whole of cell A1. Interestingly, it
- * also has an effect on the images resizing behaviour because testing has
- * demonstrated that if the image is wholly contained within one cell and is not
- * 'attached' for want of a better word, to a neighbouring cell, then that image
- * will not increase in size in response to the user dragging the column wider
- * or the cell higher.
- *
- * The following example demonstrates a slightly different way to insert an
- * image into cell A1 and to ensure that it occupies the whole of the cell. This
- * is accomplished by specifying the the images bottom right hand corner should be
- * aligned with the bottom right hand corner of the cell. It is also a case
- * where the image will not increase in size if the user increases the size of
- * the enclosing cell - irrespective of the anchors type - but it will reduce in
- * size if the cell is made smaller.
- *
- * ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
- *
- * anchor.setDx1(0);
- * anchor.setDy1(0);
- * anchor.setDx2(1023);
- * anchor.setDy2(255);
- * anchor.setCol1(0);
- * anchor.setRow1(0);
- * anchor.setCol2(0);
- * anchor.setRow2(0);
- *
- * Note that the final four method calls all pass the same value and seem to
- * indicate that the images top left hand corner is aligned with the top left
- * hand corner of cell A1 and that it's bottom right hand corner is also
- * aligned with the top left hand corner of cell A1. Yet, running this code
- * would see the image fully occupying cell A1. That is the result of the
- * values passed to parameters three and four; these I have referred to as
- * determining the images coordinates within the cell. They indicate that the
- * image should occupy - in order - the full width of the column and the full
- * height of the row.
- *
- * The co-ordinate values shown are the maxima; and they are independent of
- * row height/column width and of the font used. Passing 255 will always result
- * in the image occupying the full height of the row and passing 1023 will
- * always result in the image occupying the full width of the column. They help
- * in situations where an image is larger than a column/row and must overlap
- * into the next column/row. Using them does mean, however, that it is often
- * necessary to perform conversions between Excels characters units, points,
- * pixels and millimetres in order to establish how many rows/columns an image
- * should occupy and just what the various insets ought to be.
- *
- * Note that the setDx1(int) and setDy1(int) methods of the ClientAchor class
- * are not made use of in the code that follows. It would be fairly trivial
- * however to extend this example further and provide methods that would centre
- * an image within a cell or allow the user to specify that a plain border a
- * fixed number of millimetres wide should wrap around the image. Those first
- * two parameters would make this sort of functionality perfectly possible.
- *
- * Owing to the various conversions used, the actual size of the image may vary
- * from that required; testing has so far found this to be in the region of
- * plus or minus two millimetres. Most likely by modifying the way the
- * calculations are performed - possibly using double(s) throughout and
- * rounding the values at the correct point - it is likely that these errors
- * could be reduced or removed.
- *
- * A note concerning Excels image resizing behaviour. The ClientAnchor
- * class contains a method called setAnchorType(int) which can be used to
- * determine how Excel will resize an image in response to the user increasing
- * or decreasing the dimensions of the cell containing the image. There are
- * three values that can be passed to this method; 0 = To move and size the
- * image with the cell, 2 = To move but don't size the image with the cell,
- * 3 = To prevent the image from moving or being resized along with the cell. If
- * an image is inserted using this class and placed into a single cell then if
- * the setAnchorType(int) method is called and a value of either 0 or 2 passed
- * to it, the resultant resizing behaviour may be a surprise. The image will not
- * grow in size of the column is made wider or the row higher but it will shrink
- * if the columns width or rows height are reduced.
- *
- * @author Mark Beardsley [msb at apache.org] and Mark Southern [southern at scripps.edu]
- * @version 1.00 5th August 2009.
- * 2.00 26th February 2010.
- * Ported to make use of the the SS usermodel classes.
- * Ability to reuse the Drawing Patriarch so that multiple images
- * can be inserted without unintentionally erasing earlier images.
- * Check on image type added; i.e. jpg, jpeg or png.
- * The String used to contain the files name is now converted
- * into a URL.
- * 2.10 17th May 2012
- * Corrected gross error that occurred when using the code with
- * XSSF or SXSSF workbooks. In short, the code did not correctly
- * calculate the size of the image(s) owing to the use of EMUs
- * within the OOXML file format. That problem has largely been
- * corrected although it should be mentioned that images are not
- * sized with the same level of accuracy. Discrepancies of up to
- * 2mm have been noted in testing. Further investigation will
- * continue to rectify this issue.
- */
-public class AddDimensionedImage {
-
- // Four constants that determine how - and indeed whether - the rows
- // and columns an image may overlie should be expanded to accomodate that
- // image.
- // Passing EXPAND_ROW will result in the height of a row being increased
- // to accomodate the image if it is not already larger. The image will
- // be layed across one or more columns.
- // Passing EXPAND_COLUMN will result in the width of the column being
- // increased to accomodate the image if it is not already larger. The image
- // will be layed across one or many rows.
- // Passing EXPAND_ROW_AND_COLUMN will result in the height of the row
- // bing increased along with the width of the column to accomdate the
- // image if either is not already larger.
- // Passing OVERLAY_ROW_AND_COLUMN will result in the image being layed
- // over one or more rows and columns. No row or column will be resized,
- // instead, code will determine how many rows and columns the image should
- // overlie.
- public static final int EXPAND_ROW = 1;
- public static final int EXPAND_COLUMN = 2;
- public static final int EXPAND_ROW_AND_COLUMN = 3;
- public static final int OVERLAY_ROW_AND_COLUMN = 7;
-
- // Modified to support EMU - English Metric Units - used within the OOXML
- // workbooks, this multoplier is used to convert between measurements in
- // millimetres and in EMUs
- private static final int EMU_PER_MM = 36000;
-
- /**
- * Add an image to a worksheet.
- *
- * @param cellNumber A String that contains the location of the cell whose
- * top left hand corner should be aligned with the top
- * left hand corner of the image; for example "A1", "A2"
- * etc. This is to support the familiar Excel syntax.
- * Whilst images are are not actually inserted into cells
- * this provides a convenient method of indicating where
- * the image should be positioned on the sheet.
- * @param sheet A reference to the sheet that contains the cell referenced
- * above.
- * @param drawing An instance of the DrawingPatriarch class. This is now
- * passed into the method where it was, previously, recovered
- * from the sheet in order to allow multiple pictures be
- * inserted. If the patriarch was not 'cached in this manner
- * each time it was created any previously positioned images
- * would be simply over-written.
- * @param imageFile An instance of the URL class that encapsulates the name
- * of and path to the image that is to be 'inserted into'
- * the sheet.
- * @param reqImageWidthMM A primitive double that contains the required
- * width of the image in millimetres.
- * @param reqImageHeightMM A primitive double that contains the required
- * height of the image in millimetres.
- * @param resizeBehaviour A primitive int whose value will determine how
- * the code should react if the image is larger than
- * the cell referenced by the cellNumber parameter.
- * Four constants are provided to determine what
- * should happen;
- * AddDimensionedImage.EXPAND_ROW
- * AddDimensionedImage.EXPAND_COLUMN
- * AddDimensionedImage.EXPAND_ROW_AND_COLUMN
- * AddDimensionedImage.OVERLAY_ROW_AND_COLUMN
- * @throws java.io.FileNotFoundException If the file containing the image
- * cannot be located.
- * @throws java.io.IOException If a problem occurs whilst reading the file
- * of image data.
- * @throws java.lang.IllegalArgumentException If an invalid value is passed
- * to the resizeBehaviour
- * parameter.
- */
- public void addImageToSheet(String cellNumber, Sheet sheet, Drawing> drawing,
- URL imageFile, double reqImageWidthMM, double reqImageHeightMM,
- int resizeBehaviour) throws IOException, IllegalArgumentException {
- // Convert the String into column and row indices then chain the
- // call to the overridden addImageToSheet() method.
- CellReference cellRef = new CellReference(cellNumber);
- this.addImageToSheet(cellRef.getCol(), cellRef.getRow(), sheet, drawing,
- imageFile, reqImageWidthMM, reqImageHeightMM,resizeBehaviour);
- }
-
- /**
- * Add an image to a worksheet.
- *
- * @param colNumber A primitive int that contains the index number of a
- * column on the worksheet; POI column indices are zero
- * based. Together with the rowNumber parameter's value,
- * this parameter identifies a cell on the worksheet. The
- * images top left hand corner will be aligned with the
- * top left hand corner of this cell.
- * @param rowNumber A primitive int that contains the index number of a row
- * on the worksheet; POI row indices are zero based.
- * Together with the rowNumber parameter's value, this
- * parameter identifies a cell on the worksheet. The
- * images top left hand corner will be aligned with the
- * top left hand corner of this cell.
- * @param sheet A reference to the sheet that contains the cell identified
- * by the two parameters above.
- * @param drawing An instance of the DrawingPatriarch class. This is now
- * passed into the method where it was, previously, recovered
- * from the sheet in order to allow multiple pictures be
- * inserted. If the patriarch was not 'cached in this manner
- * each time it was created any previously positioned images
- * would be simply over-written.
- * @param imageFile An instance of the URL class that encapsulates the name
- * of and path to the image that is to be 'inserted into'
- * the sheet.
- * @param reqImageWidthMM A primitive double that contains the required
- * width of the image in millimetres.
- * @param reqImageHeightMM A primitive double that contains the required
- * height of the image in millimetres.
- * @param resizeBehaviour A primitive int whose value will determine how
- * the code should react if the image is larger than
- * the cell referenced by the colNumber and
- * rowNumber parameters. Four constants are provided
- * to determine what should happen;
- * AddDimensionedImage.EXPAND_ROW
- * AddDimensionedImage.EXPAND_COLUMN
- * AddDimensionedImage.EXPAND_ROW_AND_COLUMN
- * AddDimensionedImage.OVERLAY_ROW_AND_COLUMN
- * @throws java.io.FileNotFoundException If the file containing the image
- * cannot be located.
- * @throws java.io.IOException If a problem occurs whilst reading the file
- * of image data.
- * @throws java.lang.IllegalArgumentException If an invalid value is passed
- * to the resizeBehaviour
- * parameter or if the extension
- * of the image file indicates that
- * it is of a type that cannot
- * currently be added to the worksheet.
- */
- public void addImageToSheet(int colNumber, int rowNumber, Sheet sheet, Drawing> drawing,
- URL imageFile, double reqImageWidthMM, double reqImageHeightMM,
- int resizeBehaviour) throws IOException,
- IllegalArgumentException {
- ClientAnchor anchor = null;
- ClientAnchorDetail rowClientAnchorDetail = null;
- ClientAnchorDetail colClientAnchorDetail = null;
- int imageType = 0;
-
- // Validate the resizeBehaviour parameter.
- if((resizeBehaviour != AddDimensionedImage.EXPAND_COLUMN) &&
- (resizeBehaviour != AddDimensionedImage.EXPAND_ROW) &&
- (resizeBehaviour != AddDimensionedImage.EXPAND_ROW_AND_COLUMN) &&
- (resizeBehaviour != AddDimensionedImage.OVERLAY_ROW_AND_COLUMN)) {
- throw new IllegalArgumentException("Invalid value passed to the " +
- "resizeBehaviour parameter of AddDimensionedImage.addImageToSheet()");
- }
-
- // Call methods to calculate how the image and sheet should be
- // manipulated to accomodate the image; columns and then rows.
- colClientAnchorDetail = this.fitImageToColumns(sheet, colNumber,
- reqImageWidthMM, resizeBehaviour);
- rowClientAnchorDetail = this.fitImageToRows(sheet, rowNumber,
- reqImageHeightMM, resizeBehaviour);
-
- // Having determined if and how to resize the rows, columns and/or the
- // image, create the ClientAnchor object to position the image on
- // the worksheet. Note how the two ClientAnchorDetail records are
- // interrogated to recover the row/column co-ordinates and any insets.
- // The first two parameters are not used currently but could be if the
- // need arose to extend the functionality of this code by adding the
- // ability to specify that a clear 'border' be placed around the image.
- anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
-
- anchor.setDx1(0);
- anchor.setDy1(0);
- anchor.setDx2(colClientAnchorDetail.getInset());
- anchor.setDy2(rowClientAnchorDetail.getInset());
- anchor.setCol1(colClientAnchorDetail.getFromIndex());
- anchor.setRow1(rowClientAnchorDetail.getFromIndex());
- anchor.setCol2(colClientAnchorDetail.getToIndex());
- anchor.setRow2(rowClientAnchorDetail.getToIndex());
-
- // For now, set the anchor type to do not move or resize the
- // image as the size of the row/column is adjusted. This could easily
- // become another parameter passed to the method. Please read the note
- // above regarding the behaviour of image resizing.
- anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
-
- // Now, add the picture to the workbook. Note that unlike the similar
- // method in the HSSF Examples section, the image type is checked. First,
- // the image files location is identified by interrogating the URL passed
- // to the method, the images type is identified before it is added to the
- // sheet.
- String sURL = imageFile.toString().toLowerCase(Locale.ROOT);
- if( sURL.endsWith(".png") ) {
- imageType = Workbook.PICTURE_TYPE_PNG;
- }
- else if( sURL.endsWith(".jpg") || sURL.endsWith(".jpeg") ) {
- imageType = Workbook.PICTURE_TYPE_JPEG;
- }
- else {
- throw new IllegalArgumentException("Invalid Image file : " +
- sURL);
- }
- int index = sheet.getWorkbook().addPicture(
- IOUtils.toByteArray(imageFile.openStream()), imageType);
- drawing.createPicture(anchor, index);
- }
-
- /**
- * Determines whether the sheets columns should be re-sized to accommodate
- * the image, adjusts the columns width if necessary and creates then
- * returns a ClientAnchorDetail object that facilitates construction of
- * an ClientAnchor that will fix the image on the sheet and establish
- * it's size.
- *
- * @param sheet A reference to the sheet that will 'contain' the image.
- * @param colNumber A primitive int that contains the index number of a
- * column on the sheet.
- * @param reqImageWidthMM A primitive double that contains the required
- * width of the image in millimetres
- * @param resizeBehaviour A primitive int whose value will indicate how the
- * width of the column should be adjusted if the
- * required width of the image is greater than the
- * width of the column.
- * @return An instance of the ClientAnchorDetail class that will contain
- * the index number of the column containing the cell whose top
- * left hand corner also defines the top left hand corner of the
- * image, the index number column containing the cell whose top
- * left hand corner also defines the bottom right hand corner of
- * the image and an inset that determines how far the right hand
- * edge of the image can protrude into the next column - expressed
- * as a specific number of coordinate positions.
- */
- private ClientAnchorDetail fitImageToColumns(Sheet sheet, int colNumber,
- double reqImageWidthMM, int resizeBehaviour) {
-
- double colWidthMM = 0.0D;
- double colCoordinatesPerMM = 0.0D;
- int pictureWidthCoordinates = 0;
- ClientAnchorDetail colClientAnchorDetail = null;
-
- // Get the colum's width in millimetres
- colWidthMM = ConvertImageUnits.widthUnits2Millimetres(
- (short)sheet.getColumnWidth(colNumber));
-
- // Check that the column's width will accomodate the image at the
- // required dimension. If the width of the column is LESS than the
- // required width of the image, decide how the application should
- // respond - resize the column or overlay the image across one or more
- // columns.
- if(colWidthMM < reqImageWidthMM) {
-
- // Should the column's width simply be expanded?
- if((resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN) ||
- (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {
- // Set the width of the column by converting the required image
- // width from millimetres into Excel's column width units.
- sheet.setColumnWidth(colNumber,
- ConvertImageUnits.millimetres2WidthUnits(reqImageWidthMM));
- // To make the image occupy the full width of the column, convert
- // the required width of the image into co-ordinates. This value
- // will become the inset for the ClientAnchorDetail class that
- // is then instantiated.
- if(sheet instanceof HSSFSheet) {
- colWidthMM = reqImageWidthMM;
- colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
- colWidthMM;
- pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);
-
- }
- else {
- pictureWidthCoordinates = (int)reqImageWidthMM * AddDimensionedImage.EMU_PER_MM;
- }
- colClientAnchorDetail = new ClientAnchorDetail(colNumber,
- colNumber, pictureWidthCoordinates);
- }
- // If the user has chosen to overlay both rows and columns or just
- // to expand ONLY the size of the rows, then calculate how to lay
- // the image out across one or more columns.
- else if ((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||
- (resizeBehaviour == AddDimensionedImage.EXPAND_ROW)) {
- colClientAnchorDetail = this.calculateColumnLocation(sheet,
- colNumber, reqImageWidthMM);
- }
- }
- // If the column is wider than the image.
- else {
- if(sheet instanceof HSSFSheet) {
- // Mow many co-ordinate positions are there per millimetre?
- colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
- colWidthMM;
- // Given the width of the image, what should be it's co-ordinate?
- pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);
- }
- else {
- pictureWidthCoordinates = (int)reqImageWidthMM *
- AddDimensionedImage.EMU_PER_MM;
- }
- colClientAnchorDetail = new ClientAnchorDetail(colNumber,
- colNumber, pictureWidthCoordinates);
- }
- return(colClientAnchorDetail);
- }
-
- /**
- * Determines whether the sheets row should be re-sized to accomodate
- * the image, adjusts the rows height if necessary and creates then
- * returns a ClientAnchorDetail object that facilitates construction of
- * a ClientAnchor that will fix the image on the sheet and establish
- * it's size.
- *
- * @param sheet A reference to the sheet that will 'contain' the image.
- * @param rowNumber A primitive int that contains the index number of a
- * row on the sheet.
- * @param reqImageHeightMM A primitive double that contains the required
- * height of the image in millimetres
- * @param resizeBehaviour A primitive int whose value will indicate how the
- * height of the row should be adjusted if the
- * required height of the image is greater than the
- * height of the row.
- * @return An instance of the ClientAnchorDetail class that will contain
- * the index number of the row containing the cell whose top
- * left hand corner also defines the top left hand corner of the
- * image, the index number of the row containing the cell whose
- * top left hand corner also defines the bottom right hand
- * corner of the image and an inset that determines how far the
- * bottom edge of the image can protrude into the next (lower)
- * row - expressed as a specific number of coordinate positions.
- */
- private ClientAnchorDetail fitImageToRows(Sheet sheet, int rowNumber,
- double reqImageHeightMM, int resizeBehaviour) {
- Row row = null;
- double rowHeightMM = 0.0D;
- double rowCoordinatesPerMM = 0.0D;
- int pictureHeightCoordinates = 0;
- ClientAnchorDetail rowClientAnchorDetail = null;
-
- // Get the row and it's height
- row = sheet.getRow(rowNumber);
- if(row == null) {
- // Create row if it does not exist.
- row = sheet.createRow(rowNumber);
- }
-
- // Get the row's height in millimetres
- rowHeightMM = row.getHeightInPoints() / ConvertImageUnits.POINTS_PER_MILLIMETRE;
-
- // Check that the row's height will accomodate the image at the required
- // dimensions. If the height of the row is LESS than the required height
- // of the image, decide how the application should respond - resize the
- // row or overlay the image across a series of rows.
- if(rowHeightMM < reqImageHeightMM) {
- if((resizeBehaviour == AddDimensionedImage.EXPAND_ROW) ||
- (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {
- row.setHeightInPoints((float)(reqImageHeightMM *
- ConvertImageUnits.POINTS_PER_MILLIMETRE));
- if(sheet instanceof HSSFSheet) {
- rowHeightMM = reqImageHeightMM;
- rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
- rowHeightMM;
- pictureHeightCoordinates = (int)(reqImageHeightMM *
- rowCoordinatesPerMM);
- }
- else {
- pictureHeightCoordinates = (int)(reqImageHeightMM *
- AddDimensionedImage.EMU_PER_MM);
- }
- rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,
- rowNumber, pictureHeightCoordinates);
- }
- // If the user has chosen to overlay both rows and columns or just
- // to expand ONLY the size of the columns, then calculate how to lay
- // the image out ver one or more rows.
- else if((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||
- (resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN)) {
- rowClientAnchorDetail = this.calculateRowLocation(sheet,
- rowNumber, reqImageHeightMM);
- }
- }
- // Else, if the image is smaller than the space available
- else {
- if(sheet instanceof HSSFSheet) {
- rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
- rowHeightMM;
- pictureHeightCoordinates = (int)(reqImageHeightMM * rowCoordinatesPerMM);
- }
- else {
- pictureHeightCoordinates = (int)(reqImageHeightMM *
- AddDimensionedImage.EMU_PER_MM);
- }
- rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,
- rowNumber, pictureHeightCoordinates);
- }
- return(rowClientAnchorDetail);
- }
-
- /**
- * If the image is to overlie more than one column, calculations need to be
- * performed to determine how many columns and whether the image will
- * overlie just a part of one column in order to be presented at the
- * required size.
- *
- * @param sheet The sheet that will 'contain' the image.
- * @param startingColumn A primitive int whose value is the index of the
- * column that contains the cell whose top left hand
- * corner should be aligned with the top left hand
- * corner of the image.
- * @param reqImageWidthMM A primitive double whose value will indicate the
- * required width of the image in millimetres.
- * @return An instance of the ClientAnchorDetail class that will contain
- * the index number of the column containing the cell whose top
- * left hand corner also defines the top left hand corner of the
- * image, the index number column containing the cell whose top
- * left hand corner also defines the bottom right hand corner of
- * the image and an inset that determines how far the right hand
- * edge of the image can protrude into the next column - expressed
- * as a specific number of coordinate positions.
- */
- private ClientAnchorDetail calculateColumnLocation(Sheet sheet,
- int startingColumn,
- double reqImageWidthMM) {
- ClientAnchorDetail anchorDetail = null;
- double totalWidthMM = 0.0D;
- double colWidthMM = 0.0D;
- double overlapMM = 0.0D;
- double coordinatePositionsPerMM = 0.0D;
- int toColumn = startingColumn;
- int inset = 0;
-
- // Calculate how many columns the image will have to
- // span in order to be presented at the required size.
- while(totalWidthMM < reqImageWidthMM) {
- colWidthMM = ConvertImageUnits.widthUnits2Millimetres(
- (short)(sheet.getColumnWidth(toColumn)));
- // Note use of the cell border width constant. Testing with an image
- // declared to fit exactly into one column demonstrated that it's
- // width was greater than the width of the column the POI returned.
- // Further, this difference was a constant value that I am assuming
- // related to the cell's borders. Either way, that difference needs
- // to be allowed for in this calculation.
- totalWidthMM += (colWidthMM + ConvertImageUnits.CELL_BORDER_WIDTH_MILLIMETRES);
- toColumn++;
- }
- // De-crement by one the last column value.
- toColumn--;
- // Highly unlikely that this will be true but, if the width of a series
- // of columns is exactly equal to the required width of the image, then
- // simply build a ClientAnchorDetail object with an inset equal to the
- // total number of co-ordinate positions available in a column, a
- // from column co-ordinate (top left hand corner) equal to the value
- // of the startingColumn parameter and a to column co-ordinate equal
- // to the toColumn variable.
- //
- // Convert both values to ints to perform the test.
- if((int)totalWidthMM == (int)reqImageWidthMM) {
- // A problem could occur if the image is sized to fit into one or
- // more columns. If that occurs, the value in the toColumn variable
- // will be in error. To overcome this, there are two options, to
- // ibcrement the toColumn variable's value by one or to pass the
- // total number of co-ordinate positions to the third paramater
- // of the ClientAnchorDetail constructor. For no sepcific reason,
- // the latter option is used below.
- if(sheet instanceof HSSFSheet) {
- anchorDetail = new ClientAnchorDetail(startingColumn,
- toColumn, ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS);
- }
- else {
- anchorDetail = new ClientAnchorDetail(startingColumn,
- toColumn, (int)reqImageWidthMM * AddDimensionedImage.EMU_PER_MM);
- }
- }
- // In this case, the image will overlap part of another column and it is
- // necessary to calculate just how much - this will become the inset
- // for the ClientAnchorDetail object.
- else {
- // Firstly, claculate how much of the image should overlap into
- // the next column.
- overlapMM = reqImageWidthMM - (totalWidthMM - colWidthMM);
-
- // When the required size is very close indded to the column size,
- // the calcaulation above can produce a negative value. To prevent
- // problems occuring in later caculations, this is simply removed
- // be setting the overlapMM value to zero.
- if(overlapMM < 0) {
- overlapMM = 0.0D;
- }
-
- if(sheet instanceof HSSFSheet) {
- // Next, from the columns width, calculate how many co-ordinate
- // positons there are per millimetre
- coordinatePositionsPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
- colWidthMM;
- // From this figure, determine how many co-ordinat positions to
- // inset the left hand or bottom edge of the image.
- inset = (int)(coordinatePositionsPerMM * overlapMM);
- }
- else {
- inset = (int)overlapMM * AddDimensionedImage.EMU_PER_MM;
- }
-
- // Now create the ClientAnchorDetail object, setting the from and to
- // columns and the inset.
- anchorDetail = new ClientAnchorDetail(startingColumn, toColumn, inset);
- }
- return(anchorDetail);
- }
-
- /**
- * If the image is to overlie more than one rows, calculations need to be
- * performed to determine how many rows and whether the image will
- * overlie just a part of one row in order to be presented at the
- * required size.
- *
- * @param sheet The sheet that will 'contain' the image.
- * @param startingRow A primitive int whose value is the index of the row
- * that contains the cell whose top left hand corner
- * should be aligned with the top left hand corner of
- * the image.
- * @param reqImageHeightMM A primitive double whose value will indicate the
- * required height of the image in millimetres.
- * @return An instance of the ClientAnchorDetail class that will contain
- * the index number of the row containing the cell whose top
- * left hand corner also defines the top left hand corner of the
- * image, the index number of the row containing the cell whose top
- * left hand corner also defines the bottom right hand corner of
- * the image and an inset that determines how far the bottom edge
- * can protrude into the next (lower) row - expressed as a specific
- * number of co-ordinate positions.
- */
- private ClientAnchorDetail calculateRowLocation(Sheet sheet,
- int startingRow, double reqImageHeightMM) {
- ClientAnchorDetail clientAnchorDetail = null;
- Row row = null;
- double rowHeightMM = 0.0D;
- double totalRowHeightMM = 0.0D;
- double overlapMM = 0.0D;
- double rowCoordinatesPerMM = 0.0D;
- int toRow = startingRow;
- int inset = 0;
-
- // Step through the rows in the sheet and accumulate a total of their
- // heights.
- while(totalRowHeightMM < reqImageHeightMM) {
- row = sheet.getRow(toRow);
- // Note, if the row does not already exist on the sheet then create
- // it here.
- if(row == null) {
- row = sheet.createRow(toRow);
- }
- // Get the row's height in millimetres and add to the running total.
- rowHeightMM = row.getHeightInPoints() /
- ConvertImageUnits.POINTS_PER_MILLIMETRE;
- totalRowHeightMM += rowHeightMM;
- toRow++;
- }
- // Owing to the way the loop above works, the rowNumber will have been
- // incremented one row too far. Undo that here.
- toRow--;
- // Check to see whether the image should occupy an exact number of
- // rows. If so, build the ClientAnchorDetail record to point
- // to those rows and with an inset of the total number of co-ordinate
- // position in the row.
- //
- // To overcome problems that can occur with comparing double values for
- // equality, cast both to int(s) to truncate the value; VERY crude and
- // I do not really like it!!
- if((int)totalRowHeightMM == (int)reqImageHeightMM) {
- if(sheet instanceof HSSFSheet) {
- clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,
- ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS);
- }
- else {
- clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,
- (int)reqImageHeightMM * AddDimensionedImage.EMU_PER_MM);
- }
- }
- else {
- // Calculate how far the image will project into the next row. Note
- // that the height of the last row assessed is subtracted from the
- // total height of all rows assessed so far.
- overlapMM = reqImageHeightMM - (totalRowHeightMM - rowHeightMM);
-
- // To prevent an exception being thrown when the required width of
- // the image is very close indeed to the column size.
- if(overlapMM < 0) {
- overlapMM = 0.0D;
- }
-
- if(sheet instanceof HSSFSheet) {
- rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
- rowHeightMM;
- inset = (int)(overlapMM * rowCoordinatesPerMM);
- }
- else {
- inset = (int)overlapMM * AddDimensionedImage.EMU_PER_MM;
- }
- clientAnchorDetail = new ClientAnchorDetail(startingRow,
- toRow, inset);
- }
- return(clientAnchorDetail);
- }
-
- /**
- * The main entry point to the program. It contains code that demonstrates
- * one way to use the program.
- *
- * Note, the code is not restricted to use on new workbooks only. If an
- * image is to be inserted into an existing workbook. just open that
- * workbook, gat a reference to a sheet and pass that;
- *
- * AddDimensionedImage addImage = new AddDimensionedImage();
- *
- * File file = new File("....... Existing Workbook .......");
- * FileInputStream fis = new FileInputStream(file);
- * Workbook workbook = new HSSFWorkbook(fis);
- * HSSFSheet sheet = workbook.getSheetAt(0);
- * addImage.addImageToSheet("C3", sheet, "image.jpg", 30, 20,
- * AddDimensionedImage.EXPAND.ROW);
- *
- * @param args the command line arguments
- */
- public static void main(String[] args) throws IOException {
- String imageFile = null;
- String outputFile = null;
- FileOutputStream fos = null;
- Workbook workbook = null;
- Sheet sheet = null;
-
- if(args.length < 2){
- System.err.println("Usage: AddDimensionedImage imageFile outputFile");
- return;
- }
- workbook = new HSSFWorkbook(); // OR XSSFWorkbook
- sheet = workbook.createSheet("Picture Test");
- imageFile = args[0];
- outputFile = args[1];
- new AddDimensionedImage().addImageToSheet("B5", sheet, sheet.createDrawingPatriarch(),
- new File(imageFile).toURI().toURL(), 100, 40,
- AddDimensionedImage.EXPAND_ROW_AND_COLUMN);
- fos = new FileOutputStream(outputFile);
- workbook.write(fos);
- fos.close();
- workbook.close();
- }
-
- /**
- * The HSSFClientAnchor class accepts eight arguments. In order, these are;
- *
- * * How far the left hand edge of the image is inset from the left hand
- * edge of the cell
- * * How far the top edge of the image is inset from the top of the cell
- * * How far the right hand edge of the image is inset from the left
- * hand edge of the cell
- * * How far the bottom edge of the image is inset from the top of the
- * cell.
- * * Together, arguments five and six determine the column and row
- * coordinates of the cell whose top left hand corner will be aligned
- * with the images top left hand corner.
- * * Together, arguments seven and eight determine the column and row
- * coordinates of the cell whose top left hand corner will be aligned
- * with the images bottom right hand corner.
- *
- * An instance of the ClientAnchorDetail class provides three of the eight
- * parameters, one of the coordinates for the images top left hand corner,
- * one of the coordinates for the images bottom right hand corner and
- * either how far the image should be inset from the top or the left hand
- * edge of the cell.
- *
- * @author Mark Beardsley [msb at apache.org]
- * @version 1.00 5th August 2009.
- */
- public class ClientAnchorDetail {
-
- public int fromIndex = 0;
- public int toIndex = 0;
- public int inset = 0;
-
- /**
- * Create a new instance of the ClientAnchorDetail class using the
- * following parameters.
- *
- * @param fromIndex A primitive int that contains one of the
- * coordinates (row or column index) for the top left
- * hand corner of the image.
- * @param toIndex A primitive int that contains one of the
- * coordinates (row or column index) for the bottom
- * right hand corner of the image.
- * @param inset A primitive int that contains a value which indicates
- * how far the image should be inset from the top or the
- * left hand edge of a cell.
- */
- public ClientAnchorDetail(int fromIndex, int toIndex, int inset) {
- this.fromIndex = fromIndex;
- this.toIndex = toIndex;
- this.inset = inset;
- }
-
- /**
- * Get one of the number of the column or row that contains the cell
- * whose top left hand corner will be aligned with the top left hand
- * corner of the image.
- *
- * @return The value - row or column index - for one of the coordinates
- * of the top left hand corner of the image.
- */
- public int getFromIndex() {
- return(this.fromIndex);
- }
-
- /**
- * Get one of the number of the column or row that contains the cell
- * whose top left hand corner will be aligned with the bottom right hand
- * corner of the image.
- *
- * @return The value - row or column index - for one of the coordinates
- * of the bottom right hand corner of the image.
- */
- public int getToIndex() {
- return(this.toIndex);
- }
-
- /**
- * Get the images offset from the edge of a cell.
- *
- * @return How far either the right hand or bottom edge of the image is
- * inset from the left hand or top edge of a cell.
- */
- public int getInset() {
- return(this.inset);
- }
- }
-
- /**
- * Utility methods used to convert Excels character based column and row
- * size measurements into pixels and/or millimetres. The class also contains
- * various constants that are required in other calculations.
- *
- * @author xio[darjino@hotmail.com]
- * @version 1.01 30th July 2009.
- * Added by Mark Beardsley [msb at apache.org].
- * Additional constants.
- * widthUnits2Millimetres() and millimetres2Units() methods.
- */
- public static class ConvertImageUnits {
-
- // Each cell conatins a fixed number of co-ordinate points; this number
- // does not vary with row height or column width or with font. These two
- // constants are defined below.
- public static final int TOTAL_COLUMN_COORDINATE_POSITIONS = 1023; // MB
- public static final int TOTAL_ROW_COORDINATE_POSITIONS = 255; // MB
- // The resoultion of an image can be expressed as a specific number
- // of pixels per inch. Displays and printers differ but 96 pixels per
- // inch is an acceptable standard to beging with.
- public static final int PIXELS_PER_INCH = 96; // MB
- // Cnstants that defines how many pixels and points there are in a
- // millimetre. These values are required for the conversion algorithm.
- public static final double PIXELS_PER_MILLIMETRES = 3.78; // MB
- public static final double POINTS_PER_MILLIMETRE = 2.83; // MB
- // The column width returned by HSSF and the width of a picture when
- // positioned to exactly cover one cell are different by almost exactly
- // 2mm - give or take rounding errors. This constant allows that
- // additional amount to be accounted for when calculating how many
- // celles the image ought to overlie.
- public static final double CELL_BORDER_WIDTH_MILLIMETRES = 2.0D; // MB
- public static final short EXCEL_COLUMN_WIDTH_FACTOR = 256;
- public static final int UNIT_OFFSET_LENGTH = 7;
- public static final int[] UNIT_OFFSET_MAP = new int[]
- { 0, 36, 73, 109, 146, 182, 219 };
-
- /**
- * pixel units to excel width units(units of 1/256th of a character width)
- * @param pxs
- * @return
- */
- public static short pixel2WidthUnits(int pxs) {
- short widthUnits = (short) (EXCEL_COLUMN_WIDTH_FACTOR *
- (pxs / UNIT_OFFSET_LENGTH));
- widthUnits += UNIT_OFFSET_MAP[(pxs % UNIT_OFFSET_LENGTH)];
- return widthUnits;
- }
-
- /**
- * excel width units(units of 1/256th of a character width) to pixel
- * units.
- *
- * @param widthUnits
- * @return
- */
- public static int widthUnits2Pixel(short widthUnits) {
- int pixels = (widthUnits / EXCEL_COLUMN_WIDTH_FACTOR)
- * UNIT_OFFSET_LENGTH;
- int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR;
- pixels += Math.round(offsetWidthUnits /
- ((float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH));
- return pixels;
- }
-
- /**
- * Convert Excels width units into millimetres.
- *
- * @param widthUnits The width of the column or the height of the
- * row in Excels units.
- * @return A primitive double that contains the columns width or rows
- * height in millimetres.
- */
- public static double widthUnits2Millimetres(short widthUnits) {
- return(ConvertImageUnits.widthUnits2Pixel(widthUnits) /
- ConvertImageUnits.PIXELS_PER_MILLIMETRES);
- }
-
- /**
- * Convert into millimetres Excels width units..
- *
- * @param millimetres A primitive double that contains the columns
- * width or rows height in millimetres.
- * @return A primitive int that contains the columns width or rows
- * height in Excels units.
- */
- public static int millimetres2WidthUnits(double millimetres) {
- return(ConvertImageUnits.pixel2WidthUnits((int)(millimetres *
- ConvertImageUnits.PIXELS_PER_MILLIMETRES)));
- }
-
- public static int pointsToPixels(double points) {
- return (int) Math.round(points / 72D * PIXELS_PER_INCH);
- }
-
- public static double pointsToMillimeters(double points) {
- return points / 72D * 25.4;
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.ss.examples;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Locale;
+
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.util.IOUtils;
+
+
+/**
+ * Demonstrates how to add an image to a worksheet and set that images size
+ * to a specific number of millimetres irrespective of the width of the columns
+ * or height of the rows. Overridden methods are provided so that the location
+ * of the image - the cells row and column coordinates that define the top
+ * left hand corners of the image - can be identified either in the familiar
+ * Excel manner - A1 for instance - or using POI's methodology of a column and
+ * row index where 0, 0 would indicate cell A1.
+ *
+ * The best way to make use of these techniques is to delay adding the image to
+ * the sheet until all other work has been completed. That way, the sizes of
+ * all rows and columns will have been adjusted - assuming that step was
+ * necessary. Even though the anchors type is set to prevent the image moving
+ * or re-sizing, this setting does not have any effect until the sheet is being
+ * viewed using the Excel application.
+ *
+ * The key to the process is the ClientAnchor class. It defines methods that allow
+ * us to define the location of an image by specifying the following;
+ *
+ * * How far - in terms of coordinate positions - the image should be inset
+ * from the left hand border of a cell.
+ * * How far - in terms of coordinate positions - the image should be inset
+ * from the from the top of the cell.
+ * * How far - in terms of coordinate positions - the right hand edge of
+ * the image should protrude into a cell (measured from the cells left hand
+ * edge to the images right hand edge).
+ * * How far - in terms of coordinate positions - the bottom edge of the
+ * image should protrude into a row (measured from the cells top edge to
+ * the images bottom edge).
+ * * The index of the column that contains the cell whose top left hand
+ * corner should be aligned with the top left hand corner of the image.
+ * * The index of the row that contains the cell whose top left hand corner
+ * should be aligned with the images top left hand corner.
+ * * The index of the column that contains the cell whose top left hand
+ * corner should be aligned with the images bottom right hand corner
+ * * The index number of the row that contains the cell whose top left
+ * hand corner should be aligned with the images bottom right hand corner.
+ *
+ * It can be used to add an image into cell A1, for example, in the following
+ * manner;
+ *
+ * ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
+ *
+ * anchor.setDx1(0);
+ * anchor.setDy1(0);
+ * anchor.setDx2(0);
+ * anchor.setDy2(0);
+ * anchor.setCol1(0);
+ * anchor.setRow1(0);
+ * anchor.setCol2(1);
+ * anchor.setRow2(1);
+ *
+ * Taken together, the first four methods define the locations of the top left
+ * and bottom right hand corners of the image if you imagine that the image is
+ * represented by a simple rectangle. The setDx1() and setDy1() methods locate
+ * the top left hand corner of the image while setDx2() and and Dy2() locate the
+ * bottom right hand corner of the image. An individual image can be inserted
+ * into a single cell or is can lie across many cells and the latter four methods
+ * are used to define just where the image should be positioned. They do this by
+ * again by identifying where the top left and bottom right hand corners of the
+ * image should be located but this time in terms of the indexes of the cells
+ * in which those corners should be located. The setCol1() and setRow1() methods
+ * together identify the cell that should contain the top left hand corner of
+ * the image while setCol2() and setRow2() do the same for the images bottom
+ * right hand corner.
+ *
+ * Knowing that, it is possible to look again at the example above and to see
+ * that the top left hand corner of the image will be located in cell A1 (0, 0)
+ * and it will be aligned with the very top left hand corner of the cell. Likewise,
+ * the bottom right hand corner of the image will be located in cell B2 (1, 1) and
+ * it will again be aligned with the top left hand corner of the cell. This has the
+ * effect of making the image seem to occupy the whole of cell A1. Interestingly, it
+ * also has an effect on the images resizing behaviour because testing has
+ * demonstrated that if the image is wholly contained within one cell and is not
+ * 'attached' for want of a better word, to a neighbouring cell, then that image
+ * will not increase in size in response to the user dragging the column wider
+ * or the cell higher.
+ *
+ * The following example demonstrates a slightly different way to insert an
+ * image into cell A1 and to ensure that it occupies the whole of the cell. This
+ * is accomplished by specifying the the images bottom right hand corner should be
+ * aligned with the bottom right hand corner of the cell. It is also a case
+ * where the image will not increase in size if the user increases the size of
+ * the enclosing cell - irrespective of the anchors type - but it will reduce in
+ * size if the cell is made smaller.
+ *
+ * ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
+ *
+ * anchor.setDx1(0);
+ * anchor.setDy1(0);
+ * anchor.setDx2(1023);
+ * anchor.setDy2(255);
+ * anchor.setCol1(0);
+ * anchor.setRow1(0);
+ * anchor.setCol2(0);
+ * anchor.setRow2(0);
+ *
+ * Note that the final four method calls all pass the same value and seem to
+ * indicate that the images top left hand corner is aligned with the top left
+ * hand corner of cell A1 and that it's bottom right hand corner is also
+ * aligned with the top left hand corner of cell A1. Yet, running this code
+ * would see the image fully occupying cell A1. That is the result of the
+ * values passed to parameters three and four; these I have referred to as
+ * determining the images coordinates within the cell. They indicate that the
+ * image should occupy - in order - the full width of the column and the full
+ * height of the row.
+ *
+ * The co-ordinate values shown are the maxima; and they are independent of
+ * row height/column width and of the font used. Passing 255 will always result
+ * in the image occupying the full height of the row and passing 1023 will
+ * always result in the image occupying the full width of the column. They help
+ * in situations where an image is larger than a column/row and must overlap
+ * into the next column/row. Using them does mean, however, that it is often
+ * necessary to perform conversions between Excels characters units, points,
+ * pixels and millimetres in order to establish how many rows/columns an image
+ * should occupy and just what the various insets ought to be.
+ *
+ * Note that the setDx1(int) and setDy1(int) methods of the ClientAchor class
+ * are not made use of in the code that follows. It would be fairly trivial
+ * however to extend this example further and provide methods that would centre
+ * an image within a cell or allow the user to specify that a plain border a
+ * fixed number of millimetres wide should wrap around the image. Those first
+ * two parameters would make this sort of functionality perfectly possible.
+ *
+ * Owing to the various conversions used, the actual size of the image may vary
+ * from that required; testing has so far found this to be in the region of
+ * plus or minus two millimetres. Most likely by modifying the way the
+ * calculations are performed - possibly using double(s) throughout and
+ * rounding the values at the correct point - it is likely that these errors
+ * could be reduced or removed.
+ *
+ * A note concerning Excels image resizing behaviour. The ClientAnchor
+ * class contains a method called setAnchorType(int) which can be used to
+ * determine how Excel will resize an image in response to the user increasing
+ * or decreasing the dimensions of the cell containing the image. There are
+ * three values that can be passed to this method; 0 = To move and size the
+ * image with the cell, 2 = To move but don't size the image with the cell,
+ * 3 = To prevent the image from moving or being resized along with the cell. If
+ * an image is inserted using this class and placed into a single cell then if
+ * the setAnchorType(int) method is called and a value of either 0 or 2 passed
+ * to it, the resultant resizing behaviour may be a surprise. The image will not
+ * grow in size of the column is made wider or the row higher but it will shrink
+ * if the columns width or rows height are reduced.
+ *
+ * @author Mark Beardsley [msb at apache.org] and Mark Southern [southern at scripps.edu]
+ * @version 1.00 5th August 2009.
+ * 2.00 26th February 2010.
+ * Ported to make use of the the SS usermodel classes.
+ * Ability to reuse the Drawing Patriarch so that multiple images
+ * can be inserted without unintentionally erasing earlier images.
+ * Check on image type added; i.e. jpg, jpeg or png.
+ * The String used to contain the files name is now converted
+ * into a URL.
+ * 2.10 17th May 2012
+ * Corrected gross error that occurred when using the code with
+ * XSSF or SXSSF workbooks. In short, the code did not correctly
+ * calculate the size of the image(s) owing to the use of EMUs
+ * within the OOXML file format. That problem has largely been
+ * corrected although it should be mentioned that images are not
+ * sized with the same level of accuracy. Discrepancies of up to
+ * 2mm have been noted in testing. Further investigation will
+ * continue to rectify this issue.
+ */
+public class AddDimensionedImage {
+
+ // Four constants that determine how - and indeed whether - the rows
+ // and columns an image may overlie should be expanded to accomodate that
+ // image.
+ // Passing EXPAND_ROW will result in the height of a row being increased
+ // to accomodate the image if it is not already larger. The image will
+ // be layed across one or more columns.
+ // Passing EXPAND_COLUMN will result in the width of the column being
+ // increased to accomodate the image if it is not already larger. The image
+ // will be layed across one or many rows.
+ // Passing EXPAND_ROW_AND_COLUMN will result in the height of the row
+ // bing increased along with the width of the column to accomdate the
+ // image if either is not already larger.
+ // Passing OVERLAY_ROW_AND_COLUMN will result in the image being layed
+ // over one or more rows and columns. No row or column will be resized,
+ // instead, code will determine how many rows and columns the image should
+ // overlie.
+ public static final int EXPAND_ROW = 1;
+ public static final int EXPAND_COLUMN = 2;
+ public static final int EXPAND_ROW_AND_COLUMN = 3;
+ public static final int OVERLAY_ROW_AND_COLUMN = 7;
+
+ // Modified to support EMU - English Metric Units - used within the OOXML
+ // workbooks, this multoplier is used to convert between measurements in
+ // millimetres and in EMUs
+ private static final int EMU_PER_MM = 36000;
+
+ /**
+ * Add an image to a worksheet.
+ *
+ * @param cellNumber A String that contains the location of the cell whose
+ * top left hand corner should be aligned with the top
+ * left hand corner of the image; for example "A1", "A2"
+ * etc. This is to support the familiar Excel syntax.
+ * Whilst images are are not actually inserted into cells
+ * this provides a convenient method of indicating where
+ * the image should be positioned on the sheet.
+ * @param sheet A reference to the sheet that contains the cell referenced
+ * above.
+ * @param drawing An instance of the DrawingPatriarch class. This is now
+ * passed into the method where it was, previously, recovered
+ * from the sheet in order to allow multiple pictures be
+ * inserted. If the patriarch was not 'cached in this manner
+ * each time it was created any previously positioned images
+ * would be simply over-written.
+ * @param imageFile An instance of the URL class that encapsulates the name
+ * of and path to the image that is to be 'inserted into'
+ * the sheet.
+ * @param reqImageWidthMM A primitive double that contains the required
+ * width of the image in millimetres.
+ * @param reqImageHeightMM A primitive double that contains the required
+ * height of the image in millimetres.
+ * @param resizeBehaviour A primitive int whose value will determine how
+ * the code should react if the image is larger than
+ * the cell referenced by the cellNumber parameter.
+ * Four constants are provided to determine what
+ * should happen;
+ * AddDimensionedImage.EXPAND_ROW
+ * AddDimensionedImage.EXPAND_COLUMN
+ * AddDimensionedImage.EXPAND_ROW_AND_COLUMN
+ * AddDimensionedImage.OVERLAY_ROW_AND_COLUMN
+ * @throws java.io.FileNotFoundException If the file containing the image
+ * cannot be located.
+ * @throws java.io.IOException If a problem occurs whilst reading the file
+ * of image data.
+ * @throws java.lang.IllegalArgumentException If an invalid value is passed
+ * to the resizeBehaviour
+ * parameter.
+ */
+ public void addImageToSheet(String cellNumber, Sheet sheet, Drawing> drawing,
+ URL imageFile, double reqImageWidthMM, double reqImageHeightMM,
+ int resizeBehaviour) throws IOException, IllegalArgumentException {
+ // Convert the String into column and row indices then chain the
+ // call to the overridden addImageToSheet() method.
+ CellReference cellRef = new CellReference(cellNumber);
+ this.addImageToSheet(cellRef.getCol(), cellRef.getRow(), sheet, drawing,
+ imageFile, reqImageWidthMM, reqImageHeightMM,resizeBehaviour);
+ }
+
+ /**
+ * Add an image to a worksheet.
+ *
+ * @param colNumber A primitive int that contains the index number of a
+ * column on the worksheet; POI column indices are zero
+ * based. Together with the rowNumber parameter's value,
+ * this parameter identifies a cell on the worksheet. The
+ * images top left hand corner will be aligned with the
+ * top left hand corner of this cell.
+ * @param rowNumber A primitive int that contains the index number of a row
+ * on the worksheet; POI row indices are zero based.
+ * Together with the rowNumber parameter's value, this
+ * parameter identifies a cell on the worksheet. The
+ * images top left hand corner will be aligned with the
+ * top left hand corner of this cell.
+ * @param sheet A reference to the sheet that contains the cell identified
+ * by the two parameters above.
+ * @param drawing An instance of the DrawingPatriarch class. This is now
+ * passed into the method where it was, previously, recovered
+ * from the sheet in order to allow multiple pictures be
+ * inserted. If the patriarch was not 'cached in this manner
+ * each time it was created any previously positioned images
+ * would be simply over-written.
+ * @param imageFile An instance of the URL class that encapsulates the name
+ * of and path to the image that is to be 'inserted into'
+ * the sheet.
+ * @param reqImageWidthMM A primitive double that contains the required
+ * width of the image in millimetres.
+ * @param reqImageHeightMM A primitive double that contains the required
+ * height of the image in millimetres.
+ * @param resizeBehaviour A primitive int whose value will determine how
+ * the code should react if the image is larger than
+ * the cell referenced by the colNumber and
+ * rowNumber parameters. Four constants are provided
+ * to determine what should happen;
+ * AddDimensionedImage.EXPAND_ROW
+ * AddDimensionedImage.EXPAND_COLUMN
+ * AddDimensionedImage.EXPAND_ROW_AND_COLUMN
+ * AddDimensionedImage.OVERLAY_ROW_AND_COLUMN
+ * @throws java.io.FileNotFoundException If the file containing the image
+ * cannot be located.
+ * @throws java.io.IOException If a problem occurs whilst reading the file
+ * of image data.
+ * @throws java.lang.IllegalArgumentException If an invalid value is passed
+ * to the resizeBehaviour
+ * parameter or if the extension
+ * of the image file indicates that
+ * it is of a type that cannot
+ * currently be added to the worksheet.
+ */
+ public void addImageToSheet(int colNumber, int rowNumber, Sheet sheet, Drawing> drawing,
+ URL imageFile, double reqImageWidthMM, double reqImageHeightMM,
+ int resizeBehaviour) throws IOException,
+ IllegalArgumentException {
+ ClientAnchor anchor = null;
+ ClientAnchorDetail rowClientAnchorDetail = null;
+ ClientAnchorDetail colClientAnchorDetail = null;
+ int imageType = 0;
+
+ // Validate the resizeBehaviour parameter.
+ if((resizeBehaviour != AddDimensionedImage.EXPAND_COLUMN) &&
+ (resizeBehaviour != AddDimensionedImage.EXPAND_ROW) &&
+ (resizeBehaviour != AddDimensionedImage.EXPAND_ROW_AND_COLUMN) &&
+ (resizeBehaviour != AddDimensionedImage.OVERLAY_ROW_AND_COLUMN)) {
+ throw new IllegalArgumentException("Invalid value passed to the " +
+ "resizeBehaviour parameter of AddDimensionedImage.addImageToSheet()");
+ }
+
+ // Call methods to calculate how the image and sheet should be
+ // manipulated to accomodate the image; columns and then rows.
+ colClientAnchorDetail = this.fitImageToColumns(sheet, colNumber,
+ reqImageWidthMM, resizeBehaviour);
+ rowClientAnchorDetail = this.fitImageToRows(sheet, rowNumber,
+ reqImageHeightMM, resizeBehaviour);
+
+ // Having determined if and how to resize the rows, columns and/or the
+ // image, create the ClientAnchor object to position the image on
+ // the worksheet. Note how the two ClientAnchorDetail records are
+ // interrogated to recover the row/column co-ordinates and any insets.
+ // The first two parameters are not used currently but could be if the
+ // need arose to extend the functionality of this code by adding the
+ // ability to specify that a clear 'border' be placed around the image.
+ anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
+
+ anchor.setDx1(0);
+ anchor.setDy1(0);
+ anchor.setDx2(colClientAnchorDetail.getInset());
+ anchor.setDy2(rowClientAnchorDetail.getInset());
+ anchor.setCol1(colClientAnchorDetail.getFromIndex());
+ anchor.setRow1(rowClientAnchorDetail.getFromIndex());
+ anchor.setCol2(colClientAnchorDetail.getToIndex());
+ anchor.setRow2(rowClientAnchorDetail.getToIndex());
+
+ // For now, set the anchor type to do not move or resize the
+ // image as the size of the row/column is adjusted. This could easily
+ // become another parameter passed to the method. Please read the note
+ // above regarding the behaviour of image resizing.
+ anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
+
+ // Now, add the picture to the workbook. Note that unlike the similar
+ // method in the HSSF Examples section, the image type is checked. First,
+ // the image files location is identified by interrogating the URL passed
+ // to the method, the images type is identified before it is added to the
+ // sheet.
+ String sURL = imageFile.toString().toLowerCase(Locale.ROOT);
+ if( sURL.endsWith(".png") ) {
+ imageType = Workbook.PICTURE_TYPE_PNG;
+ }
+ else if( sURL.endsWith(".jpg") || sURL.endsWith(".jpeg") ) {
+ imageType = Workbook.PICTURE_TYPE_JPEG;
+ }
+ else {
+ throw new IllegalArgumentException("Invalid Image file : " +
+ sURL);
+ }
+ int index = sheet.getWorkbook().addPicture(
+ IOUtils.toByteArray(imageFile.openStream()), imageType);
+ drawing.createPicture(anchor, index);
+ }
+
+ /**
+ * Determines whether the sheets columns should be re-sized to accommodate
+ * the image, adjusts the columns width if necessary and creates then
+ * returns a ClientAnchorDetail object that facilitates construction of
+ * an ClientAnchor that will fix the image on the sheet and establish
+ * it's size.
+ *
+ * @param sheet A reference to the sheet that will 'contain' the image.
+ * @param colNumber A primitive int that contains the index number of a
+ * column on the sheet.
+ * @param reqImageWidthMM A primitive double that contains the required
+ * width of the image in millimetres
+ * @param resizeBehaviour A primitive int whose value will indicate how the
+ * width of the column should be adjusted if the
+ * required width of the image is greater than the
+ * width of the column.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the column containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number column containing the cell whose top
+ * left hand corner also defines the bottom right hand corner of
+ * the image and an inset that determines how far the right hand
+ * edge of the image can protrude into the next column - expressed
+ * as a specific number of coordinate positions.
+ */
+ private ClientAnchorDetail fitImageToColumns(Sheet sheet, int colNumber,
+ double reqImageWidthMM, int resizeBehaviour) {
+
+ double colWidthMM = 0.0D;
+ double colCoordinatesPerMM = 0.0D;
+ int pictureWidthCoordinates = 0;
+ ClientAnchorDetail colClientAnchorDetail = null;
+
+ // Get the colum's width in millimetres
+ colWidthMM = ConvertImageUnits.widthUnits2Millimetres(
+ (short)sheet.getColumnWidth(colNumber));
+
+ // Check that the column's width will accomodate the image at the
+ // required dimension. If the width of the column is LESS than the
+ // required width of the image, decide how the application should
+ // respond - resize the column or overlay the image across one or more
+ // columns.
+ if(colWidthMM < reqImageWidthMM) {
+
+ // Should the column's width simply be expanded?
+ if((resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {
+ // Set the width of the column by converting the required image
+ // width from millimetres into Excel's column width units.
+ sheet.setColumnWidth(colNumber,
+ ConvertImageUnits.millimetres2WidthUnits(reqImageWidthMM));
+ // To make the image occupy the full width of the column, convert
+ // the required width of the image into co-ordinates. This value
+ // will become the inset for the ClientAnchorDetail class that
+ // is then instantiated.
+ if(sheet instanceof HSSFSheet) {
+ colWidthMM = reqImageWidthMM;
+ colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
+ colWidthMM;
+ pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);
+
+ }
+ else {
+ pictureWidthCoordinates = (int)reqImageWidthMM * AddDimensionedImage.EMU_PER_MM;
+ }
+ colClientAnchorDetail = new ClientAnchorDetail(colNumber,
+ colNumber, pictureWidthCoordinates);
+ }
+ // If the user has chosen to overlay both rows and columns or just
+ // to expand ONLY the size of the rows, then calculate how to lay
+ // the image out across one or more columns.
+ else if ((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_ROW)) {
+ colClientAnchorDetail = this.calculateColumnLocation(sheet,
+ colNumber, reqImageWidthMM);
+ }
+ }
+ // If the column is wider than the image.
+ else {
+ if(sheet instanceof HSSFSheet) {
+ // Mow many co-ordinate positions are there per millimetre?
+ colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
+ colWidthMM;
+ // Given the width of the image, what should be it's co-ordinate?
+ pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);
+ }
+ else {
+ pictureWidthCoordinates = (int)reqImageWidthMM *
+ AddDimensionedImage.EMU_PER_MM;
+ }
+ colClientAnchorDetail = new ClientAnchorDetail(colNumber,
+ colNumber, pictureWidthCoordinates);
+ }
+ return(colClientAnchorDetail);
+ }
+
+ /**
+ * Determines whether the sheets row should be re-sized to accomodate
+ * the image, adjusts the rows height if necessary and creates then
+ * returns a ClientAnchorDetail object that facilitates construction of
+ * a ClientAnchor that will fix the image on the sheet and establish
+ * it's size.
+ *
+ * @param sheet A reference to the sheet that will 'contain' the image.
+ * @param rowNumber A primitive int that contains the index number of a
+ * row on the sheet.
+ * @param reqImageHeightMM A primitive double that contains the required
+ * height of the image in millimetres
+ * @param resizeBehaviour A primitive int whose value will indicate how the
+ * height of the row should be adjusted if the
+ * required height of the image is greater than the
+ * height of the row.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the row containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number of the row containing the cell whose
+ * top left hand corner also defines the bottom right hand
+ * corner of the image and an inset that determines how far the
+ * bottom edge of the image can protrude into the next (lower)
+ * row - expressed as a specific number of coordinate positions.
+ */
+ private ClientAnchorDetail fitImageToRows(Sheet sheet, int rowNumber,
+ double reqImageHeightMM, int resizeBehaviour) {
+ Row row = null;
+ double rowHeightMM = 0.0D;
+ double rowCoordinatesPerMM = 0.0D;
+ int pictureHeightCoordinates = 0;
+ ClientAnchorDetail rowClientAnchorDetail = null;
+
+ // Get the row and it's height
+ row = sheet.getRow(rowNumber);
+ if(row == null) {
+ // Create row if it does not exist.
+ row = sheet.createRow(rowNumber);
+ }
+
+ // Get the row's height in millimetres
+ rowHeightMM = row.getHeightInPoints() / ConvertImageUnits.POINTS_PER_MILLIMETRE;
+
+ // Check that the row's height will accomodate the image at the required
+ // dimensions. If the height of the row is LESS than the required height
+ // of the image, decide how the application should respond - resize the
+ // row or overlay the image across a series of rows.
+ if(rowHeightMM < reqImageHeightMM) {
+ if((resizeBehaviour == AddDimensionedImage.EXPAND_ROW) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {
+ row.setHeightInPoints((float)(reqImageHeightMM *
+ ConvertImageUnits.POINTS_PER_MILLIMETRE));
+ if(sheet instanceof HSSFSheet) {
+ rowHeightMM = reqImageHeightMM;
+ rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
+ rowHeightMM;
+ pictureHeightCoordinates = (int)(reqImageHeightMM *
+ rowCoordinatesPerMM);
+ }
+ else {
+ pictureHeightCoordinates = (int)(reqImageHeightMM *
+ AddDimensionedImage.EMU_PER_MM);
+ }
+ rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,
+ rowNumber, pictureHeightCoordinates);
+ }
+ // If the user has chosen to overlay both rows and columns or just
+ // to expand ONLY the size of the columns, then calculate how to lay
+ // the image out ver one or more rows.
+ else if((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN)) {
+ rowClientAnchorDetail = this.calculateRowLocation(sheet,
+ rowNumber, reqImageHeightMM);
+ }
+ }
+ // Else, if the image is smaller than the space available
+ else {
+ if(sheet instanceof HSSFSheet) {
+ rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
+ rowHeightMM;
+ pictureHeightCoordinates = (int)(reqImageHeightMM * rowCoordinatesPerMM);
+ }
+ else {
+ pictureHeightCoordinates = (int)(reqImageHeightMM *
+ AddDimensionedImage.EMU_PER_MM);
+ }
+ rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,
+ rowNumber, pictureHeightCoordinates);
+ }
+ return(rowClientAnchorDetail);
+ }
+
+ /**
+ * If the image is to overlie more than one column, calculations need to be
+ * performed to determine how many columns and whether the image will
+ * overlie just a part of one column in order to be presented at the
+ * required size.
+ *
+ * @param sheet The sheet that will 'contain' the image.
+ * @param startingColumn A primitive int whose value is the index of the
+ * column that contains the cell whose top left hand
+ * corner should be aligned with the top left hand
+ * corner of the image.
+ * @param reqImageWidthMM A primitive double whose value will indicate the
+ * required width of the image in millimetres.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the column containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number column containing the cell whose top
+ * left hand corner also defines the bottom right hand corner of
+ * the image and an inset that determines how far the right hand
+ * edge of the image can protrude into the next column - expressed
+ * as a specific number of coordinate positions.
+ */
+ private ClientAnchorDetail calculateColumnLocation(Sheet sheet,
+ int startingColumn,
+ double reqImageWidthMM) {
+ ClientAnchorDetail anchorDetail = null;
+ double totalWidthMM = 0.0D;
+ double colWidthMM = 0.0D;
+ double overlapMM = 0.0D;
+ double coordinatePositionsPerMM = 0.0D;
+ int toColumn = startingColumn;
+ int inset = 0;
+
+ // Calculate how many columns the image will have to
+ // span in order to be presented at the required size.
+ while(totalWidthMM < reqImageWidthMM) {
+ colWidthMM = ConvertImageUnits.widthUnits2Millimetres(
+ (short)(sheet.getColumnWidth(toColumn)));
+ // Note use of the cell border width constant. Testing with an image
+ // declared to fit exactly into one column demonstrated that it's
+ // width was greater than the width of the column the POI returned.
+ // Further, this difference was a constant value that I am assuming
+ // related to the cell's borders. Either way, that difference needs
+ // to be allowed for in this calculation.
+ totalWidthMM += (colWidthMM + ConvertImageUnits.CELL_BORDER_WIDTH_MILLIMETRES);
+ toColumn++;
+ }
+ // De-crement by one the last column value.
+ toColumn--;
+ // Highly unlikely that this will be true but, if the width of a series
+ // of columns is exactly equal to the required width of the image, then
+ // simply build a ClientAnchorDetail object with an inset equal to the
+ // total number of co-ordinate positions available in a column, a
+ // from column co-ordinate (top left hand corner) equal to the value
+ // of the startingColumn parameter and a to column co-ordinate equal
+ // to the toColumn variable.
+ //
+ // Convert both values to ints to perform the test.
+ if((int)totalWidthMM == (int)reqImageWidthMM) {
+ // A problem could occur if the image is sized to fit into one or
+ // more columns. If that occurs, the value in the toColumn variable
+ // will be in error. To overcome this, there are two options, to
+ // ibcrement the toColumn variable's value by one or to pass the
+ // total number of co-ordinate positions to the third paramater
+ // of the ClientAnchorDetail constructor. For no sepcific reason,
+ // the latter option is used below.
+ if(sheet instanceof HSSFSheet) {
+ anchorDetail = new ClientAnchorDetail(startingColumn,
+ toColumn, ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS);
+ }
+ else {
+ anchorDetail = new ClientAnchorDetail(startingColumn,
+ toColumn, (int)reqImageWidthMM * AddDimensionedImage.EMU_PER_MM);
+ }
+ }
+ // In this case, the image will overlap part of another column and it is
+ // necessary to calculate just how much - this will become the inset
+ // for the ClientAnchorDetail object.
+ else {
+ // Firstly, claculate how much of the image should overlap into
+ // the next column.
+ overlapMM = reqImageWidthMM - (totalWidthMM - colWidthMM);
+
+ // When the required size is very close indded to the column size,
+ // the calcaulation above can produce a negative value. To prevent
+ // problems occuring in later caculations, this is simply removed
+ // be setting the overlapMM value to zero.
+ if(overlapMM < 0) {
+ overlapMM = 0.0D;
+ }
+
+ if(sheet instanceof HSSFSheet) {
+ // Next, from the columns width, calculate how many co-ordinate
+ // positons there are per millimetre
+ coordinatePositionsPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
+ colWidthMM;
+ // From this figure, determine how many co-ordinat positions to
+ // inset the left hand or bottom edge of the image.
+ inset = (int)(coordinatePositionsPerMM * overlapMM);
+ }
+ else {
+ inset = (int)overlapMM * AddDimensionedImage.EMU_PER_MM;
+ }
+
+ // Now create the ClientAnchorDetail object, setting the from and to
+ // columns and the inset.
+ anchorDetail = new ClientAnchorDetail(startingColumn, toColumn, inset);
+ }
+ return(anchorDetail);
+ }
+
+ /**
+ * If the image is to overlie more than one rows, calculations need to be
+ * performed to determine how many rows and whether the image will
+ * overlie just a part of one row in order to be presented at the
+ * required size.
+ *
+ * @param sheet The sheet that will 'contain' the image.
+ * @param startingRow A primitive int whose value is the index of the row
+ * that contains the cell whose top left hand corner
+ * should be aligned with the top left hand corner of
+ * the image.
+ * @param reqImageHeightMM A primitive double whose value will indicate the
+ * required height of the image in millimetres.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the row containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number of the row containing the cell whose top
+ * left hand corner also defines the bottom right hand corner of
+ * the image and an inset that determines how far the bottom edge
+ * can protrude into the next (lower) row - expressed as a specific
+ * number of co-ordinate positions.
+ */
+ private ClientAnchorDetail calculateRowLocation(Sheet sheet,
+ int startingRow, double reqImageHeightMM) {
+ ClientAnchorDetail clientAnchorDetail = null;
+ Row row = null;
+ double rowHeightMM = 0.0D;
+ double totalRowHeightMM = 0.0D;
+ double overlapMM = 0.0D;
+ double rowCoordinatesPerMM = 0.0D;
+ int toRow = startingRow;
+ int inset = 0;
+
+ // Step through the rows in the sheet and accumulate a total of their
+ // heights.
+ while(totalRowHeightMM < reqImageHeightMM) {
+ row = sheet.getRow(toRow);
+ // Note, if the row does not already exist on the sheet then create
+ // it here.
+ if(row == null) {
+ row = sheet.createRow(toRow);
+ }
+ // Get the row's height in millimetres and add to the running total.
+ rowHeightMM = row.getHeightInPoints() /
+ ConvertImageUnits.POINTS_PER_MILLIMETRE;
+ totalRowHeightMM += rowHeightMM;
+ toRow++;
+ }
+ // Owing to the way the loop above works, the rowNumber will have been
+ // incremented one row too far. Undo that here.
+ toRow--;
+ // Check to see whether the image should occupy an exact number of
+ // rows. If so, build the ClientAnchorDetail record to point
+ // to those rows and with an inset of the total number of co-ordinate
+ // position in the row.
+ //
+ // To overcome problems that can occur with comparing double values for
+ // equality, cast both to int(s) to truncate the value; VERY crude and
+ // I do not really like it!!
+ if((int)totalRowHeightMM == (int)reqImageHeightMM) {
+ if(sheet instanceof HSSFSheet) {
+ clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,
+ ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS);
+ }
+ else {
+ clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,
+ (int)reqImageHeightMM * AddDimensionedImage.EMU_PER_MM);
+ }
+ }
+ else {
+ // Calculate how far the image will project into the next row. Note
+ // that the height of the last row assessed is subtracted from the
+ // total height of all rows assessed so far.
+ overlapMM = reqImageHeightMM - (totalRowHeightMM - rowHeightMM);
+
+ // To prevent an exception being thrown when the required width of
+ // the image is very close indeed to the column size.
+ if(overlapMM < 0) {
+ overlapMM = 0.0D;
+ }
+
+ if(sheet instanceof HSSFSheet) {
+ rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
+ rowHeightMM;
+ inset = (int)(overlapMM * rowCoordinatesPerMM);
+ }
+ else {
+ inset = (int)overlapMM * AddDimensionedImage.EMU_PER_MM;
+ }
+ clientAnchorDetail = new ClientAnchorDetail(startingRow,
+ toRow, inset);
+ }
+ return(clientAnchorDetail);
+ }
+
+ /**
+ * The main entry point to the program. It contains code that demonstrates
+ * one way to use the program.
+ *
+ * Note, the code is not restricted to use on new workbooks only. If an
+ * image is to be inserted into an existing workbook. just open that
+ * workbook, gat a reference to a sheet and pass that;
+ *
+ * AddDimensionedImage addImage = new AddDimensionedImage();
+ *
+ * File file = new File("....... Existing Workbook .......");
+ * FileInputStream fis = new FileInputStream(file);
+ * Workbook workbook = new HSSFWorkbook(fis);
+ * HSSFSheet sheet = workbook.getSheetAt(0);
+ * addImage.addImageToSheet("C3", sheet, "image.jpg", 30, 20,
+ * AddDimensionedImage.EXPAND.ROW);
+ *
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) throws IOException {
+ String imageFile = null;
+ String outputFile = null;
+ FileOutputStream fos = null;
+ Workbook workbook = null;
+ Sheet sheet = null;
+
+ if(args.length < 2){
+ System.err.println("Usage: AddDimensionedImage imageFile outputFile");
+ return;
+ }
+ workbook = new HSSFWorkbook(); // OR XSSFWorkbook
+ sheet = workbook.createSheet("Picture Test");
+ imageFile = args[0];
+ outputFile = args[1];
+ new AddDimensionedImage().addImageToSheet("B5", sheet, sheet.createDrawingPatriarch(),
+ new File(imageFile).toURI().toURL(), 100, 40,
+ AddDimensionedImage.EXPAND_ROW_AND_COLUMN);
+ fos = new FileOutputStream(outputFile);
+ workbook.write(fos);
+ fos.close();
+ workbook.close();
+ }
+
+ /**
+ * The HSSFClientAnchor class accepts eight arguments. In order, these are;
+ *
+ * * How far the left hand edge of the image is inset from the left hand
+ * edge of the cell
+ * * How far the top edge of the image is inset from the top of the cell
+ * * How far the right hand edge of the image is inset from the left
+ * hand edge of the cell
+ * * How far the bottom edge of the image is inset from the top of the
+ * cell.
+ * * Together, arguments five and six determine the column and row
+ * coordinates of the cell whose top left hand corner will be aligned
+ * with the images top left hand corner.
+ * * Together, arguments seven and eight determine the column and row
+ * coordinates of the cell whose top left hand corner will be aligned
+ * with the images bottom right hand corner.
+ *
+ * An instance of the ClientAnchorDetail class provides three of the eight
+ * parameters, one of the coordinates for the images top left hand corner,
+ * one of the coordinates for the images bottom right hand corner and
+ * either how far the image should be inset from the top or the left hand
+ * edge of the cell.
+ *
+ * @author Mark Beardsley [msb at apache.org]
+ * @version 1.00 5th August 2009.
+ */
+ public class ClientAnchorDetail {
+
+ public int fromIndex = 0;
+ public int toIndex = 0;
+ public int inset = 0;
+
+ /**
+ * Create a new instance of the ClientAnchorDetail class using the
+ * following parameters.
+ *
+ * @param fromIndex A primitive int that contains one of the
+ * coordinates (row or column index) for the top left
+ * hand corner of the image.
+ * @param toIndex A primitive int that contains one of the
+ * coordinates (row or column index) for the bottom
+ * right hand corner of the image.
+ * @param inset A primitive int that contains a value which indicates
+ * how far the image should be inset from the top or the
+ * left hand edge of a cell.
+ */
+ public ClientAnchorDetail(int fromIndex, int toIndex, int inset) {
+ this.fromIndex = fromIndex;
+ this.toIndex = toIndex;
+ this.inset = inset;
+ }
+
+ /**
+ * Get one of the number of the column or row that contains the cell
+ * whose top left hand corner will be aligned with the top left hand
+ * corner of the image.
+ *
+ * @return The value - row or column index - for one of the coordinates
+ * of the top left hand corner of the image.
+ */
+ public int getFromIndex() {
+ return(this.fromIndex);
+ }
+
+ /**
+ * Get one of the number of the column or row that contains the cell
+ * whose top left hand corner will be aligned with the bottom right hand
+ * corner of the image.
+ *
+ * @return The value - row or column index - for one of the coordinates
+ * of the bottom right hand corner of the image.
+ */
+ public int getToIndex() {
+ return(this.toIndex);
+ }
+
+ /**
+ * Get the images offset from the edge of a cell.
+ *
+ * @return How far either the right hand or bottom edge of the image is
+ * inset from the left hand or top edge of a cell.
+ */
+ public int getInset() {
+ return(this.inset);
+ }
+ }
+
+ /**
+ * Utility methods used to convert Excels character based column and row
+ * size measurements into pixels and/or millimetres. The class also contains
+ * various constants that are required in other calculations.
+ *
+ * @author xio[darjino@hotmail.com]
+ * @version 1.01 30th July 2009.
+ * Added by Mark Beardsley [msb at apache.org].
+ * Additional constants.
+ * widthUnits2Millimetres() and millimetres2Units() methods.
+ */
+ public static class ConvertImageUnits {
+
+ // Each cell conatins a fixed number of co-ordinate points; this number
+ // does not vary with row height or column width or with font. These two
+ // constants are defined below.
+ public static final int TOTAL_COLUMN_COORDINATE_POSITIONS = 1023; // MB
+ public static final int TOTAL_ROW_COORDINATE_POSITIONS = 255; // MB
+ // The resoultion of an image can be expressed as a specific number
+ // of pixels per inch. Displays and printers differ but 96 pixels per
+ // inch is an acceptable standard to beging with.
+ public static final int PIXELS_PER_INCH = 96; // MB
+ // Cnstants that defines how many pixels and points there are in a
+ // millimetre. These values are required for the conversion algorithm.
+ public static final double PIXELS_PER_MILLIMETRES = 3.78; // MB
+ public static final double POINTS_PER_MILLIMETRE = 2.83; // MB
+ // The column width returned by HSSF and the width of a picture when
+ // positioned to exactly cover one cell are different by almost exactly
+ // 2mm - give or take rounding errors. This constant allows that
+ // additional amount to be accounted for when calculating how many
+ // celles the image ought to overlie.
+ public static final double CELL_BORDER_WIDTH_MILLIMETRES = 2.0D; // MB
+ public static final short EXCEL_COLUMN_WIDTH_FACTOR = 256;
+ public static final int UNIT_OFFSET_LENGTH = 7;
+ public static final int[] UNIT_OFFSET_MAP = new int[]
+ { 0, 36, 73, 109, 146, 182, 219 };
+
+ /**
+ * pixel units to excel width units(units of 1/256th of a character width)
+ * @param pxs
+ * @return
+ */
+ public static short pixel2WidthUnits(int pxs) {
+ short widthUnits = (short) (EXCEL_COLUMN_WIDTH_FACTOR *
+ (pxs / UNIT_OFFSET_LENGTH));
+ widthUnits += UNIT_OFFSET_MAP[(pxs % UNIT_OFFSET_LENGTH)];
+ return widthUnits;
+ }
+
+ /**
+ * excel width units(units of 1/256th of a character width) to pixel
+ * units.
+ *
+ * @param widthUnits
+ * @return
+ */
+ public static int widthUnits2Pixel(short widthUnits) {
+ int pixels = (widthUnits / EXCEL_COLUMN_WIDTH_FACTOR)
+ * UNIT_OFFSET_LENGTH;
+ int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR;
+ pixels += Math.round(offsetWidthUnits /
+ ((float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH));
+ return pixels;
+ }
+
+ /**
+ * Convert Excels width units into millimetres.
+ *
+ * @param widthUnits The width of the column or the height of the
+ * row in Excels units.
+ * @return A primitive double that contains the columns width or rows
+ * height in millimetres.
+ */
+ public static double widthUnits2Millimetres(short widthUnits) {
+ return(ConvertImageUnits.widthUnits2Pixel(widthUnits) /
+ ConvertImageUnits.PIXELS_PER_MILLIMETRES);
+ }
+
+ /**
+ * Convert into millimetres Excels width units..
+ *
+ * @param millimetres A primitive double that contains the columns
+ * width or rows height in millimetres.
+ * @return A primitive int that contains the columns width or rows
+ * height in Excels units.
+ */
+ public static int millimetres2WidthUnits(double millimetres) {
+ return(ConvertImageUnits.pixel2WidthUnits((int)(millimetres *
+ ConvertImageUnits.PIXELS_PER_MILLIMETRES)));
+ }
+
+ public static int pointsToPixels(double points) {
+ return (int) Math.round(points / 72D * PIXELS_PER_INCH);
+ }
+
+ public static double pointsToMillimeters(double points) {
+ return points / 72D * 25.4;
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/ss/examples/AligningCells.java b/src/examples/src/org/apache/poi/ss/examples/AligningCells.java
index 8ee6440fdc..c767f24d96 100644
--- a/src/examples/src/org/apache/poi/ss/examples/AligningCells.java
+++ b/src/examples/src/org/apache/poi/ss/examples/AligningCells.java
@@ -1,75 +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.poi.ss.examples;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.poi.ss.usermodel.*;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-
-/**
- * Shows how various alignment options work.
- */
-public class AligningCells {
-
- public static void main(String[] args) throws IOException {
- Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
-
- Sheet sheet = wb.createSheet();
- Row row = sheet.createRow(2);
- row.setHeightInPoints(30);
- for (int i = 0; i < 8; i++) {
- //column width is set in units of 1/256th of a character width
- sheet.setColumnWidth(i, 256 * 15);
- }
-
- createCell(wb, row, 0, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
- createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION, VerticalAlignment.BOTTOM);
- createCell(wb, row, 2, HorizontalAlignment.FILL, VerticalAlignment.CENTER);
- createCell(wb, row, 3, HorizontalAlignment.GENERAL, VerticalAlignment.CENTER);
- createCell(wb, row, 4, HorizontalAlignment.JUSTIFY, VerticalAlignment.JUSTIFY);
- createCell(wb, row, 5, HorizontalAlignment.LEFT, VerticalAlignment.TOP);
- createCell(wb, row, 6, HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
-
- // Write the output to a file
- OutputStream fileOut = new FileOutputStream("ss-example-align.xlsx");
- wb.write(fileOut);
- fileOut.close();
-
- wb.close();
- }
-
- /**
- * Creates a cell and aligns it a certain way.
- *
- * @param wb the workbook
- * @param row the row to create the cell in
- * @param column the column number to create the cell in
- * @param halign the horizontal alignment for the cell.
- */
- private static void createCell(Workbook wb, Row row, int column, HorizontalAlignment halign, VerticalAlignment valign) {
- CreationHelper ch = wb.getCreationHelper();
- Cell cell = row.createCell(column);
- cell.setCellValue(ch.createRichTextString("Align It"));
- CellStyle cellStyle = wb.createCellStyle();
- cellStyle.setAlignment(halign);
- cellStyle.setVerticalAlignment(valign);
- cell.setCellStyle(cellStyle);
- }
+/* ====================================================================
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==================================================================== */
+package org.apache.poi.ss.examples;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Shows how various alignment options work.
+ */
+public class AligningCells {
+
+ public static void main(String[] args) throws IOException {
+ Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
+
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(2);
+ row.setHeightInPoints(30);
+ for (int i = 0; i < 8; i++) {
+ //column width is set in units of 1/256th of a character width
+ sheet.setColumnWidth(i, 256 * 15);
+ }
+
+ createCell(wb, row, 0, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
+ createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION, VerticalAlignment.BOTTOM);
+ createCell(wb, row, 2, HorizontalAlignment.FILL, VerticalAlignment.CENTER);
+ createCell(wb, row, 3, HorizontalAlignment.GENERAL, VerticalAlignment.CENTER);
+ createCell(wb, row, 4, HorizontalAlignment.JUSTIFY, VerticalAlignment.JUSTIFY);
+ createCell(wb, row, 5, HorizontalAlignment.LEFT, VerticalAlignment.TOP);
+ createCell(wb, row, 6, HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
+
+ // Write the output to a file
+ OutputStream fileOut = new FileOutputStream("ss-example-align.xlsx");
+ wb.write(fileOut);
+ fileOut.close();
+
+ wb.close();
+ }
+
+ /**
+ * Creates a cell and aligns it a certain way.
+ *
+ * @param wb the workbook
+ * @param row the row to create the cell in
+ * @param column the column number to create the cell in
+ * @param halign the horizontal alignment for the cell.
+ */
+ private static void createCell(Workbook wb, Row row, int column, HorizontalAlignment halign, VerticalAlignment valign) {
+ CreationHelper ch = wb.getCreationHelper();
+ Cell cell = row.createCell(column);
+ cell.setCellValue(ch.createRichTextString("Align It"));
+ CellStyle cellStyle = wb.createCellStyle();
+ cellStyle.setAlignment(halign);
+ cellStyle.setVerticalAlignment(valign);
+ cell.setCellStyle(cellStyle);
+ }
}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/ss/examples/CellStyleDetails.java b/src/examples/src/org/apache/poi/ss/examples/CellStyleDetails.java
index 7bf8c55e85..55b85a291b 100644
--- a/src/examples/src/org/apache/poi/ss/examples/CellStyleDetails.java
+++ b/src/examples/src/org/apache/poi/ss/examples/CellStyleDetails.java
@@ -1,96 +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.poi.ss.examples;
-
-import java.io.File;
-
-import org.apache.poi.hssf.usermodel.HSSFFont;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.util.HSSFColor;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.CellStyle;
-import org.apache.poi.ss.usermodel.Color;
-import org.apache.poi.ss.usermodel.DataFormatter;
-import org.apache.poi.ss.usermodel.Font;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.WorkbookFactory;
-import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.xssf.usermodel.XSSFColor;
-import org.apache.poi.xssf.usermodel.XSSFFont;
-
-/**
- * Demonstrates how to read excel styles for cells
- */
-public class CellStyleDetails {
- public static void main(String[] args) throws Exception {
- if(args.length == 0) {
- throw new IllegalArgumentException("Filename must be given");
- }
-
- Workbook wb = WorkbookFactory.create(new File(args[0]));
- DataFormatter formatter = new DataFormatter();
-
- for(int sn=0; sn
- * Partly based on the code snippets from
- * http://www.contextures.com/xlcondformat03.html
- *
- */
-public class ConditionalFormats {
-
- public static void main(String[] args) throws IOException {
- Workbook wb;
-
- if(args.length > 0 && args[0].equals("-xls")) {
- wb = new HSSFWorkbook();
- } else {
- wb = new XSSFWorkbook();
- }
-
- sameCell(wb.createSheet("Same Cell"));
- multiCell(wb.createSheet("MultiCell"));
- overlapping(wb.createSheet("Overlapping"));
- errors(wb.createSheet("Errors"));
- hideDupplicates(wb.createSheet("Hide Dups"));
- formatDuplicates(wb.createSheet("Duplicates"));
- inList(wb.createSheet("In List"));
- expiry(wb.createSheet("Expiry"));
- shadeAlt(wb.createSheet("Shade Alt"));
- shadeBands(wb.createSheet("Shade Bands"));
- iconSets(wb.createSheet("Icon Sets"));
- colourScales(wb.createSheet("Colour Scales"));
- dataBars(wb.createSheet("Data Bars"));
-
- // Write the output to a file
- String file = "cf-poi.xls";
- if(wb instanceof XSSFWorkbook) {
- file += "x";
- }
- FileOutputStream out = new FileOutputStream(file);
- wb.write(out);
- out.close();
- System.out.println("Generated: " + file);
- wb.close();
- }
-
- /**
- * Highlight cells based on their values
- */
- static void sameCell(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue(84);
- sheet.createRow(1).createCell(0).setCellValue(74);
- sheet.createRow(2).createCell(0).setCellValue(50);
- sheet.createRow(3).createCell(0).setCellValue(51);
- sheet.createRow(4).createCell(0).setCellValue(49);
- sheet.createRow(5).createCell(0).setCellValue(41);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Cell Value Is greater than 70 (Blue Fill)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(ComparisonOperator.GT, "70");
- PatternFormatting fill1 = rule1.createPatternFormatting();
- fill1.setFillBackgroundColor(IndexedColors.BLUE.index);
- fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- // Condition 2: Cell Value Is less than 50 (Green Fill)
- ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule(ComparisonOperator.LT, "50");
- PatternFormatting fill2 = rule2.createPatternFormatting();
- fill2.setFillBackgroundColor(IndexedColors.GREEN.index);
- fill2.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A1:A6")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1, rule2);
-
- sheet.getRow(0).createCell(2).setCellValue("<== Condition 1: Cell Value Is greater than 70 (Blue Fill)");
- sheet.getRow(4).createCell(2).setCellValue("<== Condition 2: Cell Value Is less than 50 (Green Fill)");
- }
-
- /**
- * Highlight multiple cells based on a formula
- */
- static void multiCell(Sheet sheet) {
- // header row
- Row row0 = sheet.createRow(0);
- row0.createCell(0).setCellValue("Units");
- row0.createCell(1).setCellValue("Cost");
- row0.createCell(2).setCellValue("Total");
-
- Row row1 = sheet.createRow(1);
- row1.createCell(0).setCellValue(71);
- row1.createCell(1).setCellValue(29);
- row1.createCell(2).setCellValue(2059);
-
- Row row2 = sheet.createRow(2);
- row2.createCell(0).setCellValue(85);
- row2.createCell(1).setCellValue(29);
- row2.createCell(2).setCellValue(2059);
-
- Row row3 = sheet.createRow(3);
- row3.createCell(0).setCellValue(71);
- row3.createCell(1).setCellValue(29);
- row3.createCell(2).setCellValue(2059);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =$B2>75 (Blue Fill)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("$A2>75");
- PatternFormatting fill1 = rule1.createPatternFormatting();
- fill1.setFillBackgroundColor(IndexedColors.BLUE.index);
- fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A2:C4")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.getRow(2).createCell(4).setCellValue("<== Condition 1: Formula Is =$B2>75 (Blue Fill)");
- }
-
- /**
- * Multiple conditional formatting rules can apply to
- * one cell, some combining, some beating others.
- * Done in order of the rules added to the
- * SheetConditionalFormatting object
- */
- static void overlapping(Sheet sheet) {
- for (int i=0; i<40; i++) {
- int rn = i+1;
- Row r = sheet.createRow(i);
- r.createCell(0).setCellValue("This is row " + rn + " (" + i + ")");
- String str = "";
- if (rn%2 == 0) {
- str = str + "even ";
- }
- if (rn%3 == 0) {
- str = str + "x3 ";
- }
- if (rn%5 == 0) {
- str = str + "x5 ";
- }
- if (rn%10 == 0) {
- str = str + "x10 ";
- }
- if (str.length() == 0) {
- str = "nothing special...";
- }
- r.createCell(1).setCellValue("It is " + str);
- }
- sheet.autoSizeColumn(0);
- sheet.autoSizeColumn(1);
-
- sheet.getRow(1).createCell(3).setCellValue("Even rows are blue");
- sheet.getRow(2).createCell(3).setCellValue("Multiples of 3 have a grey background");
- sheet.getRow(4).createCell(3).setCellValue("Multiples of 5 are bold");
- sheet.getRow(9).createCell(3).setCellValue("Multiples of 10 are red (beats even)");
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Row divides by 10, red (will beat #1)
- ConditionalFormattingRule rule1 =
- sheetCF.createConditionalFormattingRule("MOD(ROW(),10)=0");
- FontFormatting font1 = rule1.createFontFormatting();
- font1.setFontColorIndex(IndexedColors.RED.index);
-
- // Condition 2: Row is even, blue
- ConditionalFormattingRule rule2 =
- sheetCF.createConditionalFormattingRule("MOD(ROW(),2)=0");
- FontFormatting font2 = rule2.createFontFormatting();
- font2.setFontColorIndex(IndexedColors.BLUE.index);
-
- // Condition 3: Row divides by 5, bold
- ConditionalFormattingRule rule3 =
- sheetCF.createConditionalFormattingRule("MOD(ROW(),5)=0");
- FontFormatting font3 = rule3.createFontFormatting();
- font3.setFontStyle(false, true);
-
- // Condition 4: Row divides by 3, grey background
- ConditionalFormattingRule rule4 =
- sheetCF.createConditionalFormattingRule("MOD(ROW(),3)=0");
- PatternFormatting fill4 = rule4.createPatternFormatting();
- fill4.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index);
- fill4.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- // Apply
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A1:F41")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
- sheetCF.addConditionalFormatting(regions, rule2);
- sheetCF.addConditionalFormatting(regions, rule3);
- sheetCF.addConditionalFormatting(regions, rule4);
- }
-
- /**
- * Use Excel conditional formatting to check for errors,
- * and change the font colour to match the cell colour.
- * In this example, if formula result is #DIV/0! then it will have white font colour.
- */
- static void errors(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue(84);
- sheet.createRow(1).createCell(0).setCellValue(0);
- sheet.createRow(2).createCell(0).setCellFormula("ROUND(A1/A2,0)");
- sheet.createRow(3).createCell(0).setCellValue(0);
- sheet.createRow(4).createCell(0).setCellFormula("ROUND(A6/A4,0)");
- sheet.createRow(5).createCell(0).setCellValue(41);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =ISERROR(C2) (White Font)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("ISERROR(A1)");
- FontFormatting font = rule1.createFontFormatting();
- font.setFontColorIndex(IndexedColors.WHITE.index);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A1:A6")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.getRow(2).createCell(1).setCellValue("<== The error in this cell is hidden. Condition: Formula Is =ISERROR(C2) (White Font)");
- sheet.getRow(4).createCell(1).setCellValue("<== The error in this cell is hidden. Condition: Formula Is =ISERROR(C2) (White Font)");
- }
-
- /**
- * Use Excel conditional formatting to hide the duplicate values,
- * and make the list easier to read. In this example, when the table is sorted by Region,
- * the second (and subsequent) occurences of each region name will have white font colour.
- */
- static void hideDupplicates(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue("City");
- sheet.createRow(1).createCell(0).setCellValue("Boston");
- sheet.createRow(2).createCell(0).setCellValue("Boston");
- sheet.createRow(3).createCell(0).setCellValue("Chicago");
- sheet.createRow(4).createCell(0).setCellValue("Chicago");
- sheet.createRow(5).createCell(0).setCellValue("New York");
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =A2=A1 (White Font)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("A2=A1");
- FontFormatting font = rule1.createFontFormatting();
- font.setFontColorIndex(IndexedColors.WHITE.index);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A2:A6")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.getRow(1).createCell(1).setCellValue("<== the second (and subsequent) " +
- "occurences of each region name will have white font colour. " +
- "Condition: Formula Is =A2=A1 (White Font)");
- }
-
- /**
- * Use Excel conditional formatting to highlight duplicate entries in a column.
- */
- static void formatDuplicates(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue("Code");
- sheet.createRow(1).createCell(0).setCellValue(4);
- sheet.createRow(2).createCell(0).setCellValue(3);
- sheet.createRow(3).createCell(0).setCellValue(6);
- sheet.createRow(4).createCell(0).setCellValue(3);
- sheet.createRow(5).createCell(0).setCellValue(5);
- sheet.createRow(6).createCell(0).setCellValue(8);
- sheet.createRow(7).createCell(0).setCellValue(0);
- sheet.createRow(8).createCell(0).setCellValue(2);
- sheet.createRow(9).createCell(0).setCellValue(8);
- sheet.createRow(10).createCell(0).setCellValue(6);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =A2=A1 (White Font)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("COUNTIF($A$2:$A$11,A2)>1");
- FontFormatting font = rule1.createFontFormatting();
- font.setFontStyle(false, true);
- font.setFontColorIndex(IndexedColors.BLUE.index);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A2:A11")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.getRow(2).createCell(1).setCellValue("<== Duplicates numbers in the column are highlighted. " +
- "Condition: Formula Is =COUNTIF($A$2:$A$11,A2)>1 (Blue Font)");
- }
-
- /**
- * Use Excel conditional formatting to highlight items that are in a list on the worksheet.
- */
- static void inList(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue("Codes");
- sheet.createRow(1).createCell(0).setCellValue("AA");
- sheet.createRow(2).createCell(0).setCellValue("BB");
- sheet.createRow(3).createCell(0).setCellValue("GG");
- sheet.createRow(4).createCell(0).setCellValue("AA");
- sheet.createRow(5).createCell(0).setCellValue("FF");
- sheet.createRow(6).createCell(0).setCellValue("XX");
- sheet.createRow(7).createCell(0).setCellValue("CC");
-
- sheet.getRow(0).createCell(2).setCellValue("Valid");
- sheet.getRow(1).createCell(2).setCellValue("AA");
- sheet.getRow(2).createCell(2).setCellValue("BB");
- sheet.getRow(3).createCell(2).setCellValue("CC");
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =A2=A1 (White Font)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("COUNTIF($C$2:$C$4,A2)");
- PatternFormatting fill1 = rule1.createPatternFormatting();
- fill1.setFillBackgroundColor(IndexedColors.LIGHT_BLUE.index);
- fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A2:A8")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.getRow(2).createCell(3).setCellValue("<== Use Excel conditional formatting to highlight items that are in a list on the worksheet");
- }
-
- /**
- * Use Excel conditional formatting to highlight payments that are due in the next thirty days.
- * In this example, Due dates are entered in cells A2:A4.
- */
- static void expiry(Sheet sheet) {
- CellStyle style = sheet.getWorkbook().createCellStyle();
- style.setDataFormat((short)BuiltinFormats.getBuiltinFormat("d-mmm"));
-
- sheet.createRow(0).createCell(0).setCellValue("Date");
- sheet.createRow(1).createCell(0).setCellFormula("TODAY()+29");
- sheet.createRow(2).createCell(0).setCellFormula("A2+1");
- sheet.createRow(3).createCell(0).setCellFormula("A3+1");
-
- for(int rownum = 1; rownum <= 3; rownum++) {
- sheet.getRow(rownum).getCell(0).setCellStyle(style);
- }
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =A2=A1 (White Font)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("AND(A2-TODAY()>=0,A2-TODAY()<=30)");
- FontFormatting font = rule1.createFontFormatting();
- font.setFontStyle(false, true);
- font.setFontColorIndex(IndexedColors.BLUE.index);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A2:A4")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.getRow(0).createCell(1).setCellValue("Dates within the next 30 days are highlighted");
- }
-
- /**
- * Use Excel conditional formatting to shade alternating rows on the worksheet
- */
- static void shadeAlt(Sheet sheet) {
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- // Condition 1: Formula Is =A2=A1 (White Font)
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("MOD(ROW(),2)");
- PatternFormatting fill1 = rule1.createPatternFormatting();
- fill1.setFillBackgroundColor(IndexedColors.LIGHT_GREEN.index);
- fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A1:Z100")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.createRow(0).createCell(1).setCellValue("Shade Alternating Rows");
- sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),2) (Light Green Fill)");
- }
-
- /**
- * You can use Excel conditional formatting to shade bands of rows on the worksheet.
- * In this example, 3 rows are shaded light grey, and 3 are left with no shading.
- * In the MOD function, the total number of rows in the set of banded rows (6) is entered.
- */
- static void shadeBands(Sheet sheet) {
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("MOD(ROW(),6)<3");
- PatternFormatting fill1 = rule1.createPatternFormatting();
- fill1.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index);
- fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
-
- CellRangeAddress[] regions = {
- CellRangeAddress.valueOf("A1:Z100")
- };
-
- sheetCF.addConditionalFormatting(regions, rule1);
-
- sheet.createRow(0).createCell(1).setCellValue("Shade Bands of Rows");
- sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),6)<2 (Light Grey Fill)");
- }
-
- /**
- * Icon Sets / Multi-States allow you to have icons shown which vary
- * based on the values, eg Red traffic light / Yellow traffic light /
- * Green traffic light
- */
- static void iconSets(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue("Icon Sets");
- Row r = sheet.createRow(1);
- r.createCell(0).setCellValue("Reds");
- r.createCell(1).setCellValue(0);
- r.createCell(2).setCellValue(0);
- r.createCell(3).setCellValue(0);
- r = sheet.createRow(2);
- r.createCell(0).setCellValue("Yellows");
- r.createCell(1).setCellValue(5);
- r.createCell(2).setCellValue(5);
- r.createCell(3).setCellValue(5);
- r = sheet.createRow(3);
- r.createCell(0).setCellValue("Greens");
- r.createCell(1).setCellValue(10);
- r.createCell(2).setCellValue(10);
- r.createCell(3).setCellValue(10);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- CellRangeAddress[] regions = { CellRangeAddress.valueOf("B1:B4") };
- ConditionalFormattingRule rule1 =
- sheetCF.createConditionalFormattingRule(IconSet.GYR_3_TRAFFIC_LIGHTS);
- IconMultiStateFormatting im1 = rule1.getMultiStateFormatting();
- im1.getThresholds()[0].setRangeType(RangeType.MIN);
- im1.getThresholds()[1].setRangeType(RangeType.PERCENT);
- im1.getThresholds()[1].setValue(33d);
- im1.getThresholds()[2].setRangeType(RangeType.MAX);
- sheetCF.addConditionalFormatting(regions, rule1);
-
- regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C1:C4") };
- ConditionalFormattingRule rule2 =
- sheetCF.createConditionalFormattingRule(IconSet.GYR_3_FLAGS);
- IconMultiStateFormatting im2 = rule1.getMultiStateFormatting();
- im2.getThresholds()[0].setRangeType(RangeType.PERCENT);
- im2.getThresholds()[0].setValue(0d);
- im2.getThresholds()[1].setRangeType(RangeType.PERCENT);
- im2.getThresholds()[1].setValue(33d);
- im2.getThresholds()[2].setRangeType(RangeType.PERCENT);
- im2.getThresholds()[2].setValue(67d);
- sheetCF.addConditionalFormatting(regions, rule2);
-
- regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D1:D4") };
- ConditionalFormattingRule rule3 =
- sheetCF.createConditionalFormattingRule(IconSet.GYR_3_SYMBOLS_CIRCLE);
- IconMultiStateFormatting im3 = rule1.getMultiStateFormatting();
- im3.setIconOnly(true);
- im3.getThresholds()[0].setRangeType(RangeType.MIN);
- im3.getThresholds()[1].setRangeType(RangeType.NUMBER);
- im3.getThresholds()[1].setValue(3d);
- im3.getThresholds()[2].setRangeType(RangeType.NUMBER);
- im3.getThresholds()[2].setValue(7d);
- sheetCF.addConditionalFormatting(regions, rule3);
- }
-
- /**
- * Color Scales / Colour Scales / Colour Gradients allow you shade the
- * background colour of the cell based on the values, eg from Red to
- * Yellow to Green.
- */
- static void colourScales(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue("Colour Scales");
- Row r = sheet.createRow(1);
- r.createCell(0).setCellValue("Red-Yellow-Green");
- for (int i=1; i<=7; i++) {
- r.createCell(i).setCellValue((i-1)*5);
- }
- r = sheet.createRow(2);
- r.createCell(0).setCellValue("Red-White-Blue");
- for (int i=1; i<=9; i++) {
- r.createCell(i).setCellValue((i-1)*5);
- }
- r = sheet.createRow(3);
- r.createCell(0).setCellValue("Blue-Green");
- for (int i=1; i<=16; i++) {
- r.createCell(i).setCellValue((i-1));
- }
- sheet.setColumnWidth(0, 5000);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- CellRangeAddress[] regions = { CellRangeAddress.valueOf("B2:H2") };
- ConditionalFormattingRule rule1 =
- sheetCF.createConditionalFormattingColorScaleRule();
- ColorScaleFormatting cs1 = rule1.getColorScaleFormatting();
- cs1.getThresholds()[0].setRangeType(RangeType.MIN);
- cs1.getThresholds()[1].setRangeType(RangeType.PERCENTILE);
- cs1.getThresholds()[1].setValue(50d);
- cs1.getThresholds()[2].setRangeType(RangeType.MAX);
- ((ExtendedColor)cs1.getColors()[0]).setARGBHex("FFF8696B");
- ((ExtendedColor)cs1.getColors()[1]).setARGBHex("FFFFEB84");
- ((ExtendedColor)cs1.getColors()[2]).setARGBHex("FF63BE7B");
- sheetCF.addConditionalFormatting(regions, rule1);
-
- regions = new CellRangeAddress[] { CellRangeAddress.valueOf("B3:J3") };
- ConditionalFormattingRule rule2 =
- sheetCF.createConditionalFormattingColorScaleRule();
- ColorScaleFormatting cs2 = rule2.getColorScaleFormatting();
- cs2.getThresholds()[0].setRangeType(RangeType.MIN);
- cs2.getThresholds()[1].setRangeType(RangeType.PERCENTILE);
- cs2.getThresholds()[1].setValue(50d);
- cs2.getThresholds()[2].setRangeType(RangeType.MAX);
- ((ExtendedColor)cs2.getColors()[0]).setARGBHex("FFF8696B");
- ((ExtendedColor)cs2.getColors()[1]).setARGBHex("FFFCFCFF");
- ((ExtendedColor)cs2.getColors()[2]).setARGBHex("FF5A8AC6");
- sheetCF.addConditionalFormatting(regions, rule2);
-
- regions = new CellRangeAddress[] { CellRangeAddress.valueOf("B4:Q4") };
- ConditionalFormattingRule rule3=
- sheetCF.createConditionalFormattingColorScaleRule();
- ColorScaleFormatting cs3 = rule3.getColorScaleFormatting();
- cs3.setNumControlPoints(2);
- cs3.getThresholds()[0].setRangeType(RangeType.MIN);
- cs3.getThresholds()[1].setRangeType(RangeType.MAX);
- ((ExtendedColor)cs3.getColors()[0]).setARGBHex("FF5A8AC6");
- ((ExtendedColor)cs3.getColors()[1]).setARGBHex("FF63BE7B");
- sheetCF.addConditionalFormatting(regions, rule3);
- }
-
- /**
- * DataBars / Data-Bars allow you to have bars shown vary
- * based on the values, from full to empty
- */
- static void dataBars(Sheet sheet) {
- sheet.createRow(0).createCell(0).setCellValue("Data Bars");
- Row r = sheet.createRow(1);
- r.createCell(1).setCellValue("Green Positive");
- r.createCell(2).setCellValue("Blue Mix");
- r.createCell(3).setCellValue("Red Negative");
- r = sheet.createRow(2);
- r.createCell(1).setCellValue(0);
- r.createCell(2).setCellValue(0);
- r.createCell(3).setCellValue(0);
- r = sheet.createRow(3);
- r.createCell(1).setCellValue(5);
- r.createCell(2).setCellValue(-5);
- r.createCell(3).setCellValue(-5);
- r = sheet.createRow(4);
- r.createCell(1).setCellValue(10);
- r.createCell(2).setCellValue(10);
- r.createCell(3).setCellValue(-10);
- r = sheet.createRow(5);
- r.createCell(1).setCellValue(5);
- r.createCell(2).setCellValue(5);
- r.createCell(3).setCellValue(-5);
- r = sheet.createRow(6);
- r.createCell(1).setCellValue(20);
- r.createCell(2).setCellValue(-10);
- r.createCell(3).setCellValue(-20);
- sheet.setColumnWidth(0, 3000);
- sheet.setColumnWidth(1, 5000);
- sheet.setColumnWidth(2, 5000);
- sheet.setColumnWidth(3, 5000);
-
- SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
-
- ExtendedColor color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
- color.setARGBHex("FF63BE7B");
- CellRangeAddress[] regions = { CellRangeAddress.valueOf("B2:B7") };
- ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(color);
- DataBarFormatting db1 = rule1.getDataBarFormatting();
- db1.getMinThreshold().setRangeType(RangeType.MIN);
- db1.getMaxThreshold().setRangeType(RangeType.MAX);
- sheetCF.addConditionalFormatting(regions, rule1);
-
- color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
- color.setARGBHex("FF5A8AC6");
- regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C2:C7") };
- ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule(color);
- DataBarFormatting db2 = rule2.getDataBarFormatting();
- db2.getMinThreshold().setRangeType(RangeType.MIN);
- db2.getMaxThreshold().setRangeType(RangeType.MAX);
- sheetCF.addConditionalFormatting(regions, rule2);
-
- color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
- color.setARGBHex("FFF8696B");
- regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D2:D7") };
- ConditionalFormattingRule rule3 = sheetCF.createConditionalFormattingRule(color);
- DataBarFormatting db3 = rule3.getDataBarFormatting();
- db3.getMinThreshold().setRangeType(RangeType.MIN);
- db3.getMaxThreshold().setRangeType(RangeType.MAX);
- sheetCF.addConditionalFormatting(regions, rule3);
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.ss.examples;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.ColorScaleFormatting;
+import org.apache.poi.ss.usermodel.ComparisonOperator;
+import org.apache.poi.ss.usermodel.ConditionalFormattingRule;
+import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold.RangeType;
+import org.apache.poi.ss.usermodel.DataBarFormatting;
+import org.apache.poi.ss.usermodel.ExtendedColor;
+import org.apache.poi.ss.usermodel.FontFormatting;
+import org.apache.poi.ss.usermodel.IconMultiStateFormatting;
+import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.PatternFormatting;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.SheetConditionalFormatting;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Excel Conditional Formatting -- Examples
+ *
+ *
+ * Partly based on the code snippets from
+ * http://www.contextures.com/xlcondformat03.html
+ *
+ */
+public class ConditionalFormats {
+
+ public static void main(String[] args) throws IOException {
+ Workbook wb;
+
+ if(args.length > 0 && args[0].equals("-xls")) {
+ wb = new HSSFWorkbook();
+ } else {
+ wb = new XSSFWorkbook();
+ }
+
+ sameCell(wb.createSheet("Same Cell"));
+ multiCell(wb.createSheet("MultiCell"));
+ overlapping(wb.createSheet("Overlapping"));
+ errors(wb.createSheet("Errors"));
+ hideDupplicates(wb.createSheet("Hide Dups"));
+ formatDuplicates(wb.createSheet("Duplicates"));
+ inList(wb.createSheet("In List"));
+ expiry(wb.createSheet("Expiry"));
+ shadeAlt(wb.createSheet("Shade Alt"));
+ shadeBands(wb.createSheet("Shade Bands"));
+ iconSets(wb.createSheet("Icon Sets"));
+ colourScales(wb.createSheet("Colour Scales"));
+ dataBars(wb.createSheet("Data Bars"));
+
+ // Write the output to a file
+ String file = "cf-poi.xls";
+ if(wb instanceof XSSFWorkbook) {
+ file += "x";
+ }
+ FileOutputStream out = new FileOutputStream(file);
+ wb.write(out);
+ out.close();
+ System.out.println("Generated: " + file);
+ wb.close();
+ }
+
+ /**
+ * Highlight cells based on their values
+ */
+ static void sameCell(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue(84);
+ sheet.createRow(1).createCell(0).setCellValue(74);
+ sheet.createRow(2).createCell(0).setCellValue(50);
+ sheet.createRow(3).createCell(0).setCellValue(51);
+ sheet.createRow(4).createCell(0).setCellValue(49);
+ sheet.createRow(5).createCell(0).setCellValue(41);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Cell Value Is greater than 70 (Blue Fill)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(ComparisonOperator.GT, "70");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.BLUE.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ // Condition 2: Cell Value Is less than 50 (Green Fill)
+ ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule(ComparisonOperator.LT, "50");
+ PatternFormatting fill2 = rule2.createPatternFormatting();
+ fill2.setFillBackgroundColor(IndexedColors.GREEN.index);
+ fill2.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:A6")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1, rule2);
+
+ sheet.getRow(0).createCell(2).setCellValue("<== Condition 1: Cell Value Is greater than 70 (Blue Fill)");
+ sheet.getRow(4).createCell(2).setCellValue("<== Condition 2: Cell Value Is less than 50 (Green Fill)");
+ }
+
+ /**
+ * Highlight multiple cells based on a formula
+ */
+ static void multiCell(Sheet sheet) {
+ // header row
+ Row row0 = sheet.createRow(0);
+ row0.createCell(0).setCellValue("Units");
+ row0.createCell(1).setCellValue("Cost");
+ row0.createCell(2).setCellValue("Total");
+
+ Row row1 = sheet.createRow(1);
+ row1.createCell(0).setCellValue(71);
+ row1.createCell(1).setCellValue(29);
+ row1.createCell(2).setCellValue(2059);
+
+ Row row2 = sheet.createRow(2);
+ row2.createCell(0).setCellValue(85);
+ row2.createCell(1).setCellValue(29);
+ row2.createCell(2).setCellValue(2059);
+
+ Row row3 = sheet.createRow(3);
+ row3.createCell(0).setCellValue(71);
+ row3.createCell(1).setCellValue(29);
+ row3.createCell(2).setCellValue(2059);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =$B2>75 (Blue Fill)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("$A2>75");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.BLUE.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:C4")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(4).setCellValue("<== Condition 1: Formula Is =$B2>75 (Blue Fill)");
+ }
+
+ /**
+ * Multiple conditional formatting rules can apply to
+ * one cell, some combining, some beating others.
+ * Done in order of the rules added to the
+ * SheetConditionalFormatting object
+ */
+ static void overlapping(Sheet sheet) {
+ for (int i=0; i<40; i++) {
+ int rn = i+1;
+ Row r = sheet.createRow(i);
+ r.createCell(0).setCellValue("This is row " + rn + " (" + i + ")");
+ String str = "";
+ if (rn%2 == 0) {
+ str = str + "even ";
+ }
+ if (rn%3 == 0) {
+ str = str + "x3 ";
+ }
+ if (rn%5 == 0) {
+ str = str + "x5 ";
+ }
+ if (rn%10 == 0) {
+ str = str + "x10 ";
+ }
+ if (str.length() == 0) {
+ str = "nothing special...";
+ }
+ r.createCell(1).setCellValue("It is " + str);
+ }
+ sheet.autoSizeColumn(0);
+ sheet.autoSizeColumn(1);
+
+ sheet.getRow(1).createCell(3).setCellValue("Even rows are blue");
+ sheet.getRow(2).createCell(3).setCellValue("Multiples of 3 have a grey background");
+ sheet.getRow(4).createCell(3).setCellValue("Multiples of 5 are bold");
+ sheet.getRow(9).createCell(3).setCellValue("Multiples of 10 are red (beats even)");
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Row divides by 10, red (will beat #1)
+ ConditionalFormattingRule rule1 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),10)=0");
+ FontFormatting font1 = rule1.createFontFormatting();
+ font1.setFontColorIndex(IndexedColors.RED.index);
+
+ // Condition 2: Row is even, blue
+ ConditionalFormattingRule rule2 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),2)=0");
+ FontFormatting font2 = rule2.createFontFormatting();
+ font2.setFontColorIndex(IndexedColors.BLUE.index);
+
+ // Condition 3: Row divides by 5, bold
+ ConditionalFormattingRule rule3 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),5)=0");
+ FontFormatting font3 = rule3.createFontFormatting();
+ font3.setFontStyle(false, true);
+
+ // Condition 4: Row divides by 3, grey background
+ ConditionalFormattingRule rule4 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),3)=0");
+ PatternFormatting fill4 = rule4.createPatternFormatting();
+ fill4.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index);
+ fill4.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ // Apply
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:F41")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+ sheetCF.addConditionalFormatting(regions, rule2);
+ sheetCF.addConditionalFormatting(regions, rule3);
+ sheetCF.addConditionalFormatting(regions, rule4);
+ }
+
+ /**
+ * Use Excel conditional formatting to check for errors,
+ * and change the font colour to match the cell colour.
+ * In this example, if formula result is #DIV/0! then it will have white font colour.
+ */
+ static void errors(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue(84);
+ sheet.createRow(1).createCell(0).setCellValue(0);
+ sheet.createRow(2).createCell(0).setCellFormula("ROUND(A1/A2,0)");
+ sheet.createRow(3).createCell(0).setCellValue(0);
+ sheet.createRow(4).createCell(0).setCellFormula("ROUND(A6/A4,0)");
+ sheet.createRow(5).createCell(0).setCellValue(41);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =ISERROR(C2) (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("ISERROR(A1)");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontColorIndex(IndexedColors.WHITE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:A6")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(1).setCellValue("<== The error in this cell is hidden. Condition: Formula Is =ISERROR(C2) (White Font)");
+ sheet.getRow(4).createCell(1).setCellValue("<== The error in this cell is hidden. Condition: Formula Is =ISERROR(C2) (White Font)");
+ }
+
+ /**
+ * Use Excel conditional formatting to hide the duplicate values,
+ * and make the list easier to read. In this example, when the table is sorted by Region,
+ * the second (and subsequent) occurences of each region name will have white font colour.
+ */
+ static void hideDupplicates(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("City");
+ sheet.createRow(1).createCell(0).setCellValue("Boston");
+ sheet.createRow(2).createCell(0).setCellValue("Boston");
+ sheet.createRow(3).createCell(0).setCellValue("Chicago");
+ sheet.createRow(4).createCell(0).setCellValue("Chicago");
+ sheet.createRow(5).createCell(0).setCellValue("New York");
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("A2=A1");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontColorIndex(IndexedColors.WHITE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A6")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(1).createCell(1).setCellValue("<== the second (and subsequent) " +
+ "occurences of each region name will have white font colour. " +
+ "Condition: Formula Is =A2=A1 (White Font)");
+ }
+
+ /**
+ * Use Excel conditional formatting to highlight duplicate entries in a column.
+ */
+ static void formatDuplicates(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Code");
+ sheet.createRow(1).createCell(0).setCellValue(4);
+ sheet.createRow(2).createCell(0).setCellValue(3);
+ sheet.createRow(3).createCell(0).setCellValue(6);
+ sheet.createRow(4).createCell(0).setCellValue(3);
+ sheet.createRow(5).createCell(0).setCellValue(5);
+ sheet.createRow(6).createCell(0).setCellValue(8);
+ sheet.createRow(7).createCell(0).setCellValue(0);
+ sheet.createRow(8).createCell(0).setCellValue(2);
+ sheet.createRow(9).createCell(0).setCellValue(8);
+ sheet.createRow(10).createCell(0).setCellValue(6);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("COUNTIF($A$2:$A$11,A2)>1");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontStyle(false, true);
+ font.setFontColorIndex(IndexedColors.BLUE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A11")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(1).setCellValue("<== Duplicates numbers in the column are highlighted. " +
+ "Condition: Formula Is =COUNTIF($A$2:$A$11,A2)>1 (Blue Font)");
+ }
+
+ /**
+ * Use Excel conditional formatting to highlight items that are in a list on the worksheet.
+ */
+ static void inList(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Codes");
+ sheet.createRow(1).createCell(0).setCellValue("AA");
+ sheet.createRow(2).createCell(0).setCellValue("BB");
+ sheet.createRow(3).createCell(0).setCellValue("GG");
+ sheet.createRow(4).createCell(0).setCellValue("AA");
+ sheet.createRow(5).createCell(0).setCellValue("FF");
+ sheet.createRow(6).createCell(0).setCellValue("XX");
+ sheet.createRow(7).createCell(0).setCellValue("CC");
+
+ sheet.getRow(0).createCell(2).setCellValue("Valid");
+ sheet.getRow(1).createCell(2).setCellValue("AA");
+ sheet.getRow(2).createCell(2).setCellValue("BB");
+ sheet.getRow(3).createCell(2).setCellValue("CC");
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("COUNTIF($C$2:$C$4,A2)");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.LIGHT_BLUE.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A8")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(3).setCellValue("<== Use Excel conditional formatting to highlight items that are in a list on the worksheet");
+ }
+
+ /**
+ * Use Excel conditional formatting to highlight payments that are due in the next thirty days.
+ * In this example, Due dates are entered in cells A2:A4.
+ */
+ static void expiry(Sheet sheet) {
+ CellStyle style = sheet.getWorkbook().createCellStyle();
+ style.setDataFormat((short)BuiltinFormats.getBuiltinFormat("d-mmm"));
+
+ sheet.createRow(0).createCell(0).setCellValue("Date");
+ sheet.createRow(1).createCell(0).setCellFormula("TODAY()+29");
+ sheet.createRow(2).createCell(0).setCellFormula("A2+1");
+ sheet.createRow(3).createCell(0).setCellFormula("A3+1");
+
+ for(int rownum = 1; rownum <= 3; rownum++) {
+ sheet.getRow(rownum).getCell(0).setCellStyle(style);
+ }
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("AND(A2-TODAY()>=0,A2-TODAY()<=30)");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontStyle(false, true);
+ font.setFontColorIndex(IndexedColors.BLUE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A4")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(0).createCell(1).setCellValue("Dates within the next 30 days are highlighted");
+ }
+
+ /**
+ * Use Excel conditional formatting to shade alternating rows on the worksheet
+ */
+ static void shadeAlt(Sheet sheet) {
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("MOD(ROW(),2)");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.LIGHT_GREEN.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:Z100")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.createRow(0).createCell(1).setCellValue("Shade Alternating Rows");
+ sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),2) (Light Green Fill)");
+ }
+
+ /**
+ * You can use Excel conditional formatting to shade bands of rows on the worksheet.
+ * In this example, 3 rows are shaded light grey, and 3 are left with no shading.
+ * In the MOD function, the total number of rows in the set of banded rows (6) is entered.
+ */
+ static void shadeBands(Sheet sheet) {
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("MOD(ROW(),6)<3");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:Z100")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.createRow(0).createCell(1).setCellValue("Shade Bands of Rows");
+ sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),6)<2 (Light Grey Fill)");
+ }
+
+ /**
+ * Icon Sets / Multi-States allow you to have icons shown which vary
+ * based on the values, eg Red traffic light / Yellow traffic light /
+ * Green traffic light
+ */
+ static void iconSets(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Icon Sets");
+ Row r = sheet.createRow(1);
+ r.createCell(0).setCellValue("Reds");
+ r.createCell(1).setCellValue(0);
+ r.createCell(2).setCellValue(0);
+ r.createCell(3).setCellValue(0);
+ r = sheet.createRow(2);
+ r.createCell(0).setCellValue("Yellows");
+ r.createCell(1).setCellValue(5);
+ r.createCell(2).setCellValue(5);
+ r.createCell(3).setCellValue(5);
+ r = sheet.createRow(3);
+ r.createCell(0).setCellValue("Greens");
+ r.createCell(1).setCellValue(10);
+ r.createCell(2).setCellValue(10);
+ r.createCell(3).setCellValue(10);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ CellRangeAddress[] regions = { CellRangeAddress.valueOf("B1:B4") };
+ ConditionalFormattingRule rule1 =
+ sheetCF.createConditionalFormattingRule(IconSet.GYR_3_TRAFFIC_LIGHTS);
+ IconMultiStateFormatting im1 = rule1.getMultiStateFormatting();
+ im1.getThresholds()[0].setRangeType(RangeType.MIN);
+ im1.getThresholds()[1].setRangeType(RangeType.PERCENT);
+ im1.getThresholds()[1].setValue(33d);
+ im1.getThresholds()[2].setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C1:C4") };
+ ConditionalFormattingRule rule2 =
+ sheetCF.createConditionalFormattingRule(IconSet.GYR_3_FLAGS);
+ IconMultiStateFormatting im2 = rule1.getMultiStateFormatting();
+ im2.getThresholds()[0].setRangeType(RangeType.PERCENT);
+ im2.getThresholds()[0].setValue(0d);
+ im2.getThresholds()[1].setRangeType(RangeType.PERCENT);
+ im2.getThresholds()[1].setValue(33d);
+ im2.getThresholds()[2].setRangeType(RangeType.PERCENT);
+ im2.getThresholds()[2].setValue(67d);
+ sheetCF.addConditionalFormatting(regions, rule2);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D1:D4") };
+ ConditionalFormattingRule rule3 =
+ sheetCF.createConditionalFormattingRule(IconSet.GYR_3_SYMBOLS_CIRCLE);
+ IconMultiStateFormatting im3 = rule1.getMultiStateFormatting();
+ im3.setIconOnly(true);
+ im3.getThresholds()[0].setRangeType(RangeType.MIN);
+ im3.getThresholds()[1].setRangeType(RangeType.NUMBER);
+ im3.getThresholds()[1].setValue(3d);
+ im3.getThresholds()[2].setRangeType(RangeType.NUMBER);
+ im3.getThresholds()[2].setValue(7d);
+ sheetCF.addConditionalFormatting(regions, rule3);
+ }
+
+ /**
+ * Color Scales / Colour Scales / Colour Gradients allow you shade the
+ * background colour of the cell based on the values, eg from Red to
+ * Yellow to Green.
+ */
+ static void colourScales(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Colour Scales");
+ Row r = sheet.createRow(1);
+ r.createCell(0).setCellValue("Red-Yellow-Green");
+ for (int i=1; i<=7; i++) {
+ r.createCell(i).setCellValue((i-1)*5);
+ }
+ r = sheet.createRow(2);
+ r.createCell(0).setCellValue("Red-White-Blue");
+ for (int i=1; i<=9; i++) {
+ r.createCell(i).setCellValue((i-1)*5);
+ }
+ r = sheet.createRow(3);
+ r.createCell(0).setCellValue("Blue-Green");
+ for (int i=1; i<=16; i++) {
+ r.createCell(i).setCellValue((i-1));
+ }
+ sheet.setColumnWidth(0, 5000);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ CellRangeAddress[] regions = { CellRangeAddress.valueOf("B2:H2") };
+ ConditionalFormattingRule rule1 =
+ sheetCF.createConditionalFormattingColorScaleRule();
+ ColorScaleFormatting cs1 = rule1.getColorScaleFormatting();
+ cs1.getThresholds()[0].setRangeType(RangeType.MIN);
+ cs1.getThresholds()[1].setRangeType(RangeType.PERCENTILE);
+ cs1.getThresholds()[1].setValue(50d);
+ cs1.getThresholds()[2].setRangeType(RangeType.MAX);
+ ((ExtendedColor)cs1.getColors()[0]).setARGBHex("FFF8696B");
+ ((ExtendedColor)cs1.getColors()[1]).setARGBHex("FFFFEB84");
+ ((ExtendedColor)cs1.getColors()[2]).setARGBHex("FF63BE7B");
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("B3:J3") };
+ ConditionalFormattingRule rule2 =
+ sheetCF.createConditionalFormattingColorScaleRule();
+ ColorScaleFormatting cs2 = rule2.getColorScaleFormatting();
+ cs2.getThresholds()[0].setRangeType(RangeType.MIN);
+ cs2.getThresholds()[1].setRangeType(RangeType.PERCENTILE);
+ cs2.getThresholds()[1].setValue(50d);
+ cs2.getThresholds()[2].setRangeType(RangeType.MAX);
+ ((ExtendedColor)cs2.getColors()[0]).setARGBHex("FFF8696B");
+ ((ExtendedColor)cs2.getColors()[1]).setARGBHex("FFFCFCFF");
+ ((ExtendedColor)cs2.getColors()[2]).setARGBHex("FF5A8AC6");
+ sheetCF.addConditionalFormatting(regions, rule2);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("B4:Q4") };
+ ConditionalFormattingRule rule3=
+ sheetCF.createConditionalFormattingColorScaleRule();
+ ColorScaleFormatting cs3 = rule3.getColorScaleFormatting();
+ cs3.setNumControlPoints(2);
+ cs3.getThresholds()[0].setRangeType(RangeType.MIN);
+ cs3.getThresholds()[1].setRangeType(RangeType.MAX);
+ ((ExtendedColor)cs3.getColors()[0]).setARGBHex("FF5A8AC6");
+ ((ExtendedColor)cs3.getColors()[1]).setARGBHex("FF63BE7B");
+ sheetCF.addConditionalFormatting(regions, rule3);
+ }
+
+ /**
+ * DataBars / Data-Bars allow you to have bars shown vary
+ * based on the values, from full to empty
+ */
+ static void dataBars(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Data Bars");
+ Row r = sheet.createRow(1);
+ r.createCell(1).setCellValue("Green Positive");
+ r.createCell(2).setCellValue("Blue Mix");
+ r.createCell(3).setCellValue("Red Negative");
+ r = sheet.createRow(2);
+ r.createCell(1).setCellValue(0);
+ r.createCell(2).setCellValue(0);
+ r.createCell(3).setCellValue(0);
+ r = sheet.createRow(3);
+ r.createCell(1).setCellValue(5);
+ r.createCell(2).setCellValue(-5);
+ r.createCell(3).setCellValue(-5);
+ r = sheet.createRow(4);
+ r.createCell(1).setCellValue(10);
+ r.createCell(2).setCellValue(10);
+ r.createCell(3).setCellValue(-10);
+ r = sheet.createRow(5);
+ r.createCell(1).setCellValue(5);
+ r.createCell(2).setCellValue(5);
+ r.createCell(3).setCellValue(-5);
+ r = sheet.createRow(6);
+ r.createCell(1).setCellValue(20);
+ r.createCell(2).setCellValue(-10);
+ r.createCell(3).setCellValue(-20);
+ sheet.setColumnWidth(0, 3000);
+ sheet.setColumnWidth(1, 5000);
+ sheet.setColumnWidth(2, 5000);
+ sheet.setColumnWidth(3, 5000);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ ExtendedColor color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
+ color.setARGBHex("FF63BE7B");
+ CellRangeAddress[] regions = { CellRangeAddress.valueOf("B2:B7") };
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(color);
+ DataBarFormatting db1 = rule1.getDataBarFormatting();
+ db1.getMinThreshold().setRangeType(RangeType.MIN);
+ db1.getMaxThreshold().setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
+ color.setARGBHex("FF5A8AC6");
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C2:C7") };
+ ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule(color);
+ DataBarFormatting db2 = rule2.getDataBarFormatting();
+ db2.getMinThreshold().setRangeType(RangeType.MIN);
+ db2.getMaxThreshold().setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule2);
+
+ color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
+ color.setARGBHex("FFF8696B");
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D2:D7") };
+ ConditionalFormattingRule rule3 = sheetCF.createConditionalFormattingRule(color);
+ DataBarFormatting db3 = rule3.getDataBarFormatting();
+ db3.getMinThreshold().setRangeType(RangeType.MIN);
+ db3.getMaxThreshold().setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule3);
+ }
+}
diff --git a/src/examples/src/org/apache/poi/ss/examples/ExcelComparator.java b/src/examples/src/org/apache/poi/ss/examples/ExcelComparator.java
index 5ed404e2d3..645e798a0d 100644
--- a/src/examples/src/org/apache/poi/ss/examples/ExcelComparator.java
+++ b/src/examples/src/org/apache/poi/ss/examples/ExcelComparator.java
@@ -1,614 +1,614 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.ss.examples;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-
-import org.apache.poi.ss.usermodel.BorderStyle;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.CellType;
-import org.apache.poi.ss.usermodel.Color;
-import org.apache.poi.ss.usermodel.DateUtil;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.WorkbookFactory;
-import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.xssf.usermodel.XSSFCell;
-import org.apache.poi.xssf.usermodel.XSSFCellStyle;
-import org.apache.poi.xssf.usermodel.XSSFColor;
-
-/**
- * Utility to compare Excel File Contents cell by cell for all sheets.
- *
- *
This utility will be used to compare Excel File Contents cell by cell for all sheets programmatically.
- *
- *
Below are the list of Attribute comparison supported in this version.
- *
- *
- *
Cell Alignment
- *
Cell Border Attributes
- *
Cell Data
- *
Cell Data-Type
- *
Cell Fill Color
- *
Cell Fill pattern
- *
Cell Font Attributes
- *
Cell Font Family
- *
Cell Font Size
- *
Cell Protection
- *
Name of the sheets
- *
Number of Columns
- *
Number of Rows
- *
Number of Sheet
- *
- *
- *
(Some of the above attribute comparison only work for *.xlsx format currently. In future it can be enhanced.)
+ */
+public class ExcelComparator {
+
+ private static final String CELL_DATA_DOES_NOT_MATCH = "Cell Data does not Match ::";
+ private static final String CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH = "Cell Font Attributes does not Match ::";
+
+ private static class Locator {
+ Workbook workbook;
+ Sheet sheet;
+ Row row;
+ Cell cell;
+ }
+
+ List listOfDifferences = new ArrayList();
+
+ public static void main(String args[]) throws Exception {
+ if (args.length != 2 || !(new File(args[0]).exists()) || !(new File(args[1]).exists())) {
+ System.err.println("java -cp "+ExcelComparator.class.getCanonicalName()+" compare(Workbook wb1, Workbook wb2) {
+ Locator loc1 = new Locator();
+ Locator loc2 = new Locator();
+ loc1.workbook = wb1;
+ loc2.workbook = wb2;
+
+ ExcelComparator excelComparator = new ExcelComparator();
+ excelComparator.compareNumberOfSheets(loc1, loc2 );
+ excelComparator.compareSheetNames(loc1, loc2);
+ excelComparator.compareSheetData(loc1, loc2);
+
+ return excelComparator.listOfDifferences;
+ }
+
+ /**
+ * Compare data in all sheets.
+ *
+ * @param workbook1 the workbook1
+ * @param workbook2 the workbook2
+ * @param listOfDifferences the list of differences
+ * @throws ExcelCompareException the excel compare exception
+ */
+ private void compareDataInAllSheets(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ if (loc2.workbook.getNumberOfSheets() <= i) return;
+
+ loc1.sheet = loc1.workbook.getSheetAt(i);
+ loc2.sheet = loc2.workbook.getSheetAt(i);
+
+ compareDataInSheet(loc1, loc2);
+ }
+ }
+
+ private void compareDataInSheet(Locator loc1, Locator loc2) {
+ for (int j = 0; j < loc1.sheet.getPhysicalNumberOfRows(); j++) {
+ if (loc2.sheet.getPhysicalNumberOfRows() <= j) return;
+
+ loc1.row = loc1.sheet.getRow(j);
+ loc2.row = loc2.sheet.getRow(j);
+
+ if ((loc1.row == null) || (loc2.row == null)) {
+ continue;
+ }
+
+ compareDataInRow(loc1, loc2);
+ }
+ }
+
+ private void compareDataInRow(Locator loc1, Locator loc2) {
+ for (int k = 0; k < loc1.row.getLastCellNum(); k++) {
+ if (loc2.row.getPhysicalNumberOfCells() <= k) return;
+
+ loc1.cell = loc1.row.getCell(k);
+ loc2.cell = loc2.row.getCell(k);
+
+ if ((loc1.cell == null) || (loc2.cell == null)) {
+ continue;
+ }
+
+ compareDataInCell(loc1, loc2);
+ }
+ }
+
+ private void compareDataInCell(Locator loc1, Locator loc2) {
+ if (isCellTypeMatches(loc1, loc2)) {
+ final CellType loc1cellType = loc1.cell.getCellTypeEnum();
+ switch(loc1cellType) {
+ case BLANK:
+ case STRING:
+ case ERROR:
+ isCellContentMatches(loc1,loc2);
+ break;
+ case BOOLEAN:
+ isCellContentMatchesForBoolean(loc1,loc2);
+ break;
+ case FORMULA:
+ isCellContentMatchesForFormula(loc1,loc2);
+ break;
+ case NUMERIC:
+ if (DateUtil.isCellDateFormatted(loc1.cell)) {
+ isCellContentMatchesForDate(loc1,loc2);
+ } else {
+ isCellContentMatchesForNumeric(loc1,loc2);
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unexpected cell type: " + loc1cellType);
+ }
+ }
+
+ isCellFillPatternMatches(loc1,loc2);
+ isCellAlignmentMatches(loc1,loc2);
+ isCellHiddenMatches(loc1,loc2);
+ isCellLockedMatches(loc1,loc2);
+ isCellFontFamilyMatches(loc1,loc2);
+ isCellFontSizeMatches(loc1,loc2);
+ isCellFontBoldMatches(loc1,loc2);
+ isCellUnderLineMatches(loc1,loc2);
+ isCellFontItalicsMatches(loc1,loc2);
+ isCellBorderMatches(loc1,loc2,'t');
+ isCellBorderMatches(loc1,loc2,'l');
+ isCellBorderMatches(loc1,loc2,'b');
+ isCellBorderMatches(loc1,loc2,'r');
+ isCellFillBackGroundMatches(loc1,loc2);
+ }
+
+ /**
+ * Compare number of columns in sheets.
+ */
+ private void compareNumberOfColumnsInSheets(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ if (loc2.workbook.getNumberOfSheets() <= i) return;
+
+ loc1.sheet = loc1.workbook.getSheetAt(i);
+ loc2.sheet = loc2.workbook.getSheetAt(i);
+
+ Iterator ri1 = loc1.sheet.rowIterator();
+ Iterator ri2 = loc2.sheet.rowIterator();
+
+ int num1 = (ri1.hasNext()) ? ri1.next().getPhysicalNumberOfCells() : 0;
+ int num2 = (ri2.hasNext()) ? ri2.next().getPhysicalNumberOfCells() : 0;
+
+ if (num1 != num2) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 -> %s [%d] != workbook2 -> %s [%d]",
+ "Number Of Columns does not Match ::",
+ loc1.sheet.getSheetName(), num1,
+ loc2.sheet.getSheetName(), num2
+ );
+ listOfDifferences.add(str);
+ }
+ }
+ }
+
+ /**
+ * Compare number of rows in sheets.
+ */
+ private void compareNumberOfRowsInSheets(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ if (loc2.workbook.getNumberOfSheets() <= i) return;
+
+ loc1.sheet = loc1.workbook.getSheetAt(i);
+ loc2.sheet = loc2.workbook.getSheetAt(i);
+
+ int num1 = loc1.sheet.getPhysicalNumberOfRows();
+ int num2 = loc2.sheet.getPhysicalNumberOfRows();
+
+ if (num1 != num2) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 -> %s [%d] != workbook2 -> %s [%d]",
+ "Number Of Rows does not Match ::",
+ loc1.sheet.getSheetName(), num1,
+ loc2.sheet.getSheetName(), num2
+ );
+ listOfDifferences.add(str);
+ }
+ }
+
+ }
+
+ /**
+ * Compare number of sheets.
+ */
+ private void compareNumberOfSheets(Locator loc1, Locator loc2) {
+ int num1 = loc1.workbook.getNumberOfSheets();
+ int num2 = loc2.workbook.getNumberOfSheets();
+ if (num1 != num2) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 [%d] != workbook2 [%d]",
+ "Number of Sheets do not match ::",
+ num1, num2
+ );
+
+ listOfDifferences.add(str);
+
+ }
+ }
+
+ /**
+ * Compare sheet data.
+ *
+ * @param workbook1
+ * the workbook1
+ * @param workbook2
+ * the workbook2
+ * @param listOfDifferences
+ *
+ * @throws ExcelCompareException
+ * the excel compare exception
+ */
+ private void compareSheetData(Locator loc1, Locator loc2) {
+ compareNumberOfRowsInSheets(loc1, loc2);
+ compareNumberOfColumnsInSheets(loc1, loc2);
+ compareDataInAllSheets(loc1, loc2);
+
+ }
+
+ /**
+ * Compare sheet names.
+ */
+ private void compareSheetNames(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ String name1 = loc1.workbook.getSheetName(i);
+ String name2 = (loc2.workbook.getNumberOfSheets() > i) ? loc2.workbook.getSheetName(i) : "";
+
+ if (!name1.equals(name2)) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 -> %s [%d] != workbook2 -> %s [%d]",
+ "Name of the sheets do not match ::", name1, i+1, name2, i+1
+ );
+ listOfDifferences.add(str);
+ }
+ }
+ }
+
+ /**
+ * Formats the message.
+ */
+ private void addMessage(Locator loc1, Locator loc2, String messageStart, String value1, String value2) {
+ String str =
+ String.format(Locale.ROOT, "%s\nworkbook1 -> %s -> %s [%s] != workbook2 -> %s -> %s [%s]",
+ messageStart,
+ loc1.sheet.getSheetName(), new CellReference(loc1.cell).formatAsString(), value1,
+ loc2.sheet.getSheetName(), new CellReference(loc2.cell).formatAsString(), value2
+ );
+ listOfDifferences.add(str);
+ }
+
+ /**
+ * Checks if cell alignment matches.
+ */
+ private void isCellAlignmentMatches(Locator loc1, Locator loc2) {
+ // TODO: check for NPE
+ short align1 = loc1.cell.getCellStyle().getAlignment();
+ short align2 = loc2.cell.getCellStyle().getAlignment();
+ if (align1 != align2) {
+ addMessage(loc1, loc2,
+ "Cell Alignment does not Match ::",
+ Short.toString(align1),
+ Short.toString(align2)
+ );
+ }
+ }
+
+ /**
+ * Checks if cell border bottom matches.
+ */
+ private void isCellBorderMatches(Locator loc1, Locator loc2, char borderSide) {
+ if (!(loc1.cell instanceof XSSFCell)) return;
+ XSSFCellStyle style1 = ((XSSFCell)loc1.cell).getCellStyle();
+ XSSFCellStyle style2 = ((XSSFCell)loc2.cell).getCellStyle();
+ boolean b1, b2;
+ String borderName;
+ switch (borderSide) {
+ case 't': default:
+ b1 = style1.getBorderTopEnum() == BorderStyle.THIN;
+ b2 = style2.getBorderTopEnum() == BorderStyle.THIN;
+ borderName = "TOP";
+ break;
+ case 'b':
+ b1 = style1.getBorderBottomEnum() == BorderStyle.THIN;
+ b2 = style2.getBorderBottomEnum() == BorderStyle.THIN;
+ borderName = "BOTTOM";
+ break;
+ case 'l':
+ b1 = style1.getBorderLeftEnum() == BorderStyle.THIN;
+ b2 = style2.getBorderLeftEnum() == BorderStyle.THIN;
+ borderName = "LEFT";
+ break;
+ case 'r':
+ b1 = style1.getBorderRightEnum() == BorderStyle.THIN;
+ b2 = style2.getBorderRightEnum() == BorderStyle.THIN;
+ borderName = "RIGHT";
+ break;
+ }
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ "Cell Border Attributes does not Match ::",
+ (b1 ? "" : "NOT ")+borderName+" BORDER",
+ (b2 ? "" : "NOT ")+borderName+" BORDER"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell content matches.
+ */
+ private void isCellContentMatches(Locator loc1, Locator loc2) {
+ // TODO: check for null and non-rich-text cells
+ String str1 = loc1.cell.getRichStringCellValue().getString();
+ String str2 = loc2.cell.getRichStringCellValue().getString();
+ if (!str1.equals(str2)) {
+ addMessage(loc1,loc2,CELL_DATA_DOES_NOT_MATCH,str1,str2);
+ }
+ }
+
+ /**
+ * Checks if cell content matches for boolean.
+ */
+ private void isCellContentMatchesForBoolean(Locator loc1, Locator loc2) {
+ boolean b1 = loc1.cell.getBooleanCellValue();
+ boolean b2 = loc2.cell.getBooleanCellValue();
+ if (b1 != b2) {
+ addMessage(loc1,loc2,CELL_DATA_DOES_NOT_MATCH,Boolean.toString(b1),Boolean.toString(b2));
+ }
+ }
+
+ /**
+ * Checks if cell content matches for date.
+ */
+ private void isCellContentMatchesForDate(Locator loc1, Locator loc2) {
+ Date date1 = loc1.cell.getDateCellValue();
+ Date date2 = loc2.cell.getDateCellValue();
+ if (!date1.equals(date2)) {
+ addMessage(loc1, loc2, CELL_DATA_DOES_NOT_MATCH, date1.toGMTString(), date2.toGMTString());
+ }
+ }
+
+
+ /**
+ * Checks if cell content matches for formula.
+ */
+ private void isCellContentMatchesForFormula(Locator loc1, Locator loc2) {
+ // TODO: actually evaluate the formula / NPE checks
+ String form1 = loc1.cell.getCellFormula();
+ String form2 = loc2.cell.getCellFormula();
+ if (!form1.equals(form2)) {
+ addMessage(loc1, loc2, CELL_DATA_DOES_NOT_MATCH, form1, form2);
+ }
+ }
+
+ /**
+ * Checks if cell content matches for numeric.
+ */
+ private void isCellContentMatchesForNumeric(Locator loc1, Locator loc2) {
+ // TODO: Check for NaN
+ double num1 = loc1.cell.getNumericCellValue();
+ double num2 = loc2.cell.getNumericCellValue();
+ if (num1 != num2) {
+ addMessage(loc1, loc2, CELL_DATA_DOES_NOT_MATCH, Double.toString(num1), Double.toString(num2));
+ }
+ }
+
+ private String getCellFillBackground(Locator loc) {
+ Color col = loc.cell.getCellStyle().getFillForegroundColorColor();
+ return (col instanceof XSSFColor) ? ((XSSFColor)col).getARGBHex() : "NO COLOR";
+ }
+
+ /**
+ * Checks if cell file back ground matches.
+ */
+ private void isCellFillBackGroundMatches(Locator loc1, Locator loc2) {
+ String col1 = getCellFillBackground(loc1);
+ String col2 = getCellFillBackground(loc2);
+ if (!col1.equals(col2)) {
+ addMessage(loc1, loc2, "Cell Fill Color does not Match ::", col1, col2);
+ }
+ }
+ /**
+ * Checks if cell fill pattern matches.
+ */
+ private void isCellFillPatternMatches(Locator loc1, Locator loc2) {
+ // TOOO: Check for NPE
+ short fill1 = loc1.cell.getCellStyle().getFillPattern();
+ short fill2 = loc2.cell.getCellStyle().getFillPattern();
+ if (fill1 != fill2) {
+ addMessage(loc1, loc2,
+ "Cell Fill pattern does not Match ::",
+ Short.toString(fill1),
+ Short.toString(fill2)
+ );
+ }
+ }
+
+ /**
+ * Checks if cell font bold matches.
+ */
+ private void isCellFontBoldMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell)) return;
+ boolean b1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getBold();
+ boolean b2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getBold();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH,
+ (b1 ? "" : "NOT ")+"BOLD",
+ (b2 ? "" : "NOT ")+"BOLD"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell font family matches.
+ */
+ private void isCellFontFamilyMatches(Locator loc1, Locator loc2) {
+ // TODO: Check for NPEs
+ if (!(loc1.cell instanceof XSSFCell)) return;
+ String family1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getFontName();
+ String family2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getFontName();
+ if (!family1.equals(family2)) {
+ addMessage(loc1, loc2, "Cell Font Family does not Match ::", family1, family2);
+ }
+ }
+
+ /**
+ * Checks if cell font italics matches.
+ */
+ private void isCellFontItalicsMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell)) return;
+ boolean b1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getItalic();
+ boolean b2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getItalic();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH,
+ (b1 ? "" : "NOT ")+"ITALICS",
+ (b2 ? "" : "NOT ")+"ITALICS"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell font size matches.
+ */
+ private void isCellFontSizeMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell)) return;
+ short size1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getFontHeightInPoints();
+ short size2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getFontHeightInPoints();
+ if (size1 != size2) {
+ addMessage(loc1, loc2,
+ "Cell Font Size does not Match ::",
+ Short.toString(size1),
+ Short.toString(size2)
+ );
+ }
+ }
+
+ /**
+ * Checks if cell hidden matches.
+ */
+ private void isCellHiddenMatches(Locator loc1, Locator loc2) {
+ boolean b1 = loc1.cell.getCellStyle().getHidden();
+ boolean b2 = loc1.cell.getCellStyle().getHidden();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ "Cell Visibility does not Match ::",
+ (b1 ? "" : "NOT ")+"HIDDEN",
+ (b2 ? "" : "NOT ")+"HIDDEN"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell locked matches.
+ */
+ private void isCellLockedMatches(Locator loc1, Locator loc2) {
+ boolean b1 = loc1.cell.getCellStyle().getLocked();
+ boolean b2 = loc1.cell.getCellStyle().getLocked();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ "Cell Protection does not Match ::",
+ (b1 ? "" : "NOT ")+"LOCKED",
+ (b2 ? "" : "NOT ")+"LOCKED"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell type matches.
+ */
+ private boolean isCellTypeMatches(Locator loc1, Locator loc2) {
+ CellType type1 = loc1.cell.getCellTypeEnum();
+ CellType type2 = loc2.cell.getCellTypeEnum();
+ if (type1 == type2) return true;
+ addMessage(loc1, loc2,
+ "Cell Data-Type does not Match in :: ",
+ type1.name(), type2.name()
+ );
+ return false;
+ }
+
+ /**
+ * Checks if cell under line matches.
+ *
+ * @param cellWorkBook1
+ * the cell work book1
+ * @param cellWorkBook2
+ * the cell work book2
+ * @return true, if cell under line matches
+ */
+ private void isCellUnderLineMatches(Locator loc1, Locator loc2) {
+ // TOOO: distinguish underline type
+ if (!(loc1.cell instanceof XSSFCell)) return;
+ byte b1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getUnderline();
+ byte b2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getUnderline();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH,
+ (b1 == 1 ? "" : "NOT ")+"UNDERLINE",
+ (b2 == 1 ? "" : "NOT ")+"UNDERLINE"
+ );
+ }
+ }
}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/ss/examples/LinkedDropDownLists.java b/src/examples/src/org/apache/poi/ss/examples/LinkedDropDownLists.java
index 7b672b792c..8f43b4bf66 100644
--- a/src/examples/src/org/apache/poi/ss/examples/LinkedDropDownLists.java
+++ b/src/examples/src/org/apache/poi/ss/examples/LinkedDropDownLists.java
@@ -1,211 +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.poi.ss.examples;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.DataValidation;
-import org.apache.poi.ss.usermodel.DataValidationConstraint;
-import org.apache.poi.ss.usermodel.DataValidationHelper;
-import org.apache.poi.ss.usermodel.Name;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.util.CellRangeAddressList;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-
-/**
- * Demonstrates one technique that may be used to create linked or dependent
- * drop down lists. This refers to a situation in which the selection made
- * in one drop down list affects the options that are displayed in the second
- * or subsequent drop down list(s). In this example, the value the user selects
- * from the down list in cell A1 will affect the values displayed in the linked
- * drop down list in cell B1. For the sake of simplicity, the data for the drop
- * down lists is included on the same worksheet but this does not have to be the
- * case; the data could appear on a separate sheet. If this were done, then the
- * names for the regions would have to be different, they would have to include
- * the name of the sheet.
- *
- * There are two keys to this technique. The first is the use of named area or
- * regions of cells to hold the data for the drop down lists and the second is
- * making use of the INDIRECT() function to convert a name into the addresses
- * of the cells it refers to.
- *
- * Note that whilst this class builds just two linked drop down lists, there is
- * nothing to prevent more being created. Quite simply, use the value selected
- * by the user in one drop down list to determine what is shown in another and the
- * value selected in that drop down list to determine what is shown in a third,
- * and so on. Also, note that the data for the drop down lists is contained on
- * contained on the same sheet as the validations themselves. This is done simply
- * for simplicity and there is nothing to prevent a separate sheet being created
- * and used to hold the data. If this is done then problems may be encountered
- * if the sheet is opened with OpenOffice Calc. To prevent these problems, it is
- * better to include the name of the sheet when calling the setRefersToFormula()
- * method.
- *
- * @author Mark Beardsley [msb at apache.org]
- * @version 1.00 30th March 2012
- */
-public class LinkedDropDownLists {
-
- LinkedDropDownLists(String workbookName) throws IOException {
- // Using the ss.usermodel allows this class to support both binary
- // and xml based workbooks. The choice of which one to create is
- // made by checking the file extension.
- Workbook workbook;
- if (workbookName.endsWith(".xlsx")) {
- workbook = new XSSFWorkbook();
- } else {
- workbook = new HSSFWorkbook();
- }
-
- // Build the sheet that will hold the data for the validations. This
- // must be done first as it will create names that are referenced
- // later.
- Sheet sheet = workbook.createSheet("Linked Validations");
- LinkedDropDownLists.buildDataSheet(sheet);
-
- // Build the first data validation to occupy cell A1. Note
- // that it retrieves it's data from the named area or region called
- // CHOICES. Further information about this can be found in the
- // static buildDataSheet() method below.
- CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0);
- DataValidationHelper dvHelper = sheet.getDataValidationHelper();
- DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint("CHOICES");
- DataValidation validation = dvHelper.createValidation(dvConstraint, addressList);
- sheet.addValidationData(validation);
-
- // Now, build the linked or dependent drop down list that will
- // occupy cell B1. The key to the whole process is the use of the
- // INDIRECT() function. In the buildDataSheet(0 method, a series of
- // named regions are created and the names of three of them mirror
- // the options available to the user in the first drop down list
- // (in cell A1). Using the INDIRECT() function makes it possible
- // to convert the selection the user makes in that first drop down
- // into the addresses of a named region of cells and then to use
- // those cells to populate the second drop down list.
- addressList = new CellRangeAddressList(0, 0, 1, 1);
- dvConstraint = dvHelper.createFormulaListConstraint(
- "INDIRECT(UPPER($A$1))");
- validation = dvHelper.createValidation(dvConstraint, addressList);
- sheet.addValidationData(validation);
-
- FileOutputStream fos = new FileOutputStream(workbookName);
- workbook.write(fos);
- fos.close();
- workbook.close();
- }
-
- /**
- * Called to populate the named areas/regions. The contents of the cells on
- * row one will be used to populate the first drop down list. The contents of
- * the cells on rows two, three and four will be used to populate the second
- * drop down list, just which row will be determined by the choice the user
- * makes in the first drop down list.
- *
- * In all cases, the approach is to create a row, create and populate cells
- * with data and then specify a name that identifies those cells. With the
- * exception of the first range, the names that are chosen for each range
- * of cells are quite important. In short, each of the options the user
- * could select in the first drop down list is used as the name for another
- * range of cells. Thus, in this example, the user can select either
- * 'Animal', 'Vegetable' or 'Mineral' in the first drop down and so the
- * sheet contains ranges named 'ANIMAL', 'VEGETABLE' and 'MINERAL'.
- *
- * @param dataSheet An instance of a class that implements the Sheet Sheet
- * interface (HSSFSheet or XSSFSheet).
- */
- private static final void buildDataSheet(Sheet dataSheet) {
- Row row = null;
- Cell cell = null;
- Name name = null;
-
- // The first row will hold the data for the first validation.
- row = dataSheet.createRow(10);
- cell = row.createCell(0);
- cell.setCellValue("Animal");
- cell = row.createCell(1);
- cell.setCellValue("Vegetable");
- cell = row.createCell(2);
- cell.setCellValue("Mineral");
- name = dataSheet.getWorkbook().createName();
- name.setRefersToFormula("$A$11:$C$11");
- name.setNameName("CHOICES");
-
- // The next three rows will hold the data that will be used to
- // populate the second, or linked, drop down list.
- row = dataSheet.createRow(11);
- cell = row.createCell(0);
- cell.setCellValue("Lion");
- cell = row.createCell(1);
- cell.setCellValue("Tiger");
- cell = row.createCell(2);
- cell.setCellValue("Leopard");
- cell = row.createCell(3);
- cell.setCellValue("Elephant");
- cell = row.createCell(4);
- cell.setCellValue("Eagle");
- cell = row.createCell(5);
- cell.setCellValue("Horse");
- cell = row.createCell(6);
- cell.setCellValue("Zebra");
- name = dataSheet.getWorkbook().createName();
- name.setRefersToFormula("$A$12:$G$12");
- name.setNameName("ANIMAL");
-
- row = dataSheet.createRow(12);
- cell = row.createCell(0);
- cell.setCellValue("Cabbage");
- cell = row.createCell(1);
- cell.setCellValue("Cauliflower");
- cell = row.createCell(2);
- cell.setCellValue("Potato");
- cell = row.createCell(3);
- cell.setCellValue("Onion");
- cell = row.createCell(4);
- cell.setCellValue("Beetroot");
- cell = row.createCell(5);
- cell.setCellValue("Asparagus");
- cell = row.createCell(6);
- cell.setCellValue("Spinach");
- cell = row.createCell(7);
- cell.setCellValue("Chard");
- name = dataSheet.getWorkbook().createName();
- name.setRefersToFormula("$A$13:$H$13");
- name.setNameName("VEGETABLE");
-
- row = dataSheet.createRow(13);
- cell = row.createCell(0);
- cell.setCellValue("Bauxite");
- cell = row.createCell(1);
- cell.setCellValue("Quartz");
- cell = row.createCell(2);
- cell.setCellValue("Feldspar");
- cell = row.createCell(3);
- cell.setCellValue("Shist");
- cell = row.createCell(4);
- cell.setCellValue("Shale");
- cell = row.createCell(5);
- cell.setCellValue("Mica");
- name = dataSheet.getWorkbook().createName();
- name.setRefersToFormula("$A$14:$F$14");
- name.setNameName("MINERAL");
- }
-}
+ /* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ==================================================================== */
+
+package org.apache.poi.ss.examples;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.DataValidationConstraint;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.usermodel.Name;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Demonstrates one technique that may be used to create linked or dependent
+ * drop down lists. This refers to a situation in which the selection made
+ * in one drop down list affects the options that are displayed in the second
+ * or subsequent drop down list(s). In this example, the value the user selects
+ * from the down list in cell A1 will affect the values displayed in the linked
+ * drop down list in cell B1. For the sake of simplicity, the data for the drop
+ * down lists is included on the same worksheet but this does not have to be the
+ * case; the data could appear on a separate sheet. If this were done, then the
+ * names for the regions would have to be different, they would have to include
+ * the name of the sheet.
+ *
+ * There are two keys to this technique. The first is the use of named area or
+ * regions of cells to hold the data for the drop down lists and the second is
+ * making use of the INDIRECT() function to convert a name into the addresses
+ * of the cells it refers to.
+ *
+ * Note that whilst this class builds just two linked drop down lists, there is
+ * nothing to prevent more being created. Quite simply, use the value selected
+ * by the user in one drop down list to determine what is shown in another and the
+ * value selected in that drop down list to determine what is shown in a third,
+ * and so on. Also, note that the data for the drop down lists is contained on
+ * contained on the same sheet as the validations themselves. This is done simply
+ * for simplicity and there is nothing to prevent a separate sheet being created
+ * and used to hold the data. If this is done then problems may be encountered
+ * if the sheet is opened with OpenOffice Calc. To prevent these problems, it is
+ * better to include the name of the sheet when calling the setRefersToFormula()
+ * method.
+ *
+ * @author Mark Beardsley [msb at apache.org]
+ * @version 1.00 30th March 2012
+ */
+public class LinkedDropDownLists {
+
+ LinkedDropDownLists(String workbookName) throws IOException {
+ // Using the ss.usermodel allows this class to support both binary
+ // and xml based workbooks. The choice of which one to create is
+ // made by checking the file extension.
+ Workbook workbook;
+ if (workbookName.endsWith(".xlsx")) {
+ workbook = new XSSFWorkbook();
+ } else {
+ workbook = new HSSFWorkbook();
+ }
+
+ // Build the sheet that will hold the data for the validations. This
+ // must be done first as it will create names that are referenced
+ // later.
+ Sheet sheet = workbook.createSheet("Linked Validations");
+ LinkedDropDownLists.buildDataSheet(sheet);
+
+ // Build the first data validation to occupy cell A1. Note
+ // that it retrieves it's data from the named area or region called
+ // CHOICES. Further information about this can be found in the
+ // static buildDataSheet() method below.
+ CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0);
+ DataValidationHelper dvHelper = sheet.getDataValidationHelper();
+ DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint("CHOICES");
+ DataValidation validation = dvHelper.createValidation(dvConstraint, addressList);
+ sheet.addValidationData(validation);
+
+ // Now, build the linked or dependent drop down list that will
+ // occupy cell B1. The key to the whole process is the use of the
+ // INDIRECT() function. In the buildDataSheet(0 method, a series of
+ // named regions are created and the names of three of them mirror
+ // the options available to the user in the first drop down list
+ // (in cell A1). Using the INDIRECT() function makes it possible
+ // to convert the selection the user makes in that first drop down
+ // into the addresses of a named region of cells and then to use
+ // those cells to populate the second drop down list.
+ addressList = new CellRangeAddressList(0, 0, 1, 1);
+ dvConstraint = dvHelper.createFormulaListConstraint(
+ "INDIRECT(UPPER($A$1))");
+ validation = dvHelper.createValidation(dvConstraint, addressList);
+ sheet.addValidationData(validation);
+
+ FileOutputStream fos = new FileOutputStream(workbookName);
+ workbook.write(fos);
+ fos.close();
+ workbook.close();
+ }
+
+ /**
+ * Called to populate the named areas/regions. The contents of the cells on
+ * row one will be used to populate the first drop down list. The contents of
+ * the cells on rows two, three and four will be used to populate the second
+ * drop down list, just which row will be determined by the choice the user
+ * makes in the first drop down list.
+ *
+ * In all cases, the approach is to create a row, create and populate cells
+ * with data and then specify a name that identifies those cells. With the
+ * exception of the first range, the names that are chosen for each range
+ * of cells are quite important. In short, each of the options the user
+ * could select in the first drop down list is used as the name for another
+ * range of cells. Thus, in this example, the user can select either
+ * 'Animal', 'Vegetable' or 'Mineral' in the first drop down and so the
+ * sheet contains ranges named 'ANIMAL', 'VEGETABLE' and 'MINERAL'.
+ *
+ * @param dataSheet An instance of a class that implements the Sheet Sheet
+ * interface (HSSFSheet or XSSFSheet).
+ */
+ private static final void buildDataSheet(Sheet dataSheet) {
+ Row row = null;
+ Cell cell = null;
+ Name name = null;
+
+ // The first row will hold the data for the first validation.
+ row = dataSheet.createRow(10);
+ cell = row.createCell(0);
+ cell.setCellValue("Animal");
+ cell = row.createCell(1);
+ cell.setCellValue("Vegetable");
+ cell = row.createCell(2);
+ cell.setCellValue("Mineral");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$11:$C$11");
+ name.setNameName("CHOICES");
+
+ // The next three rows will hold the data that will be used to
+ // populate the second, or linked, drop down list.
+ row = dataSheet.createRow(11);
+ cell = row.createCell(0);
+ cell.setCellValue("Lion");
+ cell = row.createCell(1);
+ cell.setCellValue("Tiger");
+ cell = row.createCell(2);
+ cell.setCellValue("Leopard");
+ cell = row.createCell(3);
+ cell.setCellValue("Elephant");
+ cell = row.createCell(4);
+ cell.setCellValue("Eagle");
+ cell = row.createCell(5);
+ cell.setCellValue("Horse");
+ cell = row.createCell(6);
+ cell.setCellValue("Zebra");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$12:$G$12");
+ name.setNameName("ANIMAL");
+
+ row = dataSheet.createRow(12);
+ cell = row.createCell(0);
+ cell.setCellValue("Cabbage");
+ cell = row.createCell(1);
+ cell.setCellValue("Cauliflower");
+ cell = row.createCell(2);
+ cell.setCellValue("Potato");
+ cell = row.createCell(3);
+ cell.setCellValue("Onion");
+ cell = row.createCell(4);
+ cell.setCellValue("Beetroot");
+ cell = row.createCell(5);
+ cell.setCellValue("Asparagus");
+ cell = row.createCell(6);
+ cell.setCellValue("Spinach");
+ cell = row.createCell(7);
+ cell.setCellValue("Chard");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$13:$H$13");
+ name.setNameName("VEGETABLE");
+
+ row = dataSheet.createRow(13);
+ cell = row.createCell(0);
+ cell.setCellValue("Bauxite");
+ cell = row.createCell(1);
+ cell.setCellValue("Quartz");
+ cell = row.createCell(2);
+ cell.setCellValue("Feldspar");
+ cell = row.createCell(3);
+ cell.setCellValue("Shist");
+ cell = row.createCell(4);
+ cell.setCellValue("Shale");
+ cell = row.createCell(5);
+ cell.setCellValue("Mica");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$14:$F$14");
+ name.setNameName("MINERAL");
+ }
+}
diff --git a/src/examples/src/org/apache/poi/ss/examples/formula/SettingExternalFunction.java b/src/examples/src/org/apache/poi/ss/examples/formula/SettingExternalFunction.java
index b9e1f2d37b..51f7c03670 100644
--- a/src/examples/src/org/apache/poi/ss/examples/formula/SettingExternalFunction.java
+++ b/src/examples/src/org/apache/poi/ss/examples/formula/SettingExternalFunction.java
@@ -1,95 +1,95 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ====================================================================
- */
-
-package org.apache.poi.ss.examples.formula;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.poi.ss.formula.OperationEvaluationContext;
-import org.apache.poi.ss.formula.eval.ErrorEval;
-import org.apache.poi.ss.formula.eval.ValueEval;
-import org.apache.poi.ss.formula.functions.FreeRefFunction;
-import org.apache.poi.ss.formula.udf.UDFFinder;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-
-/**
- * Demonstrates how to use functions provided by third-party add-ins, e.g. Bloomberg Excel Add-in.
- *
- * There can be situations when you are not interested in formula evaluation,
- * you just need to set the formula and the workbook will be evaluation by the client.
- */
-public class SettingExternalFunction {
-
- /**
- * wrap external functions in a plugin
- */
- public static class BloombergAddIn implements UDFFinder {
- private final Map _functionsByName;
-
- public BloombergAddIn() {
- // dummy function that returns NA
- // don't care about the implementation, we are not interested in evaluation
- // and this method will never be called
- FreeRefFunction NA = new FreeRefFunction() {
- @Override
- public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
- return ErrorEval.NA;
- }
- };
- _functionsByName = new HashMap();
- _functionsByName.put("BDP", NA);
- _functionsByName.put("BDH", NA);
- _functionsByName.put("BDS", NA);
- }
-
- @Override
- public FreeRefFunction findFunction(String name) {
- return _functionsByName.get(name.toUpperCase(Locale.ROOT));
- }
-
- }
-
- public static void main( String[] args ) throws IOException {
-
- Workbook wb = new XSSFWorkbook(); // or new HSSFWorkbook()
-
- // register the add-in
- wb.addToolPack(new BloombergAddIn());
-
- Sheet sheet = wb.createSheet();
- Row row = sheet.createRow(0);
- row.createCell(0).setCellFormula("BDP(\"GOOG Equity\",\"CHG_PCT_YTD\")/100");
- row.createCell(1).setCellFormula("BDH(\"goog us equity\",\"EBIT\",\"1/1/2005\",\"12/31/2009\",\"per=cy\",\"curr=USD\") ");
- row.createCell(2).setCellFormula("BDS(\"goog us equity\",\"top_20_holders_public_filings\") ");
-
- FileOutputStream out = new FileOutputStream("bloomberg-demo.xlsx");
- wb.write(out);
- out.close();
-
- wb.close();
- }
-
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.ss.examples.formula;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.poi.ss.formula.OperationEvaluationContext;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Demonstrates how to use functions provided by third-party add-ins, e.g. Bloomberg Excel Add-in.
+ *
+ * There can be situations when you are not interested in formula evaluation,
+ * you just need to set the formula and the workbook will be evaluation by the client.
+ */
+public class SettingExternalFunction {
+
+ /**
+ * wrap external functions in a plugin
+ */
+ public static class BloombergAddIn implements UDFFinder {
+ private final Map _functionsByName;
+
+ public BloombergAddIn() {
+ // dummy function that returns NA
+ // don't care about the implementation, we are not interested in evaluation
+ // and this method will never be called
+ FreeRefFunction NA = new FreeRefFunction() {
+ @Override
+ public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+ return ErrorEval.NA;
+ }
+ };
+ _functionsByName = new HashMap();
+ _functionsByName.put("BDP", NA);
+ _functionsByName.put("BDH", NA);
+ _functionsByName.put("BDS", NA);
+ }
+
+ @Override
+ public FreeRefFunction findFunction(String name) {
+ return _functionsByName.get(name.toUpperCase(Locale.ROOT));
+ }
+
+ }
+
+ public static void main( String[] args ) throws IOException {
+
+ Workbook wb = new XSSFWorkbook(); // or new HSSFWorkbook()
+
+ // register the add-in
+ wb.addToolPack(new BloombergAddIn());
+
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(0);
+ row.createCell(0).setCellFormula("BDP(\"GOOG Equity\",\"CHG_PCT_YTD\")/100");
+ row.createCell(1).setCellFormula("BDH(\"goog us equity\",\"EBIT\",\"1/1/2005\",\"12/31/2009\",\"per=cy\",\"curr=USD\") ");
+ row.createCell(2).setCellFormula("BDS(\"goog us equity\",\"top_20_holders_public_filings\") ");
+
+ FileOutputStream out = new FileOutputStream("bloomberg-demo.xlsx");
+ wb.write(out);
+ out.close();
+
+ wb.close();
+ }
+
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/DataExtraction.java b/src/examples/src/org/apache/poi/xslf/usermodel/DataExtraction.java
index 3358406858..ec8fe23428 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/DataExtraction.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/DataExtraction.java
@@ -1,95 +1,95 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ====================================================================
- */
-
-package org.apache.poi.xslf.usermodel;
-
-import java.awt.Dimension;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
-
-import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
-import org.apache.poi.openxml4j.opc.PackagePart;
-
-/**
- * Demonstrates how you can extract data from a .pptx file
- */
-public final class DataExtraction {
-
- public static void main(String args[]) throws IOException, OpenXML4JException {
-
- PrintStream out = System.out;
-
- if (args.length == 0) {
- out.println("Input file is required");
- return;
- }
-
- FileInputStream is = new FileInputStream(args[0]);
- XMLSlideShow ppt = new XMLSlideShow(is);
- is.close();
-
- // Get the document's embedded files.
- for (PackagePart p : ppt.getAllEmbedds()) {
- String type = p.getContentType();
- // typically file name
- String name = p.getPartName().getName();
- out.println("Embedded file ("+type+"): "+name);
-
- InputStream pIs = p.getInputStream();
- // make sense of the part data
- pIs.close();
-
- }
-
- // Get the document's embedded files.
- for (XSLFPictureData data : ppt.getPictureData()) {
- String type = data.getContentType();
- String name = data.getFileName();
- out.println("Picture ("+type+"): "+name);
-
- InputStream pIs = data.getInputStream();
- // make sense of the image data
- pIs.close();
- }
-
- // size of the canvas in points
- Dimension pageSize = ppt.getPageSize();
- out.println("Pagesize: "+pageSize);
-
- for(XSLFSlide slide : ppt.getSlides()) {
- for(XSLFShape shape : slide){
- if(shape instanceof XSLFTextShape) {
- XSLFTextShape txShape = (XSLFTextShape)shape;
- out.println(txShape.getText());
- } else if (shape instanceof XSLFPictureShape){
- XSLFPictureShape pShape = (XSLFPictureShape)shape;
- XSLFPictureData pData = pShape.getPictureData();
- out.println(pData.getFileName());
- } else {
- out.println("Process me: " + shape.getClass());
- }
- }
- }
-
- ppt.close();
- }
-
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.awt.Dimension;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+/**
+ * Demonstrates how you can extract data from a .pptx file
+ */
+public final class DataExtraction {
+
+ public static void main(String args[]) throws IOException, OpenXML4JException {
+
+ PrintStream out = System.out;
+
+ if (args.length == 0) {
+ out.println("Input file is required");
+ return;
+ }
+
+ FileInputStream is = new FileInputStream(args[0]);
+ XMLSlideShow ppt = new XMLSlideShow(is);
+ is.close();
+
+ // Get the document's embedded files.
+ for (PackagePart p : ppt.getAllEmbedds()) {
+ String type = p.getContentType();
+ // typically file name
+ String name = p.getPartName().getName();
+ out.println("Embedded file ("+type+"): "+name);
+
+ InputStream pIs = p.getInputStream();
+ // make sense of the part data
+ pIs.close();
+
+ }
+
+ // Get the document's embedded files.
+ for (XSLFPictureData data : ppt.getPictureData()) {
+ String type = data.getContentType();
+ String name = data.getFileName();
+ out.println("Picture ("+type+"): "+name);
+
+ InputStream pIs = data.getInputStream();
+ // make sense of the image data
+ pIs.close();
+ }
+
+ // size of the canvas in points
+ Dimension pageSize = ppt.getPageSize();
+ out.println("Pagesize: "+pageSize);
+
+ for(XSLFSlide slide : ppt.getSlides()) {
+ for(XSLFShape shape : slide){
+ if(shape instanceof XSLFTextShape) {
+ XSLFTextShape txShape = (XSLFTextShape)shape;
+ out.println(txShape.getText());
+ } else if (shape instanceof XSLFPictureShape){
+ XSLFPictureShape pShape = (XSLFPictureShape)shape;
+ XSLFPictureData pData = pShape.getPictureData();
+ out.println(pData.getFileName());
+ } else {
+ out.println("Process me: " + shape.getClass());
+ }
+ }
+ }
+
+ ppt.close();
+ }
+
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/MergePresentations.java b/src/examples/src/org/apache/poi/xslf/usermodel/MergePresentations.java
index 38e9285013..73f195eb76 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/MergePresentations.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/MergePresentations.java
@@ -1,54 +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.poi.xslf.usermodel;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-
-/**
- * Merge multiple pptx presentations together
- *
- * @author Yegor Kozlov
- */
-public final class MergePresentations {
-
- public static void main(String args[]) throws Exception {
- XMLSlideShow ppt = new XMLSlideShow();
-
- for(String arg : args){
- FileInputStream is = new FileInputStream(arg);
- XMLSlideShow src = new XMLSlideShow(is);
- is.close();
-
- for(XSLFSlide srcSlide : src.getSlides()){
- ppt.createSlide().importContent(srcSlide);
- }
-
- src.close();
- }
-
- FileOutputStream out = new FileOutputStream("merged.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+/**
+ * Merge multiple pptx presentations together
+ *
+ * @author Yegor Kozlov
+ */
+public final class MergePresentations {
+
+ public static void main(String args[]) throws Exception {
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ for(String arg : args){
+ FileInputStream is = new FileInputStream(arg);
+ XMLSlideShow src = new XMLSlideShow(is);
+ is.close();
+
+ for(XSLFSlide srcSlide : src.getSlides()){
+ ppt.createSlide().importContent(srcSlide);
+ }
+
+ src.close();
+ }
+
+ FileOutputStream out = new FileOutputStream("merged.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java b/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java
index df98b92964..4ea36e56ec 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java
@@ -1,165 +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.poi.xslf.usermodel;
-
-import org.apache.poi.POIXMLDocumentPart;
-import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.xssf.usermodel.XSSFRow;
-import org.apache.poi.xssf.usermodel.XSSFSheet;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData;
-import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrVal;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.OutputStream;
-
-/**
- * Build a pie chart from a template pptx
- *
- * @author Yegor Kozlov
- */
-public class PieChartDemo {
- private static void usage(){
- System.out.println("Usage: PieChartDemo ");
- System.out.println(" pie-chart-template.pptx template with a pie chart");
- System.out.println(" pie-chart-data.txt the model to set. First line is chart title, " +
- "then go pairs {axis-label value}");
- }
-
- public static void main(String[] args) throws Exception {
- if(args.length < 2) {
- usage();
- return;
- }
-
- BufferedReader modelReader = new BufferedReader(new FileReader(args[1]));
- XMLSlideShow pptx = null;
- try {
- String chartTitle = modelReader.readLine(); // first line is chart title
-
- pptx = new XMLSlideShow(new FileInputStream(args[0]));
- XSLFSlide slide = pptx.getSlides().get(0);
-
- // find chart in the slide
- XSLFChart chart = null;
- for(POIXMLDocumentPart part : slide.getRelations()){
- if(part instanceof XSLFChart){
- chart = (XSLFChart) part;
- break;
- }
- }
-
- if(chart == null) throw new IllegalStateException("chart not found in the template");
-
- // embedded Excel workbook that holds the chart data
- POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
- XSSFWorkbook wb = new XSSFWorkbook();
- try {
- XSSFSheet sheet = wb.createSheet();
-
- CTChart ctChart = chart.getCTChart();
- CTPlotArea plotArea = ctChart.getPlotArea();
-
- CTPieChart pieChart = plotArea.getPieChartArray(0);
- //Pie Chart Series
- CTPieSer ser = pieChart.getSerArray(0);
-
- // Series Text
- CTSerTx tx = ser.getTx();
- tx.getStrRef().getStrCache().getPtArray(0).setV(chartTitle);
- sheet.createRow(0).createCell(1).setCellValue(chartTitle);
- String titleRef = new CellReference(sheet.getSheetName(), 0, 1, true, true).formatAsString();
- tx.getStrRef().setF(titleRef);
-
- // Category Axis Data
- CTAxDataSource cat = ser.getCat();
- CTStrData strData = cat.getStrRef().getStrCache();
-
- // Values
- CTNumDataSource val = ser.getVal();
- CTNumData numData = val.getNumRef().getNumCache();
-
- strData.setPtArray(null); // unset old axis text
- numData.setPtArray(null); // unset old values
-
- // set model
- int idx = 0;
- int rownum = 1;
- String ln;
- while((ln = modelReader.readLine()) != null){
- String[] vals = ln.split("\\s+");
- CTNumVal numVal = numData.addNewPt();
- numVal.setIdx(idx);
- numVal.setV(vals[1]);
-
- CTStrVal sVal = strData.addNewPt();
- sVal.setIdx(idx);
- sVal.setV(vals[0]);
-
- idx++;
- XSSFRow row = sheet.createRow(rownum++);
- row.createCell(0).setCellValue(vals[0]);
- row.createCell(1).setCellValue(Double.valueOf(vals[1]));
- }
- numData.getPtCount().setVal(idx);
- strData.getPtCount().setVal(idx);
-
- String numDataRange = new CellRangeAddress(1, rownum-1, 1, 1).formatAsString(sheet.getSheetName(), true);
- val.getNumRef().setF(numDataRange);
- String axisDataRange = new CellRangeAddress(1, rownum-1, 0, 0).formatAsString(sheet.getSheetName(), true);
- cat.getStrRef().setF(axisDataRange);
-
- // updated the embedded workbook with the data
- OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();
- try {
- wb.write(xlsOut);
- } finally {
- xlsOut.close();
- }
-
- // save the result
- OutputStream out = new FileOutputStream("pie-chart-demo-output.pptx");
- try {
- pptx.write(out);
- } finally {
- out.close();
- }
- } finally {
- wb.close();
- }
- } finally {
- if (pptx != null) pptx.close();
- modelReader.close();
- }
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import org.apache.poi.POIXMLDocumentPart;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData;
+import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrVal;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.OutputStream;
+
+/**
+ * Build a pie chart from a template pptx
+ *
+ * @author Yegor Kozlov
+ */
+public class PieChartDemo {
+ private static void usage(){
+ System.out.println("Usage: PieChartDemo ");
+ System.out.println(" pie-chart-template.pptx template with a pie chart");
+ System.out.println(" pie-chart-data.txt the model to set. First line is chart title, " +
+ "then go pairs {axis-label value}");
+ }
+
+ public static void main(String[] args) throws Exception {
+ if(args.length < 2) {
+ usage();
+ return;
+ }
+
+ BufferedReader modelReader = new BufferedReader(new FileReader(args[1]));
+ XMLSlideShow pptx = null;
+ try {
+ String chartTitle = modelReader.readLine(); // first line is chart title
+
+ pptx = new XMLSlideShow(new FileInputStream(args[0]));
+ XSLFSlide slide = pptx.getSlides().get(0);
+
+ // find chart in the slide
+ XSLFChart chart = null;
+ for(POIXMLDocumentPart part : slide.getRelations()){
+ if(part instanceof XSLFChart){
+ chart = (XSLFChart) part;
+ break;
+ }
+ }
+
+ if(chart == null) throw new IllegalStateException("chart not found in the template");
+
+ // embedded Excel workbook that holds the chart data
+ POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
+ XSSFWorkbook wb = new XSSFWorkbook();
+ try {
+ XSSFSheet sheet = wb.createSheet();
+
+ CTChart ctChart = chart.getCTChart();
+ CTPlotArea plotArea = ctChart.getPlotArea();
+
+ CTPieChart pieChart = plotArea.getPieChartArray(0);
+ //Pie Chart Series
+ CTPieSer ser = pieChart.getSerArray(0);
+
+ // Series Text
+ CTSerTx tx = ser.getTx();
+ tx.getStrRef().getStrCache().getPtArray(0).setV(chartTitle);
+ sheet.createRow(0).createCell(1).setCellValue(chartTitle);
+ String titleRef = new CellReference(sheet.getSheetName(), 0, 1, true, true).formatAsString();
+ tx.getStrRef().setF(titleRef);
+
+ // Category Axis Data
+ CTAxDataSource cat = ser.getCat();
+ CTStrData strData = cat.getStrRef().getStrCache();
+
+ // Values
+ CTNumDataSource val = ser.getVal();
+ CTNumData numData = val.getNumRef().getNumCache();
+
+ strData.setPtArray(null); // unset old axis text
+ numData.setPtArray(null); // unset old values
+
+ // set model
+ int idx = 0;
+ int rownum = 1;
+ String ln;
+ while((ln = modelReader.readLine()) != null){
+ String[] vals = ln.split("\\s+");
+ CTNumVal numVal = numData.addNewPt();
+ numVal.setIdx(idx);
+ numVal.setV(vals[1]);
+
+ CTStrVal sVal = strData.addNewPt();
+ sVal.setIdx(idx);
+ sVal.setV(vals[0]);
+
+ idx++;
+ XSSFRow row = sheet.createRow(rownum++);
+ row.createCell(0).setCellValue(vals[0]);
+ row.createCell(1).setCellValue(Double.valueOf(vals[1]));
+ }
+ numData.getPtCount().setVal(idx);
+ strData.getPtCount().setVal(idx);
+
+ String numDataRange = new CellRangeAddress(1, rownum-1, 1, 1).formatAsString(sheet.getSheetName(), true);
+ val.getNumRef().setF(numDataRange);
+ String axisDataRange = new CellRangeAddress(1, rownum-1, 0, 0).formatAsString(sheet.getSheetName(), true);
+ cat.getStrRef().setF(axisDataRange);
+
+ // updated the embedded workbook with the data
+ OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();
+ try {
+ wb.write(xlsOut);
+ } finally {
+ xlsOut.close();
+ }
+
+ // save the result
+ OutputStream out = new FileOutputStream("pie-chart-demo-output.pptx");
+ try {
+ pptx.write(out);
+ } finally {
+ out.close();
+ }
+ } finally {
+ wb.close();
+ }
+ } finally {
+ if (pptx != null) pptx.close();
+ modelReader.close();
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial1.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial1.java
index 60f5d7121d..714ebb8121 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial1.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial1.java
@@ -1,74 +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.poi.xslf.usermodel;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Demonstrates how to create slides with predefined layout
- * and fill the placeholder shapes
- *
- * @author Yegor Kozlov
- */
-public class Tutorial1 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- // XSLFSlide#createSlide() with no arguments creates a blank slide
- /*XSLFSlide blankSlide =*/ ppt.createSlide();
-
-
- XSLFSlideMaster master = ppt.getSlideMasters().get(0);
-
- XSLFSlideLayout layout1 = master.getLayout(SlideLayout.TITLE);
- XSLFSlide slide1 = ppt.createSlide(layout1) ;
- XSLFTextShape[] ph1 = slide1.getPlaceholders();
- XSLFTextShape titlePlaceholder1 = ph1[0];
- titlePlaceholder1.setText("This is a title");
- XSLFTextShape subtitlePlaceholder1 = ph1[1];
- subtitlePlaceholder1.setText("this is a subtitle");
-
- XSLFSlideLayout layout2 = master.getLayout(SlideLayout.TITLE_AND_CONTENT);
- XSLFSlide slide2 = ppt.createSlide(layout2) ;
- XSLFTextShape[] ph2 = slide2.getPlaceholders();
- XSLFTextShape titlePlaceholder2 = ph2[0];
- titlePlaceholder2.setText("This is a title");
- XSLFTextShape bodyPlaceholder = ph2[1];
- // we are going to add text by paragraphs. Clear the default placehoder text before that
- bodyPlaceholder.clearText();
- XSLFTextParagraph p1 = bodyPlaceholder.addNewTextParagraph();
- p1.setIndentLevel(0);
- p1.addNewTextRun().setText("Level1 text");
- XSLFTextParagraph p2 = bodyPlaceholder.addNewTextParagraph();
- p2.setIndentLevel(1);
- p2.addNewTextRun().setText("Level2 text");
- XSLFTextParagraph p3 = bodyPlaceholder.addNewTextParagraph();
- p3.setIndentLevel(2);
- p3.addNewTextRun().setText("Level3 text");
-
- FileOutputStream out = new FileOutputStream("slides.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Demonstrates how to create slides with predefined layout
+ * and fill the placeholder shapes
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial1 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ // XSLFSlide#createSlide() with no arguments creates a blank slide
+ /*XSLFSlide blankSlide =*/ ppt.createSlide();
+
+
+ XSLFSlideMaster master = ppt.getSlideMasters().get(0);
+
+ XSLFSlideLayout layout1 = master.getLayout(SlideLayout.TITLE);
+ XSLFSlide slide1 = ppt.createSlide(layout1) ;
+ XSLFTextShape[] ph1 = slide1.getPlaceholders();
+ XSLFTextShape titlePlaceholder1 = ph1[0];
+ titlePlaceholder1.setText("This is a title");
+ XSLFTextShape subtitlePlaceholder1 = ph1[1];
+ subtitlePlaceholder1.setText("this is a subtitle");
+
+ XSLFSlideLayout layout2 = master.getLayout(SlideLayout.TITLE_AND_CONTENT);
+ XSLFSlide slide2 = ppt.createSlide(layout2) ;
+ XSLFTextShape[] ph2 = slide2.getPlaceholders();
+ XSLFTextShape titlePlaceholder2 = ph2[0];
+ titlePlaceholder2.setText("This is a title");
+ XSLFTextShape bodyPlaceholder = ph2[1];
+ // we are going to add text by paragraphs. Clear the default placehoder text before that
+ bodyPlaceholder.clearText();
+ XSLFTextParagraph p1 = bodyPlaceholder.addNewTextParagraph();
+ p1.setIndentLevel(0);
+ p1.addNewTextRun().setText("Level1 text");
+ XSLFTextParagraph p2 = bodyPlaceholder.addNewTextParagraph();
+ p2.setIndentLevel(1);
+ p2.addNewTextRun().setText("Level2 text");
+ XSLFTextParagraph p3 = bodyPlaceholder.addNewTextParagraph();
+ p3.setIndentLevel(2);
+ p3.addNewTextRun().setText("Level3 text");
+
+ FileOutputStream out = new FileOutputStream("slides.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
index af05962eeb..3f1402ae62 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
@@ -1,85 +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.poi.xslf.usermodel;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Basic paragraph and text formatting
- *
- * @author Yegor Kozlov
- */
-public class Tutorial2 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- XSLFSlide slide1 = ppt.createSlide();
- XSLFTextBox shape1 = slide1.createTextBox();
- // initial height of the text box is 100 pt but
- Rectangle anchor = new Rectangle(10, 100, 300, 100);
- shape1.setAnchor(anchor);
-
- XSLFTextParagraph p1 = shape1.addNewTextParagraph();
- XSLFTextRun r1 = p1.addNewTextRun();
- r1.setText("Paragraph Formatting");
- r1.setFontSize(24d);
- r1.setFontColor(new Color(85, 142, 213));
-
- XSLFTextParagraph p2 = shape1.addNewTextParagraph();
- // If spaceBefore >= 0, then space is a percentage of normal line height.
- // If spaceBefore < 0, the absolute value of linespacing is the spacing in points
- p2.setSpaceBefore(-20d); // 20 pt from the previous paragraph
- p2.setSpaceAfter(300d); // 3 lines after the paragraph
- XSLFTextRun r2 = p2.addNewTextRun();
- r2.setText("Paragraph properties apply to all text residing within the corresponding paragraph.");
- r2.setFontSize(16d);
-
- XSLFTextParagraph p3 = shape1.addNewTextParagraph();
-
- XSLFTextRun r3 = p3.addNewTextRun();
- r3.setText("Run Formatting");
- r3.setFontSize(24d);
- r3.setFontColor(new Color(85, 142, 213));
-
- XSLFTextParagraph p4 = shape1.addNewTextParagraph();
- p4.setSpaceBefore(-20d); // 20 pt from the previous paragraph
- p4.setSpaceAfter(300d); // 3 lines after the paragraph
- XSLFTextRun r4 = p4.addNewTextRun();
- r4.setFontSize(16d);
- r4.setText(
- "Run level formatting is the most granular property level and allows " +
- "for the specifying of all low level text properties. The text run is " +
- "what all paragraphs are derived from and thus specifying various " +
- "properties per run will allow for a diversely formatted text paragraph.");
-
- // resize the shape to fit text
- shape1.resizeToFitText();
-
- FileOutputStream out = new FileOutputStream("text.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Basic paragraph and text formatting
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial2 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ XSLFSlide slide1 = ppt.createSlide();
+ XSLFTextBox shape1 = slide1.createTextBox();
+ // initial height of the text box is 100 pt but
+ Rectangle anchor = new Rectangle(10, 100, 300, 100);
+ shape1.setAnchor(anchor);
+
+ XSLFTextParagraph p1 = shape1.addNewTextParagraph();
+ XSLFTextRun r1 = p1.addNewTextRun();
+ r1.setText("Paragraph Formatting");
+ r1.setFontSize(24d);
+ r1.setFontColor(new Color(85, 142, 213));
+
+ XSLFTextParagraph p2 = shape1.addNewTextParagraph();
+ // If spaceBefore >= 0, then space is a percentage of normal line height.
+ // If spaceBefore < 0, the absolute value of linespacing is the spacing in points
+ p2.setSpaceBefore(-20d); // 20 pt from the previous paragraph
+ p2.setSpaceAfter(300d); // 3 lines after the paragraph
+ XSLFTextRun r2 = p2.addNewTextRun();
+ r2.setText("Paragraph properties apply to all text residing within the corresponding paragraph.");
+ r2.setFontSize(16d);
+
+ XSLFTextParagraph p3 = shape1.addNewTextParagraph();
+
+ XSLFTextRun r3 = p3.addNewTextRun();
+ r3.setText("Run Formatting");
+ r3.setFontSize(24d);
+ r3.setFontColor(new Color(85, 142, 213));
+
+ XSLFTextParagraph p4 = shape1.addNewTextParagraph();
+ p4.setSpaceBefore(-20d); // 20 pt from the previous paragraph
+ p4.setSpaceAfter(300d); // 3 lines after the paragraph
+ XSLFTextRun r4 = p4.addNewTextRun();
+ r4.setFontSize(16d);
+ r4.setText(
+ "Run level formatting is the most granular property level and allows " +
+ "for the specifying of all low level text properties. The text run is " +
+ "what all paragraphs are derived from and thus specifying various " +
+ "properties per run will allow for a diversely formatted text paragraph.");
+
+ // resize the shape to fit text
+ shape1.resizeToFitText();
+
+ FileOutputStream out = new FileOutputStream("text.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial3.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial3.java
index 4433605f34..4cab79d886 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial3.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial3.java
@@ -1,51 +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.poi.xslf.usermodel;
-
-import java.awt.Rectangle;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.sl.usermodel.Placeholder;
-
-/**
- * How to set slide title
- *
- * @author Yegor Kozlov
- */
-public class Tutorial3 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- XSLFSlide slide = ppt.createSlide();
-
- XSLFTextShape titleShape = slide.createTextBox();
- titleShape.setPlaceholder(Placeholder.TITLE);
- titleShape.setText("This is a slide title");
- titleShape.setAnchor(new Rectangle(50, 50, 400, 100));
-
- FileOutputStream out = new FileOutputStream("title.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.sl.usermodel.Placeholder;
+
+/**
+ * How to set slide title
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial3 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ XSLFSlide slide = ppt.createSlide();
+
+ XSLFTextShape titleShape = slide.createTextBox();
+ titleShape.setPlaceholder(Placeholder.TITLE);
+ titleShape.setText("This is a slide title");
+ titleShape.setAnchor(new Rectangle(50, 50, 400, 100));
+
+ FileOutputStream out = new FileOutputStream("title.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial4.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial4.java
index 94082a2ef9..0cf0fd667d 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial4.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial4.java
@@ -1,94 +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.poi.xslf.usermodel;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.sl.usermodel.TableCell.BorderEdge;
-import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
-
-/**
- * PPTX Tables
- *
- * @author Yegor Kozlov
- */
-public class Tutorial4 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- // XSLFSlide#createSlide() with no arguments creates a blank slide
- XSLFSlide slide = ppt.createSlide();
-
- XSLFTable tbl = slide.createTable();
- tbl.setAnchor(new Rectangle(50, 50, 450, 300));
-
- int numColumns = 3;
- int numRows = 5;
- XSLFTableRow headerRow = tbl.addRow();
- headerRow.setHeight(50);
- // header
- for(int i = 0; i < numColumns; i++) {
- XSLFTableCell th = headerRow.addCell();
- XSLFTextParagraph p = th.addNewTextParagraph();
- p.setTextAlign(TextAlign.CENTER);
- XSLFTextRun r = p.addNewTextRun();
- r.setText("Header " + (i+1));
- r.setBold(true);
- r.setFontColor(Color.white);
- th.setFillColor(new Color(79, 129, 189));
- th.setBorderWidth(BorderEdge.bottom, 2.0);
- th.setBorderColor(BorderEdge.bottom, Color.white);
-
- tbl.setColumnWidth(i, 150); // all columns are equally sized
- }
-
- // rows
-
- for(int rownum = 0; rownum < numRows; rownum ++){
- XSLFTableRow tr = tbl.addRow();
- tr.setHeight(50);
- // header
- for(int i = 0; i < numColumns; i++) {
- XSLFTableCell cell = tr.addCell();
- XSLFTextParagraph p = cell.addNewTextParagraph();
- XSLFTextRun r = p.addNewTextRun();
-
- r.setText("Cell " + (i+1));
- if(rownum % 2 == 0)
- cell.setFillColor(new Color(208, 216, 232));
- else
- cell.setFillColor(new Color(233, 247, 244));
-
- }
-
- }
-
-
- FileOutputStream out = new FileOutputStream("table.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.sl.usermodel.TableCell.BorderEdge;
+import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
+
+/**
+ * PPTX Tables
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial4 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ // XSLFSlide#createSlide() with no arguments creates a blank slide
+ XSLFSlide slide = ppt.createSlide();
+
+ XSLFTable tbl = slide.createTable();
+ tbl.setAnchor(new Rectangle(50, 50, 450, 300));
+
+ int numColumns = 3;
+ int numRows = 5;
+ XSLFTableRow headerRow = tbl.addRow();
+ headerRow.setHeight(50);
+ // header
+ for(int i = 0; i < numColumns; i++) {
+ XSLFTableCell th = headerRow.addCell();
+ XSLFTextParagraph p = th.addNewTextParagraph();
+ p.setTextAlign(TextAlign.CENTER);
+ XSLFTextRun r = p.addNewTextRun();
+ r.setText("Header " + (i+1));
+ r.setBold(true);
+ r.setFontColor(Color.white);
+ th.setFillColor(new Color(79, 129, 189));
+ th.setBorderWidth(BorderEdge.bottom, 2.0);
+ th.setBorderColor(BorderEdge.bottom, Color.white);
+
+ tbl.setColumnWidth(i, 150); // all columns are equally sized
+ }
+
+ // rows
+
+ for(int rownum = 0; rownum < numRows; rownum ++){
+ XSLFTableRow tr = tbl.addRow();
+ tr.setHeight(50);
+ // header
+ for(int i = 0; i < numColumns; i++) {
+ XSLFTableCell cell = tr.addCell();
+ XSLFTextParagraph p = cell.addNewTextParagraph();
+ XSLFTextRun r = p.addNewTextRun();
+
+ r.setText("Cell " + (i+1));
+ if(rownum % 2 == 0)
+ cell.setFillColor(new Color(208, 216, 232));
+ else
+ cell.setFillColor(new Color(233, 247, 244));
+
+ }
+
+ }
+
+
+ FileOutputStream out = new FileOutputStream("table.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial5.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial5.java
index 06542afd9e..988caee2e5 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial5.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial5.java
@@ -1,51 +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.poi.xslf.usermodel;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.sl.usermodel.PictureData.PictureType;
-
-/**
- * Images
- *
- * @author Yegor Kozlov
- */
-public class Tutorial5 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- XSLFSlide slide = ppt.createSlide();
-
- File img = new File(System.getProperty("POI.testdata.path", "test-data"), "slideshow/clock.jpg");
- XSLFPictureData pictureData = ppt.addPicture(img, PictureType.PNG);
-
- /*XSLFPictureShape shape =*/ slide.createPicture(pictureData);
-
- FileOutputStream out = new FileOutputStream("images.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.sl.usermodel.PictureData.PictureType;
+
+/**
+ * Images
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial5 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ XSLFSlide slide = ppt.createSlide();
+
+ File img = new File(System.getProperty("POI.testdata.path", "test-data"), "slideshow/clock.jpg");
+ XSLFPictureData pictureData = ppt.addPicture(img, PictureType.PNG);
+
+ /*XSLFPictureShape shape =*/ slide.createPicture(pictureData);
+
+ FileOutputStream out = new FileOutputStream("images.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
index 73a59d0b81..020089555d 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial6.java
@@ -1,61 +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.poi.xslf.usermodel;
-
-import java.awt.Rectangle;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Hyperlinks
- *
- * @author Yegor Kozlov
- */
-public class Tutorial6 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- XSLFSlide slide1 = ppt.createSlide();
- XSLFSlide slide2 = ppt.createSlide();
-
- XSLFTextBox shape1 = slide1.createTextBox();
- shape1.setAnchor(new Rectangle(50, 50, 200, 50));
- XSLFTextRun r1 = shape1.addNewTextParagraph().addNewTextRun();
- XSLFHyperlink link1 = r1.createHyperlink();
- r1.setText("http://poi.apache.org"); // visible text
- link1.setAddress("http://poi.apache.org"); // link address
-
- XSLFTextBox shape2 = slide1.createTextBox();
- shape2.setAnchor(new Rectangle(300, 50, 200, 50));
- XSLFTextRun r2 = shape2.addNewTextParagraph().addNewTextRun();
- XSLFHyperlink link2 = r2.createHyperlink();
- r2.setText("Go to the second slide"); // visible text
- link2.linkToSlide(slide2); // link address
-
-
-
- FileOutputStream out = new FileOutputStream("hyperlinks.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Hyperlinks
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial6 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ XSLFSlide slide1 = ppt.createSlide();
+ XSLFSlide slide2 = ppt.createSlide();
+
+ XSLFTextBox shape1 = slide1.createTextBox();
+ shape1.setAnchor(new Rectangle(50, 50, 200, 50));
+ XSLFTextRun r1 = shape1.addNewTextParagraph().addNewTextRun();
+ XSLFHyperlink link1 = r1.createHyperlink();
+ r1.setText("http://poi.apache.org"); // visible text
+ link1.setAddress("http://poi.apache.org"); // link address
+
+ XSLFTextBox shape2 = slide1.createTextBox();
+ shape2.setAnchor(new Rectangle(300, 50, 200, 50));
+ XSLFTextRun r2 = shape2.addNewTextParagraph().addNewTextRun();
+ XSLFHyperlink link2 = r2.createHyperlink();
+ r2.setText("Go to the second slide"); // visible text
+ link2.linkToSlide(slide2); // link address
+
+
+
+ FileOutputStream out = new FileOutputStream("hyperlinks.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
index 26f822d1c7..647fef7583 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
@@ -1,90 +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.poi.xslf.usermodel;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.sl.usermodel.AutoNumberingScheme;
-
-/**
- * Bullets and numbering
- *
- * @author Yegor Kozlov
- */
-public class Tutorial7 {
-
- public static void main(String[] args) throws IOException{
- XMLSlideShow ppt = new XMLSlideShow();
-
- XSLFSlide slide = ppt.createSlide();
- XSLFTextBox shape = slide.createTextBox();
- shape.setAnchor(new Rectangle(50, 50, 400, 200));
-
- XSLFTextParagraph p1 = shape.addNewTextParagraph();
- p1.setIndentLevel(0);
- p1.setBullet(true);
- XSLFTextRun r1 = p1.addNewTextRun();
- r1.setText("Bullet1");
-
- XSLFTextParagraph p2 = shape.addNewTextParagraph();
- // indentation before text
- p2.setLeftMargin(60d);
- // the bullet is set 40 pt before the text
- p2.setIndent(-40d);
- p2.setBullet(true);
- // customize bullets
- p2.setBulletFontColor(Color.red);
- p2.setBulletFont("Wingdings");
- p2.setBulletCharacter("\u0075");
- p2.setIndentLevel(1);
- XSLFTextRun r2 = p2.addNewTextRun();
- r2.setText("Bullet2");
-
- // the next three paragraphs form an auto-numbered list
- XSLFTextParagraph p3 = shape.addNewTextParagraph();
- p3.setBulletAutoNumber(AutoNumberingScheme.alphaLcParenRight, 1);
- p3.setIndentLevel(2);
- XSLFTextRun r3 = p3.addNewTextRun();
- r3.setText("Numbered List Item - 1");
-
- XSLFTextParagraph p4 = shape.addNewTextParagraph();
- p4.setBulletAutoNumber(AutoNumberingScheme.alphaLcParenRight, 2);
- p4.setIndentLevel(2);
- XSLFTextRun r4 = p4.addNewTextRun();
- r4.setText("Numbered List Item - 2");
-
- XSLFTextParagraph p5 = shape.addNewTextParagraph();
- p5.setBulletAutoNumber(AutoNumberingScheme.alphaLcParenRight, 3);
- p5.setIndentLevel(2);
- XSLFTextRun r5 = p5.addNewTextRun();
- r5.setText("Numbered List Item - 3");
-
- shape.resizeToFitText();
-
- FileOutputStream out = new FileOutputStream("list.pptx");
- ppt.write(out);
- out.close();
-
- ppt.close();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xslf.usermodel;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.sl.usermodel.AutoNumberingScheme;
+
+/**
+ * Bullets and numbering
+ *
+ * @author Yegor Kozlov
+ */
+public class Tutorial7 {
+
+ public static void main(String[] args) throws IOException{
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ XSLFSlide slide = ppt.createSlide();
+ XSLFTextBox shape = slide.createTextBox();
+ shape.setAnchor(new Rectangle(50, 50, 400, 200));
+
+ XSLFTextParagraph p1 = shape.addNewTextParagraph();
+ p1.setIndentLevel(0);
+ p1.setBullet(true);
+ XSLFTextRun r1 = p1.addNewTextRun();
+ r1.setText("Bullet1");
+
+ XSLFTextParagraph p2 = shape.addNewTextParagraph();
+ // indentation before text
+ p2.setLeftMargin(60d);
+ // the bullet is set 40 pt before the text
+ p2.setIndent(-40d);
+ p2.setBullet(true);
+ // customize bullets
+ p2.setBulletFontColor(Color.red);
+ p2.setBulletFont("Wingdings");
+ p2.setBulletCharacter("\u0075");
+ p2.setIndentLevel(1);
+ XSLFTextRun r2 = p2.addNewTextRun();
+ r2.setText("Bullet2");
+
+ // the next three paragraphs form an auto-numbered list
+ XSLFTextParagraph p3 = shape.addNewTextParagraph();
+ p3.setBulletAutoNumber(AutoNumberingScheme.alphaLcParenRight, 1);
+ p3.setIndentLevel(2);
+ XSLFTextRun r3 = p3.addNewTextRun();
+ r3.setText("Numbered List Item - 1");
+
+ XSLFTextParagraph p4 = shape.addNewTextParagraph();
+ p4.setBulletAutoNumber(AutoNumberingScheme.alphaLcParenRight, 2);
+ p4.setIndentLevel(2);
+ XSLFTextRun r4 = p4.addNewTextRun();
+ r4.setText("Numbered List Item - 2");
+
+ XSLFTextParagraph p5 = shape.addNewTextParagraph();
+ p5.setBulletAutoNumber(AutoNumberingScheme.alphaLcParenRight, 3);
+ p5.setIndentLevel(2);
+ XSLFTextRun r5 = p5.addNewTextRun();
+ r5.setText("Numbered List Item - 3");
+
+ shape.resizeToFitText();
+
+ FileOutputStream out = new FileOutputStream("list.pptx");
+ ppt.write(out);
+ out.close();
+
+ ppt.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java
index ffc3ff5236..e2eab81f53 100644
--- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java
+++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java
@@ -1,90 +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.poi.xssf.usermodel.examples;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.ClientAnchor;
-import org.apache.poi.ss.usermodel.Comment;
-import org.apache.poi.ss.usermodel.CreationHelper;
-import org.apache.poi.ss.usermodel.Drawing;
-import org.apache.poi.ss.usermodel.Font;
-import org.apache.poi.ss.usermodel.IndexedColors;
-import org.apache.poi.ss.usermodel.RichTextString;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.util.CellAddress;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-
-/**
- * Demonstrates how to work with excel cell comments.
- *
- *
- * Excel comment is a kind of a text shape,
- * so inserting a comment is very similar to placing a text box in a worksheet
- *
- *
- * @author Yegor Kozlov
- */
-public class CellComments {
- public static void main(String[] args) throws IOException {
- Workbook wb = new XSSFWorkbook();
-
- CreationHelper factory = wb.getCreationHelper();
-
- Sheet sheet = wb.createSheet();
-
- Cell cell1 = sheet.createRow(3).createCell(5);
- cell1.setCellValue("F4");
-
- Drawing> drawing = sheet.createDrawingPatriarch();
-
- ClientAnchor anchor = factory.createClientAnchor();
-
- Comment comment1 = drawing.createCellComment(anchor);
- RichTextString str1 = factory.createRichTextString("Hello, World!");
- comment1.setString(str1);
- comment1.setAuthor("Apache POI");
- cell1.setCellComment(comment1);
-
- Cell cell2 = sheet.createRow(2).createCell(2);
- cell2.setCellValue("C3");
-
- Comment comment2 = drawing.createCellComment(anchor);
- RichTextString str2 = factory.createRichTextString("XSSF can set cell comments");
- //apply custom font to the text in the comment
- Font font = wb.createFont();
- font.setFontName("Arial");
- font.setFontHeightInPoints((short)14);
- font.setBold(true);
- font.setColor(IndexedColors.RED.getIndex());
- str2.applyFont(font);
-
- comment2.setString(str2);
- comment2.setAuthor("Apache POI");
- comment2.setAddress(new CellAddress("C3"));
-
- String fname = "comments.xlsx";
- FileOutputStream out = new FileOutputStream(fname);
- wb.write(out);
- out.close();
-
- wb.close();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.xssf.usermodel.examples;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.RichTextString;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Demonstrates how to work with excel cell comments.
+ *
+ *
+ * Excel comment is a kind of a text shape,
+ * so inserting a comment is very similar to placing a text box in a worksheet
+ *
+ *
+ * @author Yegor Kozlov
+ */
+public class CellComments {
+ public static void main(String[] args) throws IOException {
+ Workbook wb = new XSSFWorkbook();
+
+ CreationHelper factory = wb.getCreationHelper();
+
+ Sheet sheet = wb.createSheet();
+
+ Cell cell1 = sheet.createRow(3).createCell(5);
+ cell1.setCellValue("F4");
+
+ Drawing> drawing = sheet.createDrawingPatriarch();
+
+ ClientAnchor anchor = factory.createClientAnchor();
+
+ Comment comment1 = drawing.createCellComment(anchor);
+ RichTextString str1 = factory.createRichTextString("Hello, World!");
+ comment1.setString(str1);
+ comment1.setAuthor("Apache POI");
+ cell1.setCellComment(comment1);
+
+ Cell cell2 = sheet.createRow(2).createCell(2);
+ cell2.setCellValue("C3");
+
+ Comment comment2 = drawing.createCellComment(anchor);
+ RichTextString str2 = factory.createRichTextString("XSSF can set cell comments");
+ //apply custom font to the text in the comment
+ Font font = wb.createFont();
+ font.setFontName("Arial");
+ font.setFontHeightInPoints((short)14);
+ font.setBold(true);
+ font.setColor(IndexedColors.RED.getIndex());
+ str2.applyFont(font);
+
+ comment2.setString(str2);
+ comment2.setAuthor("Apache POI");
+ comment2.setAddress(new CellAddress("C3"));
+
+ String fname = "comments.xlsx";
+ FileOutputStream out = new FileOutputStream(fname);
+ wb.write(out);
+ out.close();
+
+ wb.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java
index 1add0d2fb3..9fb6409ae3 100644
--- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java
+++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java
@@ -1,45 +1,45 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.xssf.usermodel.examples;
-
-import org.apache.poi.openxml4j.opc.OPCPackage;
-import org.apache.poi.xssf.extractor.XSSFExportToXml;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.apache.poi.xssf.usermodel.XSSFMap;
-
-import java.io.ByteArrayOutputStream;
-
-/**
- * Print all custom XML mappings registered in the given workbook
- */
-public class CustomXMLMapping {
-
- public static void main(String[] args) throws Exception {
- OPCPackage pkg = OPCPackage.open(args[0]);
- XSSFWorkbook wb = new XSSFWorkbook(pkg);
-
- for (XSSFMap map : wb.getCustomXMLMappings()) {
- XSSFExportToXml exporter = new XSSFExportToXml(map);
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- exporter.exportToXML(os, true);
- String xml = os.toString("UTF-8");
- System.out.println(xml);
- }
- pkg.close();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.xssf.usermodel.examples;
+
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.xssf.extractor.XSSFExportToXml;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFMap;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Print all custom XML mappings registered in the given workbook
+ */
+public class CustomXMLMapping {
+
+ public static void main(String[] args) throws Exception {
+ OPCPackage pkg = OPCPackage.open(args[0]);
+ XSSFWorkbook wb = new XSSFWorkbook(pkg);
+
+ for (XSSFMap map : wb.getCustomXMLMappings()) {
+ XSSFExportToXml exporter = new XSSFExportToXml(map);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ exporter.exportToXML(os, true);
+ String xml = os.toString("UTF-8");
+ System.out.println(xml);
+ }
+ pkg.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/EmbeddedObjects.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/EmbeddedObjects.java
index 4f65e1a73b..916c2ec50d 100644
--- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/EmbeddedObjects.java
+++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/EmbeddedObjects.java
@@ -1,67 +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.poi.xssf.usermodel.examples;
-
-import java.io.Closeable;
-import java.io.InputStream;
-
-import org.apache.poi.hslf.usermodel.HSLFSlideShow;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hwpf.HWPFDocument;
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.xslf.usermodel.XMLSlideShow;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.apache.poi.xwpf.usermodel.XWPFDocument;
-
-/**
- * Demonstrates how you can extract embedded data from a .xlsx file
- */
-public class EmbeddedObjects {
- public static void main(String[] args) throws Exception {
- XSSFWorkbook workbook = new XSSFWorkbook(args[0]);
- for (PackagePart pPart : workbook.getAllEmbedds()) {
- String contentType = pPart.getContentType();
- InputStream is = pPart.getInputStream();
- Closeable document;
- if (contentType.equals("application/vnd.ms-excel")) {
- // Excel Workbook - either binary or OpenXML
- document = new HSSFWorkbook(is);
- } else if (contentType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) {
- // Excel Workbook - OpenXML file format
- document = new XSSFWorkbook(is);
- } else if (contentType.equals("application/msword")) {
- // Word Document - binary (OLE2CDF) file format
- document = new HWPFDocument(is);
- } else if (contentType.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) {
- // Word Document - OpenXML file format
- document = new XWPFDocument(is);
- } else if (contentType.equals("application/vnd.ms-powerpoint")) {
- // PowerPoint Document - binary file format
- document = new HSLFSlideShow(is);
- } else if (contentType.equals("application/vnd.openxmlformats-officedocument.presentationml.presentation")) {
- // PowerPoint Document - OpenXML file format
- document = new XMLSlideShow(is);
- } else {
- // Any other type of embedded object.
- document = is;
- }
- document.close();
- is.close();
- }
- workbook.close();
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.xssf.usermodel.examples;
+
+import java.io.Closeable;
+import java.io.InputStream;
+
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.xslf.usermodel.XMLSlideShow;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+
+/**
+ * Demonstrates how you can extract embedded data from a .xlsx file
+ */
+public class EmbeddedObjects {
+ public static void main(String[] args) throws Exception {
+ XSSFWorkbook workbook = new XSSFWorkbook(args[0]);
+ for (PackagePart pPart : workbook.getAllEmbedds()) {
+ String contentType = pPart.getContentType();
+ InputStream is = pPart.getInputStream();
+ Closeable document;
+ if (contentType.equals("application/vnd.ms-excel")) {
+ // Excel Workbook - either binary or OpenXML
+ document = new HSSFWorkbook(is);
+ } else if (contentType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) {
+ // Excel Workbook - OpenXML file format
+ document = new XSSFWorkbook(is);
+ } else if (contentType.equals("application/msword")) {
+ // Word Document - binary (OLE2CDF) file format
+ document = new HWPFDocument(is);
+ } else if (contentType.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) {
+ // Word Document - OpenXML file format
+ document = new XWPFDocument(is);
+ } else if (contentType.equals("application/vnd.ms-powerpoint")) {
+ // PowerPoint Document - binary file format
+ document = new HSLFSlideShow(is);
+ } else if (contentType.equals("application/vnd.openxmlformats-officedocument.presentationml.presentation")) {
+ // PowerPoint Document - OpenXML file format
+ document = new XMLSlideShow(is);
+ } else {
+ // Any other type of embedded object.
+ document = is;
+ }
+ document.close();
+ is.close();
+ }
+ workbook.close();
+ }
}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/HyperlinkExample.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/HyperlinkExample.java
index a84d32a193..bd86e7129c 100644
--- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/HyperlinkExample.java
+++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/HyperlinkExample.java
@@ -38,13 +38,13 @@ public class HyperlinkExample {
public static void main(String[]args) throws IOException {
Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
- CreationHelper createHelper = wb.getCreationHelper();
-
- //cell style for hyperlinks
- //by default hyperlinks are blue and underlined
- CellStyle hlink_style = wb.createCellStyle();
- Font hlink_font = wb.createFont();
- hlink_font.setUnderline(Font.U_SINGLE);
+ CreationHelper createHelper = wb.getCreationHelper();
+
+ //cell style for hyperlinks
+ //by default hyperlinks are blue and underlined
+ CellStyle hlink_style = wb.createCellStyle();
+ Font hlink_font = wb.createFont();
+ hlink_font.setUnderline(Font.U_SINGLE);
hlink_font.setColor(IndexedColors.BLUE.getIndex());
hlink_style.setFont(hlink_font);
diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/LineChart.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/LineChart.java
index d6d5e8ed08..051af64ec7 100644
--- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/LineChart.java
+++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/LineChart.java
@@ -1,93 +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.poi.xssf.usermodel.examples;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.Chart;
-import org.apache.poi.ss.usermodel.ClientAnchor;
-import org.apache.poi.ss.usermodel.Drawing;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.charts.AxisCrosses;
-import org.apache.poi.ss.usermodel.charts.AxisPosition;
-import org.apache.poi.ss.usermodel.charts.ChartAxis;
-import org.apache.poi.ss.usermodel.charts.ChartDataSource;
-import org.apache.poi.ss.usermodel.charts.ChartLegend;
-import org.apache.poi.ss.usermodel.charts.DataSources;
-import org.apache.poi.ss.usermodel.charts.LegendPosition;
-import org.apache.poi.ss.usermodel.charts.LineChartData;
-import org.apache.poi.ss.usermodel.charts.ValueAxis;
-import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-
-/**
- * Line chart example.
- */
-public class LineChart {
-
- public static void main(String[] args) throws IOException {
- Workbook wb = new XSSFWorkbook();
- Sheet sheet = wb.createSheet("linechart");
- final int NUM_OF_ROWS = 3;
- final int NUM_OF_COLUMNS = 10;
-
- // Create a row and put some cells in it. Rows are 0 based.
- Row row;
- Cell cell;
- for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
- row = sheet.createRow((short) rowIndex);
- for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
- cell = row.createCell((short) colIndex);
- cell.setCellValue(colIndex * (rowIndex + 1));
- }
- }
-
- Drawing> drawing = sheet.createDrawingPatriarch();
- ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
-
- Chart chart = drawing.createChart(anchor);
- ChartLegend legend = chart.getOrCreateLegend();
- legend.setPosition(LegendPosition.TOP_RIGHT);
-
- LineChartData data = chart.getChartDataFactory().createLineChartData();
-
- // Use a category axis for the bottom axis.
- ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
- ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
- leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
-
- ChartDataSource xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
- ChartDataSource ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
- ChartDataSource ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
-
-
- data.addSeries(xs, ys1);
- data.addSeries(xs, ys2);
-
- chart.plot(data, bottomAxis, leftAxis);
-
- // Write the output to a file
- FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx");
- wb.write(fileOut);
- fileOut.close();
- wb.close();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.xssf.usermodel.examples;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Chart;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.charts.AxisCrosses;
+import org.apache.poi.ss.usermodel.charts.AxisPosition;
+import org.apache.poi.ss.usermodel.charts.ChartAxis;
+import org.apache.poi.ss.usermodel.charts.ChartDataSource;
+import org.apache.poi.ss.usermodel.charts.ChartLegend;
+import org.apache.poi.ss.usermodel.charts.DataSources;
+import org.apache.poi.ss.usermodel.charts.LegendPosition;
+import org.apache.poi.ss.usermodel.charts.LineChartData;
+import org.apache.poi.ss.usermodel.charts.ValueAxis;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Line chart example.
+ */
+public class LineChart {
+
+ public static void main(String[] args) throws IOException {
+ Workbook wb = new XSSFWorkbook();
+ Sheet sheet = wb.createSheet("linechart");
+ final int NUM_OF_ROWS = 3;
+ final int NUM_OF_COLUMNS = 10;
+
+ // Create a row and put some cells in it. Rows are 0 based.
+ Row row;
+ Cell cell;
+ for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
+ row = sheet.createRow((short) rowIndex);
+ for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
+ cell = row.createCell((short) colIndex);
+ cell.setCellValue(colIndex * (rowIndex + 1));
+ }
+ }
+
+ Drawing> drawing = sheet.createDrawingPatriarch();
+ ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
+
+ Chart chart = drawing.createChart(anchor);
+ ChartLegend legend = chart.getOrCreateLegend();
+ legend.setPosition(LegendPosition.TOP_RIGHT);
+
+ LineChartData data = chart.getChartDataFactory().createLineChartData();
+
+ // Use a category axis for the bottom axis.
+ ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
+ ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
+ leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
+
+ ChartDataSource xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
+ ChartDataSource ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
+ ChartDataSource ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
+
+
+ data.addSeries(xs, ys1);
+ data.addSeries(xs, ys2);
+
+ chart.plot(data, bottomAxis, leftAxis);
+
+ // Write the output to a file
+ FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx");
+ wb.write(fileOut);
+ fileOut.close();
+ wb.close();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java
index 166f39cdab..251b4568f6 100644
--- a/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java
+++ b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java
@@ -1,201 +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.poi.xwpf.usermodel.examples;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-
-import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.WorkbookFactory;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.xwpf.usermodel.XWPFDocument;
-
-/**
- * Tests whether it is possible to successfully update an Excel workbook that is
- * embedded into a WordprocessingML document. Note that the test has currently
- * only been conducted with a binary Excel workbook and NOT yet with a
- * SpreadsheetML workbook embedded into the document.
- *
- * This code was successfully tested with the following file from the POI test collection:
- * http://svn.apache.org/repos/asf/poi/trunk/test-data/document/EmbeddedDocument.docx
- */
-public class UpdateEmbeddedDoc {
-
- private XWPFDocument doc = null;
- private File docFile = null;
-
- private static final int SHEET_NUM = 0;
- private static final int ROW_NUM = 0;
- private static final int CELL_NUM = 0;
- private static final double NEW_VALUE = 100.98D;
- private static final String BINARY_EXTENSION = "xls";
- private static final String OPENXML_EXTENSION = "xlsx";
-
- /**
- * Create a new instance of the UpdateEmbeddedDoc class using the following
- * parameters;
- *
- * @param filename An instance of the String class that encapsulates the name
- * of and path to a WordprocessingML Word document that contains an
- * embedded binary Excel workbook.
- * @throws java.io.FileNotFoundException Thrown if the file cannot be found
- * on the underlying file system.
- * @throws java.io.IOException Thrown if a problem occurs in the underlying
- * file system.
- */
- public UpdateEmbeddedDoc(String filename) throws FileNotFoundException, IOException {
- this.docFile = new File(filename);
- FileInputStream fis = null;
- if (!this.docFile.exists()) {
- throw new FileNotFoundException("The Word dcoument " + filename + " does not exist.");
- }
- try {
- // Open the Word document file and instantiate the XWPFDocument
- // class.
- fis = new FileInputStream(this.docFile);
- this.doc = new XWPFDocument(fis);
- } finally {
- IOUtils.closeQuietly(fis);
- }
- }
-
- /**
- * Called to update the embedded Excel workbook. As the format and structire
- * of the workbook are known in advance, all this code attempts to do is
- * write a new value into the first cell on the first row of the first
- * worksheet. Prior to executing this method, that cell will contain the
- * value 1.
- *
- * @throws org.apache.poi.openxml4j.exceptions.OpenXML4JException
- * Rather
- * than use the specific classes (HSSF/XSSF) to handle the embedded
- * workbook this method uses those defeined in the SS stream. As
- * a result, it might be the case that a SpreadsheetML file is
- * opened for processing, throwing this exception if that file is
- * invalid.
- * @throws java.io.IOException Thrown if a problem occurs in the underlying
- * file system.
- */
- public void updateEmbeddedDoc() throws OpenXML4JException, IOException {
- List embeddedDocs = this.doc.getAllEmbedds();
- for (PackagePart pPart : embeddedDocs) {
- String ext = pPart.getPartName().getExtension();
- if (BINARY_EXTENSION.equals(ext) || OPENXML_EXTENSION.equals(ext)) {
- // Get an InputStream from the package part and pass that
- // to the create method of the WorkbookFactory class. Update
- // the resulting Workbook and then stream that out again
- // using an OutputStream obtained from the same PackagePart.
- InputStream is = pPart.getInputStream();
- Workbook workbook = null;
- OutputStream os = null;
- try {
- workbook = WorkbookFactory.create(is);
- Sheet sheet = workbook.getSheetAt(SHEET_NUM);
- Row row = sheet.getRow(ROW_NUM);
- Cell cell = row.getCell(CELL_NUM);
- cell.setCellValue(NEW_VALUE);
- os = pPart.getOutputStream();
- workbook.write(os);
- } finally {
- IOUtils.closeQuietly(os);
- IOUtils.closeQuietly(workbook);
- IOUtils.closeQuietly(is);
- }
- }
- }
-
- if (!embeddedDocs.isEmpty()) {
- // Finally, write the newly modified Word document out to file.
- FileOutputStream fos = new FileOutputStream(this.docFile);
- this.doc.write(fos);
- fos.close();
- }
- }
-
- /**
- * Called to test whether or not the embedded workbook was correctly
- * updated. This method simply recovers the first cell from the first row
- * of the first workbook and tests the value it contains.
- *
- * Note that execution will not continue up to the assertion as the
- * embedded workbook is now corrupted and causes an IllegalArgumentException
- * with the following message
- *
- * java.lang.IllegalArgumentException: Your InputStream was neither an
- * OLE2 stream, nor an OOXML stream
- *
- * to be thrown when the WorkbookFactory.createWorkbook(InputStream) method
- * is executed.
- *
- * @throws org.apache.poi.openxml4j.exceptions.OpenXML4JException
- * Rather
- * than use the specific classes (HSSF/XSSF) to handle the embedded
- * workbook this method uses those defeined in the SS stream. As
- * a result, it might be the case that a SpreadsheetML file is
- * opened for processing, throwing this exception if that file is
- * invalid.
- * @throws java.io.IOException Thrown if a problem occurs in the underlying
- * file system.
- */
- public void checkUpdatedDoc() throws OpenXML4JException, IOException {
- for (PackagePart pPart : this.doc.getAllEmbedds()) {
- String ext = pPart.getPartName().getExtension();
- if (BINARY_EXTENSION.equals(ext) || OPENXML_EXTENSION.equals(ext)) {
- InputStream is = pPart.getInputStream();
- Workbook workbook = null;
- try {
- workbook = WorkbookFactory.create(is);
- Sheet sheet = workbook.getSheetAt(SHEET_NUM);
- Row row = sheet.getRow(ROW_NUM);
- Cell cell = row.getCell(CELL_NUM);
- assertEquals(cell.getNumericCellValue(), NEW_VALUE, 0.0001);
- } finally {
- IOUtils.closeQuietly(workbook);
- IOUtils.closeQuietly(is);
- }
- }
- }
- }
-
- /**
- * Code to test updating of the embedded Excel workbook.
- *
- * @param args
- * @throws OpenXML4JException
- */
- public static void main(String[] args) throws IOException, OpenXML4JException {
- UpdateEmbeddedDoc ued = new UpdateEmbeddedDoc(args[0]);
- ued.updateEmbeddedDoc();
- ued.checkUpdatedDoc();
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.xwpf.usermodel.examples;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+
+/**
+ * Tests whether it is possible to successfully update an Excel workbook that is
+ * embedded into a WordprocessingML document. Note that the test has currently
+ * only been conducted with a binary Excel workbook and NOT yet with a
+ * SpreadsheetML workbook embedded into the document.
+ *
+ * This code was successfully tested with the following file from the POI test collection:
+ * http://svn.apache.org/repos/asf/poi/trunk/test-data/document/EmbeddedDocument.docx
+ */
+public class UpdateEmbeddedDoc {
+
+ private XWPFDocument doc = null;
+ private File docFile = null;
+
+ private static final int SHEET_NUM = 0;
+ private static final int ROW_NUM = 0;
+ private static final int CELL_NUM = 0;
+ private static final double NEW_VALUE = 100.98D;
+ private static final String BINARY_EXTENSION = "xls";
+ private static final String OPENXML_EXTENSION = "xlsx";
+
+ /**
+ * Create a new instance of the UpdateEmbeddedDoc class using the following
+ * parameters;
+ *
+ * @param filename An instance of the String class that encapsulates the name
+ * of and path to a WordprocessingML Word document that contains an
+ * embedded binary Excel workbook.
+ * @throws java.io.FileNotFoundException Thrown if the file cannot be found
+ * on the underlying file system.
+ * @throws java.io.IOException Thrown if a problem occurs in the underlying
+ * file system.
+ */
+ public UpdateEmbeddedDoc(String filename) throws FileNotFoundException, IOException {
+ this.docFile = new File(filename);
+ FileInputStream fis = null;
+ if (!this.docFile.exists()) {
+ throw new FileNotFoundException("The Word dcoument " + filename + " does not exist.");
+ }
+ try {
+ // Open the Word document file and instantiate the XWPFDocument
+ // class.
+ fis = new FileInputStream(this.docFile);
+ this.doc = new XWPFDocument(fis);
+ } finally {
+ IOUtils.closeQuietly(fis);
+ }
+ }
+
+ /**
+ * Called to update the embedded Excel workbook. As the format and structire
+ * of the workbook are known in advance, all this code attempts to do is
+ * write a new value into the first cell on the first row of the first
+ * worksheet. Prior to executing this method, that cell will contain the
+ * value 1.
+ *
+ * @throws org.apache.poi.openxml4j.exceptions.OpenXML4JException
+ * Rather
+ * than use the specific classes (HSSF/XSSF) to handle the embedded
+ * workbook this method uses those defeined in the SS stream. As
+ * a result, it might be the case that a SpreadsheetML file is
+ * opened for processing, throwing this exception if that file is
+ * invalid.
+ * @throws java.io.IOException Thrown if a problem occurs in the underlying
+ * file system.
+ */
+ public void updateEmbeddedDoc() throws OpenXML4JException, IOException {
+ List embeddedDocs = this.doc.getAllEmbedds();
+ for (PackagePart pPart : embeddedDocs) {
+ String ext = pPart.getPartName().getExtension();
+ if (BINARY_EXTENSION.equals(ext) || OPENXML_EXTENSION.equals(ext)) {
+ // Get an InputStream from the package part and pass that
+ // to the create method of the WorkbookFactory class. Update
+ // the resulting Workbook and then stream that out again
+ // using an OutputStream obtained from the same PackagePart.
+ InputStream is = pPart.getInputStream();
+ Workbook workbook = null;
+ OutputStream os = null;
+ try {
+ workbook = WorkbookFactory.create(is);
+ Sheet sheet = workbook.getSheetAt(SHEET_NUM);
+ Row row = sheet.getRow(ROW_NUM);
+ Cell cell = row.getCell(CELL_NUM);
+ cell.setCellValue(NEW_VALUE);
+ os = pPart.getOutputStream();
+ workbook.write(os);
+ } finally {
+ IOUtils.closeQuietly(os);
+ IOUtils.closeQuietly(workbook);
+ IOUtils.closeQuietly(is);
+ }
+ }
+ }
+
+ if (!embeddedDocs.isEmpty()) {
+ // Finally, write the newly modified Word document out to file.
+ FileOutputStream fos = new FileOutputStream(this.docFile);
+ this.doc.write(fos);
+ fos.close();
+ }
+ }
+
+ /**
+ * Called to test whether or not the embedded workbook was correctly
+ * updated. This method simply recovers the first cell from the first row
+ * of the first workbook and tests the value it contains.
+ *
+ * Note that execution will not continue up to the assertion as the
+ * embedded workbook is now corrupted and causes an IllegalArgumentException
+ * with the following message
+ *
+ * java.lang.IllegalArgumentException: Your InputStream was neither an
+ * OLE2 stream, nor an OOXML stream
+ *
+ * to be thrown when the WorkbookFactory.createWorkbook(InputStream) method
+ * is executed.
+ *
+ * @throws org.apache.poi.openxml4j.exceptions.OpenXML4JException
+ * Rather
+ * than use the specific classes (HSSF/XSSF) to handle the embedded
+ * workbook this method uses those defeined in the SS stream. As
+ * a result, it might be the case that a SpreadsheetML file is
+ * opened for processing, throwing this exception if that file is
+ * invalid.
+ * @throws java.io.IOException Thrown if a problem occurs in the underlying
+ * file system.
+ */
+ public void checkUpdatedDoc() throws OpenXML4JException, IOException {
+ for (PackagePart pPart : this.doc.getAllEmbedds()) {
+ String ext = pPart.getPartName().getExtension();
+ if (BINARY_EXTENSION.equals(ext) || OPENXML_EXTENSION.equals(ext)) {
+ InputStream is = pPart.getInputStream();
+ Workbook workbook = null;
+ try {
+ workbook = WorkbookFactory.create(is);
+ Sheet sheet = workbook.getSheetAt(SHEET_NUM);
+ Row row = sheet.getRow(ROW_NUM);
+ Cell cell = row.getCell(CELL_NUM);
+ assertEquals(cell.getNumericCellValue(), NEW_VALUE, 0.0001);
+ } finally {
+ IOUtils.closeQuietly(workbook);
+ IOUtils.closeQuietly(is);
+ }
+ }
+ }
+ }
+
+ /**
+ * Code to test updating of the embedded Excel workbook.
+ *
+ * @param args
+ * @throws OpenXML4JException
+ */
+ public static void main(String[] args) throws IOException, OpenXML4JException {
+ UpdateEmbeddedDoc ued = new UpdateEmbeddedDoc(args[0]);
+ ued.updateEmbeddedDoc();
+ ued.checkUpdatedDoc();
+ }
+}
diff --git a/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntHandlerTask.java b/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntHandlerTask.java
index 35f5420f01..c6baee4f24 100644
--- a/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntHandlerTask.java
+++ b/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntHandlerTask.java
@@ -1,76 +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.poi.ss.excelant;
-
-import org.apache.poi.ss.excelant.util.ExcelAntWorkbookUtil;
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.Task;
-
-/**
- * This is the class that backs the tag in the Ant task.
- *
- * Its purpose is to provide a way to manipulate a workbook in the course
- * of an ExcelAnt task. The idea being to model a way for test writers to
- * simulate the behaviors of the workbook.
- *
- * Suppose, for example, you have a workbook that has a worksheet that
- * reacts to values entered or selected by the user. It's possible in
- * Excel to change other cells based on this but this isn't easily possible
- * in POI. In ExcelAnt we handle this using the Handler, which is a Java
- * class you write to manipulate the workbook.
- *
- * In order to use this tag you must write a class that implements the
- * IExcelAntWorkbookHandler interface. After writing the
- * class you should package it and it's dependencies into a jar file to
- * add as library in your Ant build file.
- *
- * @author Jon Svede ( jon [at] loquatic [dot] com )
- * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
- *
- */
-public class ExcelAntHandlerTask extends Task {
-
- private String className ;
-
- private ExcelAntWorkbookUtil wbUtil ;
-
- public void setClassName( String cName ) {
- className = cName ;
- }
-
- protected void setEAWorkbookUtil( ExcelAntWorkbookUtil wkbkUtil ) {
- wbUtil = wkbkUtil ;
- }
-
- @Override
- public void execute() throws BuildException {
- log( "handling the workbook with class " + className, Project.MSG_INFO ) ;
- try {
- Class> clazz = Class.forName( className ) ;
- Object handlerObj = clazz.newInstance() ;
- if( handlerObj instanceof IExcelAntWorkbookHandler ) {
- IExcelAntWorkbookHandler iHandler = (IExcelAntWorkbookHandler)handlerObj ;
- iHandler.setWorkbook( wbUtil.getWorkbook() ) ;
- iHandler.execute() ;
- }
- } catch( Exception e ) {
- throw new BuildException( e.getMessage(), e ) ;
- }
- }
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.excelant;
+
+import org.apache.poi.ss.excelant.util.ExcelAntWorkbookUtil;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * This is the class that backs the tag in the Ant task.
+ *
+ * Its purpose is to provide a way to manipulate a workbook in the course
+ * of an ExcelAnt task. The idea being to model a way for test writers to
+ * simulate the behaviors of the workbook.
+ *
+ * Suppose, for example, you have a workbook that has a worksheet that
+ * reacts to values entered or selected by the user. It's possible in
+ * Excel to change other cells based on this but this isn't easily possible
+ * in POI. In ExcelAnt we handle this using the Handler, which is a Java
+ * class you write to manipulate the workbook.
+ *
+ * In order to use this tag you must write a class that implements the
+ * IExcelAntWorkbookHandler interface. After writing the
+ * class you should package it and it's dependencies into a jar file to
+ * add as library in your Ant build file.
+ *
+ * @author Jon Svede ( jon [at] loquatic [dot] com )
+ * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
+ *
+ */
+public class ExcelAntHandlerTask extends Task {
+
+ private String className ;
+
+ private ExcelAntWorkbookUtil wbUtil ;
+
+ public void setClassName( String cName ) {
+ className = cName ;
+ }
+
+ protected void setEAWorkbookUtil( ExcelAntWorkbookUtil wkbkUtil ) {
+ wbUtil = wkbkUtil ;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ log( "handling the workbook with class " + className, Project.MSG_INFO ) ;
+ try {
+ Class> clazz = Class.forName( className ) ;
+ Object handlerObj = clazz.newInstance() ;
+ if( handlerObj instanceof IExcelAntWorkbookHandler ) {
+ IExcelAntWorkbookHandler iHandler = (IExcelAntWorkbookHandler)handlerObj ;
+ iHandler.setWorkbook( wbUtil.getWorkbook() ) ;
+ iHandler.execute() ;
+ }
+ } catch( Exception e ) {
+ throw new BuildException( e.getMessage(), e ) ;
+ }
+ }
+ }
diff --git a/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntSet.java b/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntSet.java
index 974b1b9e4c..e62f0623d9 100644
--- a/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntSet.java
+++ b/src/excelant/java/org/apache/poi/ss/excelant/ExcelAntSet.java
@@ -1,48 +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.poi.ss.excelant;
-
-import org.apache.poi.ss.excelant.util.ExcelAntWorkbookUtil;
-import org.apache.tools.ant.Task;
-
-/**
- *
- * @author Jon Svede ( jon [at] loquatic [dot] com )
- * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
- *
- */
-public abstract class ExcelAntSet extends Task {
-
- protected String cellStr ;
-
- protected ExcelAntWorkbookUtil wbUtil ;
-
- public void setCell( String cellName ) {
- cellStr = cellName ;
- }
-
- public String getCell() {
- return cellStr ;
- }
-
-
- public void setWorkbookUtil( ExcelAntWorkbookUtil wb ) {
- wbUtil = wb ;
- }
-
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.excelant;
+
+import org.apache.poi.ss.excelant.util.ExcelAntWorkbookUtil;
+import org.apache.tools.ant.Task;
+
+/**
+ *
+ * @author Jon Svede ( jon [at] loquatic [dot] com )
+ * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
+ *
+ */
+public abstract class ExcelAntSet extends Task {
+
+ protected String cellStr ;
+
+ protected ExcelAntWorkbookUtil wbUtil ;
+
+ public void setCell( String cellName ) {
+ cellStr = cellName ;
+ }
+
+ public String getCell() {
+ return cellStr ;
+ }
+
+
+ public void setWorkbookUtil( ExcelAntWorkbookUtil wb ) {
+ wbUtil = wb ;
+ }
+
+}
diff --git a/src/excelant/java/org/apache/poi/ss/excelant/IExcelAntWorkbookHandler.java b/src/excelant/java/org/apache/poi/ss/excelant/IExcelAntWorkbookHandler.java
index 139e34c0d7..c8db7008d6 100644
--- a/src/excelant/java/org/apache/poi/ss/excelant/IExcelAntWorkbookHandler.java
+++ b/src/excelant/java/org/apache/poi/ss/excelant/IExcelAntWorkbookHandler.java
@@ -1,42 +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.poi.ss.excelant;
-
-import org.apache.poi.ss.usermodel.Workbook;
-
-
-/**
- * In Excel there are many ways to handle manipulating a workbook based
- * on some arbitrary user action (onChange, etc). You use this interface
- * to create classes that will handle the workbook in whatever manner is needed
- * that cannot be handled by POI.
- *
- * For example, suppose that in Excel when you update a cell the workbook
- * does some calculations and updates other cells based on that change. In
- * ExcelAnt you would set the value of the cell then write your own handler
- * then call that from your Ant task after the set task.
- *
- * @author Jon Svede ( jon [at] loquatic [dot] com )
- * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
- *
- */
-public interface IExcelAntWorkbookHandler {
- public void setWorkbook( Workbook workbook ) ;
-
- public void execute() ;
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.excelant;
+
+import org.apache.poi.ss.usermodel.Workbook;
+
+
+/**
+ * In Excel there are many ways to handle manipulating a workbook based
+ * on some arbitrary user action (onChange, etc). You use this interface
+ * to create classes that will handle the workbook in whatever manner is needed
+ * that cannot be handled by POI.
+ *
+ * For example, suppose that in Excel when you update a cell the workbook
+ * does some calculations and updates other cells based on that change. In
+ * ExcelAnt you would set the value of the cell then write your own handler
+ * then call that from your Ant task after the set task.
+ *
+ * @author Jon Svede ( jon [at] loquatic [dot] com )
+ * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
+ *
+ */
+public interface IExcelAntWorkbookHandler {
+ public void setWorkbook( Workbook workbook ) ;
+
+ public void execute() ;
+}
diff --git a/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtilFactory.java b/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtilFactory.java
index 08e7fb3d98..683ac46368 100644
--- a/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtilFactory.java
+++ b/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtilFactory.java
@@ -1,60 +1,60 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.ss.excelant.util;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * This is a factory class maps file names to WorkbookUtil instances. This
- * helps ExcelAnt be more efficient when being run many times in an Ant build.
- *
- * @author Jon Svede (jon [at] loquatic [dot] com)
- * @author Brian Bush (brian [dot] bush [at] nrel [dot] gov)
- *
- */
-public final class ExcelAntWorkbookUtilFactory {
-
- private static Map workbookUtilMap;
-
- private ExcelAntWorkbookUtilFactory() {
- }
-
- /**
- * Using the fileName, check the internal map to see if an instance
- * of the WorkbookUtil exists. If not, then add an instance to the map.
- *
- * @param fileName The filename to use as key to look for the ExcelAntWorkbookUtil.
- * @return An instance of ExcelAntWorkbookUtil associated with the filename or
- * a freshly instantiated one if none did exist before.
- */
- public static ExcelAntWorkbookUtil getInstance(String fileName) {
- if(workbookUtilMap == null) {
- workbookUtilMap = new HashMap();
- }
-
- if(workbookUtilMap.containsKey(fileName)) {
- return workbookUtilMap.get(fileName);
- }
-
- ExcelAntWorkbookUtil wbu = new ExcelAntWorkbookUtil(fileName);
- workbookUtilMap.put(fileName, wbu);
- return wbu;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.excelant.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * This is a factory class maps file names to WorkbookUtil instances. This
+ * helps ExcelAnt be more efficient when being run many times in an Ant build.
+ *
+ * @author Jon Svede (jon [at] loquatic [dot] com)
+ * @author Brian Bush (brian [dot] bush [at] nrel [dot] gov)
+ *
+ */
+public final class ExcelAntWorkbookUtilFactory {
+
+ private static Map workbookUtilMap;
+
+ private ExcelAntWorkbookUtilFactory() {
+ }
+
+ /**
+ * Using the fileName, check the internal map to see if an instance
+ * of the WorkbookUtil exists. If not, then add an instance to the map.
+ *
+ * @param fileName The filename to use as key to look for the ExcelAntWorkbookUtil.
+ * @return An instance of ExcelAntWorkbookUtil associated with the filename or
+ * a freshly instantiated one if none did exist before.
+ */
+ public static ExcelAntWorkbookUtil getInstance(String fileName) {
+ if(workbookUtilMap == null) {
+ workbookUtilMap = new HashMap();
+ }
+
+ if(workbookUtilMap.containsKey(fileName)) {
+ return workbookUtilMap.get(fileName);
+ }
+
+ ExcelAntWorkbookUtil wbu = new ExcelAntWorkbookUtil(fileName);
+ workbookUtilMap.put(fileName, wbu);
+ return wbu;
+ }
+}
diff --git a/src/excelant/testcases/org/apache/poi/ss/excelant/BuildFileTest.java b/src/excelant/testcases/org/apache/poi/ss/excelant/BuildFileTest.java
index 55ea777871..ea2bb596cf 100644
--- a/src/excelant/testcases/org/apache/poi/ss/excelant/BuildFileTest.java
+++ b/src/excelant/testcases/org/apache/poi/ss/excelant/BuildFileTest.java
@@ -1,604 +1,604 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.apache.poi.ss.excelant;
-
-import java.io.File;
-import java.io.PrintStream;
-import java.net.URL;
-
-import junit.framework.TestCase;
-
-import org.apache.poi.POIDataSamples;
-import org.apache.tools.ant.BuildEvent;
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.BuildListener;
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.ProjectHelper;
-
-/**
- * A BuildFileTest is a TestCase which executes targets from an Ant buildfile
- * for testing.
- *
- * This class provides a number of utility methods for particular build file
- * tests which extend this class.
- *
- * @see
- * http://svn.apache.org/repos/asf/ant/core/trunk/src/tests/junit/org/apache/tools/ant/BuildFileTest.java
- */
-public abstract class BuildFileTest extends TestCase {
-
- protected Project project;
-
- private StringBuffer logBuffer;
- private StringBuffer fullLogBuffer;
- private StringBuffer outBuffer;
- private StringBuffer errBuffer;
- private BuildException buildException;
-
- /**
- * Default constructor for the BuildFileTest object.
- */
- public BuildFileTest() {
- super();
- }
-
- /**
- * Constructor for the BuildFileTest object.
- *
- * @param name string to pass up to TestCase constructor
- */
- public BuildFileTest(String name) {
- super(name);
- }
-
- /**
- * Automatically calls the target called "tearDown"
- * from the build file tested if it exits.
- *
- * This allows to use Ant tasks directly in the build file
- * to clean up after each test. Note that no "setUp" target
- * is automatically called, since it's trivial to have a
- * test target depend on it.
- */
- @Override
- protected void tearDown() throws Exception {
- if (project == null) {
- /*
- * Maybe the BuildFileTest was subclassed and there is
- * no initialized project. So we could avoid getting a
- * NPE.
- * If there is an initialized project getTargets() does
- * not return null as it is initialized by an empty
- * HashSet.
- */
- return;
- }
- final String tearDown = "tearDown";
- if (project.getTargets().containsKey(tearDown)) {
- project.executeTarget(tearDown);
- }
- }
-
- /**
- * run a target, expect for any build exception
- *
- * @param target target to run
- * @param cause information string to reader of report
- */
- public void expectBuildException(String target, String cause) {
- expectSpecificBuildException(target, cause, null);
- }
-
- /**
- * Assert that only the given message has been logged with a
- * priority <= INFO when running the given target.
- */
- public void expectLog(String target, String log) {
- executeTarget(target);
- String realLog = getLog();
- assertEquals(log, realLog);
- }
-
- /**
- * Assert that the given substring is in the log messages.
- */
- public void assertLogContaining(String substring) {
- String realLog = getLog();
- assertTrue("expecting log to contain \"" + substring + "\" log was \""
- + realLog + "\"",
- realLog.indexOf(substring) >= 0);
- }
-
- /**
- * Assert that the given substring is not in the log messages.
- */
- public void assertLogNotContaining(String substring) {
- String realLog = getLog();
- assertFalse("didn't expect log to contain \"" + substring + "\" log was \""
- + realLog + "\"",
- realLog.indexOf(substring) >= 0);
- }
-
- /**
- * Assert that the given substring is in the output messages.
- *
- * @since Ant1.7
- */
- public void assertOutputContaining(String substring) {
- assertOutputContaining(null, substring);
- }
-
- /**
- * Assert that the given substring is in the output messages.
- *
- * @param message Print this message if the test fails. Defaults to
- * a meaningful text if null is passed.
- * @since Ant1.7
- */
- public void assertOutputContaining(String message, String substring) {
- String realOutput = getOutput();
- String realMessage = (message != null)
- ? message
- : "expecting output to contain \"" + substring + "\" output was \"" + realOutput + "\"";
- assertTrue(realMessage, realOutput.indexOf(substring) >= 0);
- }
-
- /**
- * Assert that the given substring is not in the output messages.
- *
- * @param message Print this message if the test fails. Defaults to
- * a meaningful text if null is passed.
- * @since Ant1.7
- */
- public void assertOutputNotContaining(String message, String substring) {
- String realOutput = getOutput();
- String realMessage = (message != null)
- ? message
- : "expecting output to not contain \"" + substring + "\" output was \"" + realOutput + "\"";
- assertFalse(realMessage, realOutput.indexOf(substring) >= 0);
- }
-
- /**
- * Assert that the given message has been logged with a priority <= INFO when running the
- * given target.
- */
- public void expectLogContaining(String target, String log) {
- executeTarget(target);
- assertLogContaining(log);
- }
-
- /**
- * Assert that the given message has not been logged with a
- * priority <= INFO when running the given target.
- */
- public void expectLogNotContaining(String target, String log) {
- executeTarget(target);
- assertLogNotContaining(log);
- }
-
- /**
- * Gets the log the BuildFileTest object.
- * Only valid if configureProject() has been called.
- *
- * @return The log value
- * @pre logBuffer!=null
- */
- public String getLog() {
- return logBuffer.toString();
- }
-
- /**
- * Assert that the given message has been logged with a priority
- * >= VERBOSE when running the given target.
- */
- public void expectDebuglog(String target, String log) {
- executeTarget(target);
- String realLog = getFullLog();
- assertEquals(log, realLog);
- }
-
- /**
- * Assert that the given substring is in the log messages.
- */
- public void assertDebuglogContaining(String substring) {
- String realLog = getFullLog();
- assertTrue("expecting debug log to contain \"" + substring
- + "\" log was \""
- + realLog + "\"",
- realLog.indexOf(substring) >= 0);
- }
-
- /**
- * Gets the log the BuildFileTest object.
- *
- * Only valid if configureProject() has been called.
- *
- * @return The log value
- * @pre fullLogBuffer!=null
- */
- public String getFullLog() {
- return fullLogBuffer.toString();
- }
-
- /**
- * execute the target, verify output matches expectations
- *
- * @param target target to execute
- * @param output output to look for
- */
- public void expectOutput(String target, String output) {
- executeTarget(target);
- String realOutput = getOutput();
- assertEquals(output, realOutput.trim());
- }
-
- /**
- * Executes the target, verify output matches expectations
- * and that we got the named error at the end
- *
- * @param target target to execute
- * @param output output to look for
- * @param error Description of Parameter
- */
- public void expectOutputAndError(String target, String output, String error) {
- executeTarget(target);
- String realOutput = getOutput();
- assertEquals(output, realOutput);
- String realError = getError();
- assertEquals(error, realError);
- }
-
- public String getOutput() {
- return cleanBuffer(outBuffer);
- }
-
- public String getError() {
- return cleanBuffer(errBuffer);
- }
-
- public BuildException getBuildException() {
- return buildException;
- }
-
- private String cleanBuffer(StringBuffer buffer) {
- StringBuffer cleanedBuffer = new StringBuffer();
- for (int i = 0; i < buffer.length(); i++) {
- char ch = buffer.charAt(i);
- if (ch != '\r') {
- cleanedBuffer.append(ch);
- }
- }
- return cleanedBuffer.toString();
- }
-
- /**
- * Sets up to run the named project
- *
- * @param filename name of project file to run
- */
- public void configureProject(String filename) throws BuildException {
- configureProject(filename, Project.MSG_DEBUG);
- }
-
- /**
- * Sets up to run the named project
- *
- * @param filename name of project file to run
- */
- public void configureProject(String filename, int logLevel)
- throws BuildException {
- logBuffer = new StringBuffer();
- fullLogBuffer = new StringBuffer();
- project = new Project();
- project.init();
- project.setNewProperty("data.dir.name", getDataDir());
- File antFile = new File(System.getProperty("root"), filename);
- project.setUserProperty("ant.file", antFile.getAbsolutePath());
- project.addBuildListener(new AntTestListener(logLevel));
- ProjectHelper.configureProject(project, antFile);
- }
-
- /**
- * Executes a target we have set up
- *
- * @param targetName target to run
- * @pre configureProject has been called
- */
- public void executeTarget(String targetName) {
- PrintStream sysOut = System.out;
- PrintStream sysErr = System.err;
- try {
- sysOut.flush();
- sysErr.flush();
- outBuffer = new StringBuffer();
- PrintStream out = new PrintStream(new AntOutputStream(outBuffer));
- System.setOut(out);
- errBuffer = new StringBuffer();
- PrintStream err = new PrintStream(new AntOutputStream(errBuffer));
- System.setErr(err);
- logBuffer = new StringBuffer();
- fullLogBuffer = new StringBuffer();
- buildException = null;
- project.executeTarget(targetName);
- } finally {
- System.setOut(sysOut);
- System.setErr(sysErr);
- }
-
- }
-
- /**
- * Get the project which has been configured for a test.
- *
- * @return the Project instance for this test.
- */
- public Project getProject() {
- return project;
- }
-
- /**
- * Gets the directory of the project.
- *
- * @return the base dir of the project
- */
- public File getProjectDir() {
- return project.getBaseDir();
- }
-
- /**
- * Runs a target, wait for a build exception.
- *
- * @param target target to run
- * @param cause information string to reader of report
- * @param msg the message value of the build exception we are waiting
- * for set to null for any build exception to be valid
- */
- public void expectSpecificBuildException(String target, String cause, String msg) {
- try {
- executeTarget(target);
- } catch (org.apache.tools.ant.BuildException ex) {
- buildException = ex;
- if ((null != msg) && (!ex.getMessage().equals(msg))) {
- fail("Should throw BuildException because '" + cause
- + "' with message '" + msg
- + "' (actual message '" + ex.getMessage() + "' instead)");
- }
- return;
- }
- fail("Should throw BuildException because: " + cause);
- }
-
- /**
- * run a target, expect an exception string
- * containing the substring we look for (case sensitive match)
- *
- * @param target target to run
- * @param cause information string to reader of report
- * @param contains substring of the build exception to look for
- */
- public void expectBuildExceptionContaining(String target, String cause, String contains) {
- try {
- executeTarget(target);
- } catch (org.apache.tools.ant.BuildException ex) {
- buildException = ex;
- if ((null != contains) && (ex.getMessage().indexOf(contains) == -1)) {
- fail("Should throw BuildException because '" + cause + "' with message containing '" + contains + "' (actual message '" + ex.getMessage() + "' instead)");
- }
- return;
- }
- fail("Should throw BuildException because: " + cause);
- }
-
- /**
- * call a target, verify property is as expected
- *
- * @param target build file target
- * @param property property name
- * @param value expected value
- */
- public void expectPropertySet(String target, String property, String value) {
- executeTarget(target);
- assertPropertyEquals(property, value);
- }
-
- /**
- * assert that a property equals a value; comparison is case sensitive.
- *
- * @param property property name
- * @param value expected value
- */
- public void assertPropertyEquals(String property, String value) {
- String result = project.getProperty(property);
- assertEquals("property " + property, value, result);
- }
-
- /**
- * assert that a property equals "true".
- *
- * @param property property name
- */
- public void assertPropertySet(String property) {
- assertPropertyEquals(property, "true");
- }
-
- /**
- * assert that a property is null.
- *
- * @param property property name
- */
- public void assertPropertyUnset(String property) {
- String result = project.getProperty(property);
- if (result != null) {
- fail("Expected property " + property
- + " to be unset, but it is set to the value: " + result);
- }
- }
-
- /**
- * call a target, verify named property is "true".
- *
- * @param target build file target
- * @param property property name
- */
- public void expectPropertySet(String target, String property) {
- expectPropertySet(target, property, "true");
- }
-
- /**
- * Call a target, verify property is null.
- *
- * @param target build file target
- * @param property property name
- */
- public void expectPropertyUnset(String target, String property) {
- expectPropertySet(target, property, null);
- }
-
- /**
- * Retrieve a resource from the caller classloader to avoid
- * assuming a vm working directory. The resource path must be
- * relative to the package name or absolute from the root path.
- *
- * @param resource the resource to retrieve its url.
- * @throws junit.framework.AssertionFailedError
- * if the resource is not found.
- */
- public URL getResource(String resource) {
- URL url = getClass().getResource(resource);
- assertNotNull("Could not find resource :" + resource, url);
- return url;
- }
-
- public static String getDataDir() {
- String dataDirName = System.getProperty(POIDataSamples.TEST_PROPERTY);
- return dataDirName == null ? "test-data" : dataDirName;
- }
-
- /**
- * an output stream which saves stuff to our buffer.
- */
- protected static class AntOutputStream extends java.io.OutputStream {
- private StringBuffer buffer;
-
- public AntOutputStream(StringBuffer buffer) {
- this.buffer = buffer;
- }
-
- @Override
- public void write(int b) {
- buffer.append((char) b);
- }
- }
-
- /**
- * Our own personal build listener.
- */
- private class AntTestListener implements BuildListener {
- private int logLevel;
-
- /**
- * Constructs a test listener which will ignore log events
- * above the given level.
- */
- public AntTestListener(int logLevel) {
- this.logLevel = logLevel;
- }
-
- /**
- * Fired before any targets are started.
- */
- @Override
- public void buildStarted(BuildEvent event) {
- }
-
- /**
- * Fired after the last target has finished. This event
- * will still be thrown if an error occurred during the build.
- *
- * @see BuildEvent#getException()
- */
- @Override
- public void buildFinished(BuildEvent event) {
- }
-
- /**
- * Fired when a target is started.
- *
- * @see BuildEvent#getTarget()
- */
- @Override
- public void targetStarted(BuildEvent event) {
- //System.out.println("targetStarted " + event.getTarget().getName());
- }
-
- /**
- * Fired when a target has finished. This event will
- * still be thrown if an error occurred during the build.
- *
- * @see BuildEvent#getException()
- */
- @Override
- public void targetFinished(BuildEvent event) {
- //System.out.println("targetFinished " + event.getTarget().getName());
- }
-
- /**
- * Fired when a task is started.
- *
- * @see BuildEvent#getTask()
- */
- @Override
- public void taskStarted(BuildEvent event) {
- //System.out.println("taskStarted " + event.getTask().getTaskName());
- }
-
- /**
- * Fired when a task has finished. This event will still
- * be throw if an error occurred during the build.
- *
- * @see BuildEvent#getException()
- */
- @Override
- public void taskFinished(BuildEvent event) {
- //System.out.println("taskFinished " + event.getTask().getTaskName());
- }
-
- /**
- * Fired whenever a message is logged.
- *
- * @see BuildEvent#getMessage()
- * @see BuildEvent#getPriority()
- */
- @Override
- public void messageLogged(BuildEvent event) {
- if (event.getPriority() > logLevel) {
- // ignore event
- return;
- }
-
- if (event.getPriority() == Project.MSG_INFO ||
- event.getPriority() == Project.MSG_WARN ||
- event.getPriority() == Project.MSG_ERR) {
- logBuffer.append(event.getMessage());
- }
- fullLogBuffer.append(event.getMessage());
- }
- }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.poi.ss.excelant;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.POIDataSamples;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+
+/**
+ * A BuildFileTest is a TestCase which executes targets from an Ant buildfile
+ * for testing.
+ *
+ * This class provides a number of utility methods for particular build file
+ * tests which extend this class.
+ *
+ * @see
+ * http://svn.apache.org/repos/asf/ant/core/trunk/src/tests/junit/org/apache/tools/ant/BuildFileTest.java
+ */
+public abstract class BuildFileTest extends TestCase {
+
+ protected Project project;
+
+ private StringBuffer logBuffer;
+ private StringBuffer fullLogBuffer;
+ private StringBuffer outBuffer;
+ private StringBuffer errBuffer;
+ private BuildException buildException;
+
+ /**
+ * Default constructor for the BuildFileTest object.
+ */
+ public BuildFileTest() {
+ super();
+ }
+
+ /**
+ * Constructor for the BuildFileTest object.
+ *
+ * @param name string to pass up to TestCase constructor
+ */
+ public BuildFileTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Automatically calls the target called "tearDown"
+ * from the build file tested if it exits.
+ *
+ * This allows to use Ant tasks directly in the build file
+ * to clean up after each test. Note that no "setUp" target
+ * is automatically called, since it's trivial to have a
+ * test target depend on it.
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ if (project == null) {
+ /*
+ * Maybe the BuildFileTest was subclassed and there is
+ * no initialized project. So we could avoid getting a
+ * NPE.
+ * If there is an initialized project getTargets() does
+ * not return null as it is initialized by an empty
+ * HashSet.
+ */
+ return;
+ }
+ final String tearDown = "tearDown";
+ if (project.getTargets().containsKey(tearDown)) {
+ project.executeTarget(tearDown);
+ }
+ }
+
+ /**
+ * run a target, expect for any build exception
+ *
+ * @param target target to run
+ * @param cause information string to reader of report
+ */
+ public void expectBuildException(String target, String cause) {
+ expectSpecificBuildException(target, cause, null);
+ }
+
+ /**
+ * Assert that only the given message has been logged with a
+ * priority <= INFO when running the given target.
+ */
+ public void expectLog(String target, String log) {
+ executeTarget(target);
+ String realLog = getLog();
+ assertEquals(log, realLog);
+ }
+
+ /**
+ * Assert that the given substring is in the log messages.
+ */
+ public void assertLogContaining(String substring) {
+ String realLog = getLog();
+ assertTrue("expecting log to contain \"" + substring + "\" log was \""
+ + realLog + "\"",
+ realLog.indexOf(substring) >= 0);
+ }
+
+ /**
+ * Assert that the given substring is not in the log messages.
+ */
+ public void assertLogNotContaining(String substring) {
+ String realLog = getLog();
+ assertFalse("didn't expect log to contain \"" + substring + "\" log was \""
+ + realLog + "\"",
+ realLog.indexOf(substring) >= 0);
+ }
+
+ /**
+ * Assert that the given substring is in the output messages.
+ *
+ * @since Ant1.7
+ */
+ public void assertOutputContaining(String substring) {
+ assertOutputContaining(null, substring);
+ }
+
+ /**
+ * Assert that the given substring is in the output messages.
+ *
+ * @param message Print this message if the test fails. Defaults to
+ * a meaningful text if null is passed.
+ * @since Ant1.7
+ */
+ public void assertOutputContaining(String message, String substring) {
+ String realOutput = getOutput();
+ String realMessage = (message != null)
+ ? message
+ : "expecting output to contain \"" + substring + "\" output was \"" + realOutput + "\"";
+ assertTrue(realMessage, realOutput.indexOf(substring) >= 0);
+ }
+
+ /**
+ * Assert that the given substring is not in the output messages.
+ *
+ * @param message Print this message if the test fails. Defaults to
+ * a meaningful text if null is passed.
+ * @since Ant1.7
+ */
+ public void assertOutputNotContaining(String message, String substring) {
+ String realOutput = getOutput();
+ String realMessage = (message != null)
+ ? message
+ : "expecting output to not contain \"" + substring + "\" output was \"" + realOutput + "\"";
+ assertFalse(realMessage, realOutput.indexOf(substring) >= 0);
+ }
+
+ /**
+ * Assert that the given message has been logged with a priority <= INFO when running the
+ * given target.
+ */
+ public void expectLogContaining(String target, String log) {
+ executeTarget(target);
+ assertLogContaining(log);
+ }
+
+ /**
+ * Assert that the given message has not been logged with a
+ * priority <= INFO when running the given target.
+ */
+ public void expectLogNotContaining(String target, String log) {
+ executeTarget(target);
+ assertLogNotContaining(log);
+ }
+
+ /**
+ * Gets the log the BuildFileTest object.
+ * Only valid if configureProject() has been called.
+ *
+ * @return The log value
+ * @pre logBuffer!=null
+ */
+ public String getLog() {
+ return logBuffer.toString();
+ }
+
+ /**
+ * Assert that the given message has been logged with a priority
+ * >= VERBOSE when running the given target.
+ */
+ public void expectDebuglog(String target, String log) {
+ executeTarget(target);
+ String realLog = getFullLog();
+ assertEquals(log, realLog);
+ }
+
+ /**
+ * Assert that the given substring is in the log messages.
+ */
+ public void assertDebuglogContaining(String substring) {
+ String realLog = getFullLog();
+ assertTrue("expecting debug log to contain \"" + substring
+ + "\" log was \""
+ + realLog + "\"",
+ realLog.indexOf(substring) >= 0);
+ }
+
+ /**
+ * Gets the log the BuildFileTest object.
+ *
+ * Only valid if configureProject() has been called.
+ *
+ * @return The log value
+ * @pre fullLogBuffer!=null
+ */
+ public String getFullLog() {
+ return fullLogBuffer.toString();
+ }
+
+ /**
+ * execute the target, verify output matches expectations
+ *
+ * @param target target to execute
+ * @param output output to look for
+ */
+ public void expectOutput(String target, String output) {
+ executeTarget(target);
+ String realOutput = getOutput();
+ assertEquals(output, realOutput.trim());
+ }
+
+ /**
+ * Executes the target, verify output matches expectations
+ * and that we got the named error at the end
+ *
+ * @param target target to execute
+ * @param output output to look for
+ * @param error Description of Parameter
+ */
+ public void expectOutputAndError(String target, String output, String error) {
+ executeTarget(target);
+ String realOutput = getOutput();
+ assertEquals(output, realOutput);
+ String realError = getError();
+ assertEquals(error, realError);
+ }
+
+ public String getOutput() {
+ return cleanBuffer(outBuffer);
+ }
+
+ public String getError() {
+ return cleanBuffer(errBuffer);
+ }
+
+ public BuildException getBuildException() {
+ return buildException;
+ }
+
+ private String cleanBuffer(StringBuffer buffer) {
+ StringBuffer cleanedBuffer = new StringBuffer();
+ for (int i = 0; i < buffer.length(); i++) {
+ char ch = buffer.charAt(i);
+ if (ch != '\r') {
+ cleanedBuffer.append(ch);
+ }
+ }
+ return cleanedBuffer.toString();
+ }
+
+ /**
+ * Sets up to run the named project
+ *
+ * @param filename name of project file to run
+ */
+ public void configureProject(String filename) throws BuildException {
+ configureProject(filename, Project.MSG_DEBUG);
+ }
+
+ /**
+ * Sets up to run the named project
+ *
+ * @param filename name of project file to run
+ */
+ public void configureProject(String filename, int logLevel)
+ throws BuildException {
+ logBuffer = new StringBuffer();
+ fullLogBuffer = new StringBuffer();
+ project = new Project();
+ project.init();
+ project.setNewProperty("data.dir.name", getDataDir());
+ File antFile = new File(System.getProperty("root"), filename);
+ project.setUserProperty("ant.file", antFile.getAbsolutePath());
+ project.addBuildListener(new AntTestListener(logLevel));
+ ProjectHelper.configureProject(project, antFile);
+ }
+
+ /**
+ * Executes a target we have set up
+ *
+ * @param targetName target to run
+ * @pre configureProject has been called
+ */
+ public void executeTarget(String targetName) {
+ PrintStream sysOut = System.out;
+ PrintStream sysErr = System.err;
+ try {
+ sysOut.flush();
+ sysErr.flush();
+ outBuffer = new StringBuffer();
+ PrintStream out = new PrintStream(new AntOutputStream(outBuffer));
+ System.setOut(out);
+ errBuffer = new StringBuffer();
+ PrintStream err = new PrintStream(new AntOutputStream(errBuffer));
+ System.setErr(err);
+ logBuffer = new StringBuffer();
+ fullLogBuffer = new StringBuffer();
+ buildException = null;
+ project.executeTarget(targetName);
+ } finally {
+ System.setOut(sysOut);
+ System.setErr(sysErr);
+ }
+
+ }
+
+ /**
+ * Get the project which has been configured for a test.
+ *
+ * @return the Project instance for this test.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Gets the directory of the project.
+ *
+ * @return the base dir of the project
+ */
+ public File getProjectDir() {
+ return project.getBaseDir();
+ }
+
+ /**
+ * Runs a target, wait for a build exception.
+ *
+ * @param target target to run
+ * @param cause information string to reader of report
+ * @param msg the message value of the build exception we are waiting
+ * for set to null for any build exception to be valid
+ */
+ public void expectSpecificBuildException(String target, String cause, String msg) {
+ try {
+ executeTarget(target);
+ } catch (org.apache.tools.ant.BuildException ex) {
+ buildException = ex;
+ if ((null != msg) && (!ex.getMessage().equals(msg))) {
+ fail("Should throw BuildException because '" + cause
+ + "' with message '" + msg
+ + "' (actual message '" + ex.getMessage() + "' instead)");
+ }
+ return;
+ }
+ fail("Should throw BuildException because: " + cause);
+ }
+
+ /**
+ * run a target, expect an exception string
+ * containing the substring we look for (case sensitive match)
+ *
+ * @param target target to run
+ * @param cause information string to reader of report
+ * @param contains substring of the build exception to look for
+ */
+ public void expectBuildExceptionContaining(String target, String cause, String contains) {
+ try {
+ executeTarget(target);
+ } catch (org.apache.tools.ant.BuildException ex) {
+ buildException = ex;
+ if ((null != contains) && (ex.getMessage().indexOf(contains) == -1)) {
+ fail("Should throw BuildException because '" + cause + "' with message containing '" + contains + "' (actual message '" + ex.getMessage() + "' instead)");
+ }
+ return;
+ }
+ fail("Should throw BuildException because: " + cause);
+ }
+
+ /**
+ * call a target, verify property is as expected
+ *
+ * @param target build file target
+ * @param property property name
+ * @param value expected value
+ */
+ public void expectPropertySet(String target, String property, String value) {
+ executeTarget(target);
+ assertPropertyEquals(property, value);
+ }
+
+ /**
+ * assert that a property equals a value; comparison is case sensitive.
+ *
+ * @param property property name
+ * @param value expected value
+ */
+ public void assertPropertyEquals(String property, String value) {
+ String result = project.getProperty(property);
+ assertEquals("property " + property, value, result);
+ }
+
+ /**
+ * assert that a property equals "true".
+ *
+ * @param property property name
+ */
+ public void assertPropertySet(String property) {
+ assertPropertyEquals(property, "true");
+ }
+
+ /**
+ * assert that a property is null.
+ *
+ * @param property property name
+ */
+ public void assertPropertyUnset(String property) {
+ String result = project.getProperty(property);
+ if (result != null) {
+ fail("Expected property " + property
+ + " to be unset, but it is set to the value: " + result);
+ }
+ }
+
+ /**
+ * call a target, verify named property is "true".
+ *
+ * @param target build file target
+ * @param property property name
+ */
+ public void expectPropertySet(String target, String property) {
+ expectPropertySet(target, property, "true");
+ }
+
+ /**
+ * Call a target, verify property is null.
+ *
+ * @param target build file target
+ * @param property property name
+ */
+ public void expectPropertyUnset(String target, String property) {
+ expectPropertySet(target, property, null);
+ }
+
+ /**
+ * Retrieve a resource from the caller classloader to avoid
+ * assuming a vm working directory. The resource path must be
+ * relative to the package name or absolute from the root path.
+ *
+ * @param resource the resource to retrieve its url.
+ * @throws junit.framework.AssertionFailedError
+ * if the resource is not found.
+ */
+ public URL getResource(String resource) {
+ URL url = getClass().getResource(resource);
+ assertNotNull("Could not find resource :" + resource, url);
+ return url;
+ }
+
+ public static String getDataDir() {
+ String dataDirName = System.getProperty(POIDataSamples.TEST_PROPERTY);
+ return dataDirName == null ? "test-data" : dataDirName;
+ }
+
+ /**
+ * an output stream which saves stuff to our buffer.
+ */
+ protected static class AntOutputStream extends java.io.OutputStream {
+ private StringBuffer buffer;
+
+ public AntOutputStream(StringBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ @Override
+ public void write(int b) {
+ buffer.append((char) b);
+ }
+ }
+
+ /**
+ * Our own personal build listener.
+ */
+ private class AntTestListener implements BuildListener {
+ private int logLevel;
+
+ /**
+ * Constructs a test listener which will ignore log events
+ * above the given level.
+ */
+ public AntTestListener(int logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ /**
+ * Fired before any targets are started.
+ */
+ @Override
+ public void buildStarted(BuildEvent event) {
+ }
+
+ /**
+ * Fired after the last target has finished. This event
+ * will still be thrown if an error occurred during the build.
+ *
+ * @see BuildEvent#getException()
+ */
+ @Override
+ public void buildFinished(BuildEvent event) {
+ }
+
+ /**
+ * Fired when a target is started.
+ *
+ * @see BuildEvent#getTarget()
+ */
+ @Override
+ public void targetStarted(BuildEvent event) {
+ //System.out.println("targetStarted " + event.getTarget().getName());
+ }
+
+ /**
+ * Fired when a target has finished. This event will
+ * still be thrown if an error occurred during the build.
+ *
+ * @see BuildEvent#getException()
+ */
+ @Override
+ public void targetFinished(BuildEvent event) {
+ //System.out.println("targetFinished " + event.getTarget().getName());
+ }
+
+ /**
+ * Fired when a task is started.
+ *
+ * @see BuildEvent#getTask()
+ */
+ @Override
+ public void taskStarted(BuildEvent event) {
+ //System.out.println("taskStarted " + event.getTask().getTaskName());
+ }
+
+ /**
+ * Fired when a task has finished. This event will still
+ * be throw if an error occurred during the build.
+ *
+ * @see BuildEvent#getException()
+ */
+ @Override
+ public void taskFinished(BuildEvent event) {
+ //System.out.println("taskFinished " + event.getTask().getTaskName());
+ }
+
+ /**
+ * Fired whenever a message is logged.
+ *
+ * @see BuildEvent#getMessage()
+ * @see BuildEvent#getPriority()
+ */
+ @Override
+ public void messageLogged(BuildEvent event) {
+ if (event.getPriority() > logLevel) {
+ // ignore event
+ return;
+ }
+
+ if (event.getPriority() == Project.MSG_INFO ||
+ event.getPriority() == Project.MSG_WARN ||
+ event.getPriority() == Project.MSG_ERR) {
+ logBuffer.append(event.getMessage());
+ }
+ fullLogBuffer.append(event.getMessage());
+ }
+ }
+
+}
diff --git a/src/excelant/testcases/org/apache/poi/ss/excelant/TestBuildFile.java b/src/excelant/testcases/org/apache/poi/ss/excelant/TestBuildFile.java
index df1993be0b..7d55c52767 100644
--- a/src/excelant/testcases/org/apache/poi/ss/excelant/TestBuildFile.java
+++ b/src/excelant/testcases/org/apache/poi/ss/excelant/TestBuildFile.java
@@ -1,119 +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.poi.ss.excelant;
-
-
-/**
- * JUnit test for the ExcelAnt tasks.
- * Leverages Ant's test framework.
- *
- * @see
- * http://svn.apache.org/repos/asf/ant/core/trunk/src/tests/junit/org/apache/tools/ant/BuildFileTest.java
- */
-public class TestBuildFile extends BuildFileTest {
-
- @Override
- public void setUp() {
- configureProject(BuildFileTest.getDataDir() + "/../src/excelant/testcases/org/apache/poi/ss/excelant/tests.xml");
- }
-
- public void testMissingFilename() {
- expectSpecificBuildException("test-nofile", "required argument not specified",
- "fileName attribute must be set!");
- }
-
- public void testFileNotFound() {
- expectSpecificBuildException("test-filenotfound", "required argument not specified",
- "Cannot load file invalid.xls. Make sure the path and file permissions are correct.");
- }
-
- public void testEvaluate() {
- executeTarget("test-evaluate");
- assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
- }
-
- public void testEvaluateNoDetails() {
- executeTarget("test-evaluate-nodetails");
- assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogNotContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
- }
-
- public void testPrecision() {
- executeTarget("test-precision");
-
- assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4. " +
- "It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-4");
- assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4. " +
- "It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-5");
- assertLogContaining("Failed to evaluate cell 'MortgageCalculator'!$B$4. " +
- "It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-10 was expected.");
- assertLogContaining("2/3 tests passed");
- }
-
- public void testPrecisionFail() {
- expectSpecificBuildException("test-precision-fails", "precision not matched",
- "\tFailed to evaluate cell 'MortgageCalculator'!$B$4. It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-10 was expected.");
- }
-
- public void testPassOnError() {
- executeTarget("test-passonerror");
- }
-
- public void testFailOnError() {
- expectBuildException("test-failonerror", "fail on error");
- assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogNotContaining("failed because 1 of 0 evaluations failed to evaluate correctly. Failed to evaluate cell 'MortageCalculatorFunction'!$D$3");
- }
-
- public void testFailOnErrorNoDetails() {
- expectBuildException("test-failonerror-nodetails", "fail on error");
- assertLogNotContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogNotContaining("failed because 1 of 0 evaluations failed to evaluate correctly. Failed to evaluate cell 'MortageCalculatorFunction'!$D$3");
- }
-
- public void testUdf() {
- executeTarget("test-udf");
- assertLogContaining("1/1 tests passed");
- }
-
- public void testSetText() {
- executeTarget("test-settext");
- assertLogContaining("1/1 tests passed");
- }
-
- public void testAddHandler() {
- executeTarget("test-addhandler");
- assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
-
- assertNotNull("The workbook should have been passed to the handler", MockExcelAntWorkbookHandler.workbook);
- assertTrue("The handler should have been executed", MockExcelAntWorkbookHandler.executed);
- }
-
- public void testAddHandlerWrongClass() {
- executeTarget("test-addhandler-wrongclass");
- assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
- assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
- }
-
- public void testAddHandlerFails() {
- expectSpecificBuildException("test-addhandler-fails", "NullPointException", null);
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.poi.ss.excelant;
+
+
+/**
+ * JUnit test for the ExcelAnt tasks.
+ * Leverages Ant's test framework.
+ *
+ * @see
+ * http://svn.apache.org/repos/asf/ant/core/trunk/src/tests/junit/org/apache/tools/ant/BuildFileTest.java
+ */
+public class TestBuildFile extends BuildFileTest {
+
+ @Override
+ public void setUp() {
+ configureProject(BuildFileTest.getDataDir() + "/../src/excelant/testcases/org/apache/poi/ss/excelant/tests.xml");
+ }
+
+ public void testMissingFilename() {
+ expectSpecificBuildException("test-nofile", "required argument not specified",
+ "fileName attribute must be set!");
+ }
+
+ public void testFileNotFound() {
+ expectSpecificBuildException("test-filenotfound", "required argument not specified",
+ "Cannot load file invalid.xls. Make sure the path and file permissions are correct.");
+ }
+
+ public void testEvaluate() {
+ executeTarget("test-evaluate");
+ assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
+ }
+
+ public void testEvaluateNoDetails() {
+ executeTarget("test-evaluate-nodetails");
+ assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogNotContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
+ }
+
+ public void testPrecision() {
+ executeTarget("test-precision");
+
+ assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4. " +
+ "It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-4");
+ assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4. " +
+ "It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-5");
+ assertLogContaining("Failed to evaluate cell 'MortgageCalculator'!$B$4. " +
+ "It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-10 was expected.");
+ assertLogContaining("2/3 tests passed");
+ }
+
+ public void testPrecisionFail() {
+ expectSpecificBuildException("test-precision-fails", "precision not matched",
+ "\tFailed to evaluate cell 'MortgageCalculator'!$B$4. It evaluated to 2285.5761494145563 when the value of 2285.576149 with precision of 1.0E-10 was expected.");
+ }
+
+ public void testPassOnError() {
+ executeTarget("test-passonerror");
+ }
+
+ public void testFailOnError() {
+ expectBuildException("test-failonerror", "fail on error");
+ assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogNotContaining("failed because 1 of 0 evaluations failed to evaluate correctly. Failed to evaluate cell 'MortageCalculatorFunction'!$D$3");
+ }
+
+ public void testFailOnErrorNoDetails() {
+ expectBuildException("test-failonerror-nodetails", "fail on error");
+ assertLogNotContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogNotContaining("failed because 1 of 0 evaluations failed to evaluate correctly. Failed to evaluate cell 'MortageCalculatorFunction'!$D$3");
+ }
+
+ public void testUdf() {
+ executeTarget("test-udf");
+ assertLogContaining("1/1 tests passed");
+ }
+
+ public void testSetText() {
+ executeTarget("test-settext");
+ assertLogContaining("1/1 tests passed");
+ }
+
+ public void testAddHandler() {
+ executeTarget("test-addhandler");
+ assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
+
+ assertNotNull("The workbook should have been passed to the handler", MockExcelAntWorkbookHandler.workbook);
+ assertTrue("The handler should have been executed", MockExcelAntWorkbookHandler.executed);
+ }
+
+ public void testAddHandlerWrongClass() {
+ executeTarget("test-addhandler-wrongclass");
+ assertLogContaining("Using input file: " + BuildFileTest.getDataDir() + "/spreadsheet/excelant.xls");
+ assertLogContaining("Succeeded when evaluating 'MortgageCalculator'!$B$4.");
+ }
+
+ public void testAddHandlerFails() {
+ expectSpecificBuildException("test-addhandler-fails", "NullPointException", null);
+ }
+}
diff --git a/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java b/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java
index 6bfeee2e71..9b49cf7718 100644
--- a/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java
+++ b/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java
@@ -1,142 +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.poi.stress;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.poi.POIOLE2TextExtractor;
-import org.apache.poi.POITextExtractor;
-import org.apache.poi.extractor.ExtractorFactory;
-import org.apache.poi.hpsf.extractor.HPSFPropertiesExtractor;
-import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
-import org.apache.xmlbeans.XmlException;
-
-public abstract class AbstractFileHandler implements FileHandler {
- public static final Set EXPECTED_EXTRACTOR_FAILURES = new HashSet();
- static {
- // password protected files
- EXPECTED_EXTRACTOR_FAILURES.add("document/bug53475-password-is-pass.docx");
- EXPECTED_EXTRACTOR_FAILURES.add("poifs/extenxls_pwd123.xlsx");
- EXPECTED_EXTRACTOR_FAILURES.add("poifs/protect.xlsx");
- EXPECTED_EXTRACTOR_FAILURES.add("poifs/protected_agile.docx");
- EXPECTED_EXTRACTOR_FAILURES.add("poifs/protected_sha512.xlsx");
-
- // unsupported file-types, no supported OLE2 parts
- EXPECTED_EXTRACTOR_FAILURES.add("hmef/quick-winmail.dat");
- EXPECTED_EXTRACTOR_FAILURES.add("hmef/winmail-sample1.dat");
- EXPECTED_EXTRACTOR_FAILURES.add("hmef/bug52400-winmail-simple.dat");
- EXPECTED_EXTRACTOR_FAILURES.add("hmef/bug52400-winmail-with-attachments.dat");
- EXPECTED_EXTRACTOR_FAILURES.add("hpsf/Test0313rur.adm");
- EXPECTED_EXTRACTOR_FAILURES.add("poifs/Notes.ole2");
- }
-
- @Override
- public void handleExtracting(File file) throws Exception {
- boolean before = ExtractorFactory.getThreadPrefersEventExtractors();
- try {
- ExtractorFactory.setThreadPrefersEventExtractors(true);
- handleExtractingInternal(file);
-
- ExtractorFactory.setThreadPrefersEventExtractors(false);
- handleExtractingInternal(file);
- } finally {
- ExtractorFactory.setThreadPrefersEventExtractors(before);
- }
-
- /* Did fail for some documents with special XML contents...
- try {
- OOXMLPrettyPrint.main(new String[] { file.getAbsolutePath(),
- "/tmp/pretty-" + file.getName() });
- } catch (ZipException e) {
- // ignore, not a Zip/OOXML file
- }*/
- }
-
- private void handleExtractingInternal(File file) throws Exception {
- long length = file.length();
- long modified = file.lastModified();
-
- POITextExtractor extractor = ExtractorFactory.createExtractor(file);
- try {
- assertNotNull(extractor);
-
- assertNotNull(extractor.getText());
-
- // also try metadata
- @SuppressWarnings("resource")
- POITextExtractor metadataExtractor = extractor.getMetadataTextExtractor();
- assertNotNull(metadataExtractor.getText());
-
- assertFalse("Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!",
- EXPECTED_EXTRACTOR_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName()));
-
- assertEquals("File should not be modified by extractor", length, file.length());
- assertEquals("File should not be modified by extractor", modified, file.lastModified());
-
- handleExtractingAsStream(file);
-
- if(extractor instanceof POIOLE2TextExtractor) {
- HPSFPropertiesExtractor hpsfExtractor = new HPSFPropertiesExtractor((POIOLE2TextExtractor)extractor);
- try {
- assertNotNull(hpsfExtractor.getDocumentSummaryInformationText());
- assertNotNull(hpsfExtractor.getSummaryInformationText());
- String text = hpsfExtractor.getText();
- //System.out.println(text);
- assertNotNull(text);
- } finally {
- hpsfExtractor.close();
- }
- }
- } catch (IllegalArgumentException e) {
- if(!EXPECTED_EXTRACTOR_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName())) {
- throw e;
- }
- } finally {
- extractor.close();
- }
- }
-
- private void handleExtractingAsStream(File file) throws IOException, OpenXML4JException, XmlException {
- InputStream stream = new FileInputStream(file);
- try {
- POITextExtractor streamExtractor = ExtractorFactory.createExtractor(stream);
- try {
- assertNotNull(streamExtractor);
-
- assertNotNull(streamExtractor.getText());
- } finally {
- streamExtractor.close();
- }
- } finally {
- stream.close();
- }
- }
-
- @Override
- public void handleAdditional(File file) throws Exception {
- // by default we do nothing here
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.stress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.poi.POIOLE2TextExtractor;
+import org.apache.poi.POITextExtractor;
+import org.apache.poi.extractor.ExtractorFactory;
+import org.apache.poi.hpsf.extractor.HPSFPropertiesExtractor;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.xmlbeans.XmlException;
+
+public abstract class AbstractFileHandler implements FileHandler {
+ public static final Set EXPECTED_EXTRACTOR_FAILURES = new HashSet();
+ static {
+ // password protected files
+ EXPECTED_EXTRACTOR_FAILURES.add("document/bug53475-password-is-pass.docx");
+ EXPECTED_EXTRACTOR_FAILURES.add("poifs/extenxls_pwd123.xlsx");
+ EXPECTED_EXTRACTOR_FAILURES.add("poifs/protect.xlsx");
+ EXPECTED_EXTRACTOR_FAILURES.add("poifs/protected_agile.docx");
+ EXPECTED_EXTRACTOR_FAILURES.add("poifs/protected_sha512.xlsx");
+
+ // unsupported file-types, no supported OLE2 parts
+ EXPECTED_EXTRACTOR_FAILURES.add("hmef/quick-winmail.dat");
+ EXPECTED_EXTRACTOR_FAILURES.add("hmef/winmail-sample1.dat");
+ EXPECTED_EXTRACTOR_FAILURES.add("hmef/bug52400-winmail-simple.dat");
+ EXPECTED_EXTRACTOR_FAILURES.add("hmef/bug52400-winmail-with-attachments.dat");
+ EXPECTED_EXTRACTOR_FAILURES.add("hpsf/Test0313rur.adm");
+ EXPECTED_EXTRACTOR_FAILURES.add("poifs/Notes.ole2");
+ }
+
+ @Override
+ public void handleExtracting(File file) throws Exception {
+ boolean before = ExtractorFactory.getThreadPrefersEventExtractors();
+ try {
+ ExtractorFactory.setThreadPrefersEventExtractors(true);
+ handleExtractingInternal(file);
+
+ ExtractorFactory.setThreadPrefersEventExtractors(false);
+ handleExtractingInternal(file);
+ } finally {
+ ExtractorFactory.setThreadPrefersEventExtractors(before);
+ }
+
+ /* Did fail for some documents with special XML contents...
+ try {
+ OOXMLPrettyPrint.main(new String[] { file.getAbsolutePath(),
+ "/tmp/pretty-" + file.getName() });
+ } catch (ZipException e) {
+ // ignore, not a Zip/OOXML file
+ }*/
+ }
+
+ private void handleExtractingInternal(File file) throws Exception {
+ long length = file.length();
+ long modified = file.lastModified();
+
+ POITextExtractor extractor = ExtractorFactory.createExtractor(file);
+ try {
+ assertNotNull(extractor);
+
+ assertNotNull(extractor.getText());
+
+ // also try metadata
+ @SuppressWarnings("resource")
+ POITextExtractor metadataExtractor = extractor.getMetadataTextExtractor();
+ assertNotNull(metadataExtractor.getText());
+
+ assertFalse("Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!",
+ EXPECTED_EXTRACTOR_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName()));
+
+ assertEquals("File should not be modified by extractor", length, file.length());
+ assertEquals("File should not be modified by extractor", modified, file.lastModified());
+
+ handleExtractingAsStream(file);
+
+ if(extractor instanceof POIOLE2TextExtractor) {
+ HPSFPropertiesExtractor hpsfExtractor = new HPSFPropertiesExtractor((POIOLE2TextExtractor)extractor);
+ try {
+ assertNotNull(hpsfExtractor.getDocumentSummaryInformationText());
+ assertNotNull(hpsfExtractor.getSummaryInformationText());
+ String text = hpsfExtractor.getText();
+ //System.out.println(text);
+ assertNotNull(text);
+ } finally {
+ hpsfExtractor.close();
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ if(!EXPECTED_EXTRACTOR_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName())) {
+ throw e;
+ }
+ } finally {
+ extractor.close();
+ }
+ }
+
+ private void handleExtractingAsStream(File file) throws IOException, OpenXML4JException, XmlException {
+ InputStream stream = new FileInputStream(file);
+ try {
+ POITextExtractor streamExtractor = ExtractorFactory.createExtractor(stream);
+ try {
+ assertNotNull(streamExtractor);
+
+ assertNotNull(streamExtractor.getText());
+ } finally {
+ streamExtractor.close();
+ }
+ } finally {
+ stream.close();
+ }
+ }
+
+ @Override
+ public void handleAdditional(File file) throws Exception {
+ // by default we do nothing here
+ }
+}
diff --git a/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java b/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java
index e798b35339..d76e959b8f 100644
--- a/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java
+++ b/src/integrationtest/org/apache/poi/stress/SlideShowHandler.java
@@ -1,126 +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.poi.stress;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.RenderingHints;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import org.apache.poi.sl.draw.DrawFactory;
-import org.apache.poi.sl.usermodel.PictureData;
-import org.apache.poi.sl.usermodel.Shape;
-import org.apache.poi.sl.usermodel.ShapeContainer;
-import org.apache.poi.sl.usermodel.Slide;
-import org.apache.poi.sl.usermodel.SlideShow;
-import org.apache.poi.sl.usermodel.SlideShowFactory;
-import org.apache.poi.sl.usermodel.TextParagraph;
-import org.apache.poi.sl.usermodel.TextRun;
-import org.apache.poi.sl.usermodel.TextShape;
-
-public abstract class SlideShowHandler extends POIFSFileHandler {
- public void handleSlideShow(SlideShow,?> ss) throws IOException {
- renderSlides(ss);
-
- readContent(ss);
- readPictures(ss);
-
- // write out the file
- ByteArrayOutputStream out = writeToArray(ss);
-
- readContent(ss);
-
- // read in the writen file
- SlideShow,?> read = SlideShowFactory.create(new ByteArrayInputStream(out.toByteArray()));
- try {
- assertNotNull(read);
- readContent(read);
- } finally {
- read.close();
- }
- }
-
- private ByteArrayOutputStream writeToArray(SlideShow,?> ss) throws IOException {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- try {
- ss.write(stream);
- } finally {
- stream.close();
- }
-
- return stream;
- }
-
-
- private void readContent(SlideShow,?> ss) {
- for (Slide,?> s : ss.getSlides()) {
- s.getTitle();
- readText(s);
- readText(s.getNotes());
- readText(s.getMasterSheet());
- }
- }
-
- private void readText(ShapeContainer,?> sc) {
- if (sc == null) return;
- for (Shape,?> s : sc) {
- if (s instanceof TextShape) {
- for (TextParagraph,?,?> tp : (TextShape,?>)s) {
- for (TextRun tr : tp) {
- tr.getRawText();
- }
- }
- }
- }
- }
-
- private void readPictures(SlideShow,?> ss) {
- for (PictureData pd : ss.getPictureData()) {
- Dimension dim = pd.getImageDimension();
- assertTrue(dim.getHeight() >= 0);
- assertTrue(dim.getWidth() >= 0);
- }
- }
-
- private void renderSlides(SlideShow,?> ss) {
- Dimension pgsize = ss.getPageSize();
-
- for (Slide,?> s : ss.getSlides()) {
- BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_ARGB);
- Graphics2D graphics = img.createGraphics();
- DrawFactory.getInstance(graphics).fixFonts(graphics);
-
- // default rendering options
- graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
- graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-
- // draw stuff
- s.draw(graphics);
-
- graphics.dispose();
- img.flush();
- }
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.stress;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.sl.draw.DrawFactory;
+import org.apache.poi.sl.usermodel.PictureData;
+import org.apache.poi.sl.usermodel.Shape;
+import org.apache.poi.sl.usermodel.ShapeContainer;
+import org.apache.poi.sl.usermodel.Slide;
+import org.apache.poi.sl.usermodel.SlideShow;
+import org.apache.poi.sl.usermodel.SlideShowFactory;
+import org.apache.poi.sl.usermodel.TextParagraph;
+import org.apache.poi.sl.usermodel.TextRun;
+import org.apache.poi.sl.usermodel.TextShape;
+
+public abstract class SlideShowHandler extends POIFSFileHandler {
+ public void handleSlideShow(SlideShow,?> ss) throws IOException {
+ renderSlides(ss);
+
+ readContent(ss);
+ readPictures(ss);
+
+ // write out the file
+ ByteArrayOutputStream out = writeToArray(ss);
+
+ readContent(ss);
+
+ // read in the writen file
+ SlideShow,?> read = SlideShowFactory.create(new ByteArrayInputStream(out.toByteArray()));
+ try {
+ assertNotNull(read);
+ readContent(read);
+ } finally {
+ read.close();
+ }
+ }
+
+ private ByteArrayOutputStream writeToArray(SlideShow,?> ss) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ ss.write(stream);
+ } finally {
+ stream.close();
+ }
+
+ return stream;
+ }
+
+
+ private void readContent(SlideShow,?> ss) {
+ for (Slide,?> s : ss.getSlides()) {
+ s.getTitle();
+ readText(s);
+ readText(s.getNotes());
+ readText(s.getMasterSheet());
+ }
+ }
+
+ private void readText(ShapeContainer,?> sc) {
+ if (sc == null) return;
+ for (Shape,?> s : sc) {
+ if (s instanceof TextShape) {
+ for (TextParagraph,?,?> tp : (TextShape,?>)s) {
+ for (TextRun tr : tp) {
+ tr.getRawText();
+ }
+ }
+ }
+ }
+ }
+
+ private void readPictures(SlideShow,?> ss) {
+ for (PictureData pd : ss.getPictureData()) {
+ Dimension dim = pd.getImageDimension();
+ assertTrue(dim.getHeight() >= 0);
+ assertTrue(dim.getWidth() >= 0);
+ }
+ }
+
+ private void renderSlides(SlideShow,?> ss) {
+ Dimension pgsize = ss.getPageSize();
+
+ for (Slide,?> s : ss.getSlides()) {
+ BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = img.createGraphics();
+ DrawFactory.getInstance(graphics).fixFonts(graphics);
+
+ // default rendering options
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+
+ // draw stuff
+ s.draw(graphics);
+
+ graphics.dispose();
+ img.flush();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/ddf/EscherColorRef.java b/src/java/org/apache/poi/ddf/EscherColorRef.java
index 33127689e7..cbd62e17b5 100644
--- a/src/java/org/apache/poi/ddf/EscherColorRef.java
+++ b/src/java/org/apache/poi/ddf/EscherColorRef.java
@@ -1,296 +1,296 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.ddf;
-
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.LittleEndian;
-
-/**
- * An OfficeArtCOLORREF structure entry which also handles color extension opid data
- */
-public class EscherColorRef {
- @SuppressWarnings("unused")
- private int opid = -1;
- private int colorRef = 0;
-
- public enum SysIndexSource {
- /** Use the fill color of the shape. */
- FILL_COLOR(0xF0),
- /** If the shape contains a line, use the line color of the shape. Otherwise, use the fill color. */
- LINE_OR_FILL_COLOR(0xF1),
- /** Use the line color of the shape. */
- LINE_COLOR(0xF2),
- /** Use the shadow color of the shape. */
- SHADOW_COLOR(0xF3),
- /** Use the current, or last-used, color. */
- CURRENT_OR_LAST_COLOR(0xF4),
- /** Use the fill background color of the shape. */
- FILL_BACKGROUND_COLOR(0xF5),
- /** Use the line background color of the shape. */
- LINE_BACKGROUND_COLOR(0xF6),
- /** If the shape contains a fill, use the fill color of the shape. Otherwise, use the line color. */
- FILL_OR_LINE_COLOR(0xF7)
- ;
- private int value;
- SysIndexSource(int value) { this.value = value; }
- }
-
- /**
- * The following enum specifies values that indicate special procedural properties that
- * are used to modify the color components of another color. These values are combined with
- * those of the {@link SysIndexSource} enum or with a user-specified color.
- * The first six values are mutually exclusive.
- */
- public enum SysIndexProcedure {
- /**
- * Darken the color by the value that is specified in the blue field.
- * A blue value of 0xFF specifies that the color is to be left unchanged,
- * whereas a blue value of 0x00 specifies that the color is to be completely darkened.
- */
- DARKEN_COLOR(0x01),
- /**
- * Lighten the color by the value that is specified in the blue field.
- * A blue value of 0xFF specifies that the color is to be left unchanged,
- * whereas a blue value of 0x00 specifies that the color is to be completely lightened.
- */
- LIGHTEN_COLOR(0x02),
- /**
- * Add a gray level RGB value. The blue field contains the gray level to add:
- * NewColor = SourceColor + gray
- */
- ADD_GRAY_LEVEL(0x03),
- /**
- * Subtract a gray level RGB value. The blue field contains the gray level to subtract:
- * NewColor = SourceColor - gray
- */
- SUB_GRAY_LEVEL(0x04),
- /**
- * Reverse-subtract a gray level RGB value. The blue field contains the gray level from
- * which to subtract:
- * NewColor = gray - SourceColor
- */
- REVERSE_GRAY_LEVEL(0x05),
- /**
- * If the color component being modified is less than the parameter contained in the blue
- * field, set it to the minimum intensity. If the color component being modified is greater
- * than or equal to the parameter, set it to the maximum intensity.
- */
- THRESHOLD(0x06),
- /**
- * After making other modifications, invert the color.
- * This enum value is only for documentation and won't be directly returned.
- */
- INVERT_AFTER(0x20),
- /**
- * After making other modifications, invert the color by toggling just the high bit of each
- * color channel.
- * This enum value is only for documentation and won't be directly returned.
- */
- INVERT_HIGHBIT_AFTER(0x40)
- ;
- private BitField mask;
- SysIndexProcedure(int mask) {
- this.mask = new BitField(mask);
- }
- }
-
- /**
- * A bit that specifies whether the system color scheme will be used to determine the color.
- * A value of 0x1 specifies that green and red will be treated as an unsigned 16-bit index
- * into the system color table. Values less than 0x00F0 map directly to system colors.
- */
- private static final BitField FLAG_SYS_INDEX = new BitField(0x10000000);
-
- /**
- * A bit that specifies whether the current application-defined color scheme will be used
- * to determine the color. A value of 0x1 specifies that red will be treated as an index
- * into the current color scheme table. If this value is 0x1, green and blue MUST be 0x00.
- */
- private static final BitField FLAG_SCHEME_INDEX = new BitField(0x08000000);
-
- /**
- * A bit that specifies whether the color is a standard RGB color.
- * 0x0 : The RGB color MAY use halftone dithering to display.
- * 0x1 : The color MUST be a solid color.
- */
- private static final BitField FLAG_SYSTEM_RGB = new BitField(0x04000000);
-
- /**
- * A bit that specifies whether the current palette will be used to determine the color.
- * A value of 0x1 specifies that red, green, and blue contain an RGB value that will be
- * matched in the current color palette. This color MUST be solid.
- */
- private static final BitField FLAG_PALETTE_RGB = new BitField(0x02000000);
-
- /**
- * A bit that specifies whether the current palette will be used to determine the color.
- * A value of 0x1 specifies that green and red will be treated as an unsigned 16-bit index into
- * the current color palette. This color MAY be dithered. If this value is 0x1, blue MUST be 0x00.
- */
- private static final BitField FLAG_PALETTE_INDEX = new BitField(0x01000000);
-
- /**
- * An unsigned integer that specifies the intensity of the blue color channel. A value
- * of 0x00 has the minimum blue intensity. A value of 0xFF has the maximum blue intensity.
- */
- private static final BitField FLAG_BLUE = new BitField(0x00FF0000);
-
- /**
- * An unsigned integer that specifies the intensity of the green color channel. A value
- * of 0x00 has the minimum green intensity. A value of 0xFF has the maximum green intensity.
- */
- private static final BitField FLAG_GREEN = new BitField(0x0000FF00);
-
- /**
- * An unsigned integer that specifies the intensity of the red color channel. A value
- * of 0x00 has the minimum red intensity. A value of 0xFF has the maximum red intensity.
- */
- private static final BitField FLAG_RED = new BitField(0x000000FF);
-
- public EscherColorRef(int colorRef) {
- this.colorRef = colorRef;
- }
-
- public EscherColorRef(byte[] source, int start, int len) {
- assert(len == 4 || len == 6);
-
- int offset = start;
- if (len == 6) {
- opid = LittleEndian.getUShort(source, offset);
- offset += 2;
- }
- colorRef = LittleEndian.getInt(source, offset);
- }
-
- public boolean hasSysIndexFlag() {
- return FLAG_SYS_INDEX.isSet(colorRef);
- }
-
- public void setSysIndexFlag(boolean flag) {
- colorRef = FLAG_SYS_INDEX.setBoolean(colorRef, flag);
- }
-
- public boolean hasSchemeIndexFlag() {
- return FLAG_SCHEME_INDEX.isSet(colorRef);
- }
-
- public void setSchemeIndexFlag(boolean flag) {
- colorRef = FLAG_SCHEME_INDEX.setBoolean(colorRef, flag);
- }
-
- public boolean hasSystemRGBFlag() {
- return FLAG_SYSTEM_RGB.isSet(colorRef);
- }
-
- public void setSystemRGBFlag(boolean flag) {
- colorRef = FLAG_SYSTEM_RGB.setBoolean(colorRef, flag);
- }
-
- public boolean hasPaletteRGBFlag() {
- return FLAG_PALETTE_RGB.isSet(colorRef);
- }
-
- public void setPaletteRGBFlag(boolean flag) {
- colorRef = FLAG_PALETTE_RGB.setBoolean(colorRef, flag);
- }
-
- public boolean hasPaletteIndexFlag() {
- return FLAG_PALETTE_INDEX.isSet(colorRef);
- }
-
- public void setPaletteIndexFlag(boolean flag) {
- colorRef = FLAG_PALETTE_INDEX.setBoolean(colorRef, flag);
- }
-
- public int[] getRGB() {
- int rgb[] = {
- FLAG_RED.getValue(colorRef),
- FLAG_GREEN.getValue(colorRef),
- FLAG_BLUE.getValue(colorRef)
- };
- return rgb;
- }
-
- /**
- * @return {@link SysIndexSource} if {@link #hasSysIndexFlag()} is {@code true}, otherwise null
- */
- public SysIndexSource getSysIndexSource() {
- if (!hasSysIndexFlag()) return null;
- int val = FLAG_RED.getValue(colorRef);
- for (SysIndexSource sis : SysIndexSource.values()) {
- if (sis.value == val) return sis;
- }
- return null;
- }
-
- /**
- * Return the {@link SysIndexProcedure} - for invert flag use {@link #getSysIndexInvert()}
- * @return {@link SysIndexProcedure} if {@link #hasSysIndexFlag()} is {@code true}, otherwise null
- */
- public SysIndexProcedure getSysIndexProcedure() {
- if (!hasSysIndexFlag()) return null;
- int val = FLAG_GREEN.getValue(colorRef);
- for (SysIndexProcedure sip : SysIndexProcedure.values()) {
- if (sip == SysIndexProcedure.INVERT_AFTER || sip == SysIndexProcedure.INVERT_HIGHBIT_AFTER) continue;
- if (sip.mask.isSet(val)) return sip;
- }
- return null;
- }
-
- /**
- * @return 0 for no invert flag, 1 for {@link SysIndexProcedure#INVERT_AFTER} and
- * 2 for {@link SysIndexProcedure#INVERT_HIGHBIT_AFTER}
- */
- public int getSysIndexInvert() {
- if (!hasSysIndexFlag()) return 0;
- int val = FLAG_GREEN.getValue(colorRef);
- if ((SysIndexProcedure.INVERT_AFTER.mask.isSet(val))) return 1;
- if ((SysIndexProcedure.INVERT_HIGHBIT_AFTER.mask.isSet(val))) return 2;
- return 0;
- }
-
- /**
- * @return index of the scheme color or -1 if {@link #hasSchemeIndexFlag()} is {@code false}
- *
- * @see org.apache.poi.hslf.record.ColorSchemeAtom#getColor(int)
- */
- public int getSchemeIndex() {
- if (!hasSchemeIndexFlag()) return -1;
- return FLAG_RED.getValue(colorRef);
- }
-
- /**
- * @return index of current palette (color) or -1 if {@link #hasPaletteIndexFlag()} is {@code false}
- */
- public int getPaletteIndex() {
- return (hasPaletteIndexFlag()) ? getIndex() : -1;
- }
-
- /**
- * @return index of system color table or -1 if {@link #hasSysIndexFlag()} is {@code false}
- *
- * @see org.apache.poi.sl.usermodel.PresetColor
- */
- public int getSysIndex() {
- return (hasSysIndexFlag()) ? getIndex() : -1;
- }
-
- private int getIndex() {
- return (FLAG_GREEN.getValue(colorRef) << 8) | FLAG_RED.getValue(colorRef);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * An OfficeArtCOLORREF structure entry which also handles color extension opid data
+ */
+public class EscherColorRef {
+ @SuppressWarnings("unused")
+ private int opid = -1;
+ private int colorRef = 0;
+
+ public enum SysIndexSource {
+ /** Use the fill color of the shape. */
+ FILL_COLOR(0xF0),
+ /** If the shape contains a line, use the line color of the shape. Otherwise, use the fill color. */
+ LINE_OR_FILL_COLOR(0xF1),
+ /** Use the line color of the shape. */
+ LINE_COLOR(0xF2),
+ /** Use the shadow color of the shape. */
+ SHADOW_COLOR(0xF3),
+ /** Use the current, or last-used, color. */
+ CURRENT_OR_LAST_COLOR(0xF4),
+ /** Use the fill background color of the shape. */
+ FILL_BACKGROUND_COLOR(0xF5),
+ /** Use the line background color of the shape. */
+ LINE_BACKGROUND_COLOR(0xF6),
+ /** If the shape contains a fill, use the fill color of the shape. Otherwise, use the line color. */
+ FILL_OR_LINE_COLOR(0xF7)
+ ;
+ private int value;
+ SysIndexSource(int value) { this.value = value; }
+ }
+
+ /**
+ * The following enum specifies values that indicate special procedural properties that
+ * are used to modify the color components of another color. These values are combined with
+ * those of the {@link SysIndexSource} enum or with a user-specified color.
+ * The first six values are mutually exclusive.
+ */
+ public enum SysIndexProcedure {
+ /**
+ * Darken the color by the value that is specified in the blue field.
+ * A blue value of 0xFF specifies that the color is to be left unchanged,
+ * whereas a blue value of 0x00 specifies that the color is to be completely darkened.
+ */
+ DARKEN_COLOR(0x01),
+ /**
+ * Lighten the color by the value that is specified in the blue field.
+ * A blue value of 0xFF specifies that the color is to be left unchanged,
+ * whereas a blue value of 0x00 specifies that the color is to be completely lightened.
+ */
+ LIGHTEN_COLOR(0x02),
+ /**
+ * Add a gray level RGB value. The blue field contains the gray level to add:
+ * NewColor = SourceColor + gray
+ */
+ ADD_GRAY_LEVEL(0x03),
+ /**
+ * Subtract a gray level RGB value. The blue field contains the gray level to subtract:
+ * NewColor = SourceColor - gray
+ */
+ SUB_GRAY_LEVEL(0x04),
+ /**
+ * Reverse-subtract a gray level RGB value. The blue field contains the gray level from
+ * which to subtract:
+ * NewColor = gray - SourceColor
+ */
+ REVERSE_GRAY_LEVEL(0x05),
+ /**
+ * If the color component being modified is less than the parameter contained in the blue
+ * field, set it to the minimum intensity. If the color component being modified is greater
+ * than or equal to the parameter, set it to the maximum intensity.
+ */
+ THRESHOLD(0x06),
+ /**
+ * After making other modifications, invert the color.
+ * This enum value is only for documentation and won't be directly returned.
+ */
+ INVERT_AFTER(0x20),
+ /**
+ * After making other modifications, invert the color by toggling just the high bit of each
+ * color channel.
+ * This enum value is only for documentation and won't be directly returned.
+ */
+ INVERT_HIGHBIT_AFTER(0x40)
+ ;
+ private BitField mask;
+ SysIndexProcedure(int mask) {
+ this.mask = new BitField(mask);
+ }
+ }
+
+ /**
+ * A bit that specifies whether the system color scheme will be used to determine the color.
+ * A value of 0x1 specifies that green and red will be treated as an unsigned 16-bit index
+ * into the system color table. Values less than 0x00F0 map directly to system colors.
+ */
+ private static final BitField FLAG_SYS_INDEX = new BitField(0x10000000);
+
+ /**
+ * A bit that specifies whether the current application-defined color scheme will be used
+ * to determine the color. A value of 0x1 specifies that red will be treated as an index
+ * into the current color scheme table. If this value is 0x1, green and blue MUST be 0x00.
+ */
+ private static final BitField FLAG_SCHEME_INDEX = new BitField(0x08000000);
+
+ /**
+ * A bit that specifies whether the color is a standard RGB color.
+ * 0x0 : The RGB color MAY use halftone dithering to display.
+ * 0x1 : The color MUST be a solid color.
+ */
+ private static final BitField FLAG_SYSTEM_RGB = new BitField(0x04000000);
+
+ /**
+ * A bit that specifies whether the current palette will be used to determine the color.
+ * A value of 0x1 specifies that red, green, and blue contain an RGB value that will be
+ * matched in the current color palette. This color MUST be solid.
+ */
+ private static final BitField FLAG_PALETTE_RGB = new BitField(0x02000000);
+
+ /**
+ * A bit that specifies whether the current palette will be used to determine the color.
+ * A value of 0x1 specifies that green and red will be treated as an unsigned 16-bit index into
+ * the current color palette. This color MAY be dithered. If this value is 0x1, blue MUST be 0x00.
+ */
+ private static final BitField FLAG_PALETTE_INDEX = new BitField(0x01000000);
+
+ /**
+ * An unsigned integer that specifies the intensity of the blue color channel. A value
+ * of 0x00 has the minimum blue intensity. A value of 0xFF has the maximum blue intensity.
+ */
+ private static final BitField FLAG_BLUE = new BitField(0x00FF0000);
+
+ /**
+ * An unsigned integer that specifies the intensity of the green color channel. A value
+ * of 0x00 has the minimum green intensity. A value of 0xFF has the maximum green intensity.
+ */
+ private static final BitField FLAG_GREEN = new BitField(0x0000FF00);
+
+ /**
+ * An unsigned integer that specifies the intensity of the red color channel. A value
+ * of 0x00 has the minimum red intensity. A value of 0xFF has the maximum red intensity.
+ */
+ private static final BitField FLAG_RED = new BitField(0x000000FF);
+
+ public EscherColorRef(int colorRef) {
+ this.colorRef = colorRef;
+ }
+
+ public EscherColorRef(byte[] source, int start, int len) {
+ assert(len == 4 || len == 6);
+
+ int offset = start;
+ if (len == 6) {
+ opid = LittleEndian.getUShort(source, offset);
+ offset += 2;
+ }
+ colorRef = LittleEndian.getInt(source, offset);
+ }
+
+ public boolean hasSysIndexFlag() {
+ return FLAG_SYS_INDEX.isSet(colorRef);
+ }
+
+ public void setSysIndexFlag(boolean flag) {
+ colorRef = FLAG_SYS_INDEX.setBoolean(colorRef, flag);
+ }
+
+ public boolean hasSchemeIndexFlag() {
+ return FLAG_SCHEME_INDEX.isSet(colorRef);
+ }
+
+ public void setSchemeIndexFlag(boolean flag) {
+ colorRef = FLAG_SCHEME_INDEX.setBoolean(colorRef, flag);
+ }
+
+ public boolean hasSystemRGBFlag() {
+ return FLAG_SYSTEM_RGB.isSet(colorRef);
+ }
+
+ public void setSystemRGBFlag(boolean flag) {
+ colorRef = FLAG_SYSTEM_RGB.setBoolean(colorRef, flag);
+ }
+
+ public boolean hasPaletteRGBFlag() {
+ return FLAG_PALETTE_RGB.isSet(colorRef);
+ }
+
+ public void setPaletteRGBFlag(boolean flag) {
+ colorRef = FLAG_PALETTE_RGB.setBoolean(colorRef, flag);
+ }
+
+ public boolean hasPaletteIndexFlag() {
+ return FLAG_PALETTE_INDEX.isSet(colorRef);
+ }
+
+ public void setPaletteIndexFlag(boolean flag) {
+ colorRef = FLAG_PALETTE_INDEX.setBoolean(colorRef, flag);
+ }
+
+ public int[] getRGB() {
+ int rgb[] = {
+ FLAG_RED.getValue(colorRef),
+ FLAG_GREEN.getValue(colorRef),
+ FLAG_BLUE.getValue(colorRef)
+ };
+ return rgb;
+ }
+
+ /**
+ * @return {@link SysIndexSource} if {@link #hasSysIndexFlag()} is {@code true}, otherwise null
+ */
+ public SysIndexSource getSysIndexSource() {
+ if (!hasSysIndexFlag()) return null;
+ int val = FLAG_RED.getValue(colorRef);
+ for (SysIndexSource sis : SysIndexSource.values()) {
+ if (sis.value == val) return sis;
+ }
+ return null;
+ }
+
+ /**
+ * Return the {@link SysIndexProcedure} - for invert flag use {@link #getSysIndexInvert()}
+ * @return {@link SysIndexProcedure} if {@link #hasSysIndexFlag()} is {@code true}, otherwise null
+ */
+ public SysIndexProcedure getSysIndexProcedure() {
+ if (!hasSysIndexFlag()) return null;
+ int val = FLAG_GREEN.getValue(colorRef);
+ for (SysIndexProcedure sip : SysIndexProcedure.values()) {
+ if (sip == SysIndexProcedure.INVERT_AFTER || sip == SysIndexProcedure.INVERT_HIGHBIT_AFTER) continue;
+ if (sip.mask.isSet(val)) return sip;
+ }
+ return null;
+ }
+
+ /**
+ * @return 0 for no invert flag, 1 for {@link SysIndexProcedure#INVERT_AFTER} and
+ * 2 for {@link SysIndexProcedure#INVERT_HIGHBIT_AFTER}
+ */
+ public int getSysIndexInvert() {
+ if (!hasSysIndexFlag()) return 0;
+ int val = FLAG_GREEN.getValue(colorRef);
+ if ((SysIndexProcedure.INVERT_AFTER.mask.isSet(val))) return 1;
+ if ((SysIndexProcedure.INVERT_HIGHBIT_AFTER.mask.isSet(val))) return 2;
+ return 0;
+ }
+
+ /**
+ * @return index of the scheme color or -1 if {@link #hasSchemeIndexFlag()} is {@code false}
+ *
+ * @see org.apache.poi.hslf.record.ColorSchemeAtom#getColor(int)
+ */
+ public int getSchemeIndex() {
+ if (!hasSchemeIndexFlag()) return -1;
+ return FLAG_RED.getValue(colorRef);
+ }
+
+ /**
+ * @return index of current palette (color) or -1 if {@link #hasPaletteIndexFlag()} is {@code false}
+ */
+ public int getPaletteIndex() {
+ return (hasPaletteIndexFlag()) ? getIndex() : -1;
+ }
+
+ /**
+ * @return index of system color table or -1 if {@link #hasSysIndexFlag()} is {@code false}
+ *
+ * @see org.apache.poi.sl.usermodel.PresetColor
+ */
+ public int getSysIndex() {
+ return (hasSysIndexFlag()) ? getIndex() : -1;
+ }
+
+ private int getIndex() {
+ return (FLAG_GREEN.getValue(colorRef) << 8) | FLAG_RED.getValue(colorRef);
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherPictBlip.java b/src/java/org/apache/poi/ddf/EscherPictBlip.java
index 1feaf94776..cbd5fe20a1 100644
--- a/src/java/org/apache/poi/ddf/EscherPictBlip.java
+++ b/src/java/org/apache/poi/ddf/EscherPictBlip.java
@@ -275,24 +275,24 @@ public final class EscherPictBlip extends EscherBlipRecord {
" Size in EMU: " + getSizeEMU() + '\n' +
" Compressed Size: " + HexDump.toHex( field_5_cbSave ) + '\n' +
" Compression: " + HexDump.toHex( field_6_fCompression ) + '\n' +
- " Filter: " + HexDump.toHex( field_7_fFilter ) + '\n' +
- " Extra Data:" + '\n' + extraData;
- }
-
- @Override
- public String toXml(String tab) {
- String extraData = "";
- StringBuilder builder = new StringBuilder();
- builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance())))
- .append(tab).append("\t").append("0x").append(HexDump.toHex( field_1_UID )).append("\n")
- .append(tab).append("\t").append("0x").append(HexDump.toHex( field_2_cb )).append("\n")
- .append(tab).append("\t").append("").append(getBounds()).append("\n")
- .append(tab).append("\t").append("").append(getSizeEMU()).append("\n")
- .append(tab).append("\t").append("0x").append(HexDump.toHex( field_5_cbSave )).append("\n")
- .append(tab).append("\t").append("0x").append(HexDump.toHex( field_6_fCompression )).append("\n")
- .append(tab).append("\t").append("0x").append(HexDump.toHex( field_7_fFilter )).append("\n")
- .append(tab).append("\t").append("").append(extraData).append("\n");
- builder.append(tab).append("").append(getClass().getSimpleName()).append(">\n");
- return builder.toString();
- }
-}
+ " Filter: " + HexDump.toHex( field_7_fFilter ) + '\n' +
+ " Extra Data:" + '\n' + extraData;
+ }
+
+ @Override
+ public String toXml(String tab) {
+ String extraData = "";
+ StringBuilder builder = new StringBuilder();
+ builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance())))
+ .append(tab).append("\t").append("0x").append(HexDump.toHex( field_1_UID )).append("\n")
+ .append(tab).append("\t").append("0x").append(HexDump.toHex( field_2_cb )).append("\n")
+ .append(tab).append("\t").append("").append(getBounds()).append("\n")
+ .append(tab).append("\t").append("").append(getSizeEMU()).append("\n")
+ .append(tab).append("\t").append("0x").append(HexDump.toHex( field_5_cbSave )).append("\n")
+ .append(tab).append("\t").append("0x").append(HexDump.toHex( field_6_fCompression )).append("\n")
+ .append(tab).append("\t").append("0x").append(HexDump.toHex( field_7_fFilter )).append("\n")
+ .append(tab).append("\t").append("").append(extraData).append("\n");
+ builder.append(tab).append("").append(getClass().getSimpleName()).append(">\n");
+ return builder.toString();
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java b/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java
index 256d0230f7..9af5104430 100644
--- a/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java
+++ b/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java
@@ -1,164 +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.poi.hssf.dev;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.hssf.model.InternalWorkbook;
-import org.apache.poi.hssf.record.DrawingGroupRecord;
-import org.apache.poi.hssf.usermodel.HSSFPatriarch;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.util.StringUtil;
-
-/**
- * Utility for representing drawings contained in a binary Excel file as a XML tree
- */
-public class BiffDrawingToXml {
- private static final String SHEET_NAME_PARAM = "-sheet-name";
- private static final String SHEET_INDEXES_PARAM = "-sheet-indexes";
- private static final String EXCLUDE_WORKBOOK_RECORDS = "-exclude-workbook";
-
- private static int getAttributeIndex(String attribute, String[] params) {
- for (int i = 0; i < params.length; i++) {
- String param = params[i];
- if (attribute.equals(param)) {
- return i;
- }
- }
- return -1;
- }
-
- private static boolean isExcludeWorkbookRecords(String[] params) {
- return -1 != getAttributeIndex(EXCLUDE_WORKBOOK_RECORDS, params);
- }
-
- private static List getIndexesByName(String[] params, HSSFWorkbook workbook) {
- List list = new ArrayList();
- int pos = getAttributeIndex(SHEET_NAME_PARAM, params);
- if (-1 != pos) {
- if (pos >= params.length) {
- throw new IllegalArgumentException("sheet name param value was not specified");
- }
- String sheetName = params[pos + 1];
- int sheetPos = workbook.getSheetIndex(sheetName);
- if (-1 == sheetPos){
- throw new IllegalArgumentException("specified sheet name has not been found in xls file");
- }
- list.add(sheetPos);
- }
- return list;
- }
-
- private static List getIndexesByIdArray(String[] params) {
- List list = new ArrayList();
- int pos = getAttributeIndex(SHEET_INDEXES_PARAM, params);
- if (-1 != pos) {
- if (pos >= params.length) {
- throw new IllegalArgumentException("sheet list value was not specified");
- }
- String sheetParam = params[pos + 1];
- String[] sheets = sheetParam.split(",");
- for (String sheet : sheets) {
- list.add(Integer.parseInt(sheet));
- }
- }
- return list;
- }
-
- private static List getSheetsIndexes(String[] params, HSSFWorkbook workbook) {
- List list = new ArrayList();
- list.addAll(getIndexesByIdArray(params));
- list.addAll(getIndexesByName(params, workbook));
- if (0 == list.size()) {
- int size = workbook.getNumberOfSheets();
- for (int i = 0; i < size; i++) {
- list.add(i);
- }
- }
- return list;
- }
-
- private static String getInputFileName(String[] params) {
- return params[params.length - 1];
- }
-
- private static String getOutputFileName(String input) {
- if (input.contains("xls")) {
- return input.replace(".xls", ".xml");
- }
- return input + ".xml";
- }
-
- public static void main(String[] params) throws IOException {
- if (0 == params.length) {
- System.out.println("Usage: BiffDrawingToXml [options] inputWorkbook");
- System.out.println("Options:");
- System.out.println(" -exclude-workbook exclude workbook-level records");
- System.out.println(" -sheet-indexes output sheets with specified indexes");
- System.out.println(" -sheet-namek output sheets with specified name");
- return;
- }
- String input = getInputFileName(params);
- FileInputStream inp = new FileInputStream(input);
- String output = getOutputFileName(input);
- FileOutputStream outputStream = new FileOutputStream(output);
- writeToFile(outputStream, inp, isExcludeWorkbookRecords(params), params);
- inp.close();
- outputStream.close();
- }
-
- public static void writeToFile(OutputStream fos, InputStream xlsWorkbook, boolean excludeWorkbookRecords, String[] params) throws IOException {
- HSSFWorkbook workbook = new HSSFWorkbook(xlsWorkbook);
- InternalWorkbook internalWorkbook = workbook.getInternalWorkbook();
- DrawingGroupRecord r = (DrawingGroupRecord) internalWorkbook.findFirstRecordBySid(DrawingGroupRecord.sid);
-
- StringBuilder builder = new StringBuilder();
- builder.append("\n");
- String tab = "\t";
- if (!excludeWorkbookRecords && r != null) {
- r.decode();
- List escherRecords = r.getEscherRecords();
- for (EscherRecord record : escherRecords) {
- builder.append(record.toXml(tab));
- }
- }
- List sheets = getSheetsIndexes(params, workbook);
- for (Integer i : sheets) {
- HSSFPatriarch p = workbook.getSheetAt(i).getDrawingPatriarch();
- if(p != null ) {
- builder.append(tab).append("\n");
- builder.append(p.getBoundAggregate().toXml(tab + "\t"));
- builder.append(tab).append("\n");
- }
- }
- builder.append("\n");
- fos.write(builder.toString().getBytes(StringUtil.UTF8));
- fos.close();
- workbook.close();
- }
-
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.hssf.dev;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.hssf.model.InternalWorkbook;
+import org.apache.poi.hssf.record.DrawingGroupRecord;
+import org.apache.poi.hssf.usermodel.HSSFPatriarch;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * Utility for representing drawings contained in a binary Excel file as a XML tree
+ */
+public class BiffDrawingToXml {
+ private static final String SHEET_NAME_PARAM = "-sheet-name";
+ private static final String SHEET_INDEXES_PARAM = "-sheet-indexes";
+ private static final String EXCLUDE_WORKBOOK_RECORDS = "-exclude-workbook";
+
+ private static int getAttributeIndex(String attribute, String[] params) {
+ for (int i = 0; i < params.length; i++) {
+ String param = params[i];
+ if (attribute.equals(param)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static boolean isExcludeWorkbookRecords(String[] params) {
+ return -1 != getAttributeIndex(EXCLUDE_WORKBOOK_RECORDS, params);
+ }
+
+ private static List getIndexesByName(String[] params, HSSFWorkbook workbook) {
+ List list = new ArrayList();
+ int pos = getAttributeIndex(SHEET_NAME_PARAM, params);
+ if (-1 != pos) {
+ if (pos >= params.length) {
+ throw new IllegalArgumentException("sheet name param value was not specified");
+ }
+ String sheetName = params[pos + 1];
+ int sheetPos = workbook.getSheetIndex(sheetName);
+ if (-1 == sheetPos){
+ throw new IllegalArgumentException("specified sheet name has not been found in xls file");
+ }
+ list.add(sheetPos);
+ }
+ return list;
+ }
+
+ private static List getIndexesByIdArray(String[] params) {
+ List list = new ArrayList();
+ int pos = getAttributeIndex(SHEET_INDEXES_PARAM, params);
+ if (-1 != pos) {
+ if (pos >= params.length) {
+ throw new IllegalArgumentException("sheet list value was not specified");
+ }
+ String sheetParam = params[pos + 1];
+ String[] sheets = sheetParam.split(",");
+ for (String sheet : sheets) {
+ list.add(Integer.parseInt(sheet));
+ }
+ }
+ return list;
+ }
+
+ private static List getSheetsIndexes(String[] params, HSSFWorkbook workbook) {
+ List list = new ArrayList();
+ list.addAll(getIndexesByIdArray(params));
+ list.addAll(getIndexesByName(params, workbook));
+ if (0 == list.size()) {
+ int size = workbook.getNumberOfSheets();
+ for (int i = 0; i < size; i++) {
+ list.add(i);
+ }
+ }
+ return list;
+ }
+
+ private static String getInputFileName(String[] params) {
+ return params[params.length - 1];
+ }
+
+ private static String getOutputFileName(String input) {
+ if (input.contains("xls")) {
+ return input.replace(".xls", ".xml");
+ }
+ return input + ".xml";
+ }
+
+ public static void main(String[] params) throws IOException {
+ if (0 == params.length) {
+ System.out.println("Usage: BiffDrawingToXml [options] inputWorkbook");
+ System.out.println("Options:");
+ System.out.println(" -exclude-workbook exclude workbook-level records");
+ System.out.println(" -sheet-indexes output sheets with specified indexes");
+ System.out.println(" -sheet-namek output sheets with specified name");
+ return;
+ }
+ String input = getInputFileName(params);
+ FileInputStream inp = new FileInputStream(input);
+ String output = getOutputFileName(input);
+ FileOutputStream outputStream = new FileOutputStream(output);
+ writeToFile(outputStream, inp, isExcludeWorkbookRecords(params), params);
+ inp.close();
+ outputStream.close();
+ }
+
+ public static void writeToFile(OutputStream fos, InputStream xlsWorkbook, boolean excludeWorkbookRecords, String[] params) throws IOException {
+ HSSFWorkbook workbook = new HSSFWorkbook(xlsWorkbook);
+ InternalWorkbook internalWorkbook = workbook.getInternalWorkbook();
+ DrawingGroupRecord r = (DrawingGroupRecord) internalWorkbook.findFirstRecordBySid(DrawingGroupRecord.sid);
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("\n");
+ String tab = "\t";
+ if (!excludeWorkbookRecords && r != null) {
+ r.decode();
+ List escherRecords = r.getEscherRecords();
+ for (EscherRecord record : escherRecords) {
+ builder.append(record.toXml(tab));
+ }
+ }
+ List sheets = getSheetsIndexes(params, workbook);
+ for (Integer i : sheets) {
+ HSSFPatriarch p = workbook.getSheetAt(i).getDrawingPatriarch();
+ if(p != null ) {
+ builder.append(tab).append("\n");
+ builder.append(p.getBoundAggregate().toXml(tab + "\t"));
+ builder.append(tab).append("\n");
+ }
+ }
+ builder.append("\n");
+ fos.write(builder.toString().getBytes(StringUtil.UTF8));
+ fos.close();
+ workbook.close();
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java b/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java
index 2e98cba55e..c5fa4bae99 100644
--- a/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java
+++ b/src/java/org/apache/poi/hssf/record/LbsDataSubRecord.java
@@ -1,420 +1,420 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.hssf.record;
-
-import org.apache.poi.ss.formula.ptg.Ptg;
-import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndianInput;
-import org.apache.poi.util.LittleEndianOutput;
-import org.apache.poi.util.StringUtil;
-
-/**
- * This structure specifies the properties of a list or drop-down list embedded object in a sheet.
- */
-public class LbsDataSubRecord extends SubRecord {
-
- public static final int sid = 0x0013;
-
- /**
- * From [MS-XLS].pdf 2.5.147 FtLbsData:
- *
- * An unsigned integer that indirectly specifies whether
- * some of the data in this structure appear in a subsequent Continue record.
- * If _cbFContinued is 0x00, all of the fields in this structure except sid and _cbFContinued
- * MUST NOT exist. If this entire structure is contained within the same record,
- * then _cbFContinued MUST be greater than or equal to the size, in bytes,
- * of this structure, not including the four bytes for the ft and _cbFContinued fields
- */
- private int _cbFContinued;
-
- /**
- * a formula that specifies the range of cell values that are the items in this list.
- */
- private int _unknownPreFormulaInt;
- private Ptg _linkPtg;
- private Byte _unknownPostFormulaByte;
-
- /**
- * An unsigned integer that specifies the number of items in the list.
- */
- private int _cLines;
-
- /**
- * An unsigned integer that specifies the one-based index of the first selected item in this list.
- * A value of 0x00 specifies there is no currently selected item.
- */
- private int _iSel;
-
- /**
- * flags that tell what data follows
- */
- private int _flags;
-
- /**
- * An ObjId that specifies the edit box associated with this list.
- * A value of 0x00 specifies that there is no edit box associated with this list.
- */
- private int _idEdit;
-
- /**
- * An optional LbsDropData that specifies properties for this dropdown control.
- * This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
- */
- private LbsDropData _dropData;
-
- /**
- * An optional array of strings where each string specifies an item in the list.
- * The number of elements in this array, if it exists, MUST be {@link #_cLines}
- */
- private String[] _rgLines;
-
- /**
- * An optional array of booleans that specifies
- * which items in the list are part of a multiple selection
- */
- private boolean[] _bsels;
-
- /**
- * @param in the stream to read data from
- * @param cbFContinued the seconf short in the record header
- * @param cmoOt the containing Obj's {@link CommonObjectDataSubRecord#field_1_objectType}
- */
- public LbsDataSubRecord(LittleEndianInput in, int cbFContinued, int cmoOt) {
- _cbFContinued = cbFContinued;
-
- int encodedTokenLen = in.readUShort();
- if (encodedTokenLen > 0) {
- int formulaSize = in.readUShort();
- _unknownPreFormulaInt = in.readInt();
-
- Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
- if (ptgs.length != 1) {
- throw new RecordFormatException("Read " + ptgs.length
- + " tokens but expected exactly 1");
- }
- _linkPtg = ptgs[0];
- switch (encodedTokenLen - formulaSize - 6) {
- case 1:
- _unknownPostFormulaByte = in.readByte();
- break;
- case 0:
- _unknownPostFormulaByte = null;
- break;
- default:
- throw new RecordFormatException("Unexpected leftover bytes");
- }
- }
-
- _cLines = in.readUShort();
- _iSel = in.readUShort();
- _flags = in.readUShort();
- _idEdit = in.readUShort();
-
- // From [MS-XLS].pdf 2.5.147 FtLbsData:
- // This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
- if(cmoOt == 0x14) {
- _dropData = new LbsDropData(in);
- }
-
- // From [MS-XLS].pdf 2.5.147 FtLbsData:
- // This array MUST exist if and only if the fValidPlex flag (0x2) is set
- if((_flags & 0x2) != 0) {
- _rgLines = new String[_cLines];
- for(int i=0; i < _cLines; i++) {
- _rgLines[i] = StringUtil.readUnicodeString(in);
- }
- }
-
- // bits 5-6 in the _flags specify the type
- // of selection behavior this list control is expected to support
-
- // From [MS-XLS].pdf 2.5.147 FtLbsData:
- // This array MUST exist if and only if the wListType field is not equal to 0.
- if(((_flags >> 4) & 0x2) != 0) {
- _bsels = new boolean[_cLines];
- for(int i=0; i < _cLines; i++) {
- _bsels[i] = in.readByte() == 1;
- }
- }
-
- }
-
- LbsDataSubRecord(){
-
- }
-
- /**
- *
- * @return a new instance of LbsDataSubRecord to construct auto-filters
- * @see org.apache.poi.hssf.usermodel.HSSFCombobox
- */
- public static LbsDataSubRecord newAutoFilterInstance(){
- LbsDataSubRecord lbs = new LbsDataSubRecord();
- lbs._cbFContinued = 0x1FEE; //autofilters seem to alway have this magic number
- lbs._iSel = 0x000;
-
- lbs._flags = 0x0301;
- lbs._dropData = new LbsDropData();
- lbs._dropData._wStyle = LbsDropData.STYLE_COMBO_SIMPLE_DROPDOWN;
-
- // the number of lines to be displayed in the dropdown
- lbs._dropData._cLine = 8;
- return lbs;
- }
-
- /**
- * @return true as LbsDataSubRecord is always the last sub-record
- */
- @Override
- public boolean isTerminating(){
- return true;
- }
-
- @Override
- protected int getDataSize() {
- int result = 2; // 2 initial shorts
-
- // optional link formula
- if (_linkPtg != null) {
- result += 2; // encoded Ptg size
- result += 4; // unknown int
- result += _linkPtg.getSize();
- if (_unknownPostFormulaByte != null) {
- result += 1;
- }
- }
-
- result += 4 * 2; // 4 shorts
- if(_dropData != null) {
- result += _dropData.getDataSize();
- }
- if(_rgLines != null) {
- for(String str : _rgLines){
- result += StringUtil.getEncodedSize(str);
- }
- }
- if(_bsels != null) {
- result += _bsels.length;
- }
- return result;
- }
-
- @Override
- public void serialize(LittleEndianOutput out) {
- out.writeShort(sid);
- out.writeShort(_cbFContinued);
-
- if (_linkPtg == null) {
- out.writeShort(0);
- } else {
- int formulaSize = _linkPtg.getSize();
- int linkSize = formulaSize + 6;
- if (_unknownPostFormulaByte != null) {
- linkSize++;
- }
- out.writeShort(linkSize);
- out.writeShort(formulaSize);
- out.writeInt(_unknownPreFormulaInt);
- _linkPtg.write(out);
- if (_unknownPostFormulaByte != null) {
- out.writeByte(_unknownPostFormulaByte.intValue());
- }
- }
-
- out.writeShort(_cLines);
- out.writeShort(_iSel);
- out.writeShort(_flags);
- out.writeShort(_idEdit);
-
- if(_dropData != null) {
- _dropData.serialize(out);
- }
-
- if(_rgLines != null) {
- for(String str : _rgLines){
- StringUtil.writeUnicodeString(out, str);
- }
- }
-
- if(_bsels != null) {
- for(boolean val : _bsels){
- out.writeByte(val ? 1 : 0);
- }
- }
- }
-
- @Override
- public LbsDataSubRecord clone() {
- // TODO: is immutable ???
- return this;
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer(256);
-
- sb.append("[ftLbsData]\n");
- sb.append(" .unknownShort1 =").append(HexDump.shortToHex(_cbFContinued)).append("\n");
- sb.append(" .formula = ").append('\n');
- if(_linkPtg != null) {
- sb.append(_linkPtg.toString()).append(_linkPtg.getRVAType()).append('\n');
- }
- sb.append(" .nEntryCount =").append(HexDump.shortToHex(_cLines)).append("\n");
- sb.append(" .selEntryIx =").append(HexDump.shortToHex(_iSel)).append("\n");
- sb.append(" .style =").append(HexDump.shortToHex(_flags)).append("\n");
- sb.append(" .unknownShort10=").append(HexDump.shortToHex(_idEdit)).append("\n");
- if(_dropData != null) {
- sb.append('\n').append(_dropData.toString());
- }
- sb.append("[/ftLbsData]\n");
- return sb.toString();
- }
-
- /**
- *
- * @return the formula that specifies the range of cell values that are the items in this list.
- */
- public Ptg getFormula(){
- return _linkPtg;
- }
-
- /**
- * @return the number of items in the list
- */
- public int getNumberOfItems(){
- return _cLines;
- }
-
- /**
- * This structure specifies properties of the dropdown list control
- */
- public static class LbsDropData {
- /**
- * Combo dropdown control
- */
- public static final int STYLE_COMBO_DROPDOWN = 0;
- /**
- * Combo Edit dropdown control
- */
- public static final int STYLE_COMBO_EDIT_DROPDOWN = 1;
- /**
- * Simple dropdown control (just the dropdown button)
- */
- public static final int STYLE_COMBO_SIMPLE_DROPDOWN = 2;
-
- /**
- * An unsigned integer that specifies the style of this dropdown.
- */
- private int _wStyle;
-
- /**
- * An unsigned integer that specifies the number of lines to be displayed in the dropdown.
- */
- private int _cLine;
-
- /**
- * An unsigned integer that specifies the smallest width in pixels allowed for the dropdown window
- */
- private int _dxMin;
-
- /**
- * a string that specifies the current string value in the dropdown
- */
- private final String _str;
-
- /**
- * Optional, undefined and MUST be ignored.
- * This field MUST exist if and only if the size of str in bytes is an odd number
- */
- private Byte _unused;
-
- public LbsDropData(){
- _str = "";
- _unused = 0;
- }
-
- public LbsDropData(LittleEndianInput in){
- _wStyle = in.readUShort();
- _cLine = in.readUShort();
- _dxMin = in.readUShort();
- _str = StringUtil.readUnicodeString(in);
- if(StringUtil.getEncodedSize(_str) % 2 != 0){
- _unused = in.readByte();
- }
- }
-
- /**
- * Set the style of this dropdown.
- *
- * Possible values:
- *
- *
0: Combo dropdown control
- *
1: Combo Edit dropdown control
- *
2: Simple dropdown control (just the dropdown button)
- *
- *
- * @param style the style - see possible values
- */
- public void setStyle(int style){
- _wStyle = style;
- }
-
- /**
- * Set the number of lines to be displayed in the dropdown.
- *
- * @param num the number of lines to be displayed in the dropdown
- */
- public void setNumLines(int num){
- _cLine = num;
- }
-
- public void serialize(LittleEndianOutput out) {
- out.writeShort(_wStyle);
- out.writeShort(_cLine);
- out.writeShort(_dxMin);
- StringUtil.writeUnicodeString(out, _str);
- if(_unused != null) {
- out.writeByte(_unused);
- }
- }
-
- public int getDataSize() {
- int size = 6;
- size += StringUtil.getEncodedSize(_str);
- if(_unused != null) {
- size++;
- }
- return size;
- }
-
- @Override
- public String toString(){
- StringBuffer sb = new StringBuffer();
- sb.append("[LbsDropData]\n");
- sb.append(" ._wStyle: ").append(_wStyle).append('\n');
- sb.append(" ._cLine: ").append(_cLine).append('\n');
- sb.append(" ._dxMin: ").append(_dxMin).append('\n');
- sb.append(" ._str: ").append(_str).append('\n');
- if(_unused != null) {
- sb.append(" ._unused: ").append(_unused).append('\n');
- }
- sb.append("[/LbsDropData]\n");
-
- return sb.toString();
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.ss.formula.ptg.Ptg;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * This structure specifies the properties of a list or drop-down list embedded object in a sheet.
+ */
+public class LbsDataSubRecord extends SubRecord {
+
+ public static final int sid = 0x0013;
+
+ /**
+ * From [MS-XLS].pdf 2.5.147 FtLbsData:
+ *
+ * An unsigned integer that indirectly specifies whether
+ * some of the data in this structure appear in a subsequent Continue record.
+ * If _cbFContinued is 0x00, all of the fields in this structure except sid and _cbFContinued
+ * MUST NOT exist. If this entire structure is contained within the same record,
+ * then _cbFContinued MUST be greater than or equal to the size, in bytes,
+ * of this structure, not including the four bytes for the ft and _cbFContinued fields
+ */
+ private int _cbFContinued;
+
+ /**
+ * a formula that specifies the range of cell values that are the items in this list.
+ */
+ private int _unknownPreFormulaInt;
+ private Ptg _linkPtg;
+ private Byte _unknownPostFormulaByte;
+
+ /**
+ * An unsigned integer that specifies the number of items in the list.
+ */
+ private int _cLines;
+
+ /**
+ * An unsigned integer that specifies the one-based index of the first selected item in this list.
+ * A value of 0x00 specifies there is no currently selected item.
+ */
+ private int _iSel;
+
+ /**
+ * flags that tell what data follows
+ */
+ private int _flags;
+
+ /**
+ * An ObjId that specifies the edit box associated with this list.
+ * A value of 0x00 specifies that there is no edit box associated with this list.
+ */
+ private int _idEdit;
+
+ /**
+ * An optional LbsDropData that specifies properties for this dropdown control.
+ * This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
+ */
+ private LbsDropData _dropData;
+
+ /**
+ * An optional array of strings where each string specifies an item in the list.
+ * The number of elements in this array, if it exists, MUST be {@link #_cLines}
+ */
+ private String[] _rgLines;
+
+ /**
+ * An optional array of booleans that specifies
+ * which items in the list are part of a multiple selection
+ */
+ private boolean[] _bsels;
+
+ /**
+ * @param in the stream to read data from
+ * @param cbFContinued the seconf short in the record header
+ * @param cmoOt the containing Obj's {@link CommonObjectDataSubRecord#field_1_objectType}
+ */
+ public LbsDataSubRecord(LittleEndianInput in, int cbFContinued, int cmoOt) {
+ _cbFContinued = cbFContinued;
+
+ int encodedTokenLen = in.readUShort();
+ if (encodedTokenLen > 0) {
+ int formulaSize = in.readUShort();
+ _unknownPreFormulaInt = in.readInt();
+
+ Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
+ if (ptgs.length != 1) {
+ throw new RecordFormatException("Read " + ptgs.length
+ + " tokens but expected exactly 1");
+ }
+ _linkPtg = ptgs[0];
+ switch (encodedTokenLen - formulaSize - 6) {
+ case 1:
+ _unknownPostFormulaByte = in.readByte();
+ break;
+ case 0:
+ _unknownPostFormulaByte = null;
+ break;
+ default:
+ throw new RecordFormatException("Unexpected leftover bytes");
+ }
+ }
+
+ _cLines = in.readUShort();
+ _iSel = in.readUShort();
+ _flags = in.readUShort();
+ _idEdit = in.readUShort();
+
+ // From [MS-XLS].pdf 2.5.147 FtLbsData:
+ // This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
+ if(cmoOt == 0x14) {
+ _dropData = new LbsDropData(in);
+ }
+
+ // From [MS-XLS].pdf 2.5.147 FtLbsData:
+ // This array MUST exist if and only if the fValidPlex flag (0x2) is set
+ if((_flags & 0x2) != 0) {
+ _rgLines = new String[_cLines];
+ for(int i=0; i < _cLines; i++) {
+ _rgLines[i] = StringUtil.readUnicodeString(in);
+ }
+ }
+
+ // bits 5-6 in the _flags specify the type
+ // of selection behavior this list control is expected to support
+
+ // From [MS-XLS].pdf 2.5.147 FtLbsData:
+ // This array MUST exist if and only if the wListType field is not equal to 0.
+ if(((_flags >> 4) & 0x2) != 0) {
+ _bsels = new boolean[_cLines];
+ for(int i=0; i < _cLines; i++) {
+ _bsels[i] = in.readByte() == 1;
+ }
+ }
+
+ }
+
+ LbsDataSubRecord(){
+
+ }
+
+ /**
+ *
+ * @return a new instance of LbsDataSubRecord to construct auto-filters
+ * @see org.apache.poi.hssf.usermodel.HSSFCombobox
+ */
+ public static LbsDataSubRecord newAutoFilterInstance(){
+ LbsDataSubRecord lbs = new LbsDataSubRecord();
+ lbs._cbFContinued = 0x1FEE; //autofilters seem to alway have this magic number
+ lbs._iSel = 0x000;
+
+ lbs._flags = 0x0301;
+ lbs._dropData = new LbsDropData();
+ lbs._dropData._wStyle = LbsDropData.STYLE_COMBO_SIMPLE_DROPDOWN;
+
+ // the number of lines to be displayed in the dropdown
+ lbs._dropData._cLine = 8;
+ return lbs;
+ }
+
+ /**
+ * @return true as LbsDataSubRecord is always the last sub-record
+ */
+ @Override
+ public boolean isTerminating(){
+ return true;
+ }
+
+ @Override
+ protected int getDataSize() {
+ int result = 2; // 2 initial shorts
+
+ // optional link formula
+ if (_linkPtg != null) {
+ result += 2; // encoded Ptg size
+ result += 4; // unknown int
+ result += _linkPtg.getSize();
+ if (_unknownPostFormulaByte != null) {
+ result += 1;
+ }
+ }
+
+ result += 4 * 2; // 4 shorts
+ if(_dropData != null) {
+ result += _dropData.getDataSize();
+ }
+ if(_rgLines != null) {
+ for(String str : _rgLines){
+ result += StringUtil.getEncodedSize(str);
+ }
+ }
+ if(_bsels != null) {
+ result += _bsels.length;
+ }
+ return result;
+ }
+
+ @Override
+ public void serialize(LittleEndianOutput out) {
+ out.writeShort(sid);
+ out.writeShort(_cbFContinued);
+
+ if (_linkPtg == null) {
+ out.writeShort(0);
+ } else {
+ int formulaSize = _linkPtg.getSize();
+ int linkSize = formulaSize + 6;
+ if (_unknownPostFormulaByte != null) {
+ linkSize++;
+ }
+ out.writeShort(linkSize);
+ out.writeShort(formulaSize);
+ out.writeInt(_unknownPreFormulaInt);
+ _linkPtg.write(out);
+ if (_unknownPostFormulaByte != null) {
+ out.writeByte(_unknownPostFormulaByte.intValue());
+ }
+ }
+
+ out.writeShort(_cLines);
+ out.writeShort(_iSel);
+ out.writeShort(_flags);
+ out.writeShort(_idEdit);
+
+ if(_dropData != null) {
+ _dropData.serialize(out);
+ }
+
+ if(_rgLines != null) {
+ for(String str : _rgLines){
+ StringUtil.writeUnicodeString(out, str);
+ }
+ }
+
+ if(_bsels != null) {
+ for(boolean val : _bsels){
+ out.writeByte(val ? 1 : 0);
+ }
+ }
+ }
+
+ @Override
+ public LbsDataSubRecord clone() {
+ // TODO: is immutable ???
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(256);
+
+ sb.append("[ftLbsData]\n");
+ sb.append(" .unknownShort1 =").append(HexDump.shortToHex(_cbFContinued)).append("\n");
+ sb.append(" .formula = ").append('\n');
+ if(_linkPtg != null) {
+ sb.append(_linkPtg.toString()).append(_linkPtg.getRVAType()).append('\n');
+ }
+ sb.append(" .nEntryCount =").append(HexDump.shortToHex(_cLines)).append("\n");
+ sb.append(" .selEntryIx =").append(HexDump.shortToHex(_iSel)).append("\n");
+ sb.append(" .style =").append(HexDump.shortToHex(_flags)).append("\n");
+ sb.append(" .unknownShort10=").append(HexDump.shortToHex(_idEdit)).append("\n");
+ if(_dropData != null) {
+ sb.append('\n').append(_dropData.toString());
+ }
+ sb.append("[/ftLbsData]\n");
+ return sb.toString();
+ }
+
+ /**
+ *
+ * @return the formula that specifies the range of cell values that are the items in this list.
+ */
+ public Ptg getFormula(){
+ return _linkPtg;
+ }
+
+ /**
+ * @return the number of items in the list
+ */
+ public int getNumberOfItems(){
+ return _cLines;
+ }
+
+ /**
+ * This structure specifies properties of the dropdown list control
+ */
+ public static class LbsDropData {
+ /**
+ * Combo dropdown control
+ */
+ public static final int STYLE_COMBO_DROPDOWN = 0;
+ /**
+ * Combo Edit dropdown control
+ */
+ public static final int STYLE_COMBO_EDIT_DROPDOWN = 1;
+ /**
+ * Simple dropdown control (just the dropdown button)
+ */
+ public static final int STYLE_COMBO_SIMPLE_DROPDOWN = 2;
+
+ /**
+ * An unsigned integer that specifies the style of this dropdown.
+ */
+ private int _wStyle;
+
+ /**
+ * An unsigned integer that specifies the number of lines to be displayed in the dropdown.
+ */
+ private int _cLine;
+
+ /**
+ * An unsigned integer that specifies the smallest width in pixels allowed for the dropdown window
+ */
+ private int _dxMin;
+
+ /**
+ * a string that specifies the current string value in the dropdown
+ */
+ private final String _str;
+
+ /**
+ * Optional, undefined and MUST be ignored.
+ * This field MUST exist if and only if the size of str in bytes is an odd number
+ */
+ private Byte _unused;
+
+ public LbsDropData(){
+ _str = "";
+ _unused = 0;
+ }
+
+ public LbsDropData(LittleEndianInput in){
+ _wStyle = in.readUShort();
+ _cLine = in.readUShort();
+ _dxMin = in.readUShort();
+ _str = StringUtil.readUnicodeString(in);
+ if(StringUtil.getEncodedSize(_str) % 2 != 0){
+ _unused = in.readByte();
+ }
+ }
+
+ /**
+ * Set the style of this dropdown.
+ *
+ * Possible values:
+ *
+ *
0: Combo dropdown control
+ *
1: Combo Edit dropdown control
+ *
2: Simple dropdown control (just the dropdown button)
+ *
+ *
+ * @param style the style - see possible values
+ */
+ public void setStyle(int style){
+ _wStyle = style;
+ }
+
+ /**
+ * Set the number of lines to be displayed in the dropdown.
+ *
+ * @param num the number of lines to be displayed in the dropdown
+ */
+ public void setNumLines(int num){
+ _cLine = num;
+ }
+
+ public void serialize(LittleEndianOutput out) {
+ out.writeShort(_wStyle);
+ out.writeShort(_cLine);
+ out.writeShort(_dxMin);
+ StringUtil.writeUnicodeString(out, _str);
+ if(_unused != null) {
+ out.writeByte(_unused);
+ }
+ }
+
+ public int getDataSize() {
+ int size = 6;
+ size += StringUtil.getEncodedSize(_str);
+ if(_unused != null) {
+ size++;
+ }
+ return size;
+ }
+
+ @Override
+ public String toString(){
+ StringBuffer sb = new StringBuffer();
+ sb.append("[LbsDropData]\n");
+ sb.append(" ._wStyle: ").append(_wStyle).append('\n');
+ sb.append(" ._cLine: ").append(_cLine).append('\n');
+ sb.append(" ._dxMin: ").append(_dxMin).append('\n');
+ sb.append(" ._str: ").append(_str).append('\n');
+ if(_unused != null) {
+ sb.append(" ._unused: ").append(_unused).append('\n');
+ }
+ sb.append("[/LbsDropData]\n");
+
+ return sb.toString();
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java b/src/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java
index 2c5ba94c09..e5f11e83e2 100644
--- a/src/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java
+++ b/src/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java
@@ -341,14 +341,14 @@ public final class RecordFactoryInputStream {
}
if (_lastRecord instanceof DrawingGroupRecord) {
((DrawingGroupRecord) _lastRecord).processContinueRecord(contRec.getData());
- return null;
- }
- if (_lastRecord instanceof DrawingRecord) {
-// ((DrawingRecord) _lastRecord).appendContinueRecord(contRec.getData());
- return contRec;
- }
- if (_lastRecord instanceof UnknownRecord) {
- //Gracefully handle records that we don't know about,
+ return null;
+ }
+ if (_lastRecord instanceof DrawingRecord) {
+// ((DrawingRecord) _lastRecord).appendContinueRecord(contRec.getData());
+ return contRec;
+ }
+ if (_lastRecord instanceof UnknownRecord) {
+ //Gracefully handle records that we don't know about,
//that happen to be continued
return record;
}
diff --git a/src/java/org/apache/poi/hssf/record/cont/ContinuableRecordInput.java b/src/java/org/apache/poi/hssf/record/cont/ContinuableRecordInput.java
index ecd8d4b595..b301d54442 100644
--- a/src/java/org/apache/poi/hssf/record/cont/ContinuableRecordInput.java
+++ b/src/java/org/apache/poi/hssf/record/cont/ContinuableRecordInput.java
@@ -1,132 +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.poi.hssf.record.cont;
-
-import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.record.ContinueRecord;
-import org.apache.poi.util.LittleEndianInput;
-
-/**
- * A decorated {@link RecordInputStream} that can read primitive data types
- * (short, int, long, etc.) spanned across a {@link ContinueRecord } boundary.
- *
- *
- * Most records construct themselves from {@link RecordInputStream}.
- * This class assumes that a {@link ContinueRecord} record break always occurs at the type boundary,
- * however, it is not always so.
- *
- * Two attachments to Bugzilla 50779
- * demonstrate that a CONTINUE break can appear right in between two bytes of a unicode character
- * or between two bytes of a short. The problematic portion of the data is
- * in a Asian Phonetic Settings Block (ExtRst) of a UnicodeString.
- *
- * {@link RecordInputStream} greedily requests the bytes to be read and stumbles on such files with a
- * "Not enough data (1) to read requested (2) bytes" exception. The ContinuableRecordInput
- * class circumvents this "type boundary" rule and reads data byte-by-byte rolling over CONTINUE if necessary.
- *
- *
- *
- * YK: For now (March 2011) this class is only used to read
- * @link org.apache.poi.hssf.record.common.UnicodeString.ExtRst} blocks of a UnicodeString.
- *
- *
- */
-public class ContinuableRecordInput implements LittleEndianInput {
- private final RecordInputStream _in;
-
- public ContinuableRecordInput(RecordInputStream in){
- _in = in;
- }
- @Override
- public int available(){
- return _in.available();
- }
-
- @Override
- public byte readByte(){
- return _in.readByte();
- }
-
- @Override
- public int readUByte(){
- return _in.readUByte();
- }
-
- @Override
- public short readShort(){
- return _in.readShort();
- }
-
- @Override
- public int readUShort(){
- int ch1 = readUByte();
- int ch2 = readUByte();
- return (ch2 << 8) + (ch1 << 0);
- }
-
- @Override
- public int readInt(){
- int ch1 = _in.readUByte();
- int ch2 = _in.readUByte();
- int ch3 = _in.readUByte();
- int ch4 = _in.readUByte();
- return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
- }
-
- @Override
- public long readLong(){
- int b0 = _in.readUByte();
- int b1 = _in.readUByte();
- int b2 = _in.readUByte();
- int b3 = _in.readUByte();
- int b4 = _in.readUByte();
- int b5 = _in.readUByte();
- int b6 = _in.readUByte();
- int b7 = _in.readUByte();
- return (((long)b7 << 56) +
- ((long)b6 << 48) +
- ((long)b5 << 40) +
- ((long)b4 << 32) +
- ((long)b3 << 24) +
- (b2 << 16) +
- (b1 << 8) +
- (b0 << 0));
- }
-
- @Override
- public double readDouble(){
- return _in.readDouble();
- }
-
- @Override
- public void readFully(byte[] buf){
- _in.readFully(buf);
- }
-
- @Override
- public void readFully(byte[] buf, int off, int len){
- _in.readFully(buf, off, len);
- }
-
- @Override
- public void readPlain(byte[] buf, int off, int len) {
- readFully(buf, off, len);
- }
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.hssf.record.cont;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.record.ContinueRecord;
+import org.apache.poi.util.LittleEndianInput;
+
+/**
+ * A decorated {@link RecordInputStream} that can read primitive data types
+ * (short, int, long, etc.) spanned across a {@link ContinueRecord } boundary.
+ *
+ *
+ * Most records construct themselves from {@link RecordInputStream}.
+ * This class assumes that a {@link ContinueRecord} record break always occurs at the type boundary,
+ * however, it is not always so.
+ *
+ * Two attachments to Bugzilla 50779
+ * demonstrate that a CONTINUE break can appear right in between two bytes of a unicode character
+ * or between two bytes of a short. The problematic portion of the data is
+ * in a Asian Phonetic Settings Block (ExtRst) of a UnicodeString.
+ *
+ * {@link RecordInputStream} greedily requests the bytes to be read and stumbles on such files with a
+ * "Not enough data (1) to read requested (2) bytes" exception. The ContinuableRecordInput
+ * class circumvents this "type boundary" rule and reads data byte-by-byte rolling over CONTINUE if necessary.
+ *
+ *
+ *
+ * YK: For now (March 2011) this class is only used to read
+ * @link org.apache.poi.hssf.record.common.UnicodeString.ExtRst} blocks of a UnicodeString.
+ *
+ *
+ */
+public class ContinuableRecordInput implements LittleEndianInput {
+ private final RecordInputStream _in;
+
+ public ContinuableRecordInput(RecordInputStream in){
+ _in = in;
+ }
+ @Override
+ public int available(){
+ return _in.available();
+ }
+
+ @Override
+ public byte readByte(){
+ return _in.readByte();
+ }
+
+ @Override
+ public int readUByte(){
+ return _in.readUByte();
+ }
+
+ @Override
+ public short readShort(){
+ return _in.readShort();
+ }
+
+ @Override
+ public int readUShort(){
+ int ch1 = readUByte();
+ int ch2 = readUByte();
+ return (ch2 << 8) + (ch1 << 0);
+ }
+
+ @Override
+ public int readInt(){
+ int ch1 = _in.readUByte();
+ int ch2 = _in.readUByte();
+ int ch3 = _in.readUByte();
+ int ch4 = _in.readUByte();
+ return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
+ }
+
+ @Override
+ public long readLong(){
+ int b0 = _in.readUByte();
+ int b1 = _in.readUByte();
+ int b2 = _in.readUByte();
+ int b3 = _in.readUByte();
+ int b4 = _in.readUByte();
+ int b5 = _in.readUByte();
+ int b6 = _in.readUByte();
+ int b7 = _in.readUByte();
+ return (((long)b7 << 56) +
+ ((long)b6 << 48) +
+ ((long)b5 << 40) +
+ ((long)b4 << 32) +
+ ((long)b3 << 24) +
+ (b2 << 16) +
+ (b1 << 8) +
+ (b0 << 0));
+ }
+
+ @Override
+ public double readDouble(){
+ return _in.readDouble();
+ }
+
+ @Override
+ public void readFully(byte[] buf){
+ _in.readFully(buf);
+ }
+
+ @Override
+ public void readFully(byte[] buf, int off, int len){
+ _in.readFully(buf, off, len);
+ }
+
+ @Override
+ public void readPlain(byte[] buf, int off, int len) {
+ readFully(buf, off, len);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
index 31cd919ed6..093d2083a8 100644
--- a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
+++ b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
@@ -1,832 +1,832 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-
-package org.apache.poi.hssf.usermodel;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.GraphicsConfiguration;
-import java.awt.Image;
-import java.awt.Paint;
-import java.awt.Polygon;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.font.FontRenderContext;
-import java.awt.font.GlyphVector;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ImageObserver;
-import java.awt.image.RenderedImage;
-import java.awt.image.renderable.RenderableImage;
-import java.io.PrintStream;
-import java.text.AttributedCharacterIterator;
-import java.util.Arrays;
-import java.util.Map;
-
-import org.apache.poi.util.Internal;
-
-public class DummyGraphics2d extends Graphics2D {
- private BufferedImage bufimg;
- private final Graphics2D g2D;
- private final PrintStream log;
-
- public DummyGraphics2d() {
- this(System.out);
- }
-
- public DummyGraphics2d(PrintStream log) {
- bufimg = new BufferedImage(1000, 1000, 2);
- g2D = (Graphics2D)bufimg.getGraphics();
- this.log = log;
- }
-
- public DummyGraphics2d(PrintStream log, Graphics2D g2D) {
- this.g2D = g2D;
- this.log = log;
- }
-
- public void addRenderingHints(Map,?> hints) {
- String l =
- "addRenderingHinds(Map):" +
- "\n hints = " + hints;
- log.println( l );
- g2D.addRenderingHints( hints );
- }
-
- public void clip(Shape s) {
- String l =
- "clip(Shape):" +
- "\n s = " + s;
- log.println( l );
- g2D.clip( s );
- }
-
- public void draw(Shape s) {
- String l =
- "draw(Shape):" +
- "\n s = " + s;
- log.println( l );
- g2D.draw( s );
- }
-
- public void drawGlyphVector(GlyphVector g, float x, float y) {
- String l =
- "drawGlyphVector(GlyphVector, float, float):" +
- "\n g = " + g +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawGlyphVector( g, x, y );
- }
-
- public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
- String l =
- "drawImage(BufferedImage, BufferedImageOp, x, y):" +
- "\n img = " + img +
- "\n op = " + op +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawImage( img, op, x, y );
- }
-
- public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
- String l =
- "drawImage(Image,AfflineTransform,ImageObserver):" +
- "\n img = " + img +
- "\n xform = " + xform +
- "\n obs = " + obs;
- log.println( l );
- return g2D.drawImage( img, xform, obs );
- }
-
- public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
- String l =
- "drawRenderableImage(RenderableImage, AfflineTransform):" +
- "\n img = " + img +
- "\n xform = " + xform;
- log.println( l );
- g2D.drawRenderableImage( img, xform );
- }
-
- public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
- String l =
- "drawRenderedImage(RenderedImage, AffineTransform):" +
- "\n img = " + img +
- "\n xform = " + xform;
- log.println( l );
- g2D.drawRenderedImage( img, xform );
- }
-
- public void drawString(AttributedCharacterIterator iterator, float x, float y) {
- String l =
- "drawString(AttributedCharacterIterator):" +
- "\n iterator = " + iterator +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawString( iterator, x, y );
- }
-
- public void drawString(String s, float x, float y) {
- String l =
- "drawString(s,x,y):" +
- "\n s = " + s +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawString( s, x, y );
- }
-
- public void fill(Shape s) {
- String l =
- "fill(Shape):" +
- "\n s = " + s;
- log.println( l );
- g2D.fill( s );
- }
-
- public Color getBackground() {
- log.println( "getBackground():" );
- return g2D.getBackground();
- }
-
- public Composite getComposite() {
- log.println( "getComposite():" );
- return g2D.getComposite();
- }
-
- public GraphicsConfiguration getDeviceConfiguration() {
- log.println( "getDeviceConfiguration():" );
- return g2D.getDeviceConfiguration();
- }
-
- public FontRenderContext getFontRenderContext() {
- log.println( "getFontRenderContext():" );
- return g2D.getFontRenderContext();
- }
-
- public Paint getPaint() {
- log.println( "getPaint():" );
- return g2D.getPaint();
- }
-
- public Object getRenderingHint(RenderingHints.Key hintKey) {
- String l =
- "getRenderingHint(RenderingHints.Key):" +
- "\n hintKey = " + hintKey;
- log.println( l );
- return g2D.getRenderingHint( hintKey );
- }
-
- public RenderingHints getRenderingHints() {
- log.println( "getRenderingHints():" );
- return g2D.getRenderingHints();
- }
-
- public Stroke getStroke() {
- log.println( "getStroke():" );
- return g2D.getStroke();
- }
-
- public AffineTransform getTransform() {
- log.println( "getTransform():" );
- return g2D.getTransform();
- }
-
- public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
- String l =
- "hit(Rectangle, Shape, onStroke):" +
- "\n rect = " + rect +
- "\n s = " + s +
- "\n onStroke = " + onStroke;
- log.println( l );
- return g2D.hit( rect, s, onStroke );
- }
-
- public void rotate(double theta) {
- String l =
- "rotate(theta):" +
- "\n theta = " + theta;
- log.println( l );
- g2D.rotate( theta );
- }
-
- public void rotate(double theta, double x, double y) {
- String l =
- "rotate(double,double,double):" +
- "\n theta = " + theta +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.rotate( theta, x, y );
- }
-
- public void scale(double sx, double sy) {
- String l =
- "scale(double,double):" +
- "\n sx = " + sx +
- "\n sy";
- log.println( l );
- g2D.scale( sx, sy );
- }
-
- public void setBackground(Color color) {
- String l =
- "setBackground(Color):" +
- "\n color = " + color;
- log.println( l );
- g2D.setBackground( color );
- }
-
- public void setComposite(Composite comp) {
- String l =
- "setComposite(Composite):" +
- "\n comp = " + comp;
- log.println( l );
- g2D.setComposite( comp );
- }
-
- public void setPaint( Paint paint ) {
- String l =
- "setPaint(Paint):" +
- "\n paint = " + paint;
- log.println( l );
- g2D.setPaint( paint );
- }
-
- public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
- String l =
- "setRenderingHint(RenderingHints.Key, Object):" +
- "\n hintKey = " + hintKey +
- "\n hintValue = " + hintValue;
- log.println( l );
- g2D.setRenderingHint( hintKey, hintValue );
- }
-
- public void setRenderingHints(Map,?> hints) {
- String l =
- "setRenderingHints(Map):" +
- "\n hints = " + hints;
- log.println( l );
- g2D.setRenderingHints( hints );
- }
-
- public void setStroke(Stroke s) {
- String l;
- if (s instanceof BasicStroke) {
- BasicStroke bs = (BasicStroke)s;
- l = "setStroke(Stoke):" +
- "\n s = BasicStroke(" +
- "\n dash[]: "+Arrays.toString(bs.getDashArray()) +
- "\n dashPhase: "+bs.getDashPhase() +
- "\n endCap: "+bs.getEndCap() +
- "\n lineJoin: "+bs.getLineJoin() +
- "\n width: "+bs.getLineWidth() +
- "\n miterLimit: "+bs.getMiterLimit() +
- "\n )";
- } else {
- l = "setStroke(Stoke):" +
- "\n s = " + s;
- }
- log.println( l );
- g2D.setStroke( s );
- }
-
- public void setTransform(AffineTransform Tx) {
- String l =
- "setTransform():" +
- "\n Tx = " + Tx;
- log.println( l );
- g2D.setTransform( Tx );
- }
-
- public void shear(double shx, double shy) {
- String l =
- "shear(shx, dhy):" +
- "\n shx = " + shx +
- "\n shy = " + shy;
- log.println( l );
- g2D.shear( shx, shy );
- }
-
- public void transform(AffineTransform Tx) {
- String l =
- "transform(AffineTransform):" +
- "\n Tx = " + Tx;
- log.println( l );
- g2D.transform( Tx );
- }
-
- public void translate(double tx, double ty) {
- String l =
- "translate(double, double):" +
- "\n tx = " + tx +
- "\n ty = " + ty;
- log.println( l );
- g2D.translate( tx, ty );
- }
-
- public void clearRect(int x, int y, int width, int height) {
- String l =
- "clearRect(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.clearRect( x, y, width, height );
- }
-
- public void clipRect(int x, int y, int width, int height) {
- String l =
- "clipRect(int, int, int, int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "height = " + height;
- log.println( l );
- g2D.clipRect( x, y, width, height );
- }
-
- public void copyArea(int x, int y, int width, int height, int dx, int dy) {
- String l =
- "copyArea(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.copyArea( x, y, width, height, dx, dy );
- }
-
- public Graphics create() {
- log.println( "create():" );
- return g2D.create();
- }
-
- public Graphics create(int x, int y, int width, int height) {
- String l =
- "create(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- return g2D.create( x, y, width, height );
- }
-
- public void dispose() {
- log.println( "dispose():" );
- g2D.dispose();
- }
-
- public void draw3DRect(int x, int y, int width, int height, boolean raised) {
- String l =
- "draw3DRect(int,int,int,int,boolean):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n raised = " + raised;
- log.println( l );
- g2D.draw3DRect( x, y, width, height, raised );
- }
-
- public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
- String l =
- "drawArc(int,int,int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n startAngle = " + startAngle +
- "\n arcAngle = " + arcAngle;
- log.println( l );
- g2D.drawArc( x, y, width, height, startAngle, arcAngle );
- }
-
- public void drawBytes(byte data[], int offset, int length, int x, int y) {
- String l =
- "drawBytes(byte[],int,int,int,int):" +
- "\n data = " + Arrays.toString(data) +
- "\n offset = " + offset +
- "\n length = " + length +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawBytes( data, offset, length, x, y );
- }
-
- public void drawChars(char data[], int offset, int length, int x, int y) {
- String l =
- "drawChars(data,int,int,int,int):" +
- "\n data = " + Arrays.toString(data) +
- "\n offset = " + offset +
- "\n length = " + length +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawChars( data, offset, length, x, y );
- }
-
- public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
- String l =
- "drawImage(Image,int,int,int,int,int,int,int,int,ImageObserver):" +
- "\n img = " + img +
- "\n dx1 = " + dx1 +
- "\n dy1 = " + dy1 +
- "\n dx2 = " + dx2 +
- "\n dy2 = " + dy2 +
- "\n sx1 = " + sx1 +
- "\n sy1 = " + sy1 +
- "\n sx2 = " + sx2 +
- "\n sy2 = " + sy2 +
- "\n observer = " + observer;
- log.println( l );
- return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer );
- }
-
- public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
- String l =
- "drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver):" +
- "\n img = " + img +
- "\n dx1 = " + dx1 +
- "\n dy1 = " + dy1 +
- "\n dx2 = " + dx2 +
- "\n dy2 = " + dy2 +
- "\n sx1 = " + sx1 +
- "\n sy1 = " + sy1 +
- "\n sx2 = " + sx2 +
- "\n sy2 = " + sy2 +
- "\n bgcolor = " + bgcolor +
- "\n observer = " + observer;
- log.println( l );
- return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer );
- }
-
- public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
- String l =
- "drawImage(Image,int,int,Color,ImageObserver):" +
- "\n img = " + img +
- "\n x = " + x +
- "\n y = " + y +
- "\n bgcolor = " + bgcolor +
- "\n observer = " + observer;
- log.println( l );
- return g2D.drawImage( img, x, y, bgcolor, observer );
- }
-
- public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
- String l =
- "drawImage(Image,int,int,observer):" +
- "\n img = " + img +
- "\n x = " + x +
- "\n y = " + y +
- "\n observer = " + observer;
- log.println( l );
- return g2D.drawImage( img, x, y, observer );
- }
-
- public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
- String l =
- "drawImage(Image,int,int,int,int,Color,ImageObserver):" +
- "\n img = " + img +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n bgcolor = " + bgcolor +
- "\n observer = " + observer;
- log.println( l );
- return g2D.drawImage( img, x, y, width, height, bgcolor, observer );
- }
-
- public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
- String l =
- "drawImage(Image,int,int,width,height,observer):" +
- "\n img = " + img +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n observer = " + observer;
- log.println( l );
- return g2D.drawImage( img, x, y, width, height, observer );
- }
-
- public void drawLine(int x1, int y1, int x2, int y2) {
- String l =
- "drawLine(int,int,int,int):" +
- "\n x1 = " + x1 +
- "\n y1 = " + y1 +
- "\n x2 = " + x2 +
- "\n y2 = " + y2;
- log.println( l );
- g2D.drawLine( x1, y1, x2, y2 );
- }
-
- public void drawOval(int x, int y, int width, int height) {
- String l =
- "drawOval(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.drawOval( x, y, width, height );
- }
-
- public void drawPolygon(Polygon p) {
- String l =
- "drawPolygon(Polygon):" +
- "\n p = " + p;
- log.println( l );
- g2D.drawPolygon( p );
- }
-
- public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
- String l =
- "drawPolygon(int[],int[],int):" +
- "\n xPoints = " + Arrays.toString(xPoints) +
- "\n yPoints = " + Arrays.toString(yPoints) +
- "\n nPoints = " + nPoints;
- log.println( l );
- g2D.drawPolygon( xPoints, yPoints, nPoints );
- }
-
- public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
- String l =
- "drawPolyline(int[],int[],int):" +
- "\n xPoints = " + Arrays.toString(xPoints) +
- "\n yPoints = " + Arrays.toString(yPoints) +
- "\n nPoints = " + nPoints;
- log.println( l );
- g2D.drawPolyline( xPoints, yPoints, nPoints );
- }
-
- public void drawRect(int x, int y, int width, int height) {
- String l =
- "drawRect(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.drawRect( x, y, width, height );
- }
-
- public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
- String l =
- "drawRoundRect(int,int,int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n arcWidth = " + arcWidth +
- "\n arcHeight = " + arcHeight;
- log.println( l );
- g2D.drawRoundRect( x, y, width, height, arcWidth, arcHeight );
- }
-
- public void drawString(AttributedCharacterIterator iterator, int x, int y) {
- String l =
- "drawString(AttributedCharacterIterator,int,int):" +
- "\n iterator = " + iterator +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawString( iterator, x, y );
- }
-
- public void drawString(String str, int x, int y) {
- String l =
- "drawString(str,int,int):" +
- "\n str = " + str +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.drawString( str, x, y );
- }
-
- public void fill3DRect(int x, int y, int width, int height, boolean raised) {
- String l =
- "fill3DRect(int,int,int,int,boolean):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n raised = " + raised;
- log.println( l );
- g2D.fill3DRect( x, y, width, height, raised );
- }
-
- public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
- String l =
- "fillArc(int,int,int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height +
- "\n startAngle = " + startAngle +
- "\n arcAngle = " + arcAngle;
- log.println( l );
- g2D.fillArc( x, y, width, height, startAngle, arcAngle );
- }
-
- public void fillOval(int x, int y, int width, int height) {
- String l =
- "fillOval(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.fillOval( x, y, width, height );
- }
-
- public void fillPolygon(Polygon p) {
- String l =
- "fillPolygon(Polygon):" +
- "\n p = " + p;
- log.println( l );
- g2D.fillPolygon( p );
- }
-
- public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
- String l =
- "fillPolygon(int[],int[],int):" +
- "\n xPoints = " + Arrays.toString(xPoints) +
- "\n yPoints = " + Arrays.toString(yPoints) +
- "\n nPoints = " + nPoints;
- log.println( l );
- g2D.fillPolygon( xPoints, yPoints, nPoints );
- }
-
- public void fillRect(int x, int y, int width, int height) {
- String l =
- "fillRect(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.fillRect( x, y, width, height );
- }
-
- public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
- String l =
- "fillRoundRect(int,int,int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.fillRoundRect( x, y, width, height, arcWidth, arcHeight );
- }
-
- // FIXME: should be protected
- // FindBugs, category MALICIOUS_CODE, FI_PUBLIC_SHOULD_BE_PROTECTED
- // A class's finalize() method should have protected access, not public
- @Internal
- @Override
- public final void finalize() {
- log.println( "finalize():" );
- g2D.finalize(); // NOSOLAR
- super.finalize();
- }
-
- public Shape getClip() {
- log.println( "getClip():" );
- return g2D.getClip();
- }
-
- public Rectangle getClipBounds() {
- log.println( "getClipBounds():" );
- return g2D.getClipBounds();
- }
-
- public Rectangle getClipBounds(Rectangle r) {
- String l =
- "getClipBounds(Rectangle):" +
- "\n r = " + r;
- log.println( l );
- return g2D.getClipBounds( r );
- }
-
- public Color getColor() {
- log.println( "getColor():" );
- return g2D.getColor();
- }
-
- public Font getFont() {
- log.println( "getFont():" );
- return g2D.getFont();
- }
-
- public FontMetrics getFontMetrics() {
- log.println( "getFontMetrics():" );
- return g2D.getFontMetrics();
- }
-
- public FontMetrics getFontMetrics(Font f) {
- log.println( "getFontMetrics():" );
- return g2D.getFontMetrics( f );
- }
-
- public boolean hitClip(int x, int y, int width, int height) {
- String l =
- "hitClip(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- return g2D.hitClip( x, y, width, height );
- }
-
- public void setClip(Shape clip) {
- String l =
- "setClip(Shape):" +
- "\n clip = " + clip;
- log.println( l );
- g2D.setClip( clip );
- }
-
- public void setClip(int x, int y, int width, int height) {
- String l =
- "setClip(int,int,int,int):" +
- "\n x = " + x +
- "\n y = " + y +
- "\n width = " + width +
- "\n height = " + height;
- log.println( l );
- g2D.setClip( x, y, width, height );
- }
-
- public void setColor(Color c) {
- String l =
- "setColor():" +
- "\n c = " + c;
- log.println( l );
- g2D.setColor( c );
- }
-
- public void setFont(Font font) {
- String l =
- "setFont(Font):" +
- "\n font = " + font;
- log.println( l );
- g2D.setFont( font );
- }
-
- public void setPaintMode() {
- log.println( "setPaintMode():" );
- g2D.setPaintMode();
- }
-
- public void setXORMode(Color c1) {
- String l =
- "setXORMode(Color):" +
- "\n c1 = " + c1;
- log.println( l );
- g2D.setXORMode( c1 );
- }
-
- public String toString() {
- log.println( "toString():" );
- return g2D.toString();
- }
-
- public void translate(int x, int y) {
- String l =
- "translate(int,int):" +
- "\n x = " + x +
- "\n y = " + y;
- log.println( l );
- g2D.translate( x, y );
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+package org.apache.poi.hssf.usermodel;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.io.PrintStream;
+import java.text.AttributedCharacterIterator;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.poi.util.Internal;
+
+public class DummyGraphics2d extends Graphics2D {
+ private BufferedImage bufimg;
+ private final Graphics2D g2D;
+ private final PrintStream log;
+
+ public DummyGraphics2d() {
+ this(System.out);
+ }
+
+ public DummyGraphics2d(PrintStream log) {
+ bufimg = new BufferedImage(1000, 1000, 2);
+ g2D = (Graphics2D)bufimg.getGraphics();
+ this.log = log;
+ }
+
+ public DummyGraphics2d(PrintStream log, Graphics2D g2D) {
+ this.g2D = g2D;
+ this.log = log;
+ }
+
+ public void addRenderingHints(Map,?> hints) {
+ String l =
+ "addRenderingHinds(Map):" +
+ "\n hints = " + hints;
+ log.println( l );
+ g2D.addRenderingHints( hints );
+ }
+
+ public void clip(Shape s) {
+ String l =
+ "clip(Shape):" +
+ "\n s = " + s;
+ log.println( l );
+ g2D.clip( s );
+ }
+
+ public void draw(Shape s) {
+ String l =
+ "draw(Shape):" +
+ "\n s = " + s;
+ log.println( l );
+ g2D.draw( s );
+ }
+
+ public void drawGlyphVector(GlyphVector g, float x, float y) {
+ String l =
+ "drawGlyphVector(GlyphVector, float, float):" +
+ "\n g = " + g +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawGlyphVector( g, x, y );
+ }
+
+ public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
+ String l =
+ "drawImage(BufferedImage, BufferedImageOp, x, y):" +
+ "\n img = " + img +
+ "\n op = " + op +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawImage( img, op, x, y );
+ }
+
+ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
+ String l =
+ "drawImage(Image,AfflineTransform,ImageObserver):" +
+ "\n img = " + img +
+ "\n xform = " + xform +
+ "\n obs = " + obs;
+ log.println( l );
+ return g2D.drawImage( img, xform, obs );
+ }
+
+ public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+ String l =
+ "drawRenderableImage(RenderableImage, AfflineTransform):" +
+ "\n img = " + img +
+ "\n xform = " + xform;
+ log.println( l );
+ g2D.drawRenderableImage( img, xform );
+ }
+
+ public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
+ String l =
+ "drawRenderedImage(RenderedImage, AffineTransform):" +
+ "\n img = " + img +
+ "\n xform = " + xform;
+ log.println( l );
+ g2D.drawRenderedImage( img, xform );
+ }
+
+ public void drawString(AttributedCharacterIterator iterator, float x, float y) {
+ String l =
+ "drawString(AttributedCharacterIterator):" +
+ "\n iterator = " + iterator +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawString( iterator, x, y );
+ }
+
+ public void drawString(String s, float x, float y) {
+ String l =
+ "drawString(s,x,y):" +
+ "\n s = " + s +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawString( s, x, y );
+ }
+
+ public void fill(Shape s) {
+ String l =
+ "fill(Shape):" +
+ "\n s = " + s;
+ log.println( l );
+ g2D.fill( s );
+ }
+
+ public Color getBackground() {
+ log.println( "getBackground():" );
+ return g2D.getBackground();
+ }
+
+ public Composite getComposite() {
+ log.println( "getComposite():" );
+ return g2D.getComposite();
+ }
+
+ public GraphicsConfiguration getDeviceConfiguration() {
+ log.println( "getDeviceConfiguration():" );
+ return g2D.getDeviceConfiguration();
+ }
+
+ public FontRenderContext getFontRenderContext() {
+ log.println( "getFontRenderContext():" );
+ return g2D.getFontRenderContext();
+ }
+
+ public Paint getPaint() {
+ log.println( "getPaint():" );
+ return g2D.getPaint();
+ }
+
+ public Object getRenderingHint(RenderingHints.Key hintKey) {
+ String l =
+ "getRenderingHint(RenderingHints.Key):" +
+ "\n hintKey = " + hintKey;
+ log.println( l );
+ return g2D.getRenderingHint( hintKey );
+ }
+
+ public RenderingHints getRenderingHints() {
+ log.println( "getRenderingHints():" );
+ return g2D.getRenderingHints();
+ }
+
+ public Stroke getStroke() {
+ log.println( "getStroke():" );
+ return g2D.getStroke();
+ }
+
+ public AffineTransform getTransform() {
+ log.println( "getTransform():" );
+ return g2D.getTransform();
+ }
+
+ public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+ String l =
+ "hit(Rectangle, Shape, onStroke):" +
+ "\n rect = " + rect +
+ "\n s = " + s +
+ "\n onStroke = " + onStroke;
+ log.println( l );
+ return g2D.hit( rect, s, onStroke );
+ }
+
+ public void rotate(double theta) {
+ String l =
+ "rotate(theta):" +
+ "\n theta = " + theta;
+ log.println( l );
+ g2D.rotate( theta );
+ }
+
+ public void rotate(double theta, double x, double y) {
+ String l =
+ "rotate(double,double,double):" +
+ "\n theta = " + theta +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.rotate( theta, x, y );
+ }
+
+ public void scale(double sx, double sy) {
+ String l =
+ "scale(double,double):" +
+ "\n sx = " + sx +
+ "\n sy";
+ log.println( l );
+ g2D.scale( sx, sy );
+ }
+
+ public void setBackground(Color color) {
+ String l =
+ "setBackground(Color):" +
+ "\n color = " + color;
+ log.println( l );
+ g2D.setBackground( color );
+ }
+
+ public void setComposite(Composite comp) {
+ String l =
+ "setComposite(Composite):" +
+ "\n comp = " + comp;
+ log.println( l );
+ g2D.setComposite( comp );
+ }
+
+ public void setPaint( Paint paint ) {
+ String l =
+ "setPaint(Paint):" +
+ "\n paint = " + paint;
+ log.println( l );
+ g2D.setPaint( paint );
+ }
+
+ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
+ String l =
+ "setRenderingHint(RenderingHints.Key, Object):" +
+ "\n hintKey = " + hintKey +
+ "\n hintValue = " + hintValue;
+ log.println( l );
+ g2D.setRenderingHint( hintKey, hintValue );
+ }
+
+ public void setRenderingHints(Map,?> hints) {
+ String l =
+ "setRenderingHints(Map):" +
+ "\n hints = " + hints;
+ log.println( l );
+ g2D.setRenderingHints( hints );
+ }
+
+ public void setStroke(Stroke s) {
+ String l;
+ if (s instanceof BasicStroke) {
+ BasicStroke bs = (BasicStroke)s;
+ l = "setStroke(Stoke):" +
+ "\n s = BasicStroke(" +
+ "\n dash[]: "+Arrays.toString(bs.getDashArray()) +
+ "\n dashPhase: "+bs.getDashPhase() +
+ "\n endCap: "+bs.getEndCap() +
+ "\n lineJoin: "+bs.getLineJoin() +
+ "\n width: "+bs.getLineWidth() +
+ "\n miterLimit: "+bs.getMiterLimit() +
+ "\n )";
+ } else {
+ l = "setStroke(Stoke):" +
+ "\n s = " + s;
+ }
+ log.println( l );
+ g2D.setStroke( s );
+ }
+
+ public void setTransform(AffineTransform Tx) {
+ String l =
+ "setTransform():" +
+ "\n Tx = " + Tx;
+ log.println( l );
+ g2D.setTransform( Tx );
+ }
+
+ public void shear(double shx, double shy) {
+ String l =
+ "shear(shx, dhy):" +
+ "\n shx = " + shx +
+ "\n shy = " + shy;
+ log.println( l );
+ g2D.shear( shx, shy );
+ }
+
+ public void transform(AffineTransform Tx) {
+ String l =
+ "transform(AffineTransform):" +
+ "\n Tx = " + Tx;
+ log.println( l );
+ g2D.transform( Tx );
+ }
+
+ public void translate(double tx, double ty) {
+ String l =
+ "translate(double, double):" +
+ "\n tx = " + tx +
+ "\n ty = " + ty;
+ log.println( l );
+ g2D.translate( tx, ty );
+ }
+
+ public void clearRect(int x, int y, int width, int height) {
+ String l =
+ "clearRect(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.clearRect( x, y, width, height );
+ }
+
+ public void clipRect(int x, int y, int width, int height) {
+ String l =
+ "clipRect(int, int, int, int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "height = " + height;
+ log.println( l );
+ g2D.clipRect( x, y, width, height );
+ }
+
+ public void copyArea(int x, int y, int width, int height, int dx, int dy) {
+ String l =
+ "copyArea(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.copyArea( x, y, width, height, dx, dy );
+ }
+
+ public Graphics create() {
+ log.println( "create():" );
+ return g2D.create();
+ }
+
+ public Graphics create(int x, int y, int width, int height) {
+ String l =
+ "create(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ return g2D.create( x, y, width, height );
+ }
+
+ public void dispose() {
+ log.println( "dispose():" );
+ g2D.dispose();
+ }
+
+ public void draw3DRect(int x, int y, int width, int height, boolean raised) {
+ String l =
+ "draw3DRect(int,int,int,int,boolean):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n raised = " + raised;
+ log.println( l );
+ g2D.draw3DRect( x, y, width, height, raised );
+ }
+
+ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
+ String l =
+ "drawArc(int,int,int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n startAngle = " + startAngle +
+ "\n arcAngle = " + arcAngle;
+ log.println( l );
+ g2D.drawArc( x, y, width, height, startAngle, arcAngle );
+ }
+
+ public void drawBytes(byte data[], int offset, int length, int x, int y) {
+ String l =
+ "drawBytes(byte[],int,int,int,int):" +
+ "\n data = " + Arrays.toString(data) +
+ "\n offset = " + offset +
+ "\n length = " + length +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawBytes( data, offset, length, x, y );
+ }
+
+ public void drawChars(char data[], int offset, int length, int x, int y) {
+ String l =
+ "drawChars(data,int,int,int,int):" +
+ "\n data = " + Arrays.toString(data) +
+ "\n offset = " + offset +
+ "\n length = " + length +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawChars( data, offset, length, x, y );
+ }
+
+ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
+ String l =
+ "drawImage(Image,int,int,int,int,int,int,int,int,ImageObserver):" +
+ "\n img = " + img +
+ "\n dx1 = " + dx1 +
+ "\n dy1 = " + dy1 +
+ "\n dx2 = " + dx2 +
+ "\n dy2 = " + dy2 +
+ "\n sx1 = " + sx1 +
+ "\n sy1 = " + sy1 +
+ "\n sx2 = " + sx2 +
+ "\n sy2 = " + sy2 +
+ "\n observer = " + observer;
+ log.println( l );
+ return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer );
+ }
+
+ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
+ String l =
+ "drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver):" +
+ "\n img = " + img +
+ "\n dx1 = " + dx1 +
+ "\n dy1 = " + dy1 +
+ "\n dx2 = " + dx2 +
+ "\n dy2 = " + dy2 +
+ "\n sx1 = " + sx1 +
+ "\n sy1 = " + sy1 +
+ "\n sx2 = " + sx2 +
+ "\n sy2 = " + sy2 +
+ "\n bgcolor = " + bgcolor +
+ "\n observer = " + observer;
+ log.println( l );
+ return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
+ String l =
+ "drawImage(Image,int,int,Color,ImageObserver):" +
+ "\n img = " + img +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n bgcolor = " + bgcolor +
+ "\n observer = " + observer;
+ log.println( l );
+ return g2D.drawImage( img, x, y, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
+ String l =
+ "drawImage(Image,int,int,observer):" +
+ "\n img = " + img +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n observer = " + observer;
+ log.println( l );
+ return g2D.drawImage( img, x, y, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
+ String l =
+ "drawImage(Image,int,int,int,int,Color,ImageObserver):" +
+ "\n img = " + img +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n bgcolor = " + bgcolor +
+ "\n observer = " + observer;
+ log.println( l );
+ return g2D.drawImage( img, x, y, width, height, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
+ String l =
+ "drawImage(Image,int,int,width,height,observer):" +
+ "\n img = " + img +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n observer = " + observer;
+ log.println( l );
+ return g2D.drawImage( img, x, y, width, height, observer );
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2) {
+ String l =
+ "drawLine(int,int,int,int):" +
+ "\n x1 = " + x1 +
+ "\n y1 = " + y1 +
+ "\n x2 = " + x2 +
+ "\n y2 = " + y2;
+ log.println( l );
+ g2D.drawLine( x1, y1, x2, y2 );
+ }
+
+ public void drawOval(int x, int y, int width, int height) {
+ String l =
+ "drawOval(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.drawOval( x, y, width, height );
+ }
+
+ public void drawPolygon(Polygon p) {
+ String l =
+ "drawPolygon(Polygon):" +
+ "\n p = " + p;
+ log.println( l );
+ g2D.drawPolygon( p );
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
+ String l =
+ "drawPolygon(int[],int[],int):" +
+ "\n xPoints = " + Arrays.toString(xPoints) +
+ "\n yPoints = " + Arrays.toString(yPoints) +
+ "\n nPoints = " + nPoints;
+ log.println( l );
+ g2D.drawPolygon( xPoints, yPoints, nPoints );
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
+ String l =
+ "drawPolyline(int[],int[],int):" +
+ "\n xPoints = " + Arrays.toString(xPoints) +
+ "\n yPoints = " + Arrays.toString(yPoints) +
+ "\n nPoints = " + nPoints;
+ log.println( l );
+ g2D.drawPolyline( xPoints, yPoints, nPoints );
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ String l =
+ "drawRect(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.drawRect( x, y, width, height );
+ }
+
+ public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ String l =
+ "drawRoundRect(int,int,int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n arcWidth = " + arcWidth +
+ "\n arcHeight = " + arcHeight;
+ log.println( l );
+ g2D.drawRoundRect( x, y, width, height, arcWidth, arcHeight );
+ }
+
+ public void drawString(AttributedCharacterIterator iterator, int x, int y) {
+ String l =
+ "drawString(AttributedCharacterIterator,int,int):" +
+ "\n iterator = " + iterator +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawString( iterator, x, y );
+ }
+
+ public void drawString(String str, int x, int y) {
+ String l =
+ "drawString(str,int,int):" +
+ "\n str = " + str +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.drawString( str, x, y );
+ }
+
+ public void fill3DRect(int x, int y, int width, int height, boolean raised) {
+ String l =
+ "fill3DRect(int,int,int,int,boolean):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n raised = " + raised;
+ log.println( l );
+ g2D.fill3DRect( x, y, width, height, raised );
+ }
+
+ public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
+ String l =
+ "fillArc(int,int,int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height +
+ "\n startAngle = " + startAngle +
+ "\n arcAngle = " + arcAngle;
+ log.println( l );
+ g2D.fillArc( x, y, width, height, startAngle, arcAngle );
+ }
+
+ public void fillOval(int x, int y, int width, int height) {
+ String l =
+ "fillOval(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.fillOval( x, y, width, height );
+ }
+
+ public void fillPolygon(Polygon p) {
+ String l =
+ "fillPolygon(Polygon):" +
+ "\n p = " + p;
+ log.println( l );
+ g2D.fillPolygon( p );
+ }
+
+ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
+ String l =
+ "fillPolygon(int[],int[],int):" +
+ "\n xPoints = " + Arrays.toString(xPoints) +
+ "\n yPoints = " + Arrays.toString(yPoints) +
+ "\n nPoints = " + nPoints;
+ log.println( l );
+ g2D.fillPolygon( xPoints, yPoints, nPoints );
+ }
+
+ public void fillRect(int x, int y, int width, int height) {
+ String l =
+ "fillRect(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.fillRect( x, y, width, height );
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ String l =
+ "fillRoundRect(int,int,int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.fillRoundRect( x, y, width, height, arcWidth, arcHeight );
+ }
+
+ // FIXME: should be protected
+ // FindBugs, category MALICIOUS_CODE, FI_PUBLIC_SHOULD_BE_PROTECTED
+ // A class's finalize() method should have protected access, not public
+ @Internal
+ @Override
+ public final void finalize() {
+ log.println( "finalize():" );
+ g2D.finalize(); // NOSOLAR
+ super.finalize();
+ }
+
+ public Shape getClip() {
+ log.println( "getClip():" );
+ return g2D.getClip();
+ }
+
+ public Rectangle getClipBounds() {
+ log.println( "getClipBounds():" );
+ return g2D.getClipBounds();
+ }
+
+ public Rectangle getClipBounds(Rectangle r) {
+ String l =
+ "getClipBounds(Rectangle):" +
+ "\n r = " + r;
+ log.println( l );
+ return g2D.getClipBounds( r );
+ }
+
+ public Color getColor() {
+ log.println( "getColor():" );
+ return g2D.getColor();
+ }
+
+ public Font getFont() {
+ log.println( "getFont():" );
+ return g2D.getFont();
+ }
+
+ public FontMetrics getFontMetrics() {
+ log.println( "getFontMetrics():" );
+ return g2D.getFontMetrics();
+ }
+
+ public FontMetrics getFontMetrics(Font f) {
+ log.println( "getFontMetrics():" );
+ return g2D.getFontMetrics( f );
+ }
+
+ public boolean hitClip(int x, int y, int width, int height) {
+ String l =
+ "hitClip(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ return g2D.hitClip( x, y, width, height );
+ }
+
+ public void setClip(Shape clip) {
+ String l =
+ "setClip(Shape):" +
+ "\n clip = " + clip;
+ log.println( l );
+ g2D.setClip( clip );
+ }
+
+ public void setClip(int x, int y, int width, int height) {
+ String l =
+ "setClip(int,int,int,int):" +
+ "\n x = " + x +
+ "\n y = " + y +
+ "\n width = " + width +
+ "\n height = " + height;
+ log.println( l );
+ g2D.setClip( x, y, width, height );
+ }
+
+ public void setColor(Color c) {
+ String l =
+ "setColor():" +
+ "\n c = " + c;
+ log.println( l );
+ g2D.setColor( c );
+ }
+
+ public void setFont(Font font) {
+ String l =
+ "setFont(Font):" +
+ "\n font = " + font;
+ log.println( l );
+ g2D.setFont( font );
+ }
+
+ public void setPaintMode() {
+ log.println( "setPaintMode():" );
+ g2D.setPaintMode();
+ }
+
+ public void setXORMode(Color c1) {
+ String l =
+ "setXORMode(Color):" +
+ "\n c1 = " + c1;
+ log.println( l );
+ g2D.setXORMode( c1 );
+ }
+
+ public String toString() {
+ log.println( "toString():" );
+ return g2D.toString();
+ }
+
+ public void translate(int x, int y) {
+ String l =
+ "translate(int,int):" +
+ "\n x = " + x +
+ "\n y = " + y;
+ log.println( l );
+ g2D.translate( x, y );
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCombobox.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCombobox.java
index 6ebca2f4c9..5116f07620 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCombobox.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCombobox.java
@@ -1,101 +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.poi.hssf.usermodel;
-
-import org.apache.poi.ddf.*;
-import org.apache.poi.hssf.record.*;
-import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
-
-/**
- *
- */
-public class HSSFCombobox extends HSSFSimpleShape {
-
- public HSSFCombobox(EscherContainerRecord spContainer, ObjRecord objRecord) {
- super(spContainer, objRecord);
- }
-
- public HSSFCombobox(HSSFShape parent, HSSFAnchor anchor) {
- super(parent, anchor);
- super.setShapeType(OBJECT_TYPE_COMBO_BOX);
- CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0);
- cod.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_COMBO_BOX);
- }
-
- @Override
- protected TextObjectRecord createTextObjRecord() {
- return null;
- }
-
- @Override
- protected EscherContainerRecord createSpContainer() {
- EscherContainerRecord spContainer = new EscherContainerRecord();
- EscherSpRecord sp = new EscherSpRecord();
- EscherOptRecord opt = new EscherOptRecord();
- EscherClientDataRecord clientData = new EscherClientDataRecord();
-
- spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
- spContainer.setOptions((short) 0x000F);
- sp.setRecordId(EscherSpRecord.RECORD_ID);
- sp.setOptions((short) ((EscherAggregate.ST_HOSTCONTROL << 4) | 0x2));
-
- sp.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE);
- opt.setRecordId(EscherOptRecord.RECORD_ID);
- opt.addEscherProperty(new EscherBoolProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 17039620));
- opt.addEscherProperty(new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x00080008));
- opt.addEscherProperty(new EscherBoolProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000));
- opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GROUPSHAPE__PRINT, 0x00020000));
-
- HSSFClientAnchor userAnchor = (HSSFClientAnchor) getAnchor();
- userAnchor.setAnchorType(AnchorType.DONT_MOVE_DO_RESIZE);
- EscherRecord anchor = userAnchor.getEscherAnchor();
- clientData.setRecordId(EscherClientDataRecord.RECORD_ID);
- clientData.setOptions((short) 0x0000);
-
- spContainer.addChildRecord(sp);
- spContainer.addChildRecord(opt);
- spContainer.addChildRecord(anchor);
- spContainer.addChildRecord(clientData);
-
- return spContainer;
- }
-
- @Override
- protected ObjRecord createObjRecord() {
- ObjRecord obj = new ObjRecord();
- CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
- c.setObjectType(HSSFSimpleShape.OBJECT_TYPE_COMBO_BOX);
- c.setLocked(true);
- c.setPrintable(false);
- c.setAutofill(true);
- c.setAutoline(false);
- FtCblsSubRecord f = new FtCblsSubRecord();
- LbsDataSubRecord l = LbsDataSubRecord.newAutoFilterInstance();
- EndSubRecord e = new EndSubRecord();
- obj.addSubRecord(c);
- obj.addSubRecord(f);
- obj.addSubRecord(l);
- obj.addSubRecord(e);
- return obj;
- }
-
- @Override
- public void setShapeType(int shapeType) {
- throw new IllegalStateException("Shape type can not be changed in "+this.getClass().getSimpleName());
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
+import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
+
+/**
+ *
+ */
+public class HSSFCombobox extends HSSFSimpleShape {
+
+ public HSSFCombobox(EscherContainerRecord spContainer, ObjRecord objRecord) {
+ super(spContainer, objRecord);
+ }
+
+ public HSSFCombobox(HSSFShape parent, HSSFAnchor anchor) {
+ super(parent, anchor);
+ super.setShapeType(OBJECT_TYPE_COMBO_BOX);
+ CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0);
+ cod.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_COMBO_BOX);
+ }
+
+ @Override
+ protected TextObjectRecord createTextObjRecord() {
+ return null;
+ }
+
+ @Override
+ protected EscherContainerRecord createSpContainer() {
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
+ spContainer.setOptions((short) 0x000F);
+ sp.setRecordId(EscherSpRecord.RECORD_ID);
+ sp.setOptions((short) ((EscherAggregate.ST_HOSTCONTROL << 4) | 0x2));
+
+ sp.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE);
+ opt.setRecordId(EscherOptRecord.RECORD_ID);
+ opt.addEscherProperty(new EscherBoolProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 17039620));
+ opt.addEscherProperty(new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x00080008));
+ opt.addEscherProperty(new EscherBoolProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GROUPSHAPE__PRINT, 0x00020000));
+
+ HSSFClientAnchor userAnchor = (HSSFClientAnchor) getAnchor();
+ userAnchor.setAnchorType(AnchorType.DONT_MOVE_DO_RESIZE);
+ EscherRecord anchor = userAnchor.getEscherAnchor();
+ clientData.setRecordId(EscherClientDataRecord.RECORD_ID);
+ clientData.setOptions((short) 0x0000);
+
+ spContainer.addChildRecord(sp);
+ spContainer.addChildRecord(opt);
+ spContainer.addChildRecord(anchor);
+ spContainer.addChildRecord(clientData);
+
+ return spContainer;
+ }
+
+ @Override
+ protected ObjRecord createObjRecord() {
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType(HSSFSimpleShape.OBJECT_TYPE_COMBO_BOX);
+ c.setLocked(true);
+ c.setPrintable(false);
+ c.setAutofill(true);
+ c.setAutoline(false);
+ FtCblsSubRecord f = new FtCblsSubRecord();
+ LbsDataSubRecord l = LbsDataSubRecord.newAutoFilterInstance();
+ EndSubRecord e = new EndSubRecord();
+ obj.addSubRecord(c);
+ obj.addSubRecord(f);
+ obj.addSubRecord(l);
+ obj.addSubRecord(e);
+ return obj;
+ }
+
+ @Override
+ public void setShapeType(int shapeType) {
+ throw new IllegalStateException("Shape type can not be changed in "+this.getClass().getSimpleName());
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java b/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
index 084a6bfe04..237ac657cc 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
@@ -29,12 +29,12 @@ public class HSSFHyperlink implements Hyperlink {
/**
* Low-level record object that stores the actual hyperlink data
*/
- final protected HyperlinkRecord record;
-
- /**
- * If we create a new hyperlink remember its type
- */
- final protected HyperlinkType link_type;
+ final protected HyperlinkRecord record;
+
+ /**
+ * If we create a new hyperlink remember its type
+ */
+ final protected HyperlinkType link_type;
/**
* Construct a new hyperlink
@@ -50,7 +50,7 @@ public class HSSFHyperlink implements Hyperlink {
{
this(HyperlinkType.forInt(type));
}
-
+
/**
* Construct a new hyperlink
*
@@ -203,14 +203,14 @@ public class HSSFHyperlink implements Hyperlink {
@Override
public void setLastColumn(int col){
record.setLastColumn((short)col);
- }
-
- /**
- * Hyperlink address. Depending on the hyperlink type it can be URL, e-mail, path to a file, etc.
- *
- * @return the address of this hyperlink
+ }
+
+ /**
+ * Hyperlink address. Depending on the hyperlink type it can be URL, e-mail, path to a file, etc.
+ *
+ * @return the address of this hyperlink
*/
- @Override
+ @Override
public String getAddress(){
return record.getAddress();
}
@@ -218,32 +218,32 @@ public class HSSFHyperlink implements Hyperlink {
return record.getTextMark();
}
- /**
- * Convenience method equivalent to {@link #setAddress(String)}
- *
- * @param textMark the place in worksheet this hyperlink refers to, e.g. 'Target Sheet'!A1'
- */
- public void setTextMark(String textMark) {
- record.setTextMark(textMark);
+ /**
+ * Convenience method equivalent to {@link #setAddress(String)}
+ *
+ * @param textMark the place in worksheet this hyperlink refers to, e.g. 'Target Sheet'!A1'
+ */
+ public void setTextMark(String textMark) {
+ record.setTextMark(textMark);
}
public String getShortFilename(){
return record.getShortFilename();
}
- /**
- * Convenience method equivalent to {@link #setAddress(String)}
- *
- * @param shortFilename the path to a file this hyperlink points to, e.g. 'readme.txt'
- */
- public void setShortFilename(String shortFilename) {
- record.setShortFilename(shortFilename);
- }
-
- /**
- * Hyperlink address. Depending on the hyperlink type it can be URL, e-mail, path to a file, etc.
- *
- * @param address the address of this hyperlink
+ /**
+ * Convenience method equivalent to {@link #setAddress(String)}
+ *
+ * @param shortFilename the path to a file this hyperlink points to, e.g. 'readme.txt'
+ */
+ public void setShortFilename(String shortFilename) {
+ record.setShortFilename(shortFilename);
+ }
+
+ /**
+ * Hyperlink address. Depending on the hyperlink type it can be URL, e-mail, path to a file, etc.
+ *
+ * @param address the address of this hyperlink
*/
- @Override
+ @Override
public void setAddress(String address){
record.setAddress(address);
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java
index 5135078c00..5659545745 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java
@@ -1,142 +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.poi.hssf.usermodel;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.poi.ddf.EscherClientDataRecord;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherOptRecord;
-import org.apache.poi.ddf.EscherProperties;
-import org.apache.poi.ddf.EscherProperty;
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherTextboxRecord;
-import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
-import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
-import org.apache.poi.hssf.record.EscherAggregate;
-import org.apache.poi.hssf.record.ObjRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.SubRecord;
-import org.apache.poi.hssf.record.TextObjectRecord;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.util.RecordFormatException;
-
-/**
- * Factory class for producing Excel Shapes from Escher records
- */
-public class HSSFShapeFactory {
- /**
- * build shape tree from escher container
- * @param container root escher container from which escher records must be taken
- * @param agg - EscherAggregate
- * @param out - shape container to which shapes must be added
- * @param root - node to create HSSFObjectData shapes
- */
- public static void createShapeTree(EscherContainerRecord container, EscherAggregate agg, HSSFShapeContainer out, DirectoryNode root) {
- if (container.getRecordId() == EscherContainerRecord.SPGR_CONTAINER) {
- ObjRecord obj = null;
- EscherClientDataRecord clientData = ((EscherContainerRecord) container.getChild(0)).getChildById(EscherClientDataRecord.RECORD_ID);
- if (null != clientData) {
- obj = (ObjRecord) agg.getShapeToObjMapping().get(clientData);
- }
- HSSFShapeGroup group = new HSSFShapeGroup(container, obj);
- List children = container.getChildContainers();
- // skip the first child record, it is group descriptor
- for (int i = 0; i < children.size(); i++) {
- EscherContainerRecord spContainer = children.get(i);
- if (i != 0) {
- createShapeTree(spContainer, agg, group, root);
- }
- }
- out.addShape(group);
- } else if (container.getRecordId() == EscherContainerRecord.SP_CONTAINER) {
- Map shapeToObj = agg.getShapeToObjMapping();
- ObjRecord objRecord = null;
- TextObjectRecord txtRecord = null;
-
- for (EscherRecord record : container) {
- switch (record.getRecordId()) {
- case EscherClientDataRecord.RECORD_ID:
- objRecord = (ObjRecord) shapeToObj.get(record);
- break;
- case EscherTextboxRecord.RECORD_ID:
- txtRecord = (TextObjectRecord) shapeToObj.get(record);
- break;
- default:
- break;
- }
- }
- if (objRecord == null) {
- throw new RecordFormatException("EscherClientDataRecord can't be found.");
- }
- if (isEmbeddedObject(objRecord)) {
- HSSFObjectData objectData = new HSSFObjectData(container, objRecord, root);
- out.addShape(objectData);
- return;
- }
- CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord) objRecord.getSubRecords().get(0);
- final HSSFShape shape;
- switch (cmo.getObjectType()) {
- case CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE:
- shape = new HSSFPicture(container, objRecord);
- break;
- case CommonObjectDataSubRecord.OBJECT_TYPE_RECTANGLE:
- shape = new HSSFSimpleShape(container, objRecord, txtRecord);
- break;
- case CommonObjectDataSubRecord.OBJECT_TYPE_LINE:
- shape = new HSSFSimpleShape(container, objRecord);
- break;
- case CommonObjectDataSubRecord.OBJECT_TYPE_COMBO_BOX:
- shape = new HSSFCombobox(container, objRecord);
- break;
- case CommonObjectDataSubRecord.OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING:
- EscherOptRecord optRecord = container.getChildById(EscherOptRecord.RECORD_ID);
- if(optRecord == null) {
- shape = new HSSFSimpleShape(container, objRecord, txtRecord);
- } else {
- EscherProperty property = optRecord.lookup(EscherProperties.GEOMETRY__VERTICES);
- if (null != property) {
- shape = new HSSFPolygon(container, objRecord, txtRecord);
- } else {
- shape = new HSSFSimpleShape(container, objRecord, txtRecord);
- }
- }
- break;
- case CommonObjectDataSubRecord.OBJECT_TYPE_TEXT:
- shape = new HSSFTextbox(container, objRecord, txtRecord);
- break;
- case CommonObjectDataSubRecord.OBJECT_TYPE_COMMENT:
- shape = new HSSFComment(container, objRecord, txtRecord, agg.getNoteRecordByObj(objRecord));
- break;
- default:
- shape = new HSSFSimpleShape(container, objRecord, txtRecord);
- }
- out.addShape(shape);
- }
- }
-
- private static boolean isEmbeddedObject(ObjRecord obj) {
- for (SubRecord sub : obj.getSubRecords()) {
- if (sub instanceof EmbeddedObjectRefSubRecord) {
- return true;
- }
- }
- return false;
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.poi.hssf.usermodel;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.ddf.EscherClientDataRecord;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherOptRecord;
+import org.apache.poi.ddf.EscherProperties;
+import org.apache.poi.ddf.EscherProperty;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherTextboxRecord;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.SubRecord;
+import org.apache.poi.hssf.record.TextObjectRecord;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.util.RecordFormatException;
+
+/**
+ * Factory class for producing Excel Shapes from Escher records
+ */
+public class HSSFShapeFactory {
+ /**
+ * build shape tree from escher container
+ * @param container root escher container from which escher records must be taken
+ * @param agg - EscherAggregate
+ * @param out - shape container to which shapes must be added
+ * @param root - node to create HSSFObjectData shapes
+ */
+ public static void createShapeTree(EscherContainerRecord container, EscherAggregate agg, HSSFShapeContainer out, DirectoryNode root) {
+ if (container.getRecordId() == EscherContainerRecord.SPGR_CONTAINER) {
+ ObjRecord obj = null;
+ EscherClientDataRecord clientData = ((EscherContainerRecord) container.getChild(0)).getChildById(EscherClientDataRecord.RECORD_ID);
+ if (null != clientData) {
+ obj = (ObjRecord) agg.getShapeToObjMapping().get(clientData);
+ }
+ HSSFShapeGroup group = new HSSFShapeGroup(container, obj);
+ List children = container.getChildContainers();
+ // skip the first child record, it is group descriptor
+ for (int i = 0; i < children.size(); i++) {
+ EscherContainerRecord spContainer = children.get(i);
+ if (i != 0) {
+ createShapeTree(spContainer, agg, group, root);
+ }
+ }
+ out.addShape(group);
+ } else if (container.getRecordId() == EscherContainerRecord.SP_CONTAINER) {
+ Map shapeToObj = agg.getShapeToObjMapping();
+ ObjRecord objRecord = null;
+ TextObjectRecord txtRecord = null;
+
+ for (EscherRecord record : container) {
+ switch (record.getRecordId()) {
+ case EscherClientDataRecord.RECORD_ID:
+ objRecord = (ObjRecord) shapeToObj.get(record);
+ break;
+ case EscherTextboxRecord.RECORD_ID:
+ txtRecord = (TextObjectRecord) shapeToObj.get(record);
+ break;
+ default:
+ break;
+ }
+ }
+ if (objRecord == null) {
+ throw new RecordFormatException("EscherClientDataRecord can't be found.");
+ }
+ if (isEmbeddedObject(objRecord)) {
+ HSSFObjectData objectData = new HSSFObjectData(container, objRecord, root);
+ out.addShape(objectData);
+ return;
+ }
+ CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord) objRecord.getSubRecords().get(0);
+ final HSSFShape shape;
+ switch (cmo.getObjectType()) {
+ case CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE:
+ shape = new HSSFPicture(container, objRecord);
+ break;
+ case CommonObjectDataSubRecord.OBJECT_TYPE_RECTANGLE:
+ shape = new HSSFSimpleShape(container, objRecord, txtRecord);
+ break;
+ case CommonObjectDataSubRecord.OBJECT_TYPE_LINE:
+ shape = new HSSFSimpleShape(container, objRecord);
+ break;
+ case CommonObjectDataSubRecord.OBJECT_TYPE_COMBO_BOX:
+ shape = new HSSFCombobox(container, objRecord);
+ break;
+ case CommonObjectDataSubRecord.OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING:
+ EscherOptRecord optRecord = container.getChildById(EscherOptRecord.RECORD_ID);
+ if(optRecord == null) {
+ shape = new HSSFSimpleShape(container, objRecord, txtRecord);
+ } else {
+ EscherProperty property = optRecord.lookup(EscherProperties.GEOMETRY__VERTICES);
+ if (null != property) {
+ shape = new HSSFPolygon(container, objRecord, txtRecord);
+ } else {
+ shape = new HSSFSimpleShape(container, objRecord, txtRecord);
+ }
+ }
+ break;
+ case CommonObjectDataSubRecord.OBJECT_TYPE_TEXT:
+ shape = new HSSFTextbox(container, objRecord, txtRecord);
+ break;
+ case CommonObjectDataSubRecord.OBJECT_TYPE_COMMENT:
+ shape = new HSSFComment(container, objRecord, txtRecord, agg.getNoteRecordByObj(objRecord));
+ break;
+ default:
+ shape = new HSSFSimpleShape(container, objRecord, txtRecord);
+ }
+ out.addShape(shape);
+ }
+ }
+
+ private static boolean isEmbeddedObject(ObjRecord obj) {
+ for (SubRecord sub : obj.getSubRecords()) {
+ if (sub instanceof EmbeddedObjectRefSubRecord) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/ChainingMode.java b/src/java/org/apache/poi/poifs/crypt/ChainingMode.java
index 7fccccfb25..88c2c8493c 100644
--- a/src/java/org/apache/poi/poifs/crypt/ChainingMode.java
+++ b/src/java/org/apache/poi/poifs/crypt/ChainingMode.java
@@ -1,33 +1,33 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.poifs.crypt;
-
-public enum ChainingMode {
- // ecb - only for standard encryption
- ecb("ECB", 1),
- cbc("CBC", 2),
- /* Cipher feedback chaining (CFB), with an 8-bit window */
- cfb("CFB8", 3);
-
- public final String jceId;
- public final int ecmaId;
- ChainingMode(String jceId, int ecmaId) {
- this.jceId = jceId;
- this.ecmaId = ecmaId;
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt;
+
+public enum ChainingMode {
+ // ecb - only for standard encryption
+ ecb("ECB", 1),
+ cbc("CBC", 2),
+ /* Cipher feedback chaining (CFB), with an 8-bit window */
+ cfb("CFB8", 3);
+
+ public final String jceId;
+ public final int ecmaId;
+ ChainingMode(String jceId, int ecmaId) {
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
index df27d58484..ffc4e600f9 100644
--- a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
+++ b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
@@ -1,280 +1,280 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.poifs.crypt;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndianInputStream;
-
-@Internal
-public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
- private final int chunkSize;
- private final int chunkBits;
-
- private final long size;
- private final byte[] chunk, plain;
- private final Cipher cipher;
-
- private int lastIndex;
- private long pos;
- private boolean chunkIsValid = false;
-
- public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize)
- throws GeneralSecurityException {
- this(stream, size, chunkSize, 0);
- }
-
- public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize, int initialPos)
- throws GeneralSecurityException {
- super(stream);
- this.size = size;
- this.pos = initialPos;
- this.chunkSize = chunkSize;
- int cs = chunkSize == -1 ? 4096 : chunkSize;
- this.chunk = new byte[cs];
- this.plain = new byte[cs];
- this.chunkBits = Integer.bitCount(chunk.length-1);
- this.lastIndex = (int)(pos >> chunkBits);
- this.cipher = initCipherForBlock(null, lastIndex);
- }
-
- public final Cipher initCipherForBlock(int block) throws IOException, GeneralSecurityException {
- if (chunkSize != -1) {
- throw new GeneralSecurityException("the cipher block can only be set for streaming encryption, e.g. CryptoAPI...");
- }
-
- chunkIsValid = false;
- return initCipherForBlock(cipher, block);
- }
-
- protected abstract Cipher initCipherForBlock(Cipher existing, int block)
- throws GeneralSecurityException;
-
- @Override
- public int read() throws IOException {
- byte[] b = new byte[1];
- if (read(b) == 1) {
- return b[0];
- }
- return -1;
- }
-
- // do not implement! -> recursion
- // public int read(byte[] b) throws IOException;
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return read(b, off, len, false);
- }
-
- private int read(byte[] b, int off, int len, boolean readPlain) throws IOException {
- int total = 0;
-
- if (available() <= 0) {
- return -1;
- }
-
- final int chunkMask = getChunkMask();
- while (len > 0) {
- if (!chunkIsValid) {
- try {
- nextChunk();
- chunkIsValid = true;
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException(e.getMessage(), e);
- }
- }
- int count = (int)(chunk.length - (pos & chunkMask));
- int avail = available();
- if (avail == 0) {
- return total;
- }
- count = Math.min(avail, Math.min(count, len));
-
- System.arraycopy(readPlain ? plain : chunk, (int)(pos & chunkMask), b, off, count);
-
- off += count;
- len -= count;
- pos += count;
- if ((pos & chunkMask) == 0) {
- chunkIsValid = false;
- }
- total += count;
- }
-
- return total;
- }
-
- @Override
- public long skip(final long n) throws IOException {
- long start = pos;
- long skip = Math.min(remainingBytes(), n);
-
- if ((((pos + skip) ^ start) & ~getChunkMask()) != 0) {
- chunkIsValid = false;
- }
- pos += skip;
- return skip;
- }
-
- @Override
- public int available() {
- return remainingBytes();
- }
-
- /**
- * Helper method for forbidden available call - we know the size beforehand, so it's ok ...
- *
- * @return the remaining byte until EOF
- */
- private int remainingBytes() {
- return (int)(size - pos);
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
- @Override
- public synchronized void mark(int readlimit) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public synchronized void reset() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- protected int getChunkMask() {
- return chunk.length-1;
- }
-
- private void nextChunk() throws GeneralSecurityException, IOException {
- if (chunkSize != -1) {
- int index = (int)(pos >> chunkBits);
- initCipherForBlock(cipher, index);
-
- if (lastIndex != index) {
- long skipN = (index - lastIndex) << chunkBits;
- if (super.skip(skipN) < skipN) {
- throw new EOFException("buffer underrun");
- };
- }
-
- lastIndex = index + 1;
- }
-
- final int todo = (int)Math.min(size, chunk.length);
- int readBytes = 0, totalBytes = 0;
- do {
- readBytes = super.read(plain, totalBytes, todo-totalBytes);
- totalBytes += Math.max(0, readBytes);
- } while (readBytes != -1 && totalBytes < todo);
-
- if (readBytes == -1 && pos+totalBytes < size && size < Integer.MAX_VALUE) {
- throw new EOFException("buffer underrun");
- }
-
- System.arraycopy(plain, 0, chunk, 0, totalBytes);
-
- invokeCipher(totalBytes, totalBytes == chunkSize);
- }
-
- /**
- * Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
- * and uses it's own implementation
- *
- * @throws BadPaddingException
- * @throws IllegalBlockSizeException
- * @throws ShortBufferException
- */
- protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException {
- if (doFinal) {
- return cipher.doFinal(chunk, 0, totalBytes, chunk);
- } else {
- return cipher.update(chunk, 0, totalBytes, chunk);
- }
- }
-
- /**
- * Used when BIFF header fields (sid, size) are being read. The internal
- * {@link Cipher} instance must step even when unencrypted bytes are read
- *
- */
- @Override
- public void readPlain(byte b[], int off, int len) {
- if (len <= 0) {
- return;
- }
-
- try {
- int readBytes, total = 0;
- do {
- readBytes = read(b, off, len, true);
- total += Math.max(0, readBytes);
- } while (readBytes > -1 && total < len);
-
- if (total < len) {
- throw new EOFException("buffer underrun");
- }
- } catch (IOException e) {
- // need to wrap checked exception, because of LittleEndianInput interface :(
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Some ciphers (actually just XOR) are based on the record size,
- * which needs to be set before decryption
- *
- * @param recordSize the size of the next record
- */
- public void setNextRecordSize(int recordSize) {
- }
-
- /**
- * @return the chunk bytes
- */
- protected byte[] getChunk() {
- return chunk;
- }
-
- /**
- * @return the plain bytes
- */
- protected byte[] getPlain() {
- return plain;
- }
-
- /**
- * @return the absolute position in the stream
- */
- public long getPos() {
- return pos;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianInputStream;
+
+@Internal
+public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
+ private final int chunkSize;
+ private final int chunkBits;
+
+ private final long size;
+ private final byte[] chunk, plain;
+ private final Cipher cipher;
+
+ private int lastIndex;
+ private long pos;
+ private boolean chunkIsValid = false;
+
+ public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize)
+ throws GeneralSecurityException {
+ this(stream, size, chunkSize, 0);
+ }
+
+ public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize, int initialPos)
+ throws GeneralSecurityException {
+ super(stream);
+ this.size = size;
+ this.pos = initialPos;
+ this.chunkSize = chunkSize;
+ int cs = chunkSize == -1 ? 4096 : chunkSize;
+ this.chunk = new byte[cs];
+ this.plain = new byte[cs];
+ this.chunkBits = Integer.bitCount(chunk.length-1);
+ this.lastIndex = (int)(pos >> chunkBits);
+ this.cipher = initCipherForBlock(null, lastIndex);
+ }
+
+ public final Cipher initCipherForBlock(int block) throws IOException, GeneralSecurityException {
+ if (chunkSize != -1) {
+ throw new GeneralSecurityException("the cipher block can only be set for streaming encryption, e.g. CryptoAPI...");
+ }
+
+ chunkIsValid = false;
+ return initCipherForBlock(cipher, block);
+ }
+
+ protected abstract Cipher initCipherForBlock(Cipher existing, int block)
+ throws GeneralSecurityException;
+
+ @Override
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ if (read(b) == 1) {
+ return b[0];
+ }
+ return -1;
+ }
+
+ // do not implement! -> recursion
+ // public int read(byte[] b) throws IOException;
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return read(b, off, len, false);
+ }
+
+ private int read(byte[] b, int off, int len, boolean readPlain) throws IOException {
+ int total = 0;
+
+ if (available() <= 0) {
+ return -1;
+ }
+
+ final int chunkMask = getChunkMask();
+ while (len > 0) {
+ if (!chunkIsValid) {
+ try {
+ nextChunk();
+ chunkIsValid = true;
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException(e.getMessage(), e);
+ }
+ }
+ int count = (int)(chunk.length - (pos & chunkMask));
+ int avail = available();
+ if (avail == 0) {
+ return total;
+ }
+ count = Math.min(avail, Math.min(count, len));
+
+ System.arraycopy(readPlain ? plain : chunk, (int)(pos & chunkMask), b, off, count);
+
+ off += count;
+ len -= count;
+ pos += count;
+ if ((pos & chunkMask) == 0) {
+ chunkIsValid = false;
+ }
+ total += count;
+ }
+
+ return total;
+ }
+
+ @Override
+ public long skip(final long n) throws IOException {
+ long start = pos;
+ long skip = Math.min(remainingBytes(), n);
+
+ if ((((pos + skip) ^ start) & ~getChunkMask()) != 0) {
+ chunkIsValid = false;
+ }
+ pos += skip;
+ return skip;
+ }
+
+ @Override
+ public int available() {
+ return remainingBytes();
+ }
+
+ /**
+ * Helper method for forbidden available call - we know the size beforehand, so it's ok ...
+ *
+ * @return the remaining byte until EOF
+ */
+ private int remainingBytes() {
+ return (int)(size - pos);
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ protected int getChunkMask() {
+ return chunk.length-1;
+ }
+
+ private void nextChunk() throws GeneralSecurityException, IOException {
+ if (chunkSize != -1) {
+ int index = (int)(pos >> chunkBits);
+ initCipherForBlock(cipher, index);
+
+ if (lastIndex != index) {
+ long skipN = (index - lastIndex) << chunkBits;
+ if (super.skip(skipN) < skipN) {
+ throw new EOFException("buffer underrun");
+ };
+ }
+
+ lastIndex = index + 1;
+ }
+
+ final int todo = (int)Math.min(size, chunk.length);
+ int readBytes = 0, totalBytes = 0;
+ do {
+ readBytes = super.read(plain, totalBytes, todo-totalBytes);
+ totalBytes += Math.max(0, readBytes);
+ } while (readBytes != -1 && totalBytes < todo);
+
+ if (readBytes == -1 && pos+totalBytes < size && size < Integer.MAX_VALUE) {
+ throw new EOFException("buffer underrun");
+ }
+
+ System.arraycopy(plain, 0, chunk, 0, totalBytes);
+
+ invokeCipher(totalBytes, totalBytes == chunkSize);
+ }
+
+ /**
+ * Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
+ * and uses it's own implementation
+ *
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws ShortBufferException
+ */
+ protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException {
+ if (doFinal) {
+ return cipher.doFinal(chunk, 0, totalBytes, chunk);
+ } else {
+ return cipher.update(chunk, 0, totalBytes, chunk);
+ }
+ }
+
+ /**
+ * Used when BIFF header fields (sid, size) are being read. The internal
+ * {@link Cipher} instance must step even when unencrypted bytes are read
+ *
+ */
+ @Override
+ public void readPlain(byte b[], int off, int len) {
+ if (len <= 0) {
+ return;
+ }
+
+ try {
+ int readBytes, total = 0;
+ do {
+ readBytes = read(b, off, len, true);
+ total += Math.max(0, readBytes);
+ } while (readBytes > -1 && total < len);
+
+ if (total < len) {
+ throw new EOFException("buffer underrun");
+ }
+ } catch (IOException e) {
+ // need to wrap checked exception, because of LittleEndianInput interface :(
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Some ciphers (actually just XOR) are based on the record size,
+ * which needs to be set before decryption
+ *
+ * @param recordSize the size of the next record
+ */
+ public void setNextRecordSize(int recordSize) {
+ }
+
+ /**
+ * @return the chunk bytes
+ */
+ protected byte[] getChunk() {
+ return chunk;
+ }
+
+ /**
+ * @return the plain bytes
+ */
+ protected byte[] getPlain() {
+ return plain;
+ }
+
+ /**
+ * @return the absolute position in the stream
+ */
+ public long getPos() {
+ return pos;
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java
index 6b18f0d9f2..dbbc8a5d53 100644
--- a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java
+++ b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java
@@ -1,300 +1,300 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.poifs.crypt;
-
-import static org.apache.poi.poifs.crypt.Decryptor.DEFAULT_POIFS_ENTRY;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.util.BitSet;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
-import org.apache.poi.poifs.filesystem.POIFSWriterListener;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.util.TempFile;
-
-@Internal
-public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
- private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class);
- private static final int STREAMING = -1;
-
- private final int chunkSize;
- private final int chunkBits;
-
- private final byte[] chunk;
- private final BitSet plainByteFlags;
- private final File fileOut;
- private final DirectoryNode dir;
-
- private long pos;
- private long totalPos;
- private long written;
-
- // the cipher can't be final, because for the last chunk we change the padding
- // and therefore need to change the cipher too
- private Cipher cipher;
-
- public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException {
- super(null);
- this.chunkSize = chunkSize;
- int cs = chunkSize == STREAMING ? 4096 : chunkSize;
- this.chunk = new byte[cs];
- this.plainByteFlags = new BitSet(cs);
- this.chunkBits = Integer.bitCount(cs-1);
- this.fileOut = TempFile.createTempFile("encrypted_package", "crypt");
- this.fileOut.deleteOnExit();
- this.out = new FileOutputStream(fileOut);
- this.dir = dir;
- this.cipher = initCipherForBlock(null, 0, false);
- }
-
- public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException {
- super(stream);
- this.chunkSize = chunkSize;
- int cs = chunkSize == STREAMING ? 4096 : chunkSize;
- this.chunk = new byte[cs];
- this.plainByteFlags = new BitSet(cs);
- this.chunkBits = Integer.bitCount(cs-1);
- this.fileOut = null;
- this.dir = null;
- this.cipher = initCipherForBlock(null, 0, false);
- }
-
- public final Cipher initCipherForBlock(int block, boolean lastChunk) throws IOException, GeneralSecurityException {
- return initCipherForBlock(cipher, block, lastChunk);
- }
-
- protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk)
- throws IOException, GeneralSecurityException;
-
- protected abstract void calculateChecksum(File fileOut, int oleStreamSize)
- throws GeneralSecurityException, IOException;
-
- protected abstract void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
- throws IOException, GeneralSecurityException;
-
- @Override
- public void write(int b) throws IOException {
- write(new byte[]{(byte)b});
- }
-
- @Override
- public void write(byte[] b) throws IOException {
- write(b, 0, b.length);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- write(b, off, len, false);
- }
-
- public void writePlain(byte[] b, int off, int len) throws IOException {
- write(b, off, len, true);
- }
-
- protected void write(byte[] b, int off, int len, boolean writePlain) throws IOException {
- if (len == 0) {
- return;
- }
-
- if (len < 0 || b.length < off+len) {
- throw new IOException("not enough bytes in your input buffer");
- }
-
- final int chunkMask = getChunkMask();
- while (len > 0) {
- int posInChunk = (int)(pos & chunkMask);
- int nextLen = Math.min(chunk.length-posInChunk, len);
- System.arraycopy(b, off, chunk, posInChunk, nextLen);
- if (writePlain) {
- plainByteFlags.set(posInChunk, posInChunk+nextLen);
- }
- pos += nextLen;
- totalPos += nextLen;
- off += nextLen;
- len -= nextLen;
- if ((pos & chunkMask) == 0) {
- writeChunk(len > 0);
- }
- }
- }
-
- protected int getChunkMask() {
- return chunk.length-1;
- }
-
- protected void writeChunk(boolean continued) throws IOException {
- if (pos == 0 || totalPos == written) {
- return;
- }
-
- int posInChunk = (int)(pos & getChunkMask());
-
- // normally posInChunk is 0, i.e. on the next chunk (-> index-1)
- // but if called on close(), posInChunk is somewhere within the chunk data
- int index = (int)(pos >> chunkBits);
- boolean lastChunk;
- if (posInChunk==0) {
- index--;
- posInChunk = chunk.length;
- lastChunk = false;
- } else {
- // pad the last chunk
- lastChunk = true;
- }
-
- int ciLen;
- try {
- boolean doFinal = true;
- long oldPos = pos;
- // reset stream (not only) in case we were interrupted by plain stream parts
- // this also needs to be set to prevent an endless loop
- pos = 0;
- if (chunkSize == STREAMING) {
- if (continued) {
- doFinal = false;
- }
- } else {
- cipher = initCipherForBlock(cipher, index, lastChunk);
- // restore pos - only streaming chunks will be reset
- pos = oldPos;
- }
- ciLen = invokeCipher(posInChunk, doFinal);
- } catch (GeneralSecurityException e) {
- throw new IOException("can't re-/initialize cipher", e);
- }
-
- out.write(chunk, 0, ciLen);
- plainByteFlags.clear();
- written += ciLen;
- }
-
- /**
- * Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
- * and uses it's own implementation
- *
- * @throws BadPaddingException
- * @throws IllegalBlockSizeException
- * @throws ShortBufferException
- */
- protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException {
- byte plain[] = (plainByteFlags.isEmpty()) ? null : chunk.clone();
-
- int ciLen = (doFinal)
- ? cipher.doFinal(chunk, 0, posInChunk, chunk)
- : cipher.update(chunk, 0, posInChunk, chunk);
-
- for (int i = plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = plainByteFlags.nextSetBit(i+1)) {
- chunk[i] = plain[i];
- }
-
- return ciLen;
- }
-
- @Override
- public void close() throws IOException {
- try {
- writeChunk(false);
-
- super.close();
-
- if (fileOut != null) {
- int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
- calculateChecksum(fileOut, (int)pos);
- dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());
- createEncryptionInfoEntry(dir, fileOut);
- }
- } catch (GeneralSecurityException e) {
- throw new IOException(e);
- }
- }
-
- protected byte[] getChunk() {
- return chunk;
- }
-
- protected BitSet getPlainByteFlags() {
- return plainByteFlags;
- }
-
- protected long getPos() {
- return pos;
- }
-
- protected long getTotalPos() {
- return totalPos;
- }
-
- /**
- * Some ciphers (actually just XOR) are based on the record size,
- * which needs to be set before encryption
- *
- * @param recordSize the size of the next record
- * @param isPlain {@code true} if the record is unencrypted
- */
- public void setNextRecordSize(int recordSize, boolean isPlain) {
- }
-
- private class EncryptedPackageWriter implements POIFSWriterListener {
- @Override
- public void processPOIFSWriterEvent(POIFSWriterEvent event) {
- try {
- OutputStream os = event.getStream();
-
- // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
- // encrypted within the EncryptedData field, not including the size of the StreamSize field.
- // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
- // value, depending on the block size of the chosen encryption algorithm
- byte buf[] = new byte[LittleEndianConsts.LONG_SIZE];
- LittleEndian.putLong(buf, 0, pos);
- os.write(buf);
-
- FileInputStream fis = new FileInputStream(fileOut);
- try {
- IOUtils.copy(fis, os);
- } finally {
- fis.close();
- }
-
- os.close();
-
- if (!fileOut.delete()) {
- LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);
- }
- } catch (IOException e) {
- throw new EncryptedDocumentException(e);
- }
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt;
+
+import static org.apache.poi.poifs.crypt.Decryptor.DEFAULT_POIFS_ENTRY;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.util.BitSet;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
+import org.apache.poi.poifs.filesystem.POIFSWriterListener;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.TempFile;
+
+@Internal
+public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
+ private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class);
+ private static final int STREAMING = -1;
+
+ private final int chunkSize;
+ private final int chunkBits;
+
+ private final byte[] chunk;
+ private final BitSet plainByteFlags;
+ private final File fileOut;
+ private final DirectoryNode dir;
+
+ private long pos;
+ private long totalPos;
+ private long written;
+
+ // the cipher can't be final, because for the last chunk we change the padding
+ // and therefore need to change the cipher too
+ private Cipher cipher;
+
+ public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException {
+ super(null);
+ this.chunkSize = chunkSize;
+ int cs = chunkSize == STREAMING ? 4096 : chunkSize;
+ this.chunk = new byte[cs];
+ this.plainByteFlags = new BitSet(cs);
+ this.chunkBits = Integer.bitCount(cs-1);
+ this.fileOut = TempFile.createTempFile("encrypted_package", "crypt");
+ this.fileOut.deleteOnExit();
+ this.out = new FileOutputStream(fileOut);
+ this.dir = dir;
+ this.cipher = initCipherForBlock(null, 0, false);
+ }
+
+ public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException {
+ super(stream);
+ this.chunkSize = chunkSize;
+ int cs = chunkSize == STREAMING ? 4096 : chunkSize;
+ this.chunk = new byte[cs];
+ this.plainByteFlags = new BitSet(cs);
+ this.chunkBits = Integer.bitCount(cs-1);
+ this.fileOut = null;
+ this.dir = null;
+ this.cipher = initCipherForBlock(null, 0, false);
+ }
+
+ public final Cipher initCipherForBlock(int block, boolean lastChunk) throws IOException, GeneralSecurityException {
+ return initCipherForBlock(cipher, block, lastChunk);
+ }
+
+ protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk)
+ throws IOException, GeneralSecurityException;
+
+ protected abstract void calculateChecksum(File fileOut, int oleStreamSize)
+ throws GeneralSecurityException, IOException;
+
+ protected abstract void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
+ throws IOException, GeneralSecurityException;
+
+ @Override
+ public void write(int b) throws IOException {
+ write(new byte[]{(byte)b});
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ write(b, off, len, false);
+ }
+
+ public void writePlain(byte[] b, int off, int len) throws IOException {
+ write(b, off, len, true);
+ }
+
+ protected void write(byte[] b, int off, int len, boolean writePlain) throws IOException {
+ if (len == 0) {
+ return;
+ }
+
+ if (len < 0 || b.length < off+len) {
+ throw new IOException("not enough bytes in your input buffer");
+ }
+
+ final int chunkMask = getChunkMask();
+ while (len > 0) {
+ int posInChunk = (int)(pos & chunkMask);
+ int nextLen = Math.min(chunk.length-posInChunk, len);
+ System.arraycopy(b, off, chunk, posInChunk, nextLen);
+ if (writePlain) {
+ plainByteFlags.set(posInChunk, posInChunk+nextLen);
+ }
+ pos += nextLen;
+ totalPos += nextLen;
+ off += nextLen;
+ len -= nextLen;
+ if ((pos & chunkMask) == 0) {
+ writeChunk(len > 0);
+ }
+ }
+ }
+
+ protected int getChunkMask() {
+ return chunk.length-1;
+ }
+
+ protected void writeChunk(boolean continued) throws IOException {
+ if (pos == 0 || totalPos == written) {
+ return;
+ }
+
+ int posInChunk = (int)(pos & getChunkMask());
+
+ // normally posInChunk is 0, i.e. on the next chunk (-> index-1)
+ // but if called on close(), posInChunk is somewhere within the chunk data
+ int index = (int)(pos >> chunkBits);
+ boolean lastChunk;
+ if (posInChunk==0) {
+ index--;
+ posInChunk = chunk.length;
+ lastChunk = false;
+ } else {
+ // pad the last chunk
+ lastChunk = true;
+ }
+
+ int ciLen;
+ try {
+ boolean doFinal = true;
+ long oldPos = pos;
+ // reset stream (not only) in case we were interrupted by plain stream parts
+ // this also needs to be set to prevent an endless loop
+ pos = 0;
+ if (chunkSize == STREAMING) {
+ if (continued) {
+ doFinal = false;
+ }
+ } else {
+ cipher = initCipherForBlock(cipher, index, lastChunk);
+ // restore pos - only streaming chunks will be reset
+ pos = oldPos;
+ }
+ ciLen = invokeCipher(posInChunk, doFinal);
+ } catch (GeneralSecurityException e) {
+ throw new IOException("can't re-/initialize cipher", e);
+ }
+
+ out.write(chunk, 0, ciLen);
+ plainByteFlags.clear();
+ written += ciLen;
+ }
+
+ /**
+ * Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
+ * and uses it's own implementation
+ *
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws ShortBufferException
+ */
+ protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException {
+ byte plain[] = (plainByteFlags.isEmpty()) ? null : chunk.clone();
+
+ int ciLen = (doFinal)
+ ? cipher.doFinal(chunk, 0, posInChunk, chunk)
+ : cipher.update(chunk, 0, posInChunk, chunk);
+
+ for (int i = plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = plainByteFlags.nextSetBit(i+1)) {
+ chunk[i] = plain[i];
+ }
+
+ return ciLen;
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ writeChunk(false);
+
+ super.close();
+
+ if (fileOut != null) {
+ int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
+ calculateChecksum(fileOut, (int)pos);
+ dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());
+ createEncryptionInfoEntry(dir, fileOut);
+ }
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ }
+ }
+
+ protected byte[] getChunk() {
+ return chunk;
+ }
+
+ protected BitSet getPlainByteFlags() {
+ return plainByteFlags;
+ }
+
+ protected long getPos() {
+ return pos;
+ }
+
+ protected long getTotalPos() {
+ return totalPos;
+ }
+
+ /**
+ * Some ciphers (actually just XOR) are based on the record size,
+ * which needs to be set before encryption
+ *
+ * @param recordSize the size of the next record
+ * @param isPlain {@code true} if the record is unencrypted
+ */
+ public void setNextRecordSize(int recordSize, boolean isPlain) {
+ }
+
+ private class EncryptedPackageWriter implements POIFSWriterListener {
+ @Override
+ public void processPOIFSWriterEvent(POIFSWriterEvent event) {
+ try {
+ OutputStream os = event.getStream();
+
+ // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
+ // encrypted within the EncryptedData field, not including the size of the StreamSize field.
+ // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
+ // value, depending on the block size of the chosen encryption algorithm
+ byte buf[] = new byte[LittleEndianConsts.LONG_SIZE];
+ LittleEndian.putLong(buf, 0, pos);
+ os.write(buf);
+
+ FileInputStream fis = new FileInputStream(fileOut);
+ try {
+ IOUtils.copy(fis, os);
+ } finally {
+ fis.close();
+ }
+
+ os.close();
+
+ if (!fileOut.delete()) {
+ LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);
+ }
+ } catch (IOException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java b/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java
index c5887354db..6da96d8524 100644
--- a/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java
+++ b/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java
@@ -1,78 +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.poi.poifs.crypt;
-
-import org.apache.poi.EncryptedDocumentException;
-
-public enum CipherAlgorithm {
- // key size for rc4: 0x00000028 - 0x00000080 (inclusive) with 8-bit increments
- // no block size, because its a streaming cipher
- rc4(CipherProvider.rc4, "RC4", 0x6801, 0x40, new int[]{0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,0x80}, -1, 20, "RC4", false),
- // aes has always a block size of 128 - only its keysize may vary
- aes128(CipherProvider.aes, "AES", 0x660E, 128, new int[]{128}, 16, 32, "AES", false),
- aes192(CipherProvider.aes, "AES", 0x660F, 192, new int[]{192}, 16, 32, "AES", false),
- aes256(CipherProvider.aes, "AES", 0x6610, 256, new int[]{256}, 16, 32, "AES", false),
- rc2(null, "RC2", -1, 0x80, new int[]{0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,0x80}, 8, 20, "RC2", false),
- des(null, "DES", -1, 64, new int[]{64}, 8/*for 56-bit*/, 32, "DES", false),
- // desx is not supported. Not sure, if it can be simulated by des3 somehow
- des3(null, "DESede", -1, 192, new int[]{192}, 8, 32, "3DES", false),
- // need bouncycastle provider for this one ...
- // see http://stackoverflow.com/questions/4436397/3des-des-encryption-using-the-jce-generating-an-acceptable-key
- des3_112(null, "DESede", -1, 128, new int[]{128}, 8, 32, "3DES_112", true),
- // only for digital signatures
- rsa(null, "RSA", -1, 1024, new int[]{1024, 2048, 3072, 4096}, -1, -1, "", false);
-
- public final CipherProvider provider;
- public final String jceId;
- public final int ecmaId;
- public final int defaultKeySize;
- public final int allowedKeySize[];
- public final int blockSize;
- public final int encryptedVerifierHashLength;
- public final String xmlId;
- public final boolean needsBouncyCastle;
-
- CipherAlgorithm(CipherProvider provider, String jceId, int ecmaId, int defaultKeySize, int allowedKeySize[], int blockSize, int encryptedVerifierHashLength, String xmlId, boolean needsBouncyCastle) {
- this.provider = provider;
- this.jceId = jceId;
- this.ecmaId = ecmaId;
- this.defaultKeySize = defaultKeySize;
- this.allowedKeySize = allowedKeySize.clone();
- this.blockSize = blockSize;
- this.encryptedVerifierHashLength = encryptedVerifierHashLength;
- this.xmlId = xmlId;
- this.needsBouncyCastle = needsBouncyCastle;
- }
-
- public static CipherAlgorithm fromEcmaId(int ecmaId) {
- for (CipherAlgorithm ca : CipherAlgorithm.values()) {
- if (ca.ecmaId == ecmaId) return ca;
- }
- throw new EncryptedDocumentException("cipher algorithm " + ecmaId + " not found");
- }
-
- public static CipherAlgorithm fromXmlId(String xmlId, int keySize) {
- for (CipherAlgorithm ca : CipherAlgorithm.values()) {
- if (!ca.xmlId.equals(xmlId)) continue;
- for (int ks : ca.allowedKeySize) {
- if (ks == keySize) return ca;
- }
- }
- throw new EncryptedDocumentException("cipher algorithm " + xmlId + "/" + keySize + " not found");
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt;
+
+import org.apache.poi.EncryptedDocumentException;
+
+public enum CipherAlgorithm {
+ // key size for rc4: 0x00000028 - 0x00000080 (inclusive) with 8-bit increments
+ // no block size, because its a streaming cipher
+ rc4(CipherProvider.rc4, "RC4", 0x6801, 0x40, new int[]{0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,0x80}, -1, 20, "RC4", false),
+ // aes has always a block size of 128 - only its keysize may vary
+ aes128(CipherProvider.aes, "AES", 0x660E, 128, new int[]{128}, 16, 32, "AES", false),
+ aes192(CipherProvider.aes, "AES", 0x660F, 192, new int[]{192}, 16, 32, "AES", false),
+ aes256(CipherProvider.aes, "AES", 0x6610, 256, new int[]{256}, 16, 32, "AES", false),
+ rc2(null, "RC2", -1, 0x80, new int[]{0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,0x80}, 8, 20, "RC2", false),
+ des(null, "DES", -1, 64, new int[]{64}, 8/*for 56-bit*/, 32, "DES", false),
+ // desx is not supported. Not sure, if it can be simulated by des3 somehow
+ des3(null, "DESede", -1, 192, new int[]{192}, 8, 32, "3DES", false),
+ // need bouncycastle provider for this one ...
+ // see http://stackoverflow.com/questions/4436397/3des-des-encryption-using-the-jce-generating-an-acceptable-key
+ des3_112(null, "DESede", -1, 128, new int[]{128}, 8, 32, "3DES_112", true),
+ // only for digital signatures
+ rsa(null, "RSA", -1, 1024, new int[]{1024, 2048, 3072, 4096}, -1, -1, "", false);
+
+ public final CipherProvider provider;
+ public final String jceId;
+ public final int ecmaId;
+ public final int defaultKeySize;
+ public final int allowedKeySize[];
+ public final int blockSize;
+ public final int encryptedVerifierHashLength;
+ public final String xmlId;
+ public final boolean needsBouncyCastle;
+
+ CipherAlgorithm(CipherProvider provider, String jceId, int ecmaId, int defaultKeySize, int allowedKeySize[], int blockSize, int encryptedVerifierHashLength, String xmlId, boolean needsBouncyCastle) {
+ this.provider = provider;
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ this.defaultKeySize = defaultKeySize;
+ this.allowedKeySize = allowedKeySize.clone();
+ this.blockSize = blockSize;
+ this.encryptedVerifierHashLength = encryptedVerifierHashLength;
+ this.xmlId = xmlId;
+ this.needsBouncyCastle = needsBouncyCastle;
+ }
+
+ public static CipherAlgorithm fromEcmaId(int ecmaId) {
+ for (CipherAlgorithm ca : CipherAlgorithm.values()) {
+ if (ca.ecmaId == ecmaId) return ca;
+ }
+ throw new EncryptedDocumentException("cipher algorithm " + ecmaId + " not found");
+ }
+
+ public static CipherAlgorithm fromXmlId(String xmlId, int keySize) {
+ for (CipherAlgorithm ca : CipherAlgorithm.values()) {
+ if (!ca.xmlId.equals(xmlId)) continue;
+ for (int ks : ca.allowedKeySize) {
+ if (ks == keySize) return ca;
+ }
+ }
+ throw new EncryptedDocumentException("cipher algorithm " + xmlId + "/" + keySize + " not found");
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/poifs/crypt/CipherProvider.java b/src/java/org/apache/poi/poifs/crypt/CipherProvider.java
index 5ffe1d33ae..ac2dbafd0a 100644
--- a/src/java/org/apache/poi/poifs/crypt/CipherProvider.java
+++ b/src/java/org/apache/poi/poifs/crypt/CipherProvider.java
@@ -1,41 +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.poi.poifs.crypt;
-
-import org.apache.poi.EncryptedDocumentException;
-
-public enum CipherProvider {
- rc4("RC4", 1, "Microsoft Base Cryptographic Provider v1.0"),
- aes("AES", 0x18, "Microsoft Enhanced RSA and AES Cryptographic Provider");
-
- public static CipherProvider fromEcmaId(int ecmaId) {
- for (CipherProvider cp : CipherProvider.values()) {
- if (cp.ecmaId == ecmaId) return cp;
- }
- throw new EncryptedDocumentException("cipher provider not found");
- }
-
- public final String jceId;
- public final int ecmaId;
- public final String cipherProviderName;
- CipherProvider(String jceId, int ecmaId, String cipherProviderName) {
- this.jceId = jceId;
- this.ecmaId = ecmaId;
- this.cipherProviderName = cipherProviderName;
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt;
+
+import org.apache.poi.EncryptedDocumentException;
+
+public enum CipherProvider {
+ rc4("RC4", 1, "Microsoft Base Cryptographic Provider v1.0"),
+ aes("AES", 0x18, "Microsoft Enhanced RSA and AES Cryptographic Provider");
+
+ public static CipherProvider fromEcmaId(int ecmaId) {
+ for (CipherProvider cp : CipherProvider.values()) {
+ if (cp.ecmaId == ecmaId) return cp;
+ }
+ throw new EncryptedDocumentException("cipher provider not found");
+ }
+
+ public final String jceId;
+ public final int ecmaId;
+ public final String cipherProviderName;
+ CipherProvider(String jceId, int ecmaId, String cipherProviderName) {
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ this.cipherProviderName = cipherProviderName;
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java b/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
index b35d2c59cd..f7265d1ed3 100644
--- a/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
+++ b/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
@@ -1,576 +1,576 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.poifs.crypt;
-
-import java.nio.charset.Charset;
-import java.security.DigestException;
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.MessageDigest;
-import java.security.Provider;
-import java.security.Security;
-import java.security.spec.AlgorithmParameterSpec;
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.util.Internal;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.StringUtil;
-
-/**
- * Helper functions used for standard and agile encryption
- */
-@Internal
-public class CryptoFunctions {
- /**
- *
The encryption key for ECMA-376 document encryption [ECMA-376] using agile
- * encryption MUST be generated by using the following method, which is derived from PKCS #5:
- * Password-Based Cryptography Version 2.0 [RFC2898].
- *
- *
Let H() be a hashing algorithm as determined by the PasswordKeyEncryptor.hashAlgorithm
- * element, H_n be the hash data of the n-th iteration, and a plus sign (+) represent concatenation.
- * The password MUST be provided as an array of Unicode characters. Limitations on the length of the
- * password and the characters used by the password are implementation-dependent.
- * The initial password hash is generated as follows:
- *
- *
- *
H_0 = H(salt + password)
- *
- *
The salt used MUST be generated randomly. The salt MUST be stored in the
- * PasswordKeyEncryptor.saltValue element contained within the \EncryptionInfo stream as
- * specified in section 2.3.4.10. The hash is then iterated by using the following approach:
- *
- *
H_n = H(iterator + H_n-1)
- *
- *
where iterator is an unsigned 32-bit value that is initially set to 0x00000000 and then incremented
- * monotonically on each iteration until PasswordKey.spinCount iterations have been performed.
- * The value of iterator on the last iteration MUST be one less than PasswordKey.spinCount.
- *
- *
For POI, H_final will be calculated by {@link #generateKey(byte[],HashAlgorithm,byte[],int)}
- *
- * @param password
- * @param hashAlgorithm
- * @param salt
- * @param spinCount
- * @return the hashed password
- */
- public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount) {
- return hashPassword(password, hashAlgorithm, salt, spinCount, true);
- }
-
- /**
- * Generalized method for read and write protection hash generation.
- * The difference is, read protection uses the order iterator then hash in the hash loop, whereas write protection
- * uses first the last hash value and then the current iterator value
- *
- * @param password
- * @param hashAlgorithm
- * @param salt
- * @param spinCount
- * @param iteratorFirst if true, the iterator is hashed before the n-1 hash value,
- * if false the n-1 hash value is applied first
- * @return the hashed password
- */
- public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount, boolean iteratorFirst) {
- // If no password was given, use the default
- if (password == null) {
- password = Decryptor.DEFAULT_PASSWORD;
- }
-
- MessageDigest hashAlg = getMessageDigest(hashAlgorithm);
-
- hashAlg.update(salt);
- byte[] hash = hashAlg.digest(StringUtil.getToUnicodeLE(password));
- byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
-
- byte[] first = (iteratorFirst ? iterator : hash);
- byte[] second = (iteratorFirst ? hash : iterator);
-
- try {
- for (int i = 0; i < spinCount; i++) {
- LittleEndian.putInt(iterator, 0, i);
- hashAlg.reset();
- hashAlg.update(first);
- hashAlg.update(second);
- hashAlg.digest(hash, 0, hash.length); // don't create hash buffer everytime new
- }
- } catch (DigestException e) {
- throw new EncryptedDocumentException("error in password hashing");
- }
-
- return hash;
- }
-
- /**
- *
Initialization vectors are used in all cases for agile encryption. An initialization vector MUST be
- * generated by using the following method, where H() is a hash function that MUST be the same as
- * specified in section 2.3.4.11 and a plus sign (+) represents concatenation:
- *
- *
If a blockKey is provided, let IV be a hash of the KeySalt and the following value:
- * {@code blockKey: IV = H(KeySalt + blockKey)}
- *
If a blockKey is not provided, let IV be equal to the following value:
- * {@code KeySalt:IV = KeySalt}
- *
If the number of bytes in the value of IV is less than the the value of the blockSize attribute
- * corresponding to the cipherAlgorithm attribute, pad the array of bytes by appending 0x36 until
- * the array is blockSize bytes. If the array of bytes is larger than blockSize bytes, truncate the
- * array to blockSize bytes.
The final hash data that is used for an encryption key is then generated by using the following
- * method:
- *
- *
H_final = H(H_n + blockKey)
- *
- *
where blockKey represents an array of bytes used to prevent two different blocks from encrypting
- * to the same cipher text.
- *
- *
If the size of the resulting H_final is smaller than that of PasswordKeyEncryptor.keyBits, the key
- * MUST be padded by appending bytes with a value of 0x36. If the hash value is larger in size than
- * PasswordKeyEncryptor.keyBits, the key is obtained by truncating the hash value.
- *
- * @param passwordHash
- * @param hashAlgorithm
- * @param blockKey
- * @param keySize
- * @return intermediate key
- */
- public static byte[] generateKey(byte[] passwordHash, HashAlgorithm hashAlgorithm, byte[] blockKey, int keySize) {
- MessageDigest hashAlgo = getMessageDigest(hashAlgorithm);
- hashAlgo.update(passwordHash);
- byte[] key = hashAlgo.digest(blockKey);
- return getBlock36(key, keySize);
- }
-
- /**
- * Initialize a new cipher object with the given cipher properties and no padding
- * If the given algorithm is not implemented in the JCE, it will try to load it from the bouncy castle
- * provider.
- *
- * @param key the secrect key
- * @param cipherAlgorithm the cipher algorithm
- * @param chain the chaining mode
- * @param vec the initialization vector (IV), can be null
- * @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
- * @return the requested cipher
- * @throws GeneralSecurityException
- * @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
- * which depends on a missing bouncy castle provider
- */
- public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode) {
- return getCipher(key, cipherAlgorithm, chain, vec, cipherMode, null);
- }
-
- /**
- * Initialize a new cipher object with the given cipher properties
- * If the given algorithm is not implemented in the JCE, it will try to load it from the bouncy castle
- * provider.
- *
- * @param key the secrect key
- * @param cipherAlgorithm the cipher algorithm
- * @param chain the chaining mode
- * @param vec the initialization vector (IV), can be null
- * @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
- * @param padding the padding (null = NOPADDING, ANSIX923Padding, PKCS5Padding, PKCS7Padding, ISO10126Padding, ...)
- * @return the requested cipher
- * @throws GeneralSecurityException
- * @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
- * which depends on a missing bouncy castle provider
- */
- public static Cipher getCipher(Key key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
- int keySizeInBytes = key.getEncoded().length;
- if (padding == null) padding = "NoPadding";
-
- try {
- // Ensure the JCE policies files allow for this sized key
- if (Cipher.getMaxAllowedKeyLength(cipherAlgorithm.jceId) < keySizeInBytes*8) {
- throw new EncryptedDocumentException("Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files");
- }
-
- Cipher cipher;
- if (cipherAlgorithm == CipherAlgorithm.rc4) {
- cipher = Cipher.getInstance(cipherAlgorithm.jceId);
- } else if (cipherAlgorithm.needsBouncyCastle) {
- registerBouncyCastle();
- cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding, "BC");
- } else {
- cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding);
- }
-
- if (vec == null) {
- cipher.init(cipherMode, key);
- } else {
- AlgorithmParameterSpec aps;
- if (cipherAlgorithm == CipherAlgorithm.rc2) {
- aps = new RC2ParameterSpec(key.getEncoded().length*8, vec);
- } else {
- aps = new IvParameterSpec(vec);
- }
- cipher.init(cipherMode, key, aps);
- }
- return cipher;
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException(e);
- }
- }
-
- /**
- * Returns a new byte array with a truncated to the given size.
- * If the hash has less then size bytes, it will be filled with 0x36-bytes
- *
- * @param hash the to be truncated/filled hash byte array
- * @param size the size of the returned byte array
- * @return the padded hash
- */
- private static byte[] getBlock36(byte[] hash, int size) {
- return getBlockX(hash, size, (byte)0x36);
- }
-
- /**
- * Returns a new byte array with a truncated to the given size.
- * If the hash has less then size bytes, it will be filled with 0-bytes
- *
- * @param hash the to be truncated/filled hash byte array
- * @param size the size of the returned byte array
- * @return the padded hash
- */
- public static byte[] getBlock0(byte[] hash, int size) {
- return getBlockX(hash, size, (byte)0);
- }
-
- private static byte[] getBlockX(byte[] hash, int size, byte fill) {
- if (hash.length == size) return hash;
-
- byte[] result = new byte[size];
- Arrays.fill(result, fill);
- System.arraycopy(hash, 0, result, 0, Math.min(result.length, hash.length));
- return result;
- }
-
- public static MessageDigest getMessageDigest(HashAlgorithm hashAlgorithm) {
- try {
- if (hashAlgorithm.needsBouncyCastle) {
- registerBouncyCastle();
- return MessageDigest.getInstance(hashAlgorithm.jceId, "BC");
- } else {
- return MessageDigest.getInstance(hashAlgorithm.jceId);
- }
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException("hash algo not supported", e);
- }
- }
-
- public static Mac getMac(HashAlgorithm hashAlgorithm) {
- try {
- if (hashAlgorithm.needsBouncyCastle) {
- registerBouncyCastle();
- return Mac.getInstance(hashAlgorithm.jceHmacId, "BC");
- } else {
- return Mac.getInstance(hashAlgorithm.jceHmacId);
- }
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException("hmac algo not supported", e);
- }
- }
-
- @SuppressWarnings("unchecked")
- public static void registerBouncyCastle() {
- if (Security.getProvider("BC") != null) {
- return;
- }
-
- try {
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- String bcProviderName = "org.bouncycastle.jce.provider.BouncyCastleProvider";
- Class clazz = (Class)cl.loadClass(bcProviderName);
- Security.addProvider(clazz.newInstance());
- } catch (Exception e) {
- throw new EncryptedDocumentException("Only the BouncyCastle provider supports your encryption settings - please add it to the classpath.", e);
- }
- }
-
- private static final int INITIAL_CODE_ARRAY[] = {
- 0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE,
- 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A,
- 0x4EC3
- };
-
- private static final byte PAD_ARRAY[] = {
- (byte)0xBB, (byte)0xFF, (byte)0xFF, (byte)0xBA, (byte)0xFF,
- (byte)0xFF, (byte)0xB9, (byte)0x80, (byte)0x00, (byte)0xBE,
- (byte)0x0F, (byte)0x00, (byte)0xBF, (byte)0x0F, (byte)0x00
- };
-
- private static final int ENCRYPTION_MATRIX[][] = {
- /* char 1 */ {0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09},
- /* char 2 */ {0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF},
- /* char 3 */ {0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0},
- /* char 4 */ {0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40},
- /* char 5 */ {0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5},
- /* char 6 */ {0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A},
- /* char 7 */ {0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9},
- /* char 8 */ {0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0},
- /* char 9 */ {0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC},
- /* char 10 */ {0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10},
- /* char 11 */ {0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168},
- /* char 12 */ {0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C},
- /* char 13 */ {0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD},
- /* char 14 */ {0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC},
- /* char 15 */ {0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4}
- };
-
- /**
- * Create the verifier for xor obfuscation (method 1)
- *
- * @see 2.3.7.1 Binary Document Password Verifier Derivation Method 1
- * @see 2.3.7.4 Binary Document Password Verifier Derivation Method 2
- * @see Part 4 - Markup Language Reference - Ecma International - 3.2.12 fileSharing
- *
- * @param password the password
- * @return the verifier (actually a short value)
- */
- public static int createXorVerifier1(String password) {
- byte[] arrByteChars = toAnsiPassword(password);
-
- // SET Verifier TO 0x0000
- short verifier = 0;
-
- if (!"".equals(password)) {
- // FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
- for (int i = arrByteChars.length-1; i >= 0; i--) {
- // SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
- verifier = rotateLeftBase15Bit(verifier);
- verifier ^= arrByteChars[i];
- }
-
- // as we haven't prepended the password length into the input array
- // we need to do it now separately ...
- verifier = rotateLeftBase15Bit(verifier);
- verifier ^= arrByteChars.length;
-
- // RETURN Verifier BITWISE XOR 0xCE4B
- verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
- }
-
- return verifier & 0xFFFF;
- }
-
- /**
- * This method generates the xor verifier for word documents < 2007 (method 2).
- * Its output will be used as password input for the newer word generations which
- * utilize a real hashing algorithm like sha1.
- *
- * @param password the password
- * @return the hashed password
- *
- * @see 2.3.7.4 Binary Document Password Verifier Derivation Method 2
- * @see How to set the editing restrictions in Word using Open XML SDK 2.0
- * @see Funny: How the new powerful cryptography implemented in Word 2007 turns it into a perfect tool for document password removal.
- */
- public static int createXorVerifier2(String password) {
- //Array to hold Key Values
- byte[] generatedKey = new byte[4];
-
- //Maximum length of the password is 15 chars.
- final int maxPasswordLength = 15;
-
- if (!"".equals(password)) {
- // Truncate the password to 15 characters
- password = password.substring(0, Math.min(password.length(), maxPasswordLength));
-
- byte[] arrByteChars = toAnsiPassword(password);
-
- // Compute the high-order word of the new key:
-
- // --> Initialize from the initial code array (see below), depending on the passwords length.
- int highOrderWord = INITIAL_CODE_ARRAY[arrByteChars.length - 1];
-
- // --> For each character in the password:
- // --> For every bit in the character, starting with the least significant and progressing to (but excluding)
- // the most significant, if the bit is set, XOR the keys high-order word with the corresponding word from
- // the Encryption Matrix
- for (int i = 0; i < arrByteChars.length; i++) {
- int tmp = maxPasswordLength - arrByteChars.length + i;
- for (int intBit = 0; intBit < 7; intBit++) {
- if ((arrByteChars[i] & (0x0001 << intBit)) != 0) {
- highOrderWord ^= ENCRYPTION_MATRIX[tmp][intBit];
- }
- }
- }
-
- // Compute the low-order word of the new key:
- int verifier = createXorVerifier1(password);
-
- // The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64],
- // and that value shall be hashed as defined by the attribute values.
-
- LittleEndian.putShort(generatedKey, 0, (short)verifier);
- LittleEndian.putShort(generatedKey, 2, (short)highOrderWord);
- }
-
- return LittleEndian.getInt(generatedKey);
- }
-
- /**
- * This method generates the xored-hashed password for word documents < 2007.
- */
- public static String xorHashPassword(String password) {
- int hashedPassword = createXorVerifier2(password);
- return String.format(Locale.ROOT, "%1$08X", hashedPassword);
- }
-
- /**
- * Convenience function which returns the reversed xored-hashed password for further
- * processing in word documents 2007 and newer, which utilize a real hashing algorithm like sha1.
- */
- public static String xorHashPasswordReversed(String password) {
- int hashedPassword = createXorVerifier2(password);
-
- return String.format(Locale.ROOT, "%1$02X%2$02X%3$02X%4$02X"
- , ( hashedPassword >>> 0 ) & 0xFF
- , ( hashedPassword >>> 8 ) & 0xFF
- , ( hashedPassword >>> 16 ) & 0xFF
- , ( hashedPassword >>> 24 ) & 0xFF
- );
- }
-
- /**
- * Create the xor key for xor obfuscation, which is used to create the xor array (method 1)
- *
- * @see 2.3.7.2 Binary Document XOR Array Initialization Method 1
- * @see 2.3.7.4 Binary Document Password Verifier Derivation Method 2
- *
- * @param password the password
- * @return the xor key
- */
- public static int createXorKey1(String password) {
- // the xor key for method 1 is part of the verifier for method 2
- // so we simply chop it from there
- return createXorVerifier2(password) >>> 16;
- }
-
- /**
- * Creates an byte array for xor obfuscation (method 1)
- *
- * @see 2.3.7.2 Binary Document XOR Array Initialization Method 1
- * @see Libre Office implementation
- *
- * @param password the password
- * @return the byte array for xor obfuscation
- */
- public static byte[] createXorArray1(String password) {
- if (password.length() > 15) {
- password = password.substring(0, 15);
- }
- byte passBytes[] = password.getBytes(Charset.forName("ASCII"));
-
- // this code is based on the libre office implementation.
- // The MS-OFFCRYPTO misses some infos about the various rotation sizes
- byte obfuscationArray[] = new byte[16];
- System.arraycopy(passBytes, 0, obfuscationArray, 0, passBytes.length);
- System.arraycopy(PAD_ARRAY, 0, obfuscationArray, passBytes.length, PAD_ARRAY.length-passBytes.length+1);
-
- int xorKey = createXorKey1(password);
-
- // rotation of key values is application dependent - Excel = 2 / Word = 7
- int nRotateSize = 2;
-
- byte baseKeyLE[] = { (byte)(xorKey & 0xFF), (byte)((xorKey >>> 8) & 0xFF) };
- for (int i=0; iPart 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection)
- */
- private static byte[] toAnsiPassword(String password) {
- // TODO: charset conversion (see ecma spec)
-
- // Get the single-byte values by iterating through the Unicode characters.
- // For each character, if the low byte is not equal to 0, take it.
- // Otherwise, take the high byte.
- byte[] arrByteChars = new byte[password.length()];
-
- for (int i = 0; i < password.length(); i++) {
- int intTemp = password.charAt(i);
- byte lowByte = (byte)(intTemp & 0xFF);
- byte highByte = (byte)((intTemp >>> 8) & 0xFF);
- arrByteChars[i] = (lowByte != 0 ? lowByte : highByte);
- }
-
- return arrByteChars;
- }
-
- private static byte rotateLeft(byte bits, int shift) {
- return (byte)(((bits & 0xff) << shift) | ((bits & 0xff) >>> (8 - shift)));
- }
-
- private static short rotateLeftBase15Bit(short verifier) {
- /*
- * IF (Verifier BITWISE AND 0x4000) is 0x0000
- * SET Intermediate1 TO 0
- * ELSE
- * SET Intermediate1 TO 1
- * ENDIF
- */
- short intermediate1 = (short)(((verifier & 0x4000) == 0) ? 0 : 1);
- /*
- * SET Intermediate2 TO Verifier MULTIPLED BY 2
- * SET most significant bit of Intermediate2 TO 0
- */
- short intermediate2 = (short)((verifier<<1) & 0x7FFF);
- /*
- * SET Intermediate3 TO Intermediate1 BITWISE OR Intermediate2
- */
- short intermediate3 = (short)(intermediate1 | intermediate2);
- return intermediate3;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt;
+
+import java.nio.charset.Charset;
+import java.security.DigestException;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * Helper functions used for standard and agile encryption
+ */
+@Internal
+public class CryptoFunctions {
+ /**
+ *
The encryption key for ECMA-376 document encryption [ECMA-376] using agile
+ * encryption MUST be generated by using the following method, which is derived from PKCS #5:
+ * Password-Based Cryptography Version 2.0 [RFC2898].
+ *
+ *
Let H() be a hashing algorithm as determined by the PasswordKeyEncryptor.hashAlgorithm
+ * element, H_n be the hash data of the n-th iteration, and a plus sign (+) represent concatenation.
+ * The password MUST be provided as an array of Unicode characters. Limitations on the length of the
+ * password and the characters used by the password are implementation-dependent.
+ * The initial password hash is generated as follows:
+ *
+ *
+ *
H_0 = H(salt + password)
+ *
+ *
The salt used MUST be generated randomly. The salt MUST be stored in the
+ * PasswordKeyEncryptor.saltValue element contained within the \EncryptionInfo stream as
+ * specified in section 2.3.4.10. The hash is then iterated by using the following approach:
+ *
+ *
H_n = H(iterator + H_n-1)
+ *
+ *
where iterator is an unsigned 32-bit value that is initially set to 0x00000000 and then incremented
+ * monotonically on each iteration until PasswordKey.spinCount iterations have been performed.
+ * The value of iterator on the last iteration MUST be one less than PasswordKey.spinCount.
+ *
+ *
For POI, H_final will be calculated by {@link #generateKey(byte[],HashAlgorithm,byte[],int)}
+ *
+ * @param password
+ * @param hashAlgorithm
+ * @param salt
+ * @param spinCount
+ * @return the hashed password
+ */
+ public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount) {
+ return hashPassword(password, hashAlgorithm, salt, spinCount, true);
+ }
+
+ /**
+ * Generalized method for read and write protection hash generation.
+ * The difference is, read protection uses the order iterator then hash in the hash loop, whereas write protection
+ * uses first the last hash value and then the current iterator value
+ *
+ * @param password
+ * @param hashAlgorithm
+ * @param salt
+ * @param spinCount
+ * @param iteratorFirst if true, the iterator is hashed before the n-1 hash value,
+ * if false the n-1 hash value is applied first
+ * @return the hashed password
+ */
+ public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount, boolean iteratorFirst) {
+ // If no password was given, use the default
+ if (password == null) {
+ password = Decryptor.DEFAULT_PASSWORD;
+ }
+
+ MessageDigest hashAlg = getMessageDigest(hashAlgorithm);
+
+ hashAlg.update(salt);
+ byte[] hash = hashAlg.digest(StringUtil.getToUnicodeLE(password));
+ byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
+
+ byte[] first = (iteratorFirst ? iterator : hash);
+ byte[] second = (iteratorFirst ? hash : iterator);
+
+ try {
+ for (int i = 0; i < spinCount; i++) {
+ LittleEndian.putInt(iterator, 0, i);
+ hashAlg.reset();
+ hashAlg.update(first);
+ hashAlg.update(second);
+ hashAlg.digest(hash, 0, hash.length); // don't create hash buffer everytime new
+ }
+ } catch (DigestException e) {
+ throw new EncryptedDocumentException("error in password hashing");
+ }
+
+ return hash;
+ }
+
+ /**
+ *
Initialization vectors are used in all cases for agile encryption. An initialization vector MUST be
+ * generated by using the following method, where H() is a hash function that MUST be the same as
+ * specified in section 2.3.4.11 and a plus sign (+) represents concatenation:
+ *
+ *
If a blockKey is provided, let IV be a hash of the KeySalt and the following value:
+ * {@code blockKey: IV = H(KeySalt + blockKey)}
+ *
If a blockKey is not provided, let IV be equal to the following value:
+ * {@code KeySalt:IV = KeySalt}
+ *
If the number of bytes in the value of IV is less than the the value of the blockSize attribute
+ * corresponding to the cipherAlgorithm attribute, pad the array of bytes by appending 0x36 until
+ * the array is blockSize bytes. If the array of bytes is larger than blockSize bytes, truncate the
+ * array to blockSize bytes.
The final hash data that is used for an encryption key is then generated by using the following
+ * method:
+ *
+ *
H_final = H(H_n + blockKey)
+ *
+ *
where blockKey represents an array of bytes used to prevent two different blocks from encrypting
+ * to the same cipher text.
+ *
+ *
If the size of the resulting H_final is smaller than that of PasswordKeyEncryptor.keyBits, the key
+ * MUST be padded by appending bytes with a value of 0x36. If the hash value is larger in size than
+ * PasswordKeyEncryptor.keyBits, the key is obtained by truncating the hash value.
+ *
+ * @param passwordHash
+ * @param hashAlgorithm
+ * @param blockKey
+ * @param keySize
+ * @return intermediate key
+ */
+ public static byte[] generateKey(byte[] passwordHash, HashAlgorithm hashAlgorithm, byte[] blockKey, int keySize) {
+ MessageDigest hashAlgo = getMessageDigest(hashAlgorithm);
+ hashAlgo.update(passwordHash);
+ byte[] key = hashAlgo.digest(blockKey);
+ return getBlock36(key, keySize);
+ }
+
+ /**
+ * Initialize a new cipher object with the given cipher properties and no padding
+ * If the given algorithm is not implemented in the JCE, it will try to load it from the bouncy castle
+ * provider.
+ *
+ * @param key the secrect key
+ * @param cipherAlgorithm the cipher algorithm
+ * @param chain the chaining mode
+ * @param vec the initialization vector (IV), can be null
+ * @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
+ * @return the requested cipher
+ * @throws GeneralSecurityException
+ * @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
+ * which depends on a missing bouncy castle provider
+ */
+ public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode) {
+ return getCipher(key, cipherAlgorithm, chain, vec, cipherMode, null);
+ }
+
+ /**
+ * Initialize a new cipher object with the given cipher properties
+ * If the given algorithm is not implemented in the JCE, it will try to load it from the bouncy castle
+ * provider.
+ *
+ * @param key the secrect key
+ * @param cipherAlgorithm the cipher algorithm
+ * @param chain the chaining mode
+ * @param vec the initialization vector (IV), can be null
+ * @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
+ * @param padding the padding (null = NOPADDING, ANSIX923Padding, PKCS5Padding, PKCS7Padding, ISO10126Padding, ...)
+ * @return the requested cipher
+ * @throws GeneralSecurityException
+ * @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
+ * which depends on a missing bouncy castle provider
+ */
+ public static Cipher getCipher(Key key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
+ int keySizeInBytes = key.getEncoded().length;
+ if (padding == null) padding = "NoPadding";
+
+ try {
+ // Ensure the JCE policies files allow for this sized key
+ if (Cipher.getMaxAllowedKeyLength(cipherAlgorithm.jceId) < keySizeInBytes*8) {
+ throw new EncryptedDocumentException("Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files");
+ }
+
+ Cipher cipher;
+ if (cipherAlgorithm == CipherAlgorithm.rc4) {
+ cipher = Cipher.getInstance(cipherAlgorithm.jceId);
+ } else if (cipherAlgorithm.needsBouncyCastle) {
+ registerBouncyCastle();
+ cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding, "BC");
+ } else {
+ cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding);
+ }
+
+ if (vec == null) {
+ cipher.init(cipherMode, key);
+ } else {
+ AlgorithmParameterSpec aps;
+ if (cipherAlgorithm == CipherAlgorithm.rc2) {
+ aps = new RC2ParameterSpec(key.getEncoded().length*8, vec);
+ } else {
+ aps = new IvParameterSpec(vec);
+ }
+ cipher.init(cipherMode, key, aps);
+ }
+ return cipher;
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ }
+
+ /**
+ * Returns a new byte array with a truncated to the given size.
+ * If the hash has less then size bytes, it will be filled with 0x36-bytes
+ *
+ * @param hash the to be truncated/filled hash byte array
+ * @param size the size of the returned byte array
+ * @return the padded hash
+ */
+ private static byte[] getBlock36(byte[] hash, int size) {
+ return getBlockX(hash, size, (byte)0x36);
+ }
+
+ /**
+ * Returns a new byte array with a truncated to the given size.
+ * If the hash has less then size bytes, it will be filled with 0-bytes
+ *
+ * @param hash the to be truncated/filled hash byte array
+ * @param size the size of the returned byte array
+ * @return the padded hash
+ */
+ public static byte[] getBlock0(byte[] hash, int size) {
+ return getBlockX(hash, size, (byte)0);
+ }
+
+ private static byte[] getBlockX(byte[] hash, int size, byte fill) {
+ if (hash.length == size) return hash;
+
+ byte[] result = new byte[size];
+ Arrays.fill(result, fill);
+ System.arraycopy(hash, 0, result, 0, Math.min(result.length, hash.length));
+ return result;
+ }
+
+ public static MessageDigest getMessageDigest(HashAlgorithm hashAlgorithm) {
+ try {
+ if (hashAlgorithm.needsBouncyCastle) {
+ registerBouncyCastle();
+ return MessageDigest.getInstance(hashAlgorithm.jceId, "BC");
+ } else {
+ return MessageDigest.getInstance(hashAlgorithm.jceId);
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("hash algo not supported", e);
+ }
+ }
+
+ public static Mac getMac(HashAlgorithm hashAlgorithm) {
+ try {
+ if (hashAlgorithm.needsBouncyCastle) {
+ registerBouncyCastle();
+ return Mac.getInstance(hashAlgorithm.jceHmacId, "BC");
+ } else {
+ return Mac.getInstance(hashAlgorithm.jceHmacId);
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("hmac algo not supported", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void registerBouncyCastle() {
+ if (Security.getProvider("BC") != null) {
+ return;
+ }
+
+ try {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ String bcProviderName = "org.bouncycastle.jce.provider.BouncyCastleProvider";
+ Class clazz = (Class)cl.loadClass(bcProviderName);
+ Security.addProvider(clazz.newInstance());
+ } catch (Exception e) {
+ throw new EncryptedDocumentException("Only the BouncyCastle provider supports your encryption settings - please add it to the classpath.", e);
+ }
+ }
+
+ private static final int INITIAL_CODE_ARRAY[] = {
+ 0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE,
+ 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A,
+ 0x4EC3
+ };
+
+ private static final byte PAD_ARRAY[] = {
+ (byte)0xBB, (byte)0xFF, (byte)0xFF, (byte)0xBA, (byte)0xFF,
+ (byte)0xFF, (byte)0xB9, (byte)0x80, (byte)0x00, (byte)0xBE,
+ (byte)0x0F, (byte)0x00, (byte)0xBF, (byte)0x0F, (byte)0x00
+ };
+
+ private static final int ENCRYPTION_MATRIX[][] = {
+ /* char 1 */ {0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09},
+ /* char 2 */ {0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF},
+ /* char 3 */ {0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0},
+ /* char 4 */ {0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40},
+ /* char 5 */ {0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5},
+ /* char 6 */ {0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A},
+ /* char 7 */ {0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9},
+ /* char 8 */ {0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0},
+ /* char 9 */ {0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC},
+ /* char 10 */ {0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10},
+ /* char 11 */ {0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168},
+ /* char 12 */ {0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C},
+ /* char 13 */ {0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD},
+ /* char 14 */ {0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC},
+ /* char 15 */ {0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4}
+ };
+
+ /**
+ * Create the verifier for xor obfuscation (method 1)
+ *
+ * @see 2.3.7.1 Binary Document Password Verifier Derivation Method 1
+ * @see 2.3.7.4 Binary Document Password Verifier Derivation Method 2
+ * @see Part 4 - Markup Language Reference - Ecma International - 3.2.12 fileSharing
+ *
+ * @param password the password
+ * @return the verifier (actually a short value)
+ */
+ public static int createXorVerifier1(String password) {
+ byte[] arrByteChars = toAnsiPassword(password);
+
+ // SET Verifier TO 0x0000
+ short verifier = 0;
+
+ if (!"".equals(password)) {
+ // FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
+ for (int i = arrByteChars.length-1; i >= 0; i--) {
+ // SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
+ verifier = rotateLeftBase15Bit(verifier);
+ verifier ^= arrByteChars[i];
+ }
+
+ // as we haven't prepended the password length into the input array
+ // we need to do it now separately ...
+ verifier = rotateLeftBase15Bit(verifier);
+ verifier ^= arrByteChars.length;
+
+ // RETURN Verifier BITWISE XOR 0xCE4B
+ verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
+ }
+
+ return verifier & 0xFFFF;
+ }
+
+ /**
+ * This method generates the xor verifier for word documents < 2007 (method 2).
+ * Its output will be used as password input for the newer word generations which
+ * utilize a real hashing algorithm like sha1.
+ *
+ * @param password the password
+ * @return the hashed password
+ *
+ * @see 2.3.7.4 Binary Document Password Verifier Derivation Method 2
+ * @see How to set the editing restrictions in Word using Open XML SDK 2.0
+ * @see Funny: How the new powerful cryptography implemented in Word 2007 turns it into a perfect tool for document password removal.
+ */
+ public static int createXorVerifier2(String password) {
+ //Array to hold Key Values
+ byte[] generatedKey = new byte[4];
+
+ //Maximum length of the password is 15 chars.
+ final int maxPasswordLength = 15;
+
+ if (!"".equals(password)) {
+ // Truncate the password to 15 characters
+ password = password.substring(0, Math.min(password.length(), maxPasswordLength));
+
+ byte[] arrByteChars = toAnsiPassword(password);
+
+ // Compute the high-order word of the new key:
+
+ // --> Initialize from the initial code array (see below), depending on the passwords length.
+ int highOrderWord = INITIAL_CODE_ARRAY[arrByteChars.length - 1];
+
+ // --> For each character in the password:
+ // --> For every bit in the character, starting with the least significant and progressing to (but excluding)
+ // the most significant, if the bit is set, XOR the keys high-order word with the corresponding word from
+ // the Encryption Matrix
+ for (int i = 0; i < arrByteChars.length; i++) {
+ int tmp = maxPasswordLength - arrByteChars.length + i;
+ for (int intBit = 0; intBit < 7; intBit++) {
+ if ((arrByteChars[i] & (0x0001 << intBit)) != 0) {
+ highOrderWord ^= ENCRYPTION_MATRIX[tmp][intBit];
+ }
+ }
+ }
+
+ // Compute the low-order word of the new key:
+ int verifier = createXorVerifier1(password);
+
+ // The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64],
+ // and that value shall be hashed as defined by the attribute values.
+
+ LittleEndian.putShort(generatedKey, 0, (short)verifier);
+ LittleEndian.putShort(generatedKey, 2, (short)highOrderWord);
+ }
+
+ return LittleEndian.getInt(generatedKey);
+ }
+
+ /**
+ * This method generates the xored-hashed password for word documents < 2007.
+ */
+ public static String xorHashPassword(String password) {
+ int hashedPassword = createXorVerifier2(password);
+ return String.format(Locale.ROOT, "%1$08X", hashedPassword);
+ }
+
+ /**
+ * Convenience function which returns the reversed xored-hashed password for further
+ * processing in word documents 2007 and newer, which utilize a real hashing algorithm like sha1.
+ */
+ public static String xorHashPasswordReversed(String password) {
+ int hashedPassword = createXorVerifier2(password);
+
+ return String.format(Locale.ROOT, "%1$02X%2$02X%3$02X%4$02X"
+ , ( hashedPassword >>> 0 ) & 0xFF
+ , ( hashedPassword >>> 8 ) & 0xFF
+ , ( hashedPassword >>> 16 ) & 0xFF
+ , ( hashedPassword >>> 24 ) & 0xFF
+ );
+ }
+
+ /**
+ * Create the xor key for xor obfuscation, which is used to create the xor array (method 1)
+ *
+ * @see 2.3.7.2 Binary Document XOR Array Initialization Method 1
+ * @see 2.3.7.4 Binary Document Password Verifier Derivation Method 2
+ *
+ * @param password the password
+ * @return the xor key
+ */
+ public static int createXorKey1(String password) {
+ // the xor key for method 1 is part of the verifier for method 2
+ // so we simply chop it from there
+ return createXorVerifier2(password) >>> 16;
+ }
+
+ /**
+ * Creates an byte array for xor obfuscation (method 1)
+ *
+ * @see 2.3.7.2 Binary Document XOR Array Initialization Method 1
+ * @see Libre Office implementation
+ *
+ * @param password the password
+ * @return the byte array for xor obfuscation
+ */
+ public static byte[] createXorArray1(String password) {
+ if (password.length() > 15) {
+ password = password.substring(0, 15);
+ }
+ byte passBytes[] = password.getBytes(Charset.forName("ASCII"));
+
+ // this code is based on the libre office implementation.
+ // The MS-OFFCRYPTO misses some infos about the various rotation sizes
+ byte obfuscationArray[] = new byte[16];
+ System.arraycopy(passBytes, 0, obfuscationArray, 0, passBytes.length);
+ System.arraycopy(PAD_ARRAY, 0, obfuscationArray, passBytes.length, PAD_ARRAY.length-passBytes.length+1);
+
+ int xorKey = createXorKey1(password);
+
+ // rotation of key values is application dependent - Excel = 2 / Word = 7
+ int nRotateSize = 2;
+
+ byte baseKeyLE[] = { (byte)(xorKey & 0xFF), (byte)((xorKey >>> 8) & 0xFF) };
+ for (int i=0; iPart 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection)
+ */
+ private static byte[] toAnsiPassword(String password) {
+ // TODO: charset conversion (see ecma spec)
+
+ // Get the single-byte values by iterating through the Unicode characters.
+ // For each character, if the low byte is not equal to 0, take it.
+ // Otherwise, take the high byte.
+ byte[] arrByteChars = new byte[password.length()];
+
+ for (int i = 0; i < password.length(); i++) {
+ int intTemp = password.charAt(i);
+ byte lowByte = (byte)(intTemp & 0xFF);
+ byte highByte = (byte)((intTemp >>> 8) & 0xFF);
+ arrByteChars[i] = (lowByte != 0 ? lowByte : highByte);
+ }
+
+ return arrByteChars;
+ }
+
+ private static byte rotateLeft(byte bits, int shift) {
+ return (byte)(((bits & 0xff) << shift) | ((bits & 0xff) >>> (8 - shift)));
+ }
+
+ private static short rotateLeftBase15Bit(short verifier) {
+ /*
+ * IF (Verifier BITWISE AND 0x4000) is 0x0000
+ * SET Intermediate1 TO 0
+ * ELSE
+ * SET Intermediate1 TO 1
+ * ENDIF
+ */
+ short intermediate1 = (short)(((verifier & 0x4000) == 0) ? 0 : 1);
+ /*
+ * SET Intermediate2 TO Verifier MULTIPLED BY 2
+ * SET most significant bit of Intermediate2 TO 0
+ */
+ short intermediate2 = (short)((verifier<<1) & 0x7FFF);
+ /*
+ * SET Intermediate3 TO Intermediate1 BITWISE OR Intermediate2
+ */
+ short intermediate3 = (short)(intermediate1 | intermediate2);
+ return intermediate3;
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java b/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java
index 923d7075ff..9c377fa2d7 100644
--- a/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java
+++ b/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java
@@ -1,370 +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.poi.poifs.crypt;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
-import org.apache.poi.poifs.filesystem.DirectoryEntry;
-import org.apache.poi.poifs.filesystem.DocumentEntry;
-import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
-import org.apache.poi.poifs.filesystem.POIFSWriterListener;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.LittleEndianInput;
-import org.apache.poi.util.LittleEndianOutput;
-import org.apache.poi.util.StringUtil;
-
-public class DataSpaceMapUtils {
- public static void addDefaultDataSpace(DirectoryEntry dir) throws IOException {
- DataSpaceMapEntry dsme = new DataSpaceMapEntry(
- new int[]{ 0 }
- , new String[]{ Decryptor.DEFAULT_POIFS_ENTRY }
- , "StrongEncryptionDataSpace"
- );
- DataSpaceMap dsm = new DataSpaceMap(new DataSpaceMapEntry[]{dsme});
- createEncryptionEntry(dir, "\u0006DataSpaces/DataSpaceMap", dsm);
-
- DataSpaceDefinition dsd = new DataSpaceDefinition(new String[]{ "StrongEncryptionTransform" });
- createEncryptionEntry(dir, "\u0006DataSpaces/DataSpaceInfo/StrongEncryptionDataSpace", dsd);
-
- TransformInfoHeader tih = new TransformInfoHeader(
- 1
- , "{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}"
- , "Microsoft.Container.EncryptionTransform"
- , 1, 0, 1, 0, 1, 0
- );
- IRMDSTransformInfo irm = new IRMDSTransformInfo(tih, 0, null);
- createEncryptionEntry(dir, "\u0006DataSpaces/TransformInfo/StrongEncryptionTransform/\u0006Primary", irm);
-
- DataSpaceVersionInfo dsvi = new DataSpaceVersionInfo("Microsoft.Container.DataSpaces", 1, 0, 1, 0, 1, 0);
- createEncryptionEntry(dir, "\u0006DataSpaces/Version", dsvi);
- }
-
- public static DocumentEntry createEncryptionEntry(DirectoryEntry dir, String path, EncryptionRecord out) throws IOException {
- String parts[] = path.split("/");
- for (int i=0; i 0) {
- for (int i=0; i<(4-scratchedBytes); i++) {
- is.readByte();
- }
- }
-
- return new String(data, 0, data.length, Charset.forName("UTF-8"));
- }
-
- public static void writeUtf8LPP4(LittleEndianOutput os, String str) {
- if (str == null || "".equals(str)) {
- os.writeInt(str == null ? 0 : 4);
- os.writeInt(0);
- } else {
- byte buf[] = str.getBytes(Charset.forName("UTF-8"));
- os.writeInt(buf.length);
- os.write(buf);
- int scratchBytes = buf.length%4;
- if (scratchBytes > 0) {
- for (int i=0; i<(4-scratchBytes); i++) {
- os.writeByte(0);
- }
- }
- }
- }
-
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
+import org.apache.poi.poifs.filesystem.POIFSWriterListener;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
+import org.apache.poi.util.StringUtil;
+
+public class DataSpaceMapUtils {
+ public static void addDefaultDataSpace(DirectoryEntry dir) throws IOException {
+ DataSpaceMapEntry dsme = new DataSpaceMapEntry(
+ new int[]{ 0 }
+ , new String[]{ Decryptor.DEFAULT_POIFS_ENTRY }
+ , "StrongEncryptionDataSpace"
+ );
+ DataSpaceMap dsm = new DataSpaceMap(new DataSpaceMapEntry[]{dsme});
+ createEncryptionEntry(dir, "\u0006DataSpaces/DataSpaceMap", dsm);
+
+ DataSpaceDefinition dsd = new DataSpaceDefinition(new String[]{ "StrongEncryptionTransform" });
+ createEncryptionEntry(dir, "\u0006DataSpaces/DataSpaceInfo/StrongEncryptionDataSpace", dsd);
+
+ TransformInfoHeader tih = new TransformInfoHeader(
+ 1
+ , "{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}"
+ , "Microsoft.Container.EncryptionTransform"
+ , 1, 0, 1, 0, 1, 0
+ );
+ IRMDSTransformInfo irm = new IRMDSTransformInfo(tih, 0, null);
+ createEncryptionEntry(dir, "\u0006DataSpaces/TransformInfo/StrongEncryptionTransform/\u0006Primary", irm);
+
+ DataSpaceVersionInfo dsvi = new DataSpaceVersionInfo("Microsoft.Container.DataSpaces", 1, 0, 1, 0, 1, 0);
+ createEncryptionEntry(dir, "\u0006DataSpaces/Version", dsvi);
+ }
+
+ public static DocumentEntry createEncryptionEntry(DirectoryEntry dir, String path, EncryptionRecord out) throws IOException {
+ String parts[] = path.split("/");
+ for (int i=0; i 0) {
+ for (int i=0; i<(4-scratchedBytes); i++) {
+ is.readByte();
+ }
+ }
+
+ return new String(data, 0, data.length, Charset.forName("UTF-8"));
+ }
+
+ public static void writeUtf8LPP4(LittleEndianOutput os, String str) {
+ if (str == null || "".equals(str)) {
+ os.writeInt(str == null ? 0 : 4);
+ os.writeInt(0);
+ } else {
+ byte buf[] = str.getBytes(Charset.forName("UTF-8"));
+ os.writeInt(buf.length);
+ os.write(buf);
+ int scratchBytes = buf.length%4;
+ if (scratchBytes > 0) {
+ for (int i=0; i<(4-scratchBytes); i++) {
+ os.writeByte(0);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java b/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
index 24371dfdd6..c32b069116 100644
--- a/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
+++ b/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
@@ -1,33 +1,33 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.poifs.crypt;
-
-import java.io.IOException;
-
-import org.apache.poi.util.LittleEndianInput;
-
-public interface EncryptionInfoBuilder {
- /**
- * initialize the builder from a stream
- */
- void initialize(EncryptionInfo ei, LittleEndianInput dis) throws IOException;
-
- /**
- * initialize the builder from scratch
- */
- void initialize(EncryptionInfo ei, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode);
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt;
+
+import java.io.IOException;
+
+import org.apache.poi.util.LittleEndianInput;
+
+public interface EncryptionInfoBuilder {
+ /**
+ * initialize the builder from a stream
+ */
+ void initialize(EncryptionInfo ei, LittleEndianInput dis) throws IOException;
+
+ /**
+ * initialize the builder from scratch
+ */
+ void initialize(EncryptionInfo ei, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode);
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java b/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java
index 50064b5a6b..2f89557996 100644
--- a/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java
+++ b/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java
@@ -1,52 +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.poi.poifs.crypt;
-
-import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
-
-/**
- * Office supports various encryption modes.
- * The encryption is either based on the whole container ({@link #agile}, {@link #standard} or {@link #binaryRC4})
- * or record based ({@link #cryptoAPI}). The record based encryption can't be accessed directly, but will be
- * invoked by using the {@link Biff8EncryptionKey#setCurrentUserPassword(String)} before saving the document.
- */
-public enum EncryptionMode {
- /* @see 2.3.6 Office Binary Document RC4 Encryption */
- binaryRC4("org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4EncryptionInfoBuilder", 1, 1, 0x0),
- /* @see 2.3.5 Office Binary Document RC4 CryptoAPI Encryption */
- cryptoAPI("org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionInfoBuilder", 4, 2, 0x04),
- /* @see 2.3.4.5 \EncryptionInfo Stream (Standard Encryption) */
- standard("org.apache.poi.poifs.crypt.standard.StandardEncryptionInfoBuilder", 4, 2, 0x24),
- /* @see 2.3.4.10 \EncryptionInfo Stream (Agile Encryption) */
- agile("org.apache.poi.poifs.crypt.agile.AgileEncryptionInfoBuilder", 4, 4, 0x40),
- /* @see XOR Obfuscation */
- xor("org.apache.poi.poifs.crypt.xor.XOREncryptionInfoBuilder", 0, 0, 0)
- ;
-
- public final String builder;
- public final int versionMajor;
- public final int versionMinor;
- public final int encryptionFlags;
-
- EncryptionMode(String builder, int versionMajor, int versionMinor, int encryptionFlags) {
- this.builder = builder;
- this.versionMajor = versionMajor;
- this.versionMinor = versionMinor;
- this.encryptionFlags = encryptionFlags;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt;
+
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+
+/**
+ * Office supports various encryption modes.
+ * The encryption is either based on the whole container ({@link #agile}, {@link #standard} or {@link #binaryRC4})
+ * or record based ({@link #cryptoAPI}). The record based encryption can't be accessed directly, but will be
+ * invoked by using the {@link Biff8EncryptionKey#setCurrentUserPassword(String)} before saving the document.
+ */
+public enum EncryptionMode {
+ /* @see 2.3.6 Office Binary Document RC4 Encryption */
+ binaryRC4("org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4EncryptionInfoBuilder", 1, 1, 0x0),
+ /* @see 2.3.5 Office Binary Document RC4 CryptoAPI Encryption */
+ cryptoAPI("org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionInfoBuilder", 4, 2, 0x04),
+ /* @see 2.3.4.5 \EncryptionInfo Stream (Standard Encryption) */
+ standard("org.apache.poi.poifs.crypt.standard.StandardEncryptionInfoBuilder", 4, 2, 0x24),
+ /* @see 2.3.4.10 \EncryptionInfo Stream (Agile Encryption) */
+ agile("org.apache.poi.poifs.crypt.agile.AgileEncryptionInfoBuilder", 4, 4, 0x40),
+ /* @see XOR Obfuscation */
+ xor("org.apache.poi.poifs.crypt.xor.XOREncryptionInfoBuilder", 0, 0, 0)
+ ;
+
+ public final String builder;
+ public final int versionMajor;
+ public final int versionMinor;
+ public final int encryptionFlags;
+
+ EncryptionMode(String builder, int versionMajor, int versionMinor, int encryptionFlags) {
+ this.builder = builder;
+ this.versionMajor = versionMajor;
+ this.versionMinor = versionMinor;
+ this.encryptionFlags = encryptionFlags;
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/Encryptor.java b/src/java/org/apache/poi/poifs/crypt/Encryptor.java
index f4c0c1639f..ff954ab6b1 100644
--- a/src/java/org/apache/poi/poifs/crypt/Encryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/Encryptor.java
@@ -1,104 +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.poi.poifs.crypt;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
-import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-public abstract class Encryptor implements Cloneable {
- protected static final String DEFAULT_POIFS_ENTRY = Decryptor.DEFAULT_POIFS_ENTRY;
- private EncryptionInfo encryptionInfo;
- private SecretKey secretKey;
-
- /**
- * Return a output stream for encrypted data.
- *
- * @param dir the node to write to
- * @return encrypted stream
- */
- public abstract OutputStream getDataStream(DirectoryNode dir)
- throws IOException, GeneralSecurityException;
-
- // for tests
- public abstract void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]);
-
- public abstract void confirmPassword(String password);
-
- public static Encryptor getInstance(EncryptionInfo info) {
- return info.getEncryptor();
- }
-
- public OutputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
- return getDataStream(fs.getRoot());
- }
- public OutputStream getDataStream(OPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
- return getDataStream(fs.getRoot());
- }
- public OutputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException {
- return getDataStream(fs.getRoot());
- }
-
- public ChunkedCipherOutputStream getDataStream(OutputStream stream, int initialOffset)
- throws IOException, GeneralSecurityException {
- throw new EncryptedDocumentException("this decryptor doesn't support writing directly to a stream");
- }
-
- public SecretKey getSecretKey() {
- return secretKey;
- }
-
- public void setSecretKey(SecretKey secretKey) {
- this.secretKey = secretKey;
- }
-
- public EncryptionInfo getEncryptionInfo() {
- return encryptionInfo;
- }
-
- public void setEncryptionInfo(EncryptionInfo encryptionInfo) {
- this.encryptionInfo = encryptionInfo;
- }
-
- /**
- * Sets the chunk size of the data stream.
- * Needs to be set before the data stream is requested.
- * When not set, the implementation uses method specific default values
- *
- * @param chunkSize the chunk size, i.e. the block size with the same encryption key
- */
- public void setChunkSize(int chunkSize) {
- throw new EncryptedDocumentException("this decryptor doesn't support changing the chunk size");
- }
-
- @Override
- public Encryptor clone() throws CloneNotSupportedException {
- Encryptor other = (Encryptor)super.clone();
- other.secretKey = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
- // encryptionInfo is set from outside
- return other;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+public abstract class Encryptor implements Cloneable {
+ protected static final String DEFAULT_POIFS_ENTRY = Decryptor.DEFAULT_POIFS_ENTRY;
+ private EncryptionInfo encryptionInfo;
+ private SecretKey secretKey;
+
+ /**
+ * Return a output stream for encrypted data.
+ *
+ * @param dir the node to write to
+ * @return encrypted stream
+ */
+ public abstract OutputStream getDataStream(DirectoryNode dir)
+ throws IOException, GeneralSecurityException;
+
+ // for tests
+ public abstract void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]);
+
+ public abstract void confirmPassword(String password);
+
+ public static Encryptor getInstance(EncryptionInfo info) {
+ return info.getEncryptor();
+ }
+
+ public OutputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
+ return getDataStream(fs.getRoot());
+ }
+ public OutputStream getDataStream(OPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
+ return getDataStream(fs.getRoot());
+ }
+ public OutputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException {
+ return getDataStream(fs.getRoot());
+ }
+
+ public ChunkedCipherOutputStream getDataStream(OutputStream stream, int initialOffset)
+ throws IOException, GeneralSecurityException {
+ throw new EncryptedDocumentException("this decryptor doesn't support writing directly to a stream");
+ }
+
+ public SecretKey getSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(SecretKey secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ public EncryptionInfo getEncryptionInfo() {
+ return encryptionInfo;
+ }
+
+ public void setEncryptionInfo(EncryptionInfo encryptionInfo) {
+ this.encryptionInfo = encryptionInfo;
+ }
+
+ /**
+ * Sets the chunk size of the data stream.
+ * Needs to be set before the data stream is requested.
+ * When not set, the implementation uses method specific default values
+ *
+ * @param chunkSize the chunk size, i.e. the block size with the same encryption key
+ */
+ public void setChunkSize(int chunkSize) {
+ throw new EncryptedDocumentException("this decryptor doesn't support changing the chunk size");
+ }
+
+ @Override
+ public Encryptor clone() throws CloneNotSupportedException {
+ Encryptor other = (Encryptor)super.clone();
+ other.secretKey = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
+ // encryptionInfo is set from outside
+ return other;
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java b/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
index 24c43325d9..0440c78c04 100644
--- a/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
+++ b/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
@@ -1,75 +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.poi.poifs.crypt;
-
-import org.apache.poi.EncryptedDocumentException;
-
-public enum HashAlgorithm {
- none ( "", 0x0000, "", 0, "", false),
- sha1 ( "SHA-1", 0x8004, "SHA1", 20, "HmacSHA1", false),
- sha256 ( "SHA-256", 0x800C, "SHA256", 32, "HmacSHA256", false),
- sha384 ( "SHA-384", 0x800D, "SHA384", 48, "HmacSHA384", false),
- sha512 ( "SHA-512", 0x800E, "SHA512", 64, "HmacSHA512", false),
- /* only for agile encryption */
- md5 ( "MD5", -1, "MD5", 16, "HmacMD5", false),
- // although sunjc2 supports md2, hmac-md2 is only supported by bouncycastle
- md2 ( "MD2", -1, "MD2", 16, "Hmac-MD2", true),
- md4 ( "MD4", -1, "MD4", 16, "Hmac-MD4", true),
- ripemd128("RipeMD128", -1, "RIPEMD-128", 16, "HMac-RipeMD128", true),
- ripemd160("RipeMD160", -1, "RIPEMD-160", 20, "HMac-RipeMD160", true),
- whirlpool("Whirlpool", -1, "WHIRLPOOL", 64, "HMac-Whirlpool", true),
- // only for xml signing
- sha224 ( "SHA-224", -1, "SHA224", 28, "HmacSHA224", true);
-
- public final String jceId;
- public final int ecmaId;
- public final String ecmaString;
- public final int hashSize;
- public final String jceHmacId;
- public final boolean needsBouncyCastle;
-
- HashAlgorithm(String jceId, int ecmaId, String ecmaString, int hashSize, String jceHmacId, boolean needsBouncyCastle) {
- this.jceId = jceId;
- this.ecmaId = ecmaId;
- this.ecmaString = ecmaString;
- this.hashSize = hashSize;
- this.jceHmacId = jceHmacId;
- this.needsBouncyCastle = needsBouncyCastle;
- }
-
- public static HashAlgorithm fromEcmaId(int ecmaId) {
- for (HashAlgorithm ha : values()) {
- if (ha.ecmaId == ecmaId) return ha;
- }
- throw new EncryptedDocumentException("hash algorithm not found");
- }
-
- public static HashAlgorithm fromEcmaId(String ecmaString) {
- for (HashAlgorithm ha : values()) {
- if (ha.ecmaString.equals(ecmaString)) return ha;
- }
- throw new EncryptedDocumentException("hash algorithm not found");
- }
-
- public static HashAlgorithm fromString(String string) {
- for (HashAlgorithm ha : values()) {
- if (ha.ecmaString.equalsIgnoreCase(string) || ha.jceId.equalsIgnoreCase(string)) return ha;
- }
- throw new EncryptedDocumentException("hash algorithm not found");
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt;
+
+import org.apache.poi.EncryptedDocumentException;
+
+public enum HashAlgorithm {
+ none ( "", 0x0000, "", 0, "", false),
+ sha1 ( "SHA-1", 0x8004, "SHA1", 20, "HmacSHA1", false),
+ sha256 ( "SHA-256", 0x800C, "SHA256", 32, "HmacSHA256", false),
+ sha384 ( "SHA-384", 0x800D, "SHA384", 48, "HmacSHA384", false),
+ sha512 ( "SHA-512", 0x800E, "SHA512", 64, "HmacSHA512", false),
+ /* only for agile encryption */
+ md5 ( "MD5", -1, "MD5", 16, "HmacMD5", false),
+ // although sunjc2 supports md2, hmac-md2 is only supported by bouncycastle
+ md2 ( "MD2", -1, "MD2", 16, "Hmac-MD2", true),
+ md4 ( "MD4", -1, "MD4", 16, "Hmac-MD4", true),
+ ripemd128("RipeMD128", -1, "RIPEMD-128", 16, "HMac-RipeMD128", true),
+ ripemd160("RipeMD160", -1, "RIPEMD-160", 20, "HMac-RipeMD160", true),
+ whirlpool("Whirlpool", -1, "WHIRLPOOL", 64, "HMac-Whirlpool", true),
+ // only for xml signing
+ sha224 ( "SHA-224", -1, "SHA224", 28, "HmacSHA224", true);
+
+ public final String jceId;
+ public final int ecmaId;
+ public final String ecmaString;
+ public final int hashSize;
+ public final String jceHmacId;
+ public final boolean needsBouncyCastle;
+
+ HashAlgorithm(String jceId, int ecmaId, String ecmaString, int hashSize, String jceHmacId, boolean needsBouncyCastle) {
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ this.ecmaString = ecmaString;
+ this.hashSize = hashSize;
+ this.jceHmacId = jceHmacId;
+ this.needsBouncyCastle = needsBouncyCastle;
+ }
+
+ public static HashAlgorithm fromEcmaId(int ecmaId) {
+ for (HashAlgorithm ha : values()) {
+ if (ha.ecmaId == ecmaId) return ha;
+ }
+ throw new EncryptedDocumentException("hash algorithm not found");
+ }
+
+ public static HashAlgorithm fromEcmaId(String ecmaString) {
+ for (HashAlgorithm ha : values()) {
+ if (ha.ecmaString.equals(ecmaString)) return ha;
+ }
+ throw new EncryptedDocumentException("hash algorithm not found");
+ }
+
+ public static HashAlgorithm fromString(String string) {
+ for (HashAlgorithm ha : values()) {
+ if (ha.ecmaString.equalsIgnoreCase(string) || ha.jceId.equalsIgnoreCase(string)) return ha;
+ }
+ throw new EncryptedDocumentException("hash algorithm not found");
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java
index e02273407c..1cc6b1b2f4 100644
--- a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java
@@ -1,166 +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.poi.poifs.crypt.binaryrc4;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.util.Arrays;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.*;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.StringUtil;
-
-public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
- private long length = -1L;
- private int chunkSize = 512;
-
- private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream {
-
- @Override
- protected Cipher initCipherForBlock(Cipher existing, int block)
- throws GeneralSecurityException {
- return BinaryRC4Decryptor.this.initCipherForBlock(existing, block);
- }
-
- public BinaryRC4CipherInputStream(DocumentInputStream stream, long size)
- throws GeneralSecurityException {
- super(stream, size, chunkSize);
- }
-
- public BinaryRC4CipherInputStream(InputStream stream)
- throws GeneralSecurityException {
- super(stream, Integer.MAX_VALUE, chunkSize);
- }
- }
-
- protected BinaryRC4Decryptor() {
- }
-
- @Override
- public boolean verifyPassword(String password) {
- EncryptionVerifier ver = getEncryptionInfo().getVerifier();
- SecretKey skey = generateSecretKey(password, ver);
- try {
- Cipher cipher = initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.DECRYPT_MODE);
- byte encryptedVerifier[] = ver.getEncryptedVerifier();
- byte verifier[] = new byte[encryptedVerifier.length];
- cipher.update(encryptedVerifier, 0, encryptedVerifier.length, verifier);
- setVerifier(verifier);
- byte encryptedVerifierHash[] = ver.getEncryptedVerifierHash();
- byte verifierHash[] = cipher.doFinal(encryptedVerifierHash);
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- byte calcVerifierHash[] = hashAlg.digest(verifier);
- if (Arrays.equals(calcVerifierHash, verifierHash)) {
- setSecretKey(skey);
- return true;
- }
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException(e);
- }
- return false;
- }
-
- @Override
- public Cipher initCipherForBlock(Cipher cipher, int block)
- throws GeneralSecurityException {
- return initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.DECRYPT_MODE);
- }
-
- protected static Cipher initCipherForBlock(Cipher cipher, int block,
- EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
- throws GeneralSecurityException {
- EncryptionVerifier ver = encryptionInfo.getVerifier();
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- byte blockKey[] = new byte[4];
- LittleEndian.putUInt(blockKey, 0, block);
- byte encKey[] = CryptoFunctions.generateKey(skey.getEncoded(), hashAlgo, blockKey, 16);
- SecretKey key = new SecretKeySpec(encKey, skey.getAlgorithm());
- if (cipher == null) {
- EncryptionHeader em = encryptionInfo.getHeader();
- cipher = CryptoFunctions.getCipher(key, em.getCipherAlgorithm(), null, null, encryptMode);
- } else {
- cipher.init(encryptMode, key);
- }
- return cipher;
- }
-
- protected static SecretKey generateSecretKey(String password, EncryptionVerifier ver) {
- if (password.length() > 255) {
- password = password.substring(0, 255);
- }
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- byte hash[] = hashAlg.digest(StringUtil.getToUnicodeLE(password));
- byte salt[] = ver.getSalt();
- hashAlg.reset();
- for (int i = 0; i < 16; i++) {
- hashAlg.update(hash, 0, 5);
- hashAlg.update(salt);
- }
-
- hash = new byte[5];
- System.arraycopy(hashAlg.digest(), 0, hash, 0, 5);
- SecretKey skey = new SecretKeySpec(hash, ver.getCipherAlgorithm().jceId);
- return skey;
- }
-
- @Override
- @SuppressWarnings("resource")
- public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException,
- GeneralSecurityException {
- DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);
- length = dis.readLong();
- return new BinaryRC4CipherInputStream(dis, length);
- }
-
- @Override
- public InputStream getDataStream(InputStream stream, int size, int initialPos)
- throws IOException, GeneralSecurityException {
- return new BinaryRC4CipherInputStream(stream);
- }
-
-
- @Override
- public long getLength() {
- if (length == -1L) {
- throw new IllegalStateException("Decryptor.getDataStream() was not called");
- }
-
- return length;
- }
-
- @Override
- public void setChunkSize(int chunkSize) {
- this.chunkSize = chunkSize;
- }
-
- @Override
- public BinaryRC4Decryptor clone() throws CloneNotSupportedException {
- return (BinaryRC4Decryptor)super.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.binaryrc4;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
+public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
+ private long length = -1L;
+ private int chunkSize = 512;
+
+ private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream {
+
+ @Override
+ protected Cipher initCipherForBlock(Cipher existing, int block)
+ throws GeneralSecurityException {
+ return BinaryRC4Decryptor.this.initCipherForBlock(existing, block);
+ }
+
+ public BinaryRC4CipherInputStream(DocumentInputStream stream, long size)
+ throws GeneralSecurityException {
+ super(stream, size, chunkSize);
+ }
+
+ public BinaryRC4CipherInputStream(InputStream stream)
+ throws GeneralSecurityException {
+ super(stream, Integer.MAX_VALUE, chunkSize);
+ }
+ }
+
+ protected BinaryRC4Decryptor() {
+ }
+
+ @Override
+ public boolean verifyPassword(String password) {
+ EncryptionVerifier ver = getEncryptionInfo().getVerifier();
+ SecretKey skey = generateSecretKey(password, ver);
+ try {
+ Cipher cipher = initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.DECRYPT_MODE);
+ byte encryptedVerifier[] = ver.getEncryptedVerifier();
+ byte verifier[] = new byte[encryptedVerifier.length];
+ cipher.update(encryptedVerifier, 0, encryptedVerifier.length, verifier);
+ setVerifier(verifier);
+ byte encryptedVerifierHash[] = ver.getEncryptedVerifierHash();
+ byte verifierHash[] = cipher.doFinal(encryptedVerifierHash);
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ byte calcVerifierHash[] = hashAlg.digest(verifier);
+ if (Arrays.equals(calcVerifierHash, verifierHash)) {
+ setSecretKey(skey);
+ return true;
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ return false;
+ }
+
+ @Override
+ public Cipher initCipherForBlock(Cipher cipher, int block)
+ throws GeneralSecurityException {
+ return initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.DECRYPT_MODE);
+ }
+
+ protected static Cipher initCipherForBlock(Cipher cipher, int block,
+ EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
+ throws GeneralSecurityException {
+ EncryptionVerifier ver = encryptionInfo.getVerifier();
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ byte blockKey[] = new byte[4];
+ LittleEndian.putUInt(blockKey, 0, block);
+ byte encKey[] = CryptoFunctions.generateKey(skey.getEncoded(), hashAlgo, blockKey, 16);
+ SecretKey key = new SecretKeySpec(encKey, skey.getAlgorithm());
+ if (cipher == null) {
+ EncryptionHeader em = encryptionInfo.getHeader();
+ cipher = CryptoFunctions.getCipher(key, em.getCipherAlgorithm(), null, null, encryptMode);
+ } else {
+ cipher.init(encryptMode, key);
+ }
+ return cipher;
+ }
+
+ protected static SecretKey generateSecretKey(String password, EncryptionVerifier ver) {
+ if (password.length() > 255) {
+ password = password.substring(0, 255);
+ }
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ byte hash[] = hashAlg.digest(StringUtil.getToUnicodeLE(password));
+ byte salt[] = ver.getSalt();
+ hashAlg.reset();
+ for (int i = 0; i < 16; i++) {
+ hashAlg.update(hash, 0, 5);
+ hashAlg.update(salt);
+ }
+
+ hash = new byte[5];
+ System.arraycopy(hashAlg.digest(), 0, hash, 0, 5);
+ SecretKey skey = new SecretKeySpec(hash, ver.getCipherAlgorithm().jceId);
+ return skey;
+ }
+
+ @Override
+ @SuppressWarnings("resource")
+ public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException,
+ GeneralSecurityException {
+ DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);
+ length = dis.readLong();
+ return new BinaryRC4CipherInputStream(dis, length);
+ }
+
+ @Override
+ public InputStream getDataStream(InputStream stream, int size, int initialPos)
+ throws IOException, GeneralSecurityException {
+ return new BinaryRC4CipherInputStream(stream);
+ }
+
+
+ @Override
+ public long getLength() {
+ if (length == -1L) {
+ throw new IllegalStateException("Decryptor.getDataStream() was not called");
+ }
+
+ return length;
+ }
+
+ @Override
+ public void setChunkSize(int chunkSize) {
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ public BinaryRC4Decryptor clone() throws CloneNotSupportedException {
+ return (BinaryRC4Decryptor)super.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java
index b9022017b9..549d0f85f9 100644
--- a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java
+++ b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java
@@ -1,51 +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.poi.poifs.crypt.binaryrc4;
-
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.CipherProvider;
-import org.apache.poi.poifs.crypt.EncryptionHeader;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-
-public class BinaryRC4EncryptionHeader extends EncryptionHeader implements EncryptionRecord, Cloneable {
-
- protected BinaryRC4EncryptionHeader() {
- setCipherAlgorithm(CipherAlgorithm.rc4);
- setKeySize(40);
- setBlockSize(-1);
- setCipherProvider(CipherProvider.rc4);
- setHashAlgorithm(HashAlgorithm.md5);
- setSizeExtra(0);
- setFlags(0);
- setCspName("");
- setChainingMode(null);
- }
-
- @Override
- public void write(LittleEndianByteArrayOutputStream littleendianbytearrayoutputstream) {
- }
-
- @Override
- public BinaryRC4EncryptionHeader clone() throws CloneNotSupportedException {
- return (BinaryRC4EncryptionHeader)super.clone();
- }
-
-
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.binaryrc4;
+
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.CipherProvider;
+import org.apache.poi.poifs.crypt.EncryptionHeader;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+
+public class BinaryRC4EncryptionHeader extends EncryptionHeader implements EncryptionRecord, Cloneable {
+
+ protected BinaryRC4EncryptionHeader() {
+ setCipherAlgorithm(CipherAlgorithm.rc4);
+ setKeySize(40);
+ setBlockSize(-1);
+ setCipherProvider(CipherProvider.rc4);
+ setHashAlgorithm(HashAlgorithm.md5);
+ setSizeExtra(0);
+ setFlags(0);
+ setCspName("");
+ setChainingMode(null);
+ }
+
+ @Override
+ public void write(LittleEndianByteArrayOutputStream littleendianbytearrayoutputstream) {
+ }
+
+ @Override
+ public BinaryRC4EncryptionHeader clone() throws CloneNotSupportedException {
+ return (BinaryRC4EncryptionHeader)super.clone();
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java
index 94ddde72ab..d8b8573597 100644
--- a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java
+++ b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java
@@ -1,59 +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.poi.poifs.crypt.binaryrc4;
-
-import java.io.IOException;
-import org.apache.poi.poifs.crypt.*;
-import org.apache.poi.util.LittleEndianInput;
-
-public class BinaryRC4EncryptionInfoBuilder implements EncryptionInfoBuilder {
-
- public BinaryRC4EncryptionInfoBuilder() {
- }
-
- @Override
- public void initialize(EncryptionInfo info, LittleEndianInput dis)
- throws IOException {
- int vMajor = info.getVersionMajor();
- int vMinor = info.getVersionMinor();
- assert (vMajor == 1 && vMinor == 1);
-
- info.setHeader(new BinaryRC4EncryptionHeader());
- info.setVerifier(new BinaryRC4EncryptionVerifier(dis));
- Decryptor dec = new BinaryRC4Decryptor();
- dec.setEncryptionInfo(info);
- info.setDecryptor(dec);
- Encryptor enc = new BinaryRC4Encryptor();
- enc.setEncryptionInfo(info);
- info.setEncryptor(enc);
- }
-
- @Override
- public void initialize(EncryptionInfo info,
- CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm,
- int keyBits, int blockSize, ChainingMode chainingMode) {
- info.setHeader(new BinaryRC4EncryptionHeader());
- info.setVerifier(new BinaryRC4EncryptionVerifier());
- Decryptor dec = new BinaryRC4Decryptor();
- dec.setEncryptionInfo(info);
- info.setDecryptor(dec);
- Encryptor enc = new BinaryRC4Encryptor();
- enc.setEncryptionInfo(info);
- info.setEncryptor(enc);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.binaryrc4;
+
+import java.io.IOException;
+import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.util.LittleEndianInput;
+
+public class BinaryRC4EncryptionInfoBuilder implements EncryptionInfoBuilder {
+
+ public BinaryRC4EncryptionInfoBuilder() {
+ }
+
+ @Override
+ public void initialize(EncryptionInfo info, LittleEndianInput dis)
+ throws IOException {
+ int vMajor = info.getVersionMajor();
+ int vMinor = info.getVersionMinor();
+ assert (vMajor == 1 && vMinor == 1);
+
+ info.setHeader(new BinaryRC4EncryptionHeader());
+ info.setVerifier(new BinaryRC4EncryptionVerifier(dis));
+ Decryptor dec = new BinaryRC4Decryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ Encryptor enc = new BinaryRC4Encryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
+ }
+
+ @Override
+ public void initialize(EncryptionInfo info,
+ CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm,
+ int keyBits, int blockSize, ChainingMode chainingMode) {
+ info.setHeader(new BinaryRC4EncryptionHeader());
+ info.setVerifier(new BinaryRC4EncryptionVerifier());
+ Decryptor dec = new BinaryRC4Decryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ Encryptor enc = new BinaryRC4Encryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java
index 654b64a3e5..b20ed0ce76 100644
--- a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java
+++ b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java
@@ -1,89 +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.poi.poifs.crypt.binaryrc4;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.*;
-import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-import org.apache.poi.util.LittleEndianInput;
-
-public class BinaryRC4EncryptionVerifier extends EncryptionVerifier implements EncryptionRecord, Cloneable {
-
- protected BinaryRC4EncryptionVerifier() {
- setSpinCount(-1);
- setCipherAlgorithm(CipherAlgorithm.rc4);
- setChainingMode(null);
- setEncryptedKey(null);
- setHashAlgorithm(HashAlgorithm.md5);
- }
-
- protected BinaryRC4EncryptionVerifier(LittleEndianInput is) {
- byte salt[] = new byte[16];
- is.readFully(salt);
- setSalt(salt);
- byte encryptedVerifier[] = new byte[16];
- is.readFully(encryptedVerifier);
- setEncryptedVerifier(encryptedVerifier);
- byte encryptedVerifierHash[] = new byte[16];
- is.readFully(encryptedVerifierHash);
- setEncryptedVerifierHash(encryptedVerifierHash);
- setSpinCount(-1);
- setCipherAlgorithm(CipherAlgorithm.rc4);
- setChainingMode(null);
- setEncryptedKey(null);
- setHashAlgorithm(HashAlgorithm.md5);
- }
-
- @Override
- protected void setSalt(byte salt[]) {
- if (salt == null || salt.length != 16) {
- throw new EncryptedDocumentException("invalid verifier salt");
- }
-
- super.setSalt(salt);
- }
-
- @Override
- protected void setEncryptedVerifier(byte encryptedVerifier[]) {
- super.setEncryptedVerifier(encryptedVerifier);
- }
-
- @Override
- protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
- super.setEncryptedVerifierHash(encryptedVerifierHash);
- }
-
- @Override
- public void write(LittleEndianByteArrayOutputStream bos) {
- byte salt[] = getSalt();
- assert (salt.length == 16);
- bos.write(salt);
- byte encryptedVerifier[] = getEncryptedVerifier();
- assert (encryptedVerifier.length == 16);
- bos.write(encryptedVerifier);
- byte encryptedVerifierHash[] = getEncryptedVerifierHash();
- assert (encryptedVerifierHash.length == 16);
- bos.write(encryptedVerifierHash);
- }
-
- @Override
- public BinaryRC4EncryptionVerifier clone() throws CloneNotSupportedException {
- return (BinaryRC4EncryptionVerifier)super.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.binaryrc4;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianInput;
+
+public class BinaryRC4EncryptionVerifier extends EncryptionVerifier implements EncryptionRecord, Cloneable {
+
+ protected BinaryRC4EncryptionVerifier() {
+ setSpinCount(-1);
+ setCipherAlgorithm(CipherAlgorithm.rc4);
+ setChainingMode(null);
+ setEncryptedKey(null);
+ setHashAlgorithm(HashAlgorithm.md5);
+ }
+
+ protected BinaryRC4EncryptionVerifier(LittleEndianInput is) {
+ byte salt[] = new byte[16];
+ is.readFully(salt);
+ setSalt(salt);
+ byte encryptedVerifier[] = new byte[16];
+ is.readFully(encryptedVerifier);
+ setEncryptedVerifier(encryptedVerifier);
+ byte encryptedVerifierHash[] = new byte[16];
+ is.readFully(encryptedVerifierHash);
+ setEncryptedVerifierHash(encryptedVerifierHash);
+ setSpinCount(-1);
+ setCipherAlgorithm(CipherAlgorithm.rc4);
+ setChainingMode(null);
+ setEncryptedKey(null);
+ setHashAlgorithm(HashAlgorithm.md5);
+ }
+
+ @Override
+ protected void setSalt(byte salt[]) {
+ if (salt == null || salt.length != 16) {
+ throw new EncryptedDocumentException("invalid verifier salt");
+ }
+
+ super.setSalt(salt);
+ }
+
+ @Override
+ protected void setEncryptedVerifier(byte encryptedVerifier[]) {
+ super.setEncryptedVerifier(encryptedVerifier);
+ }
+
+ @Override
+ protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
+ super.setEncryptedVerifierHash(encryptedVerifierHash);
+ }
+
+ @Override
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ byte salt[] = getSalt();
+ assert (salt.length == 16);
+ bos.write(salt);
+ byte encryptedVerifier[] = getEncryptedVerifier();
+ assert (encryptedVerifier.length == 16);
+ bos.write(encryptedVerifier);
+ byte encryptedVerifierHash[] = getEncryptedVerifierHash();
+ assert (encryptedVerifierHash.length == 16);
+ bos.write(encryptedVerifierHash);
+ }
+
+ @Override
+ public BinaryRC4EncryptionVerifier clone() throws CloneNotSupportedException {
+ return (BinaryRC4EncryptionVerifier)super.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java
index 15bb476ea9..743fedb38f 100644
--- a/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java
@@ -1,160 +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.poi.poifs.crypt.binaryrc4;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
-import org.apache.poi.poifs.crypt.CryptoFunctions;
-import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.Encryptor;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-
-public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
-
- private int chunkSize = 512;
-
- protected BinaryRC4Encryptor() {
- }
-
- @Override
- public void confirmPassword(String password) {
- Random r = new SecureRandom();
- byte salt[] = new byte[16];
- byte verifier[] = new byte[16];
- r.nextBytes(salt);
- r.nextBytes(verifier);
- confirmPassword(password, null, null, verifier, salt, null);
- }
-
- @Override
- public void confirmPassword(String password, byte keySpec[],
- byte keySalt[], byte verifier[], byte verifierSalt[],
- byte integritySalt[]) {
- BinaryRC4EncryptionVerifier ver = (BinaryRC4EncryptionVerifier)getEncryptionInfo().getVerifier();
- ver.setSalt(verifierSalt);
- SecretKey skey = BinaryRC4Decryptor.generateSecretKey(password, ver);
- setSecretKey(skey);
- try {
- Cipher cipher = BinaryRC4Decryptor.initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.ENCRYPT_MODE);
- byte encryptedVerifier[] = new byte[16];
- cipher.update(verifier, 0, 16, encryptedVerifier);
- ver.setEncryptedVerifier(encryptedVerifier);
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- byte calcVerifierHash[] = hashAlg.digest(verifier);
- byte encryptedVerifierHash[] = cipher.doFinal(calcVerifierHash);
- ver.setEncryptedVerifierHash(encryptedVerifierHash);
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException("Password confirmation failed", e);
- }
- }
-
- @Override
- public OutputStream getDataStream(DirectoryNode dir)
- throws IOException, GeneralSecurityException {
- OutputStream countStream = new BinaryRC4CipherOutputStream(dir);
- return countStream;
- }
-
- @Override
- public BinaryRC4CipherOutputStream getDataStream(OutputStream stream, int initialOffset)
- throws IOException, GeneralSecurityException {
- return new BinaryRC4CipherOutputStream(stream);
- }
-
- protected int getKeySizeInBytes() {
- return getEncryptionInfo().getHeader().getKeySize() / 8;
- }
-
- protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
- DataSpaceMapUtils.addDefaultDataSpace(dir);
- final EncryptionInfo info = getEncryptionInfo();
- final BinaryRC4EncryptionHeader header = (BinaryRC4EncryptionHeader)info.getHeader();
- final BinaryRC4EncryptionVerifier verifier = (BinaryRC4EncryptionVerifier)info.getVerifier();
- EncryptionRecord er = new EncryptionRecord() {
- @Override
- public void write(LittleEndianByteArrayOutputStream bos) {
- bos.writeShort(info.getVersionMajor());
- bos.writeShort(info.getVersionMinor());
- header.write(bos);
- verifier.write(bos);
- }
- };
- DataSpaceMapUtils.createEncryptionEntry(dir, "EncryptionInfo", er);
- }
-
- @Override
- public void setChunkSize(int chunkSize) {
- this.chunkSize = chunkSize;
- }
-
- @Override
- public BinaryRC4Encryptor clone() throws CloneNotSupportedException {
- return (BinaryRC4Encryptor)super.clone();
- }
-
- protected class BinaryRC4CipherOutputStream extends ChunkedCipherOutputStream {
-
- public BinaryRC4CipherOutputStream(OutputStream stream)
- throws IOException, GeneralSecurityException {
- super(stream, BinaryRC4Encryptor.this.chunkSize);
- }
-
- public BinaryRC4CipherOutputStream(DirectoryNode dir)
- throws IOException, GeneralSecurityException {
- super(dir, BinaryRC4Encryptor.this.chunkSize);
- }
-
- @Override
- protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk)
- throws GeneralSecurityException {
- return BinaryRC4Decryptor.initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
- }
-
- @Override
- protected void calculateChecksum(File file, int i) {
- }
-
- @Override
- protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
- throws IOException, GeneralSecurityException {
- BinaryRC4Encryptor.this.createEncryptionInfoEntry(dir);
- }
-
- @Override
- public void flush() throws IOException {
- writeChunk(false);
- super.flush();
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.binaryrc4;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+
+public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
+
+ private int chunkSize = 512;
+
+ protected BinaryRC4Encryptor() {
+ }
+
+ @Override
+ public void confirmPassword(String password) {
+ Random r = new SecureRandom();
+ byte salt[] = new byte[16];
+ byte verifier[] = new byte[16];
+ r.nextBytes(salt);
+ r.nextBytes(verifier);
+ confirmPassword(password, null, null, verifier, salt, null);
+ }
+
+ @Override
+ public void confirmPassword(String password, byte keySpec[],
+ byte keySalt[], byte verifier[], byte verifierSalt[],
+ byte integritySalt[]) {
+ BinaryRC4EncryptionVerifier ver = (BinaryRC4EncryptionVerifier)getEncryptionInfo().getVerifier();
+ ver.setSalt(verifierSalt);
+ SecretKey skey = BinaryRC4Decryptor.generateSecretKey(password, ver);
+ setSecretKey(skey);
+ try {
+ Cipher cipher = BinaryRC4Decryptor.initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.ENCRYPT_MODE);
+ byte encryptedVerifier[] = new byte[16];
+ cipher.update(verifier, 0, 16, encryptedVerifier);
+ ver.setEncryptedVerifier(encryptedVerifier);
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ byte calcVerifierHash[] = hashAlg.digest(verifier);
+ byte encryptedVerifierHash[] = cipher.doFinal(calcVerifierHash);
+ ver.setEncryptedVerifierHash(encryptedVerifierHash);
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("Password confirmation failed", e);
+ }
+ }
+
+ @Override
+ public OutputStream getDataStream(DirectoryNode dir)
+ throws IOException, GeneralSecurityException {
+ OutputStream countStream = new BinaryRC4CipherOutputStream(dir);
+ return countStream;
+ }
+
+ @Override
+ public BinaryRC4CipherOutputStream getDataStream(OutputStream stream, int initialOffset)
+ throws IOException, GeneralSecurityException {
+ return new BinaryRC4CipherOutputStream(stream);
+ }
+
+ protected int getKeySizeInBytes() {
+ return getEncryptionInfo().getHeader().getKeySize() / 8;
+ }
+
+ protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
+ DataSpaceMapUtils.addDefaultDataSpace(dir);
+ final EncryptionInfo info = getEncryptionInfo();
+ final BinaryRC4EncryptionHeader header = (BinaryRC4EncryptionHeader)info.getHeader();
+ final BinaryRC4EncryptionVerifier verifier = (BinaryRC4EncryptionVerifier)info.getVerifier();
+ EncryptionRecord er = new EncryptionRecord() {
+ @Override
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ bos.writeShort(info.getVersionMajor());
+ bos.writeShort(info.getVersionMinor());
+ header.write(bos);
+ verifier.write(bos);
+ }
+ };
+ DataSpaceMapUtils.createEncryptionEntry(dir, "EncryptionInfo", er);
+ }
+
+ @Override
+ public void setChunkSize(int chunkSize) {
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ public BinaryRC4Encryptor clone() throws CloneNotSupportedException {
+ return (BinaryRC4Encryptor)super.clone();
+ }
+
+ protected class BinaryRC4CipherOutputStream extends ChunkedCipherOutputStream {
+
+ public BinaryRC4CipherOutputStream(OutputStream stream)
+ throws IOException, GeneralSecurityException {
+ super(stream, BinaryRC4Encryptor.this.chunkSize);
+ }
+
+ public BinaryRC4CipherOutputStream(DirectoryNode dir)
+ throws IOException, GeneralSecurityException {
+ super(dir, BinaryRC4Encryptor.this.chunkSize);
+ }
+
+ @Override
+ protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk)
+ throws GeneralSecurityException {
+ return BinaryRC4Decryptor.initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
+ }
+
+ @Override
+ protected void calculateChecksum(File file, int i) {
+ }
+
+ @Override
+ protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
+ throws IOException, GeneralSecurityException {
+ BinaryRC4Encryptor.this.createEncryptionInfoEntry(dir);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writeChunk(false);
+ super.flush();
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
index 669931f9e9..222b425178 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
@@ -1,255 +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.poi.poifs.crypt.cryptoapi;
-
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.util.Arrays;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
-import org.apache.poi.poifs.crypt.CryptoFunctions;
-import org.apache.poi.poifs.crypt.Decryptor;
-import org.apache.poi.poifs.crypt.EncryptionHeader;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.EncryptionVerifier;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.DocumentNode;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.BoundedInputStream;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianInputStream;
-import org.apache.poi.util.StringUtil;
-
-public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
-
- private long length = -1L;
- private int chunkSize = -1;
-
- static class StreamDescriptorEntry {
- static BitField flagStream = BitFieldFactory.getInstance(1);
-
- int streamOffset;
- int streamSize;
- int block;
- int flags;
- int reserved2;
- String streamName;
- }
-
- protected CryptoAPIDecryptor() {
- }
-
- @Override
- public boolean verifyPassword(String password) {
- EncryptionVerifier ver = getEncryptionInfo().getVerifier();
- SecretKey skey = generateSecretKey(password, ver);
- try {
- Cipher cipher = initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.DECRYPT_MODE);
- byte encryptedVerifier[] = ver.getEncryptedVerifier();
- byte verifier[] = new byte[encryptedVerifier.length];
- cipher.update(encryptedVerifier, 0, encryptedVerifier.length, verifier);
- setVerifier(verifier);
- byte encryptedVerifierHash[] = ver.getEncryptedVerifierHash();
- byte verifierHash[] = cipher.doFinal(encryptedVerifierHash);
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- byte calcVerifierHash[] = hashAlg.digest(verifier);
- if (Arrays.equals(calcVerifierHash, verifierHash)) {
- setSecretKey(skey);
- return true;
- }
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException(e);
- }
- return false;
- }
-
- @Override
- public Cipher initCipherForBlock(Cipher cipher, int block)
- throws GeneralSecurityException {
- EncryptionInfo ei = getEncryptionInfo();
- SecretKey sk = getSecretKey();
- return initCipherForBlock(cipher, block, ei, sk, Cipher.DECRYPT_MODE);
- }
-
- protected static Cipher initCipherForBlock(Cipher cipher, int block,
- EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
- throws GeneralSecurityException {
- EncryptionVerifier ver = encryptionInfo.getVerifier();
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- byte blockKey[] = new byte[4];
- LittleEndian.putUInt(blockKey, 0, block);
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- hashAlg.update(skey.getEncoded());
- byte encKey[] = hashAlg.digest(blockKey);
- EncryptionHeader header = encryptionInfo.getHeader();
- int keyBits = header.getKeySize();
- encKey = CryptoFunctions.getBlock0(encKey, keyBits / 8);
- if (keyBits == 40) {
- encKey = CryptoFunctions.getBlock0(encKey, 16);
- }
- SecretKey key = new SecretKeySpec(encKey, skey.getAlgorithm());
- if (cipher == null) {
- cipher = CryptoFunctions.getCipher(key, header.getCipherAlgorithm(), null, null, encryptMode);
- } else {
- cipher.init(encryptMode, key);
- }
- return cipher;
- }
-
- protected static SecretKey generateSecretKey(String password, EncryptionVerifier ver) {
- if (password.length() > 255) {
- password = password.substring(0, 255);
- }
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- hashAlg.update(ver.getSalt());
- byte hash[] = hashAlg.digest(StringUtil.getToUnicodeLE(password));
- SecretKey skey = new SecretKeySpec(hash, ver.getCipherAlgorithm().jceId);
- return skey;
- }
-
- @Override
- public ChunkedCipherInputStream getDataStream(DirectoryNode dir)
- throws IOException, GeneralSecurityException {
- throw new IOException("not supported");
- }
-
- @Override
- public ChunkedCipherInputStream getDataStream(InputStream stream, int size, int initialPos)
- throws IOException, GeneralSecurityException {
- return new CryptoAPICipherInputStream(stream, size, initialPos);
- }
-
- /**
- * Decrypt the Document-/SummaryInformation and other optionally streams.
- * Opposed to other crypto modes, cryptoapi is record based and can't be used
- * to stream-decrypt a whole file
- *
- * @see 2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream
- */
- public POIFSFileSystem getSummaryEntries(DirectoryNode root, String encryptedStream)
- throws IOException, GeneralSecurityException {
- // HSLF: encryptedStream
- // HSSF: encryption
- DocumentNode es = (DocumentNode) root.getEntry(encryptedStream);
- DocumentInputStream dis = root.createDocumentInputStream(es);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- IOUtils.copy(dis, bos);
- dis.close();
- CryptoAPIDocumentInputStream sbis = new CryptoAPIDocumentInputStream(this, bos.toByteArray());
- LittleEndianInputStream leis = new LittleEndianInputStream(sbis);
- POIFSFileSystem fsOut = null;
- try {
- int streamDescriptorArrayOffset = (int) leis.readUInt();
- /* int streamDescriptorArraySize = (int) */ leis.readUInt();
- long skipN = streamDescriptorArrayOffset - 8L;
- if (sbis.skip(skipN) < skipN) {
- throw new EOFException("buffer underrun");
- }
- sbis.setBlock(0);
- int encryptedStreamDescriptorCount = (int) leis.readUInt();
- StreamDescriptorEntry entries[] = new StreamDescriptorEntry[encryptedStreamDescriptorCount];
- for (int i = 0; i < encryptedStreamDescriptorCount; i++) {
- StreamDescriptorEntry entry = new StreamDescriptorEntry();
- entries[i] = entry;
- entry.streamOffset = (int) leis.readUInt();
- entry.streamSize = (int) leis.readUInt();
- entry.block = leis.readUShort();
- int nameSize = leis.readUByte();
- entry.flags = leis.readUByte();
- // boolean isStream = StreamDescriptorEntry.flagStream.isSet(entry.flags);
- entry.reserved2 = leis.readInt();
- entry.streamName = StringUtil.readUnicodeLE(leis, nameSize);
- leis.readShort();
- assert(entry.streamName.length() == nameSize);
- }
-
- fsOut = new POIFSFileSystem(); // NOSONAR
- for (StreamDescriptorEntry entry : entries) {
- sbis.seek(entry.streamOffset);
- sbis.setBlock(entry.block);
- InputStream is = new BoundedInputStream(sbis, entry.streamSize);
- fsOut.createDocument(is, entry.streamName);
- is.close();
- }
- } catch (Exception e) {
- IOUtils.closeQuietly(fsOut);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException)e;
- } else if (e instanceof IOException) {
- throw (IOException)e;
- } else {
- throw new IOException("summary entries can't be read", e);
- }
- } finally {
- IOUtils.closeQuietly(leis);
- IOUtils.closeQuietly(sbis);
- }
- return fsOut;
- }
-
- /**
- * @return the length of the stream returned by {@link #getDataStream(DirectoryNode)}
- */
- @Override
- public long getLength() {
- if (length == -1L) {
- throw new IllegalStateException("Decryptor.getDataStream() was not called");
- }
- return length;
- }
-
- @Override
- public void setChunkSize(int chunkSize) {
- this.chunkSize = chunkSize;
- }
-
- @Override
- public CryptoAPIDecryptor clone() throws CloneNotSupportedException {
- return (CryptoAPIDecryptor)super.clone();
- }
-
- private class CryptoAPICipherInputStream extends ChunkedCipherInputStream {
-
- @Override
- protected Cipher initCipherForBlock(Cipher existing, int block)
- throws GeneralSecurityException {
- return CryptoAPIDecryptor.this.initCipherForBlock(existing, block);
- }
-
- public CryptoAPICipherInputStream(InputStream stream, long size, int initialPos)
- throws GeneralSecurityException {
- super(stream, size, chunkSize, initialPos);
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.cryptoapi;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.Decryptor;
+import org.apache.poi.poifs.crypt.EncryptionHeader;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionVerifier;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.DocumentNode;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.BoundedInputStream;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.StringUtil;
+
+public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
+
+ private long length = -1L;
+ private int chunkSize = -1;
+
+ static class StreamDescriptorEntry {
+ static BitField flagStream = BitFieldFactory.getInstance(1);
+
+ int streamOffset;
+ int streamSize;
+ int block;
+ int flags;
+ int reserved2;
+ String streamName;
+ }
+
+ protected CryptoAPIDecryptor() {
+ }
+
+ @Override
+ public boolean verifyPassword(String password) {
+ EncryptionVerifier ver = getEncryptionInfo().getVerifier();
+ SecretKey skey = generateSecretKey(password, ver);
+ try {
+ Cipher cipher = initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.DECRYPT_MODE);
+ byte encryptedVerifier[] = ver.getEncryptedVerifier();
+ byte verifier[] = new byte[encryptedVerifier.length];
+ cipher.update(encryptedVerifier, 0, encryptedVerifier.length, verifier);
+ setVerifier(verifier);
+ byte encryptedVerifierHash[] = ver.getEncryptedVerifierHash();
+ byte verifierHash[] = cipher.doFinal(encryptedVerifierHash);
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ byte calcVerifierHash[] = hashAlg.digest(verifier);
+ if (Arrays.equals(calcVerifierHash, verifierHash)) {
+ setSecretKey(skey);
+ return true;
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ return false;
+ }
+
+ @Override
+ public Cipher initCipherForBlock(Cipher cipher, int block)
+ throws GeneralSecurityException {
+ EncryptionInfo ei = getEncryptionInfo();
+ SecretKey sk = getSecretKey();
+ return initCipherForBlock(cipher, block, ei, sk, Cipher.DECRYPT_MODE);
+ }
+
+ protected static Cipher initCipherForBlock(Cipher cipher, int block,
+ EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
+ throws GeneralSecurityException {
+ EncryptionVerifier ver = encryptionInfo.getVerifier();
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ byte blockKey[] = new byte[4];
+ LittleEndian.putUInt(blockKey, 0, block);
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ hashAlg.update(skey.getEncoded());
+ byte encKey[] = hashAlg.digest(blockKey);
+ EncryptionHeader header = encryptionInfo.getHeader();
+ int keyBits = header.getKeySize();
+ encKey = CryptoFunctions.getBlock0(encKey, keyBits / 8);
+ if (keyBits == 40) {
+ encKey = CryptoFunctions.getBlock0(encKey, 16);
+ }
+ SecretKey key = new SecretKeySpec(encKey, skey.getAlgorithm());
+ if (cipher == null) {
+ cipher = CryptoFunctions.getCipher(key, header.getCipherAlgorithm(), null, null, encryptMode);
+ } else {
+ cipher.init(encryptMode, key);
+ }
+ return cipher;
+ }
+
+ protected static SecretKey generateSecretKey(String password, EncryptionVerifier ver) {
+ if (password.length() > 255) {
+ password = password.substring(0, 255);
+ }
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ hashAlg.update(ver.getSalt());
+ byte hash[] = hashAlg.digest(StringUtil.getToUnicodeLE(password));
+ SecretKey skey = new SecretKeySpec(hash, ver.getCipherAlgorithm().jceId);
+ return skey;
+ }
+
+ @Override
+ public ChunkedCipherInputStream getDataStream(DirectoryNode dir)
+ throws IOException, GeneralSecurityException {
+ throw new IOException("not supported");
+ }
+
+ @Override
+ public ChunkedCipherInputStream getDataStream(InputStream stream, int size, int initialPos)
+ throws IOException, GeneralSecurityException {
+ return new CryptoAPICipherInputStream(stream, size, initialPos);
+ }
+
+ /**
+ * Decrypt the Document-/SummaryInformation and other optionally streams.
+ * Opposed to other crypto modes, cryptoapi is record based and can't be used
+ * to stream-decrypt a whole file
+ *
+ * @see 2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream
+ */
+ public POIFSFileSystem getSummaryEntries(DirectoryNode root, String encryptedStream)
+ throws IOException, GeneralSecurityException {
+ // HSLF: encryptedStream
+ // HSSF: encryption
+ DocumentNode es = (DocumentNode) root.getEntry(encryptedStream);
+ DocumentInputStream dis = root.createDocumentInputStream(es);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ IOUtils.copy(dis, bos);
+ dis.close();
+ CryptoAPIDocumentInputStream sbis = new CryptoAPIDocumentInputStream(this, bos.toByteArray());
+ LittleEndianInputStream leis = new LittleEndianInputStream(sbis);
+ POIFSFileSystem fsOut = null;
+ try {
+ int streamDescriptorArrayOffset = (int) leis.readUInt();
+ /* int streamDescriptorArraySize = (int) */ leis.readUInt();
+ long skipN = streamDescriptorArrayOffset - 8L;
+ if (sbis.skip(skipN) < skipN) {
+ throw new EOFException("buffer underrun");
+ }
+ sbis.setBlock(0);
+ int encryptedStreamDescriptorCount = (int) leis.readUInt();
+ StreamDescriptorEntry entries[] = new StreamDescriptorEntry[encryptedStreamDescriptorCount];
+ for (int i = 0; i < encryptedStreamDescriptorCount; i++) {
+ StreamDescriptorEntry entry = new StreamDescriptorEntry();
+ entries[i] = entry;
+ entry.streamOffset = (int) leis.readUInt();
+ entry.streamSize = (int) leis.readUInt();
+ entry.block = leis.readUShort();
+ int nameSize = leis.readUByte();
+ entry.flags = leis.readUByte();
+ // boolean isStream = StreamDescriptorEntry.flagStream.isSet(entry.flags);
+ entry.reserved2 = leis.readInt();
+ entry.streamName = StringUtil.readUnicodeLE(leis, nameSize);
+ leis.readShort();
+ assert(entry.streamName.length() == nameSize);
+ }
+
+ fsOut = new POIFSFileSystem(); // NOSONAR
+ for (StreamDescriptorEntry entry : entries) {
+ sbis.seek(entry.streamOffset);
+ sbis.setBlock(entry.block);
+ InputStream is = new BoundedInputStream(sbis, entry.streamSize);
+ fsOut.createDocument(is, entry.streamName);
+ is.close();
+ }
+ } catch (Exception e) {
+ IOUtils.closeQuietly(fsOut);
+ if (e instanceof GeneralSecurityException) {
+ throw (GeneralSecurityException)e;
+ } else if (e instanceof IOException) {
+ throw (IOException)e;
+ } else {
+ throw new IOException("summary entries can't be read", e);
+ }
+ } finally {
+ IOUtils.closeQuietly(leis);
+ IOUtils.closeQuietly(sbis);
+ }
+ return fsOut;
+ }
+
+ /**
+ * @return the length of the stream returned by {@link #getDataStream(DirectoryNode)}
+ */
+ @Override
+ public long getLength() {
+ if (length == -1L) {
+ throw new IllegalStateException("Decryptor.getDataStream() was not called");
+ }
+ return length;
+ }
+
+ @Override
+ public void setChunkSize(int chunkSize) {
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ public CryptoAPIDecryptor clone() throws CloneNotSupportedException {
+ return (CryptoAPIDecryptor)super.clone();
+ }
+
+ private class CryptoAPICipherInputStream extends ChunkedCipherInputStream {
+
+ @Override
+ protected Cipher initCipherForBlock(Cipher existing, int block)
+ throws GeneralSecurityException {
+ return CryptoAPIDecryptor.this.initCipherForBlock(existing, block);
+ }
+
+ public CryptoAPICipherInputStream(InputStream stream, long size, int initialPos)
+ throws GeneralSecurityException {
+ super(stream, size, chunkSize, initialPos);
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java
index b54dc2f0d5..2e838f7ddf 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java
@@ -1,68 +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.poi.poifs.crypt.cryptoapi;
-
-import java.io.IOException;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.ChainingMode;
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.CipherProvider;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.poifs.crypt.standard.StandardEncryptionHeader;
-import org.apache.poi.util.LittleEndianInput;
-
-public class CryptoAPIEncryptionHeader extends StandardEncryptionHeader implements Cloneable {
-
- public CryptoAPIEncryptionHeader(LittleEndianInput is) throws IOException {
- super(is);
- }
-
- protected CryptoAPIEncryptionHeader(CipherAlgorithm cipherAlgorithm,
- HashAlgorithm hashAlgorithm, int keyBits, int blockSize,
- ChainingMode chainingMode) {
- super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
- }
-
- @Override
- public void setKeySize(int keyBits) {
- // Microsoft Base Cryptographic Provider is limited up to 40 bits
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375599(v=vs.85).aspx
- boolean found = false;
- for (int size : getCipherAlgorithm().allowedKeySize) {
- if (size == keyBits) {
- found = true;
- break;
- }
- }
- if (!found) {
- throw new EncryptedDocumentException("invalid keysize "+keyBits+" for cipher algorithm "+getCipherAlgorithm());
- }
- super.setKeySize(keyBits);
- if (keyBits > 40) {
- setCspName("Microsoft Enhanced Cryptographic Provider v1.0");
- } else {
- setCspName(CipherProvider.rc4.cipherProviderName);
- }
- }
-
- @Override
- public CryptoAPIEncryptionHeader clone() throws CloneNotSupportedException {
- return (CryptoAPIEncryptionHeader)super.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.cryptoapi;
+
+import java.io.IOException;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChainingMode;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.CipherProvider;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.standard.StandardEncryptionHeader;
+import org.apache.poi.util.LittleEndianInput;
+
+public class CryptoAPIEncryptionHeader extends StandardEncryptionHeader implements Cloneable {
+
+ public CryptoAPIEncryptionHeader(LittleEndianInput is) throws IOException {
+ super(is);
+ }
+
+ protected CryptoAPIEncryptionHeader(CipherAlgorithm cipherAlgorithm,
+ HashAlgorithm hashAlgorithm, int keyBits, int blockSize,
+ ChainingMode chainingMode) {
+ super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
+ }
+
+ @Override
+ public void setKeySize(int keyBits) {
+ // Microsoft Base Cryptographic Provider is limited up to 40 bits
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375599(v=vs.85).aspx
+ boolean found = false;
+ for (int size : getCipherAlgorithm().allowedKeySize) {
+ if (size == keyBits) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new EncryptedDocumentException("invalid keysize "+keyBits+" for cipher algorithm "+getCipherAlgorithm());
+ }
+ super.setKeySize(keyBits);
+ if (keyBits > 40) {
+ setCspName("Microsoft Enhanced Cryptographic Provider v1.0");
+ } else {
+ setCspName(CipherProvider.rc4.cipherProviderName);
+ }
+ }
+
+ @Override
+ public CryptoAPIEncryptionHeader clone() throws CloneNotSupportedException {
+ return (CryptoAPIEncryptionHeader)super.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java
index fef4dde168..c1484d691b 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java
@@ -1,74 +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.poi.poifs.crypt.cryptoapi;
-
-import java.io.IOException;
-
-import org.apache.poi.poifs.crypt.*;
-import org.apache.poi.util.LittleEndianInput;
-
-public class CryptoAPIEncryptionInfoBuilder implements EncryptionInfoBuilder {
- public CryptoAPIEncryptionInfoBuilder() {
- }
-
- /**
- * initialize the builder from a stream
- */
- @Override
- public void initialize(EncryptionInfo info, LittleEndianInput dis)
- throws IOException {
- /* int hSize = */ dis.readInt();
- CryptoAPIEncryptionHeader header = new CryptoAPIEncryptionHeader(dis);
- info.setHeader(header);
- info.setVerifier(new CryptoAPIEncryptionVerifier(dis, header));
- CryptoAPIDecryptor dec = new CryptoAPIDecryptor();
- dec.setEncryptionInfo(info);
- info.setDecryptor(dec);
- CryptoAPIEncryptor enc = new CryptoAPIEncryptor();
- enc.setEncryptionInfo(info);
- info.setEncryptor(enc);
- }
-
- /**
- * initialize the builder from scratch
- */
- @Override
- public void initialize(EncryptionInfo info,
- CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm,
- int keyBits, int blockSize, ChainingMode chainingMode) {
- if (cipherAlgorithm == null) {
- cipherAlgorithm = CipherAlgorithm.rc4;
- }
- if (hashAlgorithm == null) {
- hashAlgorithm = HashAlgorithm.sha1;
- }
- if (keyBits == -1) {
- keyBits = 0x28;
- }
- assert(cipherAlgorithm == CipherAlgorithm.rc4 && hashAlgorithm == HashAlgorithm.sha1);
-
- info.setHeader(new CryptoAPIEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
- info.setVerifier(new CryptoAPIEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
- CryptoAPIDecryptor dec = new CryptoAPIDecryptor();
- dec.setEncryptionInfo(info);
- info.setDecryptor(dec);
- CryptoAPIEncryptor enc = new CryptoAPIEncryptor();
- enc.setEncryptionInfo(info);
- info.setEncryptor(enc);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.cryptoapi;
+
+import java.io.IOException;
+
+import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.util.LittleEndianInput;
+
+public class CryptoAPIEncryptionInfoBuilder implements EncryptionInfoBuilder {
+ public CryptoAPIEncryptionInfoBuilder() {
+ }
+
+ /**
+ * initialize the builder from a stream
+ */
+ @Override
+ public void initialize(EncryptionInfo info, LittleEndianInput dis)
+ throws IOException {
+ /* int hSize = */ dis.readInt();
+ CryptoAPIEncryptionHeader header = new CryptoAPIEncryptionHeader(dis);
+ info.setHeader(header);
+ info.setVerifier(new CryptoAPIEncryptionVerifier(dis, header));
+ CryptoAPIDecryptor dec = new CryptoAPIDecryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ CryptoAPIEncryptor enc = new CryptoAPIEncryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
+ }
+
+ /**
+ * initialize the builder from scratch
+ */
+ @Override
+ public void initialize(EncryptionInfo info,
+ CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm,
+ int keyBits, int blockSize, ChainingMode chainingMode) {
+ if (cipherAlgorithm == null) {
+ cipherAlgorithm = CipherAlgorithm.rc4;
+ }
+ if (hashAlgorithm == null) {
+ hashAlgorithm = HashAlgorithm.sha1;
+ }
+ if (keyBits == -1) {
+ keyBits = 0x28;
+ }
+ assert(cipherAlgorithm == CipherAlgorithm.rc4 && hashAlgorithm == HashAlgorithm.sha1);
+
+ info.setHeader(new CryptoAPIEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+ info.setVerifier(new CryptoAPIEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+ CryptoAPIDecryptor dec = new CryptoAPIDecryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ CryptoAPIEncryptor enc = new CryptoAPIEncryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java
index d2c87b7ab9..f5482c58e7 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java
@@ -1,58 +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.poi.poifs.crypt.cryptoapi;
-
-import org.apache.poi.poifs.crypt.ChainingMode;
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.poifs.crypt.standard.StandardEncryptionVerifier;
-import org.apache.poi.util.LittleEndianInput;
-
-public class CryptoAPIEncryptionVerifier extends StandardEncryptionVerifier implements Cloneable {
-
- protected CryptoAPIEncryptionVerifier(LittleEndianInput is,
- CryptoAPIEncryptionHeader header) {
- super(is, header);
- }
-
- protected CryptoAPIEncryptionVerifier(CipherAlgorithm cipherAlgorithm,
- HashAlgorithm hashAlgorithm, int keyBits, int blockSize,
- ChainingMode chainingMode) {
- super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
- }
-
- @Override
- protected void setSalt(byte salt[]) {
- super.setSalt(salt);
- }
-
- @Override
- protected void setEncryptedVerifier(byte encryptedVerifier[]) {
- super.setEncryptedVerifier(encryptedVerifier);
- }
-
- @Override
- protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
- super.setEncryptedVerifierHash(encryptedVerifierHash);
- }
-
- @Override
- public CryptoAPIEncryptionVerifier clone() throws CloneNotSupportedException {
- return (CryptoAPIEncryptionVerifier)super.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.cryptoapi;
+
+import org.apache.poi.poifs.crypt.ChainingMode;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.standard.StandardEncryptionVerifier;
+import org.apache.poi.util.LittleEndianInput;
+
+public class CryptoAPIEncryptionVerifier extends StandardEncryptionVerifier implements Cloneable {
+
+ protected CryptoAPIEncryptionVerifier(LittleEndianInput is,
+ CryptoAPIEncryptionHeader header) {
+ super(is, header);
+ }
+
+ protected CryptoAPIEncryptionVerifier(CipherAlgorithm cipherAlgorithm,
+ HashAlgorithm hashAlgorithm, int keyBits, int blockSize,
+ ChainingMode chainingMode) {
+ super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
+ }
+
+ @Override
+ protected void setSalt(byte salt[]) {
+ super.setSalt(salt);
+ }
+
+ @Override
+ protected void setEncryptedVerifier(byte encryptedVerifier[]) {
+ super.setEncryptedVerifier(encryptedVerifier);
+ }
+
+ @Override
+ protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
+ super.setEncryptedVerifierHash(encryptedVerifierHash);
+ }
+
+ @Override
+ public CryptoAPIEncryptionVerifier clone() throws CloneNotSupportedException {
+ return (CryptoAPIEncryptionVerifier)super.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
index 5740380d0f..2a47922883 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
@@ -1,277 +1,277 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.poifs.crypt.cryptoapi;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.hpsf.DocumentSummaryInformation;
-import org.apache.poi.hpsf.PropertySetFactory;
-import org.apache.poi.hpsf.SummaryInformation;
-import org.apache.poi.hpsf.WritingNotSupportedException;
-import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
-import org.apache.poi.poifs.crypt.CryptoFunctions;
-import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.Encryptor;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor.StreamDescriptorEntry;
-import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-import org.apache.poi.util.StringUtil;
-
-public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
-
- private int chunkSize = 512;
-
- protected CryptoAPIEncryptor() {
- }
-
- @Override
- public void confirmPassword(String password) {
- Random r = new SecureRandom();
- byte salt[] = new byte[16];
- byte verifier[] = new byte[16];
- r.nextBytes(salt);
- r.nextBytes(verifier);
- confirmPassword(password, null, null, verifier, salt, null);
- }
-
- @Override
- public void confirmPassword(String password, byte keySpec[],
- byte keySalt[], byte verifier[], byte verifierSalt[],
- byte integritySalt[]) {
- assert(verifier != null && verifierSalt != null);
- CryptoAPIEncryptionVerifier ver = (CryptoAPIEncryptionVerifier)getEncryptionInfo().getVerifier();
- ver.setSalt(verifierSalt);
- SecretKey skey = CryptoAPIDecryptor.generateSecretKey(password, ver);
- setSecretKey(skey);
- try {
- Cipher cipher = initCipherForBlock(null, 0);
- byte encryptedVerifier[] = new byte[verifier.length];
- cipher.update(verifier, 0, verifier.length, encryptedVerifier);
- ver.setEncryptedVerifier(encryptedVerifier);
- HashAlgorithm hashAlgo = ver.getHashAlgorithm();
- MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
- byte calcVerifierHash[] = hashAlg.digest(verifier);
- byte encryptedVerifierHash[] = cipher.doFinal(calcVerifierHash);
- ver.setEncryptedVerifierHash(encryptedVerifierHash);
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException("Password confirmation failed", e);
- }
- }
-
- /**
- * Initializes a cipher object for a given block index for encryption
- *
- * @param cipher may be null, otherwise the given instance is reset to the new block index
- * @param block the block index, e.g. the persist/slide id (hslf)
- * @return a new cipher object, if cipher was null, otherwise the reinitialized cipher
- * @throws GeneralSecurityException
- */
- public Cipher initCipherForBlock(Cipher cipher, int block)
- throws GeneralSecurityException {
- return CryptoAPIDecryptor.initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
- }
-
- @Override
- public ChunkedCipherOutputStream getDataStream(DirectoryNode dir)
- throws IOException, GeneralSecurityException {
- throw new IOException("not supported");
- }
-
- @Override
- public CryptoAPICipherOutputStream getDataStream(OutputStream stream, int initialOffset)
- throws IOException, GeneralSecurityException {
- return new CryptoAPICipherOutputStream(stream);
- }
-
- /**
- * Encrypt the Document-/SummaryInformation and other optionally streams.
- * Opposed to other crypto modes, cryptoapi is record based and can't be used
- * to stream-encrypt a whole file
- *
- * @see 2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream
- */
- public OutputStream getSummaryEntries(DirectoryNode dir)
- throws IOException, GeneralSecurityException {
- CryptoAPIDocumentOutputStream bos = new CryptoAPIDocumentOutputStream(this); // NOSONAR
- byte buf[] = new byte[8];
-
- bos.write(buf, 0, 8); // skip header
- String entryNames[] = {
- SummaryInformation.DEFAULT_STREAM_NAME,
- DocumentSummaryInformation.DEFAULT_STREAM_NAME
- };
-
- List descList = new ArrayList();
-
- int block = 0;
- for (String entryName : entryNames) {
- if (!dir.hasEntry(entryName)) {
- continue;
- }
- StreamDescriptorEntry descEntry = new StreamDescriptorEntry();
- descEntry.block = block;
- descEntry.streamOffset = bos.size();
- descEntry.streamName = entryName;
- descEntry.flags = StreamDescriptorEntry.flagStream.setValue(0, 1);
- descEntry.reserved2 = 0;
-
- bos.setBlock(block);
- DocumentInputStream dis = dir.createDocumentInputStream(entryName);
- IOUtils.copy(dis, bos);
- dis.close();
-
- descEntry.streamSize = bos.size() - descEntry.streamOffset;
- descList.add(descEntry);
-
- dir.getEntry(entryName).delete();
-
- block++;
- }
-
- int streamDescriptorArrayOffset = bos.size();
-
- bos.setBlock(0);
- LittleEndian.putUInt(buf, 0, descList.size());
- bos.write(buf, 0, 4);
-
- for (StreamDescriptorEntry sde : descList) {
- LittleEndian.putUInt(buf, 0, sde.streamOffset);
- bos.write(buf, 0, 4);
- LittleEndian.putUInt(buf, 0, sde.streamSize);
- bos.write(buf, 0, 4);
- LittleEndian.putUShort(buf, 0, sde.block);
- bos.write(buf, 0, 2);
- LittleEndian.putUByte(buf, 0, (short)sde.streamName.length());
- bos.write(buf, 0, 1);
- LittleEndian.putUByte(buf, 0, (short)sde.flags);
- bos.write(buf, 0, 1);
- LittleEndian.putUInt(buf, 0, sde.reserved2);
- bos.write(buf, 0, 4);
- byte nameBytes[] = StringUtil.getToUnicodeLE(sde.streamName);
- bos.write(nameBytes, 0, nameBytes.length);
- LittleEndian.putShort(buf, 0, (short)0); // null-termination
- bos.write(buf, 0, 2);
- }
-
- int savedSize = bos.size();
- int streamDescriptorArraySize = savedSize - streamDescriptorArrayOffset;
- LittleEndian.putUInt(buf, 0, streamDescriptorArrayOffset);
- LittleEndian.putUInt(buf, 4, streamDescriptorArraySize);
-
- bos.reset();
- bos.setBlock(0);
- bos.write(buf, 0, 8);
- bos.setSize(savedSize);
-
- dir.createDocument("EncryptedSummary", new ByteArrayInputStream(bos.getBuf(), 0, savedSize));
- DocumentSummaryInformation dsi = PropertySetFactory.newDocumentSummaryInformation();
-
- try {
- dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
- } catch (WritingNotSupportedException e) {
- throw new IOException(e);
- }
-
- return bos;
- }
-
- protected int getKeySizeInBytes() {
- return getEncryptionInfo().getHeader().getKeySize() / 8;
- }
-
- @Override
- public void setChunkSize(int chunkSize) {
- this.chunkSize = chunkSize;
- }
-
- protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
- DataSpaceMapUtils.addDefaultDataSpace(dir);
- final EncryptionInfo info = getEncryptionInfo();
- final CryptoAPIEncryptionHeader header = (CryptoAPIEncryptionHeader)getEncryptionInfo().getHeader();
- final CryptoAPIEncryptionVerifier verifier = (CryptoAPIEncryptionVerifier)getEncryptionInfo().getVerifier();
- EncryptionRecord er = new EncryptionRecord() {
- @Override
- public void write(LittleEndianByteArrayOutputStream bos) {
- bos.writeShort(info.getVersionMajor());
- bos.writeShort(info.getVersionMinor());
- header.write(bos);
- verifier.write(bos);
- }
- };
- DataSpaceMapUtils.createEncryptionEntry(dir, "EncryptionInfo", er);
- }
-
-
- @Override
- public CryptoAPIEncryptor clone() throws CloneNotSupportedException {
- return (CryptoAPIEncryptor)super.clone();
- }
-
- protected class CryptoAPICipherOutputStream extends ChunkedCipherOutputStream {
-
- @Override
- protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk)
- throws IOException, GeneralSecurityException {
- flush();
- EncryptionInfo ei = getEncryptionInfo();
- SecretKey sk = getSecretKey();
- return CryptoAPIDecryptor.initCipherForBlock(cipher, block, ei, sk, Cipher.ENCRYPT_MODE);
- }
-
- @Override
- protected void calculateChecksum(File file, int i) {
- }
-
- @Override
- protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
- throws IOException, GeneralSecurityException {
- throw new EncryptedDocumentException("createEncryptionInfoEntry not supported");
- }
-
- public CryptoAPICipherOutputStream(OutputStream stream)
- throws IOException, GeneralSecurityException {
- super(stream, CryptoAPIEncryptor.this.chunkSize);
- }
-
- @Override
- public void flush() throws IOException {
- writeChunk(false);
- super.flush();
- }
- }
-
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.cryptoapi;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.WritingNotSupportedException;
+import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor.StreamDescriptorEntry;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.StringUtil;
+
+public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
+
+ private int chunkSize = 512;
+
+ protected CryptoAPIEncryptor() {
+ }
+
+ @Override
+ public void confirmPassword(String password) {
+ Random r = new SecureRandom();
+ byte salt[] = new byte[16];
+ byte verifier[] = new byte[16];
+ r.nextBytes(salt);
+ r.nextBytes(verifier);
+ confirmPassword(password, null, null, verifier, salt, null);
+ }
+
+ @Override
+ public void confirmPassword(String password, byte keySpec[],
+ byte keySalt[], byte verifier[], byte verifierSalt[],
+ byte integritySalt[]) {
+ assert(verifier != null && verifierSalt != null);
+ CryptoAPIEncryptionVerifier ver = (CryptoAPIEncryptionVerifier)getEncryptionInfo().getVerifier();
+ ver.setSalt(verifierSalt);
+ SecretKey skey = CryptoAPIDecryptor.generateSecretKey(password, ver);
+ setSecretKey(skey);
+ try {
+ Cipher cipher = initCipherForBlock(null, 0);
+ byte encryptedVerifier[] = new byte[verifier.length];
+ cipher.update(verifier, 0, verifier.length, encryptedVerifier);
+ ver.setEncryptedVerifier(encryptedVerifier);
+ HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+ MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
+ byte calcVerifierHash[] = hashAlg.digest(verifier);
+ byte encryptedVerifierHash[] = cipher.doFinal(calcVerifierHash);
+ ver.setEncryptedVerifierHash(encryptedVerifierHash);
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("Password confirmation failed", e);
+ }
+ }
+
+ /**
+ * Initializes a cipher object for a given block index for encryption
+ *
+ * @param cipher may be null, otherwise the given instance is reset to the new block index
+ * @param block the block index, e.g. the persist/slide id (hslf)
+ * @return a new cipher object, if cipher was null, otherwise the reinitialized cipher
+ * @throws GeneralSecurityException
+ */
+ public Cipher initCipherForBlock(Cipher cipher, int block)
+ throws GeneralSecurityException {
+ return CryptoAPIDecryptor.initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
+ }
+
+ @Override
+ public ChunkedCipherOutputStream getDataStream(DirectoryNode dir)
+ throws IOException, GeneralSecurityException {
+ throw new IOException("not supported");
+ }
+
+ @Override
+ public CryptoAPICipherOutputStream getDataStream(OutputStream stream, int initialOffset)
+ throws IOException, GeneralSecurityException {
+ return new CryptoAPICipherOutputStream(stream);
+ }
+
+ /**
+ * Encrypt the Document-/SummaryInformation and other optionally streams.
+ * Opposed to other crypto modes, cryptoapi is record based and can't be used
+ * to stream-encrypt a whole file
+ *
+ * @see 2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream
+ */
+ public OutputStream getSummaryEntries(DirectoryNode dir)
+ throws IOException, GeneralSecurityException {
+ CryptoAPIDocumentOutputStream bos = new CryptoAPIDocumentOutputStream(this); // NOSONAR
+ byte buf[] = new byte[8];
+
+ bos.write(buf, 0, 8); // skip header
+ String entryNames[] = {
+ SummaryInformation.DEFAULT_STREAM_NAME,
+ DocumentSummaryInformation.DEFAULT_STREAM_NAME
+ };
+
+ List descList = new ArrayList();
+
+ int block = 0;
+ for (String entryName : entryNames) {
+ if (!dir.hasEntry(entryName)) {
+ continue;
+ }
+ StreamDescriptorEntry descEntry = new StreamDescriptorEntry();
+ descEntry.block = block;
+ descEntry.streamOffset = bos.size();
+ descEntry.streamName = entryName;
+ descEntry.flags = StreamDescriptorEntry.flagStream.setValue(0, 1);
+ descEntry.reserved2 = 0;
+
+ bos.setBlock(block);
+ DocumentInputStream dis = dir.createDocumentInputStream(entryName);
+ IOUtils.copy(dis, bos);
+ dis.close();
+
+ descEntry.streamSize = bos.size() - descEntry.streamOffset;
+ descList.add(descEntry);
+
+ dir.getEntry(entryName).delete();
+
+ block++;
+ }
+
+ int streamDescriptorArrayOffset = bos.size();
+
+ bos.setBlock(0);
+ LittleEndian.putUInt(buf, 0, descList.size());
+ bos.write(buf, 0, 4);
+
+ for (StreamDescriptorEntry sde : descList) {
+ LittleEndian.putUInt(buf, 0, sde.streamOffset);
+ bos.write(buf, 0, 4);
+ LittleEndian.putUInt(buf, 0, sde.streamSize);
+ bos.write(buf, 0, 4);
+ LittleEndian.putUShort(buf, 0, sde.block);
+ bos.write(buf, 0, 2);
+ LittleEndian.putUByte(buf, 0, (short)sde.streamName.length());
+ bos.write(buf, 0, 1);
+ LittleEndian.putUByte(buf, 0, (short)sde.flags);
+ bos.write(buf, 0, 1);
+ LittleEndian.putUInt(buf, 0, sde.reserved2);
+ bos.write(buf, 0, 4);
+ byte nameBytes[] = StringUtil.getToUnicodeLE(sde.streamName);
+ bos.write(nameBytes, 0, nameBytes.length);
+ LittleEndian.putShort(buf, 0, (short)0); // null-termination
+ bos.write(buf, 0, 2);
+ }
+
+ int savedSize = bos.size();
+ int streamDescriptorArraySize = savedSize - streamDescriptorArrayOffset;
+ LittleEndian.putUInt(buf, 0, streamDescriptorArrayOffset);
+ LittleEndian.putUInt(buf, 4, streamDescriptorArraySize);
+
+ bos.reset();
+ bos.setBlock(0);
+ bos.write(buf, 0, 8);
+ bos.setSize(savedSize);
+
+ dir.createDocument("EncryptedSummary", new ByteArrayInputStream(bos.getBuf(), 0, savedSize));
+ DocumentSummaryInformation dsi = PropertySetFactory.newDocumentSummaryInformation();
+
+ try {
+ dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ } catch (WritingNotSupportedException e) {
+ throw new IOException(e);
+ }
+
+ return bos;
+ }
+
+ protected int getKeySizeInBytes() {
+ return getEncryptionInfo().getHeader().getKeySize() / 8;
+ }
+
+ @Override
+ public void setChunkSize(int chunkSize) {
+ this.chunkSize = chunkSize;
+ }
+
+ protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
+ DataSpaceMapUtils.addDefaultDataSpace(dir);
+ final EncryptionInfo info = getEncryptionInfo();
+ final CryptoAPIEncryptionHeader header = (CryptoAPIEncryptionHeader)getEncryptionInfo().getHeader();
+ final CryptoAPIEncryptionVerifier verifier = (CryptoAPIEncryptionVerifier)getEncryptionInfo().getVerifier();
+ EncryptionRecord er = new EncryptionRecord() {
+ @Override
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ bos.writeShort(info.getVersionMajor());
+ bos.writeShort(info.getVersionMinor());
+ header.write(bos);
+ verifier.write(bos);
+ }
+ };
+ DataSpaceMapUtils.createEncryptionEntry(dir, "EncryptionInfo", er);
+ }
+
+
+ @Override
+ public CryptoAPIEncryptor clone() throws CloneNotSupportedException {
+ return (CryptoAPIEncryptor)super.clone();
+ }
+
+ protected class CryptoAPICipherOutputStream extends ChunkedCipherOutputStream {
+
+ @Override
+ protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk)
+ throws IOException, GeneralSecurityException {
+ flush();
+ EncryptionInfo ei = getEncryptionInfo();
+ SecretKey sk = getSecretKey();
+ return CryptoAPIDecryptor.initCipherForBlock(cipher, block, ei, sk, Cipher.ENCRYPT_MODE);
+ }
+
+ @Override
+ protected void calculateChecksum(File file, int i) {
+ }
+
+ @Override
+ protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
+ throws IOException, GeneralSecurityException {
+ throw new EncryptedDocumentException("createEncryptionInfoEntry not supported");
+ }
+
+ public CryptoAPICipherOutputStream(OutputStream stream)
+ throws IOException, GeneralSecurityException {
+ super(stream, CryptoAPIEncryptor.this.chunkSize);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writeChunk(false);
+ super.flush();
+ }
+ }
+
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/standard/EncryptionRecord.java b/src/java/org/apache/poi/poifs/crypt/standard/EncryptionRecord.java
index bf65fbe796..3b45190497 100644
--- a/src/java/org/apache/poi/poifs/crypt/standard/EncryptionRecord.java
+++ b/src/java/org/apache/poi/poifs/crypt/standard/EncryptionRecord.java
@@ -1,23 +1,23 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.poifs.crypt.standard;
-
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-
-public interface EncryptionRecord {
- void write(LittleEndianByteArrayOutputStream os);
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt.standard;
+
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+
+public interface EncryptionRecord {
+ void write(LittleEndianByteArrayOutputStream os);
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java b/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
index a04f74d400..2c42c60f8e 100644
--- a/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
+++ b/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
@@ -1,130 +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.poi.poifs.crypt.standard;
-
-import static org.apache.poi.poifs.crypt.EncryptionInfo.flagAES;
-import static org.apache.poi.poifs.crypt.EncryptionInfo.flagCryptoAPI;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.poifs.crypt.ChainingMode;
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.CipherProvider;
-import org.apache.poi.poifs.crypt.EncryptionHeader;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.LittleEndianInput;
-import org.apache.poi.util.LittleEndianOutput;
-import org.apache.poi.util.StringUtil;
-
-public class StandardEncryptionHeader extends EncryptionHeader implements EncryptionRecord, Cloneable {
-
- protected StandardEncryptionHeader(LittleEndianInput is) throws IOException {
- setFlags(is.readInt());
- setSizeExtra(is.readInt());
- setCipherAlgorithm(CipherAlgorithm.fromEcmaId(is.readInt()));
- setHashAlgorithm(HashAlgorithm.fromEcmaId(is.readInt()));
- int keySize = is.readInt();
- if (keySize == 0) {
- // for the sake of inheritance of the cryptoAPI classes
- // see 2.3.5.1 RC4 CryptoAPI Encryption Header
- // If set to 0x00000000, it MUST be interpreted as 0x00000028 bits.
- keySize = 0x28;
- }
- setKeySize(keySize);
- setBlockSize(getKeySize());
- setCipherProvider(CipherProvider.fromEcmaId(is.readInt()));
-
- is.readLong(); // skip reserved
-
- // CSPName may not always be specified
- // In some cases, the salt value of the EncryptionVerifier is the next chunk of data
- if (is instanceof RecordInputStream) {
- ((RecordInputStream)is).mark(LittleEndianConsts.INT_SIZE+1);
- } else {
- ((InputStream)is).mark(LittleEndianConsts.INT_SIZE+1);
- }
- int checkForSalt = is.readInt();
- if (is instanceof RecordInputStream) {
- ((RecordInputStream)is).reset();
- } else {
- ((InputStream)is).reset();
- }
-
- if (checkForSalt == 16) {
- setCspName("");
- } else {
- StringBuilder builder = new StringBuilder();
- while (true) {
- char c = (char) is.readShort();
- if (c == 0) {
- break;
- }
- builder.append(c);
- }
- setCspName(builder.toString());
- }
-
- setChainingMode(ChainingMode.ecb);
- setKeySalt(null);
- }
-
- protected StandardEncryptionHeader(CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
- setCipherAlgorithm(cipherAlgorithm);
- setHashAlgorithm(hashAlgorithm);
- setKeySize(keyBits);
- setBlockSize(blockSize);
- setCipherProvider(cipherAlgorithm.provider);
- setFlags(flagCryptoAPI.setBoolean(0, true)
- | flagAES.setBoolean(0, cipherAlgorithm.provider == CipherProvider.aes));
- // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb931357(v=vs.85).aspx for a full list
- // setCspName("Microsoft Enhanced RSA and AES Cryptographic Provider");
- }
-
- /**
- * serializes the header
- */
- @Override
- public void write(LittleEndianByteArrayOutputStream bos) {
- int startIdx = bos.getWriteIndex();
- LittleEndianOutput sizeOutput = bos.createDelayedOutput(LittleEndianConsts.INT_SIZE);
- bos.writeInt(getFlags());
- bos.writeInt(0); // size extra
- bos.writeInt(getCipherAlgorithm().ecmaId);
- bos.writeInt(getHashAlgorithm().ecmaId);
- bos.writeInt(getKeySize());
- bos.writeInt(getCipherProvider().ecmaId);
- bos.writeInt(0); // reserved1
- bos.writeInt(0); // reserved2
- String cspName = getCspName();
- if (cspName == null) {
- cspName = getCipherProvider().cipherProviderName;
- }
- bos.write(StringUtil.getToUnicodeLE(cspName));
- bos.writeShort(0);
- int headerSize = bos.getWriteIndex()-startIdx-LittleEndianConsts.INT_SIZE;
- sizeOutput.writeInt(headerSize);
- }
-
- @Override
- public StandardEncryptionHeader clone() throws CloneNotSupportedException {
- return (StandardEncryptionHeader)super.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt.standard;
+
+import static org.apache.poi.poifs.crypt.EncryptionInfo.flagAES;
+import static org.apache.poi.poifs.crypt.EncryptionInfo.flagCryptoAPI;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.poifs.crypt.ChainingMode;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.CipherProvider;
+import org.apache.poi.poifs.crypt.EncryptionHeader;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
+import org.apache.poi.util.StringUtil;
+
+public class StandardEncryptionHeader extends EncryptionHeader implements EncryptionRecord, Cloneable {
+
+ protected StandardEncryptionHeader(LittleEndianInput is) throws IOException {
+ setFlags(is.readInt());
+ setSizeExtra(is.readInt());
+ setCipherAlgorithm(CipherAlgorithm.fromEcmaId(is.readInt()));
+ setHashAlgorithm(HashAlgorithm.fromEcmaId(is.readInt()));
+ int keySize = is.readInt();
+ if (keySize == 0) {
+ // for the sake of inheritance of the cryptoAPI classes
+ // see 2.3.5.1 RC4 CryptoAPI Encryption Header
+ // If set to 0x00000000, it MUST be interpreted as 0x00000028 bits.
+ keySize = 0x28;
+ }
+ setKeySize(keySize);
+ setBlockSize(getKeySize());
+ setCipherProvider(CipherProvider.fromEcmaId(is.readInt()));
+
+ is.readLong(); // skip reserved
+
+ // CSPName may not always be specified
+ // In some cases, the salt value of the EncryptionVerifier is the next chunk of data
+ if (is instanceof RecordInputStream) {
+ ((RecordInputStream)is).mark(LittleEndianConsts.INT_SIZE+1);
+ } else {
+ ((InputStream)is).mark(LittleEndianConsts.INT_SIZE+1);
+ }
+ int checkForSalt = is.readInt();
+ if (is instanceof RecordInputStream) {
+ ((RecordInputStream)is).reset();
+ } else {
+ ((InputStream)is).reset();
+ }
+
+ if (checkForSalt == 16) {
+ setCspName("");
+ } else {
+ StringBuilder builder = new StringBuilder();
+ while (true) {
+ char c = (char) is.readShort();
+ if (c == 0) {
+ break;
+ }
+ builder.append(c);
+ }
+ setCspName(builder.toString());
+ }
+
+ setChainingMode(ChainingMode.ecb);
+ setKeySalt(null);
+ }
+
+ protected StandardEncryptionHeader(CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
+ setCipherAlgorithm(cipherAlgorithm);
+ setHashAlgorithm(hashAlgorithm);
+ setKeySize(keyBits);
+ setBlockSize(blockSize);
+ setCipherProvider(cipherAlgorithm.provider);
+ setFlags(flagCryptoAPI.setBoolean(0, true)
+ | flagAES.setBoolean(0, cipherAlgorithm.provider == CipherProvider.aes));
+ // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb931357(v=vs.85).aspx for a full list
+ // setCspName("Microsoft Enhanced RSA and AES Cryptographic Provider");
+ }
+
+ /**
+ * serializes the header
+ */
+ @Override
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ int startIdx = bos.getWriteIndex();
+ LittleEndianOutput sizeOutput = bos.createDelayedOutput(LittleEndianConsts.INT_SIZE);
+ bos.writeInt(getFlags());
+ bos.writeInt(0); // size extra
+ bos.writeInt(getCipherAlgorithm().ecmaId);
+ bos.writeInt(getHashAlgorithm().ecmaId);
+ bos.writeInt(getKeySize());
+ bos.writeInt(getCipherProvider().ecmaId);
+ bos.writeInt(0); // reserved1
+ bos.writeInt(0); // reserved2
+ String cspName = getCspName();
+ if (cspName == null) {
+ cspName = getCipherProvider().cipherProviderName;
+ }
+ bos.write(StringUtil.getToUnicodeLE(cspName));
+ bos.writeShort(0);
+ int headerSize = bos.getWriteIndex()-startIdx-LittleEndianConsts.INT_SIZE;
+ sizeOutput.writeInt(headerSize);
+ }
+
+ @Override
+ public StandardEncryptionHeader clone() throws CloneNotSupportedException {
+ return (StandardEncryptionHeader)super.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionInfoBuilder.java b/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionInfoBuilder.java
index c2bffdd05a..771a6895be 100644
--- a/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionInfoBuilder.java
+++ b/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionInfoBuilder.java
@@ -1,96 +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.poi.poifs.crypt.standard;
-
-import java.io.IOException;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.ChainingMode;
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.EncryptionInfoBuilder;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
-import org.apache.poi.util.LittleEndianInput;
-
-public class StandardEncryptionInfoBuilder implements EncryptionInfoBuilder {
-
- /**
- * initialize the builder from a stream
- */
- @Override
- public void initialize(EncryptionInfo info, LittleEndianInput dis) throws IOException {
- /* int hSize = */ dis.readInt();
- StandardEncryptionHeader header = new StandardEncryptionHeader(dis);
- info.setHeader(header);
- info.setVerifier(new StandardEncryptionVerifier(dis, header));
-
- if (info.getVersionMinor() == 2 && (info.getVersionMajor() == 3 || info.getVersionMajor() == 4)) {
- StandardDecryptor dec = new StandardDecryptor();
- dec.setEncryptionInfo(info);
- info.setDecryptor(dec);
- }
- }
-
- /**
- * initialize the builder from scratch
- */
- @Override
- public void initialize(EncryptionInfo info, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
- if (cipherAlgorithm == null) {
- cipherAlgorithm = CipherAlgorithm.aes128;
- }
- if (cipherAlgorithm != CipherAlgorithm.aes128 &&
- cipherAlgorithm != CipherAlgorithm.aes192 &&
- cipherAlgorithm != CipherAlgorithm.aes256) {
- throw new EncryptedDocumentException("Standard encryption only supports AES128/192/256.");
- }
-
- if (hashAlgorithm == null) {
- hashAlgorithm = HashAlgorithm.sha1;
- }
- if (hashAlgorithm != HashAlgorithm.sha1) {
- throw new EncryptedDocumentException("Standard encryption only supports SHA-1.");
- }
- if (chainingMode == null) {
- chainingMode = ChainingMode.ecb;
- }
- if (chainingMode != ChainingMode.ecb) {
- throw new EncryptedDocumentException("Standard encryption only supports ECB chaining.");
- }
- if (keyBits == -1) {
- keyBits = cipherAlgorithm.defaultKeySize;
- }
- if (blockSize == -1) {
- blockSize = cipherAlgorithm.blockSize;
- }
- boolean found = false;
- for (int ks : cipherAlgorithm.allowedKeySize) {
- found |= (ks == keyBits);
- }
- if (!found) {
- throw new EncryptedDocumentException("KeySize "+keyBits+" not allowed for Cipher "+cipherAlgorithm.toString());
- }
- info.setHeader(new StandardEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
- info.setVerifier(new StandardEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
- StandardDecryptor dec = new StandardDecryptor();
- dec.setEncryptionInfo(info);
- info.setDecryptor(dec);
- StandardEncryptor enc = new StandardEncryptor();
- enc.setEncryptionInfo(info);
- info.setEncryptor(enc);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.poifs.crypt.standard;
+
+import java.io.IOException;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChainingMode;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionInfoBuilder;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.util.LittleEndianInput;
+
+public class StandardEncryptionInfoBuilder implements EncryptionInfoBuilder {
+
+ /**
+ * initialize the builder from a stream
+ */
+ @Override
+ public void initialize(EncryptionInfo info, LittleEndianInput dis) throws IOException {
+ /* int hSize = */ dis.readInt();
+ StandardEncryptionHeader header = new StandardEncryptionHeader(dis);
+ info.setHeader(header);
+ info.setVerifier(new StandardEncryptionVerifier(dis, header));
+
+ if (info.getVersionMinor() == 2 && (info.getVersionMajor() == 3 || info.getVersionMajor() == 4)) {
+ StandardDecryptor dec = new StandardDecryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ }
+ }
+
+ /**
+ * initialize the builder from scratch
+ */
+ @Override
+ public void initialize(EncryptionInfo info, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
+ if (cipherAlgorithm == null) {
+ cipherAlgorithm = CipherAlgorithm.aes128;
+ }
+ if (cipherAlgorithm != CipherAlgorithm.aes128 &&
+ cipherAlgorithm != CipherAlgorithm.aes192 &&
+ cipherAlgorithm != CipherAlgorithm.aes256) {
+ throw new EncryptedDocumentException("Standard encryption only supports AES128/192/256.");
+ }
+
+ if (hashAlgorithm == null) {
+ hashAlgorithm = HashAlgorithm.sha1;
+ }
+ if (hashAlgorithm != HashAlgorithm.sha1) {
+ throw new EncryptedDocumentException("Standard encryption only supports SHA-1.");
+ }
+ if (chainingMode == null) {
+ chainingMode = ChainingMode.ecb;
+ }
+ if (chainingMode != ChainingMode.ecb) {
+ throw new EncryptedDocumentException("Standard encryption only supports ECB chaining.");
+ }
+ if (keyBits == -1) {
+ keyBits = cipherAlgorithm.defaultKeySize;
+ }
+ if (blockSize == -1) {
+ blockSize = cipherAlgorithm.blockSize;
+ }
+ boolean found = false;
+ for (int ks : cipherAlgorithm.allowedKeySize) {
+ found |= (ks == keyBits);
+ }
+ if (!found) {
+ throw new EncryptedDocumentException("KeySize "+keyBits+" not allowed for Cipher "+cipherAlgorithm.toString());
+ }
+ info.setHeader(new StandardEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+ info.setVerifier(new StandardEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+ StandardDecryptor dec = new StandardDecryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ StandardEncryptor enc = new StandardEncryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptor.java b/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptor.java
index 2fcd912a95..274a778cca 100644
--- a/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptor.java
@@ -1,235 +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.poi.poifs.crypt.standard;
-
-import static org.apache.poi.poifs.crypt.DataSpaceMapUtils.createEncryptionEntry;
-import static org.apache.poi.poifs.crypt.standard.StandardDecryptor.generateSecretKey;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.SecretKey;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.CryptoFunctions;
-import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.EncryptionVerifier;
-import org.apache.poi.poifs.crypt.Encryptor;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
-import org.apache.poi.poifs.filesystem.POIFSWriterListener;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndianByteArrayOutputStream;
-import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.LittleEndianOutputStream;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.util.TempFile;
-
-public class StandardEncryptor extends Encryptor implements Cloneable {
- private static final POILogger logger = POILogFactory.getLogger(StandardEncryptor.class);
-
- protected StandardEncryptor() {
- }
-
- @Override
- public void confirmPassword(String password) {
- // see [MS-OFFCRYPTO] - 2.3.3 EncryptionVerifier
- Random r = new SecureRandom();
- byte[] salt = new byte[16], verifier = new byte[16];
- r.nextBytes(salt);
- r.nextBytes(verifier);
-
- confirmPassword(password, null, null, salt, verifier, null);
- }
-
-
- /**
- * Fills the fields of verifier and header with the calculated hashes based
- * on the password and a random salt
- *
- * see [MS-OFFCRYPTO] - 2.3.4.7 ECMA-376 Document Encryption Key Generation
- */
- @Override
- public void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]) {
- StandardEncryptionVerifier ver = (StandardEncryptionVerifier)getEncryptionInfo().getVerifier();
-
- ver.setSalt(verifierSalt);
- SecretKey secretKey = generateSecretKey(password, ver, getKeySizeInBytes());
- setSecretKey(secretKey);
- Cipher cipher = getCipher(secretKey, null);
-
- try {
- byte encryptedVerifier[] = cipher.doFinal(verifier);
- MessageDigest hashAlgo = CryptoFunctions.getMessageDigest(ver.getHashAlgorithm());
- byte calcVerifierHash[] = hashAlgo.digest(verifier);
-
- // 2.3.3 EncryptionVerifier ...
- // An array of bytes that contains the encrypted form of the
- // hash of the randomly generated Verifier value. The length of the array MUST be the size of
- // the encryption block size multiplied by the number of blocks needed to encrypt the hash of the
- // Verifier. If the encryption algorithm is RC4, the length MUST be 20 bytes. If the encryption
- // algorithm is AES, the length MUST be 32 bytes. After decrypting the EncryptedVerifierHash
- // field, only the first VerifierHashSize bytes MUST be used.
- int encVerHashSize = ver.getCipherAlgorithm().encryptedVerifierHashLength;
- byte encryptedVerifierHash[] = cipher.doFinal(Arrays.copyOf(calcVerifierHash, encVerHashSize));
-
- ver.setEncryptedVerifier(encryptedVerifier);
- ver.setEncryptedVerifierHash(encryptedVerifierHash);
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException("Password confirmation failed", e);
- }
-
- }
-
- private Cipher getCipher(SecretKey key, String padding) {
- EncryptionVerifier ver = getEncryptionInfo().getVerifier();
- return CryptoFunctions.getCipher(key, ver.getCipherAlgorithm(), ver.getChainingMode(), null, Cipher.ENCRYPT_MODE, padding);
- }
-
- @Override
- public OutputStream getDataStream(final DirectoryNode dir)
- throws IOException, GeneralSecurityException {
- createEncryptionInfoEntry(dir);
- DataSpaceMapUtils.addDefaultDataSpace(dir);
- return new StandardCipherOutputStream(dir);
- }
-
- protected class StandardCipherOutputStream extends FilterOutputStream implements POIFSWriterListener {
- protected long countBytes;
- protected final File fileOut;
- protected final DirectoryNode dir;
-
- @SuppressWarnings("resource")
- private StandardCipherOutputStream(DirectoryNode dir, File fileOut) throws IOException {
- // although not documented, we need the same padding as with agile encryption
- // and instead of calculating the missing bytes for the block size ourselves
- // we leave it up to the CipherOutputStream, which generates/saves them on close()
- // ... we can't use "NoPadding" here
- //
- // see also [MS-OFFCRYPT] - 2.3.4.15
- // The final data block MUST be padded to the next integral multiple of the
- // KeyData.blockSize value. Any padding bytes can be used. Note that the StreamSize
- // field of the EncryptedPackage field specifies the number of bytes of
- // unencrypted data as specified in section 2.3.4.4.
- super(
- new CipherOutputStream(new FileOutputStream(fileOut), getCipher(getSecretKey(), "PKCS5Padding"))
- );
- this.fileOut = fileOut;
- this.dir = dir;
- }
-
- protected StandardCipherOutputStream(DirectoryNode dir) throws IOException {
- this(dir, TempFile.createTempFile("encrypted_package", "crypt"));
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- out.write(b, off, len);
- countBytes += len;
- }
-
- @Override
- public void write(int b) throws IOException {
- out.write(b);
- countBytes++;
- }
-
- @Override
- public void close() throws IOException {
- // the CipherOutputStream adds the padding bytes on close()
- super.close();
- writeToPOIFS();
- }
-
- void writeToPOIFS() throws IOException {
- int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
- dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, this);
- // TODO: any properties???
- }
-
- @Override
- public void processPOIFSWriterEvent(POIFSWriterEvent event) {
- try {
- LittleEndianOutputStream leos = new LittleEndianOutputStream(event.getStream());
-
- // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
- // encrypted within the EncryptedData field, not including the size of the StreamSize field.
- // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
- // value, depending on the block size of the chosen encryption algorithm
- leos.writeLong(countBytes);
-
- FileInputStream fis = new FileInputStream(fileOut);
- try {
- IOUtils.copy(fis, leos);
- } finally {
- fis.close();
- }
- if (!fileOut.delete()) {
- logger.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);
- }
-
- leos.close();
- } catch (IOException e) {
- throw new EncryptedDocumentException(e);
- }
- }
- }
-
- protected int getKeySizeInBytes() {
- return getEncryptionInfo().getHeader().getKeySize()/8;
- }
-
- protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
- final EncryptionInfo info = getEncryptionInfo();
- final StandardEncryptionHeader header = (StandardEncryptionHeader)info.getHeader();
- final StandardEncryptionVerifier verifier = (StandardEncryptionVerifier)info.getVerifier();
-
- EncryptionRecord er = new EncryptionRecord(){
- @Override
- public void write(LittleEndianByteArrayOutputStream bos) {
- bos.writeShort(info.getVersionMajor());
- bos.writeShort(info.getVersionMinor());
- bos.writeInt(info.getEncryptionFlags());
- header.write(bos);
- verifier.write(bos);
- }
- };
-
- createEncryptionEntry(dir, "EncryptionInfo", er);
-
- // TODO: any properties???
- }
-
- @Override
- public StandardEncryptor clone() throws CloneNotSupportedException {
- return (StandardEncryptor)super.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.standard;
+
+import static org.apache.poi.poifs.crypt.DataSpaceMapUtils.createEncryptionEntry;
+import static org.apache.poi.poifs.crypt.standard.StandardDecryptor.generateSecretKey;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionVerifier;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
+import org.apache.poi.poifs.filesystem.POIFSWriterListener;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianOutputStream;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.TempFile;
+
+public class StandardEncryptor extends Encryptor implements Cloneable {
+ private static final POILogger logger = POILogFactory.getLogger(StandardEncryptor.class);
+
+ protected StandardEncryptor() {
+ }
+
+ @Override
+ public void confirmPassword(String password) {
+ // see [MS-OFFCRYPTO] - 2.3.3 EncryptionVerifier
+ Random r = new SecureRandom();
+ byte[] salt = new byte[16], verifier = new byte[16];
+ r.nextBytes(salt);
+ r.nextBytes(verifier);
+
+ confirmPassword(password, null, null, salt, verifier, null);
+ }
+
+
+ /**
+ * Fills the fields of verifier and header with the calculated hashes based
+ * on the password and a random salt
+ *
+ * see [MS-OFFCRYPTO] - 2.3.4.7 ECMA-376 Document Encryption Key Generation
+ */
+ @Override
+ public void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]) {
+ StandardEncryptionVerifier ver = (StandardEncryptionVerifier)getEncryptionInfo().getVerifier();
+
+ ver.setSalt(verifierSalt);
+ SecretKey secretKey = generateSecretKey(password, ver, getKeySizeInBytes());
+ setSecretKey(secretKey);
+ Cipher cipher = getCipher(secretKey, null);
+
+ try {
+ byte encryptedVerifier[] = cipher.doFinal(verifier);
+ MessageDigest hashAlgo = CryptoFunctions.getMessageDigest(ver.getHashAlgorithm());
+ byte calcVerifierHash[] = hashAlgo.digest(verifier);
+
+ // 2.3.3 EncryptionVerifier ...
+ // An array of bytes that contains the encrypted form of the
+ // hash of the randomly generated Verifier value. The length of the array MUST be the size of
+ // the encryption block size multiplied by the number of blocks needed to encrypt the hash of the
+ // Verifier. If the encryption algorithm is RC4, the length MUST be 20 bytes. If the encryption
+ // algorithm is AES, the length MUST be 32 bytes. After decrypting the EncryptedVerifierHash
+ // field, only the first VerifierHashSize bytes MUST be used.
+ int encVerHashSize = ver.getCipherAlgorithm().encryptedVerifierHashLength;
+ byte encryptedVerifierHash[] = cipher.doFinal(Arrays.copyOf(calcVerifierHash, encVerHashSize));
+
+ ver.setEncryptedVerifier(encryptedVerifier);
+ ver.setEncryptedVerifierHash(encryptedVerifierHash);
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("Password confirmation failed", e);
+ }
+
+ }
+
+ private Cipher getCipher(SecretKey key, String padding) {
+ EncryptionVerifier ver = getEncryptionInfo().getVerifier();
+ return CryptoFunctions.getCipher(key, ver.getCipherAlgorithm(), ver.getChainingMode(), null, Cipher.ENCRYPT_MODE, padding);
+ }
+
+ @Override
+ public OutputStream getDataStream(final DirectoryNode dir)
+ throws IOException, GeneralSecurityException {
+ createEncryptionInfoEntry(dir);
+ DataSpaceMapUtils.addDefaultDataSpace(dir);
+ return new StandardCipherOutputStream(dir);
+ }
+
+ protected class StandardCipherOutputStream extends FilterOutputStream implements POIFSWriterListener {
+ protected long countBytes;
+ protected final File fileOut;
+ protected final DirectoryNode dir;
+
+ @SuppressWarnings("resource")
+ private StandardCipherOutputStream(DirectoryNode dir, File fileOut) throws IOException {
+ // although not documented, we need the same padding as with agile encryption
+ // and instead of calculating the missing bytes for the block size ourselves
+ // we leave it up to the CipherOutputStream, which generates/saves them on close()
+ // ... we can't use "NoPadding" here
+ //
+ // see also [MS-OFFCRYPT] - 2.3.4.15
+ // The final data block MUST be padded to the next integral multiple of the
+ // KeyData.blockSize value. Any padding bytes can be used. Note that the StreamSize
+ // field of the EncryptedPackage field specifies the number of bytes of
+ // unencrypted data as specified in section 2.3.4.4.
+ super(
+ new CipherOutputStream(new FileOutputStream(fileOut), getCipher(getSecretKey(), "PKCS5Padding"))
+ );
+ this.fileOut = fileOut;
+ this.dir = dir;
+ }
+
+ protected StandardCipherOutputStream(DirectoryNode dir) throws IOException {
+ this(dir, TempFile.createTempFile("encrypted_package", "crypt"));
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ countBytes += len;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ countBytes++;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // the CipherOutputStream adds the padding bytes on close()
+ super.close();
+ writeToPOIFS();
+ }
+
+ void writeToPOIFS() throws IOException {
+ int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
+ dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, this);
+ // TODO: any properties???
+ }
+
+ @Override
+ public void processPOIFSWriterEvent(POIFSWriterEvent event) {
+ try {
+ LittleEndianOutputStream leos = new LittleEndianOutputStream(event.getStream());
+
+ // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
+ // encrypted within the EncryptedData field, not including the size of the StreamSize field.
+ // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
+ // value, depending on the block size of the chosen encryption algorithm
+ leos.writeLong(countBytes);
+
+ FileInputStream fis = new FileInputStream(fileOut);
+ try {
+ IOUtils.copy(fis, leos);
+ } finally {
+ fis.close();
+ }
+ if (!fileOut.delete()) {
+ logger.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);
+ }
+
+ leos.close();
+ } catch (IOException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ }
+ }
+
+ protected int getKeySizeInBytes() {
+ return getEncryptionInfo().getHeader().getKeySize()/8;
+ }
+
+ protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
+ final EncryptionInfo info = getEncryptionInfo();
+ final StandardEncryptionHeader header = (StandardEncryptionHeader)info.getHeader();
+ final StandardEncryptionVerifier verifier = (StandardEncryptionVerifier)info.getVerifier();
+
+ EncryptionRecord er = new EncryptionRecord(){
+ @Override
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ bos.writeShort(info.getVersionMajor());
+ bos.writeShort(info.getVersionMinor());
+ bos.writeInt(info.getEncryptionFlags());
+ header.write(bos);
+ verifier.write(bos);
+ }
+ };
+
+ createEncryptionEntry(dir, "EncryptionInfo", er);
+
+ // TODO: any properties???
+ }
+
+ @Override
+ public StandardEncryptor clone() throws CloneNotSupportedException {
+ return (StandardEncryptor)super.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java b/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
index d11841dd59..7ccdf5ed2f 100644
--- a/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
+++ b/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
@@ -1,401 +1,401 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.poifs.filesystem;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.LittleEndianOutputStream;
-import org.apache.poi.util.StringUtil;
-
-/**
- * Represents an Ole10Native record which is wrapped around certain binary
- * files being embedded in OLE2 documents.
- *
- * @author Rainer Schwarze
- */
-public class Ole10Native {
-
- public static final String OLE10_NATIVE = "\u0001Ole10Native";
- protected static final String ISO1 = "ISO-8859-1";
-
- // (the fields as they appear in the raw record:)
- private int totalSize; // 4 bytes, total size of record not including this field
- private short flags1 = 2; // 2 bytes, unknown, mostly [02 00]
- private String label; // ASCIIZ, stored in this field without the terminating zero
- private String fileName; // ASCIIZ, stored in this field without the terminating zero
- private short flags2 = 0; // 2 bytes, unknown, mostly [00 00]
- private short unknown1 = 3; // see below
- private String command; // ASCIIZ, stored in this field without the terminating zero
- private byte[] dataBuffer; // varying size, the actual native data
- private short flags3 = 0; // some final flags? or zero terminators?, sometimes not there
-
- /**
- * the field encoding mode - merely a try-and-error guess ...
- **/
- private enum EncodingMode {
- /**
- * the data is stored in parsed format - including label, command, etc.
- */
- parsed,
- /**
- * the data is stored raw after the length field
- */
- unparsed,
- /**
- * the data is stored raw after the length field and the flags1 field
- */
- compact
- }
-
- private EncodingMode mode;
-
-
-
- /**
- * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
- * to include a stream "{01}Ole10Native" which contains the actual
- * data relevant for this class.
- *
- * @param poifs POI Filesystem object
- * @return Returns an instance of this class
- * @throws IOException on IO error
- * @throws Ole10NativeException on invalid or unexcepted data format
- */
- public static Ole10Native createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {
- return createFromEmbeddedOleObject(poifs.getRoot());
- }
-
- /**
- * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
- * to include a stream "{01}Ole10Native" which contains the actual
- * data relevant for this class.
- *
- * @param directory POI Filesystem object
- * @return Returns an instance of this class
- * @throws IOException on IO error
- * @throws Ole10NativeException on invalid or unexcepted data format
- */
- public static Ole10Native createFromEmbeddedOleObject(DirectoryNode directory) throws IOException, Ole10NativeException {
- DocumentEntry nativeEntry =
- (DocumentEntry)directory.getEntry(OLE10_NATIVE);
- byte[] data = new byte[nativeEntry.getSize()];
- int readBytes = directory.createDocumentInputStream(nativeEntry).read(data);
- assert(readBytes == data.length);
-
- return new Ole10Native(data, 0);
- }
-
- /**
- * Creates an instance and fills the fields based on ... the fields
- */
- public Ole10Native(String label, String filename, String command, byte[] data) {
- setLabel(label);
- setFileName(filename);
- setCommand(command);
- setDataBuffer(data);
- mode = EncodingMode.parsed;
- }
-
- /**
- * Creates an instance and fills the fields based on the data in the given buffer.
- *
- * @param data The buffer containing the Ole10Native record
- * @param offset The start offset of the record in the buffer
- * @throws Ole10NativeException on invalid or unexcepted data format
- */
- public Ole10Native(byte[] data, int offset) throws Ole10NativeException {
- int ofs = offset; // current offset, initialized to start
-
- if (data.length < offset + 2) {
- throw new Ole10NativeException("data is too small");
- }
-
- totalSize = LittleEndian.getInt(data, ofs);
- ofs += LittleEndianConsts.INT_SIZE;
-
- mode = EncodingMode.unparsed;
- if (LittleEndian.getShort(data, ofs) == 2) {
- // some files like equations don't have a valid filename,
- // but somehow encode the formula right away in the ole10 header
- if (Character.isISOControl(data[ofs+LittleEndianConsts.SHORT_SIZE])) {
- mode = EncodingMode.compact;
- } else {
- mode = EncodingMode.parsed;
- }
- }
-
- int dataSize;
- switch (mode) {
- case parsed: {
- flags1 = LittleEndian.getShort(data, ofs);
-
- // structured format
- ofs += LittleEndianConsts.SHORT_SIZE;
-
- int len = getStringLength(data, ofs);
- label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
- ofs += len;
-
- len = getStringLength(data, ofs);
- fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
- ofs += len;
-
- flags2 = LittleEndian.getShort(data, ofs);
- ofs += LittleEndianConsts.SHORT_SIZE;
-
- unknown1 = LittleEndian.getShort(data, ofs);
- ofs += LittleEndianConsts.SHORT_SIZE;
-
- len = LittleEndian.getInt(data, ofs);
- ofs += LittleEndianConsts.INT_SIZE;
- command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
- ofs += len;
-
- if (totalSize < ofs) {
- throw new Ole10NativeException("Invalid Ole10Native");
- }
-
- dataSize = LittleEndian.getInt(data, ofs);
- ofs += LittleEndianConsts.INT_SIZE;
-
- if (dataSize < 0 || totalSize - (ofs - LittleEndianConsts.INT_SIZE) < dataSize) {
- throw new Ole10NativeException("Invalid Ole10Native");
- }
- break;
- }
- case compact:
- flags1 = LittleEndian.getShort(data, ofs);
- ofs += LittleEndianConsts.SHORT_SIZE;
- dataSize = totalSize - LittleEndianConsts.SHORT_SIZE;
- break;
- default:
- case unparsed:
- dataSize = totalSize;
- break;
- }
-
- if ((long)dataSize + (long)ofs > (long)data.length) { //cast to avoid overflow
- throw new Ole10NativeException("Invalid Ole10Native: declared data length > available data");
- }
- dataBuffer = new byte[dataSize];
- System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
- ofs += dataSize;
- }
-
- /*
- * Helper - determine length of zero terminated string (ASCIIZ).
- */
- private static int getStringLength(byte[] data, int ofs) {
- int len = 0;
- while (len + ofs < data.length && data[ofs + len] != 0) {
- len++;
- }
- len++;
- return len;
- }
-
- /**
- * Returns the value of the totalSize field - the total length of the
- * structure is totalSize + 4 (value of this field + size of this field).
- *
- * @return the totalSize
- */
- public int getTotalSize() {
- return totalSize;
- }
-
- /**
- * Returns flags1 - currently unknown - usually 0x0002.
- *
- * @return the flags1
- */
- public short getFlags1() {
- return flags1;
- }
-
- /**
- * Returns the label field - usually the name of the file (without
- * directory) but probably may be any name specified during
- * packaging/embedding the data.
- *
- * @return the label
- */
- public String getLabel() {
- return label;
- }
-
- /**
- * Returns the fileName field - usually the name of the file being embedded
- * including the full path.
- *
- * @return the fileName
- */
- public String getFileName() {
- return fileName;
- }
-
- /**
- * Returns flags2 - currently unknown - mostly 0x0000.
- *
- * @return the flags2
- */
- public short getFlags2() {
- return flags2;
- }
-
- /**
- * Returns unknown1 field - currently unknown.
- *
- * @return the unknown1
- */
- public short getUnknown1() {
- return unknown1;
- }
-
- /**
- * Returns the command field - usually the name of the file being embedded
- * including the full path, may be a command specified during embedding the
- * file.
- *
- * @return the command
- */
- public String getCommand() {
- return command;
- }
-
- /**
- * Returns the size of the embedded file. If the size is 0 (zero), no data
- * has been embedded. To be sure, that no data has been embedded, check
- * whether {@link #getDataBuffer()} returns null.
- *
- * @return the dataSize
- */
- public int getDataSize() {
- return dataBuffer.length;
- }
-
- /**
- * Returns the buffer containing the embedded file's data, or
- * null if no data was embedded. Note that an embedding may
- * provide information about the data, but the actual data is not included.
- * (So label, filename etc. are available, but this method returns
- * null.)
- *
- * @return the dataBuffer
- */
- public byte[] getDataBuffer() {
- return dataBuffer;
- }
-
- /**
- * Returns the flags3 - currently unknown.
- *
- * @return the flags3
- */
- public short getFlags3() {
- return flags3;
- }
-
- /**
- * Have the contents printer out into an OutputStream, used when writing a
- * file back out to disk (Normally, atom classes will keep their bytes
- * around, but non atom classes will just request the bytes from their
- * children, then chuck on their header and return)
- */
- public void writeOut(OutputStream out) throws IOException {
- // byte intbuf[] = new byte[LittleEndianConsts.INT_SIZE];
- // byte shortbuf[] = new byte[LittleEndianConsts.SHORT_SIZE];
-
- @SuppressWarnings("resource")
- LittleEndianOutputStream leosOut = new LittleEndianOutputStream(out);
-
- switch (mode) {
- case parsed: {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- LittleEndianOutputStream leos = new LittleEndianOutputStream(bos);
- // total size, will be determined later ..
-
- leos.writeShort(getFlags1());
- leos.write(getLabel().getBytes(ISO1));
- leos.write(0);
- leos.write(getFileName().getBytes(ISO1));
- leos.write(0);
- leos.writeShort(getFlags2());
- leos.writeShort(getUnknown1());
- leos.writeInt(getCommand().length() + 1);
- leos.write(getCommand().getBytes(ISO1));
- leos.write(0);
- leos.writeInt(getDataSize());
- leos.write(getDataBuffer());
- leos.writeShort(getFlags3());
- leos.close(); // satisfy compiler ...
-
- leosOut.writeInt(bos.size()); // total size
- bos.writeTo(out);
- break;
- }
- case compact:
- leosOut.writeInt(getDataSize()+LittleEndianConsts.SHORT_SIZE);
- leosOut.writeShort(getFlags1());
- out.write(getDataBuffer());
- break;
- default:
- case unparsed:
- leosOut.writeInt(getDataSize());
- out.write(getDataBuffer());
- break;
- }
-
- }
-
- public void setFlags1(short flags1) {
- this.flags1 = flags1;
- }
-
- public void setFlags2(short flags2) {
- this.flags2 = flags2;
- }
-
- public void setFlags3(short flags3) {
- this.flags3 = flags3;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
- public void setCommand(String command) {
- this.command = command;
- }
-
- public void setUnknown1(short unknown1) {
- this.unknown1 = unknown1;
- }
-
- public void setDataBuffer(byte dataBuffer[]) {
- this.dataBuffer = dataBuffer.clone();
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.filesystem;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianOutputStream;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * Represents an Ole10Native record which is wrapped around certain binary
+ * files being embedded in OLE2 documents.
+ *
+ * @author Rainer Schwarze
+ */
+public class Ole10Native {
+
+ public static final String OLE10_NATIVE = "\u0001Ole10Native";
+ protected static final String ISO1 = "ISO-8859-1";
+
+ // (the fields as they appear in the raw record:)
+ private int totalSize; // 4 bytes, total size of record not including this field
+ private short flags1 = 2; // 2 bytes, unknown, mostly [02 00]
+ private String label; // ASCIIZ, stored in this field without the terminating zero
+ private String fileName; // ASCIIZ, stored in this field without the terminating zero
+ private short flags2 = 0; // 2 bytes, unknown, mostly [00 00]
+ private short unknown1 = 3; // see below
+ private String command; // ASCIIZ, stored in this field without the terminating zero
+ private byte[] dataBuffer; // varying size, the actual native data
+ private short flags3 = 0; // some final flags? or zero terminators?, sometimes not there
+
+ /**
+ * the field encoding mode - merely a try-and-error guess ...
+ **/
+ private enum EncodingMode {
+ /**
+ * the data is stored in parsed format - including label, command, etc.
+ */
+ parsed,
+ /**
+ * the data is stored raw after the length field
+ */
+ unparsed,
+ /**
+ * the data is stored raw after the length field and the flags1 field
+ */
+ compact
+ }
+
+ private EncodingMode mode;
+
+
+
+ /**
+ * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
+ * to include a stream "{01}Ole10Native" which contains the actual
+ * data relevant for this class.
+ *
+ * @param poifs POI Filesystem object
+ * @return Returns an instance of this class
+ * @throws IOException on IO error
+ * @throws Ole10NativeException on invalid or unexcepted data format
+ */
+ public static Ole10Native createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {
+ return createFromEmbeddedOleObject(poifs.getRoot());
+ }
+
+ /**
+ * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
+ * to include a stream "{01}Ole10Native" which contains the actual
+ * data relevant for this class.
+ *
+ * @param directory POI Filesystem object
+ * @return Returns an instance of this class
+ * @throws IOException on IO error
+ * @throws Ole10NativeException on invalid or unexcepted data format
+ */
+ public static Ole10Native createFromEmbeddedOleObject(DirectoryNode directory) throws IOException, Ole10NativeException {
+ DocumentEntry nativeEntry =
+ (DocumentEntry)directory.getEntry(OLE10_NATIVE);
+ byte[] data = new byte[nativeEntry.getSize()];
+ int readBytes = directory.createDocumentInputStream(nativeEntry).read(data);
+ assert(readBytes == data.length);
+
+ return new Ole10Native(data, 0);
+ }
+
+ /**
+ * Creates an instance and fills the fields based on ... the fields
+ */
+ public Ole10Native(String label, String filename, String command, byte[] data) {
+ setLabel(label);
+ setFileName(filename);
+ setCommand(command);
+ setDataBuffer(data);
+ mode = EncodingMode.parsed;
+ }
+
+ /**
+ * Creates an instance and fills the fields based on the data in the given buffer.
+ *
+ * @param data The buffer containing the Ole10Native record
+ * @param offset The start offset of the record in the buffer
+ * @throws Ole10NativeException on invalid or unexcepted data format
+ */
+ public Ole10Native(byte[] data, int offset) throws Ole10NativeException {
+ int ofs = offset; // current offset, initialized to start
+
+ if (data.length < offset + 2) {
+ throw new Ole10NativeException("data is too small");
+ }
+
+ totalSize = LittleEndian.getInt(data, ofs);
+ ofs += LittleEndianConsts.INT_SIZE;
+
+ mode = EncodingMode.unparsed;
+ if (LittleEndian.getShort(data, ofs) == 2) {
+ // some files like equations don't have a valid filename,
+ // but somehow encode the formula right away in the ole10 header
+ if (Character.isISOControl(data[ofs+LittleEndianConsts.SHORT_SIZE])) {
+ mode = EncodingMode.compact;
+ } else {
+ mode = EncodingMode.parsed;
+ }
+ }
+
+ int dataSize;
+ switch (mode) {
+ case parsed: {
+ flags1 = LittleEndian.getShort(data, ofs);
+
+ // structured format
+ ofs += LittleEndianConsts.SHORT_SIZE;
+
+ int len = getStringLength(data, ofs);
+ label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
+ ofs += len;
+
+ len = getStringLength(data, ofs);
+ fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
+ ofs += len;
+
+ flags2 = LittleEndian.getShort(data, ofs);
+ ofs += LittleEndianConsts.SHORT_SIZE;
+
+ unknown1 = LittleEndian.getShort(data, ofs);
+ ofs += LittleEndianConsts.SHORT_SIZE;
+
+ len = LittleEndian.getInt(data, ofs);
+ ofs += LittleEndianConsts.INT_SIZE;
+ command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
+ ofs += len;
+
+ if (totalSize < ofs) {
+ throw new Ole10NativeException("Invalid Ole10Native");
+ }
+
+ dataSize = LittleEndian.getInt(data, ofs);
+ ofs += LittleEndianConsts.INT_SIZE;
+
+ if (dataSize < 0 || totalSize - (ofs - LittleEndianConsts.INT_SIZE) < dataSize) {
+ throw new Ole10NativeException("Invalid Ole10Native");
+ }
+ break;
+ }
+ case compact:
+ flags1 = LittleEndian.getShort(data, ofs);
+ ofs += LittleEndianConsts.SHORT_SIZE;
+ dataSize = totalSize - LittleEndianConsts.SHORT_SIZE;
+ break;
+ default:
+ case unparsed:
+ dataSize = totalSize;
+ break;
+ }
+
+ if ((long)dataSize + (long)ofs > (long)data.length) { //cast to avoid overflow
+ throw new Ole10NativeException("Invalid Ole10Native: declared data length > available data");
+ }
+ dataBuffer = new byte[dataSize];
+ System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
+ ofs += dataSize;
+ }
+
+ /*
+ * Helper - determine length of zero terminated string (ASCIIZ).
+ */
+ private static int getStringLength(byte[] data, int ofs) {
+ int len = 0;
+ while (len + ofs < data.length && data[ofs + len] != 0) {
+ len++;
+ }
+ len++;
+ return len;
+ }
+
+ /**
+ * Returns the value of the totalSize field - the total length of the
+ * structure is totalSize + 4 (value of this field + size of this field).
+ *
+ * @return the totalSize
+ */
+ public int getTotalSize() {
+ return totalSize;
+ }
+
+ /**
+ * Returns flags1 - currently unknown - usually 0x0002.
+ *
+ * @return the flags1
+ */
+ public short getFlags1() {
+ return flags1;
+ }
+
+ /**
+ * Returns the label field - usually the name of the file (without
+ * directory) but probably may be any name specified during
+ * packaging/embedding the data.
+ *
+ * @return the label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Returns the fileName field - usually the name of the file being embedded
+ * including the full path.
+ *
+ * @return the fileName
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * Returns flags2 - currently unknown - mostly 0x0000.
+ *
+ * @return the flags2
+ */
+ public short getFlags2() {
+ return flags2;
+ }
+
+ /**
+ * Returns unknown1 field - currently unknown.
+ *
+ * @return the unknown1
+ */
+ public short getUnknown1() {
+ return unknown1;
+ }
+
+ /**
+ * Returns the command field - usually the name of the file being embedded
+ * including the full path, may be a command specified during embedding the
+ * file.
+ *
+ * @return the command
+ */
+ public String getCommand() {
+ return command;
+ }
+
+ /**
+ * Returns the size of the embedded file. If the size is 0 (zero), no data
+ * has been embedded. To be sure, that no data has been embedded, check
+ * whether {@link #getDataBuffer()} returns null.
+ *
+ * @return the dataSize
+ */
+ public int getDataSize() {
+ return dataBuffer.length;
+ }
+
+ /**
+ * Returns the buffer containing the embedded file's data, or
+ * null if no data was embedded. Note that an embedding may
+ * provide information about the data, but the actual data is not included.
+ * (So label, filename etc. are available, but this method returns
+ * null.)
+ *
+ * @return the dataBuffer
+ */
+ public byte[] getDataBuffer() {
+ return dataBuffer;
+ }
+
+ /**
+ * Returns the flags3 - currently unknown.
+ *
+ * @return the flags3
+ */
+ public short getFlags3() {
+ return flags3;
+ }
+
+ /**
+ * Have the contents printer out into an OutputStream, used when writing a
+ * file back out to disk (Normally, atom classes will keep their bytes
+ * around, but non atom classes will just request the bytes from their
+ * children, then chuck on their header and return)
+ */
+ public void writeOut(OutputStream out) throws IOException {
+ // byte intbuf[] = new byte[LittleEndianConsts.INT_SIZE];
+ // byte shortbuf[] = new byte[LittleEndianConsts.SHORT_SIZE];
+
+ @SuppressWarnings("resource")
+ LittleEndianOutputStream leosOut = new LittleEndianOutputStream(out);
+
+ switch (mode) {
+ case parsed: {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ LittleEndianOutputStream leos = new LittleEndianOutputStream(bos);
+ // total size, will be determined later ..
+
+ leos.writeShort(getFlags1());
+ leos.write(getLabel().getBytes(ISO1));
+ leos.write(0);
+ leos.write(getFileName().getBytes(ISO1));
+ leos.write(0);
+ leos.writeShort(getFlags2());
+ leos.writeShort(getUnknown1());
+ leos.writeInt(getCommand().length() + 1);
+ leos.write(getCommand().getBytes(ISO1));
+ leos.write(0);
+ leos.writeInt(getDataSize());
+ leos.write(getDataBuffer());
+ leos.writeShort(getFlags3());
+ leos.close(); // satisfy compiler ...
+
+ leosOut.writeInt(bos.size()); // total size
+ bos.writeTo(out);
+ break;
+ }
+ case compact:
+ leosOut.writeInt(getDataSize()+LittleEndianConsts.SHORT_SIZE);
+ leosOut.writeShort(getFlags1());
+ out.write(getDataBuffer());
+ break;
+ default:
+ case unparsed:
+ leosOut.writeInt(getDataSize());
+ out.write(getDataBuffer());
+ break;
+ }
+
+ }
+
+ public void setFlags1(short flags1) {
+ this.flags1 = flags1;
+ }
+
+ public void setFlags2(short flags2) {
+ this.flags2 = flags2;
+ }
+
+ public void setFlags3(short flags3) {
+ this.flags3 = flags3;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+ public void setUnknown1(short unknown1) {
+ this.unknown1 = unknown1;
+ }
+
+ public void setDataBuffer(byte dataBuffer[]) {
+ this.dataBuffer = dataBuffer.clone();
+ }
+}
diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java b/src/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java
index c5c84aff9d..665ccf5bdc 100644
--- a/src/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java
+++ b/src/java/org/apache/poi/poifs/macros/VBAMacroExtractor.java
@@ -1,58 +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.poi.poifs.macros;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.macros;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.poi.util.StringUtil;
-
-/**
- * This tool extracts out the source of all VBA Modules of an office file,
+import java.util.Map.Entry;
+
+import org.apache.poi.util.StringUtil;
+
+/**
+ * This tool extracts out the source of all VBA Modules of an office file,
* both OOXML (eg XLSM) and OLE2/POIFS (eg DOC), to STDOUT or a directory.
*
- * @since 3.15-beta2
- */
-public class VBAMacroExtractor {
- public static void main(String args[]) throws IOException {
- if (args.length == 0) {
- System.err.println("Use:");
- System.err.println(" VBAMacroExtractor [output]");
- System.err.println("");
- System.err.println("If an output directory is given, macros are written there");
- System.err.println("Otherwise they are output to the screen");
- System.exit(1);
- }
-
- File input = new File(args[0]);
- File output = null;
- if (args.length > 1) {
- output = new File(args[1]);
- }
-
- VBAMacroExtractor extractor = new VBAMacroExtractor();
- extractor.extract(input, output);
- }
+ * @since 3.15-beta2
+ */
+public class VBAMacroExtractor {
+ public static void main(String args[]) throws IOException {
+ if (args.length == 0) {
+ System.err.println("Use:");
+ System.err.println(" VBAMacroExtractor [output]");
+ System.err.println("");
+ System.err.println("If an output directory is given, macros are written there");
+ System.err.println("Otherwise they are output to the screen");
+ System.exit(1);
+ }
+
+ File input = new File(args[0]);
+ File output = null;
+ if (args.length > 1) {
+ output = new File(args[1]);
+ }
+
+ VBAMacroExtractor extractor = new VBAMacroExtractor();
+ extractor.extract(input, output);
+ }
/**
* Extracts the VBA modules from a macro-enabled office file and writes them
@@ -66,45 +66,45 @@ public class VBAMacroExtractor {
* @param outputDir the directory to write the extracted VBA modules to.
* @param extension file extension of the extracted VBA modules
* @since 3.15-beta2
- */
- public void extract(File input, File outputDir, String extension) throws IOException {
- if (! input.exists()) throw new FileNotFoundException(input.toString());
- System.err.print("Extracting VBA Macros from " + input + " to ");
- if (outputDir != null) {
+ */
+ public void extract(File input, File outputDir, String extension) throws IOException {
+ if (! input.exists()) throw new FileNotFoundException(input.toString());
+ System.err.print("Extracting VBA Macros from " + input + " to ");
+ if (outputDir != null) {
if (!outputDir.exists() && !outputDir.mkdirs()) {
throw new IOException("Output directory " + outputDir + " could not be created");
- }
- System.err.println(outputDir);
- } else {
- System.err.println("STDOUT");
- }
-
- VBAMacroReader reader = new VBAMacroReader(input);
- Map macros = reader.readMacros();
- reader.close();
-
- final String divider = "---------------------------------------";
+ }
+ System.err.println(outputDir);
+ } else {
+ System.err.println("STDOUT");
+ }
+
+ VBAMacroReader reader = new VBAMacroReader(input);
+ Map macros = reader.readMacros();
+ reader.close();
+
+ final String divider = "---------------------------------------";
for (Entry entry : macros.entrySet()) {
String moduleName = entry.getKey();
- String moduleCode = entry.getValue();
- if (outputDir == null) {
- System.out.println(divider);
- System.out.println(moduleName);
- System.out.println("");
- System.out.println(moduleCode);
- } else {
- File out = new File(outputDir, moduleName + extension);
- FileOutputStream fout = new FileOutputStream(out);
- OutputStreamWriter fwriter = new OutputStreamWriter(fout, StringUtil.UTF8);
- fwriter.write(moduleCode);
- fwriter.close();
- fout.close();
- System.out.println("Extracted " + out);
- }
- }
- if (outputDir == null) {
- System.out.println(divider);
- }
+ String moduleCode = entry.getValue();
+ if (outputDir == null) {
+ System.out.println(divider);
+ System.out.println(moduleName);
+ System.out.println("");
+ System.out.println(moduleCode);
+ } else {
+ File out = new File(outputDir, moduleName + extension);
+ FileOutputStream fout = new FileOutputStream(out);
+ OutputStreamWriter fwriter = new OutputStreamWriter(fout, StringUtil.UTF8);
+ fwriter.write(moduleCode);
+ fwriter.close();
+ fout.close();
+ System.out.println("Extracted " + out);
+ }
+ }
+ if (outputDir == null) {
+ System.out.println(divider);
+ }
}
/**
@@ -118,8 +118,8 @@ public class VBAMacroExtractor {
* @param input the macro-enabled office file.
* @param outputDir the directory to write the extracted VBA modules to.
* @since 3.15-beta2
- */
+ */
public void extract(File input, File outputDir) throws IOException {
extract(input, outputDir, ".vba");
- }
-}
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
index 818b10b10d..43818cb5d6 100644
--- a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
+++ b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
@@ -1,306 +1,306 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import java.awt.AlphaComposite;
-import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Insets;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.AffineTransformOp;
-import java.awt.image.BufferedImage;
-import java.awt.image.RescaleOp;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Iterator;
-
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReadParam;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.stream.ImageInputStream;
-import javax.imageio.stream.MemoryCacheImageInputStream;
-
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-
-/**
- * For now this class renders only images supported by the javax.imageio.ImageIO framework.
- **/
-public class BitmapImageRenderer implements ImageRenderer {
- private final static POILogger LOG = POILogFactory.getLogger(ImageRenderer.class);
-
- protected BufferedImage img;
-
- @Override
- public void loadImage(InputStream data, String contentType) throws IOException {
- img = readImage(data, contentType);
- }
-
- @Override
- public void loadImage(byte data[], String contentType) throws IOException {
- img = readImage(new ByteArrayInputStream(data), contentType);
- }
-
- /**
- * Read the image data via ImageIO and optionally try to workaround metadata errors.
- * The resulting image is of image type {@link BufferedImage#TYPE_INT_ARGB}
- *
- * @param data the data stream
- * @param contentType the content type
- * @return the bufferedImage or null, if there was no image reader for this content type
- * @throws IOException thrown if there was an error while processing the image
- */
- private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
- IOException lastException = null;
- BufferedImage img = null;
- if (data.markSupported()) {
- data.mark(data.available());
- }
-
- // currently don't use FileCacheImageInputStream,
- // because of the risk of filling the file handles (see #59166)
- ImageInputStream iis = new MemoryCacheImageInputStream(data);
- try {
- iis = new MemoryCacheImageInputStream(data);
- iis.mark();
-
- Iterator iter = ImageIO.getImageReaders(iis);
- while (img==null && iter.hasNext()) {
- ImageReader reader = iter.next();
- ImageReadParam param = reader.getDefaultReadParam();
- // 0:default mode, 1:fallback mode
- for (int mode=0; img==null && mode<3; mode++) {
- lastException = null;
- try {
- iis.reset();
- } catch (IOException e) {
- if (data.markSupported()) {
- data.reset();
- data.mark(data.available());
- iis.close();
- iis = new MemoryCacheImageInputStream(data);
- } else {
- // can't restore the input stream, so we need to stop processing here
- lastException = e;
- break;
- }
- }
- iis.mark();
-
- try {
-
- switch (mode) {
- case 0:
- reader.setInput(iis, false, true);
- img = reader.read(0, param);
- break;
- case 1: {
- // try to load picture in gray scale mode
- // fallback mode for invalid image band metadata
- // see http://stackoverflow.com/questions/10416378
- Iterator imageTypes = reader.getImageTypes(0);
- while (imageTypes.hasNext()) {
- ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
- int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
- if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
- param.setDestinationType(imageTypeSpecifier);
- break;
- }
- }
- reader.setInput(iis, false, true);
- img = reader.read(0, param);
- break;
- }
- case 2: {
- // try to load truncated pictures by supplying a BufferedImage
- // and use the processed data up till the point of error
- reader.setInput(iis, false, true);
- int height = reader.getHeight(0);
- int width = reader.getWidth(0);
-
- Iterator imageTypes = reader.getImageTypes(0);
- if (imageTypes.hasNext()) {
- ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
- img = imageTypeSpecifier.createBufferedImage(width, height);
- param.setDestination(img);
- } else {
- lastException = new IOException("unable to load even a truncated version of the image.");
- break;
- }
-
- try {
- reader.read(0, param);
- } finally {
- if (img.getType() != BufferedImage.TYPE_INT_ARGB) {
- int y = findTruncatedBlackBox(img, width, height);
- if (y < height) {
- BufferedImage argbImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = argbImg.createGraphics();
- g.clipRect(0, 0, width, y);
- g.drawImage(img, 0, 0, null);
- g.dispose();
- img.flush();
- img = argbImg;
- }
- }
- }
- break;
- }
- }
-
- } catch (IOException e) {
- if (mode < 2) {
- lastException = e;
- }
- } catch (RuntimeException e) {
- if (mode < 2) {
- lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);
- }
- }
- }
- reader.dispose();
- }
- } finally {
- iis.close();
- }
-
- // If you don't have an image at the end of all readers
- if (img == null) {
- if (lastException != null) {
- // rethrow exception - be aware that the exception source can be in
- // multiple locations above ...
- throw lastException;
- }
- LOG.log(POILogger.WARN, "Content-type: "+contentType+" is not support. Image ignored.");
- return null;
- }
-
- // add alpha channel
- if (img.getType() != BufferedImage.TYPE_INT_ARGB) {
- BufferedImage argbImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
- Graphics g = argbImg.getGraphics();
- g.drawImage(img, 0, 0, null);
- g.dispose();
- return argbImg;
- }
-
- return img;
- }
-
- private static int findTruncatedBlackBox(BufferedImage img, int width, int height) {
- // scan through the image to find the black box after the truncated data
- int h = height-1;
- for (; h > 0; h--) {
- for (int w = width-1; w > 0; w-=width/10) {
- int p = img.getRGB(w, h);
- if (p != 0xff000000) {
- return h+1;
- }
- }
- }
- return 0;
- }
-
-
- @Override
- public BufferedImage getImage() {
- return img;
- }
-
- @Override
- public BufferedImage getImage(Dimension dim) {
- double w_old = img.getWidth();
- double h_old = img.getHeight();
- BufferedImage scaled = new BufferedImage((int)w_old, (int)h_old, BufferedImage.TYPE_INT_ARGB);
- double w_new = dim.getWidth();
- double h_new = dim.getHeight();
- AffineTransform at = new AffineTransform();
- at.scale(w_new/w_old, h_new/h_old);
- AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
- scaleOp.filter(img, scaled);
- return scaled;
- }
-
- @Override
- public Dimension getDimension() {
- return (img == null)
- ? new Dimension(0,0)
- : new Dimension(img.getWidth(),img.getHeight());
- }
-
- @Override
- public void setAlpha(double alpha) {
- if (img == null) return;
-
- Dimension dim = getDimension();
- BufferedImage newImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = newImg.createGraphics();
- RescaleOp op = new RescaleOp(new float[]{1.0f, 1.0f, 1.0f, (float)alpha}, new float[]{0,0,0,0}, null);
- g.drawImage(img, op, 0, 0);
- g.dispose();
-
- img = newImg;
- }
-
-
- @Override
- public boolean drawImage(
- Graphics2D graphics,
- Rectangle2D anchor) {
- return drawImage(graphics, anchor, null);
- }
-
- @Override
- public boolean drawImage(
- Graphics2D graphics,
- Rectangle2D anchor,
- Insets clip) {
- if (img == null) return false;
-
- boolean isClipped = true;
- if (clip == null) {
- isClipped = false;
- clip = new Insets(0,0,0,0);
- }
-
- int iw = img.getWidth();
- int ih = img.getHeight();
-
-
- double cw = (100000-clip.left-clip.right) / 100000.0;
- double ch = (100000-clip.top-clip.bottom) / 100000.0;
- double sx = anchor.getWidth()/(iw*cw);
- double sy = anchor.getHeight()/(ih*ch);
- double tx = anchor.getX()-(iw*sx*clip.left/100000.0);
- double ty = anchor.getY()-(ih*sy*clip.top/100000.0);
-
- AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
-
- Shape clipOld = graphics.getClip();
- if (isClipped) graphics.clip(anchor.getBounds2D());
- graphics.drawRenderedImage(img, at);
- graphics.setClip(clipOld);
-
- return true;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.AlphaComposite;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.awt.image.RescaleOp;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+/**
+ * For now this class renders only images supported by the javax.imageio.ImageIO framework.
+ **/
+public class BitmapImageRenderer implements ImageRenderer {
+ private final static POILogger LOG = POILogFactory.getLogger(ImageRenderer.class);
+
+ protected BufferedImage img;
+
+ @Override
+ public void loadImage(InputStream data, String contentType) throws IOException {
+ img = readImage(data, contentType);
+ }
+
+ @Override
+ public void loadImage(byte data[], String contentType) throws IOException {
+ img = readImage(new ByteArrayInputStream(data), contentType);
+ }
+
+ /**
+ * Read the image data via ImageIO and optionally try to workaround metadata errors.
+ * The resulting image is of image type {@link BufferedImage#TYPE_INT_ARGB}
+ *
+ * @param data the data stream
+ * @param contentType the content type
+ * @return the bufferedImage or null, if there was no image reader for this content type
+ * @throws IOException thrown if there was an error while processing the image
+ */
+ private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
+ IOException lastException = null;
+ BufferedImage img = null;
+ if (data.markSupported()) {
+ data.mark(data.available());
+ }
+
+ // currently don't use FileCacheImageInputStream,
+ // because of the risk of filling the file handles (see #59166)
+ ImageInputStream iis = new MemoryCacheImageInputStream(data);
+ try {
+ iis = new MemoryCacheImageInputStream(data);
+ iis.mark();
+
+ Iterator iter = ImageIO.getImageReaders(iis);
+ while (img==null && iter.hasNext()) {
+ ImageReader reader = iter.next();
+ ImageReadParam param = reader.getDefaultReadParam();
+ // 0:default mode, 1:fallback mode
+ for (int mode=0; img==null && mode<3; mode++) {
+ lastException = null;
+ try {
+ iis.reset();
+ } catch (IOException e) {
+ if (data.markSupported()) {
+ data.reset();
+ data.mark(data.available());
+ iis.close();
+ iis = new MemoryCacheImageInputStream(data);
+ } else {
+ // can't restore the input stream, so we need to stop processing here
+ lastException = e;
+ break;
+ }
+ }
+ iis.mark();
+
+ try {
+
+ switch (mode) {
+ case 0:
+ reader.setInput(iis, false, true);
+ img = reader.read(0, param);
+ break;
+ case 1: {
+ // try to load picture in gray scale mode
+ // fallback mode for invalid image band metadata
+ // see http://stackoverflow.com/questions/10416378
+ Iterator imageTypes = reader.getImageTypes(0);
+ while (imageTypes.hasNext()) {
+ ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
+ int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
+ if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
+ param.setDestinationType(imageTypeSpecifier);
+ break;
+ }
+ }
+ reader.setInput(iis, false, true);
+ img = reader.read(0, param);
+ break;
+ }
+ case 2: {
+ // try to load truncated pictures by supplying a BufferedImage
+ // and use the processed data up till the point of error
+ reader.setInput(iis, false, true);
+ int height = reader.getHeight(0);
+ int width = reader.getWidth(0);
+
+ Iterator imageTypes = reader.getImageTypes(0);
+ if (imageTypes.hasNext()) {
+ ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
+ img = imageTypeSpecifier.createBufferedImage(width, height);
+ param.setDestination(img);
+ } else {
+ lastException = new IOException("unable to load even a truncated version of the image.");
+ break;
+ }
+
+ try {
+ reader.read(0, param);
+ } finally {
+ if (img.getType() != BufferedImage.TYPE_INT_ARGB) {
+ int y = findTruncatedBlackBox(img, width, height);
+ if (y < height) {
+ BufferedImage argbImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = argbImg.createGraphics();
+ g.clipRect(0, 0, width, y);
+ g.drawImage(img, 0, 0, null);
+ g.dispose();
+ img.flush();
+ img = argbImg;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ } catch (IOException e) {
+ if (mode < 2) {
+ lastException = e;
+ }
+ } catch (RuntimeException e) {
+ if (mode < 2) {
+ lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);
+ }
+ }
+ }
+ reader.dispose();
+ }
+ } finally {
+ iis.close();
+ }
+
+ // If you don't have an image at the end of all readers
+ if (img == null) {
+ if (lastException != null) {
+ // rethrow exception - be aware that the exception source can be in
+ // multiple locations above ...
+ throw lastException;
+ }
+ LOG.log(POILogger.WARN, "Content-type: "+contentType+" is not support. Image ignored.");
+ return null;
+ }
+
+ // add alpha channel
+ if (img.getType() != BufferedImage.TYPE_INT_ARGB) {
+ BufferedImage argbImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ Graphics g = argbImg.getGraphics();
+ g.drawImage(img, 0, 0, null);
+ g.dispose();
+ return argbImg;
+ }
+
+ return img;
+ }
+
+ private static int findTruncatedBlackBox(BufferedImage img, int width, int height) {
+ // scan through the image to find the black box after the truncated data
+ int h = height-1;
+ for (; h > 0; h--) {
+ for (int w = width-1; w > 0; w-=width/10) {
+ int p = img.getRGB(w, h);
+ if (p != 0xff000000) {
+ return h+1;
+ }
+ }
+ }
+ return 0;
+ }
+
+
+ @Override
+ public BufferedImage getImage() {
+ return img;
+ }
+
+ @Override
+ public BufferedImage getImage(Dimension dim) {
+ double w_old = img.getWidth();
+ double h_old = img.getHeight();
+ BufferedImage scaled = new BufferedImage((int)w_old, (int)h_old, BufferedImage.TYPE_INT_ARGB);
+ double w_new = dim.getWidth();
+ double h_new = dim.getHeight();
+ AffineTransform at = new AffineTransform();
+ at.scale(w_new/w_old, h_new/h_old);
+ AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
+ scaleOp.filter(img, scaled);
+ return scaled;
+ }
+
+ @Override
+ public Dimension getDimension() {
+ return (img == null)
+ ? new Dimension(0,0)
+ : new Dimension(img.getWidth(),img.getHeight());
+ }
+
+ @Override
+ public void setAlpha(double alpha) {
+ if (img == null) return;
+
+ Dimension dim = getDimension();
+ BufferedImage newImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = newImg.createGraphics();
+ RescaleOp op = new RescaleOp(new float[]{1.0f, 1.0f, 1.0f, (float)alpha}, new float[]{0,0,0,0}, null);
+ g.drawImage(img, op, 0, 0);
+ g.dispose();
+
+ img = newImg;
+ }
+
+
+ @Override
+ public boolean drawImage(
+ Graphics2D graphics,
+ Rectangle2D anchor) {
+ return drawImage(graphics, anchor, null);
+ }
+
+ @Override
+ public boolean drawImage(
+ Graphics2D graphics,
+ Rectangle2D anchor,
+ Insets clip) {
+ if (img == null) return false;
+
+ boolean isClipped = true;
+ if (clip == null) {
+ isClipped = false;
+ clip = new Insets(0,0,0,0);
+ }
+
+ int iw = img.getWidth();
+ int ih = img.getHeight();
+
+
+ double cw = (100000-clip.left-clip.right) / 100000.0;
+ double ch = (100000-clip.top-clip.bottom) / 100000.0;
+ double sx = anchor.getWidth()/(iw*cw);
+ double sy = anchor.getHeight()/(ih*ch);
+ double tx = anchor.getX()-(iw*sx*clip.left/100000.0);
+ double ty = anchor.getY()-(ih*sy*clip.top/100000.0);
+
+ AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
+
+ Shape clipOld = graphics.getClip();
+ if (isClipped) graphics.clip(anchor.getBounds2D());
+ graphics.drawRenderedImage(img, at);
+ graphics.setClip(clipOld);
+
+ return true;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawAutoShape.java b/src/java/org/apache/poi/sl/draw/DrawAutoShape.java
index 9cda7a32a6..5503542a10 100644
--- a/src/java/org/apache/poi/sl/draw/DrawAutoShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawAutoShape.java
@@ -1,27 +1,27 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import org.apache.poi.sl.usermodel.*;
-
-
-public class DrawAutoShape extends DrawTextShape {
- public DrawAutoShape(AutoShape,?> shape) {
- super(shape);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import org.apache.poi.sl.usermodel.*;
+
+
+public class DrawAutoShape extends DrawTextShape {
+ public DrawAutoShape(AutoShape,?> shape) {
+ super(shape);
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawBackground.java b/src/java/org/apache/poi/sl/draw/DrawBackground.java
index aa12b470cb..1e9d5945b3 100644
--- a/src/java/org/apache/poi/sl/draw/DrawBackground.java
+++ b/src/java/org/apache/poi/sl/draw/DrawBackground.java
@@ -1,69 +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.poi.sl.draw;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.geom.Rectangle2D;
-
-import org.apache.poi.sl.usermodel.Background;
-import org.apache.poi.sl.usermodel.PlaceableShape;
-import org.apache.poi.sl.usermodel.ShapeContainer;
-import org.apache.poi.sl.usermodel.Sheet;
-
-
-public class DrawBackground extends DrawShape {
- public DrawBackground(Background,?> shape) {
- super(shape);
- }
-
- @SuppressWarnings("rawtypes")
- public void draw(Graphics2D graphics) {
- Dimension pg = shape.getSheet().getSlideShow().getPageSize();
- final Rectangle2D anchor = new Rectangle2D.Double(0, 0, pg.getWidth(), pg.getHeight());
-
- PlaceableShape,?> ps = new PlaceableShape(){
- public ShapeContainer,?> getParent() { return null; }
- public Rectangle2D getAnchor() { return anchor; }
- public void setAnchor(Rectangle2D newAnchor) {}
- public double getRotation() { return 0; }
- public void setRotation(double theta) {}
- public void setFlipHorizontal(boolean flip) {}
- public void setFlipVertical(boolean flip) {}
- public boolean getFlipHorizontal() { return false; }
- public boolean getFlipVertical() { return false; }
- public Sheet,?> getSheet() { return shape.getSheet(); }
- };
-
- DrawFactory drawFact = DrawFactory.getInstance(graphics);
- DrawPaint dp = drawFact.getPaint(ps);
- Paint fill = dp.getPaint(graphics, getShape().getFillStyle().getPaint());
- Rectangle2D anchor2 = getAnchor(graphics, anchor);
-
- if(fill != null) {
- graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, anchor);
- graphics.setPaint(fill);
- graphics.fill(anchor2);
- }
- }
-
- protected Background,?> getShape() {
- return (Background,?>)shape;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.poi.sl.usermodel.Background;
+import org.apache.poi.sl.usermodel.PlaceableShape;
+import org.apache.poi.sl.usermodel.ShapeContainer;
+import org.apache.poi.sl.usermodel.Sheet;
+
+
+public class DrawBackground extends DrawShape {
+ public DrawBackground(Background,?> shape) {
+ super(shape);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void draw(Graphics2D graphics) {
+ Dimension pg = shape.getSheet().getSlideShow().getPageSize();
+ final Rectangle2D anchor = new Rectangle2D.Double(0, 0, pg.getWidth(), pg.getHeight());
+
+ PlaceableShape,?> ps = new PlaceableShape(){
+ public ShapeContainer,?> getParent() { return null; }
+ public Rectangle2D getAnchor() { return anchor; }
+ public void setAnchor(Rectangle2D newAnchor) {}
+ public double getRotation() { return 0; }
+ public void setRotation(double theta) {}
+ public void setFlipHorizontal(boolean flip) {}
+ public void setFlipVertical(boolean flip) {}
+ public boolean getFlipHorizontal() { return false; }
+ public boolean getFlipVertical() { return false; }
+ public Sheet,?> getSheet() { return shape.getSheet(); }
+ };
+
+ DrawFactory drawFact = DrawFactory.getInstance(graphics);
+ DrawPaint dp = drawFact.getPaint(ps);
+ Paint fill = dp.getPaint(graphics, getShape().getFillStyle().getPaint());
+ Rectangle2D anchor2 = getAnchor(graphics, anchor);
+
+ if(fill != null) {
+ graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, anchor);
+ graphics.setPaint(fill);
+ graphics.fill(anchor2);
+ }
+ }
+
+ protected Background,?> getShape() {
+ return (Background,?>)shape;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawConnectorShape.java b/src/java/org/apache/poi/sl/draw/DrawConnectorShape.java
index 00bcd1b58e..bb973f5f8a 100644
--- a/src/java/org/apache/poi/sl/draw/DrawConnectorShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawConnectorShape.java
@@ -1,26 +1,26 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import org.apache.poi.sl.usermodel.ConnectorShape;
-
-public class DrawConnectorShape extends DrawSimpleShape {
- public DrawConnectorShape(ConnectorShape,?> shape) {
- super(shape);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import org.apache.poi.sl.usermodel.ConnectorShape;
+
+public class DrawConnectorShape extends DrawSimpleShape {
+ public DrawConnectorShape(ConnectorShape,?> shape) {
+ super(shape);
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawFactory.java b/src/java/org/apache/poi/sl/draw/DrawFactory.java
index f06ccdc1b0..53b2bcba78 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFactory.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFactory.java
@@ -1,239 +1,239 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import java.awt.Graphics2D;
-import java.awt.font.TextLayout;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.text.AttributedString;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.poi.sl.usermodel.Background;
-import org.apache.poi.sl.usermodel.ConnectorShape;
-import org.apache.poi.sl.usermodel.FreeformShape;
-import org.apache.poi.sl.usermodel.GraphicalFrame;
-import org.apache.poi.sl.usermodel.GroupShape;
-import org.apache.poi.sl.usermodel.MasterSheet;
-import org.apache.poi.sl.usermodel.PictureShape;
-import org.apache.poi.sl.usermodel.PlaceableShape;
-import org.apache.poi.sl.usermodel.Shape;
-import org.apache.poi.sl.usermodel.Sheet;
-import org.apache.poi.sl.usermodel.Slide;
-import org.apache.poi.sl.usermodel.TableShape;
-import org.apache.poi.sl.usermodel.TextBox;
-import org.apache.poi.sl.usermodel.TextParagraph;
-import org.apache.poi.sl.usermodel.TextShape;
-import org.apache.poi.util.JvmBugs;
-
-public class DrawFactory {
- protected static final ThreadLocal defaultFactory = new ThreadLocal();
-
- /**
- * Set a custom draw factory for the current thread.
- * This is a fallback, for operations where usercode can't set a graphics context.
- * Preferably use the rendering hint {@link Drawable#DRAW_FACTORY} to set the factory.
- *
- * @param factory
- */
- public static void setDefaultFactory(DrawFactory factory) {
- defaultFactory.set(factory);
- }
-
- /**
- * Returns the DrawFactory, preferably via a graphics instance.
- * If graphics is null, the current thread local is checked or
- * if it is not set, a new factory is created.
- *
- * @param graphics the current graphics context or null
- * @return the draw factory
- */
- public static DrawFactory getInstance(Graphics2D graphics) {
- // first try to find the factory over the rendering hint
- DrawFactory factory = null;
- boolean isHint = false;
- if (graphics != null) {
- factory = (DrawFactory)graphics.getRenderingHint(Drawable.DRAW_FACTORY);
- isHint = (factory != null);
- }
- // secondly try the thread local default
- if (factory == null) {
- factory = defaultFactory.get();
- }
- // and at last, use the default factory
- if (factory == null) {
- factory = new DrawFactory();
- }
- if (graphics != null && !isHint) {
- graphics.setRenderingHint(Drawable.DRAW_FACTORY, factory);
- }
- return factory;
- }
-
- public Drawable getDrawable(Shape,?> shape) {
- if (shape instanceof TextBox) {
- return getDrawable((TextBox,?>)shape);
- } else if (shape instanceof FreeformShape) {
- return getDrawable((FreeformShape,?>)shape);
- } else if (shape instanceof TextShape) {
- return getDrawable((TextShape,?>)shape);
- } else if (shape instanceof TableShape) {
- return getDrawable((TableShape,?>)shape);
- } else if (shape instanceof GroupShape) {
- return getDrawable((GroupShape,?>)shape);
- } else if (shape instanceof PictureShape) {
- return getDrawable((PictureShape,?>)shape);
- } else if (shape instanceof GraphicalFrame) {
- return getDrawable((GraphicalFrame,?>)shape);
- } else if (shape instanceof Background) {
- return getDrawable((Background,?>)shape);
- } else if (shape instanceof ConnectorShape) {
- return getDrawable((ConnectorShape,?>)shape);
- } else if (shape instanceof Slide) {
- return getDrawable((Slide,?>)shape);
- } else if (shape instanceof MasterSheet) {
- return getDrawable((MasterSheet,?>)shape);
- } else if (shape instanceof Sheet) {
- return getDrawable((Sheet,?>)shape);
- } else if (shape.getClass().isAnnotationPresent(DrawNotImplemented.class)) {
- return new DrawNothing(shape);
- }
-
- throw new IllegalArgumentException("Unsupported shape type: "+shape.getClass());
- }
-
- public DrawSlide getDrawable(Slide,?> sheet) {
- return new DrawSlide(sheet);
- }
-
- public DrawSheet getDrawable(Sheet,?> sheet) {
- return new DrawSheet(sheet);
- }
-
- public DrawMasterSheet getDrawable(MasterSheet,?> sheet) {
- return new DrawMasterSheet(sheet);
- }
-
- public DrawTextBox getDrawable(TextBox,?> shape) {
- return new DrawTextBox(shape);
- }
-
- public DrawFreeformShape getDrawable(FreeformShape,?> shape) {
- return new DrawFreeformShape(shape);
- }
-
- public DrawConnectorShape getDrawable(ConnectorShape,?> shape) {
- return new DrawConnectorShape(shape);
- }
-
- public DrawTableShape getDrawable(TableShape,?> shape) {
- return new DrawTableShape(shape);
- }
-
- public DrawTextShape getDrawable(TextShape,?> shape) {
- return new DrawTextShape(shape);
- }
-
- public DrawGroupShape getDrawable(GroupShape,?> shape) {
- return new DrawGroupShape(shape);
- }
-
- public DrawPictureShape getDrawable(PictureShape,?> shape) {
- return new DrawPictureShape(shape);
- }
-
- public DrawGraphicalFrame getDrawable(GraphicalFrame,?> shape) {
- return new DrawGraphicalFrame(shape);
- }
-
- public DrawTextParagraph getDrawable(TextParagraph,?,?> paragraph) {
- return new DrawTextParagraph(paragraph);
- }
-
- public DrawBackground getDrawable(Background,?> shape) {
- return new DrawBackground(shape);
- }
-
- public DrawTextFragment getTextFragment(TextLayout layout, AttributedString str) {
- return new DrawTextFragment(layout, str);
- }
-
- public DrawPaint getPaint(PlaceableShape,?> shape) {
- return new DrawPaint(shape);
- }
-
- /**
- * Convenience method for drawing single shapes.
- * For drawing whole slides, use {@link Slide#draw(Graphics2D)}
- *
- * @param graphics the graphics context to draw to
- * @param shape the shape
- * @param bounds the bounds within the graphics context to draw to
- */
- public void drawShape(Graphics2D graphics, Shape,?> shape, Rectangle2D bounds) {
- Rectangle2D shapeBounds = shape.getAnchor();
- if (shapeBounds.isEmpty() || (bounds != null && bounds.isEmpty())) {
- return;
- }
-
- AffineTransform txg = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- AffineTransform tx = new AffineTransform();
- try {
- if (bounds != null) {
- double scaleX = bounds.getWidth()/shapeBounds.getWidth();
- double scaleY = bounds.getHeight()/shapeBounds.getHeight();
- tx.translate(bounds.getCenterX(), bounds.getCenterY());
- tx.scale(scaleX, scaleY);
- tx.translate(-shapeBounds.getCenterX(), -shapeBounds.getCenterY());
- }
- graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx);
-
- Drawable d = getDrawable(shape);
- d.applyTransform(graphics);
- d.draw(graphics);
- } finally {
- graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, txg);
- }
- }
-
-
- /**
- * Replace font families for Windows JVM 6, which contains a font rendering error.
- * This is likely to be removed, when POI upgrades to JDK 7
- *
- * @param graphics the graphics context which will contain the font mapping
- */
- public void fixFonts(Graphics2D graphics) {
- if (!JvmBugs.hasLineBreakMeasurerBug()) return;
- @SuppressWarnings("unchecked")
- Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP);
- if (fontMap == null) {
- fontMap = new HashMap();
- graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
- }
-
- String fonts[][] = { { "Calibri", "Lucida Sans" }, { "Cambria", "Lucida Bright" } };
-
- for (String f[] : fonts) {
- if (!fontMap.containsKey(f[0])) {
- fontMap.put(f[0], f[1]);
- }
- }
- }
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.text.AttributedString;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.sl.usermodel.Background;
+import org.apache.poi.sl.usermodel.ConnectorShape;
+import org.apache.poi.sl.usermodel.FreeformShape;
+import org.apache.poi.sl.usermodel.GraphicalFrame;
+import org.apache.poi.sl.usermodel.GroupShape;
+import org.apache.poi.sl.usermodel.MasterSheet;
+import org.apache.poi.sl.usermodel.PictureShape;
+import org.apache.poi.sl.usermodel.PlaceableShape;
+import org.apache.poi.sl.usermodel.Shape;
+import org.apache.poi.sl.usermodel.Sheet;
+import org.apache.poi.sl.usermodel.Slide;
+import org.apache.poi.sl.usermodel.TableShape;
+import org.apache.poi.sl.usermodel.TextBox;
+import org.apache.poi.sl.usermodel.TextParagraph;
+import org.apache.poi.sl.usermodel.TextShape;
+import org.apache.poi.util.JvmBugs;
+
+public class DrawFactory {
+ protected static final ThreadLocal defaultFactory = new ThreadLocal();
+
+ /**
+ * Set a custom draw factory for the current thread.
+ * This is a fallback, for operations where usercode can't set a graphics context.
+ * Preferably use the rendering hint {@link Drawable#DRAW_FACTORY} to set the factory.
+ *
+ * @param factory
+ */
+ public static void setDefaultFactory(DrawFactory factory) {
+ defaultFactory.set(factory);
+ }
+
+ /**
+ * Returns the DrawFactory, preferably via a graphics instance.
+ * If graphics is null, the current thread local is checked or
+ * if it is not set, a new factory is created.
+ *
+ * @param graphics the current graphics context or null
+ * @return the draw factory
+ */
+ public static DrawFactory getInstance(Graphics2D graphics) {
+ // first try to find the factory over the rendering hint
+ DrawFactory factory = null;
+ boolean isHint = false;
+ if (graphics != null) {
+ factory = (DrawFactory)graphics.getRenderingHint(Drawable.DRAW_FACTORY);
+ isHint = (factory != null);
+ }
+ // secondly try the thread local default
+ if (factory == null) {
+ factory = defaultFactory.get();
+ }
+ // and at last, use the default factory
+ if (factory == null) {
+ factory = new DrawFactory();
+ }
+ if (graphics != null && !isHint) {
+ graphics.setRenderingHint(Drawable.DRAW_FACTORY, factory);
+ }
+ return factory;
+ }
+
+ public Drawable getDrawable(Shape,?> shape) {
+ if (shape instanceof TextBox) {
+ return getDrawable((TextBox,?>)shape);
+ } else if (shape instanceof FreeformShape) {
+ return getDrawable((FreeformShape,?>)shape);
+ } else if (shape instanceof TextShape) {
+ return getDrawable((TextShape,?>)shape);
+ } else if (shape instanceof TableShape) {
+ return getDrawable((TableShape,?>)shape);
+ } else if (shape instanceof GroupShape) {
+ return getDrawable((GroupShape,?>)shape);
+ } else if (shape instanceof PictureShape) {
+ return getDrawable((PictureShape,?>)shape);
+ } else if (shape instanceof GraphicalFrame) {
+ return getDrawable((GraphicalFrame,?>)shape);
+ } else if (shape instanceof Background) {
+ return getDrawable((Background,?>)shape);
+ } else if (shape instanceof ConnectorShape) {
+ return getDrawable((ConnectorShape,?>)shape);
+ } else if (shape instanceof Slide) {
+ return getDrawable((Slide,?>)shape);
+ } else if (shape instanceof MasterSheet) {
+ return getDrawable((MasterSheet,?>)shape);
+ } else if (shape instanceof Sheet) {
+ return getDrawable((Sheet,?>)shape);
+ } else if (shape.getClass().isAnnotationPresent(DrawNotImplemented.class)) {
+ return new DrawNothing(shape);
+ }
+
+ throw new IllegalArgumentException("Unsupported shape type: "+shape.getClass());
+ }
+
+ public DrawSlide getDrawable(Slide,?> sheet) {
+ return new DrawSlide(sheet);
+ }
+
+ public DrawSheet getDrawable(Sheet,?> sheet) {
+ return new DrawSheet(sheet);
+ }
+
+ public DrawMasterSheet getDrawable(MasterSheet,?> sheet) {
+ return new DrawMasterSheet(sheet);
+ }
+
+ public DrawTextBox getDrawable(TextBox,?> shape) {
+ return new DrawTextBox(shape);
+ }
+
+ public DrawFreeformShape getDrawable(FreeformShape,?> shape) {
+ return new DrawFreeformShape(shape);
+ }
+
+ public DrawConnectorShape getDrawable(ConnectorShape,?> shape) {
+ return new DrawConnectorShape(shape);
+ }
+
+ public DrawTableShape getDrawable(TableShape,?> shape) {
+ return new DrawTableShape(shape);
+ }
+
+ public DrawTextShape getDrawable(TextShape,?> shape) {
+ return new DrawTextShape(shape);
+ }
+
+ public DrawGroupShape getDrawable(GroupShape,?> shape) {
+ return new DrawGroupShape(shape);
+ }
+
+ public DrawPictureShape getDrawable(PictureShape,?> shape) {
+ return new DrawPictureShape(shape);
+ }
+
+ public DrawGraphicalFrame getDrawable(GraphicalFrame,?> shape) {
+ return new DrawGraphicalFrame(shape);
+ }
+
+ public DrawTextParagraph getDrawable(TextParagraph,?,?> paragraph) {
+ return new DrawTextParagraph(paragraph);
+ }
+
+ public DrawBackground getDrawable(Background,?> shape) {
+ return new DrawBackground(shape);
+ }
+
+ public DrawTextFragment getTextFragment(TextLayout layout, AttributedString str) {
+ return new DrawTextFragment(layout, str);
+ }
+
+ public DrawPaint getPaint(PlaceableShape,?> shape) {
+ return new DrawPaint(shape);
+ }
+
+ /**
+ * Convenience method for drawing single shapes.
+ * For drawing whole slides, use {@link Slide#draw(Graphics2D)}
+ *
+ * @param graphics the graphics context to draw to
+ * @param shape the shape
+ * @param bounds the bounds within the graphics context to draw to
+ */
+ public void drawShape(Graphics2D graphics, Shape,?> shape, Rectangle2D bounds) {
+ Rectangle2D shapeBounds = shape.getAnchor();
+ if (shapeBounds.isEmpty() || (bounds != null && bounds.isEmpty())) {
+ return;
+ }
+
+ AffineTransform txg = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+ AffineTransform tx = new AffineTransform();
+ try {
+ if (bounds != null) {
+ double scaleX = bounds.getWidth()/shapeBounds.getWidth();
+ double scaleY = bounds.getHeight()/shapeBounds.getHeight();
+ tx.translate(bounds.getCenterX(), bounds.getCenterY());
+ tx.scale(scaleX, scaleY);
+ tx.translate(-shapeBounds.getCenterX(), -shapeBounds.getCenterY());
+ }
+ graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx);
+
+ Drawable d = getDrawable(shape);
+ d.applyTransform(graphics);
+ d.draw(graphics);
+ } finally {
+ graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, txg);
+ }
+ }
+
+
+ /**
+ * Replace font families for Windows JVM 6, which contains a font rendering error.
+ * This is likely to be removed, when POI upgrades to JDK 7
+ *
+ * @param graphics the graphics context which will contain the font mapping
+ */
+ public void fixFonts(Graphics2D graphics) {
+ if (!JvmBugs.hasLineBreakMeasurerBug()) return;
+ @SuppressWarnings("unchecked")
+ Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP);
+ if (fontMap == null) {
+ fontMap = new HashMap();
+ graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
+ }
+
+ String fonts[][] = { { "Calibri", "Lucida Sans" }, { "Cambria", "Lucida Bright" } };
+
+ for (String f[] : fonts) {
+ if (!fontMap.containsKey(f[0])) {
+ fontMap.put(f[0], f[1]);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/sl/draw/DrawFontManager.java b/src/java/org/apache/poi/sl/draw/DrawFontManager.java
index 2416837b09..5b74f8400c 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFontManager.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFontManager.java
@@ -1,56 +1,56 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ====================================================================
- */
-
-package org.apache.poi.sl.draw;
-
-/**
- * Manages fonts when rendering slides.
- *
- * Use this class to handle unknown / missing fonts or to substitute fonts
- */
-public interface DrawFontManager {
-
- /**
- * select a font to be used to paint text
- *
- * @param typeface the font family as defined in the .pptx file.
- * This can be unknown or missing in the graphic environment.
- * @param pitchFamily a pitch-and-family,
- * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
- * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
- * for how to calculate those (ancient) values
- *
- * @return the font to be used to paint text
- */
- String getRendererableFont(String typeface, int pitchFamily);
-
- /**
- * In case the original font doesn't contain a glyph, use the
- * returned fallback font as an alternative
- *
- * @param typeface the font family as defined in the .pptx file.
- * @param pitchFamily a pitch-and-family,
- * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
- * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
- * for how to calculate those (ancient) values
- *
- * @return the font to be used as a fallback for the original typeface
- */
- String getFallbackFont(String typeface, int pitchFamily);
-}
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+
+package org.apache.poi.sl.draw;
+
+/**
+ * Manages fonts when rendering slides.
+ *
+ * Use this class to handle unknown / missing fonts or to substitute fonts
+ */
+public interface DrawFontManager {
+
+ /**
+ * select a font to be used to paint text
+ *
+ * @param typeface the font family as defined in the .pptx file.
+ * This can be unknown or missing in the graphic environment.
+ * @param pitchFamily a pitch-and-family,
+ * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
+ * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
+ * for how to calculate those (ancient) values
+ *
+ * @return the font to be used to paint text
+ */
+ String getRendererableFont(String typeface, int pitchFamily);
+
+ /**
+ * In case the original font doesn't contain a glyph, use the
+ * returned fallback font as an alternative
+ *
+ * @param typeface the font family as defined in the .pptx file.
+ * @param pitchFamily a pitch-and-family,
+ * see {@link org.apache.poi.hwmf.record.HwmfFont#getFamily()} and
+ * {@link org.apache.poi.hwmf.record.HwmfFont#getPitch()}
+ * for how to calculate those (ancient) values
+ *
+ * @return the font to be used as a fallback for the original typeface
+ */
+ String getFallbackFont(String typeface, int pitchFamily);
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
index d8f986881d..de9dd5322d 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
@@ -1,61 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Path2D;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.poi.sl.draw.geom.Outline;
-import org.apache.poi.sl.draw.geom.Path;
-import org.apache.poi.sl.usermodel.FillStyle;
-import org.apache.poi.sl.usermodel.FreeformShape;
-import org.apache.poi.sl.usermodel.StrokeStyle;
-
-public class DrawFreeformShape extends DrawAutoShape {
- public DrawFreeformShape(FreeformShape,?> shape) {
- super(shape);
- }
-
- protected Collection computeOutlines(Graphics2D graphics) {
- List lst = new ArrayList();
- FreeformShape,?> fsh = getShape();
- Path2D sh = fsh.getPath();
-
- AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- if (tx == null) {
- tx = new AffineTransform();
- }
-
- java.awt.Shape canvasShape = tx.createTransformedShape(sh);
-
- FillStyle fs = fsh.getFillStyle();
- StrokeStyle ss = fsh.getStrokeStyle();
- Path path = new Path(fs != null, ss != null);
- lst.add(new Outline(canvasShape, path));
- return lst;
- }
-
- @Override
- protected FreeformShape,?> getShape() {
- return (FreeformShape,?>)shape;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.poi.sl.draw.geom.Outline;
+import org.apache.poi.sl.draw.geom.Path;
+import org.apache.poi.sl.usermodel.FillStyle;
+import org.apache.poi.sl.usermodel.FreeformShape;
+import org.apache.poi.sl.usermodel.StrokeStyle;
+
+public class DrawFreeformShape extends DrawAutoShape {
+ public DrawFreeformShape(FreeformShape,?> shape) {
+ super(shape);
+ }
+
+ protected Collection computeOutlines(Graphics2D graphics) {
+ List lst = new ArrayList();
+ FreeformShape,?> fsh = getShape();
+ Path2D sh = fsh.getPath();
+
+ AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+ if (tx == null) {
+ tx = new AffineTransform();
+ }
+
+ java.awt.Shape canvasShape = tx.createTransformedShape(sh);
+
+ FillStyle fs = fsh.getFillStyle();
+ StrokeStyle ss = fsh.getStrokeStyle();
+ Path path = new Path(fs != null, ss != null);
+ lst.add(new Outline(canvasShape, path));
+ return lst;
+ }
+
+ @Override
+ protected FreeformShape,?> getShape() {
+ return (FreeformShape,?>)shape;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawGraphicalFrame.java b/src/java/org/apache/poi/sl/draw/DrawGraphicalFrame.java
index c4b75f19d7..0628429f83 100644
--- a/src/java/org/apache/poi/sl/draw/DrawGraphicalFrame.java
+++ b/src/java/org/apache/poi/sl/draw/DrawGraphicalFrame.java
@@ -1,40 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-
-import org.apache.poi.sl.usermodel.GraphicalFrame;
-import org.apache.poi.sl.usermodel.PictureShape;
-
-
-public class DrawGraphicalFrame extends DrawShape {
-
- public DrawGraphicalFrame(GraphicalFrame,?> shape) {
- super(shape);
- }
-
- public void draw(Graphics2D context) {
- PictureShape,?> ps = ((GraphicalFrame,?>)getShape()).getFallbackPicture();
- if (ps == null) {
- return;
- }
- DrawPictureShape dps = DrawFactory.getInstance(context).getDrawable(ps);
- dps.draw(context);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+
+import org.apache.poi.sl.usermodel.GraphicalFrame;
+import org.apache.poi.sl.usermodel.PictureShape;
+
+
+public class DrawGraphicalFrame extends DrawShape {
+
+ public DrawGraphicalFrame(GraphicalFrame,?> shape) {
+ super(shape);
+ }
+
+ public void draw(Graphics2D context) {
+ PictureShape,?> ps = ((GraphicalFrame,?>)getShape()).getFallbackPicture();
+ if (ps == null) {
+ return;
+ }
+ DrawPictureShape dps = DrawFactory.getInstance(context).getDrawable(ps);
+ dps.draw(context);
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawGroupShape.java b/src/java/org/apache/poi/sl/draw/DrawGroupShape.java
index 999e34c546..3472d85437 100644
--- a/src/java/org/apache/poi/sl/draw/DrawGroupShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawGroupShape.java
@@ -1,75 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-
-import org.apache.poi.sl.usermodel.*;
-
-
-public class DrawGroupShape extends DrawShape {
-
- public DrawGroupShape(GroupShape,?> shape) {
- super(shape);
- }
-
- public void draw(Graphics2D graphics) {
-
- // the coordinate system of this group of shape
- Rectangle2D interior = getShape().getInteriorAnchor();
- // anchor of this group relative to the parent shape
- Rectangle2D exterior = getShape().getAnchor();
-
- AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- AffineTransform tx0 = new AffineTransform(tx);
-
- double scaleX = interior.getWidth() == 0. ? 1.0 : exterior.getWidth() / interior.getWidth();
- double scaleY = interior.getHeight() == 0. ? 1.0 : exterior.getHeight() / interior.getHeight();
-
- tx.translate(exterior.getX(), exterior.getY());
- tx.scale(scaleX, scaleY);
- tx.translate(-interior.getX(), -interior.getY());
-
- DrawFactory drawFact = DrawFactory.getInstance(graphics);
- AffineTransform at2 = graphics.getTransform();
-
- for (Shape,?> child : getShape()) {
- // remember the initial transform and restore it after we are done with the drawing
- AffineTransform at = graphics.getTransform();
- graphics.setRenderingHint(Drawable.GSAVE, true);
-
- Drawable draw = drawFact.getDrawable(child);
- draw.applyTransform(graphics);
- draw.draw(graphics);
-
- // restore the coordinate system
- graphics.setTransform(at);
- graphics.setRenderingHint(Drawable.GRESTORE, true);
- }
-
- graphics.setTransform(at2);
- graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx0);
- }
-
- @Override
- protected GroupShape,?> getShape() {
- return (GroupShape,?>)shape;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.poi.sl.usermodel.*;
+
+
+public class DrawGroupShape extends DrawShape {
+
+ public DrawGroupShape(GroupShape,?> shape) {
+ super(shape);
+ }
+
+ public void draw(Graphics2D graphics) {
+
+ // the coordinate system of this group of shape
+ Rectangle2D interior = getShape().getInteriorAnchor();
+ // anchor of this group relative to the parent shape
+ Rectangle2D exterior = getShape().getAnchor();
+
+ AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+ AffineTransform tx0 = new AffineTransform(tx);
+
+ double scaleX = interior.getWidth() == 0. ? 1.0 : exterior.getWidth() / interior.getWidth();
+ double scaleY = interior.getHeight() == 0. ? 1.0 : exterior.getHeight() / interior.getHeight();
+
+ tx.translate(exterior.getX(), exterior.getY());
+ tx.scale(scaleX, scaleY);
+ tx.translate(-interior.getX(), -interior.getY());
+
+ DrawFactory drawFact = DrawFactory.getInstance(graphics);
+ AffineTransform at2 = graphics.getTransform();
+
+ for (Shape,?> child : getShape()) {
+ // remember the initial transform and restore it after we are done with the drawing
+ AffineTransform at = graphics.getTransform();
+ graphics.setRenderingHint(Drawable.GSAVE, true);
+
+ Drawable draw = drawFact.getDrawable(child);
+ draw.applyTransform(graphics);
+ draw.draw(graphics);
+
+ // restore the coordinate system
+ graphics.setTransform(at);
+ graphics.setRenderingHint(Drawable.GRESTORE, true);
+ }
+
+ graphics.setTransform(at2);
+ graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx0);
+ }
+
+ @Override
+ protected GroupShape,?> getShape() {
+ return (GroupShape,?>)shape;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java b/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java
index a39cf7d68e..9dbbe251cc 100644
--- a/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java
+++ b/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java
@@ -1,53 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-
-import org.apache.poi.sl.usermodel.MasterSheet;
-import org.apache.poi.sl.usermodel.Placeholder;
-import org.apache.poi.sl.usermodel.Shape;
-import org.apache.poi.sl.usermodel.SimpleShape;
-import org.apache.poi.sl.usermodel.Slide;
-
-
-public class DrawMasterSheet extends DrawSheet {
-
- public DrawMasterSheet(MasterSheet,?> sheet) {
- super(sheet);
- }
-
- /**
- * Checks if this sheet displays the specified shape.
- *
- * Subclasses can override it and skip certain shapes from drawings,
- * for instance, slide masters and layouts don't display placeholders
- */
- @Override
- protected boolean canDraw(Graphics2D graphics, Shape,?> shape) {
- if (shape instanceof SimpleShape) {
- // in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF
- Placeholder ph = ((SimpleShape,?>)shape).getPlaceholder();
- if (ph != null) {
- Slide,?> slide = (Slide,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
- return slide.getDisplayPlaceholder(ph);
- }
- }
- return true;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+
+import org.apache.poi.sl.usermodel.MasterSheet;
+import org.apache.poi.sl.usermodel.Placeholder;
+import org.apache.poi.sl.usermodel.Shape;
+import org.apache.poi.sl.usermodel.SimpleShape;
+import org.apache.poi.sl.usermodel.Slide;
+
+
+public class DrawMasterSheet extends DrawSheet {
+
+ public DrawMasterSheet(MasterSheet,?> sheet) {
+ super(sheet);
+ }
+
+ /**
+ * Checks if this sheet displays the specified shape.
+ *
+ * Subclasses can override it and skip certain shapes from drawings,
+ * for instance, slide masters and layouts don't display placeholders
+ */
+ @Override
+ protected boolean canDraw(Graphics2D graphics, Shape,?> shape) {
+ if (shape instanceof SimpleShape) {
+ // in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF
+ Placeholder ph = ((SimpleShape,?>)shape).getPlaceholder();
+ if (ph != null) {
+ Slide,?> slide = (Slide,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
+ return slide.getDisplayPlaceholder(ph);
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawNotImplemented.java b/src/java/org/apache/poi/sl/draw/DrawNotImplemented.java
index 45b80eaaa1..246e8d8a1b 100644
--- a/src/java/org/apache/poi/sl/draw/DrawNotImplemented.java
+++ b/src/java/org/apache/poi/sl/draw/DrawNotImplemented.java
@@ -1,35 +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.poi.sl.draw;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import org.apache.poi.util.Internal;
-
-
-/**
- * This is a marker annotation for classes which don't have a defined
- * draw implementation.
- */
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Internal
-public @interface DrawNotImplemented {
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import org.apache.poi.util.Internal;
+
+
+/**
+ * This is a marker annotation for classes which don't have a defined
+ * draw implementation.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Internal
+public @interface DrawNotImplemented {
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawNothing.java b/src/java/org/apache/poi/sl/draw/DrawNothing.java
index d1710b2352..f875da7fc5 100644
--- a/src/java/org/apache/poi/sl/draw/DrawNothing.java
+++ b/src/java/org/apache/poi/sl/draw/DrawNothing.java
@@ -1,47 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-
-import org.apache.poi.sl.usermodel.Shape;
-
-
-public class DrawNothing implements Drawable {
-
- protected final Shape,?> shape;
-
- public DrawNothing(Shape,?> shape) {
- this.shape = shape;
- }
-
- /**
- * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
- *
- * @param graphics the graphics whos transform matrix will be modified
- */
- public void applyTransform(Graphics2D graphics) {
- }
-
-
- public void draw(Graphics2D graphics) {
- }
-
- public void drawContent(Graphics2D context) {
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+
+import org.apache.poi.sl.usermodel.Shape;
+
+
+public class DrawNothing implements Drawable {
+
+ protected final Shape,?> shape;
+
+ public DrawNothing(Shape,?> shape) {
+ this.shape = shape;
+ }
+
+ /**
+ * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
+ *
+ * @param graphics the graphics whos transform matrix will be modified
+ */
+ public void applyTransform(Graphics2D graphics) {
+ }
+
+
+ public void draw(Graphics2D graphics) {
+ }
+
+ public void drawContent(Graphics2D context) {
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java
index 4bfbe43004..d42e38338a 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPaint.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java
@@ -1,524 +1,524 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.LinearGradientPaint;
-import java.awt.MultipleGradientPaint.ColorSpaceType;
-import java.awt.MultipleGradientPaint.CycleMethod;
-import java.awt.Paint;
-import java.awt.RadialGradientPaint;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.poi.sl.usermodel.ColorStyle;
-import org.apache.poi.sl.usermodel.PaintStyle;
-import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
-import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
-import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
-import org.apache.poi.sl.usermodel.PlaceableShape;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-
-
-/**
- * This class handles color transformations.
- *
- * @see HSL code taken from Java Tips Weblog
- */
-public class DrawPaint {
- // HSL code is public domain - see https://tips4java.wordpress.com/contact-us/
-
- private static final POILogger LOG = POILogFactory.getLogger(DrawPaint.class);
-
- private static final Color TRANSPARENT = new Color(1f,1f,1f,0f);
-
- protected PlaceableShape,?> shape;
-
- public DrawPaint(PlaceableShape,?> shape) {
- this.shape = shape;
- }
-
- private static class SimpleSolidPaint implements SolidPaint {
- private final ColorStyle solidColor;
-
- SimpleSolidPaint(final Color color) {
- if (color == null) {
- throw new NullPointerException("Color needs to be specified");
- }
- this.solidColor = new ColorStyle(){
- public Color getColor() {
- return new Color(color.getRed(), color.getGreen(), color.getBlue());
- }
- public int getAlpha() { return (int)Math.round(color.getAlpha()*100000./255.); }
- public int getHueOff() { return -1; }
- public int getHueMod() { return -1; }
- public int getSatOff() { return -1; }
- public int getSatMod() { return -1; }
- public int getLumOff() { return -1; }
- public int getLumMod() { return -1; }
- public int getShade() { return -1; }
- public int getTint() { return -1; }
- };
- }
-
- SimpleSolidPaint(ColorStyle color) {
- if (color == null) {
- throw new NullPointerException("Color needs to be specified");
- }
- this.solidColor = color;
- }
-
- public ColorStyle getSolidColor() {
- return solidColor;
- }
- }
-
- public static SolidPaint createSolidPaint(final Color color) {
- return (color == null) ? null : new SimpleSolidPaint(color);
- }
-
- public static SolidPaint createSolidPaint(final ColorStyle color) {
- return (color == null) ? null : new SimpleSolidPaint(color);
- }
-
- public Paint getPaint(Graphics2D graphics, PaintStyle paint) {
- if (paint instanceof SolidPaint) {
- return getSolidPaint((SolidPaint)paint, graphics);
- } else if (paint instanceof GradientPaint) {
- return getGradientPaint((GradientPaint)paint, graphics);
- } else if (paint instanceof TexturePaint) {
- return getTexturePaint((TexturePaint)paint, graphics);
- }
- return null;
- }
-
- protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) {
- return applyColorTransform(fill.getSolidColor());
- }
-
- protected Paint getGradientPaint(GradientPaint fill, Graphics2D graphics) {
- switch (fill.getGradientType()) {
- case linear:
- return createLinearGradientPaint(fill, graphics);
- case circular:
- return createRadialGradientPaint(fill, graphics);
- case shape:
- return createPathGradientPaint(fill, graphics);
- default:
- throw new UnsupportedOperationException("gradient fill of type "+fill+" not supported.");
- }
- }
-
- protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) {
- InputStream is = fill.getImageData();
- if (is == null) return null;
- assert(graphics != null);
-
- ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType());
-
- try {
- try {
- renderer.loadImage(is, fill.getContentType());
- } finally {
- is.close();
- }
- } catch (IOException e) {
- LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e);
- return null;
- }
-
- int alpha = fill.getAlpha();
- if (0 <= alpha && alpha < 100000) {
- renderer.setAlpha(alpha/100000.f);
- }
-
- Rectangle2D textAnchor = shape.getAnchor();
- BufferedImage image;
- if ("image/x-wmf".equals(fill.getContentType())) {
- // don't rely on wmf dimensions, use dimension of anchor
- // TODO: check pixels vs. points for image dimension
- image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight()));
- } else {
- image = renderer.getImage();
- }
-
- if(image == null) {
- LOG.log(POILogger.ERROR, "Can't load image data");
- return null;
- }
- Paint paint = new java.awt.TexturePaint(image, textAnchor);
-
- return paint;
- }
-
- /**
- * Convert color transformations in {@link ColorStyle} to a {@link Color} instance
- *
- * @see Using Office Open XML to Customize Document Formatting in the 2007 Office System
- * @see saturation modulation (satMod)
- * @see Office Open XML satMod results in more than 100% saturation
- */
- public static Color applyColorTransform(ColorStyle color){
- // TODO: The colors don't match 100% the results of Powerpoint, maybe because we still
- // operate in sRGB and not scRGB ... work in progress ...
- if (color == null || color.getColor() == null) {
- return TRANSPARENT;
- }
-
- Color result = color.getColor();
-
- double alpha = getAlpha(result, color);
- double hsl[] = RGB2HSL(result); // values are in the range [0..100] (usually ...)
- applyHslModOff(hsl, 0, color.getHueMod(), color.getHueOff());
- applyHslModOff(hsl, 1, color.getSatMod(), color.getSatOff());
- applyHslModOff(hsl, 2, color.getLumMod(), color.getLumOff());
- applyShade(hsl, color);
- applyTint(hsl, color);
-
- result = HSL2RGB(hsl[0], hsl[1], hsl[2], alpha);
-
- return result;
- }
-
- private static double getAlpha(Color c, ColorStyle fc) {
- double alpha = c.getAlpha()/255d;
- int fcAlpha = fc.getAlpha();
- if (fcAlpha != -1) {
- alpha *= fcAlpha/100000d;
- }
- return Math.min(1, Math.max(0, alpha));
- }
-
- /**
- * Apply the modulation and offset adjustments to the given HSL part
- *
- * Example for lumMod/lumOff:
- * The lumMod value is the percent luminance. A lumMod value of "60000",
- * is 60% of the luminance of the original color.
- * When the color is a shade of the original theme color, the lumMod
- * attribute is the only one of the tags shown here that appears.
- * The tag appears after the tag when the color is a
- * tint of the original. The lumOff value always equals 1-lumMod, which is used in the tint calculation
- *
- * Despite having different ways to display the tint and shade percentages,
- * all of the programs use the same method to calculate the resulting color.
- * Convert the original RGB value to HSL ... and then adjust the luminance (L)
- * with one of the following equations before converting the HSL value back to RGB.
- * (The % tint in the following equations refers to the tint, themetint, themeshade,
- * or lumMod values, as applicable.)
- *
- * @param hsl the hsl values
- * @param hslPart the hsl part to modify [0..2]
- * @param mod the modulation adjustment
- * @param off the offset adjustment
- * @return the modified hsl value
- *
- */
- private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) {
- if (mod == -1) mod = 100000;
- if (off == -1) off = 0;
- if (!(mod == 100000 && off == 0)) {
- double fOff = off / 1000d;
- double fMod = mod / 100000d;
- hsl[hslPart] = hsl[hslPart]*fMod+fOff;
- }
- }
-
- /**
- * Apply the shade
- *
- * For a shade, the equation is luminance * %tint.
- */
- private static void applyShade(double hsl[], ColorStyle fc) {
- int shade = fc.getShade();
- if (shade == -1) return;
-
- double fshade = shade / 100000.d;
-
- hsl[2] *= fshade;
- }
-
- /**
- * Apply the tint
- *
- * For a tint, the equation is luminance * %tint + (1-%tint).
- * (Note that 1-%tint is equal to the lumOff value in DrawingML.)
- */
- private static void applyTint(double hsl[], ColorStyle fc) {
- int tint = fc.getTint();
- if (tint == -1) return;
-
- double ftint = tint / 100000.f;
-
- hsl[2] = hsl[2] * ftint + (100 - ftint*100.);
- }
-
-
- protected Paint createLinearGradientPaint(GradientPaint fill, Graphics2D graphics) {
- double angle = fill.getGradientAngle();
- Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
-
- AffineTransform at = AffineTransform.getRotateInstance(
- Math.toRadians(angle),
- anchor.getX() + anchor.getWidth() / 2,
- anchor.getY() + anchor.getHeight() / 2);
-
- double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth());
- Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2,
- anchor.getY() + anchor.getHeight() / 2);
- p1 = at.transform(p1, null);
-
- Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight() / 2);
- p2 = at.transform(p2, null);
-
- snapToAnchor(p1, anchor);
- snapToAnchor(p2, anchor);
-
- if (p1.equals(p2)) {
- // gradient paint on the same point throws an exception ... and doesn't make sense
- return null;
- }
-
- float[] fractions = fill.getGradientFractions();
- Color[] colors = new Color[fractions.length];
-
- int i = 0;
- for (ColorStyle fc : fill.getGradientColors()) {
- // if fc is null, use transparent color to get color of background
- colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc);
- }
-
- AffineTransform grAt = new AffineTransform();
- if(fill.isRotatedWithShape()) {
- double rotation = shape.getRotation();
- if (rotation != 0.) {
- double centerX = anchor.getX() + anchor.getWidth() / 2;
- double centerY = anchor.getY() + anchor.getHeight() / 2;
-
- grAt.translate(centerX, centerY);
- grAt.rotate(Math.toRadians(-rotation));
- grAt.translate(-centerX, -centerY);
- }
- }
-
- return new LinearGradientPaint
- (p1, p2, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, grAt);
- }
-
- protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) {
- Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
-
- Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2,
- anchor.getY() + anchor.getHeight()/2);
-
- float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
-
- float[] fractions = fill.getGradientFractions();
- Color[] colors = new Color[fractions.length];
-
- int i=0;
- for (ColorStyle fc : fill.getGradientColors()) {
- colors[i++] = applyColorTransform(fc);
- }
-
- return new RadialGradientPaint(pCenter, radius, fractions, colors);
- }
-
- protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) {
- // currently we ignore an eventually center setting
-
- float[] fractions = fill.getGradientFractions();
- Color[] colors = new Color[fractions.length];
-
- int i=0;
- for (ColorStyle fc : fill.getGradientColors()) {
- colors[i++] = applyColorTransform(fc);
- }
-
- return new PathGradientPaint(colors, fractions);
- }
-
- protected void snapToAnchor(Point2D p, Rectangle2D anchor) {
- if (p.getX() < anchor.getX()) {
- p.setLocation(anchor.getX(), p.getY());
- } else if (p.getX() > (anchor.getX() + anchor.getWidth())) {
- p.setLocation(anchor.getX() + anchor.getWidth(), p.getY());
- }
-
- if (p.getY() < anchor.getY()) {
- p.setLocation(p.getX(), anchor.getY());
- } else if (p.getY() > (anchor.getY() + anchor.getHeight())) {
- p.setLocation(p.getX(), anchor.getY() + anchor.getHeight());
- }
- }
-
- /**
- * Convert HSL values to a RGB Color.
- *
- * @param h Hue is specified as degrees in the range 0 - 360.
- * @param s Saturation is specified as a percentage in the range 1 - 100.
- * @param l Luminance is specified as a percentage in the range 1 - 100.
- * @param alpha the alpha value between 0 - 1
- *
- * @return the RGB Color object
- */
- public static Color HSL2RGB(double h, double s, double l, double alpha) {
- // we clamp the values, as it possible to come up with more than 100% sat/lum
- // (see links in applyColorTransform() for more info)
- s = Math.max(0, Math.min(100, s));
- l = Math.max(0, Math.min(100, l));
-
- if (alpha <0.0f || alpha > 1.0f) {
- String message = "Color parameter outside of expected range - Alpha: " + alpha;
- throw new IllegalArgumentException( message );
- }
-
- // Formula needs all values between 0 - 1.
-
- h = h % 360.0f;
- h /= 360f;
- s /= 100f;
- l /= 100f;
-
- double q = (l < 0.5d)
- ? l * (1d + s)
- : (l + s) - (s * l);
-
- double p = 2d * l - q;
-
- double r = Math.max(0, HUE2RGB(p, q, h + (1.0d / 3.0d)));
- double g = Math.max(0, HUE2RGB(p, q, h));
- double b = Math.max(0, HUE2RGB(p, q, h - (1.0d / 3.0d)));
-
- r = Math.min(r, 1.0d);
- g = Math.min(g, 1.0d);
- b = Math.min(b, 1.0d);
-
- return new Color((float)r, (float)g, (float)b, (float)alpha);
- }
-
- private static double HUE2RGB(double p, double q, double h) {
- if (h < 0d) h += 1d;
-
- if (h > 1d) h -= 1d;
-
- if (6d * h < 1d) {
- return p + ((q - p) * 6d * h);
- }
-
- if (2d * h < 1d) {
- return q;
- }
-
- if (3d * h < 2d) {
- return p + ( (q - p) * 6d * ((2.0d / 3.0d) - h) );
- }
-
- return p;
- }
-
-
- /**
- * Convert a RGB Color to it corresponding HSL values.
- *
- * @return an array containing the 3 HSL values.
- */
- private static double[] RGB2HSL(Color color)
- {
- // Get RGB values in the range 0 - 1
-
- float[] rgb = color.getRGBColorComponents( null );
- double r = rgb[0];
- double g = rgb[1];
- double b = rgb[2];
-
- // Minimum and Maximum RGB values are used in the HSL calculations
-
- double min = Math.min(r, Math.min(g, b));
- double max = Math.max(r, Math.max(g, b));
-
- // Calculate the Hue
-
- double h = 0;
-
- if (max == min) {
- h = 0;
- } else if (max == r) {
- h = ((60d * (g - b) / (max - min)) + 360d) % 360d;
- } else if (max == g) {
- h = (60d * (b - r) / (max - min)) + 120d;
- } else if (max == b) {
- h = (60d * (r - g) / (max - min)) + 240d;
- }
-
- // Calculate the Luminance
-
- double l = (max + min) / 2d;
-
- // Calculate the Saturation
-
- double s = 0;
-
- if (max == min) {
- s = 0;
- } else if (l <= .5d) {
- s = (max - min) / (max + min);
- } else {
- s = (max - min) / (2d - max - min);
- }
-
- return new double[] {h, s * 100, l * 100};
- }
-
- /**
- * Convert sRGB float component [0..1] from sRGB to linear RGB [0..100000]
- *
- * @see Color#getRGBColorComponents(float[])
- */
- public static int srgb2lin(float sRGB) {
- // scRGB has a linear gamma of 1.0, scale the AWT-Color which is in sRGB to linear RGB
- // see https://en.wikipedia.org/wiki/SRGB (the reverse transformation)
- if (sRGB <= 0.04045d) {
- return (int)Math.rint(100000d * sRGB / 12.92d);
- } else {
- return (int)Math.rint(100000d * Math.pow((sRGB + 0.055d) / 1.055d, 2.4d));
- }
- }
-
- /**
- * Convert linear RGB [0..100000] to sRGB float component [0..1]
- *
- * @see Color#getRGBColorComponents(float[])
- */
- public static float lin2srgb(int linRGB) {
- // color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color
- // see https://en.wikipedia.org/wiki/SRGB (The forward transformation)
- if (linRGB <= 0.0031308d) {
- return (float)(linRGB / 100000d * 12.92d);
- } else {
- return (float)(1.055d * Math.pow(linRGB / 100000d, 1.0d/2.4d) - 0.055d);
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
+import java.awt.MultipleGradientPaint.ColorSpaceType;
+import java.awt.MultipleGradientPaint.CycleMethod;
+import java.awt.Paint;
+import java.awt.RadialGradientPaint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.sl.usermodel.ColorStyle;
+import org.apache.poi.sl.usermodel.PaintStyle;
+import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
+import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
+import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
+import org.apache.poi.sl.usermodel.PlaceableShape;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+
+/**
+ * This class handles color transformations.
+ *
+ * @see HSL code taken from Java Tips Weblog
+ */
+public class DrawPaint {
+ // HSL code is public domain - see https://tips4java.wordpress.com/contact-us/
+
+ private static final POILogger LOG = POILogFactory.getLogger(DrawPaint.class);
+
+ private static final Color TRANSPARENT = new Color(1f,1f,1f,0f);
+
+ protected PlaceableShape,?> shape;
+
+ public DrawPaint(PlaceableShape,?> shape) {
+ this.shape = shape;
+ }
+
+ private static class SimpleSolidPaint implements SolidPaint {
+ private final ColorStyle solidColor;
+
+ SimpleSolidPaint(final Color color) {
+ if (color == null) {
+ throw new NullPointerException("Color needs to be specified");
+ }
+ this.solidColor = new ColorStyle(){
+ public Color getColor() {
+ return new Color(color.getRed(), color.getGreen(), color.getBlue());
+ }
+ public int getAlpha() { return (int)Math.round(color.getAlpha()*100000./255.); }
+ public int getHueOff() { return -1; }
+ public int getHueMod() { return -1; }
+ public int getSatOff() { return -1; }
+ public int getSatMod() { return -1; }
+ public int getLumOff() { return -1; }
+ public int getLumMod() { return -1; }
+ public int getShade() { return -1; }
+ public int getTint() { return -1; }
+ };
+ }
+
+ SimpleSolidPaint(ColorStyle color) {
+ if (color == null) {
+ throw new NullPointerException("Color needs to be specified");
+ }
+ this.solidColor = color;
+ }
+
+ public ColorStyle getSolidColor() {
+ return solidColor;
+ }
+ }
+
+ public static SolidPaint createSolidPaint(final Color color) {
+ return (color == null) ? null : new SimpleSolidPaint(color);
+ }
+
+ public static SolidPaint createSolidPaint(final ColorStyle color) {
+ return (color == null) ? null : new SimpleSolidPaint(color);
+ }
+
+ public Paint getPaint(Graphics2D graphics, PaintStyle paint) {
+ if (paint instanceof SolidPaint) {
+ return getSolidPaint((SolidPaint)paint, graphics);
+ } else if (paint instanceof GradientPaint) {
+ return getGradientPaint((GradientPaint)paint, graphics);
+ } else if (paint instanceof TexturePaint) {
+ return getTexturePaint((TexturePaint)paint, graphics);
+ }
+ return null;
+ }
+
+ protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) {
+ return applyColorTransform(fill.getSolidColor());
+ }
+
+ protected Paint getGradientPaint(GradientPaint fill, Graphics2D graphics) {
+ switch (fill.getGradientType()) {
+ case linear:
+ return createLinearGradientPaint(fill, graphics);
+ case circular:
+ return createRadialGradientPaint(fill, graphics);
+ case shape:
+ return createPathGradientPaint(fill, graphics);
+ default:
+ throw new UnsupportedOperationException("gradient fill of type "+fill+" not supported.");
+ }
+ }
+
+ protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) {
+ InputStream is = fill.getImageData();
+ if (is == null) return null;
+ assert(graphics != null);
+
+ ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType());
+
+ try {
+ try {
+ renderer.loadImage(is, fill.getContentType());
+ } finally {
+ is.close();
+ }
+ } catch (IOException e) {
+ LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e);
+ return null;
+ }
+
+ int alpha = fill.getAlpha();
+ if (0 <= alpha && alpha < 100000) {
+ renderer.setAlpha(alpha/100000.f);
+ }
+
+ Rectangle2D textAnchor = shape.getAnchor();
+ BufferedImage image;
+ if ("image/x-wmf".equals(fill.getContentType())) {
+ // don't rely on wmf dimensions, use dimension of anchor
+ // TODO: check pixels vs. points for image dimension
+ image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight()));
+ } else {
+ image = renderer.getImage();
+ }
+
+ if(image == null) {
+ LOG.log(POILogger.ERROR, "Can't load image data");
+ return null;
+ }
+ Paint paint = new java.awt.TexturePaint(image, textAnchor);
+
+ return paint;
+ }
+
+ /**
+ * Convert color transformations in {@link ColorStyle} to a {@link Color} instance
+ *
+ * @see Using Office Open XML to Customize Document Formatting in the 2007 Office System
+ * @see saturation modulation (satMod)
+ * @see Office Open XML satMod results in more than 100% saturation
+ */
+ public static Color applyColorTransform(ColorStyle color){
+ // TODO: The colors don't match 100% the results of Powerpoint, maybe because we still
+ // operate in sRGB and not scRGB ... work in progress ...
+ if (color == null || color.getColor() == null) {
+ return TRANSPARENT;
+ }
+
+ Color result = color.getColor();
+
+ double alpha = getAlpha(result, color);
+ double hsl[] = RGB2HSL(result); // values are in the range [0..100] (usually ...)
+ applyHslModOff(hsl, 0, color.getHueMod(), color.getHueOff());
+ applyHslModOff(hsl, 1, color.getSatMod(), color.getSatOff());
+ applyHslModOff(hsl, 2, color.getLumMod(), color.getLumOff());
+ applyShade(hsl, color);
+ applyTint(hsl, color);
+
+ result = HSL2RGB(hsl[0], hsl[1], hsl[2], alpha);
+
+ return result;
+ }
+
+ private static double getAlpha(Color c, ColorStyle fc) {
+ double alpha = c.getAlpha()/255d;
+ int fcAlpha = fc.getAlpha();
+ if (fcAlpha != -1) {
+ alpha *= fcAlpha/100000d;
+ }
+ return Math.min(1, Math.max(0, alpha));
+ }
+
+ /**
+ * Apply the modulation and offset adjustments to the given HSL part
+ *
+ * Example for lumMod/lumOff:
+ * The lumMod value is the percent luminance. A lumMod value of "60000",
+ * is 60% of the luminance of the original color.
+ * When the color is a shade of the original theme color, the lumMod
+ * attribute is the only one of the tags shown here that appears.
+ * The tag appears after the tag when the color is a
+ * tint of the original. The lumOff value always equals 1-lumMod, which is used in the tint calculation
+ *
+ * Despite having different ways to display the tint and shade percentages,
+ * all of the programs use the same method to calculate the resulting color.
+ * Convert the original RGB value to HSL ... and then adjust the luminance (L)
+ * with one of the following equations before converting the HSL value back to RGB.
+ * (The % tint in the following equations refers to the tint, themetint, themeshade,
+ * or lumMod values, as applicable.)
+ *
+ * @param hsl the hsl values
+ * @param hslPart the hsl part to modify [0..2]
+ * @param mod the modulation adjustment
+ * @param off the offset adjustment
+ * @return the modified hsl value
+ *
+ */
+ private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) {
+ if (mod == -1) mod = 100000;
+ if (off == -1) off = 0;
+ if (!(mod == 100000 && off == 0)) {
+ double fOff = off / 1000d;
+ double fMod = mod / 100000d;
+ hsl[hslPart] = hsl[hslPart]*fMod+fOff;
+ }
+ }
+
+ /**
+ * Apply the shade
+ *
+ * For a shade, the equation is luminance * %tint.
+ */
+ private static void applyShade(double hsl[], ColorStyle fc) {
+ int shade = fc.getShade();
+ if (shade == -1) return;
+
+ double fshade = shade / 100000.d;
+
+ hsl[2] *= fshade;
+ }
+
+ /**
+ * Apply the tint
+ *
+ * For a tint, the equation is luminance * %tint + (1-%tint).
+ * (Note that 1-%tint is equal to the lumOff value in DrawingML.)
+ */
+ private static void applyTint(double hsl[], ColorStyle fc) {
+ int tint = fc.getTint();
+ if (tint == -1) return;
+
+ double ftint = tint / 100000.f;
+
+ hsl[2] = hsl[2] * ftint + (100 - ftint*100.);
+ }
+
+
+ protected Paint createLinearGradientPaint(GradientPaint fill, Graphics2D graphics) {
+ double angle = fill.getGradientAngle();
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
+
+ AffineTransform at = AffineTransform.getRotateInstance(
+ Math.toRadians(angle),
+ anchor.getX() + anchor.getWidth() / 2,
+ anchor.getY() + anchor.getHeight() / 2);
+
+ double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth());
+ Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2,
+ anchor.getY() + anchor.getHeight() / 2);
+ p1 = at.transform(p1, null);
+
+ Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight() / 2);
+ p2 = at.transform(p2, null);
+
+ snapToAnchor(p1, anchor);
+ snapToAnchor(p2, anchor);
+
+ if (p1.equals(p2)) {
+ // gradient paint on the same point throws an exception ... and doesn't make sense
+ return null;
+ }
+
+ float[] fractions = fill.getGradientFractions();
+ Color[] colors = new Color[fractions.length];
+
+ int i = 0;
+ for (ColorStyle fc : fill.getGradientColors()) {
+ // if fc is null, use transparent color to get color of background
+ colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc);
+ }
+
+ AffineTransform grAt = new AffineTransform();
+ if(fill.isRotatedWithShape()) {
+ double rotation = shape.getRotation();
+ if (rotation != 0.) {
+ double centerX = anchor.getX() + anchor.getWidth() / 2;
+ double centerY = anchor.getY() + anchor.getHeight() / 2;
+
+ grAt.translate(centerX, centerY);
+ grAt.rotate(Math.toRadians(-rotation));
+ grAt.translate(-centerX, -centerY);
+ }
+ }
+
+ return new LinearGradientPaint
+ (p1, p2, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, grAt);
+ }
+
+ protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) {
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
+
+ Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2,
+ anchor.getY() + anchor.getHeight()/2);
+
+ float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
+
+ float[] fractions = fill.getGradientFractions();
+ Color[] colors = new Color[fractions.length];
+
+ int i=0;
+ for (ColorStyle fc : fill.getGradientColors()) {
+ colors[i++] = applyColorTransform(fc);
+ }
+
+ return new RadialGradientPaint(pCenter, radius, fractions, colors);
+ }
+
+ protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) {
+ // currently we ignore an eventually center setting
+
+ float[] fractions = fill.getGradientFractions();
+ Color[] colors = new Color[fractions.length];
+
+ int i=0;
+ for (ColorStyle fc : fill.getGradientColors()) {
+ colors[i++] = applyColorTransform(fc);
+ }
+
+ return new PathGradientPaint(colors, fractions);
+ }
+
+ protected void snapToAnchor(Point2D p, Rectangle2D anchor) {
+ if (p.getX() < anchor.getX()) {
+ p.setLocation(anchor.getX(), p.getY());
+ } else if (p.getX() > (anchor.getX() + anchor.getWidth())) {
+ p.setLocation(anchor.getX() + anchor.getWidth(), p.getY());
+ }
+
+ if (p.getY() < anchor.getY()) {
+ p.setLocation(p.getX(), anchor.getY());
+ } else if (p.getY() > (anchor.getY() + anchor.getHeight())) {
+ p.setLocation(p.getX(), anchor.getY() + anchor.getHeight());
+ }
+ }
+
+ /**
+ * Convert HSL values to a RGB Color.
+ *
+ * @param h Hue is specified as degrees in the range 0 - 360.
+ * @param s Saturation is specified as a percentage in the range 1 - 100.
+ * @param l Luminance is specified as a percentage in the range 1 - 100.
+ * @param alpha the alpha value between 0 - 1
+ *
+ * @return the RGB Color object
+ */
+ public static Color HSL2RGB(double h, double s, double l, double alpha) {
+ // we clamp the values, as it possible to come up with more than 100% sat/lum
+ // (see links in applyColorTransform() for more info)
+ s = Math.max(0, Math.min(100, s));
+ l = Math.max(0, Math.min(100, l));
+
+ if (alpha <0.0f || alpha > 1.0f) {
+ String message = "Color parameter outside of expected range - Alpha: " + alpha;
+ throw new IllegalArgumentException( message );
+ }
+
+ // Formula needs all values between 0 - 1.
+
+ h = h % 360.0f;
+ h /= 360f;
+ s /= 100f;
+ l /= 100f;
+
+ double q = (l < 0.5d)
+ ? l * (1d + s)
+ : (l + s) - (s * l);
+
+ double p = 2d * l - q;
+
+ double r = Math.max(0, HUE2RGB(p, q, h + (1.0d / 3.0d)));
+ double g = Math.max(0, HUE2RGB(p, q, h));
+ double b = Math.max(0, HUE2RGB(p, q, h - (1.0d / 3.0d)));
+
+ r = Math.min(r, 1.0d);
+ g = Math.min(g, 1.0d);
+ b = Math.min(b, 1.0d);
+
+ return new Color((float)r, (float)g, (float)b, (float)alpha);
+ }
+
+ private static double HUE2RGB(double p, double q, double h) {
+ if (h < 0d) h += 1d;
+
+ if (h > 1d) h -= 1d;
+
+ if (6d * h < 1d) {
+ return p + ((q - p) * 6d * h);
+ }
+
+ if (2d * h < 1d) {
+ return q;
+ }
+
+ if (3d * h < 2d) {
+ return p + ( (q - p) * 6d * ((2.0d / 3.0d) - h) );
+ }
+
+ return p;
+ }
+
+
+ /**
+ * Convert a RGB Color to it corresponding HSL values.
+ *
+ * @return an array containing the 3 HSL values.
+ */
+ private static double[] RGB2HSL(Color color)
+ {
+ // Get RGB values in the range 0 - 1
+
+ float[] rgb = color.getRGBColorComponents( null );
+ double r = rgb[0];
+ double g = rgb[1];
+ double b = rgb[2];
+
+ // Minimum and Maximum RGB values are used in the HSL calculations
+
+ double min = Math.min(r, Math.min(g, b));
+ double max = Math.max(r, Math.max(g, b));
+
+ // Calculate the Hue
+
+ double h = 0;
+
+ if (max == min) {
+ h = 0;
+ } else if (max == r) {
+ h = ((60d * (g - b) / (max - min)) + 360d) % 360d;
+ } else if (max == g) {
+ h = (60d * (b - r) / (max - min)) + 120d;
+ } else if (max == b) {
+ h = (60d * (r - g) / (max - min)) + 240d;
+ }
+
+ // Calculate the Luminance
+
+ double l = (max + min) / 2d;
+
+ // Calculate the Saturation
+
+ double s = 0;
+
+ if (max == min) {
+ s = 0;
+ } else if (l <= .5d) {
+ s = (max - min) / (max + min);
+ } else {
+ s = (max - min) / (2d - max - min);
+ }
+
+ return new double[] {h, s * 100, l * 100};
+ }
+
+ /**
+ * Convert sRGB float component [0..1] from sRGB to linear RGB [0..100000]
+ *
+ * @see Color#getRGBColorComponents(float[])
+ */
+ public static int srgb2lin(float sRGB) {
+ // scRGB has a linear gamma of 1.0, scale the AWT-Color which is in sRGB to linear RGB
+ // see https://en.wikipedia.org/wiki/SRGB (the reverse transformation)
+ if (sRGB <= 0.04045d) {
+ return (int)Math.rint(100000d * sRGB / 12.92d);
+ } else {
+ return (int)Math.rint(100000d * Math.pow((sRGB + 0.055d) / 1.055d, 2.4d));
+ }
+ }
+
+ /**
+ * Convert linear RGB [0..100000] to sRGB float component [0..1]
+ *
+ * @see Color#getRGBColorComponents(float[])
+ */
+ public static float lin2srgb(int linRGB) {
+ // color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color
+ // see https://en.wikipedia.org/wiki/SRGB (The forward transformation)
+ if (linRGB <= 0.0031308d) {
+ return (float)(linRGB / 100000d * 12.92d);
+ } else {
+ return (float)(1.055d * Math.pow(linRGB / 100000d, 1.0d/2.4d) - 0.055d);
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
index bdc5ab68c4..2d3764e8a2 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
@@ -1,204 +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.poi.sl.draw;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.Insets;
-import java.awt.geom.Rectangle2D;
-import java.io.IOException;
-
-import org.apache.poi.sl.usermodel.PictureData;
-import org.apache.poi.sl.usermodel.PictureData.PictureType;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.sl.usermodel.PictureShape;
-import org.apache.poi.sl.usermodel.RectAlign;
-
-
-public class DrawPictureShape extends DrawSimpleShape {
- private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
- private static final String WMF_IMAGE_RENDERER = "org.apache.poi.hwmf.draw.HwmfSLImageRenderer";
-
- public DrawPictureShape(PictureShape,?> shape) {
- super(shape);
- }
-
- @Override
- public void drawContent(Graphics2D graphics) {
- PictureData data = getShape().getPictureData();
- if(data == null) return;
-
- Rectangle2D anchor = getAnchor(graphics, getShape());
- Insets insets = getShape().getClipping();
-
- try {
- ImageRenderer renderer = getImageRenderer(graphics, data.getContentType());
- renderer.loadImage(data.getData(), data.getContentType());
- renderer.drawImage(graphics, anchor, insets);
- } catch (IOException e) {
- LOG.log(POILogger.ERROR, "image can't be loaded/rendered.", e);
- }
- }
-
- /**
- * Returns an ImageRenderer for the PictureData
- *
- * @param graphics
- * @return the image renderer
- */
- public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
- ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
- if (renderer != null) {
- return renderer;
- }
-
- if (PictureType.WMF.contentType.equals(contentType)) {
- try {
- @SuppressWarnings("unchecked")
- Class extends ImageRenderer> irc = (Class extends ImageRenderer>)
- Thread.currentThread().getContextClassLoader().loadClass(WMF_IMAGE_RENDERER);
- return irc.newInstance();
- } catch (Exception e) {
- // WMF image renderer is not on the classpath, continuing with BitmapRenderer
- // although this doesn't make much sense ...
- LOG.log(POILogger.ERROR, "WMF image renderer is not on the classpath - include poi-scratchpad jar!", e);
- }
- }
-
- return new BitmapImageRenderer();
- }
-
- @Override
- protected PictureShape,?> getShape() {
- return (PictureShape,?>)shape;
- }
-
- /**
- * Resize this picture to the default size.
- *
- * For PNG and JPEG resizes the image to 100%,
- * for other types, if the size can't be determined it will be 200x200 pixels.
- */
- public void resize() {
- PictureShape,?> ps = getShape();
- Dimension dim = ps.getPictureData().getImageDimension();
-
- Rectangle2D origRect = ps.getAnchor();
- double x = origRect.getX();
- double y = origRect.getY();
- double w = dim.getWidth();
- double h = dim.getHeight();
- ps.setAnchor(new Rectangle2D.Double(x, y, w, h));
- }
-
-
- /**
- * Fit picture shape into the target rectangle, maintaining the aspect ratio
- * and repositioning within the target rectangle with a centered alignment.
- *
- * @param target The target rectangle
- */
- public void resize(Rectangle2D target) {
- resize(target, RectAlign.CENTER);
- }
-
-
- /**
- * Fit picture shape into the target rectangle, maintaining the aspect ratio
- * and repositioning within the target rectangle based on the specified
- * alignment (gravity).
- *
- * @param target The target rectangle
- * @param align
- * The alignment within the target rectangle when resizing.
- * A null value corresponds to RectAlign.CENTER
- */
- public void resize(Rectangle2D target, RectAlign align) {
- PictureShape,?> ps = getShape();
- Dimension dim = ps.getPictureData().getImageDimension();
- if (dim.width <= 0 || dim.height <= 0) {
- // nothing useful to be done for this case
- ps.setAnchor(target);
- return;
- }
-
- double w = target.getWidth();
- double h = target.getHeight();
-
- // scaling
- double sx = w / dim.width;
- double sy = h / dim.height;
-
- // position adjustments
- double dx = 0, dy = 0;
-
- if (sx > sy) {
- // use y-scaling for both, reposition x accordingly
- w = sy * dim.width;
- dx = target.getWidth() - w;
- } else if (sy > sx) {
- // use x-scaling for both, reposition y accordingly
- h = sx * dim.height;
- dy = target.getHeight() - h;
- } else {
- // uniform scaling, can use target values directly
- ps.setAnchor(target);
- return;
- }
-
- // the positioning
- double x = target.getX();
- double y = target.getY();
- switch (align) {
- case TOP: // X=balance, Y=ok
- x += dx/2;
- break;
- case TOP_RIGHT: // X=shift, Y=ok
- x += dx;
- break;
- case RIGHT: // X=shift, Y=balance
- x += dx;
- y += dy/2;
- break;
- case BOTTOM_RIGHT: // X=shift, Y=shift
- x += dx;
- y += dy;
- break;
- case BOTTOM: // X=balance, Y=shift
- x += dx/2;
- y += dy;
- break;
- case BOTTOM_LEFT: // X=ok, Y=shift
- y += dy;
- break;
- case LEFT: // X=ok, Y=balance
- y += dy/2;
- break;
- case TOP_LEFT: // X=ok, Y=ok
- /* no-op */
- break;
- default: // CENTER: X=balance, Y=balance
- x += dx/2;
- y += dy/2;
- break;
- }
-
- ps.setAnchor(new Rectangle2D.Double(x, y, w, h));
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+import org.apache.poi.sl.usermodel.PictureData;
+import org.apache.poi.sl.usermodel.PictureData.PictureType;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.sl.usermodel.PictureShape;
+import org.apache.poi.sl.usermodel.RectAlign;
+
+
+public class DrawPictureShape extends DrawSimpleShape {
+ private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
+ private static final String WMF_IMAGE_RENDERER = "org.apache.poi.hwmf.draw.HwmfSLImageRenderer";
+
+ public DrawPictureShape(PictureShape,?> shape) {
+ super(shape);
+ }
+
+ @Override
+ public void drawContent(Graphics2D graphics) {
+ PictureData data = getShape().getPictureData();
+ if(data == null) return;
+
+ Rectangle2D anchor = getAnchor(graphics, getShape());
+ Insets insets = getShape().getClipping();
+
+ try {
+ ImageRenderer renderer = getImageRenderer(graphics, data.getContentType());
+ renderer.loadImage(data.getData(), data.getContentType());
+ renderer.drawImage(graphics, anchor, insets);
+ } catch (IOException e) {
+ LOG.log(POILogger.ERROR, "image can't be loaded/rendered.", e);
+ }
+ }
+
+ /**
+ * Returns an ImageRenderer for the PictureData
+ *
+ * @param graphics
+ * @return the image renderer
+ */
+ public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
+ ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
+ if (renderer != null) {
+ return renderer;
+ }
+
+ if (PictureType.WMF.contentType.equals(contentType)) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class extends ImageRenderer> irc = (Class extends ImageRenderer>)
+ Thread.currentThread().getContextClassLoader().loadClass(WMF_IMAGE_RENDERER);
+ return irc.newInstance();
+ } catch (Exception e) {
+ // WMF image renderer is not on the classpath, continuing with BitmapRenderer
+ // although this doesn't make much sense ...
+ LOG.log(POILogger.ERROR, "WMF image renderer is not on the classpath - include poi-scratchpad jar!", e);
+ }
+ }
+
+ return new BitmapImageRenderer();
+ }
+
+ @Override
+ protected PictureShape,?> getShape() {
+ return (PictureShape,?>)shape;
+ }
+
+ /**
+ * Resize this picture to the default size.
+ *
+ * For PNG and JPEG resizes the image to 100%,
+ * for other types, if the size can't be determined it will be 200x200 pixels.
+ */
+ public void resize() {
+ PictureShape,?> ps = getShape();
+ Dimension dim = ps.getPictureData().getImageDimension();
+
+ Rectangle2D origRect = ps.getAnchor();
+ double x = origRect.getX();
+ double y = origRect.getY();
+ double w = dim.getWidth();
+ double h = dim.getHeight();
+ ps.setAnchor(new Rectangle2D.Double(x, y, w, h));
+ }
+
+
+ /**
+ * Fit picture shape into the target rectangle, maintaining the aspect ratio
+ * and repositioning within the target rectangle with a centered alignment.
+ *
+ * @param target The target rectangle
+ */
+ public void resize(Rectangle2D target) {
+ resize(target, RectAlign.CENTER);
+ }
+
+
+ /**
+ * Fit picture shape into the target rectangle, maintaining the aspect ratio
+ * and repositioning within the target rectangle based on the specified
+ * alignment (gravity).
+ *
+ * @param target The target rectangle
+ * @param align
+ * The alignment within the target rectangle when resizing.
+ * A null value corresponds to RectAlign.CENTER
+ */
+ public void resize(Rectangle2D target, RectAlign align) {
+ PictureShape,?> ps = getShape();
+ Dimension dim = ps.getPictureData().getImageDimension();
+ if (dim.width <= 0 || dim.height <= 0) {
+ // nothing useful to be done for this case
+ ps.setAnchor(target);
+ return;
+ }
+
+ double w = target.getWidth();
+ double h = target.getHeight();
+
+ // scaling
+ double sx = w / dim.width;
+ double sy = h / dim.height;
+
+ // position adjustments
+ double dx = 0, dy = 0;
+
+ if (sx > sy) {
+ // use y-scaling for both, reposition x accordingly
+ w = sy * dim.width;
+ dx = target.getWidth() - w;
+ } else if (sy > sx) {
+ // use x-scaling for both, reposition y accordingly
+ h = sx * dim.height;
+ dy = target.getHeight() - h;
+ } else {
+ // uniform scaling, can use target values directly
+ ps.setAnchor(target);
+ return;
+ }
+
+ // the positioning
+ double x = target.getX();
+ double y = target.getY();
+ switch (align) {
+ case TOP: // X=balance, Y=ok
+ x += dx/2;
+ break;
+ case TOP_RIGHT: // X=shift, Y=ok
+ x += dx;
+ break;
+ case RIGHT: // X=shift, Y=balance
+ x += dx;
+ y += dy/2;
+ break;
+ case BOTTOM_RIGHT: // X=shift, Y=shift
+ x += dx;
+ y += dy;
+ break;
+ case BOTTOM: // X=balance, Y=shift
+ x += dx/2;
+ y += dy;
+ break;
+ case BOTTOM_LEFT: // X=ok, Y=shift
+ y += dy;
+ break;
+ case LEFT: // X=ok, Y=balance
+ y += dy/2;
+ break;
+ case TOP_LEFT: // X=ok, Y=ok
+ /* no-op */
+ break;
+ default: // CENTER: X=balance, Y=balance
+ x += dx/2;
+ y += dy/2;
+ break;
+ }
+
+ ps.setAnchor(new Rectangle2D.Double(x, y, w, h));
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawShape.java b/src/java/org/apache/poi/sl/draw/DrawShape.java
index bb1f490bf6..6ce586f1a5 100644
--- a/src/java/org/apache/poi/sl/draw/DrawShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawShape.java
@@ -1,241 +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.poi.sl.draw;
-
-import java.awt.BasicStroke;
-import java.awt.Graphics2D;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.util.Locale;
-
-import org.apache.poi.sl.usermodel.PlaceableShape;
-import org.apache.poi.sl.usermodel.Shape;
-import org.apache.poi.sl.usermodel.StrokeStyle;
-import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
-import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
-
-
-public class DrawShape implements Drawable {
-
- protected final Shape,?> shape;
-
- public DrawShape(Shape,?> shape) {
- this.shape = shape;
- }
-
- /**
- * Sometimes it's necessary to distinguish between XSLF/HSLF for the rendering.
- * Use this method on the shape to determine, if we work on the BIFF implementation
- *
- * @param shape the shape to render
- * @return {@code true} if HSLF implementation is used
- */
- protected static boolean isHSLF(Shape,?> shape) {
- return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");
- }
-
- /**
- * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
- *
- * @param graphics the graphics whos transform matrix will be modified
- */
- @Override
- public void applyTransform(Graphics2D graphics) {
- if (!(shape instanceof PlaceableShape)) {
- return;
- }
-
- PlaceableShape,?> ps = (PlaceableShape,?>)shape;
- final boolean isHSLF = isHSLF(shape);
- AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- if (tx == null) {
- tx = new AffineTransform();
- }
- final Rectangle2D anchor = tx.createTransformedShape(ps.getAnchor()).getBounds2D();
-
- char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };
- for (char ch : cmds) {
- switch (ch) {
- case 'h':
- //flip horizontal
- if (ps.getFlipHorizontal()) {
- graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
- graphics.scale(-1, 1);
- graphics.translate(-anchor.getX(), -anchor.getY());
- }
- break;
- case 'v':
- //flip vertical
- if (ps.getFlipVertical()) {
- graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
- graphics.scale(1, -1);
- graphics.translate(-anchor.getX(), -anchor.getY());
- }
- break;
- case 'r':
- // rotation
- double rotation = ps.getRotation();
- if (rotation != 0.) {
- // PowerPoint rotates shapes relative to the geometric center
- double centerX = anchor.getCenterX();
- double centerY = anchor.getCenterY();
-
- // normalize rotation
- rotation %= 360.;
- if (rotation < 0) {
- rotation += 360.;
- }
-
- int quadrant = (((int)rotation+45)/90)%4;
- double scaleX = 1.0, scaleY = 1.0;
-
- // scale to bounding box (bug #53176)
- if (quadrant == 1 || quadrant == 3) {
- // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation
- // (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple
- // of 90 degrees and then resize the bounding box to its original bbox. After that we can
- // rotate the shape to the exact rotation amount.
- // It's strange that you'll need to rotate the shape back and forth again, but you can
- // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
- // be already (differently) scaled, so you can paint the shape in its default orientation
- // and later on, turn it around again to compare it with its original size ...
-
- AffineTransform txs;
- if (isHSLF) {
- txs = new AffineTransform(tx);
- } else {
- // this handling is only based on try and error ... not sure why xslf is handled differently.
- txs = new AffineTransform();
- txs.translate(centerX, centerY);
- txs.rotate(Math.PI/2.); // actually doesn't matter if +/- 90 degrees
- txs.translate(-centerX, -centerY);
- txs.concatenate(tx);
- }
-
- txs.translate(centerX, centerY);
- txs.rotate(Math.PI/2.);
- txs.translate(-centerX, -centerY);
-
- Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
-
- scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());
- scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());
- } else {
- quadrant = 0;
- }
-
- // transformation is applied reversed ...
- graphics.translate(centerX, centerY);
- double rot = Math.toRadians(rotation-quadrant*90.);
- if (rot != 0) {
- graphics.rotate(rot);
- }
- graphics.scale(scaleX, scaleY);
- rot = Math.toRadians(quadrant*90.);
- if (rot != 0) {
- graphics.rotate(rot);
- }
- graphics.translate(-centerX, -centerY);
- }
- break;
- default:
- throw new RuntimeException("unexpected transform code " + ch);
- }
- }
- }
-
- private static double safeScale(double dim1, double dim2) {
- if (dim1 == 0.) {
- return 1;
- }
- return (dim2 == 0.) ? 1 : dim1/dim2;
- }
-
- @Override
- public void draw(Graphics2D graphics) {
- }
-
- @Override
- public void drawContent(Graphics2D graphics) {
- }
-
- public static Rectangle2D getAnchor(Graphics2D graphics, PlaceableShape,?> shape) {
- return getAnchor(graphics, shape.getAnchor());
- }
-
- public static Rectangle2D getAnchor(Graphics2D graphics, Rectangle2D anchor) {
- if(graphics == null) {
- return anchor;
- }
-
- AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
- if(tx != null) {
- anchor = tx.createTransformedShape(anchor).getBounds2D();
- }
- return anchor;
- }
-
- protected Shape,?> getShape() {
- return shape;
- }
-
- protected static BasicStroke getStroke(StrokeStyle strokeStyle) {
- float lineWidth = (float) strokeStyle.getLineWidth();
- if (lineWidth == 0.0f) {
- // Both PowerPoint and OOo draw zero-length lines as 0.25pt
- lineWidth = 0.25f;
- }
-
- LineDash lineDash = strokeStyle.getLineDash();
- if (lineDash == null) {
- lineDash = LineDash.SOLID;
- }
-
- int dashPatI[] = lineDash.pattern;
- final float dash_phase = 0;
- float[] dashPatF = null;
- if (dashPatI != null) {
- dashPatF = new float[dashPatI.length];
- for (int i=0; i shape;
+
+ public DrawShape(Shape,?> shape) {
+ this.shape = shape;
+ }
+
+ /**
+ * Sometimes it's necessary to distinguish between XSLF/HSLF for the rendering.
+ * Use this method on the shape to determine, if we work on the BIFF implementation
+ *
+ * @param shape the shape to render
+ * @return {@code true} if HSLF implementation is used
+ */
+ protected static boolean isHSLF(Shape,?> shape) {
+ return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");
+ }
+
+ /**
+ * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
+ *
+ * @param graphics the graphics whos transform matrix will be modified
+ */
+ @Override
+ public void applyTransform(Graphics2D graphics) {
+ if (!(shape instanceof PlaceableShape)) {
+ return;
+ }
+
+ PlaceableShape,?> ps = (PlaceableShape,?>)shape;
+ final boolean isHSLF = isHSLF(shape);
+ AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+ if (tx == null) {
+ tx = new AffineTransform();
+ }
+ final Rectangle2D anchor = tx.createTransformedShape(ps.getAnchor()).getBounds2D();
+
+ char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };
+ for (char ch : cmds) {
+ switch (ch) {
+ case 'h':
+ //flip horizontal
+ if (ps.getFlipHorizontal()) {
+ graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
+ graphics.scale(-1, 1);
+ graphics.translate(-anchor.getX(), -anchor.getY());
+ }
+ break;
+ case 'v':
+ //flip vertical
+ if (ps.getFlipVertical()) {
+ graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
+ graphics.scale(1, -1);
+ graphics.translate(-anchor.getX(), -anchor.getY());
+ }
+ break;
+ case 'r':
+ // rotation
+ double rotation = ps.getRotation();
+ if (rotation != 0.) {
+ // PowerPoint rotates shapes relative to the geometric center
+ double centerX = anchor.getCenterX();
+ double centerY = anchor.getCenterY();
+
+ // normalize rotation
+ rotation %= 360.;
+ if (rotation < 0) {
+ rotation += 360.;
+ }
+
+ int quadrant = (((int)rotation+45)/90)%4;
+ double scaleX = 1.0, scaleY = 1.0;
+
+ // scale to bounding box (bug #53176)
+ if (quadrant == 1 || quadrant == 3) {
+ // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation
+ // (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple
+ // of 90 degrees and then resize the bounding box to its original bbox. After that we can
+ // rotate the shape to the exact rotation amount.
+ // It's strange that you'll need to rotate the shape back and forth again, but you can
+ // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
+ // be already (differently) scaled, so you can paint the shape in its default orientation
+ // and later on, turn it around again to compare it with its original size ...
+
+ AffineTransform txs;
+ if (isHSLF) {
+ txs = new AffineTransform(tx);
+ } else {
+ // this handling is only based on try and error ... not sure why xslf is handled differently.
+ txs = new AffineTransform();
+ txs.translate(centerX, centerY);
+ txs.rotate(Math.PI/2.); // actually doesn't matter if +/- 90 degrees
+ txs.translate(-centerX, -centerY);
+ txs.concatenate(tx);
+ }
+
+ txs.translate(centerX, centerY);
+ txs.rotate(Math.PI/2.);
+ txs.translate(-centerX, -centerY);
+
+ Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
+
+ scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());
+ scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());
+ } else {
+ quadrant = 0;
+ }
+
+ // transformation is applied reversed ...
+ graphics.translate(centerX, centerY);
+ double rot = Math.toRadians(rotation-quadrant*90.);
+ if (rot != 0) {
+ graphics.rotate(rot);
+ }
+ graphics.scale(scaleX, scaleY);
+ rot = Math.toRadians(quadrant*90.);
+ if (rot != 0) {
+ graphics.rotate(rot);
+ }
+ graphics.translate(-centerX, -centerY);
+ }
+ break;
+ default:
+ throw new RuntimeException("unexpected transform code " + ch);
+ }
+ }
+ }
+
+ private static double safeScale(double dim1, double dim2) {
+ if (dim1 == 0.) {
+ return 1;
+ }
+ return (dim2 == 0.) ? 1 : dim1/dim2;
+ }
+
+ @Override
+ public void draw(Graphics2D graphics) {
+ }
+
+ @Override
+ public void drawContent(Graphics2D graphics) {
+ }
+
+ public static Rectangle2D getAnchor(Graphics2D graphics, PlaceableShape,?> shape) {
+ return getAnchor(graphics, shape.getAnchor());
+ }
+
+ public static Rectangle2D getAnchor(Graphics2D graphics, Rectangle2D anchor) {
+ if(graphics == null) {
+ return anchor;
+ }
+
+ AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+ if(tx != null) {
+ anchor = tx.createTransformedShape(anchor).getBounds2D();
+ }
+ return anchor;
+ }
+
+ protected Shape,?> getShape() {
+ return shape;
+ }
+
+ protected static BasicStroke getStroke(StrokeStyle strokeStyle) {
+ float lineWidth = (float) strokeStyle.getLineWidth();
+ if (lineWidth == 0.0f) {
+ // Both PowerPoint and OOo draw zero-length lines as 0.25pt
+ lineWidth = 0.25f;
+ }
+
+ LineDash lineDash = strokeStyle.getLineDash();
+ if (lineDash == null) {
+ lineDash = LineDash.SOLID;
+ }
+
+ int dashPatI[] = lineDash.pattern;
+ final float dash_phase = 0;
+ float[] dashPatF = null;
+ if (dashPatI != null) {
+ dashPatF = new float[dashPatI.length];
+ for (int i=0; i sheet;
-
- public DrawSheet(Sheet,?> sheet) {
- this.sheet = sheet;
- }
-
- @Override
- public void draw(Graphics2D graphics) {
- Dimension dim = sheet.getSlideShow().getPageSize();
- Color whiteTrans = new Color(1f,1f,1f,0f);
- graphics.setColor(whiteTrans);
- graphics.fillRect(0, 0, (int)dim.getWidth(), (int)dim.getHeight());
-
- DrawFactory drawFact = DrawFactory.getInstance(graphics);
- MasterSheet,?> master = sheet.getMasterSheet();
-
- if(sheet.getFollowMasterGraphics() && master != null) {
- Drawable drawer = drawFact.getDrawable(master);
- drawer.draw(graphics);
- }
-
- graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, new AffineTransform());
-
- for (Shape,?> shape : sheet.getShapes()) {
- if(!canDraw(graphics, shape)) {
- continue;
- }
-
- // remember the initial transform and restore it after we are done with drawing
- AffineTransform at = graphics.getTransform();
-
- // concrete implementations can make sense of this hint,
- // for example PSGraphics2D or PDFGraphics2D would call gsave() / grestore
- graphics.setRenderingHint(Drawable.GSAVE, true);
-
- // apply rotation and flipping
- Drawable drawer = drawFact.getDrawable(shape);
- drawer.applyTransform(graphics);
- // draw stuff
- drawer.draw(graphics);
-
- // restore the coordinate system
- graphics.setTransform(at);
-
- graphics.setRenderingHint(Drawable.GRESTORE, true);
- }
- }
-
- @Override
- public void applyTransform(Graphics2D context) {
- }
-
- @Override
- public void drawContent(Graphics2D context) {
- }
-
- /**
- * Checks if this sheet displays the specified shape.
- *
- * Subclasses can override it and skip certain shapes from drawings,
- * for instance, slide masters and layouts don't display placeholders
- */
- protected boolean canDraw(Graphics2D graphics, Shape,?> shape){
- return true;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import org.apache.poi.sl.usermodel.MasterSheet;
+import org.apache.poi.sl.usermodel.Shape;
+import org.apache.poi.sl.usermodel.Sheet;
+
+
+public class DrawSheet implements Drawable {
+
+ protected final Sheet,?> sheet;
+
+ public DrawSheet(Sheet,?> sheet) {
+ this.sheet = sheet;
+ }
+
+ @Override
+ public void draw(Graphics2D graphics) {
+ Dimension dim = sheet.getSlideShow().getPageSize();
+ Color whiteTrans = new Color(1f,1f,1f,0f);
+ graphics.setColor(whiteTrans);
+ graphics.fillRect(0, 0, (int)dim.getWidth(), (int)dim.getHeight());
+
+ DrawFactory drawFact = DrawFactory.getInstance(graphics);
+ MasterSheet,?> master = sheet.getMasterSheet();
+
+ if(sheet.getFollowMasterGraphics() && master != null) {
+ Drawable drawer = drawFact.getDrawable(master);
+ drawer.draw(graphics);
+ }
+
+ graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, new AffineTransform());
+
+ for (Shape,?> shape : sheet.getShapes()) {
+ if(!canDraw(graphics, shape)) {
+ continue;
+ }
+
+ // remember the initial transform and restore it after we are done with drawing
+ AffineTransform at = graphics.getTransform();
+
+ // concrete implementations can make sense of this hint,
+ // for example PSGraphics2D or PDFGraphics2D would call gsave() / grestore
+ graphics.setRenderingHint(Drawable.GSAVE, true);
+
+ // apply rotation and flipping
+ Drawable drawer = drawFact.getDrawable(shape);
+ drawer.applyTransform(graphics);
+ // draw stuff
+ drawer.draw(graphics);
+
+ // restore the coordinate system
+ graphics.setTransform(at);
+
+ graphics.setRenderingHint(Drawable.GRESTORE, true);
+ }
+ }
+
+ @Override
+ public void applyTransform(Graphics2D context) {
+ }
+
+ @Override
+ public void drawContent(Graphics2D context) {
+ }
+
+ /**
+ * Checks if this sheet displays the specified shape.
+ *
+ * Subclasses can override it and skip certain shapes from drawings,
+ * for instance, slide masters and layouts don't display placeholders
+ */
+ protected boolean canDraw(Graphics2D graphics, Shape,?> shape){
+ return true;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
index ef0cc08478..ed52a6c9e1 100644
--- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
@@ -1,446 +1,446 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.Path2D;
-import java.awt.geom.Rectangle2D;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.stream.EventFilter;
-import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.events.StartElement;
-import javax.xml.stream.events.XMLEvent;
-
-import org.apache.poi.sl.draw.binding.CTCustomGeometry2D;
-import org.apache.poi.sl.draw.geom.Context;
-import org.apache.poi.sl.draw.geom.CustomGeometry;
-import org.apache.poi.sl.draw.geom.Outline;
-import org.apache.poi.sl.draw.geom.Path;
-import org.apache.poi.sl.usermodel.LineDecoration;
-import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
-import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
-import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
-import org.apache.poi.sl.usermodel.Shadow;
-import org.apache.poi.sl.usermodel.SimpleShape;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.Units;
-
-
-public class DrawSimpleShape extends DrawShape {
-
- private static final double DECO_SIZE_POW = 1.5d;
-
- public DrawSimpleShape(SimpleShape,?> shape) {
- super(shape);
- }
-
- @Override
- public void draw(Graphics2D graphics) {
- DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(getShape());
- Paint fill = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint());
- Paint line = drawPaint.getPaint(graphics, getShape().getStrokeStyle().getPaint());
- BasicStroke stroke = getStroke(); // the stroke applies both to the shadow and the shape
- graphics.setStroke(stroke);
-
- Collection elems = computeOutlines(graphics);
-
- // first paint the shadow
- drawShadow(graphics, elems, fill, line);
-
- // then fill the shape interior
- if (fill != null) {
- graphics.setPaint(fill);
- for (Outline o : elems) {
- if (o.getPath().isFilled()){
- java.awt.Shape s = o.getOutline();
- graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
- graphics.fill(s);
- }
- }
- }
-
- // then draw any content within this shape (text, image, etc.)
- drawContent(graphics);
-
- // then stroke the shape outline
- if(line != null) {
- graphics.setPaint(line);
- graphics.setStroke(stroke);
- for(Outline o : elems){
- if(o.getPath().isStroked()){
- java.awt.Shape s = o.getOutline();
- graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
- graphics.draw(s);
- }
- }
- }
-
- // draw line decorations
- drawDecoration(graphics, line, stroke);
- }
-
- protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {
- if(line == null) {
- return;
- }
- graphics.setPaint(line);
-
- List lst = new ArrayList();
- LineDecoration deco = getShape().getLineDecoration();
- Outline head = getHeadDecoration(graphics, deco, stroke);
- if (head != null) {
- lst.add(head);
- }
- Outline tail = getTailDecoration(graphics, deco, stroke);
- if (tail != null) {
- lst.add(tail);
- }
-
-
- for(Outline o : lst){
- java.awt.Shape s = o.getOutline();
- Path p = o.getPath();
- graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
-
- if(p.isFilled()) {
- graphics.fill(s);
- }
- if(p.isStroked()) {
- graphics.draw(s);
- }
- }
- }
-
- protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
- if (deco == null || stroke == null) {
- return null;
- }
- DecorationSize tailLength = deco.getTailLength();
- if (tailLength == null) {
- tailLength = DecorationSize.MEDIUM;
- }
- DecorationSize tailWidth = deco.getTailWidth();
- if (tailWidth == null) {
- tailWidth = DecorationSize.MEDIUM;
- }
-
- double lineWidth = Math.max(2.5, stroke.getLineWidth());
-
- Rectangle2D anchor = getAnchor(graphics, getShape());
- double x2 = anchor.getX() + anchor.getWidth(),
- y2 = anchor.getY() + anchor.getHeight();
-
- double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
-
- AffineTransform at = new AffineTransform();
- java.awt.Shape tailShape = null;
- Path p = null;
- Rectangle2D bounds;
- final double scaleY = Math.pow(DECO_SIZE_POW, tailWidth.ordinal()+1.);
- final double scaleX = Math.pow(DECO_SIZE_POW, tailLength.ordinal()+1.);
-
- DecorationShape tailShapeEnum = deco.getTailShape();
-
- if (tailShapeEnum == null) {
- return null;
- }
-
- switch (tailShapeEnum) {
- case OVAL:
- p = new Path();
- tailShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
- bounds = tailShape.getBounds2D();
- at.translate(x2 - bounds.getWidth() / 2, y2 - bounds.getHeight() / 2);
- at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
- break;
- case STEALTH:
- case ARROW:
- p = new Path(false, true);
- Path2D.Double arrow = new Path2D.Double();
- arrow.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
- arrow.lineTo(0, 0);
- arrow.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
- tailShape = arrow;
- at.translate(x2, y2);
- at.rotate(alpha);
- break;
- case TRIANGLE:
- p = new Path();
- Path2D.Double triangle = new Path2D.Double();
- triangle.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
- triangle.lineTo(0, 0);
- triangle.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
- triangle.closePath();
- tailShape = triangle;
- at.translate(x2, y2);
- at.rotate(alpha);
- break;
- default:
- break;
- }
-
- if (tailShape != null) {
- tailShape = at.createTransformedShape(tailShape);
- }
- return tailShape == null ? null : new Outline(tailShape, p);
- }
-
- protected Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
- if (deco == null || stroke == null) {
- return null;
- }
- DecorationSize headLength = deco.getHeadLength();
- if (headLength == null) {
- headLength = DecorationSize.MEDIUM;
- }
- DecorationSize headWidth = deco.getHeadWidth();
- if (headWidth == null) {
- headWidth = DecorationSize.MEDIUM;
- }
-
- double lineWidth = Math.max(2.5, stroke.getLineWidth());
-
- Rectangle2D anchor = getAnchor(graphics, getShape());
- double x1 = anchor.getX(), y1 = anchor.getY();
-
- double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
-
- AffineTransform at = new AffineTransform();
- java.awt.Shape headShape = null;
- Path p = null;
- Rectangle2D bounds;
- final double scaleY = Math.pow(DECO_SIZE_POW, headWidth.ordinal()+1.);
- final double scaleX = Math.pow(DECO_SIZE_POW, headLength.ordinal()+1.);
- DecorationShape headShapeEnum = deco.getHeadShape();
-
- if (headShapeEnum == null) {
- return null;
- }
-
- switch (headShapeEnum) {
- case OVAL:
- p = new Path();
- headShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
- bounds = headShape.getBounds2D();
- at.translate(x1 - bounds.getWidth() / 2, y1 - bounds.getHeight() / 2);
- at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
- break;
- case STEALTH:
- case ARROW:
- p = new Path(false, true);
- Path2D.Double arrow = new Path2D.Double();
- arrow.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
- arrow.lineTo(0, 0);
- arrow.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
- headShape = arrow;
- at.translate(x1, y1);
- at.rotate(alpha);
- break;
- case TRIANGLE:
- p = new Path();
- Path2D.Double triangle = new Path2D.Double();
- triangle.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
- triangle.lineTo(0, 0);
- triangle.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
- triangle.closePath();
- headShape = triangle;
- at.translate(x1, y1);
- at.rotate(alpha);
- break;
- default:
- break;
- }
-
- if (headShape != null) {
- headShape = at.createTransformedShape(headShape);
- }
- return headShape == null ? null : new Outline(headShape, p);
- }
-
- public BasicStroke getStroke() {
- return getStroke(getShape().getStrokeStyle());
- }
-
- protected void drawShadow(
- Graphics2D graphics
- , Collection outlines
- , Paint fill
- , Paint line
- ) {
- Shadow,?> shadow = getShape().getShadow();
- if (shadow == null || (fill == null && line == null)) {
- return;
- }
-
- SolidPaint shadowPaint = shadow.getFillStyle();
- Color shadowColor = DrawPaint.applyColorTransform(shadowPaint.getSolidColor());
-
- double shapeRotation = getShape().getRotation();
- if(getShape().getFlipVertical()) {
- shapeRotation += 180;
- }
- double angle = shadow.getAngle() - shapeRotation;
- double dist = shadow.getDistance();
- double dx = dist * Math.cos(Math.toRadians(angle));
- double dy = dist * Math.sin(Math.toRadians(angle));
-
- graphics.translate(dx, dy);
-
- for(Outline o : outlines){
- java.awt.Shape s = o.getOutline();
- Path p = o.getPath();
- graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
- graphics.setPaint(shadowColor);
-
- if(fill != null && p.isFilled()){
- graphics.fill(s);
- } else if (line != null && p.isStroked()) {
- graphics.draw(s);
- }
- }
-
- graphics.translate(-dx, -dy);
- }
-
- protected static CustomGeometry getCustomGeometry(String name) {
- return getCustomGeometry(name, null);
- }
-
- protected static CustomGeometry getCustomGeometry(String name, Graphics2D graphics) {
- @SuppressWarnings("unchecked")
- Map presets = (graphics == null)
- ? null
- : (Map)graphics.getRenderingHint(Drawable.PRESET_GEOMETRY_CACHE);
-
- if (presets == null) {
- presets = new HashMap();
- if (graphics != null) {
- graphics.setRenderingHint(Drawable.PRESET_GEOMETRY_CACHE, presets);
- }
-
- String packageName = "org.apache.poi.sl.draw.binding";
- InputStream presetIS = Drawable.class.getResourceAsStream("presetShapeDefinitions.xml");
-
- // StAX:
- EventFilter startElementFilter = new EventFilter() {
- @Override
- public boolean accept(XMLEvent event) {
- return event.isStartElement();
- }
- };
-
- try {
- XMLInputFactory staxFactory = XMLInputFactory.newInstance();
- XMLEventReader staxReader = staxFactory.createXMLEventReader(presetIS);
- XMLEventReader staxFiltRd = staxFactory.createFilteredReader(staxReader, startElementFilter);
- // Ignore StartElement:
- staxFiltRd.nextEvent();
- // JAXB:
- JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
- Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
-
- while (staxFiltRd.peek() != null) {
- StartElement evRoot = (StartElement)staxFiltRd.peek();
- String cusName = evRoot.getName().getLocalPart();
- // XMLEvent ev = staxReader.nextEvent();
- JAXBElement el = unmarshaller.unmarshal(staxReader, CTCustomGeometry2D.class);
- CTCustomGeometry2D cusGeom = el.getValue();
-
- presets.put(cusName, new CustomGeometry(cusGeom));
- }
-
- staxFiltRd.close();
- staxReader.close();
- } catch (Exception e) {
- throw new RuntimeException("Unable to load preset geometries.", e);
- } finally {
- IOUtils.closeQuietly(presetIS);
- }
- }
-
- return presets.get(name);
- }
-
- protected Collection computeOutlines(Graphics2D graphics) {
-
- List lst = new ArrayList();
- CustomGeometry geom = getShape().getGeometry();
- if(geom == null) {
- return lst;
- }
-
- Rectangle2D anchor = getAnchor(graphics, getShape());
- for (Path p : geom) {
-
- double w = p.getW() == -1 ? anchor.getWidth() * Units.EMU_PER_POINT : p.getW();
- double h = p.getH() == -1 ? anchor.getHeight() * Units.EMU_PER_POINT : p.getH();
-
- // the guides in the shape definitions are all defined relative to each other,
- // so we build the path starting from (0,0).
- final Rectangle2D pathAnchor = new Rectangle2D.Double(0,0,w,h);
-
- Context ctx = new Context(geom, pathAnchor, getShape());
-
- java.awt.Shape gp = p.getPath(ctx);
-
- // translate the result to the canvas coordinates in points
- AffineTransform at = new AffineTransform();
- at.translate(anchor.getX(), anchor.getY());
-
- double scaleX, scaleY;
- if (p.getW() != -1) {
- scaleX = anchor.getWidth() / p.getW();
- } else {
- scaleX = 1.0 / Units.EMU_PER_POINT;
- }
- if (p.getH() != -1) {
- scaleY = anchor.getHeight() / p.getH();
- } else {
- scaleY = 1.0 / Units.EMU_PER_POINT;
- }
-
- at.scale(scaleX, scaleY);
-
- java.awt.Shape canvasShape = at.createTransformedShape(gp);
-
- lst.add(new Outline(canvasShape, p));
- }
-
- return lst;
- }
-
- @Override
- protected SimpleShape,?> getShape() {
- return (SimpleShape,?>)shape;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.EventFilter;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+import org.apache.poi.sl.draw.binding.CTCustomGeometry2D;
+import org.apache.poi.sl.draw.geom.Context;
+import org.apache.poi.sl.draw.geom.CustomGeometry;
+import org.apache.poi.sl.draw.geom.Outline;
+import org.apache.poi.sl.draw.geom.Path;
+import org.apache.poi.sl.usermodel.LineDecoration;
+import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
+import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
+import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
+import org.apache.poi.sl.usermodel.Shadow;
+import org.apache.poi.sl.usermodel.SimpleShape;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.Units;
+
+
+public class DrawSimpleShape extends DrawShape {
+
+ private static final double DECO_SIZE_POW = 1.5d;
+
+ public DrawSimpleShape(SimpleShape,?> shape) {
+ super(shape);
+ }
+
+ @Override
+ public void draw(Graphics2D graphics) {
+ DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(getShape());
+ Paint fill = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint());
+ Paint line = drawPaint.getPaint(graphics, getShape().getStrokeStyle().getPaint());
+ BasicStroke stroke = getStroke(); // the stroke applies both to the shadow and the shape
+ graphics.setStroke(stroke);
+
+ Collection elems = computeOutlines(graphics);
+
+ // first paint the shadow
+ drawShadow(graphics, elems, fill, line);
+
+ // then fill the shape interior
+ if (fill != null) {
+ graphics.setPaint(fill);
+ for (Outline o : elems) {
+ if (o.getPath().isFilled()){
+ java.awt.Shape s = o.getOutline();
+ graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+ graphics.fill(s);
+ }
+ }
+ }
+
+ // then draw any content within this shape (text, image, etc.)
+ drawContent(graphics);
+
+ // then stroke the shape outline
+ if(line != null) {
+ graphics.setPaint(line);
+ graphics.setStroke(stroke);
+ for(Outline o : elems){
+ if(o.getPath().isStroked()){
+ java.awt.Shape s = o.getOutline();
+ graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+ graphics.draw(s);
+ }
+ }
+ }
+
+ // draw line decorations
+ drawDecoration(graphics, line, stroke);
+ }
+
+ protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {
+ if(line == null) {
+ return;
+ }
+ graphics.setPaint(line);
+
+ List lst = new ArrayList();
+ LineDecoration deco = getShape().getLineDecoration();
+ Outline head = getHeadDecoration(graphics, deco, stroke);
+ if (head != null) {
+ lst.add(head);
+ }
+ Outline tail = getTailDecoration(graphics, deco, stroke);
+ if (tail != null) {
+ lst.add(tail);
+ }
+
+
+ for(Outline o : lst){
+ java.awt.Shape s = o.getOutline();
+ Path p = o.getPath();
+ graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+
+ if(p.isFilled()) {
+ graphics.fill(s);
+ }
+ if(p.isStroked()) {
+ graphics.draw(s);
+ }
+ }
+ }
+
+ protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
+ if (deco == null || stroke == null) {
+ return null;
+ }
+ DecorationSize tailLength = deco.getTailLength();
+ if (tailLength == null) {
+ tailLength = DecorationSize.MEDIUM;
+ }
+ DecorationSize tailWidth = deco.getTailWidth();
+ if (tailWidth == null) {
+ tailWidth = DecorationSize.MEDIUM;
+ }
+
+ double lineWidth = Math.max(2.5, stroke.getLineWidth());
+
+ Rectangle2D anchor = getAnchor(graphics, getShape());
+ double x2 = anchor.getX() + anchor.getWidth(),
+ y2 = anchor.getY() + anchor.getHeight();
+
+ double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
+
+ AffineTransform at = new AffineTransform();
+ java.awt.Shape tailShape = null;
+ Path p = null;
+ Rectangle2D bounds;
+ final double scaleY = Math.pow(DECO_SIZE_POW, tailWidth.ordinal()+1.);
+ final double scaleX = Math.pow(DECO_SIZE_POW, tailLength.ordinal()+1.);
+
+ DecorationShape tailShapeEnum = deco.getTailShape();
+
+ if (tailShapeEnum == null) {
+ return null;
+ }
+
+ switch (tailShapeEnum) {
+ case OVAL:
+ p = new Path();
+ tailShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
+ bounds = tailShape.getBounds2D();
+ at.translate(x2 - bounds.getWidth() / 2, y2 - bounds.getHeight() / 2);
+ at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
+ break;
+ case STEALTH:
+ case ARROW:
+ p = new Path(false, true);
+ Path2D.Double arrow = new Path2D.Double();
+ arrow.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
+ arrow.lineTo(0, 0);
+ arrow.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
+ tailShape = arrow;
+ at.translate(x2, y2);
+ at.rotate(alpha);
+ break;
+ case TRIANGLE:
+ p = new Path();
+ Path2D.Double triangle = new Path2D.Double();
+ triangle.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
+ triangle.lineTo(0, 0);
+ triangle.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
+ triangle.closePath();
+ tailShape = triangle;
+ at.translate(x2, y2);
+ at.rotate(alpha);
+ break;
+ default:
+ break;
+ }
+
+ if (tailShape != null) {
+ tailShape = at.createTransformedShape(tailShape);
+ }
+ return tailShape == null ? null : new Outline(tailShape, p);
+ }
+
+ protected Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
+ if (deco == null || stroke == null) {
+ return null;
+ }
+ DecorationSize headLength = deco.getHeadLength();
+ if (headLength == null) {
+ headLength = DecorationSize.MEDIUM;
+ }
+ DecorationSize headWidth = deco.getHeadWidth();
+ if (headWidth == null) {
+ headWidth = DecorationSize.MEDIUM;
+ }
+
+ double lineWidth = Math.max(2.5, stroke.getLineWidth());
+
+ Rectangle2D anchor = getAnchor(graphics, getShape());
+ double x1 = anchor.getX(), y1 = anchor.getY();
+
+ double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
+
+ AffineTransform at = new AffineTransform();
+ java.awt.Shape headShape = null;
+ Path p = null;
+ Rectangle2D bounds;
+ final double scaleY = Math.pow(DECO_SIZE_POW, headWidth.ordinal()+1.);
+ final double scaleX = Math.pow(DECO_SIZE_POW, headLength.ordinal()+1.);
+ DecorationShape headShapeEnum = deco.getHeadShape();
+
+ if (headShapeEnum == null) {
+ return null;
+ }
+
+ switch (headShapeEnum) {
+ case OVAL:
+ p = new Path();
+ headShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
+ bounds = headShape.getBounds2D();
+ at.translate(x1 - bounds.getWidth() / 2, y1 - bounds.getHeight() / 2);
+ at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
+ break;
+ case STEALTH:
+ case ARROW:
+ p = new Path(false, true);
+ Path2D.Double arrow = new Path2D.Double();
+ arrow.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
+ arrow.lineTo(0, 0);
+ arrow.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
+ headShape = arrow;
+ at.translate(x1, y1);
+ at.rotate(alpha);
+ break;
+ case TRIANGLE:
+ p = new Path();
+ Path2D.Double triangle = new Path2D.Double();
+ triangle.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
+ triangle.lineTo(0, 0);
+ triangle.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
+ triangle.closePath();
+ headShape = triangle;
+ at.translate(x1, y1);
+ at.rotate(alpha);
+ break;
+ default:
+ break;
+ }
+
+ if (headShape != null) {
+ headShape = at.createTransformedShape(headShape);
+ }
+ return headShape == null ? null : new Outline(headShape, p);
+ }
+
+ public BasicStroke getStroke() {
+ return getStroke(getShape().getStrokeStyle());
+ }
+
+ protected void drawShadow(
+ Graphics2D graphics
+ , Collection outlines
+ , Paint fill
+ , Paint line
+ ) {
+ Shadow,?> shadow = getShape().getShadow();
+ if (shadow == null || (fill == null && line == null)) {
+ return;
+ }
+
+ SolidPaint shadowPaint = shadow.getFillStyle();
+ Color shadowColor = DrawPaint.applyColorTransform(shadowPaint.getSolidColor());
+
+ double shapeRotation = getShape().getRotation();
+ if(getShape().getFlipVertical()) {
+ shapeRotation += 180;
+ }
+ double angle = shadow.getAngle() - shapeRotation;
+ double dist = shadow.getDistance();
+ double dx = dist * Math.cos(Math.toRadians(angle));
+ double dy = dist * Math.sin(Math.toRadians(angle));
+
+ graphics.translate(dx, dy);
+
+ for(Outline o : outlines){
+ java.awt.Shape s = o.getOutline();
+ Path p = o.getPath();
+ graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+ graphics.setPaint(shadowColor);
+
+ if(fill != null && p.isFilled()){
+ graphics.fill(s);
+ } else if (line != null && p.isStroked()) {
+ graphics.draw(s);
+ }
+ }
+
+ graphics.translate(-dx, -dy);
+ }
+
+ protected static CustomGeometry getCustomGeometry(String name) {
+ return getCustomGeometry(name, null);
+ }
+
+ protected static CustomGeometry getCustomGeometry(String name, Graphics2D graphics) {
+ @SuppressWarnings("unchecked")
+ Map presets = (graphics == null)
+ ? null
+ : (Map)graphics.getRenderingHint(Drawable.PRESET_GEOMETRY_CACHE);
+
+ if (presets == null) {
+ presets = new HashMap();
+ if (graphics != null) {
+ graphics.setRenderingHint(Drawable.PRESET_GEOMETRY_CACHE, presets);
+ }
+
+ String packageName = "org.apache.poi.sl.draw.binding";
+ InputStream presetIS = Drawable.class.getResourceAsStream("presetShapeDefinitions.xml");
+
+ // StAX:
+ EventFilter startElementFilter = new EventFilter() {
+ @Override
+ public boolean accept(XMLEvent event) {
+ return event.isStartElement();
+ }
+ };
+
+ try {
+ XMLInputFactory staxFactory = XMLInputFactory.newInstance();
+ XMLEventReader staxReader = staxFactory.createXMLEventReader(presetIS);
+ XMLEventReader staxFiltRd = staxFactory.createFilteredReader(staxReader, startElementFilter);
+ // Ignore StartElement:
+ staxFiltRd.nextEvent();
+ // JAXB:
+ JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+
+ while (staxFiltRd.peek() != null) {
+ StartElement evRoot = (StartElement)staxFiltRd.peek();
+ String cusName = evRoot.getName().getLocalPart();
+ // XMLEvent ev = staxReader.nextEvent();
+ JAXBElement el = unmarshaller.unmarshal(staxReader, CTCustomGeometry2D.class);
+ CTCustomGeometry2D cusGeom = el.getValue();
+
+ presets.put(cusName, new CustomGeometry(cusGeom));
+ }
+
+ staxFiltRd.close();
+ staxReader.close();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to load preset geometries.", e);
+ } finally {
+ IOUtils.closeQuietly(presetIS);
+ }
+ }
+
+ return presets.get(name);
+ }
+
+ protected Collection computeOutlines(Graphics2D graphics) {
+
+ List lst = new ArrayList();
+ CustomGeometry geom = getShape().getGeometry();
+ if(geom == null) {
+ return lst;
+ }
+
+ Rectangle2D anchor = getAnchor(graphics, getShape());
+ for (Path p : geom) {
+
+ double w = p.getW() == -1 ? anchor.getWidth() * Units.EMU_PER_POINT : p.getW();
+ double h = p.getH() == -1 ? anchor.getHeight() * Units.EMU_PER_POINT : p.getH();
+
+ // the guides in the shape definitions are all defined relative to each other,
+ // so we build the path starting from (0,0).
+ final Rectangle2D pathAnchor = new Rectangle2D.Double(0,0,w,h);
+
+ Context ctx = new Context(geom, pathAnchor, getShape());
+
+ java.awt.Shape gp = p.getPath(ctx);
+
+ // translate the result to the canvas coordinates in points
+ AffineTransform at = new AffineTransform();
+ at.translate(anchor.getX(), anchor.getY());
+
+ double scaleX, scaleY;
+ if (p.getW() != -1) {
+ scaleX = anchor.getWidth() / p.getW();
+ } else {
+ scaleX = 1.0 / Units.EMU_PER_POINT;
+ }
+ if (p.getH() != -1) {
+ scaleY = anchor.getHeight() / p.getH();
+ } else {
+ scaleY = 1.0 / Units.EMU_PER_POINT;
+ }
+
+ at.scale(scaleX, scaleY);
+
+ java.awt.Shape canvasShape = at.createTransformedShape(gp);
+
+ lst.add(new Outline(canvasShape, p));
+ }
+
+ return lst;
+ }
+
+ @Override
+ protected SimpleShape,?> getShape() {
+ return (SimpleShape,?>)shape;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawSlide.java b/src/java/org/apache/poi/sl/draw/DrawSlide.java
index 2320e907a6..295c584387 100644
--- a/src/java/org/apache/poi/sl/draw/DrawSlide.java
+++ b/src/java/org/apache/poi/sl/draw/DrawSlide.java
@@ -1,44 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-
-import org.apache.poi.sl.usermodel.*;
-
-
-public class DrawSlide extends DrawSheet {
-
- public DrawSlide(Slide,?> slide) {
- super(slide);
- }
-
- public void draw(Graphics2D graphics) {
- graphics.setRenderingHint(Drawable.CURRENT_SLIDE, this.sheet);
-
- Background,?> bg = sheet.getBackground();
- if(bg != null) {
- DrawFactory drawFact = DrawFactory.getInstance(graphics);
- Drawable db = drawFact.getDrawable(bg);
- db.draw(graphics);
- }
-
- super.draw(graphics);
- graphics.setRenderingHint(Drawable.CURRENT_SLIDE, null);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+
+import org.apache.poi.sl.usermodel.*;
+
+
+public class DrawSlide extends DrawSheet {
+
+ public DrawSlide(Slide,?> slide) {
+ super(slide);
+ }
+
+ public void draw(Graphics2D graphics) {
+ graphics.setRenderingHint(Drawable.CURRENT_SLIDE, this.sheet);
+
+ Background,?> bg = sheet.getBackground();
+ if(bg != null) {
+ DrawFactory drawFact = DrawFactory.getInstance(graphics);
+ Drawable db = drawFact.getDrawable(bg);
+ db.draw(graphics);
+ }
+
+ super.draw(graphics);
+ graphics.setRenderingHint(Drawable.CURRENT_SLIDE, null);
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawTableShape.java b/src/java/org/apache/poi/sl/draw/DrawTableShape.java
index 947b2ad13c..905196fc90 100644
--- a/src/java/org/apache/poi/sl/draw/DrawTableShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawTableShape.java
@@ -1,257 +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.poi.sl.draw;
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.geom.Line2D;
-import java.awt.geom.Rectangle2D;
-
-import org.apache.poi.sl.usermodel.GroupShape;
-import org.apache.poi.sl.usermodel.StrokeStyle;
-import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;
-import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
-import org.apache.poi.sl.usermodel.TableCell;
-import org.apache.poi.sl.usermodel.TableCell.BorderEdge;
-import org.apache.poi.util.Internal;
-import org.apache.poi.sl.usermodel.TableShape;
-
-public class DrawTableShape extends DrawShape {
- /**
- * Additional spacing between cells
- */
- @Internal
- public static final int borderSize = 2;
-
- public DrawTableShape(TableShape,?> shape) {
- super(shape);
- }
-
- protected Drawable getGroupShape(Graphics2D graphics) {
- if (shape instanceof GroupShape) {
- DrawFactory df = DrawFactory.getInstance(graphics);
- return df.getDrawable((GroupShape,?>)shape);
- }
- return null;
- }
-
- public void applyTransform(Graphics2D graphics) {
- Drawable d = getGroupShape(graphics);
- if (d != null) {
- d.applyTransform(graphics);
- } else {
- super.applyTransform(graphics);
- }
- }
-
- public void draw(Graphics2D graphics) {
- Drawable d = getGroupShape(graphics);
- if (d != null) {
- d.draw(graphics);
- return;
- }
-
- TableShape,?> ts = getShape();
- DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(ts);
- final int rows = ts.getNumberOfRows();
- final int cols = ts.getNumberOfColumns();
-
- // draw background boxes
- for (int row=0; row tc = ts.getCell(row, col);
- if (tc == null || tc.isMerged()) {
- continue;
- }
-
- Paint fillPaint = drawPaint.getPaint(graphics, tc.getFillStyle().getPaint());
- graphics.setPaint(fillPaint);
- Rectangle2D cellAnc = tc.getAnchor();
- graphics.fill(cellAnc);
-
- for (BorderEdge edge : BorderEdge.values()) {
- StrokeStyle stroke = tc.getBorderStyle(edge);
- if (stroke == null) {
- continue;
- }
- graphics.setStroke(getStroke(stroke));
- Paint linePaint = drawPaint.getPaint(graphics, stroke.getPaint());
- graphics.setPaint(linePaint);
-
- double x=cellAnc.getX(), y=cellAnc.getY(), w=cellAnc.getWidth(), h=cellAnc.getHeight();
- Line2D line;
- switch (edge) {
- default:
- case bottom:
- line = new Line2D.Double(x-borderSize, y+h, x+w+borderSize, y+h);
- break;
- case left:
- line = new Line2D.Double(x, y, x, y+h+borderSize);
- break;
- case right:
- line = new Line2D.Double(x+w, y, x+w, y+h+borderSize);
- break;
- case top:
- line = new Line2D.Double(x-borderSize, y, x+w+borderSize, y);
- break;
- }
-
- graphics.draw(line);
- }
- }
- }
-
- // draw text
- drawContent(graphics);
- }
-
- public void drawContent(Graphics2D graphics) {
- Drawable d = getGroupShape(graphics);
- if (d != null) {
- d.drawContent(graphics);
- return;
- }
-
- TableShape,?> ts = getShape();
- DrawFactory df = DrawFactory.getInstance(graphics);
-
- final int rows = ts.getNumberOfRows();
- final int cols = ts.getNumberOfColumns();
-
- for (int row=0; row tc = ts.getCell(row, col);
- if (tc != null) {
- DrawTextShape dts = df.getDrawable(tc);
- dts.drawContent(graphics);
- }
- }
- }
- }
-
- @Override
- protected TableShape,?> getShape() {
- return (TableShape,?>)shape;
- }
-
- /**
- * Format the table and apply the specified Line to all cell boundaries,
- * both outside and inside.
- * An empty args parameter removes the affected border.
- *
- * @param args a varargs array possible containing {@link Double} (width),
- * {@link LineCompound}, {@link Color}, {@link LineDash}
- */
- public void setAllBorders(Object... args) {
- TableShape,?> table = getShape();
- final int rows = table.getNumberOfRows();
- final int cols = table.getNumberOfColumns();
-
- BorderEdge edges[] = { BorderEdge.top, BorderEdge.left, null, null };
- for (int row = 0; row < rows; row++) {
- for (int col = 0; col < cols; col++) {
- edges[2] = (col == cols - 1) ? BorderEdge.right : null;
- edges[3] = (row == rows - 1) ? BorderEdge.bottom : null;
- setEdges(table.getCell(row, col), edges, args);
- }
- }
- }
-
- /**
- * Format the outside border using the specified Line object
- * An empty args parameter removes the affected border.
- *
- * @param args a varargs array possible containing {@link Double} (width),
- * {@link LineCompound}, {@link Color}, {@link LineDash}
- */
- public void setOutsideBorders(Object... args){
- if (args.length == 0) return;
-
- TableShape,?> table = getShape();
- final int rows = table.getNumberOfRows();
- final int cols = table.getNumberOfColumns();
-
- BorderEdge edges[] = new BorderEdge[4];
- for (int row = 0; row < rows; row++) {
- for (int col = 0; col < cols; col++) {
- edges[0] = (col == 0) ? BorderEdge.left : null;
- edges[1] = (col == cols - 1) ? BorderEdge.right : null;
- edges[2] = (row == 0) ? BorderEdge.top : null;
- edges[3] = (row == rows - 1) ? BorderEdge.bottom : null;
- setEdges(table.getCell(row, col), edges, args);
- }
- }
- }
-
- /**
- * Format the inside border using the specified Line object
- * An empty args parameter removes the affected border.
- *
- * @param args a varargs array possible containing {@link Double} (width),
- * {@link LineCompound}, {@link Color}, {@link LineDash}
- */
- public void setInsideBorders(Object... args) {
- if (args.length == 0) return;
-
- TableShape,?> table = getShape();
- final int rows = table.getNumberOfRows();
- final int cols = table.getNumberOfColumns();
-
- BorderEdge edges[] = new BorderEdge[2];
- for (int row = 0; row < rows; row++) {
- for (int col = 0; col < cols; col++) {
- edges[0] = (col > 0 && col < cols - 1) ? BorderEdge.right : null;
- edges[1] = (row > 0 && row < rows - 1) ? BorderEdge.bottom : null;
- setEdges(table.getCell(row, col), edges, args);
- }
- }
- }
-
- /**
- * Apply the border attributes (args) to the given cell and edges
- *
- * @param cell the cell
- * @param edges the border edges
- * @param args the border attributes
- */
- private static void setEdges(TableCell,?> cell, BorderEdge edges[], Object... args) {
- if (cell == null) {
- return;
- }
- for (BorderEdge be : edges) {
- if (be != null) {
- if (args.length == 0) {
- cell.removeBorder(be);
- } else {
- for (Object o : args) {
- if (o instanceof Double) {
- cell.setBorderWidth(be, (Double)o);
- } else if (o instanceof Color) {
- cell.setBorderColor(be, (Color)o);
- } else if (o instanceof LineDash) {
- cell.setBorderDash(be, (LineDash)o);
- } else if (o instanceof LineCompound) {
- cell.setBorderCompound(be, (LineCompound)o);
- }
- }
- }
- }
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.poi.sl.usermodel.GroupShape;
+import org.apache.poi.sl.usermodel.StrokeStyle;
+import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;
+import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
+import org.apache.poi.sl.usermodel.TableCell;
+import org.apache.poi.sl.usermodel.TableCell.BorderEdge;
+import org.apache.poi.util.Internal;
+import org.apache.poi.sl.usermodel.TableShape;
+
+public class DrawTableShape extends DrawShape {
+ /**
+ * Additional spacing between cells
+ */
+ @Internal
+ public static final int borderSize = 2;
+
+ public DrawTableShape(TableShape,?> shape) {
+ super(shape);
+ }
+
+ protected Drawable getGroupShape(Graphics2D graphics) {
+ if (shape instanceof GroupShape) {
+ DrawFactory df = DrawFactory.getInstance(graphics);
+ return df.getDrawable((GroupShape,?>)shape);
+ }
+ return null;
+ }
+
+ public void applyTransform(Graphics2D graphics) {
+ Drawable d = getGroupShape(graphics);
+ if (d != null) {
+ d.applyTransform(graphics);
+ } else {
+ super.applyTransform(graphics);
+ }
+ }
+
+ public void draw(Graphics2D graphics) {
+ Drawable d = getGroupShape(graphics);
+ if (d != null) {
+ d.draw(graphics);
+ return;
+ }
+
+ TableShape,?> ts = getShape();
+ DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(ts);
+ final int rows = ts.getNumberOfRows();
+ final int cols = ts.getNumberOfColumns();
+
+ // draw background boxes
+ for (int row=0; row tc = ts.getCell(row, col);
+ if (tc == null || tc.isMerged()) {
+ continue;
+ }
+
+ Paint fillPaint = drawPaint.getPaint(graphics, tc.getFillStyle().getPaint());
+ graphics.setPaint(fillPaint);
+ Rectangle2D cellAnc = tc.getAnchor();
+ graphics.fill(cellAnc);
+
+ for (BorderEdge edge : BorderEdge.values()) {
+ StrokeStyle stroke = tc.getBorderStyle(edge);
+ if (stroke == null) {
+ continue;
+ }
+ graphics.setStroke(getStroke(stroke));
+ Paint linePaint = drawPaint.getPaint(graphics, stroke.getPaint());
+ graphics.setPaint(linePaint);
+
+ double x=cellAnc.getX(), y=cellAnc.getY(), w=cellAnc.getWidth(), h=cellAnc.getHeight();
+ Line2D line;
+ switch (edge) {
+ default:
+ case bottom:
+ line = new Line2D.Double(x-borderSize, y+h, x+w+borderSize, y+h);
+ break;
+ case left:
+ line = new Line2D.Double(x, y, x, y+h+borderSize);
+ break;
+ case right:
+ line = new Line2D.Double(x+w, y, x+w, y+h+borderSize);
+ break;
+ case top:
+ line = new Line2D.Double(x-borderSize, y, x+w+borderSize, y);
+ break;
+ }
+
+ graphics.draw(line);
+ }
+ }
+ }
+
+ // draw text
+ drawContent(graphics);
+ }
+
+ public void drawContent(Graphics2D graphics) {
+ Drawable d = getGroupShape(graphics);
+ if (d != null) {
+ d.drawContent(graphics);
+ return;
+ }
+
+ TableShape,?> ts = getShape();
+ DrawFactory df = DrawFactory.getInstance(graphics);
+
+ final int rows = ts.getNumberOfRows();
+ final int cols = ts.getNumberOfColumns();
+
+ for (int row=0; row tc = ts.getCell(row, col);
+ if (tc != null) {
+ DrawTextShape dts = df.getDrawable(tc);
+ dts.drawContent(graphics);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected TableShape,?> getShape() {
+ return (TableShape,?>)shape;
+ }
+
+ /**
+ * Format the table and apply the specified Line to all cell boundaries,
+ * both outside and inside.
+ * An empty args parameter removes the affected border.
+ *
+ * @param args a varargs array possible containing {@link Double} (width),
+ * {@link LineCompound}, {@link Color}, {@link LineDash}
+ */
+ public void setAllBorders(Object... args) {
+ TableShape,?> table = getShape();
+ final int rows = table.getNumberOfRows();
+ final int cols = table.getNumberOfColumns();
+
+ BorderEdge edges[] = { BorderEdge.top, BorderEdge.left, null, null };
+ for (int row = 0; row < rows; row++) {
+ for (int col = 0; col < cols; col++) {
+ edges[2] = (col == cols - 1) ? BorderEdge.right : null;
+ edges[3] = (row == rows - 1) ? BorderEdge.bottom : null;
+ setEdges(table.getCell(row, col), edges, args);
+ }
+ }
+ }
+
+ /**
+ * Format the outside border using the specified Line object
+ * An empty args parameter removes the affected border.
+ *
+ * @param args a varargs array possible containing {@link Double} (width),
+ * {@link LineCompound}, {@link Color}, {@link LineDash}
+ */
+ public void setOutsideBorders(Object... args){
+ if (args.length == 0) return;
+
+ TableShape,?> table = getShape();
+ final int rows = table.getNumberOfRows();
+ final int cols = table.getNumberOfColumns();
+
+ BorderEdge edges[] = new BorderEdge[4];
+ for (int row = 0; row < rows; row++) {
+ for (int col = 0; col < cols; col++) {
+ edges[0] = (col == 0) ? BorderEdge.left : null;
+ edges[1] = (col == cols - 1) ? BorderEdge.right : null;
+ edges[2] = (row == 0) ? BorderEdge.top : null;
+ edges[3] = (row == rows - 1) ? BorderEdge.bottom : null;
+ setEdges(table.getCell(row, col), edges, args);
+ }
+ }
+ }
+
+ /**
+ * Format the inside border using the specified Line object
+ * An empty args parameter removes the affected border.
+ *
+ * @param args a varargs array possible containing {@link Double} (width),
+ * {@link LineCompound}, {@link Color}, {@link LineDash}
+ */
+ public void setInsideBorders(Object... args) {
+ if (args.length == 0) return;
+
+ TableShape,?> table = getShape();
+ final int rows = table.getNumberOfRows();
+ final int cols = table.getNumberOfColumns();
+
+ BorderEdge edges[] = new BorderEdge[2];
+ for (int row = 0; row < rows; row++) {
+ for (int col = 0; col < cols; col++) {
+ edges[0] = (col > 0 && col < cols - 1) ? BorderEdge.right : null;
+ edges[1] = (row > 0 && row < rows - 1) ? BorderEdge.bottom : null;
+ setEdges(table.getCell(row, col), edges, args);
+ }
+ }
+ }
+
+ /**
+ * Apply the border attributes (args) to the given cell and edges
+ *
+ * @param cell the cell
+ * @param edges the border edges
+ * @param args the border attributes
+ */
+ private static void setEdges(TableCell,?> cell, BorderEdge edges[], Object... args) {
+ if (cell == null) {
+ return;
+ }
+ for (BorderEdge be : edges) {
+ if (be != null) {
+ if (args.length == 0) {
+ cell.removeBorder(be);
+ } else {
+ for (Object o : args) {
+ if (o instanceof Double) {
+ cell.setBorderWidth(be, (Double)o);
+ } else if (o instanceof Color) {
+ cell.setBorderColor(be, (Color)o);
+ } else if (o instanceof LineDash) {
+ cell.setBorderDash(be, (LineDash)o);
+ } else if (o instanceof LineCompound) {
+ cell.setBorderCompound(be, (LineCompound)o);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawTextBox.java b/src/java/org/apache/poi/sl/draw/DrawTextBox.java
index d44d10807e..1b2b2f444c 100644
--- a/src/java/org/apache/poi/sl/draw/DrawTextBox.java
+++ b/src/java/org/apache/poi/sl/draw/DrawTextBox.java
@@ -1,26 +1,26 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import org.apache.poi.sl.usermodel.*;
-
-public class DrawTextBox extends DrawAutoShape {
- public DrawTextBox(TextBox,?> shape) {
- super(shape);
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import org.apache.poi.sl.usermodel.*;
+
+public class DrawTextBox extends DrawAutoShape {
+ public DrawTextBox(TextBox,?> shape) {
+ super(shape);
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawTextFragment.java b/src/java/org/apache/poi/sl/draw/DrawTextFragment.java
index acb6b4c766..178983d5e1 100644
--- a/src/java/org/apache/poi/sl/draw/DrawTextFragment.java
+++ b/src/java/org/apache/poi/sl/draw/DrawTextFragment.java
@@ -1,105 +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.poi.sl.draw;
-
-import java.awt.Graphics2D;
-import java.awt.font.TextLayout;
-import java.text.*;
-
-public class DrawTextFragment implements Drawable {
- final TextLayout layout;
- final AttributedString str;
- double x, y;
-
- public DrawTextFragment(TextLayout layout, AttributedString str) {
- this.layout = layout;
- this.str = str;
- }
-
- public void setPosition(double x, double y) {
- // TODO: replace it, by applyTransform????
- this.x = x;
- this.y = y;
- }
-
- public void draw(Graphics2D graphics){
- if(str == null) {
- return;
- }
-
- double yBaseline = y + layout.getAscent();
-
- Integer textMode = (Integer)graphics.getRenderingHint(Drawable.TEXT_RENDERING_MODE);
- if(textMode != null && textMode == Drawable.TEXT_AS_SHAPES){
- layout.draw(graphics, (float)x, (float)yBaseline);
- } else {
- graphics.drawString(str.getIterator(), (float)x, (float)yBaseline );
- }
- }
-
- public void applyTransform(Graphics2D graphics) {
- }
-
- public void drawContent(Graphics2D graphics) {
- }
-
- public TextLayout getLayout() {
- return layout;
- }
-
- public AttributedString getAttributedString() {
- return str;
- }
-
- /**
- * @return full height of this text run which is sum of ascent, descent and leading
- */
- public float getHeight(){
- double h = Math.ceil(layout.getAscent()) + Math.ceil(layout.getDescent()) + layout.getLeading();
- return (float)h;
- }
-
- /**
- *
- * @return width if this text run
- */
- public float getWidth(){
- return layout.getAdvance();
- }
-
- /**
- *
- * @return the string to be painted
- */
- public String getString(){
- if (str == null) return "";
-
- AttributedCharacterIterator it = str.getIterator();
- StringBuilder buf = new StringBuilder();
- for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
- buf.append(c);
- }
- return buf.toString();
- }
-
- @Override
- public String toString(){
- return "[" + getClass().getSimpleName() + "] " + getString();
- }
-
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.font.TextLayout;
+import java.text.*;
+
+public class DrawTextFragment implements Drawable {
+ final TextLayout layout;
+ final AttributedString str;
+ double x, y;
+
+ public DrawTextFragment(TextLayout layout, AttributedString str) {
+ this.layout = layout;
+ this.str = str;
+ }
+
+ public void setPosition(double x, double y) {
+ // TODO: replace it, by applyTransform????
+ this.x = x;
+ this.y = y;
+ }
+
+ public void draw(Graphics2D graphics){
+ if(str == null) {
+ return;
+ }
+
+ double yBaseline = y + layout.getAscent();
+
+ Integer textMode = (Integer)graphics.getRenderingHint(Drawable.TEXT_RENDERING_MODE);
+ if(textMode != null && textMode == Drawable.TEXT_AS_SHAPES){
+ layout.draw(graphics, (float)x, (float)yBaseline);
+ } else {
+ graphics.drawString(str.getIterator(), (float)x, (float)yBaseline );
+ }
+ }
+
+ public void applyTransform(Graphics2D graphics) {
+ }
+
+ public void drawContent(Graphics2D graphics) {
+ }
+
+ public TextLayout getLayout() {
+ return layout;
+ }
+
+ public AttributedString getAttributedString() {
+ return str;
+ }
+
+ /**
+ * @return full height of this text run which is sum of ascent, descent and leading
+ */
+ public float getHeight(){
+ double h = Math.ceil(layout.getAscent()) + Math.ceil(layout.getDescent()) + layout.getLeading();
+ return (float)h;
+ }
+
+ /**
+ *
+ * @return width if this text run
+ */
+ public float getWidth(){
+ return layout.getAdvance();
+ }
+
+ /**
+ *
+ * @return the string to be painted
+ */
+ public String getString(){
+ if (str == null) return "";
+
+ AttributedCharacterIterator it = str.getIterator();
+ StringBuilder buf = new StringBuilder();
+ for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
+ buf.append(c);
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public String toString(){
+ return "[" + getClass().getSimpleName() + "] " + getString();
+ }
+
+}
diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
index f2b24b0ff2..c60d0b45c7 100644
--- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
+++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java
@@ -1,731 +1,731 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.font.FontRenderContext;
-import java.awt.font.LineBreakMeasurer;
-import java.awt.font.TextAttribute;
-import java.awt.font.TextLayout;
-import java.awt.geom.Rectangle2D;
-import java.io.InvalidObjectException;
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedCharacterIterator.Attribute;
-import java.text.AttributedString;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.poi.sl.usermodel.AutoNumberingScheme;
-import org.apache.poi.sl.usermodel.Hyperlink;
-import org.apache.poi.sl.usermodel.Insets2D;
-import org.apache.poi.sl.usermodel.PaintStyle;
-import org.apache.poi.sl.usermodel.PlaceableShape;
-import org.apache.poi.sl.usermodel.ShapeContainer;
-import org.apache.poi.sl.usermodel.Sheet;
-import org.apache.poi.sl.usermodel.Slide;
-import org.apache.poi.sl.usermodel.TextParagraph;
-import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
-import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
-import org.apache.poi.sl.usermodel.TextRun;
-import org.apache.poi.sl.usermodel.TextRun.FieldType;
-import org.apache.poi.sl.usermodel.TextRun.TextCap;
-import org.apache.poi.sl.usermodel.TextShape;
-import org.apache.poi.sl.usermodel.TextShape.TextDirection;
-import org.apache.poi.util.StringUtil;
-import org.apache.poi.util.Units;
-
-
-public class DrawTextParagraph implements Drawable {
- /** Keys for passing hyperlinks to the graphics context */
- public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href");
- public static final XlinkAttribute HYPERLINK_LABEL = new XlinkAttribute("label");
-
- protected TextParagraph,?,?> paragraph;
- double x, y;
- protected List lines = new ArrayList();
- protected String rawText;
- protected DrawTextFragment bullet;
- protected int autoNbrIdx;
-
- /**
- * the highest line in this paragraph. Used for line spacing.
- */
- protected double maxLineHeight;
-
- /**
- * Defines an attribute used for storing the hyperlink associated with
- * some renderable text.
- */
- private static class XlinkAttribute extends Attribute {
-
- XlinkAttribute(String name) {
- super(name);
- }
-
- /**
- * Resolves instances being deserialized to the predefined constants.
- */
- @Override
- protected Object readResolve() throws InvalidObjectException {
- if (HYPERLINK_HREF.getName().equals(getName())) {
- return HYPERLINK_HREF;
- }
- if (HYPERLINK_LABEL.getName().equals(getName())) {
- return HYPERLINK_LABEL;
- }
- throw new InvalidObjectException("unknown attribute name");
- }
- }
-
-
- public DrawTextParagraph(TextParagraph,?,?> paragraph) {
- this.paragraph = paragraph;
- }
-
- public void setPosition(double x, double y) {
- // TODO: replace it, by applyTransform????
- this.x = x;
- this.y = y;
- }
-
- public double getY() {
- return y;
- }
-
- /**
- * Sets the auto numbering index of the handled paragraph
- * @param index the auto numbering index
- */
- public void setAutoNumberingIdx(int index) {
- autoNbrIdx = index;
- }
-
- @Override
- public void draw(Graphics2D graphics){
- if (lines.isEmpty()) {
- return;
- }
-
- double penY = y;
-
- boolean firstLine = true;
- int indentLevel = paragraph.getIndentLevel();
- Double leftMargin = paragraph.getLeftMargin();
- if (leftMargin == null) {
- // if the marL attribute is omitted, then a value of 347663 is implied
- leftMargin = Units.toPoints(347663L*indentLevel);
- }
- Double indent = paragraph.getIndent();
- if (indent == null) {
- indent = Units.toPoints(347663L*indentLevel);
- }
- if (isHSLF()) {
- // special handling for HSLF
- indent -= leftMargin;
- }
-
-// Double rightMargin = paragraph.getRightMargin();
-// if (rightMargin == null) {
-// rightMargin = 0d;
-// }
-
- //The vertical line spacing
- Double spacing = paragraph.getLineSpacing();
- if (spacing == null) {
- spacing = 100d;
- }
-
- for(DrawTextFragment line : lines){
- double penX;
-
- if(firstLine) {
- if (!isEmptyParagraph()) {
- // TODO: find out character style for empty, but bulleted/numbered lines
- bullet = getBullet(graphics, line.getAttributedString().getIterator());
- }
-
- if (bullet != null){
- bullet.setPosition(x+leftMargin+indent, penY);
- bullet.draw(graphics);
- // don't let text overlay the bullet and advance by the bullet width
- double bulletWidth = bullet.getLayout().getAdvance() + 1;
- penX = x + Math.max(leftMargin, leftMargin+indent+bulletWidth);
- } else {
- penX = x + leftMargin;
- }
- } else {
- penX = x + leftMargin;
- }
-
- Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
- // Insets are already applied on DrawTextShape.drawContent
- // but (outer) anchor need to be adjusted
- Insets2D insets = paragraph.getParentShape().getInsets();
- double leftInset = insets.left;
- double rightInset = insets.right;
-
- TextAlign ta = paragraph.getTextAlign();
- if (ta == null) {
- ta = TextAlign.LEFT;
- }
- switch (ta) {
- case CENTER:
- penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset - leftMargin) / 2;
- break;
- case RIGHT:
- penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset);
- break;
- default:
- break;
- }
-
- line.setPosition(penX, penY);
- line.draw(graphics);
-
- if(spacing > 0) {
- // If linespacing >= 0, then linespacing is a percentage of normal line height.
- penY += spacing*0.01* line.getHeight();
- } else {
- // negative value means absolute spacing in points
- penY += -spacing;
- }
-
- firstLine = false;
- }
-
- y = penY - y;
- }
-
- public float getFirstLineHeight() {
- return (lines.isEmpty()) ? 0 : lines.get(0).getHeight();
- }
-
- public float getLastLineHeight() {
- return (lines.isEmpty()) ? 0 : lines.get(lines.size()-1).getHeight();
- }
-
- public boolean isEmptyParagraph() {
- return (lines.isEmpty() || rawText.trim().isEmpty());
- }
-
- @Override
- public void applyTransform(Graphics2D graphics) {
- }
-
- @Override
- public void drawContent(Graphics2D graphics) {
- }
-
- /**
- * break text into lines, each representing a line of text that fits in the wrapping width
- *
- * @param graphics The drawing context for computing text-lengths.
- */
- protected void breakText(Graphics2D graphics){
- lines.clear();
-
- DrawFactory fact = DrawFactory.getInstance(graphics);
- StringBuilder text = new StringBuilder();
- AttributedString at = getAttributedString(graphics, text);
- boolean emptyParagraph = ("".equals(text.toString().trim()));
-
- AttributedCharacterIterator it = at.getIterator();
- LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
- for (;;) {
- int startIndex = measurer.getPosition();
-
- double wrappingWidth = getWrappingWidth(lines.size() == 0, graphics) + 1; // add a pixel to compensate rounding errors
- // shape width can be smaller that the sum of insets (this was proved by a test file)
- if(wrappingWidth < 0) {
- wrappingWidth = 1;
- }
-
- int nextBreak = text.indexOf("\n", startIndex + 1);
- if (nextBreak == -1) {
- nextBreak = it.getEndIndex();
- }
-
- TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true);
- if (layout == null) {
- // layout can be null if the entire word at the current position
- // does not fit within the wrapping width. Try with requireNextWord=false.
- layout = measurer.nextLayout((float)wrappingWidth, nextBreak, false);
- }
-
- if(layout == null) {
- // exit if can't break any more
- break;
- }
-
- int endIndex = measurer.getPosition();
- // skip over new line breaks (we paint 'clear' text runs not starting or ending with \n)
- if(endIndex < it.getEndIndex() && text.charAt(endIndex) == '\n'){
- measurer.setPosition(endIndex + 1);
- }
-
- TextAlign hAlign = paragraph.getTextAlign();
- if(hAlign == TextAlign.JUSTIFY || hAlign == TextAlign.JUSTIFY_LOW) {
- layout = layout.getJustifiedLayout((float)wrappingWidth);
- }
-
- AttributedString str = (emptyParagraph)
- ? null // we will not paint empty paragraphs
- : new AttributedString(it, startIndex, endIndex);
- DrawTextFragment line = fact.getTextFragment(layout, str);
- lines.add(line);
-
- maxLineHeight = Math.max(maxLineHeight, line.getHeight());
-
- if(endIndex == it.getEndIndex()) {
- break;
- }
- }
-
- rawText = text.toString();
- }
-
- protected DrawTextFragment getBullet(Graphics2D graphics, AttributedCharacterIterator firstLineAttr) {
- BulletStyle bulletStyle = paragraph.getBulletStyle();
- if (bulletStyle == null) {
- return null;
- }
-
- String buCharacter;
- AutoNumberingScheme ans = bulletStyle.getAutoNumberingScheme();
- if (ans != null) {
- buCharacter = ans.format(autoNbrIdx);
- } else {
- buCharacter = bulletStyle.getBulletCharacter();
- }
- if (buCharacter == null) {
- return null;
- }
-
- String buFont = bulletStyle.getBulletFont();
- if (buFont == null) {
- buFont = paragraph.getDefaultFontFamily();
- }
- assert(buFont != null);
-
- PlaceableShape,?> ps = getParagraphShape();
- PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor();
- Paint fgPaint;
- if (fgPaintStyle == null) {
- fgPaint = (Paint)firstLineAttr.getAttribute(TextAttribute.FOREGROUND);
- } else {
- fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);
- }
-
- float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);
- Double buSz = bulletStyle.getBulletFontSize();
- if (buSz == null) {
- buSz = 100d;
- }
- if (buSz > 0) {
- fontSize *= buSz* 0.01;
- } else {
- fontSize = (float)-buSz;
- }
-
-
- AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));
- str.addAttribute(TextAttribute.FOREGROUND, fgPaint);
- str.addAttribute(TextAttribute.FAMILY, buFont);
- str.addAttribute(TextAttribute.SIZE, fontSize);
-
- TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
- DrawFactory fact = DrawFactory.getInstance(graphics);
- return fact.getTextFragment(layout, str);
- }
-
- protected String getRenderableText(Graphics2D graphics, TextRun tr) {
- if (tr.getFieldType() == FieldType.SLIDE_NUMBER) {
- Slide,?> slide = (Slide,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
- return (slide == null) ? "" : Integer.toString(slide.getSlideNumber());
- }
- StringBuilder buf = new StringBuilder();
- TextCap cap = tr.getTextCap();
- String tabs = null;
- for (char c : tr.getRawText().toCharArray()) {
- switch (c) {
- case '\t':
- if (tabs == null) {
- tabs = tab2space(tr);
- }
- buf.append(tabs);
- break;
- case '\u000b':
- buf.append('\n');
- break;
- default:
- switch (cap) {
- case ALL: c = Character.toUpperCase(c); break;
- case SMALL: c = Character.toLowerCase(c); break;
- case NONE: break;
- }
-
- buf.append(c);
- break;
- }
- }
-
- return buf.toString();
- }
-
- /**
- * Replace a tab with the effective number of white spaces.
- */
- private String tab2space(TextRun tr) {
- AttributedString string = new AttributedString(" ");
- String fontFamily = tr.getFontFamily();
- if (fontFamily == null) {
- fontFamily = "Lucida Sans";
- }
- string.addAttribute(TextAttribute.FAMILY, fontFamily);
-
- Double fs = tr.getFontSize();
- if (fs == null) {
- fs = 12d;
- }
- string.addAttribute(TextAttribute.SIZE, fs.floatValue());
-
- TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));
- double wspace = l.getAdvance();
-
- Double tabSz = paragraph.getDefaultTabSize();
- if (tabSz == null) {
- tabSz = wspace*4;
- }
-
- int numSpaces = (int)Math.ceil(tabSz / wspace);
- StringBuilder buf = new StringBuilder();
- for(int i = 0; i < numSpaces; i++) {
- buf.append(' ');
- }
- return buf.toString();
- }
-
-
- /**
- * Returns wrapping width to break lines in this paragraph
- *
- * @param firstLine whether the first line is breaking
- *
- * @return wrapping width in points
- */
- protected double getWrappingWidth(boolean firstLine, Graphics2D graphics){
- TextShape,?> ts = paragraph.getParentShape();
-
- // internal margins for the text box
- Insets2D insets = ts.getInsets();
- double leftInset = insets.left;
- double rightInset = insets.right;
-
- int indentLevel = paragraph.getIndentLevel();
- if (indentLevel == -1) {
- // default to 0, if indentLevel is not set
- indentLevel = 0;
- }
- Double leftMargin = paragraph.getLeftMargin();
- if (leftMargin == null) {
- // if the marL attribute is omitted, then a value of 347663 is implied
- leftMargin = Units.toPoints(347663L*(indentLevel+1));
- }
- Double indent = paragraph.getIndent();
- if (indent == null) {
- indent = Units.toPoints(347663L*indentLevel);
- }
- Double rightMargin = paragraph.getRightMargin();
- if (rightMargin == null) {
- rightMargin = 0d;
- }
-
- Rectangle2D anchor = DrawShape.getAnchor(graphics, ts);
- TextDirection textDir = ts.getTextDirection();
- double width;
- if (!ts.getWordWrap()) {
- Dimension pageDim = ts.getSheet().getSlideShow().getPageSize();
- // if wordWrap == false then we return the advance to the (right) border of the sheet
- switch (textDir) {
- default:
- width = pageDim.getWidth() - anchor.getX();
- break;
- case VERTICAL:
- width = pageDim.getHeight() - anchor.getX();
- break;
- case VERTICAL_270:
- width = anchor.getX();
- break;
- }
- } else {
- switch (textDir) {
- default:
- width = anchor.getWidth() - leftInset - rightInset - leftMargin - rightMargin;
- break;
- case VERTICAL:
- case VERTICAL_270:
- width = anchor.getHeight() - leftInset - rightInset - leftMargin - rightMargin;
- break;
- }
- if (firstLine && !isHSLF()) {
- if (bullet != null){
- if (indent > 0) {
- width -= indent;
- }
- } else {
- if (indent > 0) {
- width -= indent; // first line indentation
- } else if (indent < 0) { // hanging indentation: the first line start at the left margin
- width += leftMargin;
- }
- }
- }
- }
-
- return width;
- }
-
- private static class AttributedStringData {
- Attribute attribute;
- Object value;
- int beginIndex, endIndex;
- AttributedStringData(Attribute attribute, Object value, int beginIndex, int endIndex) {
- this.attribute = attribute;
- this.value = value;
- this.beginIndex = beginIndex;
- this.endIndex = endIndex;
- }
- }
-
- /**
- * Helper method for paint style relative to bounds, e.g. gradient paint
- */
- @SuppressWarnings("rawtypes")
- private PlaceableShape,?> getParagraphShape() {
- return new PlaceableShape(){
- @Override
- public ShapeContainer,?> getParent() { return null; }
- @Override
- public Rectangle2D getAnchor() { return paragraph.getParentShape().getAnchor(); }
- @Override
- public void setAnchor(Rectangle2D anchor) {}
- @Override
- public double getRotation() { return 0; }
- @Override
- public void setRotation(double theta) {}
- @Override
- public void setFlipHorizontal(boolean flip) {}
- @Override
- public void setFlipVertical(boolean flip) {}
- @Override
- public boolean getFlipHorizontal() { return false; }
- @Override
- public boolean getFlipVertical() { return false; }
- @Override
- public Sheet,?> getSheet() { return paragraph.getParentShape().getSheet(); }
- };
- }
-
- protected AttributedString getAttributedString(Graphics2D graphics, StringBuilder text){
- List attList = new ArrayList();
- if (text == null) {
- text = new StringBuilder();
- }
-
- PlaceableShape,?> ps = getParagraphShape();
- DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);
- @SuppressWarnings("unchecked")
- Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP);
- @SuppressWarnings("unchecked")
- Map fallbackMap = (Map)graphics.getRenderingHint(Drawable.FONT_FALLBACK);
-
- for (TextRun run : paragraph){
- String runText = getRenderableText(graphics, run);
- // skip empty runs
- if (runText.isEmpty()) {
- continue;
- }
-
- // user can pass an custom object to convert fonts
- String mappedFont = run.getFontFamily();
- String fallbackFont = Font.SANS_SERIF;
-
- if (mappedFont == null) {
- mappedFont = paragraph.getDefaultFontFamily();
- }
- if (mappedFont == null) {
- mappedFont = Font.SANS_SERIF;
- }
- if (fontHandler != null) {
- String font = fontHandler.getRendererableFont(mappedFont, run.getPitchAndFamily());
- if (font != null) {
- mappedFont = font;
- }
- font = fontHandler.getFallbackFont(mappedFont, run.getPitchAndFamily());
- if (font != null) {
- fallbackFont = font;
- }
- } else {
- if (fontMap != null) {
- if (fontMap.containsKey(mappedFont)) {
- mappedFont = fontMap.get(mappedFont);
- } else if (fontMap.containsKey("*")) {
- mappedFont = fontMap.get("*");
- }
- }
- if (fallbackMap != null) {
- if (fallbackMap.containsKey(mappedFont)) {
- fallbackFont = fallbackMap.get(mappedFont);
- } else if (fallbackMap.containsKey("*")) {
- fallbackFont = fallbackMap.get("*");
- }
- }
- }
-
- runText = mapFontCharset(runText,mappedFont);
- int beginIndex = text.length();
- text.append(runText);
- int endIndex = text.length();
-
- attList.add(new AttributedStringData(TextAttribute.FAMILY, mappedFont, beginIndex, endIndex));
-
- PaintStyle fgPaintStyle = run.getFontColor();
- Paint fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);
- attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex));
-
- Double fontSz = run.getFontSize();
- if (fontSz == null) {
- fontSz = paragraph.getDefaultFontSize();
- }
- attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));
-
- if(run.isBold()) {
- attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex));
- }
- if(run.isItalic()) {
- attList.add(new AttributedStringData(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, beginIndex, endIndex));
- }
- if(run.isUnderlined()) {
- attList.add(new AttributedStringData(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, beginIndex, endIndex));
- attList.add(new AttributedStringData(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, beginIndex, endIndex));
- }
- if(run.isStrikethrough()) {
- attList.add(new AttributedStringData(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, beginIndex, endIndex));
- }
- if(run.isSubscript()) {
- attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, beginIndex, endIndex));
- }
- if(run.isSuperscript()) {
- attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
- }
-
- Hyperlink,?> hl = run.getHyperlink();
- if (hl != null) {
- attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));
- attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));
- }
-
- int style = (run.isBold() ? Font.BOLD : 0) | (run.isItalic() ? Font.ITALIC : 0);
- Font f = new Font(mappedFont, style, (int)Math.rint(fontSz));
-
- // check for unsupported characters and add a fallback font for these
- char textChr[] = runText.toCharArray();
- int nextEnd = f.canDisplayUpTo(textChr, 0, textChr.length);
- int last = nextEnd;
- boolean isNextValid = (nextEnd == 0);
- while ( nextEnd != -1 && nextEnd <= textChr.length ) {
- if (isNextValid) {
- nextEnd = f.canDisplayUpTo(textChr, nextEnd, textChr.length);
- isNextValid = false;
- } else {
- if (nextEnd >= textChr.length || f.canDisplay(Character.codePointAt(textChr, nextEnd, textChr.length)) ) {
- attList.add(new AttributedStringData(TextAttribute.FAMILY, fallbackFont, beginIndex+last, beginIndex+Math.min(nextEnd,textChr.length)));
- if (nextEnd >= textChr.length) {
- break;
- }
- last = nextEnd;
- isNextValid = true;
- } else {
- boolean isHS = Character.isHighSurrogate(textChr[nextEnd]);
- nextEnd+=(isHS?2:1);
- }
- }
- }
- }
-
- // ensure that the paragraph contains at least one character
- // We need this trick to correctly measure text
- if (text.length() == 0) {
- Double fontSz = paragraph.getDefaultFontSize();
- text.append(" ");
- attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), 0, 1));
- }
-
- AttributedString string = new AttributedString(text.toString());
- for (AttributedStringData asd : attList) {
- string.addAttribute(asd.attribute, asd.value, asd.beginIndex, asd.endIndex);
- }
-
- return string;
- }
-
- /**
- * @return {@code true} if the HSLF implementation is used
- */
- protected boolean isHSLF() {
- return DrawShape.isHSLF(paragraph.getParentShape());
- }
-
- /**
- * Map text charset depending on font family.
- * Currently this only maps for wingdings font (into unicode private use area)
- *
- * @param text the raw text
- * @param fontFamily the font family
- * @return AttributedString with mapped codepoints
- *
- * @see Drawing exotic fonts in a java applet
- * @see StringUtil#mapMsCodepointString(String)
- */
- protected String mapFontCharset(String text, String fontFamily) {
- // TODO: find a real charset mapping solution instead of hard coding for Wingdings
- String attStr = text;
- if ("Wingdings".equalsIgnoreCase(fontFamily)) {
- // wingdings doesn't contain high-surrogates, so chars are ok
- boolean changed = false;
- char chrs[] = attStr.toCharArray();
- for (int i=0; i paragraph;
+ double x, y;
+ protected List lines = new ArrayList();
+ protected String rawText;
+ protected DrawTextFragment bullet;
+ protected int autoNbrIdx;
+
+ /**
+ * the highest line in this paragraph. Used for line spacing.
+ */
+ protected double maxLineHeight;
+
+ /**
+ * Defines an attribute used for storing the hyperlink associated with
+ * some renderable text.
+ */
+ private static class XlinkAttribute extends Attribute {
+
+ XlinkAttribute(String name) {
+ super(name);
+ }
+
+ /**
+ * Resolves instances being deserialized to the predefined constants.
+ */
+ @Override
+ protected Object readResolve() throws InvalidObjectException {
+ if (HYPERLINK_HREF.getName().equals(getName())) {
+ return HYPERLINK_HREF;
+ }
+ if (HYPERLINK_LABEL.getName().equals(getName())) {
+ return HYPERLINK_LABEL;
+ }
+ throw new InvalidObjectException("unknown attribute name");
+ }
+ }
+
+
+ public DrawTextParagraph(TextParagraph,?,?> paragraph) {
+ this.paragraph = paragraph;
+ }
+
+ public void setPosition(double x, double y) {
+ // TODO: replace it, by applyTransform????
+ this.x = x;
+ this.y = y;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ /**
+ * Sets the auto numbering index of the handled paragraph
+ * @param index the auto numbering index
+ */
+ public void setAutoNumberingIdx(int index) {
+ autoNbrIdx = index;
+ }
+
+ @Override
+ public void draw(Graphics2D graphics){
+ if (lines.isEmpty()) {
+ return;
+ }
+
+ double penY = y;
+
+ boolean firstLine = true;
+ int indentLevel = paragraph.getIndentLevel();
+ Double leftMargin = paragraph.getLeftMargin();
+ if (leftMargin == null) {
+ // if the marL attribute is omitted, then a value of 347663 is implied
+ leftMargin = Units.toPoints(347663L*indentLevel);
+ }
+ Double indent = paragraph.getIndent();
+ if (indent == null) {
+ indent = Units.toPoints(347663L*indentLevel);
+ }
+ if (isHSLF()) {
+ // special handling for HSLF
+ indent -= leftMargin;
+ }
+
+// Double rightMargin = paragraph.getRightMargin();
+// if (rightMargin == null) {
+// rightMargin = 0d;
+// }
+
+ //The vertical line spacing
+ Double spacing = paragraph.getLineSpacing();
+ if (spacing == null) {
+ spacing = 100d;
+ }
+
+ for(DrawTextFragment line : lines){
+ double penX;
+
+ if(firstLine) {
+ if (!isEmptyParagraph()) {
+ // TODO: find out character style for empty, but bulleted/numbered lines
+ bullet = getBullet(graphics, line.getAttributedString().getIterator());
+ }
+
+ if (bullet != null){
+ bullet.setPosition(x+leftMargin+indent, penY);
+ bullet.draw(graphics);
+ // don't let text overlay the bullet and advance by the bullet width
+ double bulletWidth = bullet.getLayout().getAdvance() + 1;
+ penX = x + Math.max(leftMargin, leftMargin+indent+bulletWidth);
+ } else {
+ penX = x + leftMargin;
+ }
+ } else {
+ penX = x + leftMargin;
+ }
+
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
+ // Insets are already applied on DrawTextShape.drawContent
+ // but (outer) anchor need to be adjusted
+ Insets2D insets = paragraph.getParentShape().getInsets();
+ double leftInset = insets.left;
+ double rightInset = insets.right;
+
+ TextAlign ta = paragraph.getTextAlign();
+ if (ta == null) {
+ ta = TextAlign.LEFT;
+ }
+ switch (ta) {
+ case CENTER:
+ penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset - leftMargin) / 2;
+ break;
+ case RIGHT:
+ penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset);
+ break;
+ default:
+ break;
+ }
+
+ line.setPosition(penX, penY);
+ line.draw(graphics);
+
+ if(spacing > 0) {
+ // If linespacing >= 0, then linespacing is a percentage of normal line height.
+ penY += spacing*0.01* line.getHeight();
+ } else {
+ // negative value means absolute spacing in points
+ penY += -spacing;
+ }
+
+ firstLine = false;
+ }
+
+ y = penY - y;
+ }
+
+ public float getFirstLineHeight() {
+ return (lines.isEmpty()) ? 0 : lines.get(0).getHeight();
+ }
+
+ public float getLastLineHeight() {
+ return (lines.isEmpty()) ? 0 : lines.get(lines.size()-1).getHeight();
+ }
+
+ public boolean isEmptyParagraph() {
+ return (lines.isEmpty() || rawText.trim().isEmpty());
+ }
+
+ @Override
+ public void applyTransform(Graphics2D graphics) {
+ }
+
+ @Override
+ public void drawContent(Graphics2D graphics) {
+ }
+
+ /**
+ * break text into lines, each representing a line of text that fits in the wrapping width
+ *
+ * @param graphics The drawing context for computing text-lengths.
+ */
+ protected void breakText(Graphics2D graphics){
+ lines.clear();
+
+ DrawFactory fact = DrawFactory.getInstance(graphics);
+ StringBuilder text = new StringBuilder();
+ AttributedString at = getAttributedString(graphics, text);
+ boolean emptyParagraph = ("".equals(text.toString().trim()));
+
+ AttributedCharacterIterator it = at.getIterator();
+ LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
+ for (;;) {
+ int startIndex = measurer.getPosition();
+
+ double wrappingWidth = getWrappingWidth(lines.size() == 0, graphics) + 1; // add a pixel to compensate rounding errors
+ // shape width can be smaller that the sum of insets (this was proved by a test file)
+ if(wrappingWidth < 0) {
+ wrappingWidth = 1;
+ }
+
+ int nextBreak = text.indexOf("\n", startIndex + 1);
+ if (nextBreak == -1) {
+ nextBreak = it.getEndIndex();
+ }
+
+ TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true);
+ if (layout == null) {
+ // layout can be null if the entire word at the current position
+ // does not fit within the wrapping width. Try with requireNextWord=false.
+ layout = measurer.nextLayout((float)wrappingWidth, nextBreak, false);
+ }
+
+ if(layout == null) {
+ // exit if can't break any more
+ break;
+ }
+
+ int endIndex = measurer.getPosition();
+ // skip over new line breaks (we paint 'clear' text runs not starting or ending with \n)
+ if(endIndex < it.getEndIndex() && text.charAt(endIndex) == '\n'){
+ measurer.setPosition(endIndex + 1);
+ }
+
+ TextAlign hAlign = paragraph.getTextAlign();
+ if(hAlign == TextAlign.JUSTIFY || hAlign == TextAlign.JUSTIFY_LOW) {
+ layout = layout.getJustifiedLayout((float)wrappingWidth);
+ }
+
+ AttributedString str = (emptyParagraph)
+ ? null // we will not paint empty paragraphs
+ : new AttributedString(it, startIndex, endIndex);
+ DrawTextFragment line = fact.getTextFragment(layout, str);
+ lines.add(line);
+
+ maxLineHeight = Math.max(maxLineHeight, line.getHeight());
+
+ if(endIndex == it.getEndIndex()) {
+ break;
+ }
+ }
+
+ rawText = text.toString();
+ }
+
+ protected DrawTextFragment getBullet(Graphics2D graphics, AttributedCharacterIterator firstLineAttr) {
+ BulletStyle bulletStyle = paragraph.getBulletStyle();
+ if (bulletStyle == null) {
+ return null;
+ }
+
+ String buCharacter;
+ AutoNumberingScheme ans = bulletStyle.getAutoNumberingScheme();
+ if (ans != null) {
+ buCharacter = ans.format(autoNbrIdx);
+ } else {
+ buCharacter = bulletStyle.getBulletCharacter();
+ }
+ if (buCharacter == null) {
+ return null;
+ }
+
+ String buFont = bulletStyle.getBulletFont();
+ if (buFont == null) {
+ buFont = paragraph.getDefaultFontFamily();
+ }
+ assert(buFont != null);
+
+ PlaceableShape,?> ps = getParagraphShape();
+ PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor();
+ Paint fgPaint;
+ if (fgPaintStyle == null) {
+ fgPaint = (Paint)firstLineAttr.getAttribute(TextAttribute.FOREGROUND);
+ } else {
+ fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);
+ }
+
+ float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);
+ Double buSz = bulletStyle.getBulletFontSize();
+ if (buSz == null) {
+ buSz = 100d;
+ }
+ if (buSz > 0) {
+ fontSize *= buSz* 0.01;
+ } else {
+ fontSize = (float)-buSz;
+ }
+
+
+ AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));
+ str.addAttribute(TextAttribute.FOREGROUND, fgPaint);
+ str.addAttribute(TextAttribute.FAMILY, buFont);
+ str.addAttribute(TextAttribute.SIZE, fontSize);
+
+ TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
+ DrawFactory fact = DrawFactory.getInstance(graphics);
+ return fact.getTextFragment(layout, str);
+ }
+
+ protected String getRenderableText(Graphics2D graphics, TextRun tr) {
+ if (tr.getFieldType() == FieldType.SLIDE_NUMBER) {
+ Slide,?> slide = (Slide,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
+ return (slide == null) ? "" : Integer.toString(slide.getSlideNumber());
+ }
+ StringBuilder buf = new StringBuilder();
+ TextCap cap = tr.getTextCap();
+ String tabs = null;
+ for (char c : tr.getRawText().toCharArray()) {
+ switch (c) {
+ case '\t':
+ if (tabs == null) {
+ tabs = tab2space(tr);
+ }
+ buf.append(tabs);
+ break;
+ case '\u000b':
+ buf.append('\n');
+ break;
+ default:
+ switch (cap) {
+ case ALL: c = Character.toUpperCase(c); break;
+ case SMALL: c = Character.toLowerCase(c); break;
+ case NONE: break;
+ }
+
+ buf.append(c);
+ break;
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Replace a tab with the effective number of white spaces.
+ */
+ private String tab2space(TextRun tr) {
+ AttributedString string = new AttributedString(" ");
+ String fontFamily = tr.getFontFamily();
+ if (fontFamily == null) {
+ fontFamily = "Lucida Sans";
+ }
+ string.addAttribute(TextAttribute.FAMILY, fontFamily);
+
+ Double fs = tr.getFontSize();
+ if (fs == null) {
+ fs = 12d;
+ }
+ string.addAttribute(TextAttribute.SIZE, fs.floatValue());
+
+ TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));
+ double wspace = l.getAdvance();
+
+ Double tabSz = paragraph.getDefaultTabSize();
+ if (tabSz == null) {
+ tabSz = wspace*4;
+ }
+
+ int numSpaces = (int)Math.ceil(tabSz / wspace);
+ StringBuilder buf = new StringBuilder();
+ for(int i = 0; i < numSpaces; i++) {
+ buf.append(' ');
+ }
+ return buf.toString();
+ }
+
+
+ /**
+ * Returns wrapping width to break lines in this paragraph
+ *
+ * @param firstLine whether the first line is breaking
+ *
+ * @return wrapping width in points
+ */
+ protected double getWrappingWidth(boolean firstLine, Graphics2D graphics){
+ TextShape,?> ts = paragraph.getParentShape();
+
+ // internal margins for the text box
+ Insets2D insets = ts.getInsets();
+ double leftInset = insets.left;
+ double rightInset = insets.right;
+
+ int indentLevel = paragraph.getIndentLevel();
+ if (indentLevel == -1) {
+ // default to 0, if indentLevel is not set
+ indentLevel = 0;
+ }
+ Double leftMargin = paragraph.getLeftMargin();
+ if (leftMargin == null) {
+ // if the marL attribute is omitted, then a value of 347663 is implied
+ leftMargin = Units.toPoints(347663L*(indentLevel+1));
+ }
+ Double indent = paragraph.getIndent();
+ if (indent == null) {
+ indent = Units.toPoints(347663L*indentLevel);
+ }
+ Double rightMargin = paragraph.getRightMargin();
+ if (rightMargin == null) {
+ rightMargin = 0d;
+ }
+
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, ts);
+ TextDirection textDir = ts.getTextDirection();
+ double width;
+ if (!ts.getWordWrap()) {
+ Dimension pageDim = ts.getSheet().getSlideShow().getPageSize();
+ // if wordWrap == false then we return the advance to the (right) border of the sheet
+ switch (textDir) {
+ default:
+ width = pageDim.getWidth() - anchor.getX();
+ break;
+ case VERTICAL:
+ width = pageDim.getHeight() - anchor.getX();
+ break;
+ case VERTICAL_270:
+ width = anchor.getX();
+ break;
+ }
+ } else {
+ switch (textDir) {
+ default:
+ width = anchor.getWidth() - leftInset - rightInset - leftMargin - rightMargin;
+ break;
+ case VERTICAL:
+ case VERTICAL_270:
+ width = anchor.getHeight() - leftInset - rightInset - leftMargin - rightMargin;
+ break;
+ }
+ if (firstLine && !isHSLF()) {
+ if (bullet != null){
+ if (indent > 0) {
+ width -= indent;
+ }
+ } else {
+ if (indent > 0) {
+ width -= indent; // first line indentation
+ } else if (indent < 0) { // hanging indentation: the first line start at the left margin
+ width += leftMargin;
+ }
+ }
+ }
+ }
+
+ return width;
+ }
+
+ private static class AttributedStringData {
+ Attribute attribute;
+ Object value;
+ int beginIndex, endIndex;
+ AttributedStringData(Attribute attribute, Object value, int beginIndex, int endIndex) {
+ this.attribute = attribute;
+ this.value = value;
+ this.beginIndex = beginIndex;
+ this.endIndex = endIndex;
+ }
+ }
+
+ /**
+ * Helper method for paint style relative to bounds, e.g. gradient paint
+ */
+ @SuppressWarnings("rawtypes")
+ private PlaceableShape,?> getParagraphShape() {
+ return new PlaceableShape(){
+ @Override
+ public ShapeContainer,?> getParent() { return null; }
+ @Override
+ public Rectangle2D getAnchor() { return paragraph.getParentShape().getAnchor(); }
+ @Override
+ public void setAnchor(Rectangle2D anchor) {}
+ @Override
+ public double getRotation() { return 0; }
+ @Override
+ public void setRotation(double theta) {}
+ @Override
+ public void setFlipHorizontal(boolean flip) {}
+ @Override
+ public void setFlipVertical(boolean flip) {}
+ @Override
+ public boolean getFlipHorizontal() { return false; }
+ @Override
+ public boolean getFlipVertical() { return false; }
+ @Override
+ public Sheet,?> getSheet() { return paragraph.getParentShape().getSheet(); }
+ };
+ }
+
+ protected AttributedString getAttributedString(Graphics2D graphics, StringBuilder text){
+ List attList = new ArrayList();
+ if (text == null) {
+ text = new StringBuilder();
+ }
+
+ PlaceableShape,?> ps = getParagraphShape();
+ DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);
+ @SuppressWarnings("unchecked")
+ Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP);
+ @SuppressWarnings("unchecked")
+ Map fallbackMap = (Map)graphics.getRenderingHint(Drawable.FONT_FALLBACK);
+
+ for (TextRun run : paragraph){
+ String runText = getRenderableText(graphics, run);
+ // skip empty runs
+ if (runText.isEmpty()) {
+ continue;
+ }
+
+ // user can pass an custom object to convert fonts
+ String mappedFont = run.getFontFamily();
+ String fallbackFont = Font.SANS_SERIF;
+
+ if (mappedFont == null) {
+ mappedFont = paragraph.getDefaultFontFamily();
+ }
+ if (mappedFont == null) {
+ mappedFont = Font.SANS_SERIF;
+ }
+ if (fontHandler != null) {
+ String font = fontHandler.getRendererableFont(mappedFont, run.getPitchAndFamily());
+ if (font != null) {
+ mappedFont = font;
+ }
+ font = fontHandler.getFallbackFont(mappedFont, run.getPitchAndFamily());
+ if (font != null) {
+ fallbackFont = font;
+ }
+ } else {
+ if (fontMap != null) {
+ if (fontMap.containsKey(mappedFont)) {
+ mappedFont = fontMap.get(mappedFont);
+ } else if (fontMap.containsKey("*")) {
+ mappedFont = fontMap.get("*");
+ }
+ }
+ if (fallbackMap != null) {
+ if (fallbackMap.containsKey(mappedFont)) {
+ fallbackFont = fallbackMap.get(mappedFont);
+ } else if (fallbackMap.containsKey("*")) {
+ fallbackFont = fallbackMap.get("*");
+ }
+ }
+ }
+
+ runText = mapFontCharset(runText,mappedFont);
+ int beginIndex = text.length();
+ text.append(runText);
+ int endIndex = text.length();
+
+ attList.add(new AttributedStringData(TextAttribute.FAMILY, mappedFont, beginIndex, endIndex));
+
+ PaintStyle fgPaintStyle = run.getFontColor();
+ Paint fgPaint = new DrawPaint(ps).getPaint(graphics, fgPaintStyle);
+ attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgPaint, beginIndex, endIndex));
+
+ Double fontSz = run.getFontSize();
+ if (fontSz == null) {
+ fontSz = paragraph.getDefaultFontSize();
+ }
+ attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));
+
+ if(run.isBold()) {
+ attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex));
+ }
+ if(run.isItalic()) {
+ attList.add(new AttributedStringData(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, beginIndex, endIndex));
+ }
+ if(run.isUnderlined()) {
+ attList.add(new AttributedStringData(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, beginIndex, endIndex));
+ attList.add(new AttributedStringData(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, beginIndex, endIndex));
+ }
+ if(run.isStrikethrough()) {
+ attList.add(new AttributedStringData(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, beginIndex, endIndex));
+ }
+ if(run.isSubscript()) {
+ attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, beginIndex, endIndex));
+ }
+ if(run.isSuperscript()) {
+ attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
+ }
+
+ Hyperlink,?> hl = run.getHyperlink();
+ if (hl != null) {
+ attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));
+ attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));
+ }
+
+ int style = (run.isBold() ? Font.BOLD : 0) | (run.isItalic() ? Font.ITALIC : 0);
+ Font f = new Font(mappedFont, style, (int)Math.rint(fontSz));
+
+ // check for unsupported characters and add a fallback font for these
+ char textChr[] = runText.toCharArray();
+ int nextEnd = f.canDisplayUpTo(textChr, 0, textChr.length);
+ int last = nextEnd;
+ boolean isNextValid = (nextEnd == 0);
+ while ( nextEnd != -1 && nextEnd <= textChr.length ) {
+ if (isNextValid) {
+ nextEnd = f.canDisplayUpTo(textChr, nextEnd, textChr.length);
+ isNextValid = false;
+ } else {
+ if (nextEnd >= textChr.length || f.canDisplay(Character.codePointAt(textChr, nextEnd, textChr.length)) ) {
+ attList.add(new AttributedStringData(TextAttribute.FAMILY, fallbackFont, beginIndex+last, beginIndex+Math.min(nextEnd,textChr.length)));
+ if (nextEnd >= textChr.length) {
+ break;
+ }
+ last = nextEnd;
+ isNextValid = true;
+ } else {
+ boolean isHS = Character.isHighSurrogate(textChr[nextEnd]);
+ nextEnd+=(isHS?2:1);
+ }
+ }
+ }
+ }
+
+ // ensure that the paragraph contains at least one character
+ // We need this trick to correctly measure text
+ if (text.length() == 0) {
+ Double fontSz = paragraph.getDefaultFontSize();
+ text.append(" ");
+ attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), 0, 1));
+ }
+
+ AttributedString string = new AttributedString(text.toString());
+ for (AttributedStringData asd : attList) {
+ string.addAttribute(asd.attribute, asd.value, asd.beginIndex, asd.endIndex);
+ }
+
+ return string;
+ }
+
+ /**
+ * @return {@code true} if the HSLF implementation is used
+ */
+ protected boolean isHSLF() {
+ return DrawShape.isHSLF(paragraph.getParentShape());
+ }
+
+ /**
+ * Map text charset depending on font family.
+ * Currently this only maps for wingdings font (into unicode private use area)
+ *
+ * @param text the raw text
+ * @param fontFamily the font family
+ * @return AttributedString with mapped codepoints
+ *
+ * @see Drawing exotic fonts in a java applet
+ * @see StringUtil#mapMsCodepointString(String)
+ */
+ protected String mapFontCharset(String text, String fontFamily) {
+ // TODO: find a real charset mapping solution instead of hard coding for Wingdings
+ String attStr = text;
+ if ("Wingdings".equalsIgnoreCase(fontFamily)) {
+ // wingdings doesn't contain high-surrogates, so chars are ok
+ boolean changed = false;
+ char chrs[] = attStr.toCharArray();
+ for (int i=0; i shape) {
- super(shape);
- }
-
- @Override
- public void drawContent(Graphics2D graphics) {
- DrawFactory.getInstance(graphics).fixFonts(graphics);
-
- TextShape,?> s = getShape();
-
- Rectangle2D anchor = DrawShape.getAnchor(graphics, s);
- Insets2D insets = s.getInsets();
- double x = anchor.getX() + insets.left;
- double y = anchor.getY();
-
- // remember the initial transform
- AffineTransform tx = graphics.getTransform();
-
- // Transform of text in flipped shapes is special.
- // At this point the flip and rotation transform is already applied
- // (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down".
- // See Bugzilla 54210.
-
- boolean vertFlip = s.getFlipVertical();
- boolean horzFlip = s.getFlipHorizontal();
- ShapeContainer,?> sc = s.getParent();
- while (sc instanceof PlaceableShape) {
- PlaceableShape,?> ps = (PlaceableShape,?>)sc;
- vertFlip ^= ps.getFlipVertical();
- horzFlip ^= ps.getFlipHorizontal();
- sc = ps.getParent();
- }
-
- // Horizontal flipping applies only to shape outline and not to the text in the shape.
- // Applying flip second time restores the original not-flipped transform
- if (horzFlip ^ vertFlip) {
- final double ax = anchor.getX();
- final double ay = anchor.getY();
- graphics.translate(ax + anchor.getWidth(), ay);
- graphics.scale(-1, 1);
- graphics.translate(-ax, -ay);
- }
-
- Double textRot = s.getTextRotation();
- if (textRot != null && textRot != 0) {
- final double cx = anchor.getCenterX();
- final double cy = anchor.getCenterY();
- graphics.translate(cx, cy);
- graphics.rotate(Math.toRadians(textRot));
- graphics.translate(-cx, -cy);
- }
-
- // first dry-run to calculate the total height of the text
- double textHeight;
-
- switch (s.getVerticalAlignment()){
- default:
- case TOP:
- y += insets.top;
- break;
- case BOTTOM:
- textHeight = getTextHeight(graphics);
- y += anchor.getHeight() - textHeight - insets.bottom;
- break;
- case MIDDLE:
- textHeight = getTextHeight(graphics);
- double delta = anchor.getHeight() - textHeight - insets.top - insets.bottom;
- y += insets.top + delta/2;
- break;
- }
-
- TextDirection textDir = s.getTextDirection();
- if (textDir == TextDirection.VERTICAL || textDir == TextDirection.VERTICAL_270) {
- final double deg = (textDir == TextDirection.VERTICAL) ? 90 : 270;
- final double cx = anchor.getCenterX();
- final double cy = anchor.getCenterY();
- graphics.translate(cx, cy);
- graphics.rotate(Math.toRadians(deg));
- graphics.translate(-cx, -cy);
-
- // old top/left edge is now bottom/left or top/right - as we operate on the already
- // rotated drawing context, both verticals can be moved in the same direction
- final double w = anchor.getWidth();
- final double h = anchor.getHeight();
- final double dx = (w-h)/2d;
- graphics.translate(dx,-dx);
- }
-
- drawParagraphs(graphics, x, y);
-
- // restore the transform
- graphics.setTransform(tx);
- }
-
- /**
- * paint the paragraphs starting from top left (x,y)
- *
- * @return the vertical advance, i.e. the cumulative space occupied by the text
- */
- public double drawParagraphs(Graphics2D graphics, double x, double y) {
- DrawFactory fact = DrawFactory.getInstance(graphics);
-
- double y0 = y;
- //noinspection RedundantCast
- @SuppressWarnings("cast")
- Iterator extends TextParagraph,?,? extends TextRun>> paragraphs =
- (Iterator extends TextParagraph,?,? extends TextRun>>) getShape().iterator();
-
- boolean isFirstLine = true;
- for (int autoNbrIdx=0; paragraphs.hasNext(); autoNbrIdx++){
- TextParagraph,?,? extends TextRun> p = paragraphs.next();
- DrawTextParagraph dp = fact.getDrawable(p);
- BulletStyle bs = p.getBulletStyle();
- if (bs == null || bs.getAutoNumberingScheme() == null) {
- autoNbrIdx = -1;
- } else {
- Integer startAt = bs.getAutoNumberingStartAt();
- if (startAt == null) startAt = 1;
- // TODO: handle reset auto number indexes
- if (startAt > autoNbrIdx) autoNbrIdx = startAt;
- }
- dp.setAutoNumberingIdx(autoNbrIdx);
- dp.breakText(graphics);
-
- if (!isFirstLine) {
- // the amount of vertical white space before the paragraph
- Double spaceBefore = p.getSpaceBefore();
- if (spaceBefore == null) spaceBefore = 0d;
- if(spaceBefore > 0) {
- // positive value means percentage spacing of the height of the first line, e.g.
- // the higher the first line, the bigger the space before the paragraph
- y += spaceBefore*0.01*dp.getFirstLineHeight();
- } else {
- // negative value means the absolute spacing in points
- y += -spaceBefore;
- }
- }
- isFirstLine = false;
-
- dp.setPosition(x, y);
- dp.draw(graphics);
- y += dp.getY();
-
- if (paragraphs.hasNext()) {
- Double spaceAfter = p.getSpaceAfter();
- if (spaceAfter == null) spaceAfter = 0d;
- if(spaceAfter > 0) {
- // positive value means percentage spacing of the height of the last line, e.g.
- // the higher the last line, the bigger the space after the paragraph
- y += spaceAfter*0.01*dp.getLastLineHeight();
- } else {
- // negative value means the absolute spacing in points
- y += -spaceAfter;
- }
- }
- }
- return y - y0;
- }
-
- /**
- * Compute the cumulative height occupied by the text
- *
- * @return the height in points
- */
- public double getTextHeight() {
- return getTextHeight(null);
- }
-
- /**
- * Compute the cumulative height occupied by the text
- *
- * @param oldGraphics the graphics context, which properties are to be copied, may be null
- * @return the height in points
- */
- protected double getTextHeight(Graphics2D oldGraphics) {
- // dry-run in a 1x1 image and return the vertical advance
- BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
- Graphics2D graphics = img.createGraphics();
- if (oldGraphics != null) {
- graphics.addRenderingHints(oldGraphics.getRenderingHints());
- graphics.setTransform(oldGraphics.getTransform());
- }
- DrawFactory.getInstance(graphics).fixFonts(graphics);
- return drawParagraphs(graphics, 0, 0);
- }
-
- @Override
- protected TextShape,?> getShape() {
- return (TextShape,?>)shape;
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.Iterator;
+
+import org.apache.poi.sl.usermodel.Insets2D;
+import org.apache.poi.sl.usermodel.PlaceableShape;
+import org.apache.poi.sl.usermodel.ShapeContainer;
+import org.apache.poi.sl.usermodel.TextParagraph;
+import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
+import org.apache.poi.sl.usermodel.TextRun;
+import org.apache.poi.sl.usermodel.TextShape;
+import org.apache.poi.sl.usermodel.TextShape.TextDirection;
+
+public class DrawTextShape extends DrawSimpleShape {
+
+ public DrawTextShape(TextShape,?> shape) {
+ super(shape);
+ }
+
+ @Override
+ public void drawContent(Graphics2D graphics) {
+ DrawFactory.getInstance(graphics).fixFonts(graphics);
+
+ TextShape,?> s = getShape();
+
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, s);
+ Insets2D insets = s.getInsets();
+ double x = anchor.getX() + insets.left;
+ double y = anchor.getY();
+
+ // remember the initial transform
+ AffineTransform tx = graphics.getTransform();
+
+ // Transform of text in flipped shapes is special.
+ // At this point the flip and rotation transform is already applied
+ // (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down".
+ // See Bugzilla 54210.
+
+ boolean vertFlip = s.getFlipVertical();
+ boolean horzFlip = s.getFlipHorizontal();
+ ShapeContainer,?> sc = s.getParent();
+ while (sc instanceof PlaceableShape) {
+ PlaceableShape,?> ps = (PlaceableShape,?>)sc;
+ vertFlip ^= ps.getFlipVertical();
+ horzFlip ^= ps.getFlipHorizontal();
+ sc = ps.getParent();
+ }
+
+ // Horizontal flipping applies only to shape outline and not to the text in the shape.
+ // Applying flip second time restores the original not-flipped transform
+ if (horzFlip ^ vertFlip) {
+ final double ax = anchor.getX();
+ final double ay = anchor.getY();
+ graphics.translate(ax + anchor.getWidth(), ay);
+ graphics.scale(-1, 1);
+ graphics.translate(-ax, -ay);
+ }
+
+ Double textRot = s.getTextRotation();
+ if (textRot != null && textRot != 0) {
+ final double cx = anchor.getCenterX();
+ final double cy = anchor.getCenterY();
+ graphics.translate(cx, cy);
+ graphics.rotate(Math.toRadians(textRot));
+ graphics.translate(-cx, -cy);
+ }
+
+ // first dry-run to calculate the total height of the text
+ double textHeight;
+
+ switch (s.getVerticalAlignment()){
+ default:
+ case TOP:
+ y += insets.top;
+ break;
+ case BOTTOM:
+ textHeight = getTextHeight(graphics);
+ y += anchor.getHeight() - textHeight - insets.bottom;
+ break;
+ case MIDDLE:
+ textHeight = getTextHeight(graphics);
+ double delta = anchor.getHeight() - textHeight - insets.top - insets.bottom;
+ y += insets.top + delta/2;
+ break;
+ }
+
+ TextDirection textDir = s.getTextDirection();
+ if (textDir == TextDirection.VERTICAL || textDir == TextDirection.VERTICAL_270) {
+ final double deg = (textDir == TextDirection.VERTICAL) ? 90 : 270;
+ final double cx = anchor.getCenterX();
+ final double cy = anchor.getCenterY();
+ graphics.translate(cx, cy);
+ graphics.rotate(Math.toRadians(deg));
+ graphics.translate(-cx, -cy);
+
+ // old top/left edge is now bottom/left or top/right - as we operate on the already
+ // rotated drawing context, both verticals can be moved in the same direction
+ final double w = anchor.getWidth();
+ final double h = anchor.getHeight();
+ final double dx = (w-h)/2d;
+ graphics.translate(dx,-dx);
+ }
+
+ drawParagraphs(graphics, x, y);
+
+ // restore the transform
+ graphics.setTransform(tx);
+ }
+
+ /**
+ * paint the paragraphs starting from top left (x,y)
+ *
+ * @return the vertical advance, i.e. the cumulative space occupied by the text
+ */
+ public double drawParagraphs(Graphics2D graphics, double x, double y) {
+ DrawFactory fact = DrawFactory.getInstance(graphics);
+
+ double y0 = y;
+ //noinspection RedundantCast
+ @SuppressWarnings("cast")
+ Iterator extends TextParagraph,?,? extends TextRun>> paragraphs =
+ (Iterator extends TextParagraph,?,? extends TextRun>>) getShape().iterator();
+
+ boolean isFirstLine = true;
+ for (int autoNbrIdx=0; paragraphs.hasNext(); autoNbrIdx++){
+ TextParagraph,?,? extends TextRun> p = paragraphs.next();
+ DrawTextParagraph dp = fact.getDrawable(p);
+ BulletStyle bs = p.getBulletStyle();
+ if (bs == null || bs.getAutoNumberingScheme() == null) {
+ autoNbrIdx = -1;
+ } else {
+ Integer startAt = bs.getAutoNumberingStartAt();
+ if (startAt == null) startAt = 1;
+ // TODO: handle reset auto number indexes
+ if (startAt > autoNbrIdx) autoNbrIdx = startAt;
+ }
+ dp.setAutoNumberingIdx(autoNbrIdx);
+ dp.breakText(graphics);
+
+ if (!isFirstLine) {
+ // the amount of vertical white space before the paragraph
+ Double spaceBefore = p.getSpaceBefore();
+ if (spaceBefore == null) spaceBefore = 0d;
+ if(spaceBefore > 0) {
+ // positive value means percentage spacing of the height of the first line, e.g.
+ // the higher the first line, the bigger the space before the paragraph
+ y += spaceBefore*0.01*dp.getFirstLineHeight();
+ } else {
+ // negative value means the absolute spacing in points
+ y += -spaceBefore;
+ }
+ }
+ isFirstLine = false;
+
+ dp.setPosition(x, y);
+ dp.draw(graphics);
+ y += dp.getY();
+
+ if (paragraphs.hasNext()) {
+ Double spaceAfter = p.getSpaceAfter();
+ if (spaceAfter == null) spaceAfter = 0d;
+ if(spaceAfter > 0) {
+ // positive value means percentage spacing of the height of the last line, e.g.
+ // the higher the last line, the bigger the space after the paragraph
+ y += spaceAfter*0.01*dp.getLastLineHeight();
+ } else {
+ // negative value means the absolute spacing in points
+ y += -spaceAfter;
+ }
+ }
+ }
+ return y - y0;
+ }
+
+ /**
+ * Compute the cumulative height occupied by the text
+ *
+ * @return the height in points
+ */
+ public double getTextHeight() {
+ return getTextHeight(null);
+ }
+
+ /**
+ * Compute the cumulative height occupied by the text
+ *
+ * @param oldGraphics the graphics context, which properties are to be copied, may be null
+ * @return the height in points
+ */
+ protected double getTextHeight(Graphics2D oldGraphics) {
+ // dry-run in a 1x1 image and return the vertical advance
+ BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
+ Graphics2D graphics = img.createGraphics();
+ if (oldGraphics != null) {
+ graphics.addRenderingHints(oldGraphics.getRenderingHints());
+ graphics.setTransform(oldGraphics.getTransform());
+ }
+ DrawFactory.getInstance(graphics).fixFonts(graphics);
+ return drawParagraphs(graphics, 0, 0);
+ }
+
+ @Override
+ protected TextShape,?> getShape() {
+ return (TextShape,?>)shape;
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/Drawable.java b/src/java/org/apache/poi/sl/draw/Drawable.java
index cc85dde905..4ec653ae50 100644
--- a/src/java/org/apache/poi/sl/draw/Drawable.java
+++ b/src/java/org/apache/poi/sl/draw/Drawable.java
@@ -1,161 +1,161 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-import java.awt.Graphics2D;
-import java.awt.RenderingHints;
-
-import org.apache.poi.util.Internal;
-
-
-public interface Drawable {
- class DrawableHint extends RenderingHints.Key {
- protected DrawableHint(int id) {
- super(id);
- }
-
- public boolean isCompatibleValue(Object val) {
- return true;
- }
-
- public String toString() {
- switch (intKey()) {
- case 1: return "DRAW_FACTORY";
- case 2: return "GROUP_TRANSFORM";
- case 3: return "IMAGE_RENDERER";
- case 4: return "TEXT_RENDERING_MODE";
- case 5: return "GRADIENT_SHAPE";
- case 6: return "PRESET_GEOMETRY_CACHE";
- case 7: return "FONT_HANDLER";
- case 8: return "FONT_FALLBACK";
- case 9: return "FONT_MAP";
- case 10: return "GSAVE";
- case 11: return "GRESTORE";
- default: return "UNKNOWN_ID "+intKey();
- }
- }
- }
-
- /**
- * {@link DrawFactory} which will be used to draw objects into this graphics context
- */
- DrawableHint DRAW_FACTORY = new DrawableHint(1);
-
- /**
- * Key will be internally used to store affine transformation temporarily within group shapes
- */
- @Internal
- DrawableHint GROUP_TRANSFORM = new DrawableHint(2);
-
- /**
- * Use a custom image renderer of an instance of {@link ImageRenderer}
- */
- DrawableHint IMAGE_RENDERER = new DrawableHint(3);
-
- /**
- * how to render text:
- *
- * {@link #TEXT_AS_CHARACTERS} (default) means to draw via
- * {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}.
- * This mode draws text as characters. Use it if the target graphics writes the actual
- * character codes instead of glyph outlines (PDFGraphics2D, SVGGraphics2D, etc.)
- *
- * {@link #TEXT_AS_SHAPES} means to render via
- * {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}.
- * This mode draws glyphs as shapes and provides some advanced capabilities such as
- * justification and font substitution. Use it if the target graphics is an image.
- *
- */
- DrawableHint TEXT_RENDERING_MODE = new DrawableHint(4);
-
- /**
- * PathGradientPaint needs the shape to be set.
- * It will be achieved through setting it in the rendering hints
- */
- DrawableHint GRADIENT_SHAPE = new DrawableHint(5);
-
-
- /**
- * Internal key for caching the preset geometries
- */
- DrawableHint PRESET_GEOMETRY_CACHE = new DrawableHint(6);
-
- /**
- * draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}
- */
- int TEXT_AS_CHARACTERS = 1;
-
- /**
- * draw text via {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}
- */
- int TEXT_AS_SHAPES = 2;
-
- /**
- * Use this object to resolve unknown / missing fonts when rendering slides.
- * The font handler must be of type {@link DrawFontManager}.
- *
- * In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored
- */
- DrawableHint FONT_HANDLER = new DrawableHint(7);
-
- /**
- * Key for a font fallback map of type {@code Map} which maps
- * the original font family (key) to the fallback font family (value).
- * In case there is also a {@code FONT_MAP} registered, the original font
- * is first mapped via the font_map and then the fallback font is determined
- */
- DrawableHint FONT_FALLBACK = new DrawableHint(8);
-
- /**
- * Key for a font map of type {@code Map} which maps
- * the original font family (key) to the mapped font family (value)
- */
- DrawableHint FONT_MAP = new DrawableHint(9);
-
- DrawableHint GSAVE = new DrawableHint(10);
- DrawableHint GRESTORE = new DrawableHint(11);
-
- /**
- * The Common SL Draw API works sometimes cascading, but there are places
- * where the current slide context need to be evaluated, e.g. when slide numbers
- * are printed. In this situation we need to have a way to access the current slide
- */
- DrawableHint CURRENT_SLIDE = new DrawableHint(12);
-
-
- /**
- * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
- *
- * @param graphics the graphics whos transform matrix will be modified
- */
- void applyTransform(Graphics2D graphics);
-
- /**
- * Draw this shape into the supplied canvas
- *
- * @param graphics the graphics to draw into
- */
- void draw(Graphics2D graphics);
-
- /**
- * draw any content within this shape (image, text, etc.).
- *
- * @param graphics the graphics to draw into
- */
- void drawContent(Graphics2D graphics);
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+
+import org.apache.poi.util.Internal;
+
+
+public interface Drawable {
+ class DrawableHint extends RenderingHints.Key {
+ protected DrawableHint(int id) {
+ super(id);
+ }
+
+ public boolean isCompatibleValue(Object val) {
+ return true;
+ }
+
+ public String toString() {
+ switch (intKey()) {
+ case 1: return "DRAW_FACTORY";
+ case 2: return "GROUP_TRANSFORM";
+ case 3: return "IMAGE_RENDERER";
+ case 4: return "TEXT_RENDERING_MODE";
+ case 5: return "GRADIENT_SHAPE";
+ case 6: return "PRESET_GEOMETRY_CACHE";
+ case 7: return "FONT_HANDLER";
+ case 8: return "FONT_FALLBACK";
+ case 9: return "FONT_MAP";
+ case 10: return "GSAVE";
+ case 11: return "GRESTORE";
+ default: return "UNKNOWN_ID "+intKey();
+ }
+ }
+ }
+
+ /**
+ * {@link DrawFactory} which will be used to draw objects into this graphics context
+ */
+ DrawableHint DRAW_FACTORY = new DrawableHint(1);
+
+ /**
+ * Key will be internally used to store affine transformation temporarily within group shapes
+ */
+ @Internal
+ DrawableHint GROUP_TRANSFORM = new DrawableHint(2);
+
+ /**
+ * Use a custom image renderer of an instance of {@link ImageRenderer}
+ */
+ DrawableHint IMAGE_RENDERER = new DrawableHint(3);
+
+ /**
+ * how to render text:
+ *
+ * {@link #TEXT_AS_CHARACTERS} (default) means to draw via
+ * {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}.
+ * This mode draws text as characters. Use it if the target graphics writes the actual
+ * character codes instead of glyph outlines (PDFGraphics2D, SVGGraphics2D, etc.)
+ *
+ * {@link #TEXT_AS_SHAPES} means to render via
+ * {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}.
+ * This mode draws glyphs as shapes and provides some advanced capabilities such as
+ * justification and font substitution. Use it if the target graphics is an image.
+ *
+ */
+ DrawableHint TEXT_RENDERING_MODE = new DrawableHint(4);
+
+ /**
+ * PathGradientPaint needs the shape to be set.
+ * It will be achieved through setting it in the rendering hints
+ */
+ DrawableHint GRADIENT_SHAPE = new DrawableHint(5);
+
+
+ /**
+ * Internal key for caching the preset geometries
+ */
+ DrawableHint PRESET_GEOMETRY_CACHE = new DrawableHint(6);
+
+ /**
+ * draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}
+ */
+ int TEXT_AS_CHARACTERS = 1;
+
+ /**
+ * draw text via {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}
+ */
+ int TEXT_AS_SHAPES = 2;
+
+ /**
+ * Use this object to resolve unknown / missing fonts when rendering slides.
+ * The font handler must be of type {@link DrawFontManager}.
+ *
+ * In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored
+ */
+ DrawableHint FONT_HANDLER = new DrawableHint(7);
+
+ /**
+ * Key for a font fallback map of type {@code Map} which maps
+ * the original font family (key) to the fallback font family (value).
+ * In case there is also a {@code FONT_MAP} registered, the original font
+ * is first mapped via the font_map and then the fallback font is determined
+ */
+ DrawableHint FONT_FALLBACK = new DrawableHint(8);
+
+ /**
+ * Key for a font map of type {@code Map} which maps
+ * the original font family (key) to the mapped font family (value)
+ */
+ DrawableHint FONT_MAP = new DrawableHint(9);
+
+ DrawableHint GSAVE = new DrawableHint(10);
+ DrawableHint GRESTORE = new DrawableHint(11);
+
+ /**
+ * The Common SL Draw API works sometimes cascading, but there are places
+ * where the current slide context need to be evaluated, e.g. when slide numbers
+ * are printed. In this situation we need to have a way to access the current slide
+ */
+ DrawableHint CURRENT_SLIDE = new DrawableHint(12);
+
+
+ /**
+ * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
+ *
+ * @param graphics the graphics whos transform matrix will be modified
+ */
+ void applyTransform(Graphics2D graphics);
+
+ /**
+ * Draw this shape into the supplied canvas
+ *
+ * @param graphics the graphics to draw into
+ */
+ void draw(Graphics2D graphics);
+
+ /**
+ * draw any content within this shape (image, text, etc.).
+ *
+ * @param graphics the graphics to draw into
+ */
+ void drawContent(Graphics2D graphics);
+}
diff --git a/src/java/org/apache/poi/sl/draw/ImageRenderer.java b/src/java/org/apache/poi/sl/draw/ImageRenderer.java
index 6b8c49a143..7ecc96a967 100644
--- a/src/java/org/apache/poi/sl/draw/ImageRenderer.java
+++ b/src/java/org/apache/poi/sl/draw/ImageRenderer.java
@@ -1,130 +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.poi.sl.draw;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.Insets;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Classes can implement this interfaces to support other formats, for
- * example, use Apache Batik to render WMF, PICT can be rendered using Apple QuickTime API for Java:
- *
- *
- */
-public interface ImageRenderer {
- /**
- * Load and buffer the image
- *
- * @param data the raw image stream
- * @param contentType the content type
- */
- void loadImage(InputStream data, String contentType) throws IOException;
-
- /**
- * Load and buffer the image
- *
- * @param data the raw image bytes
- * @param contentType the content type
- */
- void loadImage(byte data[], String contentType) throws IOException;
-
- /**
- * @return the dimension of the buffered image
- */
- Dimension getDimension();
-
- /**
- * @param alpha the alpha [0..1] to be added to the image (possibly already containing an alpha channel)
- */
- void setAlpha(double alpha);
-
- /**
- * @return the image as buffered image
- */
- BufferedImage getImage();
-
- /**
- * @param dim the dimension in pixels of the returned image
- * @return the image as buffered image
- *
- * @since POI 3.15-beta2
- */
- BufferedImage getImage(Dimension dim);
-
- /**
- * Render picture data into the supplied graphics
- *
- * @return true if the picture data was successfully rendered
- */
- boolean drawImage(Graphics2D graphics, Rectangle2D anchor);
-
- /**
- * Render picture data into the supplied graphics
- *
- * @return true if the picture data was successfully rendered
- */
- boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip);
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+package org.apache.poi.sl.draw;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Classes can implement this interfaces to support other formats, for
+ * example, use Apache Batik to render WMF, PICT can be rendered using Apple QuickTime API for Java:
+ *
+ *
+ */
+public interface ImageRenderer {
+ /**
+ * Load and buffer the image
+ *
+ * @param data the raw image stream
+ * @param contentType the content type
+ */
+ void loadImage(InputStream data, String contentType) throws IOException;
+
+ /**
+ * Load and buffer the image
+ *
+ * @param data the raw image bytes
+ * @param contentType the content type
+ */
+ void loadImage(byte data[], String contentType) throws IOException;
+
+ /**
+ * @return the dimension of the buffered image
+ */
+ Dimension getDimension();
+
+ /**
+ * @param alpha the alpha [0..1] to be added to the image (possibly already containing an alpha channel)
+ */
+ void setAlpha(double alpha);
+
+ /**
+ * @return the image as buffered image
+ */
+ BufferedImage getImage();
+
+ /**
+ * @param dim the dimension in pixels of the returned image
+ * @return the image as buffered image
+ *
+ * @since POI 3.15-beta2
+ */
+ BufferedImage getImage(Dimension dim);
+
+ /**
+ * Render picture data into the supplied graphics
+ *
+ * @return true if the picture data was successfully rendered
+ */
+ boolean drawImage(Graphics2D graphics, Rectangle2D anchor);
+
+ /**
+ * Render picture data into the supplied graphics
+ *
+ * @return true if the picture data was successfully rendered
+ */
+ boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip);
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java
index 63cae11e9e..d4a2a5fa5f 100644
--- a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java
+++ b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java
@@ -1,188 +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.poi.sl.draw;
-
-import java.awt.*;
-import java.awt.MultipleGradientPaint.ColorSpaceType;
-import java.awt.MultipleGradientPaint.CycleMethod;
-import java.awt.geom.*;
-import java.awt.image.*;
-
-class PathGradientPaint implements Paint {
-
- // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html
- protected final Color colors[];
- protected final float fractions[];
- protected final int capStyle;
- protected final int joinStyle;
- protected final int transparency;
-
-
- public PathGradientPaint(Color colors[], float fractions[]) {
- this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
- }
-
- public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) {
- this.colors = colors.clone();
- this.fractions = fractions.clone();
- this.capStyle = capStyle;
- this.joinStyle = joinStyle;
-
- // determine transparency
- boolean opaque = true;
- for (Color c : colors) {
- if (c != null) {
- opaque = opaque && (c.getAlpha() == 0xff);
- }
- }
- this.transparency = opaque ? OPAQUE : TRANSLUCENT;
- }
-
- public PaintContext createContext(ColorModel cm,
- Rectangle deviceBounds,
- Rectangle2D userBounds,
- AffineTransform transform,
- RenderingHints hints) {
- return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints);
- }
-
- public int getTransparency() {
- return transparency;
- }
-
- class PathGradientContext implements PaintContext {
- protected final Rectangle deviceBounds;
- protected final Rectangle2D userBounds;
- protected final AffineTransform xform;
- protected final RenderingHints hints;
-
- /**
- * for POI: the shape will be only known when the subclasses determines the concrete implementation
- * in the draw/-content method, so we need to postpone the setting/creation as long as possible
- **/
- protected final Shape shape;
- protected final PaintContext pCtx;
- protected final int gradientSteps;
- WritableRaster raster;
-
- public PathGradientContext(
- ColorModel cm
- , Rectangle deviceBounds
- , Rectangle2D userBounds
- , AffineTransform xform
- , RenderingHints hints
- ) {
- shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
- if (shape == null) {
- throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint Drawable.GRADIANT_SHAPE.");
- }
-
- this.deviceBounds = deviceBounds;
- this.userBounds = userBounds;
- this.xform = xform;
- this.hints = hints;
-
- gradientSteps = getGradientSteps(shape);
-
- Point2D start = new Point2D.Double(0, 0);
- Point2D end = new Point2D.Double(gradientSteps, 0);
- LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform());
-
- Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1);
- pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints);
- }
-
- public void dispose() {}
-
- public ColorModel getColorModel() {
- return pCtx.getColorModel();
- }
-
- public Raster getRaster(int xOffset, int yOffset, int w, int h) {
- ColorModel cm = getColorModel();
- if (raster == null) createRaster();
-
- // TODO: eventually use caching here
- WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h);
- Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h);
- if (!childRect.intersects(deviceBounds)) {
- // usually doesn't happen ...
- return childRaster;
- }
-
- Rectangle2D destRect = new Rectangle2D.Double();
- Rectangle2D.intersect(childRect, deviceBounds, destRect);
- int dx = (int)(destRect.getX()-deviceBounds.getX());
- int dy = (int)(destRect.getY()-deviceBounds.getY());
- int dw = (int)destRect.getWidth();
- int dh = (int)destRect.getHeight();
- Object data = raster.getDataElements(dx, dy, dw, dh, null);
- dx = (int)(destRect.getX()-childRect.getX());
- dy = (int)(destRect.getY()-childRect.getY());
- childRaster.setDataElements(dx, dy, dw, dh, data);
-
- return childRaster;
- }
-
- protected int getGradientSteps(Shape gradientShape) {
- Rectangle rect = gradientShape.getBounds();
- int lower = 1;
- int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
- while (lower < upper-1) {
- int mid = lower + (upper - lower) / 2;
- BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle);
- Area area = new Area(bs.createStrokedShape(gradientShape));
- if (area.isSingular()) {
- upper = mid;
- } else {
- lower = mid;
- }
- }
- return upper;
- }
-
-
-
- protected void createRaster() {
- ColorModel cm = getColorModel();
- raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight());
- BufferedImage img = new BufferedImage(cm, raster, false, null);
- Graphics2D graphics = img.createGraphics();
- graphics.setRenderingHints(hints);
- graphics.translate(-deviceBounds.getX(), -deviceBounds.getY());
- graphics.transform(xform);
-
- Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1);
- int rgb[] = new int[cm.getNumComponents()];
-
- for (int i = gradientSteps-1; i>=0; i--) {
- img2.getPixel(i, 0, rgb);
- Color c = new Color(rgb[0],rgb[1],rgb[2]);
- if (rgb.length == 4) {
- // it doesn't work to use just a color with transparency ...
- graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f));
- }
- graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle));
- graphics.setColor(c);
- graphics.draw(shape);
- }
-
- graphics.dispose();
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+import java.awt.*;
+import java.awt.MultipleGradientPaint.ColorSpaceType;
+import java.awt.MultipleGradientPaint.CycleMethod;
+import java.awt.geom.*;
+import java.awt.image.*;
+
+class PathGradientPaint implements Paint {
+
+ // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html
+ protected final Color colors[];
+ protected final float fractions[];
+ protected final int capStyle;
+ protected final int joinStyle;
+ protected final int transparency;
+
+
+ public PathGradientPaint(Color colors[], float fractions[]) {
+ this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
+ }
+
+ public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) {
+ this.colors = colors.clone();
+ this.fractions = fractions.clone();
+ this.capStyle = capStyle;
+ this.joinStyle = joinStyle;
+
+ // determine transparency
+ boolean opaque = true;
+ for (Color c : colors) {
+ if (c != null) {
+ opaque = opaque && (c.getAlpha() == 0xff);
+ }
+ }
+ this.transparency = opaque ? OPAQUE : TRANSLUCENT;
+ }
+
+ public PaintContext createContext(ColorModel cm,
+ Rectangle deviceBounds,
+ Rectangle2D userBounds,
+ AffineTransform transform,
+ RenderingHints hints) {
+ return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints);
+ }
+
+ public int getTransparency() {
+ return transparency;
+ }
+
+ class PathGradientContext implements PaintContext {
+ protected final Rectangle deviceBounds;
+ protected final Rectangle2D userBounds;
+ protected final AffineTransform xform;
+ protected final RenderingHints hints;
+
+ /**
+ * for POI: the shape will be only known when the subclasses determines the concrete implementation
+ * in the draw/-content method, so we need to postpone the setting/creation as long as possible
+ **/
+ protected final Shape shape;
+ protected final PaintContext pCtx;
+ protected final int gradientSteps;
+ WritableRaster raster;
+
+ public PathGradientContext(
+ ColorModel cm
+ , Rectangle deviceBounds
+ , Rectangle2D userBounds
+ , AffineTransform xform
+ , RenderingHints hints
+ ) {
+ shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
+ if (shape == null) {
+ throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint Drawable.GRADIANT_SHAPE.");
+ }
+
+ this.deviceBounds = deviceBounds;
+ this.userBounds = userBounds;
+ this.xform = xform;
+ this.hints = hints;
+
+ gradientSteps = getGradientSteps(shape);
+
+ Point2D start = new Point2D.Double(0, 0);
+ Point2D end = new Point2D.Double(gradientSteps, 0);
+ LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform());
+
+ Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1);
+ pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints);
+ }
+
+ public void dispose() {}
+
+ public ColorModel getColorModel() {
+ return pCtx.getColorModel();
+ }
+
+ public Raster getRaster(int xOffset, int yOffset, int w, int h) {
+ ColorModel cm = getColorModel();
+ if (raster == null) createRaster();
+
+ // TODO: eventually use caching here
+ WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h);
+ Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h);
+ if (!childRect.intersects(deviceBounds)) {
+ // usually doesn't happen ...
+ return childRaster;
+ }
+
+ Rectangle2D destRect = new Rectangle2D.Double();
+ Rectangle2D.intersect(childRect, deviceBounds, destRect);
+ int dx = (int)(destRect.getX()-deviceBounds.getX());
+ int dy = (int)(destRect.getY()-deviceBounds.getY());
+ int dw = (int)destRect.getWidth();
+ int dh = (int)destRect.getHeight();
+ Object data = raster.getDataElements(dx, dy, dw, dh, null);
+ dx = (int)(destRect.getX()-childRect.getX());
+ dy = (int)(destRect.getY()-childRect.getY());
+ childRaster.setDataElements(dx, dy, dw, dh, data);
+
+ return childRaster;
+ }
+
+ protected int getGradientSteps(Shape gradientShape) {
+ Rectangle rect = gradientShape.getBounds();
+ int lower = 1;
+ int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
+ while (lower < upper-1) {
+ int mid = lower + (upper - lower) / 2;
+ BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle);
+ Area area = new Area(bs.createStrokedShape(gradientShape));
+ if (area.isSingular()) {
+ upper = mid;
+ } else {
+ lower = mid;
+ }
+ }
+ return upper;
+ }
+
+
+
+ protected void createRaster() {
+ ColorModel cm = getColorModel();
+ raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight());
+ BufferedImage img = new BufferedImage(cm, raster, false, null);
+ Graphics2D graphics = img.createGraphics();
+ graphics.setRenderingHints(hints);
+ graphics.translate(-deviceBounds.getX(), -deviceBounds.getY());
+ graphics.transform(xform);
+
+ Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1);
+ int rgb[] = new int[cm.getNumComponents()];
+
+ for (int i = gradientSteps-1; i>=0; i--) {
+ img2.getPixel(i, 0, rgb);
+ Color c = new Color(rgb[0],rgb[1],rgb[2]);
+ if (rgb.length == 4) {
+ // it doesn't work to use just a color with transparency ...
+ graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f));
+ }
+ graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle));
+ graphics.setColor(c);
+ graphics.draw(shape);
+ }
+
+ graphics.dispose();
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/SLGraphics.java b/src/java/org/apache/poi/sl/draw/SLGraphics.java
index 47dba62884..c9b70cfba0 100644
--- a/src/java/org/apache/poi/sl/draw/SLGraphics.java
+++ b/src/java/org/apache/poi/sl/draw/SLGraphics.java
@@ -1,1846 +1,1846 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw;
-
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.GraphicsConfiguration;
-import java.awt.GraphicsEnvironment;
-import java.awt.Image;
-import java.awt.Paint;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
-import java.awt.font.GlyphVector;
-import java.awt.font.TextLayout;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.Line2D;
-import java.awt.geom.Path2D;
-import java.awt.geom.RoundRectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ImageObserver;
-import java.awt.image.RenderedImage;
-import java.awt.image.renderable.RenderableImage;
-import java.text.AttributedCharacterIterator;
-import java.util.Map;
-
-import org.apache.poi.sl.usermodel.FreeformShape;
-import org.apache.poi.sl.usermodel.GroupShape;
-import org.apache.poi.sl.usermodel.Insets2D;
-import org.apache.poi.sl.usermodel.SimpleShape;
-import org.apache.poi.sl.usermodel.StrokeStyle;
-import org.apache.poi.sl.usermodel.TextBox;
-import org.apache.poi.sl.usermodel.TextRun;
-import org.apache.poi.sl.usermodel.VerticalAlignment;
-import org.apache.poi.util.NotImplemented;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.util.SuppressForbidden;
-
-/**
- * Translates Graphics2D calls into PowerPoint.
- *
- * @author Yegor Kozlov
- */
-public final class SLGraphics extends Graphics2D implements Cloneable {
-
- protected POILogger log = POILogFactory.getLogger(this.getClass());
-
- //The ppt object to write into.
- private GroupShape,?> _group;
-
- private AffineTransform _transform;
- private Stroke _stroke;
- private Paint _paint;
- private Font _font;
- private Color _foreground;
- private Color _background;
- private RenderingHints _hints;
-
- /**
- * Construct Java Graphics object which translates graphic calls in ppt drawing layer.
- *
- * @param group The shape group to write the graphics calls into.
- */
- public SLGraphics(GroupShape,?> group){
- this._group = group;
-
- _transform = new AffineTransform();
- _stroke = new BasicStroke();
- _paint = Color.black;
- _font = new Font("Arial", Font.PLAIN, 12);
- _background = Color.black;
- _foreground = Color.white;
- _hints = new RenderingHints(null);
- }
-
- /**
- * @return the shape group being used for drawing
- */
- public GroupShape,?> getShapeGroup(){
- return _group;
- }
-
- /**
- * Gets the current font.
- * @return this graphics context's current font.
- * @see java.awt.Font
- * @see java.awt.Graphics#setFont(Font)
- */
- public Font getFont(){
- return _font;
- }
-
- /**
- * Sets this graphics context's font to the specified font.
- * All subsequent text operations using this graphics context
- * use this font.
- * @param font the font.
- * @see java.awt.Graphics#getFont
- * @see java.awt.Graphics#drawString(java.lang.String, int, int)
- * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int)
- * @see java.awt.Graphics#drawChars(char[], int, int, int, int)
- */
- public void setFont(Font font){
- this._font = font;
- }
-
- /**
- * Gets this graphics context's current color.
- * @return this graphics context's current color.
- * @see java.awt.Color
- * @see java.awt.Graphics#setColor
- */
- public Color getColor(){
- return _foreground;
- }
-
- /**
- * Sets this graphics context's current color to the specified
- * color. All subsequent graphics operations using this graphics
- * context use this specified color.
- * @param c the new rendering color.
- * @see java.awt.Color
- * @see java.awt.Graphics#getColor
- */
- public void setColor(Color c) {
- setPaint(c);
- }
-
- /**
- * Returns the current Stroke in the
- * Graphics2D context.
- * @return the current Graphics2DStroke,
- * which defines the line style.
- * @see #setStroke
- */
- public Stroke getStroke(){
- return _stroke;
- }
-
- /**
- * Sets the Stroke for the Graphics2D context.
- * @param s the Stroke object to be used to stroke a
- * Shape during the rendering process
- */
- public void setStroke(Stroke s){
- this._stroke = s;
- }
-
- /**
- * Returns the current Paint of the
- * Graphics2D context.
- * @return the current Graphics2DPaint,
- * which defines a color or pattern.
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- */
- public Paint getPaint(){
- return _paint;
- }
-
- /**
- * Sets the Paint attribute for the
- * Graphics2D context. Calling this method
- * with a nullPaint object does
- * not have any effect on the current Paint attribute
- * of this Graphics2D.
- * @param paint the Paint object to be used to generate
- * color during the rendering process, or null
- * @see java.awt.Graphics#setColor
- */
- public void setPaint(Paint paint){
- if(paint == null) return;
-
- this._paint = paint;
- if (paint instanceof Color) _foreground = (Color)paint;
- }
-
- /**
- * Returns a copy of the current Transform in the
- * Graphics2D context.
- * @return the current AffineTransform in the
- * Graphics2D context.
- * @see #_transform
- * @see #setTransform
- */
- public AffineTransform getTransform(){
- return new AffineTransform(_transform);
- }
-
- /**
- * Sets the Transform in the Graphics2D
- * context.
- * @param Tx the AffineTransform object to be used in the
- * rendering process
- * @see #_transform
- * @see AffineTransform
- */
- public void setTransform(AffineTransform Tx) {
- _transform = new AffineTransform(Tx);
- }
-
- /**
- * Strokes the outline of a Shape using the settings of the
- * current Graphics2D context. The rendering attributes
- * applied include the Clip, Transform,
- * Paint, Composite and
- * Stroke attributes.
- * @param shape the Shape to be rendered
- * @see #setStroke
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #_transform
- * @see #setTransform
- * @see #clip
- * @see #setClip
- * @see #setComposite
- */
- public void draw(Shape shape){
- Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
- FreeformShape,?> p = _group.createFreeform();
- p.setPath(path);
- p.setFillColor(null);
- applyStroke(p);
- if (_paint instanceof Color) {
- p.setStrokeStyle((Color)_paint);
- }
- }
-
- /**
- * Renders the text specified by the specified String,
- * using the current text attribute state in the Graphics2D context.
- * The baseline of the first character is at position
- * (x, y) in the User Space.
- * The rendering attributes applied include the Clip,
- * Transform, Paint, Font and
- * Composite attributes. For characters in script systems
- * such as Hebrew and Arabic, the glyphs can be rendered from right to
- * left, in which case the coordinate supplied is the location of the
- * leftmost character on the baseline.
- * @param s the String to be rendered
- * @param x the x coordinate of the location where the
- * String should be rendered
- * @param y the y coordinate of the location where the
- * String should be rendered
- * @throws NullPointerException if str is
- * null
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see java.awt.Graphics#setFont
- * @see #setTransform
- * @see #setComposite
- * @see #setClip
- */
- public void drawString(String s, float x, float y) {
- TextBox,?> txt = _group.createTextBox();
-
- TextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0);
- rt.setFontSize((double)_font.getSize());
- rt.setFontFamily(_font.getFamily());
-
- if (getColor() != null) rt.setFontColor(DrawPaint.createSolidPaint(getColor()));
- if (_font.isBold()) rt.setBold(true);
- if (_font.isItalic()) rt.setItalic(true);
-
- txt.setText(s);
-
- txt.setInsets(new Insets2D(0,0,0,0));
- txt.setWordWrap(false);
- txt.setHorizontalCentered(false);
- txt.setVerticalAlignment(VerticalAlignment.MIDDLE);
-
-
- TextLayout layout = new TextLayout(s, _font, getFontRenderContext());
- float ascent = layout.getAscent();
-
- float width = (float) Math.floor(layout.getAdvance());
- /**
- * Even if top and bottom margins are set to 0 PowerPoint
- * always sets extra space between the text and its bounding box.
- *
- * The approximation height = ascent*2 works good enough in most cases
- */
- float height = ascent * 2;
-
- /*
- In powerpoint anchor of a shape is its top left corner.
- Java graphics sets string coordinates by the baseline of the first character
- so we need to shift up by the height of the textbox
- */
- y -= height / 2 + ascent / 2;
-
- /*
- In powerpoint anchor of a shape is its top left corner.
- Java graphics sets string coordinates by the baseline of the first character
- so we need to shift down by the height of the textbox
- */
- txt.setAnchor(new Rectangle((int)x, (int)y, (int)width, (int)height));
- }
-
- /**
- * Fills the interior of a Shape using the settings of the
- * Graphics2D context. The rendering attributes applied
- * include the Clip, Transform,
- * Paint, and Composite.
- * @param shape the Shape to be filled
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #_transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- */
- public void fill(Shape shape){
- Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
- FreeformShape,?> p = _group.createFreeform();
- p.setPath(path);
- applyPaint(p);
- p.setStrokeStyle(); //Fills must be "No Line"
- }
-
- /**
- * Translates the origin of the graphics context to the point
- * (x, y) in the current coordinate system.
- * Modifies this graphics context so that its new origin corresponds
- * to the point (x, y) in this graphics context's
- * original coordinate system. All coordinates used in subsequent
- * rendering operations on this graphics context will be relative
- * to this new origin.
- * @param x the x coordinate.
- * @param y the y coordinate.
- */
- public void translate(int x, int y){
- _transform.translate(x, y);
- }
-
- /**
- * Intersects the current Clip with the interior of the
- * specified Shape and sets the Clip to the
- * resulting intersection. The specified Shape is
- * transformed with the current Graphics2D
- * Transform before being intersected with the current
- * Clip. This method is used to make the current
- * Clip smaller.
- * To make the Clip larger, use setClip.
- * The user clip modified by this method is independent of the
- * clipping associated with device bounds and visibility. If no clip has
- * previously been set, or if the clip has been cleared using
- * {@link java.awt.Graphics#setClip(Shape) setClip} with a
- * null argument, the specified Shape becomes
- * the new user clip.
- * @param s the Shape to be intersected with the current
- * Clip. If s is null,
- * this method clears the current Clip.
- */
- @NotImplemented
- public void clip(Shape s){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Gets the current clipping area.
- * This method returns the user clip, which is independent of the
- * clipping associated with device bounds and window visibility.
- * If no clip has previously been set, or if the clip has been
- * cleared using setClip(null), this method returns
- * null.
- * @return a Shape object representing the
- * current clipping area, or null if
- * no clip is set.
- * @see java.awt.Graphics#getClipBounds()
- * @see java.awt.Graphics#clipRect
- * @see java.awt.Graphics#setClip(int, int, int, int)
- * @see java.awt.Graphics#setClip(Shape)
- * @since JDK1.1
- */
- @NotImplemented
- public Shape getClip(){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return null;
- }
-
- /**
- * Concatenates the current Graphics2D
- * Transform with a scaling transformation
- * Subsequent rendering is resized according to the specified scaling
- * factors relative to the previous scaling.
- * This is equivalent to calling transform(S), where S is an
- * AffineTransform represented by the following matrix:
- *
- * [ sx 0 0 ]
- * [ 0 sy 0 ]
- * [ 0 0 1 ]
- *
- * @param sx the amount by which X coordinates in subsequent
- * rendering operations are multiplied relative to previous
- * rendering operations.
- * @param sy the amount by which Y coordinates in subsequent
- * rendering operations are multiplied relative to previous
- * rendering operations.
- */
- public void scale(double sx, double sy){
- _transform.scale(sx, sy);
- }
-
- /**
- * Draws an outlined round-cornered rectangle using this graphics
- * context's current color. The left and right edges of the rectangle
- * are at x and x + width,
- * respectively. The top and bottom edges of the rectangle are at
- * y and y + height.
- * @param x the x coordinate of the rectangle to be drawn.
- * @param y the y coordinate of the rectangle to be drawn.
- * @param width the width of the rectangle to be drawn.
- * @param height the height of the rectangle to be drawn.
- * @param arcWidth the horizontal diameter of the arc
- * at the four corners.
- * @param arcHeight the vertical diameter of the arc
- * at the four corners.
- * @see java.awt.Graphics#fillRoundRect
- */
- public void drawRoundRect(int x, int y, int width, int height,
- int arcWidth, int arcHeight){
- RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
- draw(rect);
- }
-
- /**
- * Draws the text given by the specified string, using this
- * graphics context's current font and color. The baseline of the
- * first character is at position (x, y) in this
- * graphics context's coordinate system.
- * @param str the string to be drawn.
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @see java.awt.Graphics#drawBytes
- * @see java.awt.Graphics#drawChars
- */
- public void drawString(String str, int x, int y){
- drawString(str, (float)x, (float)y);
- }
-
- /**
- * Fills an oval bounded by the specified rectangle with the
- * current color.
- * @param x the x coordinate of the upper left corner
- * of the oval to be filled.
- * @param y the y coordinate of the upper left corner
- * of the oval to be filled.
- * @param width the width of the oval to be filled.
- * @param height the height of the oval to be filled.
- * @see java.awt.Graphics#drawOval
- */
- public void fillOval(int x, int y, int width, int height){
- Ellipse2D oval = new Ellipse2D.Double(x, y, width, height);
- fill(oval);
- }
-
- /**
- * Fills the specified rounded corner rectangle with the current color.
- * The left and right edges of the rectangle
- * are at x and x + width - 1,
- * respectively. The top and bottom edges of the rectangle are at
- * y and y + height - 1.
- * @param x the x coordinate of the rectangle to be filled.
- * @param y the y coordinate of the rectangle to be filled.
- * @param width the width of the rectangle to be filled.
- * @param height the height of the rectangle to be filled.
- * @param arcWidth the horizontal diameter
- * of the arc at the four corners.
- * @param arcHeight the vertical diameter
- * of the arc at the four corners.
- * @see java.awt.Graphics#drawRoundRect
- */
- public void fillRoundRect(int x, int y, int width, int height,
- int arcWidth, int arcHeight){
-
- RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
- fill(rect);
- }
-
- /**
- * Fills a circular or elliptical arc covering the specified rectangle.
- *
- * The resulting arc begins at startAngle and extends
- * for arcAngle degrees.
- * Angles are interpreted such that 0 degrees
- * is at the 3 o'clock position.
- * A positive value indicates a counter-clockwise rotation
- * while a negative value indicates a clockwise rotation.
- *
- * The center of the arc is the center of the rectangle whose origin
- * is (x, y) and whose size is specified by the
- * width and height arguments.
- *
- * The resulting arc covers an area
- * width + 1 pixels wide
- * by height + 1 pixels tall.
- *
- * The angles are specified relative to the non-square extents of
- * the bounding rectangle such that 45 degrees always falls on the
- * line from the center of the ellipse to the upper right corner of
- * the bounding rectangle. As a result, if the bounding rectangle is
- * noticeably longer in one axis than the other, the angles to the
- * start and end of the arc segment will be skewed farther along the
- * longer axis of the bounds.
- * @param x the x coordinate of the
- * upper-left corner of the arc to be filled.
- * @param y the y coordinate of the
- * upper-left corner of the arc to be filled.
- * @param width the width of the arc to be filled.
- * @param height the height of the arc to be filled.
- * @param startAngle the beginning angle.
- * @param arcAngle the angular extent of the arc,
- * relative to the start angle.
- * @see java.awt.Graphics#drawArc
- */
- public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle){
- Arc2D arc = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);
- fill(arc);
- }
-
- /**
- * Draws the outline of a circular or elliptical arc
- * covering the specified rectangle.
- *
- * The resulting arc begins at startAngle and extends
- * for arcAngle degrees, using the current color.
- * Angles are interpreted such that 0 degrees
- * is at the 3 o'clock position.
- * A positive value indicates a counter-clockwise rotation
- * while a negative value indicates a clockwise rotation.
- *
- * The center of the arc is the center of the rectangle whose origin
- * is (x, y) and whose size is specified by the
- * width and height arguments.
- *
- * The resulting arc covers an area
- * width + 1 pixels wide
- * by height + 1 pixels tall.
- *
- * The angles are specified relative to the non-square extents of
- * the bounding rectangle such that 45 degrees always falls on the
- * line from the center of the ellipse to the upper right corner of
- * the bounding rectangle. As a result, if the bounding rectangle is
- * noticeably longer in one axis than the other, the angles to the
- * start and end of the arc segment will be skewed farther along the
- * longer axis of the bounds.
- * @param x the x coordinate of the
- * upper-left corner of the arc to be drawn.
- * @param y the y coordinate of the
- * upper-left corner of the arc to be drawn.
- * @param width the width of the arc to be drawn.
- * @param height the height of the arc to be drawn.
- * @param startAngle the beginning angle.
- * @param arcAngle the angular extent of the arc,
- * relative to the start angle.
- * @see java.awt.Graphics#fillArc
- */
- public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
- Arc2D arc = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);
- draw(arc);
- }
-
-
- /**
- * Draws a sequence of connected lines defined by
- * arrays of x and y coordinates.
- * Each pair of (x, y) coordinates defines a point.
- * The figure is not closed if the first point
- * differs from the last point.
- * @param xPoints an array of x points
- * @param yPoints an array of y points
- * @param nPoints the total number of points
- * @see java.awt.Graphics#drawPolygon(int[], int[], int)
- * @since JDK1.1
- */
- public void drawPolyline(int[] xPoints, int[] yPoints,
- int nPoints){
- if(nPoints > 0){
- GeneralPath path = new GeneralPath();
- path.moveTo(xPoints[0], yPoints[0]);
- for(int i=1; ix, y,
- * width, and height arguments.
- *
- * The oval covers an area that is
- * width + 1 pixels wide
- * and height + 1 pixels tall.
- * @param x the x coordinate of the upper left
- * corner of the oval to be drawn.
- * @param y the y coordinate of the upper left
- * corner of the oval to be drawn.
- * @param width the width of the oval to be drawn.
- * @param height the height of the oval to be drawn.
- * @see java.awt.Graphics#fillOval
- */
- public void drawOval(int x, int y, int width, int height){
- Ellipse2D oval = new Ellipse2D.Double(x, y, width, height);
- draw(oval);
- }
-
- /**
- * Draws as much of the specified image as is currently available.
- * The image is drawn with its top-left corner at
- * (x, y) in this graphics context's coordinate
- * space. Transparent pixels are drawn in the specified
- * background color.
- *
- * This operation is equivalent to filling a rectangle of the
- * width and height of the specified image with the given color and then
- * drawing the image on top of it, but possibly more efficient.
- *
- * This method returns immediately in all cases, even if the
- * complete image has not yet been loaded, and it has not been dithered
- * and converted for the current output device.
- *
- * If the image has not yet been completely loaded, then
- * drawImage returns false. As more of
- * the image becomes available, the process that draws the image notifies
- * the specified image observer.
- * @param img the specified image to be drawn.
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @param bgcolor the background color to paint under the
- * non-opaque portions of the image.
- * @param observer object to be notified as more of
- * the image is converted.
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- @NotImplemented
- public boolean drawImage(Image img, int x, int y,
- Color bgcolor,
- ImageObserver observer){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
-
- return false;
- }
-
- /**
- * Draws as much of the specified image as has already been scaled
- * to fit inside the specified rectangle.
- *
- * The image is drawn inside the specified rectangle of this
- * graphics context's coordinate space, and is scaled if
- * necessary. Transparent pixels are drawn in the specified
- * background color.
- * This operation is equivalent to filling a rectangle of the
- * width and height of the specified image with the given color and then
- * drawing the image on top of it, but possibly more efficient.
- *
- * This method returns immediately in all cases, even if the
- * entire image has not yet been scaled, dithered, and converted
- * for the current output device.
- * If the current output representation is not yet complete then
- * drawImage returns false. As more of
- * the image becomes available, the process that draws the image notifies
- * the specified image observer.
- *
- * A scaled version of an image will not necessarily be
- * available immediately just because an unscaled version of the
- * image has been constructed for this output device. Each size of
- * the image may be cached separately and generated from the original
- * data in a separate image production sequence.
- * @param img the specified image to be drawn.
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @param width the width of the rectangle.
- * @param height the height of the rectangle.
- * @param bgcolor the background color to paint under the
- * non-opaque portions of the image.
- * @param observer object to be notified as more of
- * the image is converted.
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- @NotImplemented
- public boolean drawImage(Image img, int x, int y,
- int width, int height,
- Color bgcolor,
- ImageObserver observer){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
-
- return false;
- }
-
-
- /**
- * Draws as much of the specified area of the specified image as is
- * currently available, scaling it on the fly to fit inside the
- * specified area of the destination drawable surface. Transparent pixels
- * do not affect whatever pixels are already there.
- *
- * This method returns immediately in all cases, even if the
- * image area to be drawn has not yet been scaled, dithered, and converted
- * for the current output device.
- * If the current output representation is not yet complete then
- * drawImage returns false. As more of
- * the image becomes available, the process that draws the image notifies
- * the specified image observer.
- *
- * This method always uses the unscaled version of the image
- * to render the scaled rectangle and performs the required
- * scaling on the fly. It does not use a cached, scaled version
- * of the image for this operation. Scaling of the image from source
- * to destination is performed such that the first coordinate
- * of the source rectangle is mapped to the first coordinate of
- * the destination rectangle, and the second source coordinate is
- * mapped to the second destination coordinate. The subimage is
- * scaled and flipped as needed to preserve those mappings.
- * @param img the specified image to be drawn
- * @param dx1 the x coordinate of the first corner of the
- * destination rectangle.
- * @param dy1 the y coordinate of the first corner of the
- * destination rectangle.
- * @param dx2 the x coordinate of the second corner of the
- * destination rectangle.
- * @param dy2 the y coordinate of the second corner of the
- * destination rectangle.
- * @param sx1 the x coordinate of the first corner of the
- * source rectangle.
- * @param sy1 the y coordinate of the first corner of the
- * source rectangle.
- * @param sx2 the x coordinate of the second corner of the
- * source rectangle.
- * @param sy2 the y coordinate of the second corner of the
- * source rectangle.
- * @param observer object to be notified as more of the image is
- * scaled and converted.
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- * @since JDK1.1
- */
- @NotImplemented
- public boolean drawImage(Image img,
- int dx1, int dy1, int dx2, int dy2,
- int sx1, int sy1, int sx2, int sy2,
- ImageObserver observer){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return false;
- }
-
- /**
- * Draws as much of the specified area of the specified image as is
- * currently available, scaling it on the fly to fit inside the
- * specified area of the destination drawable surface.
- *
- * Transparent pixels are drawn in the specified background color.
- * This operation is equivalent to filling a rectangle of the
- * width and height of the specified image with the given color and then
- * drawing the image on top of it, but possibly more efficient.
- *
- * This method returns immediately in all cases, even if the
- * image area to be drawn has not yet been scaled, dithered, and converted
- * for the current output device.
- * If the current output representation is not yet complete then
- * drawImage returns false. As more of
- * the image becomes available, the process that draws the image notifies
- * the specified image observer.
- *
- * This method always uses the unscaled version of the image
- * to render the scaled rectangle and performs the required
- * scaling on the fly. It does not use a cached, scaled version
- * of the image for this operation. Scaling of the image from source
- * to destination is performed such that the first coordinate
- * of the source rectangle is mapped to the first coordinate of
- * the destination rectangle, and the second source coordinate is
- * mapped to the second destination coordinate. The subimage is
- * scaled and flipped as needed to preserve those mappings.
- * @param img the specified image to be drawn
- * @param dx1 the x coordinate of the first corner of the
- * destination rectangle.
- * @param dy1 the y coordinate of the first corner of the
- * destination rectangle.
- * @param dx2 the x coordinate of the second corner of the
- * destination rectangle.
- * @param dy2 the y coordinate of the second corner of the
- * destination rectangle.
- * @param sx1 the x coordinate of the first corner of the
- * source rectangle.
- * @param sy1 the y coordinate of the first corner of the
- * source rectangle.
- * @param sx2 the x coordinate of the second corner of the
- * source rectangle.
- * @param sy2 the y coordinate of the second corner of the
- * source rectangle.
- * @param bgcolor the background color to paint under the
- * non-opaque portions of the image.
- * @param observer object to be notified as more of the image is
- * scaled and converted.
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- * @since JDK1.1
- */
- @NotImplemented
- public boolean drawImage(Image img,
- int dx1, int dy1, int dx2, int dy2,
- int sx1, int sy1, int sx2, int sy2,
- Color bgcolor,
- ImageObserver observer){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return false;
- }
-
- /**
- * Draws as much of the specified image as is currently available.
- * The image is drawn with its top-left corner at
- * (x, y) in this graphics context's coordinate
- * space. Transparent pixels in the image do not affect whatever
- * pixels are already there.
- *
- * This method returns immediately in all cases, even if the
- * complete image has not yet been loaded, and it has not been dithered
- * and converted for the current output device.
- *
- * If the image has completely loaded and its pixels are
- * no longer being changed, then
- * drawImage returns true.
- * Otherwise, drawImage returns false
- * and as more of
- * the image becomes available
- * or it is time to draw another frame of animation,
- * the process that loads the image notifies
- * the specified image observer.
- * @param img the specified image to be drawn. This method does
- * nothing if img is null.
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @param observer object to be notified as more of
- * the image is converted.
- * @return false if the image pixels are still changing;
- * true otherwise.
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- @NotImplemented
- public boolean drawImage(Image img, int x, int y,
- ImageObserver observer) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return false;
- }
-
- /**
- * Disposes of this graphics context and releases
- * any system resources that it is using.
- * A Graphics object cannot be used after
- * disposehas been called.
- *
- * When a Java program runs, a large number of Graphics
- * objects can be created within a short time frame.
- * Although the finalization process of the garbage collector
- * also disposes of the same system resources, it is preferable
- * to manually free the associated resources by calling this
- * method rather than to rely on a finalization process which
- * may not run to completion for a long period of time.
- *
- * Graphics objects which are provided as arguments to the
- * paint and update methods
- * of components are automatically released by the system when
- * those methods return. For efficiency, programmers should
- * call dispose when finished using
- * a Graphics object only if it was created
- * directly from a component or another Graphics object.
- * @see java.awt.Graphics#finalize
- * @see java.awt.Component#paint
- * @see java.awt.Component#update
- * @see java.awt.Component#getGraphics
- * @see java.awt.Graphics#create
- */
- public void dispose() {
- }
-
- /**
- * Draws a line, using the current color, between the points
- * (x1, y1) and (x2, y2)
- * in this graphics context's coordinate system.
- * @param x1 the first point's x coordinate.
- * @param y1 the first point's y coordinate.
- * @param x2 the second point's x coordinate.
- * @param y2 the second point's y coordinate.
- */
- public void drawLine(int x1, int y1, int x2, int y2){
- Line2D line = new Line2D.Double(x1, y1, x2, y2);
- draw(line);
- }
-
- /**
- * Fills a closed polygon defined by
- * arrays of x and y coordinates.
- *
- * This method draws the polygon defined by nPoint line
- * segments, where the first nPoint - 1
- * line segments are line segments from
- * (xPoints[i - 1], yPoints[i - 1])
- * to (xPoints[i], yPoints[i]), for
- * 1 ≤ i ≤ nPoints.
- * The figure is automatically closed by drawing a line connecting
- * the final point to the first point, if those points are different.
- *
- * The area inside the polygon is defined using an
- * even-odd fill rule, also known as the alternating rule.
- * @param xPoints a an array of x coordinates.
- * @param yPoints a an array of y coordinates.
- * @param nPoints a the total number of points.
- * @see java.awt.Graphics#drawPolygon(int[], int[], int)
- */
- public void fillPolygon(int[] xPoints, int[] yPoints,
- int nPoints){
- java.awt.Polygon polygon = new java.awt.Polygon(xPoints, yPoints, nPoints);
- fill(polygon);
- }
-
- /**
- * Fills the specified rectangle.
- * The left and right edges of the rectangle are at
- * x and x + width - 1.
- * The top and bottom edges are at
- * y and y + height - 1.
- * The resulting rectangle covers an area
- * width pixels wide by
- * height pixels tall.
- * The rectangle is filled using the graphics context's current color.
- * @param x the x coordinate
- * of the rectangle to be filled.
- * @param y the y coordinate
- * of the rectangle to be filled.
- * @param width the width of the rectangle to be filled.
- * @param height the height of the rectangle to be filled.
- * @see java.awt.Graphics#clearRect
- * @see java.awt.Graphics#drawRect
- */
- public void fillRect(int x, int y, int width, int height){
- Rectangle rect = new Rectangle(x, y, width, height);
- fill(rect);
- }
-
- /**
- * Draws the outline of the specified rectangle.
- * The left and right edges of the rectangle are at
- * x and x + width.
- * The top and bottom edges are at
- * y and y + height.
- * The rectangle is drawn using the graphics context's current color.
- * @param x the x coordinate
- * of the rectangle to be drawn.
- * @param y the y coordinate
- * of the rectangle to be drawn.
- * @param width the width of the rectangle to be drawn.
- * @param height the height of the rectangle to be drawn.
- * @see java.awt.Graphics#fillRect
- * @see java.awt.Graphics#clearRect
- */
- public void drawRect(int x, int y, int width, int height) {
- Rectangle rect = new Rectangle(x, y, width, height);
- draw(rect);
- }
-
- /**
- * Draws a closed polygon defined by
- * arrays of x and y coordinates.
- * Each pair of (x, y) coordinates defines a point.
- *
- * This method draws the polygon defined by nPoint line
- * segments, where the first nPoint - 1
- * line segments are line segments from
- * (xPoints[i - 1], yPoints[i - 1])
- * to (xPoints[i], yPoints[i]), for
- * 1 ≤ i ≤ nPoints.
- * The figure is automatically closed by drawing a line connecting
- * the final point to the first point, if those points are different.
- * @param xPoints a an array of x coordinates.
- * @param yPoints a an array of y coordinates.
- * @param nPoints a the total number of points.
- * @see java.awt.Graphics#fillPolygon(int[],int[],int)
- * @see java.awt.Graphics#drawPolyline
- */
- public void drawPolygon(int[] xPoints, int[] yPoints,
- int nPoints){
- java.awt.Polygon polygon = new java.awt.Polygon(xPoints, yPoints, nPoints);
- draw(polygon);
- }
-
- /**
- * Intersects the current clip with the specified rectangle.
- * The resulting clipping area is the intersection of the current
- * clipping area and the specified rectangle. If there is no
- * current clipping area, either because the clip has never been
- * set, or the clip has been cleared using setClip(null),
- * the specified rectangle becomes the new clip.
- * This method sets the user clip, which is independent of the
- * clipping associated with device bounds and window visibility.
- * This method can only be used to make the current clip smaller.
- * To set the current clip larger, use any of the setClip methods.
- * Rendering operations have no effect outside of the clipping area.
- * @param x the x coordinate of the rectangle to intersect the clip with
- * @param y the y coordinate of the rectangle to intersect the clip with
- * @param width the width of the rectangle to intersect the clip with
- * @param height the height of the rectangle to intersect the clip with
- * @see #setClip(int, int, int, int)
- * @see #setClip(Shape)
- */
- public void clipRect(int x, int y, int width, int height){
- clip(new Rectangle(x, y, width, height));
- }
-
- /**
- * Sets the current clipping area to an arbitrary clip shape.
- * Not all objects that implement the Shape
- * interface can be used to set the clip. The only
- * Shape objects that are guaranteed to be
- * supported are Shape objects that are
- * obtained via the getClip method and via
- * Rectangle objects. This method sets the
- * user clip, which is independent of the clipping associated
- * with device bounds and window visibility.
- * @param clip the Shape to use to set the clip
- * @see java.awt.Graphics#getClip()
- * @see java.awt.Graphics#clipRect
- * @see java.awt.Graphics#setClip(int, int, int, int)
- * @since JDK1.1
- */
- @NotImplemented
- public void setClip(Shape clip) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Returns the bounding rectangle of the current clipping area.
- * This method refers to the user clip, which is independent of the
- * clipping associated with device bounds and window visibility.
- * If no clip has previously been set, or if the clip has been
- * cleared using setClip(null), this method returns
- * null.
- * The coordinates in the rectangle are relative to the coordinate
- * system origin of this graphics context.
- * @return the bounding rectangle of the current clipping area,
- * or null if no clip is set.
- * @see java.awt.Graphics#getClip
- * @see java.awt.Graphics#clipRect
- * @see java.awt.Graphics#setClip(int, int, int, int)
- * @see java.awt.Graphics#setClip(Shape)
- * @since JDK1.1
- */
- public Rectangle getClipBounds(){
- Shape c = getClip();
- if (c==null) {
- return null;
- }
- return c.getBounds();
- }
-
- /**
- * Draws the text given by the specified iterator, using this
- * graphics context's current color. The iterator has to specify a font
- * for each character. The baseline of the
- * first character is at position (x, y) in this
- * graphics context's coordinate system.
- * @param iterator the iterator whose text is to be drawn
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @see java.awt.Graphics#drawBytes
- * @see java.awt.Graphics#drawChars
- */
- public void drawString(AttributedCharacterIterator iterator,
- int x, int y){
- drawString(iterator, (float)x, (float)y);
- }
-
- /**
- * Clears the specified rectangle by filling it with the background
- * color of the current drawing surface. This operation does not
- * use the current paint mode.
- *
- * Beginning with Java 1.1, the background color
- * of offscreen images may be system dependent. Applications should
- * use setColor followed by fillRect to
- * ensure that an offscreen image is cleared to a specific color.
- * @param x the x coordinate of the rectangle to clear.
- * @param y the y coordinate of the rectangle to clear.
- * @param width the width of the rectangle to clear.
- * @param height the height of the rectangle to clear.
- * @see java.awt.Graphics#fillRect(int, int, int, int)
- * @see java.awt.Graphics#drawRect
- * @see java.awt.Graphics#setColor(java.awt.Color)
- * @see java.awt.Graphics#setPaintMode
- * @see java.awt.Graphics#setXORMode(java.awt.Color)
- */
- public void clearRect(int x, int y, int width, int height) {
- Paint paint = getPaint();
- setColor(getBackground());
- fillRect(x, y, width, height);
- setPaint(paint);
- }
-
- public void copyArea(int x, int y, int width, int height, int dx, int dy) {
- }
-
- /**
- * Sets the current clip to the rectangle specified by the given
- * coordinates. This method sets the user clip, which is
- * independent of the clipping associated with device bounds
- * and window visibility.
- * Rendering operations have no effect outside of the clipping area.
- * @param x the x coordinate of the new clip rectangle.
- * @param y the y coordinate of the new clip rectangle.
- * @param width the width of the new clip rectangle.
- * @param height the height of the new clip rectangle.
- * @see java.awt.Graphics#clipRect
- * @see java.awt.Graphics#setClip(Shape)
- * @since JDK1.1
- */
- public void setClip(int x, int y, int width, int height){
- setClip(new Rectangle(x, y, width, height));
- }
-
- /**
- * Concatenates the current Graphics2D
- * Transform with a rotation transform.
- * Subsequent rendering is rotated by the specified radians relative
- * to the previous origin.
- * This is equivalent to calling transform(R), where R is an
- * AffineTransform represented by the following matrix:
- *
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- */
- public void rotate(double theta){
- _transform.rotate(theta);
- }
-
- /**
- * Concatenates the current Graphics2D
- * Transform with a translated rotation
- * transform. Subsequent rendering is transformed by a transform
- * which is constructed by translating to the specified location,
- * rotating by the specified radians, and translating back by the same
- * amount as the original translation. This is equivalent to the
- * following sequence of calls:
- *
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- * @param x x coordinate of the origin of the rotation
- * @param y y coordinate of the origin of the rotation
- */
- public void rotate(double theta, double x, double y){
- _transform.rotate(theta, x, y);
- }
-
- /**
- * Concatenates the current Graphics2D
- * Transform with a shearing transform.
- * Subsequent renderings are sheared by the specified
- * multiplier relative to the previous position.
- * This is equivalent to calling transform(SH), where SH
- * is an AffineTransform represented by the following
- * matrix:
- *
- * [ 1 shx 0 ]
- * [ shy 1 0 ]
- * [ 0 0 1 ]
- *
- * @param shx the multiplier by which coordinates are shifted in
- * the positive X axis direction as a function of their Y coordinate
- * @param shy the multiplier by which coordinates are shifted in
- * the positive Y axis direction as a function of their X coordinate
- */
- public void shear(double shx, double shy){
- _transform.shear(shx, shy);
- }
-
- /**
- * Get the rendering context of the Font within this
- * Graphics2D context.
- * The {@link FontRenderContext}
- * encapsulates application hints such as anti-aliasing and
- * fractional metrics, as well as target device specific information
- * such as dots-per-inch. This information should be provided by the
- * application when using objects that perform typographical
- * formatting, such as Font and
- * TextLayout. This information should also be provided
- * by applications that perform their own layout and need accurate
- * measurements of various characteristics of glyphs such as advance
- * and line height when various rendering hints have been applied to
- * the text rendering.
- *
- * @return a reference to an instance of FontRenderContext.
- * @see java.awt.font.FontRenderContext
- * @see java.awt.Font#createGlyphVector(FontRenderContext,char[])
- * @see java.awt.font.TextLayout
- * @since JDK1.2
- */
- public FontRenderContext getFontRenderContext() {
- boolean isAntiAliased = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals(
- getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));
- boolean usesFractionalMetrics = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals(
- getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS));
-
-
- return new FontRenderContext(new AffineTransform(), isAntiAliased, usesFractionalMetrics);
- }
-
- /**
- * Composes an AffineTransform object with the
- * Transform in this Graphics2D according
- * to the rule last-specified-first-applied. If the current
- * Transform is Cx, the result of composition
- * with Tx is a new Transform Cx'. Cx' becomes the
- * current Transform for this Graphics2D.
- * Transforming a point p by the updated Transform Cx' is
- * equivalent to first transforming p by Tx and then transforming
- * the result by the original Transform Cx. In other
- * words, Cx'(p) = Cx(Tx(p)). A copy of the Tx is made, if necessary,
- * so further modifications to Tx do not affect rendering.
- * @param Tx the AffineTransform object to be composed with
- * the current Transform
- * @see #setTransform
- * @see AffineTransform
- */
- public void transform(AffineTransform Tx) {
- _transform.concatenate(Tx);
- }
-
- /**
- * Renders a BufferedImage that is
- * filtered with a
- * {@link BufferedImageOp}.
- * The rendering attributes applied include the Clip,
- * Transform
- * and Composite attributes. This is equivalent to:
- *
- * @param img the BufferedImage to be rendered
- * @param op the filter to be applied to the image before rendering
- * @param x the x coordinate in user space where the image is rendered
- * @param y the y coordinate in user space where the image is rendered
- * @see #_transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip(Shape)
- */
- public void drawImage(BufferedImage img,
- BufferedImageOp op,
- int x,
- int y){
- img = op.filter(img, null);
- drawImage(img, x, y, null);
- }
-
- /**
- * Sets the background color for the Graphics2D context.
- * The background color is used for clearing a region.
- * When a Graphics2D is constructed for a
- * Component, the background color is
- * inherited from the Component. Setting the background color
- * in the Graphics2D context only affects the subsequent
- * clearRect calls and not the background color of the
- * Component. To change the background
- * of the Component, use appropriate methods of
- * the Component.
- * @param color the background color that isused in
- * subsequent calls to clearRect
- * @see #getBackground
- * @see java.awt.Graphics#clearRect
- */
- public void setBackground(Color color) {
- if(color == null)
- return;
-
- _background = color;
- }
-
- /**
- * Returns the background color used for clearing a region.
- * @return the current Graphics2DColor,
- * which defines the background color.
- * @see #setBackground
- */
- public Color getBackground(){
- return _background;
- }
-
- /**
- * Sets the Composite for the Graphics2D context.
- * The Composite is used in all drawing methods such as
- * drawImage, drawString, draw,
- * and fill. It specifies how new pixels are to be combined
- * with the existing pixels on the graphics device during the rendering
- * process.
- *
If this Graphics2D context is drawing to a
- * Component on the display screen and the
- * Composite is a custom object rather than an
- * instance of the AlphaComposite class, and if
- * there is a security manager, its checkPermission
- * method is called with an AWTPermission("readDisplayPixels")
- * permission.
- *
- * @param comp the Composite object to be used for rendering
- * @throws SecurityException
- * if a custom Composite object is being
- * used to render to the screen and a security manager
- * is set and its checkPermission method
- * does not allow the operation.
- * @see java.awt.Graphics#setXORMode
- * @see java.awt.Graphics#setPaintMode
- * @see java.awt.AlphaComposite
- */
- @NotImplemented
- public void setComposite(Composite comp){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Returns the current Composite in the
- * Graphics2D context.
- * @return the current Graphics2DComposite,
- * which defines a compositing style.
- * @see #setComposite
- */
- @NotImplemented
- public Composite getComposite(){
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return null;
- }
-
- /**
- * Returns the value of a single preference for the rendering algorithms.
- * Hint categories include controls for rendering quality and overall
- * time/quality trade-off in the rendering process. Refer to the
- * RenderingHints class for definitions of some common
- * keys and values.
- * @param hintKey the key corresponding to the hint to get.
- * @return an object representing the value for the specified hint key.
- * Some of the keys and their associated values are defined in the
- * RenderingHints class.
- * @see RenderingHints
- */
- public Object getRenderingHint(RenderingHints.Key hintKey){
- return _hints.get(hintKey);
- }
-
- /**
- * Sets the value of a single preference for the rendering algorithms.
- * Hint categories include controls for rendering quality and overall
- * time/quality trade-off in the rendering process. Refer to the
- * RenderingHints class for definitions of some common
- * keys and values.
- * @param hintKey the key of the hint to be set.
- * @param hintValue the value indicating preferences for the specified
- * hint category.
- * @see RenderingHints
- */
- public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue){
- _hints.put(hintKey, hintValue);
- }
-
-
- /**
- * Renders the text of the specified
- * {@link GlyphVector} using
- * the Graphics2D context's rendering attributes.
- * The rendering attributes applied include the Clip,
- * Transform, Paint, and
- * Composite attributes. The GlyphVector
- * specifies individual glyphs from a {@link Font}.
- * The GlyphVector can also contain the glyph positions.
- * This is the fastest way to render a set of characters to the
- * screen.
- *
- * @param g the GlyphVector to be rendered
- * @param x the x position in user space where the glyphs should be
- * rendered
- * @param y the y position in user space where the glyphs should be
- * rendered
- *
- * @see java.awt.Font#createGlyphVector(FontRenderContext, char[])
- * @see java.awt.font.GlyphVector
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #setTransform
- * @see #setComposite
- * @see #setClip(Shape)
- */
- public void drawGlyphVector(GlyphVector g, float x, float y) {
- Shape glyphOutline = g.getOutline(x, y);
- fill(glyphOutline);
- }
-
- /**
- * Returns the device configuration associated with this
- * Graphics2D.
- * @return the device configuration
- */
- public GraphicsConfiguration getDeviceConfiguration() {
- return GraphicsEnvironment.getLocalGraphicsEnvironment().
- getDefaultScreenDevice().getDefaultConfiguration();
- }
-
- /**
- * Sets the values of an arbitrary number of preferences for the
- * rendering algorithms.
- * Only values for the rendering hints that are present in the
- * specified Map object are modified.
- * All other preferences not present in the specified
- * object are left unmodified.
- * Hint categories include controls for rendering quality and
- * overall time/quality trade-off in the rendering process.
- * Refer to the RenderingHints class for definitions of
- * some common keys and values.
- * @param hints the rendering hints to be set
- * @see RenderingHints
- */
- public void addRenderingHints(Map,?> hints){
- this._hints.putAll(hints);
- }
-
- /**
- * Concatenates the current
- * Graphics2DTransform
- * with a translation transform.
- * Subsequent rendering is translated by the specified
- * distance relative to the previous position.
- * This is equivalent to calling transform(T), where T is an
- * AffineTransform represented by the following matrix:
- *
- * [ 1 0 tx ]
- * [ 0 1 ty ]
- * [ 0 0 1 ]
- *
- * @param tx the distance to translate along the x-axis
- * @param ty the distance to translate along the y-axis
- */
- public void translate(double tx, double ty){
- _transform.translate(tx, ty);
- }
-
- /**
- * Renders the text of the specified iterator, using the
- * Graphics2D context's current Paint. The
- * iterator must specify a font
- * for each character. The baseline of the
- * first character is at position (x, y) in the
- * User Space.
- * The rendering attributes applied include the Clip,
- * Transform, Paint, and
- * Composite attributes.
- * For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character
- * on the baseline.
- * @param iterator the iterator whose text is to be rendered
- * @param x the x coordinate where the iterator's text is to be
- * rendered
- * @param y the y coordinate where the iterator's text is to be
- * rendered
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #setTransform
- * @see #setComposite
- * @see #setClip
- */
- @NotImplemented
- public void drawString(AttributedCharacterIterator iterator, float x, float y) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Checks whether or not the specified Shape intersects
- * the specified {@link Rectangle}, which is in device
- * space. If onStroke is false, this method checks
- * whether or not the interior of the specified Shape
- * intersects the specified Rectangle. If
- * onStroke is true, this method checks
- * whether or not the Stroke of the specified
- * Shape outline intersects the specified
- * Rectangle.
- * The rendering attributes taken into account include the
- * Clip, Transform, and Stroke
- * attributes.
- * @param rect the area in device space to check for a hit
- * @param s the Shape to check for a hit
- * @param onStroke flag used to choose between testing the
- * stroked or the filled shape. If the flag is true, the
- * Stroke oultine is tested. If the flag is
- * false, the filled Shape is tested.
- * @return true if there is a hit; false
- * otherwise.
- * @see #setStroke
- * @see #fill(Shape)
- * @see #draw(Shape)
- * @see #_transform
- * @see #setTransform
- * @see #clip
- * @see #setClip(Shape)
- */
- public boolean hit(Rectangle rect,
- Shape s,
- boolean onStroke){
- if (onStroke) {
- s = getStroke().createStrokedShape(s);
- }
-
- s = getTransform().createTransformedShape(s);
-
- return s.intersects(rect);
- }
-
- /**
- * Gets the preferences for the rendering algorithms. Hint categories
- * include controls for rendering quality and overall time/quality
- * trade-off in the rendering process.
- * Returns all of the hint key/value pairs that were ever specified in
- * one operation. Refer to the
- * RenderingHints class for definitions of some common
- * keys and values.
- * @return a reference to an instance of RenderingHints
- * that contains the current preferences.
- * @see RenderingHints
- */
- public RenderingHints getRenderingHints(){
- return _hints;
- }
-
- /**
- * Replaces the values of all preferences for the rendering
- * algorithms with the specified hints.
- * The existing values for all rendering hints are discarded and
- * the new set of known hints and values are initialized from the
- * specified {@link Map} object.
- * Hint categories include controls for rendering quality and
- * overall time/quality trade-off in the rendering process.
- * Refer to the RenderingHints class for definitions of
- * some common keys and values.
- * @param hints the rendering hints to be set
- * @see RenderingHints
- */
- public void setRenderingHints(Map,?> hints){
- this._hints = new RenderingHints(null);
- this._hints.putAll(hints);
- }
-
- /**
- * Renders an image, applying a transform from image space into user space
- * before drawing.
- * The transformation from user space into device space is done with
- * the current Transform in the Graphics2D.
- * The specified transformation is applied to the image before the
- * transform attribute in the Graphics2D context is applied.
- * The rendering attributes applied include the Clip,
- * Transform, and Composite attributes.
- * Note that no rendering is done if the specified transform is
- * noninvertible.
- * @param img the Image to be rendered
- * @param xform the transformation from image space into user space
- * @param obs the {@link ImageObserver}
- * to be notified as more of the Image
- * is converted
- * @return true if the Image is
- * fully loaded and completely rendered;
- * false if the Image is still being loaded.
- * @see #_transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip(Shape)
- */
- @NotImplemented
- public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return false;
- }
-
- /**
- * Draws as much of the specified image as has already been scaled
- * to fit inside the specified rectangle.
- *
- * The image is drawn inside the specified rectangle of this
- * graphics context's coordinate space, and is scaled if
- * necessary. Transparent pixels do not affect whatever pixels
- * are already there.
- *
- * This method returns immediately in all cases, even if the
- * entire image has not yet been scaled, dithered, and converted
- * for the current output device.
- * If the current output representation is not yet complete, then
- * drawImage returns false. As more of
- * the image becomes available, the process that loads the image notifies
- * the image observer by calling its imageUpdate method.
- *
- * A scaled version of an image will not necessarily be
- * available immediately just because an unscaled version of the
- * image has been constructed for this output device. Each size of
- * the image may be cached separately and generated from the original
- * data in a separate image production sequence.
- * @param img the specified image to be drawn. This method does
- * nothing if img is null.
- * @param x the x coordinate.
- * @param y the y coordinate.
- * @param width the width of the rectangle.
- * @param height the height of the rectangle.
- * @param observer object to be notified as more of
- * the image is converted.
- * @return false if the image pixels are still changing;
- * true otherwise.
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- @NotImplemented
- public boolean drawImage(Image img, int x, int y,
- int width, int height,
- ImageObserver observer) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- return false;
- }
-
- /**
- * Creates a new Graphics object that is
- * a copy of this Graphics object.
- * @return a new graphics context that is a copy of
- * this graphics context.
- */
- public Graphics create() {
- try {
- return (Graphics)clone();
- } catch (CloneNotSupportedException e){
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Gets the font metrics for the specified font.
- * @return the font metrics for the specified font.
- * @param f the specified font
- * @see java.awt.Graphics#getFont
- * @see java.awt.FontMetrics
- * @see java.awt.Graphics#getFontMetrics()
- */
- @SuppressWarnings("deprecation")
- @SuppressForbidden
- public FontMetrics getFontMetrics(Font f) {
- return Toolkit.getDefaultToolkit().getFontMetrics(f);
- }
-
- /**
- * Sets the paint mode of this graphics context to alternate between
- * this graphics context's current color and the new specified color.
- * This specifies that logical pixel operations are performed in the
- * XOR mode, which alternates pixels between the current color and
- * a specified XOR color.
- *
- * When drawing operations are performed, pixels which are the
- * current color are changed to the specified color, and vice versa.
- *
- * Pixels that are of colors other than those two colors are changed
- * in an unpredictable but reversible manner; if the same figure is
- * drawn twice, then all pixels are restored to their original values.
- * @param c1 the XOR alternation color
- */
- @NotImplemented
- public void setXORMode(Color c1) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Sets the paint mode of this graphics context to overwrite the
- * destination with this graphics context's current color.
- * This sets the logical pixel operation function to the paint or
- * overwrite mode. All subsequent rendering operations will
- * overwrite the destination with the current color.
- */
- @NotImplemented
- public void setPaintMode() {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Renders a
- * {@link RenderableImage},
- * applying a transform from image space into user space before drawing.
- * The transformation from user space into device space is done with
- * the current Transform in the Graphics2D.
- * The specified transformation is applied to the image before the
- * transform attribute in the Graphics2D context is applied.
- * The rendering attributes applied include the Clip,
- * Transform, and Composite attributes. Note
- * that no rendering is done if the specified transform is
- * noninvertible.
- *
- * Rendering hints set on the Graphics2D object might
- * be used in rendering the RenderableImage.
- * If explicit control is required over specific hints recognized by a
- * specific RenderableImage, or if knowledge of which hints
- * are used is required, then a RenderedImage should be
- * obtained directly from the RenderableImage
- * and rendered using
- *{@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}.
- * @param img the image to be rendered. This method does
- * nothing if img is null.
- * @param xform the transformation from image space into user space
- * @see #_transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- * @see #drawRenderedImage
- */
- @NotImplemented
- public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- /**
- * Renders a {@link RenderedImage},
- * applying a transform from image
- * space into user space before drawing.
- * The transformation from user space into device space is done with
- * the current Transform in the Graphics2D.
- * The specified transformation is applied to the image before the
- * transform attribute in the Graphics2D context is applied.
- * The rendering attributes applied include the Clip,
- * Transform, and Composite attributes. Note
- * that no rendering is done if the specified transform is
- * noninvertible.
- * @param img the image to be rendered. This method does
- * nothing if img is null.
- * @param xform the transformation from image space into user space
- * @see #_transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- */
- @NotImplemented
- public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
- if (log.check(POILogger.WARN)) {
- log.log(POILogger.WARN, "Not implemented");
- }
- }
-
- protected void applyStroke(SimpleShape,?> shape) {
- if (_stroke instanceof BasicStroke){
- BasicStroke bs = (BasicStroke)_stroke;
- shape.setStrokeStyle((double)bs.getLineWidth());
- float[] dash = bs.getDashArray();
- if (dash != null) {
- //TODO: implement more dashing styles
- shape.setStrokeStyle(StrokeStyle.LineDash.DASH);
- }
- }
- }
-
- protected void applyPaint(SimpleShape,?> shape) {
- if (_paint instanceof Color) {
- shape.setFillColor((Color)_paint);
- }
- }
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw;
+
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+import org.apache.poi.sl.usermodel.FreeformShape;
+import org.apache.poi.sl.usermodel.GroupShape;
+import org.apache.poi.sl.usermodel.Insets2D;
+import org.apache.poi.sl.usermodel.SimpleShape;
+import org.apache.poi.sl.usermodel.StrokeStyle;
+import org.apache.poi.sl.usermodel.TextBox;
+import org.apache.poi.sl.usermodel.TextRun;
+import org.apache.poi.sl.usermodel.VerticalAlignment;
+import org.apache.poi.util.NotImplemented;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.SuppressForbidden;
+
+/**
+ * Translates Graphics2D calls into PowerPoint.
+ *
+ * @author Yegor Kozlov
+ */
+public final class SLGraphics extends Graphics2D implements Cloneable {
+
+ protected POILogger log = POILogFactory.getLogger(this.getClass());
+
+ //The ppt object to write into.
+ private GroupShape,?> _group;
+
+ private AffineTransform _transform;
+ private Stroke _stroke;
+ private Paint _paint;
+ private Font _font;
+ private Color _foreground;
+ private Color _background;
+ private RenderingHints _hints;
+
+ /**
+ * Construct Java Graphics object which translates graphic calls in ppt drawing layer.
+ *
+ * @param group The shape group to write the graphics calls into.
+ */
+ public SLGraphics(GroupShape,?> group){
+ this._group = group;
+
+ _transform = new AffineTransform();
+ _stroke = new BasicStroke();
+ _paint = Color.black;
+ _font = new Font("Arial", Font.PLAIN, 12);
+ _background = Color.black;
+ _foreground = Color.white;
+ _hints = new RenderingHints(null);
+ }
+
+ /**
+ * @return the shape group being used for drawing
+ */
+ public GroupShape,?> getShapeGroup(){
+ return _group;
+ }
+
+ /**
+ * Gets the current font.
+ * @return this graphics context's current font.
+ * @see java.awt.Font
+ * @see java.awt.Graphics#setFont(Font)
+ */
+ public Font getFont(){
+ return _font;
+ }
+
+ /**
+ * Sets this graphics context's font to the specified font.
+ * All subsequent text operations using this graphics context
+ * use this font.
+ * @param font the font.
+ * @see java.awt.Graphics#getFont
+ * @see java.awt.Graphics#drawString(java.lang.String, int, int)
+ * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int)
+ * @see java.awt.Graphics#drawChars(char[], int, int, int, int)
+ */
+ public void setFont(Font font){
+ this._font = font;
+ }
+
+ /**
+ * Gets this graphics context's current color.
+ * @return this graphics context's current color.
+ * @see java.awt.Color
+ * @see java.awt.Graphics#setColor
+ */
+ public Color getColor(){
+ return _foreground;
+ }
+
+ /**
+ * Sets this graphics context's current color to the specified
+ * color. All subsequent graphics operations using this graphics
+ * context use this specified color.
+ * @param c the new rendering color.
+ * @see java.awt.Color
+ * @see java.awt.Graphics#getColor
+ */
+ public void setColor(Color c) {
+ setPaint(c);
+ }
+
+ /**
+ * Returns the current Stroke in the
+ * Graphics2D context.
+ * @return the current Graphics2DStroke,
+ * which defines the line style.
+ * @see #setStroke
+ */
+ public Stroke getStroke(){
+ return _stroke;
+ }
+
+ /**
+ * Sets the Stroke for the Graphics2D context.
+ * @param s the Stroke object to be used to stroke a
+ * Shape during the rendering process
+ */
+ public void setStroke(Stroke s){
+ this._stroke = s;
+ }
+
+ /**
+ * Returns the current Paint of the
+ * Graphics2D context.
+ * @return the current Graphics2DPaint,
+ * which defines a color or pattern.
+ * @see #setPaint
+ * @see java.awt.Graphics#setColor
+ */
+ public Paint getPaint(){
+ return _paint;
+ }
+
+ /**
+ * Sets the Paint attribute for the
+ * Graphics2D context. Calling this method
+ * with a nullPaint object does
+ * not have any effect on the current Paint attribute
+ * of this Graphics2D.
+ * @param paint the Paint object to be used to generate
+ * color during the rendering process, or null
+ * @see java.awt.Graphics#setColor
+ */
+ public void setPaint(Paint paint){
+ if(paint == null) return;
+
+ this._paint = paint;
+ if (paint instanceof Color) _foreground = (Color)paint;
+ }
+
+ /**
+ * Returns a copy of the current Transform in the
+ * Graphics2D context.
+ * @return the current AffineTransform in the
+ * Graphics2D context.
+ * @see #_transform
+ * @see #setTransform
+ */
+ public AffineTransform getTransform(){
+ return new AffineTransform(_transform);
+ }
+
+ /**
+ * Sets the Transform in the Graphics2D
+ * context.
+ * @param Tx the AffineTransform object to be used in the
+ * rendering process
+ * @see #_transform
+ * @see AffineTransform
+ */
+ public void setTransform(AffineTransform Tx) {
+ _transform = new AffineTransform(Tx);
+ }
+
+ /**
+ * Strokes the outline of a Shape using the settings of the
+ * current Graphics2D context. The rendering attributes
+ * applied include the Clip, Transform,
+ * Paint, Composite and
+ * Stroke attributes.
+ * @param shape the Shape to be rendered
+ * @see #setStroke
+ * @see #setPaint
+ * @see java.awt.Graphics#setColor
+ * @see #_transform
+ * @see #setTransform
+ * @see #clip
+ * @see #setClip
+ * @see #setComposite
+ */
+ public void draw(Shape shape){
+ Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
+ FreeformShape,?> p = _group.createFreeform();
+ p.setPath(path);
+ p.setFillColor(null);
+ applyStroke(p);
+ if (_paint instanceof Color) {
+ p.setStrokeStyle((Color)_paint);
+ }
+ }
+
+ /**
+ * Renders the text specified by the specified String,
+ * using the current text attribute state in the Graphics2D context.
+ * The baseline of the first character is at position
+ * (x, y) in the User Space.
+ * The rendering attributes applied include the Clip,
+ * Transform, Paint, Font and
+ * Composite attributes. For characters in script systems
+ * such as Hebrew and Arabic, the glyphs can be rendered from right to
+ * left, in which case the coordinate supplied is the location of the
+ * leftmost character on the baseline.
+ * @param s the String to be rendered
+ * @param x the x coordinate of the location where the
+ * String should be rendered
+ * @param y the y coordinate of the location where the
+ * String should be rendered
+ * @throws NullPointerException if str is
+ * null
+ * @see #setPaint
+ * @see java.awt.Graphics#setColor
+ * @see java.awt.Graphics#setFont
+ * @see #setTransform
+ * @see #setComposite
+ * @see #setClip
+ */
+ public void drawString(String s, float x, float y) {
+ TextBox,?> txt = _group.createTextBox();
+
+ TextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0);
+ rt.setFontSize((double)_font.getSize());
+ rt.setFontFamily(_font.getFamily());
+
+ if (getColor() != null) rt.setFontColor(DrawPaint.createSolidPaint(getColor()));
+ if (_font.isBold()) rt.setBold(true);
+ if (_font.isItalic()) rt.setItalic(true);
+
+ txt.setText(s);
+
+ txt.setInsets(new Insets2D(0,0,0,0));
+ txt.setWordWrap(false);
+ txt.setHorizontalCentered(false);
+ txt.setVerticalAlignment(VerticalAlignment.MIDDLE);
+
+
+ TextLayout layout = new TextLayout(s, _font, getFontRenderContext());
+ float ascent = layout.getAscent();
+
+ float width = (float) Math.floor(layout.getAdvance());
+ /**
+ * Even if top and bottom margins are set to 0 PowerPoint
+ * always sets extra space between the text and its bounding box.
+ *
+ * The approximation height = ascent*2 works good enough in most cases
+ */
+ float height = ascent * 2;
+
+ /*
+ In powerpoint anchor of a shape is its top left corner.
+ Java graphics sets string coordinates by the baseline of the first character
+ so we need to shift up by the height of the textbox
+ */
+ y -= height / 2 + ascent / 2;
+
+ /*
+ In powerpoint anchor of a shape is its top left corner.
+ Java graphics sets string coordinates by the baseline of the first character
+ so we need to shift down by the height of the textbox
+ */
+ txt.setAnchor(new Rectangle((int)x, (int)y, (int)width, (int)height));
+ }
+
+ /**
+ * Fills the interior of a Shape using the settings of the
+ * Graphics2D context. The rendering attributes applied
+ * include the Clip, Transform,
+ * Paint, and Composite.
+ * @param shape the Shape to be filled
+ * @see #setPaint
+ * @see java.awt.Graphics#setColor
+ * @see #_transform
+ * @see #setTransform
+ * @see #setComposite
+ * @see #clip
+ * @see #setClip
+ */
+ public void fill(Shape shape){
+ Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
+ FreeformShape,?> p = _group.createFreeform();
+ p.setPath(path);
+ applyPaint(p);
+ p.setStrokeStyle(); //Fills must be "No Line"
+ }
+
+ /**
+ * Translates the origin of the graphics context to the point
+ * (x, y) in the current coordinate system.
+ * Modifies this graphics context so that its new origin corresponds
+ * to the point (x, y) in this graphics context's
+ * original coordinate system. All coordinates used in subsequent
+ * rendering operations on this graphics context will be relative
+ * to this new origin.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void translate(int x, int y){
+ _transform.translate(x, y);
+ }
+
+ /**
+ * Intersects the current Clip with the interior of the
+ * specified Shape and sets the Clip to the
+ * resulting intersection. The specified Shape is
+ * transformed with the current Graphics2D
+ * Transform before being intersected with the current
+ * Clip. This method is used to make the current
+ * Clip smaller.
+ * To make the Clip larger, use setClip.
+ * The user clip modified by this method is independent of the
+ * clipping associated with device bounds and visibility. If no clip has
+ * previously been set, or if the clip has been cleared using
+ * {@link java.awt.Graphics#setClip(Shape) setClip} with a
+ * null argument, the specified Shape becomes
+ * the new user clip.
+ * @param s the Shape to be intersected with the current
+ * Clip. If s is null,
+ * this method clears the current Clip.
+ */
+ @NotImplemented
+ public void clip(Shape s){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Gets the current clipping area.
+ * This method returns the user clip, which is independent of the
+ * clipping associated with device bounds and window visibility.
+ * If no clip has previously been set, or if the clip has been
+ * cleared using setClip(null), this method returns
+ * null.
+ * @return a Shape object representing the
+ * current clipping area, or null if
+ * no clip is set.
+ * @see java.awt.Graphics#getClipBounds()
+ * @see java.awt.Graphics#clipRect
+ * @see java.awt.Graphics#setClip(int, int, int, int)
+ * @see java.awt.Graphics#setClip(Shape)
+ * @since JDK1.1
+ */
+ @NotImplemented
+ public Shape getClip(){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return null;
+ }
+
+ /**
+ * Concatenates the current Graphics2D
+ * Transform with a scaling transformation
+ * Subsequent rendering is resized according to the specified scaling
+ * factors relative to the previous scaling.
+ * This is equivalent to calling transform(S), where S is an
+ * AffineTransform represented by the following matrix:
+ *
+ * [ sx 0 0 ]
+ * [ 0 sy 0 ]
+ * [ 0 0 1 ]
+ *
+ * @param sx the amount by which X coordinates in subsequent
+ * rendering operations are multiplied relative to previous
+ * rendering operations.
+ * @param sy the amount by which Y coordinates in subsequent
+ * rendering operations are multiplied relative to previous
+ * rendering operations.
+ */
+ public void scale(double sx, double sy){
+ _transform.scale(sx, sy);
+ }
+
+ /**
+ * Draws an outlined round-cornered rectangle using this graphics
+ * context's current color. The left and right edges of the rectangle
+ * are at x and x + width,
+ * respectively. The top and bottom edges of the rectangle are at
+ * y and y + height.
+ * @param x the x coordinate of the rectangle to be drawn.
+ * @param y the y coordinate of the rectangle to be drawn.
+ * @param width the width of the rectangle to be drawn.
+ * @param height the height of the rectangle to be drawn.
+ * @param arcWidth the horizontal diameter of the arc
+ * at the four corners.
+ * @param arcHeight the vertical diameter of the arc
+ * at the four corners.
+ * @see java.awt.Graphics#fillRoundRect
+ */
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight){
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
+ draw(rect);
+ }
+
+ /**
+ * Draws the text given by the specified string, using this
+ * graphics context's current font and color. The baseline of the
+ * first character is at position (x, y) in this
+ * graphics context's coordinate system.
+ * @param str the string to be drawn.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ * @see java.awt.Graphics#drawBytes
+ * @see java.awt.Graphics#drawChars
+ */
+ public void drawString(String str, int x, int y){
+ drawString(str, (float)x, (float)y);
+ }
+
+ /**
+ * Fills an oval bounded by the specified rectangle with the
+ * current color.
+ * @param x the x coordinate of the upper left corner
+ * of the oval to be filled.
+ * @param y the y coordinate of the upper left corner
+ * of the oval to be filled.
+ * @param width the width of the oval to be filled.
+ * @param height the height of the oval to be filled.
+ * @see java.awt.Graphics#drawOval
+ */
+ public void fillOval(int x, int y, int width, int height){
+ Ellipse2D oval = new Ellipse2D.Double(x, y, width, height);
+ fill(oval);
+ }
+
+ /**
+ * Fills the specified rounded corner rectangle with the current color.
+ * The left and right edges of the rectangle
+ * are at x and x + width - 1,
+ * respectively. The top and bottom edges of the rectangle are at
+ * y and y + height - 1.
+ * @param x the x coordinate of the rectangle to be filled.
+ * @param y the y coordinate of the rectangle to be filled.
+ * @param width the width of the rectangle to be filled.
+ * @param height the height of the rectangle to be filled.
+ * @param arcWidth the horizontal diameter
+ * of the arc at the four corners.
+ * @param arcHeight the vertical diameter
+ * of the arc at the four corners.
+ * @see java.awt.Graphics#drawRoundRect
+ */
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight){
+
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
+ fill(rect);
+ }
+
+ /**
+ * Fills a circular or elliptical arc covering the specified rectangle.
+ *
+ * The resulting arc begins at startAngle and extends
+ * for arcAngle degrees.
+ * Angles are interpreted such that 0 degrees
+ * is at the 3 o'clock position.
+ * A positive value indicates a counter-clockwise rotation
+ * while a negative value indicates a clockwise rotation.
+ *
+ * The center of the arc is the center of the rectangle whose origin
+ * is (x, y) and whose size is specified by the
+ * width and height arguments.
+ *
+ * The resulting arc covers an area
+ * width + 1 pixels wide
+ * by height + 1 pixels tall.
+ *
+ * The angles are specified relative to the non-square extents of
+ * the bounding rectangle such that 45 degrees always falls on the
+ * line from the center of the ellipse to the upper right corner of
+ * the bounding rectangle. As a result, if the bounding rectangle is
+ * noticeably longer in one axis than the other, the angles to the
+ * start and end of the arc segment will be skewed farther along the
+ * longer axis of the bounds.
+ * @param x the x coordinate of the
+ * upper-left corner of the arc to be filled.
+ * @param y the y coordinate of the
+ * upper-left corner of the arc to be filled.
+ * @param width the width of the arc to be filled.
+ * @param height the height of the arc to be filled.
+ * @param startAngle the beginning angle.
+ * @param arcAngle the angular extent of the arc,
+ * relative to the start angle.
+ * @see java.awt.Graphics#drawArc
+ */
+ public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle){
+ Arc2D arc = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);
+ fill(arc);
+ }
+
+ /**
+ * Draws the outline of a circular or elliptical arc
+ * covering the specified rectangle.
+ *
+ * The resulting arc begins at startAngle and extends
+ * for arcAngle degrees, using the current color.
+ * Angles are interpreted such that 0 degrees
+ * is at the 3 o'clock position.
+ * A positive value indicates a counter-clockwise rotation
+ * while a negative value indicates a clockwise rotation.
+ *
+ * The center of the arc is the center of the rectangle whose origin
+ * is (x, y) and whose size is specified by the
+ * width and height arguments.
+ *
+ * The resulting arc covers an area
+ * width + 1 pixels wide
+ * by height + 1 pixels tall.
+ *
+ * The angles are specified relative to the non-square extents of
+ * the bounding rectangle such that 45 degrees always falls on the
+ * line from the center of the ellipse to the upper right corner of
+ * the bounding rectangle. As a result, if the bounding rectangle is
+ * noticeably longer in one axis than the other, the angles to the
+ * start and end of the arc segment will be skewed farther along the
+ * longer axis of the bounds.
+ * @param x the x coordinate of the
+ * upper-left corner of the arc to be drawn.
+ * @param y the y coordinate of the
+ * upper-left corner of the arc to be drawn.
+ * @param width the width of the arc to be drawn.
+ * @param height the height of the arc to be drawn.
+ * @param startAngle the beginning angle.
+ * @param arcAngle the angular extent of the arc,
+ * relative to the start angle.
+ * @see java.awt.Graphics#fillArc
+ */
+ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
+ Arc2D arc = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);
+ draw(arc);
+ }
+
+
+ /**
+ * Draws a sequence of connected lines defined by
+ * arrays of x and y coordinates.
+ * Each pair of (x, y) coordinates defines a point.
+ * The figure is not closed if the first point
+ * differs from the last point.
+ * @param xPoints an array of x points
+ * @param yPoints an array of y points
+ * @param nPoints the total number of points
+ * @see java.awt.Graphics#drawPolygon(int[], int[], int)
+ * @since JDK1.1
+ */
+ public void drawPolyline(int[] xPoints, int[] yPoints,
+ int nPoints){
+ if(nPoints > 0){
+ GeneralPath path = new GeneralPath();
+ path.moveTo(xPoints[0], yPoints[0]);
+ for(int i=1; ix, y,
+ * width, and height arguments.
+ *
+ * The oval covers an area that is
+ * width + 1 pixels wide
+ * and height + 1 pixels tall.
+ * @param x the x coordinate of the upper left
+ * corner of the oval to be drawn.
+ * @param y the y coordinate of the upper left
+ * corner of the oval to be drawn.
+ * @param width the width of the oval to be drawn.
+ * @param height the height of the oval to be drawn.
+ * @see java.awt.Graphics#fillOval
+ */
+ public void drawOval(int x, int y, int width, int height){
+ Ellipse2D oval = new Ellipse2D.Double(x, y, width, height);
+ draw(oval);
+ }
+
+ /**
+ * Draws as much of the specified image as is currently available.
+ * The image is drawn with its top-left corner at
+ * (x, y) in this graphics context's coordinate
+ * space. Transparent pixels are drawn in the specified
+ * background color.
+ *
+ * This operation is equivalent to filling a rectangle of the
+ * width and height of the specified image with the given color and then
+ * drawing the image on top of it, but possibly more efficient.
+ *
+ * This method returns immediately in all cases, even if the
+ * complete image has not yet been loaded, and it has not been dithered
+ * and converted for the current output device.
+ *
+ * If the image has not yet been completely loaded, then
+ * drawImage returns false. As more of
+ * the image becomes available, the process that draws the image notifies
+ * the specified image observer.
+ * @param img the specified image to be drawn.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ * @param bgcolor the background color to paint under the
+ * non-opaque portions of the image.
+ * @param observer object to be notified as more of
+ * the image is converted.
+ * @see java.awt.Image
+ * @see java.awt.image.ImageObserver
+ * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
+ */
+ @NotImplemented
+ public boolean drawImage(Image img, int x, int y,
+ Color bgcolor,
+ ImageObserver observer){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+
+ return false;
+ }
+
+ /**
+ * Draws as much of the specified image as has already been scaled
+ * to fit inside the specified rectangle.
+ *
+ * The image is drawn inside the specified rectangle of this
+ * graphics context's coordinate space, and is scaled if
+ * necessary. Transparent pixels are drawn in the specified
+ * background color.
+ * This operation is equivalent to filling a rectangle of the
+ * width and height of the specified image with the given color and then
+ * drawing the image on top of it, but possibly more efficient.
+ *
+ * This method returns immediately in all cases, even if the
+ * entire image has not yet been scaled, dithered, and converted
+ * for the current output device.
+ * If the current output representation is not yet complete then
+ * drawImage returns false. As more of
+ * the image becomes available, the process that draws the image notifies
+ * the specified image observer.
+ *
+ * A scaled version of an image will not necessarily be
+ * available immediately just because an unscaled version of the
+ * image has been constructed for this output device. Each size of
+ * the image may be cached separately and generated from the original
+ * data in a separate image production sequence.
+ * @param img the specified image to be drawn.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ * @param width the width of the rectangle.
+ * @param height the height of the rectangle.
+ * @param bgcolor the background color to paint under the
+ * non-opaque portions of the image.
+ * @param observer object to be notified as more of
+ * the image is converted.
+ * @see java.awt.Image
+ * @see java.awt.image.ImageObserver
+ * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
+ */
+ @NotImplemented
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ Color bgcolor,
+ ImageObserver observer){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Draws as much of the specified area of the specified image as is
+ * currently available, scaling it on the fly to fit inside the
+ * specified area of the destination drawable surface. Transparent pixels
+ * do not affect whatever pixels are already there.
+ *
+ * This method returns immediately in all cases, even if the
+ * image area to be drawn has not yet been scaled, dithered, and converted
+ * for the current output device.
+ * If the current output representation is not yet complete then
+ * drawImage returns false. As more of
+ * the image becomes available, the process that draws the image notifies
+ * the specified image observer.
+ *
+ * This method always uses the unscaled version of the image
+ * to render the scaled rectangle and performs the required
+ * scaling on the fly. It does not use a cached, scaled version
+ * of the image for this operation. Scaling of the image from source
+ * to destination is performed such that the first coordinate
+ * of the source rectangle is mapped to the first coordinate of
+ * the destination rectangle, and the second source coordinate is
+ * mapped to the second destination coordinate. The subimage is
+ * scaled and flipped as needed to preserve those mappings.
+ * @param img the specified image to be drawn
+ * @param dx1 the x coordinate of the first corner of the
+ * destination rectangle.
+ * @param dy1 the y coordinate of the first corner of the
+ * destination rectangle.
+ * @param dx2 the x coordinate of the second corner of the
+ * destination rectangle.
+ * @param dy2 the y coordinate of the second corner of the
+ * destination rectangle.
+ * @param sx1 the x coordinate of the first corner of the
+ * source rectangle.
+ * @param sy1 the y coordinate of the first corner of the
+ * source rectangle.
+ * @param sx2 the x coordinate of the second corner of the
+ * source rectangle.
+ * @param sy2 the y coordinate of the second corner of the
+ * source rectangle.
+ * @param observer object to be notified as more of the image is
+ * scaled and converted.
+ * @see java.awt.Image
+ * @see java.awt.image.ImageObserver
+ * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
+ * @since JDK1.1
+ */
+ @NotImplemented
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return false;
+ }
+
+ /**
+ * Draws as much of the specified area of the specified image as is
+ * currently available, scaling it on the fly to fit inside the
+ * specified area of the destination drawable surface.
+ *
+ * Transparent pixels are drawn in the specified background color.
+ * This operation is equivalent to filling a rectangle of the
+ * width and height of the specified image with the given color and then
+ * drawing the image on top of it, but possibly more efficient.
+ *
+ * This method returns immediately in all cases, even if the
+ * image area to be drawn has not yet been scaled, dithered, and converted
+ * for the current output device.
+ * If the current output representation is not yet complete then
+ * drawImage returns false. As more of
+ * the image becomes available, the process that draws the image notifies
+ * the specified image observer.
+ *
+ * This method always uses the unscaled version of the image
+ * to render the scaled rectangle and performs the required
+ * scaling on the fly. It does not use a cached, scaled version
+ * of the image for this operation. Scaling of the image from source
+ * to destination is performed such that the first coordinate
+ * of the source rectangle is mapped to the first coordinate of
+ * the destination rectangle, and the second source coordinate is
+ * mapped to the second destination coordinate. The subimage is
+ * scaled and flipped as needed to preserve those mappings.
+ * @param img the specified image to be drawn
+ * @param dx1 the x coordinate of the first corner of the
+ * destination rectangle.
+ * @param dy1 the y coordinate of the first corner of the
+ * destination rectangle.
+ * @param dx2 the x coordinate of the second corner of the
+ * destination rectangle.
+ * @param dy2 the y coordinate of the second corner of the
+ * destination rectangle.
+ * @param sx1 the x coordinate of the first corner of the
+ * source rectangle.
+ * @param sy1 the y coordinate of the first corner of the
+ * source rectangle.
+ * @param sx2 the x coordinate of the second corner of the
+ * source rectangle.
+ * @param sy2 the y coordinate of the second corner of the
+ * source rectangle.
+ * @param bgcolor the background color to paint under the
+ * non-opaque portions of the image.
+ * @param observer object to be notified as more of the image is
+ * scaled and converted.
+ * @see java.awt.Image
+ * @see java.awt.image.ImageObserver
+ * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
+ * @since JDK1.1
+ */
+ @NotImplemented
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ Color bgcolor,
+ ImageObserver observer){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return false;
+ }
+
+ /**
+ * Draws as much of the specified image as is currently available.
+ * The image is drawn with its top-left corner at
+ * (x, y) in this graphics context's coordinate
+ * space. Transparent pixels in the image do not affect whatever
+ * pixels are already there.
+ *
+ * This method returns immediately in all cases, even if the
+ * complete image has not yet been loaded, and it has not been dithered
+ * and converted for the current output device.
+ *
+ * If the image has completely loaded and its pixels are
+ * no longer being changed, then
+ * drawImage returns true.
+ * Otherwise, drawImage returns false
+ * and as more of
+ * the image becomes available
+ * or it is time to draw another frame of animation,
+ * the process that loads the image notifies
+ * the specified image observer.
+ * @param img the specified image to be drawn. This method does
+ * nothing if img is null.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ * @param observer object to be notified as more of
+ * the image is converted.
+ * @return false if the image pixels are still changing;
+ * true otherwise.
+ * @see java.awt.Image
+ * @see java.awt.image.ImageObserver
+ * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
+ */
+ @NotImplemented
+ public boolean drawImage(Image img, int x, int y,
+ ImageObserver observer) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return false;
+ }
+
+ /**
+ * Disposes of this graphics context and releases
+ * any system resources that it is using.
+ * A Graphics object cannot be used after
+ * disposehas been called.
+ *
+ * When a Java program runs, a large number of Graphics
+ * objects can be created within a short time frame.
+ * Although the finalization process of the garbage collector
+ * also disposes of the same system resources, it is preferable
+ * to manually free the associated resources by calling this
+ * method rather than to rely on a finalization process which
+ * may not run to completion for a long period of time.
+ *
+ * Graphics objects which are provided as arguments to the
+ * paint and update methods
+ * of components are automatically released by the system when
+ * those methods return. For efficiency, programmers should
+ * call dispose when finished using
+ * a Graphics object only if it was created
+ * directly from a component or another Graphics object.
+ * @see java.awt.Graphics#finalize
+ * @see java.awt.Component#paint
+ * @see java.awt.Component#update
+ * @see java.awt.Component#getGraphics
+ * @see java.awt.Graphics#create
+ */
+ public void dispose() {
+ }
+
+ /**
+ * Draws a line, using the current color, between the points
+ * (x1, y1) and (x2, y2)
+ * in this graphics context's coordinate system.
+ * @param x1 the first point's x coordinate.
+ * @param y1 the first point's y coordinate.
+ * @param x2 the second point's x coordinate.
+ * @param y2 the second point's y coordinate.
+ */
+ public void drawLine(int x1, int y1, int x2, int y2){
+ Line2D line = new Line2D.Double(x1, y1, x2, y2);
+ draw(line);
+ }
+
+ /**
+ * Fills a closed polygon defined by
+ * arrays of x and y coordinates.
+ *
+ * This method draws the polygon defined by nPoint line
+ * segments, where the first nPoint - 1
+ * line segments are line segments from
+ * (xPoints[i - 1], yPoints[i - 1])
+ * to (xPoints[i], yPoints[i]), for
+ * 1 ≤ i ≤ nPoints.
+ * The figure is automatically closed by drawing a line connecting
+ * the final point to the first point, if those points are different.
+ *
+ * The area inside the polygon is defined using an
+ * even-odd fill rule, also known as the alternating rule.
+ * @param xPoints a an array of x coordinates.
+ * @param yPoints a an array of y coordinates.
+ * @param nPoints a the total number of points.
+ * @see java.awt.Graphics#drawPolygon(int[], int[], int)
+ */
+ public void fillPolygon(int[] xPoints, int[] yPoints,
+ int nPoints){
+ java.awt.Polygon polygon = new java.awt.Polygon(xPoints, yPoints, nPoints);
+ fill(polygon);
+ }
+
+ /**
+ * Fills the specified rectangle.
+ * The left and right edges of the rectangle are at
+ * x and x + width - 1.
+ * The top and bottom edges are at
+ * y and y + height - 1.
+ * The resulting rectangle covers an area
+ * width pixels wide by
+ * height pixels tall.
+ * The rectangle is filled using the graphics context's current color.
+ * @param x the x coordinate
+ * of the rectangle to be filled.
+ * @param y the y coordinate
+ * of the rectangle to be filled.
+ * @param width the width of the rectangle to be filled.
+ * @param height the height of the rectangle to be filled.
+ * @see java.awt.Graphics#clearRect
+ * @see java.awt.Graphics#drawRect
+ */
+ public void fillRect(int x, int y, int width, int height){
+ Rectangle rect = new Rectangle(x, y, width, height);
+ fill(rect);
+ }
+
+ /**
+ * Draws the outline of the specified rectangle.
+ * The left and right edges of the rectangle are at
+ * x and x + width.
+ * The top and bottom edges are at
+ * y and y + height.
+ * The rectangle is drawn using the graphics context's current color.
+ * @param x the x coordinate
+ * of the rectangle to be drawn.
+ * @param y the y coordinate
+ * of the rectangle to be drawn.
+ * @param width the width of the rectangle to be drawn.
+ * @param height the height of the rectangle to be drawn.
+ * @see java.awt.Graphics#fillRect
+ * @see java.awt.Graphics#clearRect
+ */
+ public void drawRect(int x, int y, int width, int height) {
+ Rectangle rect = new Rectangle(x, y, width, height);
+ draw(rect);
+ }
+
+ /**
+ * Draws a closed polygon defined by
+ * arrays of x and y coordinates.
+ * Each pair of (x, y) coordinates defines a point.
+ *
+ * This method draws the polygon defined by nPoint line
+ * segments, where the first nPoint - 1
+ * line segments are line segments from
+ * (xPoints[i - 1], yPoints[i - 1])
+ * to (xPoints[i], yPoints[i]), for
+ * 1 ≤ i ≤ nPoints.
+ * The figure is automatically closed by drawing a line connecting
+ * the final point to the first point, if those points are different.
+ * @param xPoints a an array of x coordinates.
+ * @param yPoints a an array of y coordinates.
+ * @param nPoints a the total number of points.
+ * @see java.awt.Graphics#fillPolygon(int[],int[],int)
+ * @see java.awt.Graphics#drawPolyline
+ */
+ public void drawPolygon(int[] xPoints, int[] yPoints,
+ int nPoints){
+ java.awt.Polygon polygon = new java.awt.Polygon(xPoints, yPoints, nPoints);
+ draw(polygon);
+ }
+
+ /**
+ * Intersects the current clip with the specified rectangle.
+ * The resulting clipping area is the intersection of the current
+ * clipping area and the specified rectangle. If there is no
+ * current clipping area, either because the clip has never been
+ * set, or the clip has been cleared using setClip(null),
+ * the specified rectangle becomes the new clip.
+ * This method sets the user clip, which is independent of the
+ * clipping associated with device bounds and window visibility.
+ * This method can only be used to make the current clip smaller.
+ * To set the current clip larger, use any of the setClip methods.
+ * Rendering operations have no effect outside of the clipping area.
+ * @param x the x coordinate of the rectangle to intersect the clip with
+ * @param y the y coordinate of the rectangle to intersect the clip with
+ * @param width the width of the rectangle to intersect the clip with
+ * @param height the height of the rectangle to intersect the clip with
+ * @see #setClip(int, int, int, int)
+ * @see #setClip(Shape)
+ */
+ public void clipRect(int x, int y, int width, int height){
+ clip(new Rectangle(x, y, width, height));
+ }
+
+ /**
+ * Sets the current clipping area to an arbitrary clip shape.
+ * Not all objects that implement the Shape
+ * interface can be used to set the clip. The only
+ * Shape objects that are guaranteed to be
+ * supported are Shape objects that are
+ * obtained via the getClip method and via
+ * Rectangle objects. This method sets the
+ * user clip, which is independent of the clipping associated
+ * with device bounds and window visibility.
+ * @param clip the Shape to use to set the clip
+ * @see java.awt.Graphics#getClip()
+ * @see java.awt.Graphics#clipRect
+ * @see java.awt.Graphics#setClip(int, int, int, int)
+ * @since JDK1.1
+ */
+ @NotImplemented
+ public void setClip(Shape clip) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Returns the bounding rectangle of the current clipping area.
+ * This method refers to the user clip, which is independent of the
+ * clipping associated with device bounds and window visibility.
+ * If no clip has previously been set, or if the clip has been
+ * cleared using setClip(null), this method returns
+ * null.
+ * The coordinates in the rectangle are relative to the coordinate
+ * system origin of this graphics context.
+ * @return the bounding rectangle of the current clipping area,
+ * or null if no clip is set.
+ * @see java.awt.Graphics#getClip
+ * @see java.awt.Graphics#clipRect
+ * @see java.awt.Graphics#setClip(int, int, int, int)
+ * @see java.awt.Graphics#setClip(Shape)
+ * @since JDK1.1
+ */
+ public Rectangle getClipBounds(){
+ Shape c = getClip();
+ if (c==null) {
+ return null;
+ }
+ return c.getBounds();
+ }
+
+ /**
+ * Draws the text given by the specified iterator, using this
+ * graphics context's current color. The iterator has to specify a font
+ * for each character. The baseline of the
+ * first character is at position (x, y) in this
+ * graphics context's coordinate system.
+ * @param iterator the iterator whose text is to be drawn
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ * @see java.awt.Graphics#drawBytes
+ * @see java.awt.Graphics#drawChars
+ */
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y){
+ drawString(iterator, (float)x, (float)y);
+ }
+
+ /**
+ * Clears the specified rectangle by filling it with the background
+ * color of the current drawing surface. This operation does not
+ * use the current paint mode.
+ *
+ * Beginning with Java 1.1, the background color
+ * of offscreen images may be system dependent. Applications should
+ * use setColor followed by fillRect to
+ * ensure that an offscreen image is cleared to a specific color.
+ * @param x the x coordinate of the rectangle to clear.
+ * @param y the y coordinate of the rectangle to clear.
+ * @param width the width of the rectangle to clear.
+ * @param height the height of the rectangle to clear.
+ * @see java.awt.Graphics#fillRect(int, int, int, int)
+ * @see java.awt.Graphics#drawRect
+ * @see java.awt.Graphics#setColor(java.awt.Color)
+ * @see java.awt.Graphics#setPaintMode
+ * @see java.awt.Graphics#setXORMode(java.awt.Color)
+ */
+ public void clearRect(int x, int y, int width, int height) {
+ Paint paint = getPaint();
+ setColor(getBackground());
+ fillRect(x, y, width, height);
+ setPaint(paint);
+ }
+
+ public void copyArea(int x, int y, int width, int height, int dx, int dy) {
+ }
+
+ /**
+ * Sets the current clip to the rectangle specified by the given
+ * coordinates. This method sets the user clip, which is
+ * independent of the clipping associated with device bounds
+ * and window visibility.
+ * Rendering operations have no effect outside of the clipping area.
+ * @param x the x coordinate of the new clip rectangle.
+ * @param y the y coordinate of the new clip rectangle.
+ * @param width the width of the new clip rectangle.
+ * @param height the height of the new clip rectangle.
+ * @see java.awt.Graphics#clipRect
+ * @see java.awt.Graphics#setClip(Shape)
+ * @since JDK1.1
+ */
+ public void setClip(int x, int y, int width, int height){
+ setClip(new Rectangle(x, y, width, height));
+ }
+
+ /**
+ * Concatenates the current Graphics2D
+ * Transform with a rotation transform.
+ * Subsequent rendering is rotated by the specified radians relative
+ * to the previous origin.
+ * This is equivalent to calling transform(R), where R is an
+ * AffineTransform represented by the following matrix:
+ *
+ * Rotating with a positive angle theta rotates points on the positive
+ * x axis toward the positive y axis.
+ * @param theta the angle of rotation in radians
+ */
+ public void rotate(double theta){
+ _transform.rotate(theta);
+ }
+
+ /**
+ * Concatenates the current Graphics2D
+ * Transform with a translated rotation
+ * transform. Subsequent rendering is transformed by a transform
+ * which is constructed by translating to the specified location,
+ * rotating by the specified radians, and translating back by the same
+ * amount as the original translation. This is equivalent to the
+ * following sequence of calls:
+ *
+ * Rotating with a positive angle theta rotates points on the positive
+ * x axis toward the positive y axis.
+ * @param theta the angle of rotation in radians
+ * @param x x coordinate of the origin of the rotation
+ * @param y y coordinate of the origin of the rotation
+ */
+ public void rotate(double theta, double x, double y){
+ _transform.rotate(theta, x, y);
+ }
+
+ /**
+ * Concatenates the current Graphics2D
+ * Transform with a shearing transform.
+ * Subsequent renderings are sheared by the specified
+ * multiplier relative to the previous position.
+ * This is equivalent to calling transform(SH), where SH
+ * is an AffineTransform represented by the following
+ * matrix:
+ *
+ * [ 1 shx 0 ]
+ * [ shy 1 0 ]
+ * [ 0 0 1 ]
+ *
+ * @param shx the multiplier by which coordinates are shifted in
+ * the positive X axis direction as a function of their Y coordinate
+ * @param shy the multiplier by which coordinates are shifted in
+ * the positive Y axis direction as a function of their X coordinate
+ */
+ public void shear(double shx, double shy){
+ _transform.shear(shx, shy);
+ }
+
+ /**
+ * Get the rendering context of the Font within this
+ * Graphics2D context.
+ * The {@link FontRenderContext}
+ * encapsulates application hints such as anti-aliasing and
+ * fractional metrics, as well as target device specific information
+ * such as dots-per-inch. This information should be provided by the
+ * application when using objects that perform typographical
+ * formatting, such as Font and
+ * TextLayout. This information should also be provided
+ * by applications that perform their own layout and need accurate
+ * measurements of various characteristics of glyphs such as advance
+ * and line height when various rendering hints have been applied to
+ * the text rendering.
+ *
+ * @return a reference to an instance of FontRenderContext.
+ * @see java.awt.font.FontRenderContext
+ * @see java.awt.Font#createGlyphVector(FontRenderContext,char[])
+ * @see java.awt.font.TextLayout
+ * @since JDK1.2
+ */
+ public FontRenderContext getFontRenderContext() {
+ boolean isAntiAliased = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals(
+ getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));
+ boolean usesFractionalMetrics = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals(
+ getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS));
+
+
+ return new FontRenderContext(new AffineTransform(), isAntiAliased, usesFractionalMetrics);
+ }
+
+ /**
+ * Composes an AffineTransform object with the
+ * Transform in this Graphics2D according
+ * to the rule last-specified-first-applied. If the current
+ * Transform is Cx, the result of composition
+ * with Tx is a new Transform Cx'. Cx' becomes the
+ * current Transform for this Graphics2D.
+ * Transforming a point p by the updated Transform Cx' is
+ * equivalent to first transforming p by Tx and then transforming
+ * the result by the original Transform Cx. In other
+ * words, Cx'(p) = Cx(Tx(p)). A copy of the Tx is made, if necessary,
+ * so further modifications to Tx do not affect rendering.
+ * @param Tx the AffineTransform object to be composed with
+ * the current Transform
+ * @see #setTransform
+ * @see AffineTransform
+ */
+ public void transform(AffineTransform Tx) {
+ _transform.concatenate(Tx);
+ }
+
+ /**
+ * Renders a BufferedImage that is
+ * filtered with a
+ * {@link BufferedImageOp}.
+ * The rendering attributes applied include the Clip,
+ * Transform
+ * and Composite attributes. This is equivalent to:
+ *
+ * @param img the BufferedImage to be rendered
+ * @param op the filter to be applied to the image before rendering
+ * @param x the x coordinate in user space where the image is rendered
+ * @param y the y coordinate in user space where the image is rendered
+ * @see #_transform
+ * @see #setTransform
+ * @see #setComposite
+ * @see #clip
+ * @see #setClip(Shape)
+ */
+ public void drawImage(BufferedImage img,
+ BufferedImageOp op,
+ int x,
+ int y){
+ img = op.filter(img, null);
+ drawImage(img, x, y, null);
+ }
+
+ /**
+ * Sets the background color for the Graphics2D context.
+ * The background color is used for clearing a region.
+ * When a Graphics2D is constructed for a
+ * Component, the background color is
+ * inherited from the Component. Setting the background color
+ * in the Graphics2D context only affects the subsequent
+ * clearRect calls and not the background color of the
+ * Component. To change the background
+ * of the Component, use appropriate methods of
+ * the Component.
+ * @param color the background color that isused in
+ * subsequent calls to clearRect
+ * @see #getBackground
+ * @see java.awt.Graphics#clearRect
+ */
+ public void setBackground(Color color) {
+ if(color == null)
+ return;
+
+ _background = color;
+ }
+
+ /**
+ * Returns the background color used for clearing a region.
+ * @return the current Graphics2DColor,
+ * which defines the background color.
+ * @see #setBackground
+ */
+ public Color getBackground(){
+ return _background;
+ }
+
+ /**
+ * Sets the Composite for the Graphics2D context.
+ * The Composite is used in all drawing methods such as
+ * drawImage, drawString, draw,
+ * and fill. It specifies how new pixels are to be combined
+ * with the existing pixels on the graphics device during the rendering
+ * process.
+ *
If this Graphics2D context is drawing to a
+ * Component on the display screen and the
+ * Composite is a custom object rather than an
+ * instance of the AlphaComposite class, and if
+ * there is a security manager, its checkPermission
+ * method is called with an AWTPermission("readDisplayPixels")
+ * permission.
+ *
+ * @param comp the Composite object to be used for rendering
+ * @throws SecurityException
+ * if a custom Composite object is being
+ * used to render to the screen and a security manager
+ * is set and its checkPermission method
+ * does not allow the operation.
+ * @see java.awt.Graphics#setXORMode
+ * @see java.awt.Graphics#setPaintMode
+ * @see java.awt.AlphaComposite
+ */
+ @NotImplemented
+ public void setComposite(Composite comp){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Returns the current Composite in the
+ * Graphics2D context.
+ * @return the current Graphics2DComposite,
+ * which defines a compositing style.
+ * @see #setComposite
+ */
+ @NotImplemented
+ public Composite getComposite(){
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return null;
+ }
+
+ /**
+ * Returns the value of a single preference for the rendering algorithms.
+ * Hint categories include controls for rendering quality and overall
+ * time/quality trade-off in the rendering process. Refer to the
+ * RenderingHints class for definitions of some common
+ * keys and values.
+ * @param hintKey the key corresponding to the hint to get.
+ * @return an object representing the value for the specified hint key.
+ * Some of the keys and their associated values are defined in the
+ * RenderingHints class.
+ * @see RenderingHints
+ */
+ public Object getRenderingHint(RenderingHints.Key hintKey){
+ return _hints.get(hintKey);
+ }
+
+ /**
+ * Sets the value of a single preference for the rendering algorithms.
+ * Hint categories include controls for rendering quality and overall
+ * time/quality trade-off in the rendering process. Refer to the
+ * RenderingHints class for definitions of some common
+ * keys and values.
+ * @param hintKey the key of the hint to be set.
+ * @param hintValue the value indicating preferences for the specified
+ * hint category.
+ * @see RenderingHints
+ */
+ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue){
+ _hints.put(hintKey, hintValue);
+ }
+
+
+ /**
+ * Renders the text of the specified
+ * {@link GlyphVector} using
+ * the Graphics2D context's rendering attributes.
+ * The rendering attributes applied include the Clip,
+ * Transform, Paint, and
+ * Composite attributes. The GlyphVector
+ * specifies individual glyphs from a {@link Font}.
+ * The GlyphVector can also contain the glyph positions.
+ * This is the fastest way to render a set of characters to the
+ * screen.
+ *
+ * @param g the GlyphVector to be rendered
+ * @param x the x position in user space where the glyphs should be
+ * rendered
+ * @param y the y position in user space where the glyphs should be
+ * rendered
+ *
+ * @see java.awt.Font#createGlyphVector(FontRenderContext, char[])
+ * @see java.awt.font.GlyphVector
+ * @see #setPaint
+ * @see java.awt.Graphics#setColor
+ * @see #setTransform
+ * @see #setComposite
+ * @see #setClip(Shape)
+ */
+ public void drawGlyphVector(GlyphVector g, float x, float y) {
+ Shape glyphOutline = g.getOutline(x, y);
+ fill(glyphOutline);
+ }
+
+ /**
+ * Returns the device configuration associated with this
+ * Graphics2D.
+ * @return the device configuration
+ */
+ public GraphicsConfiguration getDeviceConfiguration() {
+ return GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getDefaultConfiguration();
+ }
+
+ /**
+ * Sets the values of an arbitrary number of preferences for the
+ * rendering algorithms.
+ * Only values for the rendering hints that are present in the
+ * specified Map object are modified.
+ * All other preferences not present in the specified
+ * object are left unmodified.
+ * Hint categories include controls for rendering quality and
+ * overall time/quality trade-off in the rendering process.
+ * Refer to the RenderingHints class for definitions of
+ * some common keys and values.
+ * @param hints the rendering hints to be set
+ * @see RenderingHints
+ */
+ public void addRenderingHints(Map,?> hints){
+ this._hints.putAll(hints);
+ }
+
+ /**
+ * Concatenates the current
+ * Graphics2DTransform
+ * with a translation transform.
+ * Subsequent rendering is translated by the specified
+ * distance relative to the previous position.
+ * This is equivalent to calling transform(T), where T is an
+ * AffineTransform represented by the following matrix:
+ *
+ * [ 1 0 tx ]
+ * [ 0 1 ty ]
+ * [ 0 0 1 ]
+ *
+ * @param tx the distance to translate along the x-axis
+ * @param ty the distance to translate along the y-axis
+ */
+ public void translate(double tx, double ty){
+ _transform.translate(tx, ty);
+ }
+
+ /**
+ * Renders the text of the specified iterator, using the
+ * Graphics2D context's current Paint. The
+ * iterator must specify a font
+ * for each character. The baseline of the
+ * first character is at position (x, y) in the
+ * User Space.
+ * The rendering attributes applied include the Clip,
+ * Transform, Paint, and
+ * Composite attributes.
+ * For characters in script systems such as Hebrew and Arabic,
+ * the glyphs can be rendered from right to left, in which case the
+ * coordinate supplied is the location of the leftmost character
+ * on the baseline.
+ * @param iterator the iterator whose text is to be rendered
+ * @param x the x coordinate where the iterator's text is to be
+ * rendered
+ * @param y the y coordinate where the iterator's text is to be
+ * rendered
+ * @see #setPaint
+ * @see java.awt.Graphics#setColor
+ * @see #setTransform
+ * @see #setComposite
+ * @see #setClip
+ */
+ @NotImplemented
+ public void drawString(AttributedCharacterIterator iterator, float x, float y) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Checks whether or not the specified Shape intersects
+ * the specified {@link Rectangle}, which is in device
+ * space. If onStroke is false, this method checks
+ * whether or not the interior of the specified Shape
+ * intersects the specified Rectangle. If
+ * onStroke is true, this method checks
+ * whether or not the Stroke of the specified
+ * Shape outline intersects the specified
+ * Rectangle.
+ * The rendering attributes taken into account include the
+ * Clip, Transform, and Stroke
+ * attributes.
+ * @param rect the area in device space to check for a hit
+ * @param s the Shape to check for a hit
+ * @param onStroke flag used to choose between testing the
+ * stroked or the filled shape. If the flag is true, the
+ * Stroke oultine is tested. If the flag is
+ * false, the filled Shape is tested.
+ * @return true if there is a hit; false
+ * otherwise.
+ * @see #setStroke
+ * @see #fill(Shape)
+ * @see #draw(Shape)
+ * @see #_transform
+ * @see #setTransform
+ * @see #clip
+ * @see #setClip(Shape)
+ */
+ public boolean hit(Rectangle rect,
+ Shape s,
+ boolean onStroke){
+ if (onStroke) {
+ s = getStroke().createStrokedShape(s);
+ }
+
+ s = getTransform().createTransformedShape(s);
+
+ return s.intersects(rect);
+ }
+
+ /**
+ * Gets the preferences for the rendering algorithms. Hint categories
+ * include controls for rendering quality and overall time/quality
+ * trade-off in the rendering process.
+ * Returns all of the hint key/value pairs that were ever specified in
+ * one operation. Refer to the
+ * RenderingHints class for definitions of some common
+ * keys and values.
+ * @return a reference to an instance of RenderingHints
+ * that contains the current preferences.
+ * @see RenderingHints
+ */
+ public RenderingHints getRenderingHints(){
+ return _hints;
+ }
+
+ /**
+ * Replaces the values of all preferences for the rendering
+ * algorithms with the specified hints.
+ * The existing values for all rendering hints are discarded and
+ * the new set of known hints and values are initialized from the
+ * specified {@link Map} object.
+ * Hint categories include controls for rendering quality and
+ * overall time/quality trade-off in the rendering process.
+ * Refer to the RenderingHints class for definitions of
+ * some common keys and values.
+ * @param hints the rendering hints to be set
+ * @see RenderingHints
+ */
+ public void setRenderingHints(Map,?> hints){
+ this._hints = new RenderingHints(null);
+ this._hints.putAll(hints);
+ }
+
+ /**
+ * Renders an image, applying a transform from image space into user space
+ * before drawing.
+ * The transformation from user space into device space is done with
+ * the current Transform in the Graphics2D.
+ * The specified transformation is applied to the image before the
+ * transform attribute in the Graphics2D context is applied.
+ * The rendering attributes applied include the Clip,
+ * Transform, and Composite attributes.
+ * Note that no rendering is done if the specified transform is
+ * noninvertible.
+ * @param img the Image to be rendered
+ * @param xform the transformation from image space into user space
+ * @param obs the {@link ImageObserver}
+ * to be notified as more of the Image
+ * is converted
+ * @return true if the Image is
+ * fully loaded and completely rendered;
+ * false if the Image is still being loaded.
+ * @see #_transform
+ * @see #setTransform
+ * @see #setComposite
+ * @see #clip
+ * @see #setClip(Shape)
+ */
+ @NotImplemented
+ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return false;
+ }
+
+ /**
+ * Draws as much of the specified image as has already been scaled
+ * to fit inside the specified rectangle.
+ *
+ * The image is drawn inside the specified rectangle of this
+ * graphics context's coordinate space, and is scaled if
+ * necessary. Transparent pixels do not affect whatever pixels
+ * are already there.
+ *
+ * This method returns immediately in all cases, even if the
+ * entire image has not yet been scaled, dithered, and converted
+ * for the current output device.
+ * If the current output representation is not yet complete, then
+ * drawImage returns false. As more of
+ * the image becomes available, the process that loads the image notifies
+ * the image observer by calling its imageUpdate method.
+ *
+ * A scaled version of an image will not necessarily be
+ * available immediately just because an unscaled version of the
+ * image has been constructed for this output device. Each size of
+ * the image may be cached separately and generated from the original
+ * data in a separate image production sequence.
+ * @param img the specified image to be drawn. This method does
+ * nothing if img is null.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ * @param width the width of the rectangle.
+ * @param height the height of the rectangle.
+ * @param observer object to be notified as more of
+ * the image is converted.
+ * @return false if the image pixels are still changing;
+ * true otherwise.
+ * @see java.awt.Image
+ * @see java.awt.image.ImageObserver
+ * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
+ */
+ @NotImplemented
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ ImageObserver observer) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ return false;
+ }
+
+ /**
+ * Creates a new Graphics object that is
+ * a copy of this Graphics object.
+ * @return a new graphics context that is a copy of
+ * this graphics context.
+ */
+ public Graphics create() {
+ try {
+ return (Graphics)clone();
+ } catch (CloneNotSupportedException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Gets the font metrics for the specified font.
+ * @return the font metrics for the specified font.
+ * @param f the specified font
+ * @see java.awt.Graphics#getFont
+ * @see java.awt.FontMetrics
+ * @see java.awt.Graphics#getFontMetrics()
+ */
+ @SuppressWarnings("deprecation")
+ @SuppressForbidden
+ public FontMetrics getFontMetrics(Font f) {
+ return Toolkit.getDefaultToolkit().getFontMetrics(f);
+ }
+
+ /**
+ * Sets the paint mode of this graphics context to alternate between
+ * this graphics context's current color and the new specified color.
+ * This specifies that logical pixel operations are performed in the
+ * XOR mode, which alternates pixels between the current color and
+ * a specified XOR color.
+ *
+ * When drawing operations are performed, pixels which are the
+ * current color are changed to the specified color, and vice versa.
+ *
+ * Pixels that are of colors other than those two colors are changed
+ * in an unpredictable but reversible manner; if the same figure is
+ * drawn twice, then all pixels are restored to their original values.
+ * @param c1 the XOR alternation color
+ */
+ @NotImplemented
+ public void setXORMode(Color c1) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Sets the paint mode of this graphics context to overwrite the
+ * destination with this graphics context's current color.
+ * This sets the logical pixel operation function to the paint or
+ * overwrite mode. All subsequent rendering operations will
+ * overwrite the destination with the current color.
+ */
+ @NotImplemented
+ public void setPaintMode() {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Renders a
+ * {@link RenderableImage},
+ * applying a transform from image space into user space before drawing.
+ * The transformation from user space into device space is done with
+ * the current Transform in the Graphics2D.
+ * The specified transformation is applied to the image before the
+ * transform attribute in the Graphics2D context is applied.
+ * The rendering attributes applied include the Clip,
+ * Transform, and Composite attributes. Note
+ * that no rendering is done if the specified transform is
+ * noninvertible.
+ *
+ * Rendering hints set on the Graphics2D object might
+ * be used in rendering the RenderableImage.
+ * If explicit control is required over specific hints recognized by a
+ * specific RenderableImage, or if knowledge of which hints
+ * are used is required, then a RenderedImage should be
+ * obtained directly from the RenderableImage
+ * and rendered using
+ *{@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}.
+ * @param img the image to be rendered. This method does
+ * nothing if img is null.
+ * @param xform the transformation from image space into user space
+ * @see #_transform
+ * @see #setTransform
+ * @see #setComposite
+ * @see #clip
+ * @see #setClip
+ * @see #drawRenderedImage
+ */
+ @NotImplemented
+ public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ /**
+ * Renders a {@link RenderedImage},
+ * applying a transform from image
+ * space into user space before drawing.
+ * The transformation from user space into device space is done with
+ * the current Transform in the Graphics2D.
+ * The specified transformation is applied to the image before the
+ * transform attribute in the Graphics2D context is applied.
+ * The rendering attributes applied include the Clip,
+ * Transform, and Composite attributes. Note
+ * that no rendering is done if the specified transform is
+ * noninvertible.
+ * @param img the image to be rendered. This method does
+ * nothing if img is null.
+ * @param xform the transformation from image space into user space
+ * @see #_transform
+ * @see #setTransform
+ * @see #setComposite
+ * @see #clip
+ * @see #setClip
+ */
+ @NotImplemented
+ public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+ if (log.check(POILogger.WARN)) {
+ log.log(POILogger.WARN, "Not implemented");
+ }
+ }
+
+ protected void applyStroke(SimpleShape,?> shape) {
+ if (_stroke instanceof BasicStroke){
+ BasicStroke bs = (BasicStroke)_stroke;
+ shape.setStrokeStyle((double)bs.getLineWidth());
+ float[] dash = bs.getDashArray();
+ if (dash != null) {
+ //TODO: implement more dashing styles
+ shape.setStrokeStyle(StrokeStyle.LineDash.DASH);
+ }
+ }
+ }
+
+ protected void applyPaint(SimpleShape,?> shape) {
+ if (_paint instanceof Color) {
+ shape.setFillColor((Color)_paint);
+ }
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java b/src/java/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java
index 32ee2a03b0..98a9b1822f 100644
--- a/src/java/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java
+++ b/src/java/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java
@@ -1,109 +1,109 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.sl.draw.binding;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlType;
-
-
-/**
- *
Java class for CT_AdjPoint2D complex type.
- *
- *
The following schema fragment specifies the expected content contained within this class.
- *
- *
- *
- *
- */
-@XmlAccessorType(XmlAccessType.FIELD)
-@XmlType(name = "CT_AdjPoint2D", namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")
-public class CTAdjPoint2D {
-
- @XmlAttribute(required = true)
- protected String x;
- @XmlAttribute(required = true)
- protected String y;
-
- /**
- * Gets the value of the x property.
- *
- * @return
- * possible object is
- * {@link String }
- *
- */
- public String getX() {
- return x;
- }
-
- /**
- * Sets the value of the x property.
- *
- * @param value
- * allowed object is
- * {@link String }
- *
- */
- public void setX(String value) {
- this.x = value;
- }
-
- public boolean isSetX() {
- return (this.x!= null);
- }
-
- /**
- * Gets the value of the y property.
- *
- * @return
- * possible object is
- * {@link String }
- *
- */
- public String getY() {
- return y;
- }
-
- /**
- * Sets the value of the y property.
- *
- * @param value
- * allowed object is
- * {@link String }
- *
- */
- public void setY(String value) {
- this.y = value;
- }
-
- public boolean isSetY() {
- return (this.y!= null);
- }
-
-}
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.draw.binding;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
Java class for CT_AdjPoint2D complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "CT_AdjPoint2D", namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")
+public class CTAdjPoint2D {
+
+ @XmlAttribute(required = true)
+ protected String x;
+ @XmlAttribute(required = true)
+ protected String y;
+
+ /**
+ * Gets the value of the x property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getX() {
+ return x;
+ }
+
+ /**
+ * Sets the value of the x property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setX(String value) {
+ this.x = value;
+ }
+
+ public boolean isSetX() {
+ return (this.x!= null);
+ }
+
+ /**
+ * Gets the value of the y property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getY() {
+ return y;
+ }
+
+ /**
+ * Sets the value of the y property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setY(String value) {
+ this.y = value;
+ }
+
+ public boolean isSetY() {
+ return (this.y!= null);
+ }
+
+}
diff --git a/src/java/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java b/src/java/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java
index d684cccb74..0ca4ce5371 100644
--- a/src/java/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java
+++ b/src/java/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java
@@ -1,99 +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.poi.sl.draw.binding;
-
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElements;
-import javax.xml.bind.annotation.XmlType;
-
-
-/**
- *
Java class for CT_AdjustHandleList complex type.
- *
- *
The following schema fragment specifies the expected content contained within this class.
- *
- *