aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.properties3
-rw-r--r--build.xml68
-rw-r--r--[-rwxr-xr-x]checkstyle-4.0.xml0
-rw-r--r--checkstyle-5.0.xml201
-rw-r--r--examples/fo/advanced/bleed-and-crop-marks.fo155
-rwxr-xr-xfixsvnprops.sh13
-rw-r--r--forrest.properties2
-rw-r--r--hyph/hyphenation.dtd4
-rw-r--r--lib/xmlgraphics-commons-1.4svn.jarbin540172 -> 552758 bytes
-rw-r--r--src/codegen/unicode/java/org/apache/fop/hyphenation/UnicodeClasses.java400
-rw-r--r--src/codegen/unicode/java/org/apache/fop/text/linebreak/GenerateLineBreakUtils.java44
-rw-r--r--src/codegen/unicode/java/org/apache/fop/util/License.java119
-rw-r--r--src/documentation/content/xdocs/compliance.ihtml24
-rw-r--r--src/documentation/content/xdocs/quickstartguide.xml4
-rw-r--r--src/documentation/content/xdocs/resources.xml1
-rw-r--r--src/documentation/content/xdocs/trunk/embedding.xml3
-rw-r--r--src/documentation/content/xdocs/trunk/events.xml25
-rw-r--r--src/documentation/content/xdocs/trunk/extensions.xml108
-rw-r--r--src/documentation/content/xdocs/trunk/output.xml78
-rw-r--r--src/documentation/skinconf.xml2
-rw-r--r--src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory2
-rw-r--r--src/java/org/apache/fop/afp/AFPDataObjectInfo.java24
-rw-r--r--src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java115
-rw-r--r--src/java/org/apache/fop/afp/AFPEventProducer.java10
-rw-r--r--src/java/org/apache/fop/afp/AFPEventProducer.xml1
-rw-r--r--src/java/org/apache/fop/afp/AFPGraphics2D.java32
-rw-r--r--src/java/org/apache/fop/afp/AFPImageObjectInfo.java2
-rw-r--r--src/java/org/apache/fop/afp/AFPResourceInfo.java2
-rw-r--r--src/java/org/apache/fop/afp/AFPResourceManager.java99
-rw-r--r--src/java/org/apache/fop/afp/AbstractAFPPainter.java5
-rw-r--r--src/java/org/apache/fop/afp/DataStream.java98
-rw-r--r--src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java32
-rw-r--r--src/java/org/apache/fop/afp/fonts/AFPFont.java12
-rw-r--r--src/java/org/apache/fop/afp/fonts/AFPFontReader.java121
-rw-r--r--src/java/org/apache/fop/afp/fonts/CharacterSet.java20
-rw-r--r--src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java2
-rw-r--r--src/java/org/apache/fop/afp/fonts/OutlineFont.java17
-rw-r--r--src/java/org/apache/fop/afp/fonts/RasterFont.java127
-rw-r--r--src/java/org/apache/fop/afp/goca/AbstractGraphicsCoord.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/AbstractGraphicsDrawingOrderContainer.java21
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsBox.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsData.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsFillet.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsFullArc.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsLine.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetArcParameters.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetCharacterSet.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetCurrentPosition.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetLineType.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetPatternSymbol.java2
-rw-r--r--src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/AbstractDataObject.java7
-rw-r--r--src/java/org/apache/fop/afp/modca/AbstractEnvironmentGroup.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/AbstractPageObject.java25
-rw-r--r--src/java/org/apache/fop/afp/modca/AbstractResourceEnvironmentGroupContainer.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/AbstractResourceGroupContainer.java19
-rw-r--r--src/java/org/apache/fop/afp/modca/ImageObject.java7
-rw-r--r--src/java/org/apache/fop/afp/modca/InterchangeSet.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/MapCodedFont.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/MapDataResource.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/ObjectContainer.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/PageGroup.java13
-rw-r--r--src/java/org/apache/fop/afp/modca/PageSegment.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/Registry.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/ResourceEnvironmentGroup.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/ResourceObject.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/TagLogicalElement.java4
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/AbstractTriplet.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java11
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/MeasurementUnitsTriplet.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/ObjectAreaSizeTriplet.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/ObjectClassificationTriplet.java2
-rw-r--r--src/java/org/apache/fop/afp/modca/triplets/ResourceObjectTypeTriplet.java2
-rw-r--r--src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java46
-rw-r--r--src/java/org/apache/fop/afp/util/CubicBezierApproximator.java126
-rw-r--r--src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java6
-rw-r--r--src/java/org/apache/fop/apps/FOURIResolver.java2
-rw-r--r--src/java/org/apache/fop/area/AreaTreeParser.java9
-rw-r--r--src/java/org/apache/fop/area/PageViewport.java13
-rw-r--r--src/java/org/apache/fop/cli/CommandLineOptions.java15
-rw-r--r--src/java/org/apache/fop/events/EventFormatter.xml10
-rw-r--r--src/java/org/apache/fop/events/EventFormatter_de.xml46
-rw-r--r--src/java/org/apache/fop/fo/FONode.java31
-rw-r--r--src/java/org/apache/fop/fo/FOTreeBuilder.java3
-rw-r--r--src/java/org/apache/fop/fo/FObj.java6
-rw-r--r--src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java12
-rw-r--r--src/java/org/apache/fop/fo/expr/ICCColorFunction.java13
-rw-r--r--src/java/org/apache/fop/fo/expr/PropertyParser.java19
-rw-r--r--src/java/org/apache/fop/fo/expr/PropertyTokenizer.java19
-rw-r--r--src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java10
-rw-r--r--src/java/org/apache/fop/fo/flow/table/EffRow.java36
-rw-r--r--src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java23
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableBody.java2
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TablePart.java14
-rw-r--r--src/java/org/apache/fop/fo/properties/LengthProperty.java5
-rw-r--r--src/java/org/apache/fop/fonts/FontAdder.java2
-rw-r--r--src/java/org/apache/fop/fonts/FontDetector.java2
-rw-r--r--src/java/org/apache/fop/fonts/FontInfoConfigurator.java2
-rw-r--r--src/java/org/apache/fop/fonts/SingleByteFont.java2
-rw-r--r--src/java/org/apache/fop/fonts/apps/PFMReader.java31
-rw-r--r--src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java3
-rw-r--r--src/java/org/apache/fop/fonts/type1/PFMFile.java8
-rw-r--r--src/java/org/apache/fop/hyphenation/PatternParser.java86
-rw-r--r--src/java/org/apache/fop/hyphenation/SerializeHyphPattern.java138
-rw-r--r--src/java/org/apache/fop/hyphenation/classes.xml7652
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java20
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBreaker.java248
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java40
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java5
-rw-r--r--src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java2
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java329
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java51
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java30
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java490
-rw-r--r--src/java/org/apache/fop/layoutmgr/BreakElement.java49
-rw-r--r--src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java777
-rw-r--r--src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java232
-rw-r--r--src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java12
-rw-r--r--src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java16
-rw-r--r--src/java/org/apache/fop/layoutmgr/Keep.java152
-rw-r--r--src/java/org/apache/fop/layoutmgr/KeepUtil.java109
-rw-r--r--src/java/org/apache/fop/layoutmgr/KnuthPenalty.java90
-rw-r--r--src/java/org/apache/fop/layoutmgr/KnuthSequence.java66
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutContext.java72
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutManager.java33
-rw-r--r--src/java/org/apache/fop/layoutmgr/LeafPosition.java11
-rw-r--r--src/java/org/apache/fop/layoutmgr/Page.java4
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreaker.java353
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java350
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageProvider.java72
-rw-r--r--src/java/org/apache/fop/layoutmgr/Position.java5
-rw-r--r--src/java/org/apache/fop/layoutmgr/SpaceResolver.java6
-rw-r--r--src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java115
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java12
-rwxr-xr-xsrc/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java32
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java617
-rw-r--r--src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java17
-rw-r--r--src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java17
-rw-r--r--src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java49
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/ActiveCell.java32
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java4
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java22
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java22
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java43
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java21
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java20
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableStepper.java28
-rw-r--r--src/java/org/apache/fop/pdf/PDFColor.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java17
-rw-r--r--src/java/org/apache/fop/pdf/PDFFileSpec.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFFunction.java11
-rw-r--r--src/java/org/apache/fop/pdf/PDFGState.java6
-rw-r--r--src/java/org/apache/fop/pdf/PDFGoTo.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFGoToRemote.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFLaunch.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFLink.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFObject.java16
-rw-r--r--src/java/org/apache/fop/pdf/PDFPage.java76
-rw-r--r--src/java/org/apache/fop/pdf/PDFPattern.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFShading.java10
-rw-r--r--src/java/org/apache/fop/render/AbstractConfigurator.java2
-rw-r--r--src/java/org/apache/fop/render/DummyPercentBaseContext.java47
-rw-r--r--src/java/org/apache/fop/render/afp/AFPCustomizable.java6
-rw-r--r--src/java/org/apache/fop/render/afp/AFPDocumentHandler.java55
-rw-r--r--src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java6
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java26
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRenderer.java157
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java50
-rw-r--r--src/java/org/apache/fop/render/afp/AFPShadingMode.java74
-rwxr-xr-xsrc/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java12
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java2
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java60
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java87
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java89
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java8
-rwxr-xr-xsrc/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java11
-rw-r--r--src/java/org/apache/fop/render/awt/AWTRenderer.java20
-rw-r--r--src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java235
-rw-r--r--src/java/org/apache/fop/render/extensions/prepress/PageScale.java92
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java6
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java14
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java42
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DRenderer.java39
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java7
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java5
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLGenerator.java77
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPageDefinition.java6
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRenderer.java6
-rw-r--r--src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java4
-rw-r--r--src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java10
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java14
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java52
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java19
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFEventProducer.java7
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFEventProducer.xml3
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java5
-rw-r--r--src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java55
-rw-r--r--src/java/org/apache/fop/render/ps/FontResourceCache.java93
-rw-r--r--src/java/org/apache/fop/render/ps/PSBatikFlowTextElementBridge.java86
-rw-r--r--src/java/org/apache/fop/render/ps/PSBridgeContext.java113
-rw-r--r--src/java/org/apache/fop/render/ps/PSDocumentHandler.java46
-rw-r--r--src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java14
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java5
-rw-r--r--src/java/org/apache/fop/render/ps/PSRenderer.java6
-rw-r--r--src/java/org/apache/fop/render/ps/PSSVGFlowRootElementBridge.java86
-rw-r--r--src/java/org/apache/fop/render/ps/PSSVGHandler.java15
-rw-r--r--src/java/org/apache/fop/render/ps/PSTextElementBridge.java63
-rw-r--r--src/java/org/apache/fop/render/ps/PSTextPainter.java869
-rwxr-xr-xsrc/java/org/apache/fop/render/rtf/FOPRtfAttributes.java18
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java6
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java48
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPTranscoder.java143
-rw-r--r--src/java/org/apache/fop/svg/NativeTextPainter.java224
-rw-r--r--src/java/org/apache/fop/svg/PDFBridgeContext.java11
-rw-r--r--src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java23
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java7
-rw-r--r--src/java/org/apache/fop/svg/PDFTextPainter.java363
-rw-r--r--src/java/org/apache/fop/svg/PDFTranscoder.java92
-rw-r--r--src/java/org/apache/fop/tools/anttasks/SerializeHyphPattern.java178
-rw-r--r--src/java/org/apache/fop/util/ColorExt.java4
-rw-r--r--src/java/org/apache/fop/util/ColorUtil.java81
-rw-r--r--src/java/org/apache/fop/util/DOM2SAX.java3
-rw-r--r--src/java/org/apache/fop/util/bitmap/DitherUtil.java153
-rw-r--r--src/sandbox/org/apache/fop/render/svg/EmbeddedSVGImageHandler.java20
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java88
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPainter.java57
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGUtil.java78
-rw-r--r--status.xml97
-rw-r--r--test/fotree/testcases/pixel_length.fo33
-rw-r--r--test/fotree/testcases/pixel_length_96dpi.fo34
-rw-r--r--test/java/org/apache/fop/StandardTestSuite.java4
-rw-r--r--test/java/org/apache/fop/config/FOURIResolverTestCase.java1
-rw-r--r--test/java/org/apache/fop/config/UserConfigTestSuite.java1
-rw-r--r--test/java/org/apache/fop/fotreetest/FOTreeTester.java21
-rw-r--r--test/java/org/apache/fop/fotreetest/ResultCollector.java25
-rw-r--r--test/java/org/apache/fop/fotreetest/ext/AssertElement.java19
-rw-r--r--test/java/org/apache/fop/render/extensions/prepress/PageBoundariesTest.java176
-rw-r--r--test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java79
-rw-r--r--test/java/org/apache/fop/util/ColorUtilTestCase.java21
-rw-r--r--test/layoutengine/disabled-testcases.xml6
-rw-r--r--test/layoutengine/standard-testcases/afp-extension_1.xml17
-rw-r--r--test/layoutengine/standard-testcases/color_1.xml5
-rw-r--r--test/layoutengine/standard-testcases/flow_changing-ipd_1.xml97
-rw-r--r--test/layoutengine/standard-testcases/flow_changing-ipd_2.xml75
-rw-r--r--test/layoutengine/standard-testcases/flow_changing-ipd_3.xml86
-rw-r--r--test/layoutengine/standard-testcases/flow_changing-ipd_4.xml159
-rw-r--r--test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml101
-rw-r--r--test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml89
-rw-r--r--test/layoutengine/standard-testcases/footnote_in_table_2.xml4
-rw-r--r--test/layoutengine/standard-testcases/footnote_in_table_3.xml77
-rw-r--r--test/layoutengine/standard-testcases/footnote_in_table_fixed-height.xml67
-rw-r--r--test/layoutengine/standard-testcases/inline_block_nested_6.xml7
-rw-r--r--test/layoutengine/standard-testcases/keep_within-column_basic.xml155
-rw-r--r--test/layoutengine/standard-testcases/keep_within-page_multi-column_overflow.xml58
-rw-r--r--test/layoutengine/standard-testcases/marker_white-space_npe.xml77
-rw-r--r--test/layoutengine/standard-testcases/markers_empty_bug46960.xml54
-rw-r--r--test/layoutengine/standard-testcases/page-number-citation-last_break-after_bug45702.xml152
-rw-r--r--test/layoutengine/standard-testcases/table-row_keep-together.xml6
-rw-r--r--test/layoutengine/standard-testcases/table_in_marker_bug47101.xml82
-rw-r--r--test/layoutengine/standard-testcases/table_keep-together.xml4
-rw-r--r--test/resources/afp/F1SAMPLE.afpbin0 -> 834 bytes
269 files changed, 17719 insertions, 4009 deletions
diff --git a/build.properties b/build.properties
index c5e483204..9c6472cee 100644
--- a/build.properties
+++ b/build.properties
@@ -50,5 +50,6 @@
# layoutengine.disabled = test/layoutengine/disabled-testcases.txt
## Specify an alternate directory to scan for user supplied
-## hyphenation pattern files.
+## hyphenation pattern files and Unicode data files.
# user.hyph.dir = /home/bart/offo
+# unidata.dir = /usr/share/doc/Unicode/UNIDATA
diff --git a/build.xml b/build.xml
index e9cc16a21..467e29192 100644
--- a/build.xml
+++ b/build.xml
@@ -157,6 +157,8 @@ list of possible build targets.
<property name="fo.examples.force" value="false"/>
<property name="lib.dir" value="${basedir}/lib"/>
<property name="user.hyph.dir" value="${basedir}/hyph"/>
+ <property name="unidata.dir" value="${basedir}/UNIDATA"/>
+ <property name="hyph.stacksize" value="512k"/>
<property name="build.dir" value="${basedir}/build"/>
<property name="build.gensrc.dir" value="${build.dir}/gensrc"/>
<property name="build.classes.dir" value="${build.dir}/classes"/>
@@ -355,12 +357,13 @@ list of possible build targets.
<fileset dir="${src.java.dir}">
<include name="**/*.java"/>
<exclude name="org/apache/fop/render/*/**/*.java"/>
+ <exclude name="org/apache/fop/afp/**/*.java"/>
</fileset>
</eventResourceGenerator>
<fixcrlf file="${src.java.dir}/org/apache/fop/events/EventFormatter.xml" tab="remove" tablength="2"/>
<eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/afp/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml">
<fileset dir="${src.java.dir}">
- <include name="org/apache/fop/render/afp/**/*.java"/>
+ <include name="org/apache/fop/afp/**/*.java"/>
</fileset>
</eventResourceGenerator>
<fixcrlf file="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml" tab="remove" tablength="2"/>
@@ -424,21 +427,54 @@ list of possible build targets.
</copy>
</target>
<target name="compile" depends="compile-java, compile-copy-resources" description="Compiles the source code"/>
-<!-- =================================================================== -->
-<!-- compiles hyphenation patterns -->
-<!-- =================================================================== -->
- <target name="compile-hyphenation" depends="compile">
+ <!-- =================================================================== -->
+ <!-- Helper task to generate source files that have already been checked -->
+ <!-- into the repository. This task uses Unicode Character Database -->
+ <!-- files. This task need only be run when the latter files have been -->
+ <!-- updated. This target should never be part of the normal build -->
+ <!-- process. Output is UnicodeClasses.CLASSES_XML -->
+ <!-- (src/java/org/apache/fop/hyphenation/classes.xml). -->
+ <!-- =================================================================== -->
+ <target name="codegen-hyphenation-classes">
+ <javac destdir="${build.codegen-classes.dir}" fork="${javac.fork}" debug="${javac.debug}" deprecation="${javac.deprecation}" optimize="${javac.optimize}" source="${javac.source}" target="${javac.target}">
+ <src path="${src.codegen.dir}/unicode/java"/>
+ </javac>
+ <java classname="org.apache.fop.hyphenation.UnicodeClasses" resultproperty="classes.result" classpath="${build.codegen-classes.dir}">
+ <arg value="${src.dir}/java/org/apache/fop/hyphenation/classes.xml"/>
+ </java>
+ <condition property="classes.result.message" value="Generation of classes successful">
+ <not>
+ <isfailure code="${classes.result}"/>
+ </not>
+ </condition>
+ <condition property="classes.result.message" value="Generation of classes failed">
+ <isfailure code="${classes.result}"/>
+ </condition>
+ <echo message="${classes.result.message}"/>
+ </target>
+ <!-- =================================================================== -->
+ <!-- compiles hyphenation patterns -->
+ <!-- =================================================================== -->
+ <target name="compile-hyphenation" depends="compile" description="Compiles the hyphenation pattern files">
<path id="hyph-classpath">
<path refid="libs-build-classpath"/>
<pathelement location="${build.classes.dir}"/>
</path>
- <taskdef name="serHyph" classname="org.apache.fop.tools.anttasks.SerializeHyphPattern" classpathref="hyph-classpath"/>
<mkdir dir="${build.classes.dir}/hyph"/>
- <serHyph targetDir="${build.classes.dir}/hyph">
- <fileset dir="${user.hyph.dir}">
- <include name="*.xml"/>
- </fileset>
- </serHyph>
+ <java classname="org.apache.fop.hyphenation.SerializeHyphPattern" fork="true" resultproperty="hyph.result" classpathref="hyph-classpath">
+ <arg value="${user.hyph.dir}"/>
+ <arg value="${build.classes.dir}/hyph"/>
+ <jvmarg value="-Xss${hyph.stacksize}"/>
+ </java>
+ <condition property="hyph.result.message" value="Hyphenation successful">
+ <not>
+ <isfailure code="${hyph.result}"/>
+ </not>
+ </condition>
+ <condition property="hyph.result.message" value="Hyphenation failed">
+ <isfailure code="${hyph.result}"/>
+ </condition>
+ <echo message="${hyph.result.message}"/>
</target>
<target name="uptodate-jar-hyphenation" depends="compile-hyphenation">
<uptodate property="jar.hyphenation.uptodate" targetfile="${build.dir}/fop-hyph.jar">
@@ -1006,7 +1042,15 @@ NOTE:
<property name="javadoc.level" value=""/>
<echo message="Producing the javadoc files${javadoc.level}"/>
<mkdir dir="${build.javadocs.dir}"/>
- <javadoc packagenames="${javadoc.packages}" destdir="${build.javadocs.dir}" author="true" version="true" windowtitle="${Name} ${version} API" doctitle="Apache Formatting Objects Processor (FOP)" bottom="Copyright ${year} The Apache Software Foundation. All Rights Reserved." overview="${src.dir}/java/org/apache/fop/overview.html" use="true" failonerror="true" source="${javac.source}" public="${javadoc.public}" package="${javadoc.package}" private="${javadoc.private}" maxmemory="128M">
+ <javadoc failonerror="true" source="${javac.source}" destdir="${build.javadocs.dir}"
+ packagenames="${javadoc.packages}"
+ public="${javadoc.public}" package="${javadoc.package}" private="${javadoc.private}"
+ author="true" version="true" use="true"
+ windowtitle="${Name} ${version} API"
+ doctitle="Apache Formatting Objects Processor (FOP)"
+ bottom="Copyright ${year} The Apache Software Foundation. All Rights Reserved."
+ overview="${src.dir}/java/org/apache/fop/overview.html"
+ maxmemory="256M">
<header><![CDATA[${name} ${version}]]></header>
<footer><![CDATA[${name} ${version}]]></footer>
<classpath>
diff --git a/checkstyle-4.0.xml b/checkstyle-4.0.xml
index 19d4c33f0..19d4c33f0 100755..100644
--- a/checkstyle-4.0.xml
+++ b/checkstyle-4.0.xml
diff --git a/checkstyle-5.0.xml b/checkstyle-5.0.xml
new file mode 100644
index 000000000..242b71b90
--- /dev/null
+++ b/checkstyle-5.0.xml
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+<module name="Checker">
+ <module name="TreeWalker">
+ <module name="ArrayTypeStyleCheck">
+ <property name="javaStyle" value="true"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="ModifierOrderCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="RedundantModifierCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="METHOD_DEF, VARIABLE_DEF"/>
+ </module>
+ <module name="UpperEllCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="AvoidNestedBlocksCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="EmptyBlockCheck">
+ <property name="option" value="text"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF, LITERAL_FOR, LITERAL_TRY, LITERAL_WHILE, STATIC_INIT"/>
+ </module>
+ <module name="LeftCurlyCheck">
+ <property name="maxLineLength" value="100"/>
+ <property name="option" value="eol"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="CLASS_DEF, CTOR_DEF, INTERFACE_DEF, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF"/>
+ </module>
+ <module name="NeedBracesCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="LITERAL_DO, LITERAL_ELSE, LITERAL_IF, LITERAL_FOR, LITERAL_WHILE"/>
+ </module>
+ <module name="RightCurlyCheck">
+ <property name="option" value="same"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="LITERAL_CATCH, LITERAL_ELSE, LITERAL_TRY"/>
+ </module>
+ <module name="DoubleCheckedLockingCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="EmptyStatementCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="EqualsHashCodeCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="HiddenFieldCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="PARAMETER_DEF, VARIABLE_DEF"/>
+ <property name="ignoreConstructorParameter" value="true"/>
+ <property name="ignoreSetter" value="true"/>
+ </module>
+ <module name="InnerAssignmentCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="ASSIGN, BAND_ASSIGN, BOR_ASSIGN, BSR_ASSIGN, BXOR_ASSIGN, DIV_ASSIGN, MINUS_ASSIGN, MOD_ASSIGN, PLUS_ASSIGN, SL_ASSIGN, SR_ASSIGN, STAR_ASSIGN"/>
+ </module>
+ <module name="MissingSwitchDefaultCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="RedundantThrowsCheck">
+ <property name="allowSubclasses" value="false"/>
+ <property name="allowUnchecked" value="false"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="SimplifyBooleanExpressionCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="SimplifyBooleanReturnCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="FinalClassCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="HideUtilityClassConstructorCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="VisibilityModifierCheck">
+ <property name="packageAllowed" value="false"/>
+ <property name="protectedAllowed" value="true"/>
+ <property name="publicMemberPattern" value="^serialVersionUID"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="AvoidStarImportCheck">
+ <property name="severity" value="error"/>
+ </module>
+ <module name="JavadocMethodCheck">
+ <property name="allowMissingParamTags" value="false"/>
+ <property name="allowMissingReturnTag" value="false"/>
+ <property name="allowMissingThrowsTags" value="false"/>
+ <property name="allowThrowsTagsForSubclasses" value="false"/>
+ <property name="allowUndeclaredRTE" value="false"/>
+ <property name="scope" value="protected"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="JavadocTypeCheck">
+ <property name="scope" value="protected"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF"/>
+ </module>
+ <module name="JavadocVariableCheck">
+ <property name="scope" value="protected"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="ConstantNameCheck">
+ <property name="format" value="^[A-Z](_?[A-Z0-9]+)*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="LocalFinalVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="LocalVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="MemberNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="MethodNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="PackageNameCheck">
+ <property name="format" value="^[a-z]+(\.[a-zA-Z_][a-zA-Z0-9_]*)*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="ParameterNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="StaticVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="TypeNameCheck">
+ <property name="format" value="^[A-Z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF"/>
+ </module>
+ <module name="LineLengthCheck">
+ <property name="ignorePattern" value="^$"/>
+ <property name="max" value="100"/>
+ <property name="severity" value="warning"/>
+ <property name="tabWidth" value="4"/>
+ </module>
+ <module name="MethodLengthCheck">
+ <property name="max" value="150"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="ParameterNumberCheck">
+ <property name="max" value="7"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="EmptyForIteratorPadCheck">
+ <property name="option" value="nospace"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="NoWhitespaceAfterCheck">
+ <property name="allowLineBreaks" value="true"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="ARRAY_INIT, BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS"/>
+ </module>
+ <module name="NoWhitespaceBeforeCheck">
+ <property name="allowLineBreaks" value="true"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="SEMI, POST_DEC, POST_INC"/>
+ </module>
+ <module name="OperatorWrapCheck">
+ <property name="option" value="nl"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, SL, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN"/>
+ </module>
+ <module name="WhitespaceAfterCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="COMMA, SEMI"/>
+ </module>
+ <module name="WhitespaceAroundCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LCURLY, LE, LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN"/>
+ </module>
+ </module>
+ <module name="RegexpHeader">
+ <property name="headerFile" value="${samedir}/checkstyle.header"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="FileLengthCheck">
+ <property name="max" value="2000"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="FileTabCharacter">
+ <property name="severity" value="error"/>
+ </module>
+</module>
diff --git a/examples/fo/advanced/bleed-and-crop-marks.fo b/examples/fo/advanced/bleed-and-crop-marks.fo
new file mode 100644
index 000000000..dfc499267
--- /dev/null
+++ b/examples/fo/advanced/bleed-and-crop-marks.fo
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="ISO-8859-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.
+-->
+<!-- $Id$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
+ font-family="sans-serif" font-size="9pt">
+
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="business-card"
+ page-width="84mm" page-height="53mm"
+ fox:bleed="3mm" fox:crop-box="media-box" fox:crop-offset="10mm" fox:scale="4">
+ <fo:region-body margin-bottom="3mm" margin-left="3mm" margin-right="3mm"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+
+ <fo:page-sequence master-reference="business-card">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container id="crop-marks" absolute-position="fixed">
+ <fo:block line-height="1" font-size="0pt">
+ <fo:instream-foreign-object>
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="84mm" height="53mm" viewBox="0 0 84 53" overflow="visible">
+ <style type="text/css"><![CDATA[
+ .line { fill:none; stroke:black; stroke-width:0.1 }
+ .colorbox { stroke:black; stroke-width:0.1 }
+]]></style>
+
+ <defs>
+ <rect id="box" class="colorbox" width="5" height="4"/>
+ </defs>
+ <g id="cut-marks">
+ <polyline class="line" points="0,-3 0,-10" />
+ <polyline class="line" points="84,-3 84,-10" />
+
+ <polyline class="line" points="-3,0 -10,0" />
+ <polyline class="line" points="-3,53 -10,53" />
+
+ <polyline class="line" points="0,56 0,63" />
+ <polyline class="line" points="84,56 84,63" />
+
+ <polyline class="line" points="87,0 94,0" />
+ <polyline class="line" points="87,53 94,53" />
+ </g>
+
+ <g id="grays" transform="translate(88,4)">
+ <use xlink:href="#box" y="0" style="fill:rgb(0%,0%,0%)"/>
+ <use xlink:href="#box" y="4" style="fill:rgb(10%,10%,10%)"/>
+ <use xlink:href="#box" y="8" style="fill:rgb(20%,20%,20%)"/>
+ <use xlink:href="#box" y="12" style="fill:rgb(30%,30%,30%)"/>
+ <use xlink:href="#box" y="16" style="fill:rgb(40%,40%,40%)"/>
+ <use xlink:href="#box" y="20" style="fill:rgb(50%,50%,50%)"/>
+ <use xlink:href="#box" y="24" style="fill:rgb(60%,60%,60%)"/>
+ <use xlink:href="#box" y="28" style="fill:rgb(70%,70%,70%)"/>
+ <use xlink:href="#box" y="32" style="fill:rgb(80%,80%,80%)"/>
+ <use xlink:href="#box" y="36" style="fill:rgb(90%,90%,90%)"/>
+ <use xlink:href="#box" y="40" style="fill:rgb(100%,100%,100%)"/>
+ </g>
+
+ <g id="cyan" transform="translate(4,-9)">
+ <use xlink:href="#box" x="0" style="fill:rgb(0%,100%,100%)"/>
+ <use xlink:href="#box" x="5" style="fill:rgb(5%,100%,100%)"/>
+ <use xlink:href="#box" x="10" style="fill:rgb(25%,100%,100%)"/>
+ <use xlink:href="#box" x="15" style="fill:rgb(50%,100%,100%)"/>
+ <use xlink:href="#box" x="20" style="fill:rgb(75%,100%,100%)"/>
+ <use xlink:href="#box" x="25" style="fill:rgb(95%,100%,100%)"/>
+ </g>
+ <g id="magenta" transform="translate(50,-9)">
+ <use xlink:href="#box" x="0" style="fill:rgb(100%,0%,100%)"/>
+ <use xlink:href="#box" x="5" style="fill:rgb(100%,5%,100%)"/>
+ <use xlink:href="#box" x="10" style="fill:rgb(100%,25%,100%)"/>
+ <use xlink:href="#box" x="15" style="fill:rgb(100%,50%,100%)"/>
+ <use xlink:href="#box" x="20" style="fill:rgb(100%,75%,100%)"/>
+ <use xlink:href="#box" x="25" style="fill:rgb(100%,95%,100%)"/>
+ </g>
+ <g id="yellow" transform="translate(4,58)">
+ <use xlink:href="#box" x="0" style="fill:rgb(100%,100%,0%)"/>
+ <use xlink:href="#box" x="5" style="fill:rgb(100%,100%,5%)"/>
+ <use xlink:href="#box" x="10" style="fill:rgb(100%,100%,25%)"/>
+ <use xlink:href="#box" x="15" style="fill:rgb(100%,100%,50%)"/>
+ <use xlink:href="#box" x="20" style="fill:rgb(100%,100%,75%)"/>
+ <use xlink:href="#box" x="25" style="fill:rgb(100%,100%,95%)"/>
+ </g>
+ <g id="base-colors" transform="translate(50,58)">
+ <use xlink:href="#box" x="0" style="fill:red"/>
+ <use xlink:href="#box" x="5" style="fill:green"/>
+ <use xlink:href="#box" x="10" style="fill:blue"/>
+ <use xlink:href="#box" x="15" style="fill:cyan"/>
+ <use xlink:href="#box" x="20" style="fill:magenta"/>
+ <use xlink:href="#box" x="25" style="fill:yellow"/>
+ </g>
+
+ </svg>
+ </fo:instream-foreign-object>
+ </fo:block>
+ </fo:block-container>
+
+ <fo:block-container id="background" absolute-position="fixed"
+ inline-progression-dimension="84mm" block-progression-dimension="14mm"
+ overflow="visible">
+ <fo:block line-height="1" font-size="0pt">
+ <fo:instream-foreign-object>
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ width="84mm" height="14mm" viewBox="0 0 84 14" overflow="visible">
+
+ <defs>
+ <linearGradient id="MyGradient">
+ <stop offset="0%" stop-color="#FF0000"/>
+ <stop offset="30%" stop-color="#FF0000"/>
+ <stop offset="55%" stop-color="#FFFFFF"/>
+ <stop offset="100%" stop-color="#FFFFFF"/>
+ </linearGradient>
+ </defs>
+ <rect x="-3" y="-3" width="87" height="17" style="fill:url(#MyGradient); stroke:none"/>
+ </svg>
+ </fo:instream-foreign-object>
+ </fo:block>
+ </fo:block-container>
+
+ <fo:block-container block-progression-dimension="14mm" display-align="center">
+ <fo:block font-weight="bold" font-size="9mm" line-height="1.5" color="white">ACME</fo:block>
+ </fo:block-container>
+ <fo:block start-indent="3mm" end-indent="3mm"/>
+ <fo:block text-align="end" space-before="3mm" space-before.conditionality="retain">
+ <fo:block font-size="14pt" font-weight="bold" space-after="2mm">
+ Pepé Le Pew
+ </fo:block>
+ <fo:block font-size="12pt" space-after="4mm">
+ Expert d'Amour
+ </fo:block>
+ </fo:block>
+ <fo:block>
+ <fo:block>13 Pleasantstreet</fo:block>
+ <fo:block>Acme Acres</fo:block>
+ <fo:block>+1 (23) 456-7890</fo:block>
+ <fo:block>pepe@looney.toon</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/fixsvnprops.sh b/fixsvnprops.sh
index c5688a238..72a3efacb 100755
--- a/fixsvnprops.sh
+++ b/fixsvnprops.sh
@@ -1,7 +1,14 @@
-#!/bin/sh
+#!/bin/bash
-find . -name \*.java -exec svn ps svn:keywords "Id" '{}' \;
-find . -name \*.xml -exec svn ps svn:keywords "Revision Id" '{}' \;
+for i in $(find . -name \*.java)
+do
+ svn pg svn:keywords $i | grep Id > /dev/null || svn ps svn:keywords "Id" $i
+done
+
+for i in $(find . -name \*.xml)
+do
+ svn pg svn:keywords $i | grep "Revision Id" > /dev/null || svn ps svn:keywords "Revision Id" $i
+done
find . -name \*.java -exec svn ps svn:eol-style native '{}' \;
find . -name \*.xml -exec svn ps svn:eol-style native '{}' \;
diff --git a/forrest.properties b/forrest.properties
index 62f777d63..6e9872d96 100644
--- a/forrest.properties
+++ b/forrest.properties
@@ -92,7 +92,7 @@ forrest.validate.skins.stylesheets=${forrest.validate.skins}
# (DEBUG, INFO, WARN, ERROR, FATAL_ERROR)
#project.debuglevel=ERROR
# Max memory to allocate to Java
-#forrest.maxmemory=64m
+forrest.maxmemory=128m
# Any other arguments to pass to the JVM. For example, to run on an X-less
# server, set to -Djava.awt.headless=true
#forrest.jvmargs=
diff --git a/hyph/hyphenation.dtd b/hyph/hyphenation.dtd
index fcd394116..d2171df5f 100644
--- a/hyph/hyphenation.dtd
+++ b/hyph/hyphenation.dtd
@@ -26,7 +26,7 @@
<!ELEMENT hyphen-char EMPTY>
<!ATTLIST hyphen-char value CDATA #REQUIRED>
-<!-- Default minimun length in characters of hyphenated word fragments
+<!-- Default minimum length in characters of hyphenated word fragments
before and after the line break. For some languages this is not
only for aesthetic purposes, wrong hyphens may be generated if this
is not accounted for.
@@ -55,7 +55,7 @@
<!-- The hyphenation patterns, space separated. A pattern is made of 'equivalent'
characters as described before, between any two word characters a digit
in the range 0 to 9 may be specified. The absence of a digit is equivalent
- to zero. The '.' character is reserved to indicate begining or ending
+ to zero. The '.' character is reserved to indicate beginning or ending
of words. -->
<!ELEMENT patterns (#PCDATA)>
diff --git a/lib/xmlgraphics-commons-1.4svn.jar b/lib/xmlgraphics-commons-1.4svn.jar
index 11a3d97b3..7624e0a42 100644
--- a/lib/xmlgraphics-commons-1.4svn.jar
+++ b/lib/xmlgraphics-commons-1.4svn.jar
Binary files differ
diff --git a/src/codegen/unicode/java/org/apache/fop/hyphenation/UnicodeClasses.java b/src/codegen/unicode/java/org/apache/fop/hyphenation/UnicodeClasses.java
new file mode 100644
index 000000000..4b3b9f734
--- /dev/null
+++ b/src/codegen/unicode/java/org/apache/fop/hyphenation/UnicodeClasses.java
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.hyphenation;
+
+import java.io.BufferedReader;
+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.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.fop.util.License;
+
+/**
+ * Create the default classes file classes.xml,
+ * for use in building hyphenation patterns
+ * from pattern files which do not contain their own classes.
+ * The class contains three methods to do that.
+ * The method fromJava gets its infirmation from Java's compiled-in Unicode Character Database,
+ * the method fromUCD gets its information from the UCD files,
+ * the method fromTeX gets its information from the file unicode-letters-XeTeX.tex,
+ * which is the basis of XeTeX's unicode support.
+ * In the build file only the method from UCD is used;
+ * the other two methods are there for demonstration.
+ * The methods fromJava and fromTeX are commented out because they are not Java 1.4 compliant.
+ */
+public final class UnicodeClasses {
+
+ public static String UNICODE_DIR = "http://www.unicode.org/Public/UNIDATA/";
+
+ /**
+ * Disallow constructor for this utility class
+ */
+ private UnicodeClasses() { }
+
+ /**
+ * Write a comment that this is a generated file,
+ * and instructions on how to generate it
+ * @param w the writer which writes the comment
+ * @throws IOException if the write operation fails
+ */
+ public static void writeGenerated(Writer w) throws IOException {
+ w.write("<!-- !!! THIS IS A GENERATED FILE !!! -->\n");
+ w.write("<!-- If updates are needed, then: -->\n");
+ w.write("<!-- * run 'ant codegen-hyphenation-classes', -->\n");
+ w.write("<!-- which will generate a new file classes.xml -->\n");
+ w.write("<!-- in 'src/java/org/apache/fop/hyphenation' -->\n");
+ w.write("<!-- * commit the changed file -->\n");
+ }
+
+ /**
+ * Generate classes.xml from Java's compiled-in Unicode Character Database
+ * @param hexcode whether to prefix each class with the hexcode (only for debugging purposes)
+ * @param outfilePath output file
+ * @throws IOException
+ */
+ public static void fromJava(boolean hexcode, String outfilePath) throws IOException {
+ File f = new File(outfilePath);
+ if (f.exists()) {
+ f.delete();
+ }
+ f.createNewFile();
+ FileOutputStream fw = new FileOutputStream(f);
+ OutputStreamWriter ow = new OutputStreamWriter(fw, "utf-8");
+ int maxChar;
+ maxChar = Character.MAX_VALUE;
+
+ ow.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ License.writeXMLLicenseId(ow);
+ ow.write("\n");
+ writeGenerated(ow);
+ ow.write("\n");
+ ow.write("<classes>\n");
+ // loop over the first Unicode plane
+ for (int code = Character.MIN_VALUE; code <= maxChar; ++code) {
+
+ // skip surrogate area
+ if (code == Character.MIN_SURROGATE) {
+ code = Character.MAX_SURROGATE;
+ continue;
+ }
+
+ // we are only interested in LC, UC and TC letters which are their own LC,
+ // and in 'other letters'
+ if (!(((Character.isLowerCase(code) || Character.isUpperCase(code)
+ || Character.isTitleCase(code))
+ && code == Character.toLowerCase(code))
+ || Character.getType(code) == Character.OTHER_LETTER)) {
+ continue;
+ }
+
+ // skip a number of blocks
+ Character.UnicodeBlock ubi = Character.UnicodeBlock.of(code);
+ if (ubi.equals(Character.UnicodeBlock.SUPERSCRIPTS_AND_SUBSCRIPTS)
+ || ubi.equals(Character.UnicodeBlock.LETTERLIKE_SYMBOLS)
+ || ubi.equals(Character.UnicodeBlock.ALPHABETIC_PRESENTATION_FORMS)
+ || ubi.equals(Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS)
+ || ubi.equals(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS)
+ || ubi.equals(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A)
+ || ubi.equals(Character.UnicodeBlock.HANGUL_SYLLABLES)) {
+ continue;
+ }
+
+ int uppercode = Character.toUpperCase(code);
+ int titlecode = Character.toTitleCase(code);
+ StringBuilder s = new StringBuilder();
+ if (hexcode) {
+ s.append("0x" + Integer.toHexString(code) + " ");
+ }
+ s.append(Character.toChars(code));
+ if (uppercode != code) {
+ s.append(Character.toChars(uppercode));
+ }
+ if (titlecode != code && titlecode != uppercode) {
+ s.append(Character.toChars(titlecode));
+ }
+ ow.write(s.toString() + "\n");
+ }
+ ow.write("</classes>\n");
+ ow.flush();
+ ow.close();
+ }
+
+
+ /**
+ * The column numbers in the UCD file
+ */
+ public static final int UNICODE = 0, GENERAL_CATEGORY = 2, SIMPLE_UPPERCASE_MAPPING = 12,
+ SIMPLE_LOWERCASE_MAPPING = 13, SIMPLE_TITLECASE_MAPPING = 14, NUM_FIELDS = 15;
+
+ /**
+ * Generate classes.xml from Unicode Character Database files
+ * @param hexcode whether to prefix each class with the hexcode (only for debugging purposes)
+ * @param unidataPath path to the directory with UCD files
+ * @param outfilePath output file
+ * @throws IOException if the input files are not found
+ * @throws URISyntaxException
+ * @throws FOPException
+ */
+ public static void fromUCD(boolean hexcode, String unidataPath, String outfilePath)
+ throws IOException, URISyntaxException {
+ URI unidata;
+ if (unidataPath.endsWith("/")) {
+ unidata = new URI(unidataPath);
+ } else {
+ unidata = new URI(unidataPath + "/");
+ }
+ String scheme = unidata.getScheme();
+ if (scheme == null || !(scheme.equals("file") || scheme.equals("http"))) {
+ throw new FileNotFoundException
+ ("URI with file or http scheme required for UNIDATA input directory");
+ }
+
+ File f = new File(outfilePath);
+ if (f.exists()) {
+ f.delete();
+ }
+ f.createNewFile();
+ FileOutputStream fw = new FileOutputStream(f);
+ OutputStreamWriter ow = new OutputStreamWriter(fw, "utf-8");
+
+ URI inuri = unidata.resolve("Blocks.txt");
+ InputStream inis = null;
+ if (scheme.equals("file")) {
+ File in = new File(inuri);
+ inis = new FileInputStream(in);
+ } else if (scheme.equals("http")) {
+ inis = inuri.toURL().openStream();
+ }
+ InputStreamReader insr = new InputStreamReader(inis, "utf-8");
+ BufferedReader inbr = new BufferedReader(insr);
+ Map blocks = new HashMap();
+ for (String line = inbr.readLine(); line != null; line = inbr.readLine()) {
+ if (line.startsWith("#") || line.matches("^\\s*$")) {
+ continue;
+ }
+ String[] parts = line.split(";");
+ String block = parts[1].trim();
+ String[] indices = parts[0].split("\\.\\.");
+ int[] ind = {Integer.parseInt(indices[0], 16), Integer.parseInt(indices[1], 16)};
+ blocks.put(block, ind);
+ }
+ inbr.close();
+
+ inuri = unidata.resolve("UnicodeData.txt");
+ if (scheme.equals("file")) {
+ File in = new File(inuri);
+ inis = new FileInputStream(in);
+ } else if (scheme.equals("http")) {
+ inis = inuri.toURL().openStream();
+ }
+ insr = new InputStreamReader(inis, "utf-8");
+ inbr = new BufferedReader(insr);
+ int maxChar;
+ maxChar = Character.MAX_VALUE;
+
+ ow.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ License.writeXMLLicenseId(ow);
+ ow.write("\n");
+ writeGenerated(ow);
+ ow.write("\n");
+ ow.write("<classes>\n");
+ for (String line = inbr.readLine(); line != null; line = inbr.readLine()) {
+ String[] fields = line.split(";", NUM_FIELDS);
+ int code = Integer.parseInt(fields[UNICODE], 16);
+ if (code > maxChar) {
+ break;
+ }
+ if (((fields[GENERAL_CATEGORY].equals("Ll") || fields[GENERAL_CATEGORY].equals("Lu")
+ || fields[GENERAL_CATEGORY].equals("Lt"))
+ && ("".equals(fields[SIMPLE_LOWERCASE_MAPPING])
+ || fields[UNICODE].equals(fields[SIMPLE_LOWERCASE_MAPPING])))
+ || fields[GENERAL_CATEGORY].equals("Lo")) {
+ String[] blockNames = {"Superscripts and Subscripts",
+ "Letterlike Symbols",
+ "Alphabetic Presentation Forms",
+ "Halfwidth and Fullwidth Forms",
+ "CJK Unified Ideographs",
+ "CJK Unified Ideographs Extension A",
+ "Hangul Syllables"};
+ int j;
+ for (j = 0; j < blockNames.length; ++j) {
+ int[] ind = (int[]) blocks.get(blockNames[j]);
+ if (code >= ind[0] && code <= ind[1]) {
+ break;
+ }
+ }
+ if (j < blockNames.length) {
+ continue;
+ }
+
+ int uppercode = -1, titlecode = -1;
+ if (!"".equals(fields[SIMPLE_UPPERCASE_MAPPING])) {
+ uppercode = Integer.parseInt(fields[SIMPLE_UPPERCASE_MAPPING], 16);
+ }
+ if (!"".equals(fields[SIMPLE_TITLECASE_MAPPING])) {
+ titlecode = Integer.parseInt(fields[SIMPLE_TITLECASE_MAPPING], 16);
+ }
+ StringBuilder s = new StringBuilder();
+ if (hexcode) {
+ s.append("0x" + fields[UNICODE].replaceFirst("^0+", "").toLowerCase() + " ");
+ }
+ s.append(Character.toChars(code));
+ if (uppercode != -1 && uppercode != code) {
+ s.append(Character.toChars(uppercode));
+ }
+ if (titlecode != -1 && titlecode != code && titlecode != uppercode) {
+ s.append(Character.toChars(titlecode));
+ }
+ ow.write(s.toString() + "\n");
+ }
+ }
+ ow.write("</classes>\n");
+ ow.flush();
+ ow.close();
+ inbr.close();
+ }
+
+ /**
+ * Generate classes.xml from XeTeX's Unicode letters file
+ * @param hexcode whether to prefix each class with the hexcode (only for debugging purposes)
+ * @param lettersPath path to XeTeX's Unicode letters file unicode-letters-XeTeX.tex
+ * @param outfilePath output file
+ * @throws IOException
+ */
+ public static void fromTeX(boolean hexcode, String lettersPath, String outfilePath)
+ throws IOException {
+ File in = new File(lettersPath);
+
+ File f = new File(outfilePath);
+ if (f.exists()) {
+ f.delete();
+ }
+ f.createNewFile();
+ FileOutputStream fw = new FileOutputStream(f);
+ OutputStreamWriter ow = new OutputStreamWriter(fw, "utf-8");
+
+ FileInputStream inis = new FileInputStream(in);
+ InputStreamReader insr = new InputStreamReader(inis, "utf-8");
+ BufferedReader inbr = new BufferedReader(insr);
+
+ ow.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ License.writeXMLLicenseId(ow);
+ ow.write("\n");
+ writeGenerated(ow);
+ ow.write("\n");
+ ow.write("<classes>\n");
+ for (String line = inbr.readLine(); line != null; line = inbr.readLine()) {
+ String[] codes = line.split("\\s+");
+ if (!(codes[0].equals("\\L") || codes[0].equals("\\l"))) {
+ continue;
+ }
+ if (codes.length == 3) {
+ ow.write("\"" + line + "\" has two codes");
+ continue;
+ }
+ if (codes[0].equals("\\l") && codes.length != 2) {
+ ow.write("\"" + line + "\" should have one code");
+ continue;
+ }
+ else if (codes[0].equals("\\L") && codes.length != 4) {
+ ow.write("\"" + line + "\" should have three codes");
+ continue;
+ }
+ if (codes[0].equals("\\l") || (codes[0].equals("\\L") && codes[1].equals(codes[3]))) {
+ StringBuilder s = new StringBuilder();
+ if (hexcode) {
+ s.append("0x" + codes[1].replaceFirst("^0+", "").toLowerCase() + " ");
+ }
+ s.append(Character.toChars(Integer.parseInt(codes[1], 16)));
+ if (codes[0].equals("\\L")) {
+ s.append(Character.toChars(Integer.parseInt(codes[2], 16)));
+ }
+ ow.write(s.toString() + "\n");
+ }
+ }
+ ow.write("</classes>\n");
+ ow.flush();
+ ow.close();
+ inbr.close();
+ }
+
+
+ /**
+ * @param args [--hexcode] [--java|--ucd|--tex] outfile [infile]
+ * @throws IOException if the input file cannot be found
+ * @throws URISyntaxException if the input URI is incorrect
+ */
+ public static void main(String[] args) throws IOException, URISyntaxException {
+ String type = "ucd", prefix = "--", infile = null, outfile = null;
+ boolean hexcode = false;
+ int i;
+ for (i = 0; i < args.length && args[i].startsWith(prefix); ++i) {
+ String option = args[i].substring(prefix.length());
+ if (option.equals("java") || option.equals("ucd") || option.equals("tex")) {
+ type = option;
+ } else if (option.equals("hexcode")) {
+ hexcode = true;
+ } else {
+ System.err.println("Unknown option: " + option);
+ System.exit(1);
+ }
+ }
+ if (i < args.length) {
+ outfile = args[i];
+ } else {
+ System.err.println("Output file is required; aborting");
+ System.exit(1);
+ }
+ if (++i < args.length) {
+ infile = args[i];
+ }
+
+ if (type.equals("java") && infile != null) {
+ System.err.println("Type java does not allow an infile");
+ System.exit(1);
+ } else if (type.equals("ucd") && infile == null) {
+ infile = UNICODE_DIR;
+ } else if (type.equals("tex") && infile == null) {
+ System.err.println("Type tex requires an input file");
+ System.exit(1);
+ }
+ if (type.equals("java")) {
+ fromJava(hexcode, outfile);
+ } else if (type.equals("ucd")) {
+ fromUCD(hexcode, infile, outfile);
+ } else if (type.equals("tex")) {
+ fromTeX(hexcode, infile, outfile);
+ } else {
+ System.err.println("Unknown type: " + type + ", nothing done");
+ System.exit(1);
+ }
+ }
+
+}
diff --git a/src/codegen/unicode/java/org/apache/fop/text/linebreak/GenerateLineBreakUtils.java b/src/codegen/unicode/java/org/apache/fop/text/linebreak/GenerateLineBreakUtils.java
index cb0288334..f96b7484f 100644
--- a/src/codegen/unicode/java/org/apache/fop/text/linebreak/GenerateLineBreakUtils.java
+++ b/src/codegen/unicode/java/org/apache/fop/text/linebreak/GenerateLineBreakUtils.java
@@ -31,6 +31,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.fop.util.License;
+
/**
* <p>Utility for generating a Java class representing line break properties
* from the Unicode property files.</p>
@@ -226,31 +228,14 @@ public class GenerateLineBreakUtils {
int idx = 0;
StringBuffer doStaticLinkCode = new StringBuffer();
PrintWriter out = new PrintWriter(new FileWriter(outFileName));
- out.println("/*");
- out.println(" * Licensed to the Apache Software Foundation (ASF) under one or more");
- out.println(" * contributor license agreements. See the NOTICE file distributed with");
- out.println(" * this work for additional information regarding copyright ownership.");
- out.println(" * The ASF licenses this file to You under the Apache License, Version 2.0");
- out.println(" * (the \"License\"); you may not use this file except in compliance with");
- out.println(" * the License. You may obtain a copy of the License at");
- out.println(" * ");
- out.println(" * http://www.apache.org/licenses/LICENSE-2.0");
- out.println(" * ");
- out.println(" * Unless required by applicable law or agreed to in writing, software");
- out.println(" * distributed under the License is distributed on an \"AS IS\" BASIS,");
- out.println(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
- out.println(" * See the License for the specific language governing permissions and");
- out.println(" * limitations under the License.");
- out.println(" */");
- out.println();
- out.println("/* $Id$ */");
+ License.writeJavaLicenseId(out);
out.println();
out.println("package org.apache.fop.text.linebreak;");
out.println();
- out.println("/* ");
- out.println(" * !!! THIS IS A GENERATED FILE !!! ");
+ out.println("/*");
+ out.println(" * !!! THIS IS A GENERATED FILE !!!");
out.println(" * If updates to the source are needed, then:");
- out.println(" * - apply the necessary modifications to ");
+ out.println(" * - apply the necessary modifications to");
out.println(" * 'src/codegen/unicode/java/org/apache/fop/text/linebreak/GenerateLineBreakUtils.java'");
out.println(" * - run 'ant codegen-unicode', which will generate a new LineBreakUtils.java");
out.println(" * in 'src/java/org/apache/fop/text/linebreak'");
@@ -276,7 +261,7 @@ public class GenerateLineBreakUtils {
boolean printComma = false;
for (int i = 1; i <= lineBreakPropertyValueCount; i++) {
if (printComma) {
- out.println(", ");
+ out.println(",");
} else {
printComma = true;
}
@@ -376,8 +361,13 @@ public class GenerateLineBreakUtils {
for (int i = 0; i < lineBreakPropertyShortNames.size(); i++) {
name = (String)lineBreakPropertyShortNames.get(i);
if (printComma) {
- out.print(", ");
- lineLength++;
+ if (lineLength <= MAX_LINE_LENGTH - 2) {
+ out.print(", ");
+ } else {
+ out.print(",");
+ }
+ // count the space anyway to force a linebreak if the comma causes lineLength == MAX_LINE_LENGTH
+ lineLength += 2;
} else {
printComma = true;
}
@@ -418,7 +408,7 @@ public class GenerateLineBreakUtils {
out.println("};");
out.println();
out.println(" /**");
- out.println(" * Return the short name for the linebreak property corresponding ");
+ out.println(" * Return the short name for the linebreak property corresponding");
out.println(" * to the given symbolic constant.");
out.println(" *");
out.println(" * @param i the numeric value of the linebreak property");
@@ -433,7 +423,7 @@ public class GenerateLineBreakUtils {
out.println(" }");
out.println();
out.println(" /**");
- out.println(" * Return the long name for the linebreak property corresponding ");
+ out.println(" * Return the long name for the linebreak property corresponding");
out.println(" * to the given symbolic constant.");
out.println(" *");
out.println(" * @param i the numeric value of the linebreak property");
@@ -458,7 +448,7 @@ public class GenerateLineBreakUtils {
out.println(" }");
out.println();
out.println(" /**");
- out.println(" * Return the break class constant for the given pair of linebreak ");
+ out.println(" * Return the break class constant for the given pair of linebreak");
out.println(" * property constants.");
out.println(" *");
out.println(" * @param lineBreakPropertyBefore the linebreak property for the first character");
diff --git a/src/codegen/unicode/java/org/apache/fop/util/License.java b/src/codegen/unicode/java/org/apache/fop/util/License.java
new file mode 100644
index 000000000..b6e3db8a4
--- /dev/null
+++ b/src/codegen/unicode/java/org/apache/fop/util/License.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+
+/**
+ * Write the Apache license text in various forms
+ */
+public final class License {
+
+ /**
+ * The Apache license text as a string array
+ */
+ public static final String[] license
+ = {"Licensed to the Apache Software Foundation (ASF) under one or more",
+ "contributor license agreements. See the NOTICE file distributed with",
+ "this work for additional information regarding copyright ownership.",
+ "The ASF licenses this file to You under the Apache License, Version 2.0",
+ "(the \"License\"); you may not use this file except in compliance with",
+ "the License. You may obtain a copy of the License at",
+ "",
+ " http://www.apache.org/licenses/LICENSE-2.0",
+ "",
+ "Unless required by applicable law or agreed to in writing, software",
+ "distributed under the License is distributed on an \"AS IS\" BASIS,",
+ "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.",
+ "See the License for the specific language governing permissions and",
+ "limitations under the License."
+ };
+
+ /**
+ * The subversion Id keyword line
+ */
+ public static final String id = "$Id$";
+
+ /**
+ * Calculate the maximum line length in the Apache license text
+ * for use in formatting
+ */
+ private static int MAX_LENGTH;
+ static {
+ int j = 0;
+ for (int i = 0; i < license.length; ++i) {
+ if (j < license[i].length()) {
+ j = license[i].length();
+ }
+ }
+ MAX_LENGTH = j;
+ }
+
+ /**
+ * Write the Apache license text as commented lines for a Java file
+ * @param w the writer which writes the comment
+ * @throws IOException if the write operation fails
+ */
+ public static void writeJavaLicenseId(Writer w) throws IOException {
+ w.write("/*\n");
+ for (int i = 0; i < license.length; ++i) {
+ if (license[i].equals("")) {
+ w.write(" *\n");
+ } else {
+ w.write(" * " + license[i] + "\n");
+ }
+ }
+ w.write(" */\n");
+ w.write("\n");
+ w.write("/* " + id + " */\n");
+ }
+
+ /**
+ * Write the Apache license text as commented lines for an XML file
+ * @param w the writer which writes the comment
+ * @throws IOException if the write operation fails
+ */
+ public static void writeXMLLicenseId(Writer w) throws IOException {
+ for (int i = 0; i < license.length; ++i) {
+ w.write(String.format("<!-- %-" + MAX_LENGTH + "s -->\n", new Object[] {license[i]}));
+ }
+ w.write("\n");
+ w.write("<!-- " + id + " -->\n");
+ }
+
+ /**
+ * For testing purposes
+ * @param args optional, --java or --xml
+ * @throws IOException if the write operation fails
+ */
+ public static void main(String[] args) throws IOException {
+ StringWriter w = new StringWriter();
+ if (args.length == 0 || args[0].equals("--java")) {
+ writeJavaLicenseId(w);
+ } else if (args[0].equals("--xml")) {
+ writeXMLLicenseId(w);
+ }
+ System.out.println(w.toString());
+ }
+
+}
diff --git a/src/documentation/content/xdocs/compliance.ihtml b/src/documentation/content/xdocs/compliance.ihtml
index 781299361..0b3a7706a 100644
--- a/src/documentation/content/xdocs/compliance.ihtml
+++ b/src/documentation/content/xdocs/compliance.ihtml
@@ -533,11 +533,11 @@
<td class="no">no</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
<td align="center">&nbsp;</td>
</tr>
@@ -552,11 +552,11 @@
<td class="no">no</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
<td align="center">&nbsp;</td>
</tr>
@@ -870,11 +870,11 @@
<td class="no">no</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
- <td class="no">no</td>
+ <td class="yes">yes</td>
<td align="center">&nbsp;</td>
</tr>
@@ -1714,11 +1714,11 @@
<td class="partial">partial</td>
- <td class="partial">partial</td>
+ <td class="yes">yes</td>
<td align="left">
<ul>
- <li>[0.94 and later] Only works as expected with inline-level content.</li>
+ <li>[0.95] Only works as expected with inline-level content.</li>
</ul>
</td>
</tr>
@@ -4827,7 +4827,7 @@
<li>[0.95] works on all implemented FOs, except list- and inline-level
FOs.</li>
<li>[Trunk] does not work on inline-level FOs.</li>
-
+
<li>[0.95 and earlier] &lt;integer&gt; values are not supported.</li>
<li>[Trunk] minimal support for &lt;integer&gt; value.</li>
</ul>
diff --git a/src/documentation/content/xdocs/quickstartguide.xml b/src/documentation/content/xdocs/quickstartguide.xml
index ca205f5a2..7993e4bde 100644
--- a/src/documentation/content/xdocs/quickstartguide.xml
+++ b/src/documentation/content/xdocs/quickstartguide.xml
@@ -15,13 +15,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- $Id: gethelp.xml 627324 2008-02-13 09:35:01Z maxberger $ -->
+<!-- $Id$ -->
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.3//EN" "http://forrest.apache.org/dtd/document-v13.dtd">
<document>
<header>
<title>FOP: Quick Start Guide</title>
<subtitle>Everything you need to start using and appreciating Apache FOP quickly.</subtitle>
- <version>$Revision: 627324 $</version>
+ <version>$Revision$</version>
</header>
<body>
<section id="essentials">
diff --git a/src/documentation/content/xdocs/resources.xml b/src/documentation/content/xdocs/resources.xml
index 252605d42..db45d7437 100644
--- a/src/documentation/content/xdocs/resources.xml
+++ b/src/documentation/content/xdocs/resources.xml
@@ -247,6 +247,7 @@
[software] <jump href="http://www.ahmadsoft.org/fopbridge.html">FOP Bridge</jump> is an Eclipse plug-in
that allows to convert FO documents using Apache FOP directly inside the Eclipse workbench (LGPL).
</li>
+ <li>[software] <jump href="http://maven.apache.org/doxia/doxia/doxia-modules/doxia-module-fo/">Maven Doxia FO Module</jump>. Doxia is a content generation framework which aims to provide its users with powerful techniques for generating static and dynamic content: Doxia can be used in web-based publishing context to generate static sites, in addition to being incorporated into dynamic content generation systems like blogs, wikis and content management systems. Doxia provides a module providing XSL Formatting Objects.</li>
</ul>
</section>
</section>
diff --git a/src/documentation/content/xdocs/trunk/embedding.xml b/src/documentation/content/xdocs/trunk/embedding.xml
index 95e96104b..362062bbe 100644
--- a/src/documentation/content/xdocs/trunk/embedding.xml
+++ b/src/documentation/content/xdocs/trunk/embedding.xml
@@ -557,6 +557,9 @@ fopFactory.setUserConfig(new File("C:/Temp/mycfg.xml"));]]></source>
Fine-tune your stylesheet to make the XSLT process more efficient and to create XSL-FO that can
be processed by FOP more efficiently. Less is more: Try to make use of property inheritance where possible.
</li>
+ <li>
+ You may also wish to consider trying to reduce <a href="http://xmlgraphics.apache.org/fop/trunk/running.html#memory">memory usage</a>.
+ </li>
</ul>
</section>
<section id="multithreading">
diff --git a/src/documentation/content/xdocs/trunk/events.xml b/src/documentation/content/xdocs/trunk/events.xml
index 51a1bb6cc..dc08b0099 100644
--- a/src/documentation/content/xdocs/trunk/events.xml
+++ b/src/documentation/content/xdocs/trunk/events.xml
@@ -20,7 +20,7 @@
<document>
<header>
<title>Events/Processing Feedback</title>
- <version>$Revision: 634267 $</version>
+ <version>$Revision$</version>
</header>
<body>
<section id="introduction">
@@ -152,7 +152,28 @@ foUserAgent.getEventBroadcaster().addEventListener(new SysOutEventListener());]]
<p>
By default, FOP continues processing even if an image wasn't found. If you have
more strict requirements and want FOP to stop if an image is not available, you can
- do something like the following:
+ do something like the following in the simplest case:
+ </p>
+ <source><![CDATA[public class MyEventListener implements EventListener {
+
+ public void processEvent(Event event) {
+ if ("org.apache.fop.events.ResourceEventProducer".equals(
+ event.getEventGroupID())) {
+ event.setSeverity(EventSeverity.FATAL);
+ } else {
+ //ignore all other events (or do something of your choice)
+ }
+ }
+
+}]]></source>
+ <p>
+ Increasing the event severity to FATAL will signal the event broadcaster to throw
+ an exception and stop further processing. In the above case, all resource-related
+ events will cause FOP to stop processing.
+ </p>
+ <p>
+ You can also customize the exception to throw (you can may throw a RuntimeException
+ or subclass yourself) and/or which event to respond to:
</p>
<source><![CDATA[public class MyEventListener implements EventListener {
diff --git a/src/documentation/content/xdocs/trunk/extensions.xml b/src/documentation/content/xdocs/trunk/extensions.xml
index 878494ecb..107f6c5b3 100644
--- a/src/documentation/content/xdocs/trunk/extensions.xml
+++ b/src/documentation/content/xdocs/trunk/extensions.xml
@@ -226,8 +226,114 @@ to following pages. Here is an example of FO code creating such a table-header:<
color space the CMYK value is converted to an sRGB value.
</p>
</section>
+ <section id="pseudo-color-profiles">
+ <title>#CMYK pseudo-profile</title>
+ <p><code>color rgb-icc(numeric, numeric, numeric, #CMYK, numeric, numeric, numeric, numeric)</code></p>
+ <p>
+ The <code>rgb-icc</code> function will respond to a pseudo-profile called "#CMYK"
+ which indicates a device-specific CMYK color space. The "#CMYK" profile is implicitely
+ available and doesn't have to be (and cannot be) defined through an
+ <code>fo:color-profile</code> element. It is provided for compatibility with certain
+ commercial XSL-FO implementations. Please note that this is not part of the official
+ specification but rather a convention. The following two color specifications are
+ equivalent:
+ </p>
+ <ul>
+ <li><code>cmyk(0%,0%,20%,40%)</code></li>
+ <li><code>rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)</code></li>
+ </ul>
+ </section>
+ </section>
+ <section id="prepress">
+ <title>Prepress Support</title>
+
+ <p>
+ This section defines a number of extensions related to
+ <a href="http://en.wikipedia.org/wiki/Prepress">prepress</a> support.
+ <code>fox:scale</code> defines a general scale factor for the generated pages.
+ <code>fox:bleed</code> defines the
+ <a href="http://en.wikipedia.org/wiki/Bleed_%28printing%29">bleed area</a> for a page.
+ <code>fox:crop-offset</code> defines the outer edges of the area in which crop marks,
+ registration marks, color bars and page information are placed.
+ For details, please read on below.
+ </p>
+ <note>
+ Those extensions have been implemented in the PDF and Java2D renderers only.
+ </note>
+
+ <section id="scale">
+ <title>fox:scale</title>
+ <p>Value: &lt;number&gt;{1,2}</p>
+ <p>Initial: 1</p>
+ <p>Applies to: fo:simple-page-master</p>
+ <p>
+ This property specifies a scale factor along resp. the x and y axes. If only one number
+ is provided it is used for both the x and y scales. A scale factor smaller than 1
+ shrinks the page. A scale factor greater than 1 enlarges the page.
+ </p>
+ </section>
+ <section id="bleed">
+ <title>fox:bleed</title>
+ <p>
+ Value: &lt;length&gt;{1,4}
+ </p>
+ <p>
+ Initial: 0pt
+ </p>
+ <p>Applies to: fo:simple-page-master</p>
+ <p>
+ If there is only one value, it applies to all sides. If there are two values, the top and bottom
+ bleed widths are set to the first value and the right and left bleed widths are set to the second.
+ If there are three values, the top is set to the first value, the left and right are set to the second,
+ and the bottom is set to the third. If there are four values, they apply to the top, right, bottom, and
+ left, respectively.
+ (Corresponds to <a href="http://www.w3.org/TR/xsl11/#padding">the definition of
+ padding</a>).
+ </p>
+ <p>
+ This extension indirectly defines the BleedBox and is calculated by expanding the TrimBox by
+ the bleed widths. The lengths must be non-negative.
+ </p>
+ </section>
+ <section id="cropOffset">
+ <title>fox:crop-offset</title>
+ <p>
+ Value: &lt;length&gt;{1,4}
+ </p>
+ <p>
+ Initial: bleed (see below)
+ </p>
+ <p>Applies to: fo:simple-page-master</p>
+ <p>
+ Same behaviour as with fox:bleed. The initial value is set to the same values as the
+ fox:bleed property.
+ </p>
+ <p>
+ This extension indirectly defines the MediaBox and is calculated by expanding
+ the TrimBox by the crop offsets. The lengths must be non-negative.
+ </p>
+ </section>
+ <section id="cropBox">
+ <title>fox:crop-box</title>
+ <p>
+ Value: [trim-box | bleed-box | media-box]
+ </p>
+ <p>
+ Initial: media-box
+ </p>
+ <p>Applies to: fo:simple-page-master</p>
+ <p>
+ The crop box controls how Acrobat displays the page (CropBox in PDF) or how the Java2DRenderer sizes
+ the output media. The PDF specification defines that the CropBox defaults to the MediaBox. This extension
+ follows that definition. To simplify usage and cover most use cases, the three supported enumeration
+ values "trim-box", "bleed-box" and "media-box" set the CropBox to one of those three other boxes.
+ </p>
+ <p>
+ If requested in the future, we could offer to specify the CropBox in absolute coordinates rather
+ than just by referencing another box.
+ </p>
+ </section>
</section>
-
</section>
</body>
</document>
diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml
index c2548b390..9c88c27d0 100644
--- a/src/documentation/content/xdocs/trunk/output.xml
+++ b/src/documentation/content/xdocs/trunk/output.xml
@@ -422,6 +422,28 @@ out = proc.getOutputStream();]]></source>
Consult the technical reference for your printer for all available values.
</p>
</section>
+ <section id="pcl-output-bin">
+ <title>Output Bin</title>
+ <p>
+ The <code>output-bin</code> extension attribute on fo:simple-page-master allows to
+ select the output bin into which the printed output should be fed. Example:
+ </p>
+ <source><![CDATA[
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="simple" pcl:output-bin="2">
+ ...
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+]]></source>
+ <p>
+ Note: the output bin number is a positive integer and the value depends on
+ the target printer. Not all PCL printers support the same output bins.
+ Usually,
+ "1" is the upper output bin,
+ "2" is the lower (rear) output bin.
+ Consult the technical reference for your printer for all available values.
+ </p>
+ </section>
<section id="pcl-duplex-mode">
<title>Page Duplex Mode</title>
<p>
@@ -651,6 +673,11 @@ out = proc.getOutputStream();]]></source>
<font-triplet name="monospace" style="normal" weight="bold"/>
<font-triplet name="Courier" style="normal" weight="bold"/>
</font>]]></source>
+ <p>
+ By default, all manually configured fonts are embedded, unless they are matched in the
+ <a href="fonts.html#embedding"><code>referenced-fonts</code> section of the configuration file</a>.
+ However, the default fonts shown above will not be embedded.
+ </p>
</section>
<section id="afp-renderer-resolution-config">
<title>Output Resolution</title>
@@ -680,6 +707,20 @@ out = proc.getOutputStream();]]></source>
Support for native image formats (e.g. JPEG, GIF) is not always available on printer implementations
so by default this configuration option is set to "false".</p>
</section>
+ <section id="afp-shading-config">
+ <title>Shading</title>
+ <p>
+ By default, filled rectangles are painted using their given color using a PTOCA I-axis rule
+ (DIR). But not all environments handle these colors correctly. That's why a setting is
+ supported that paints the rectangles using an ordered dither pattern (bi-level) with
+ an inline IOCA FS10 image that is used together with the "replicate and trim" mapping.
+ The optional "shading" element can be used to control the shading mode. Its default value
+ is "color". To enable the dithered mode, use "dithered". Example:
+ </p>
+ <source><![CDATA[
+ <shading>dithered</shading>
+]]></source>
+ </section>
<section id="afp-resource-group-file">
<title>Resource Group File</title>
<p>By default the AFP Renderer will place all data resource objects such as images within
@@ -816,11 +857,40 @@ out = proc.getOutputStream();]]></source>
]]></source>
<p>
The invoke-medium-map element is allowed as child of fo:page-sequence (page group
- level). It is NOT supported on document level (fo:root), yet. FOP also doesn't support
- specifying medium maps inside XML (using BMM/EMM). It can only reference an existing
- medium map by name. The medium map has to be constructed through different means and
- available on the target platform.
+ level) or fo:simple-page-master. It is NOT supported on document level (fo:root), yet.
+ FOP also doesn't support specifying medium maps inside XML (using BMM/EMM). It can
+ only reference an existing medium map by name. The medium map has to be constructed
+ through different means and available on the target platform.
+ </p>
+ </section>
+ <section id="afp-form-maps">
+ <title>Form Maps/Defs</title>
+ <p>
+ Apache FOP supports embedding an external form map resource in the
+ generated AFP output. This is done using the <code>afp:include-form-map</code>
+ extension. An example:
+ </p>
+ <source><![CDATA[
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp">
+ [..]
+ <fo:declarations>
+ <afp:include-form-map name="F1SAMP1" src="file:f1samp1.fde"/>
+ </fo:declarations>
+]]></source>
+ <p>
+ The <code>afp:include-form-map</code> is to be placed as a direct child of
+ <code>fo:declarations</code>. The <code>name</code> is an AFP resource name
+ (max. 8 characters) and the <code>src</code> attribute is the URI identifying the
+ external form map resource. When such a form map is embedded, you can use the
+ <code>afp:invoke-medium-map</code> extension (described above) to invoke any medium
+ map included in the form map.
</p>
+ <note>
+ Apache FOP doesn't support a way to define a form map or medium map using XML means
+ inside an XSL-FO document. You will have to build the form map with some third-party
+ tool.
+ </note>
</section>
</section>
<section id="afp-foreign-attributes">
diff --git a/src/documentation/skinconf.xml b/src/documentation/skinconf.xml
index 1944ad856..ccafd9541 100644
--- a/src/documentation/skinconf.xml
+++ b/src/documentation/skinconf.xml
@@ -369,6 +369,7 @@ which will be used to configure the chosen Forrest skin.
<width>88</width>
<height>31</height>
</credit>
+ <!--
<credit box-location="alt2">
<name>ApacheCon Europe 2009</name>
<url>http://eu.apachecon.com/</url>
@@ -376,6 +377,7 @@ which will be used to configure the chosen Forrest skin.
<width>125</width>
<height>125</height>
</credit>
+ -->
<credit box-location="alt2">
<name>ApacheCon US 2009</name>
<url>http://us.apachecon.com/</url>
diff --git a/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory b/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory
index 8dc13dbfd..96caa94a4 100644
--- a/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory
+++ b/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory
@@ -1,5 +1,5 @@
org.apache.fop.events.FOPEventModelFactory
-org.apache.fop.render.afp.AFPEventProducer$EventModelFactory
+org.apache.fop.afp.AFPEventProducer$EventModelFactory
org.apache.fop.render.bitmap.BitmapRendererEventProducer$EventModelFactory
org.apache.fop.render.pcl.PCLEventProducer$EventModelFactory
org.apache.fop.render.pdf.PDFEventProducer$EventModelFactory
diff --git a/src/java/org/apache/fop/afp/AFPDataObjectInfo.java b/src/java/org/apache/fop/afp/AFPDataObjectInfo.java
index 011118683..158189b76 100644
--- a/src/java/org/apache/fop/afp/AFPDataObjectInfo.java
+++ b/src/java/org/apache/fop/afp/AFPDataObjectInfo.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp;
@@ -23,6 +23,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.afp.modca.Registry;
+import org.apache.fop.afp.modca.triplets.MappingOptionTriplet;
/**
* A list of parameters associated with an AFP data objects
@@ -57,6 +58,9 @@ public class AFPDataObjectInfo {
/** controls whether to create a page segment or a simple object */
private boolean createPageSegment;
+ /** controls the mapping of the image data into the image area */
+ private byte mappingOption = MappingOptionTriplet.SCALE_TO_FILL;
+
/**
* Default constructor
*/
@@ -253,6 +257,23 @@ public class AFPDataObjectInfo {
return this.createPageSegment;
}
+ /**
+ * Sets the way an image is mapped into its target area.
+ * @param mappingOption the mapping option (Valid values: see Mapping Option Triplet)
+ */
+ public void setMappingOption(byte mappingOption) {
+ this.mappingOption = mappingOption;
+ }
+
+ /**
+ * Returns the way an image is mapped into its target area. By default, this is "scale to fill"
+ * behavior.
+ * @return the mapping option value from the Mapping Option Triplet
+ */
+ public byte getMappingOption() {
+ return mappingOption;
+ }
+
/** {@inheritDoc} */
public String toString() {
return "AFPDataObjectInfo{"
@@ -264,4 +285,5 @@ public class AFPDataObjectInfo {
+ (objectAreaInfo != null ? ", objectAreaInfo=" + objectAreaInfo : "")
+ (resourceInfo != null ? ", resourceInfo=" + resourceInfo : "");
}
+
}
diff --git a/src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java b/src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java
new file mode 100644
index 000000000..79e4979fd
--- /dev/null
+++ b/src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.util.MimeConstants;
+
+import org.apache.fop.afp.modca.triplets.MappingOptionTriplet;
+import org.apache.fop.util.bitmap.DitherUtil;
+
+
+/**
+ * A painter of rectangles in AFP
+ */
+public class AFPDitheredRectanglePainter extends AbstractAFPPainter {
+
+ private AFPResourceManager resourceManager;
+
+ /**
+ * Main constructor
+ *
+ * @param paintingState the AFP painting state
+ * @param dataStream the AFP datastream
+ * @param resourceManager the resource manager
+ */
+ public AFPDitheredRectanglePainter(AFPPaintingState paintingState, DataStream dataStream,
+ AFPResourceManager resourceManager) {
+ super(paintingState, dataStream);
+ this.resourceManager = resourceManager;
+ }
+
+ /** {@inheritDoc} */
+ public void paint(PaintingInfo paintInfo) throws IOException {
+ RectanglePaintingInfo rectanglePaintInfo = (RectanglePaintingInfo)paintInfo;
+ if (rectanglePaintInfo.getWidth() <= 0 || rectanglePaintInfo.getHeight() <= 0) {
+ return;
+ }
+
+ int ditherMatrix = DitherUtil.DITHER_MATRIX_8X8;
+ Dimension ditherSize = new Dimension(ditherMatrix, ditherMatrix);
+
+ //Prepare an FS10 bi-level image
+ AFPImageObjectInfo imageObjectInfo = new AFPImageObjectInfo();
+ imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS10);
+ //imageObjectInfo.setCreatePageSegment(true);
+ imageObjectInfo.getResourceInfo().setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE));
+ imageObjectInfo.getResourceInfo().setImageDimension(ditherSize);
+ imageObjectInfo.setBitsPerPixel(1);
+ imageObjectInfo.setColor(false);
+ //Note: the following may not be supported by older implementations
+ imageObjectInfo.setMappingOption(MappingOptionTriplet.REPLICATE_AND_TRIM);
+
+ //Dither image size
+ int resolution = paintingState.getResolution();
+ ImageSize ditherBitmapSize = new ImageSize(
+ ditherSize.width, ditherSize.height, resolution);
+ imageObjectInfo.setDataHeightRes((int)Math.round(
+ ditherBitmapSize.getDpiHorizontal() * 10));
+ imageObjectInfo.setDataWidthRes((int)Math.round(
+ ditherBitmapSize.getDpiVertical() * 10));
+ imageObjectInfo.setDataWidth(ditherSize.width);
+ imageObjectInfo.setDataHeight(ditherSize.height);
+
+ //Create dither image
+ Color col = paintingState.getColor();
+ byte[] dither = DitherUtil.getBayerDither(ditherMatrix, col, false);
+ imageObjectInfo.setData(dither);
+
+ //Positioning
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
+ int rotation = paintingState.getRotation();
+ AffineTransform at = paintingState.getData().getTransform();
+ Point2D origin = at.transform(new Point2D.Float(
+ rectanglePaintInfo.getX() * 1000,
+ rectanglePaintInfo.getY() * 1000), null);
+ objectAreaInfo.setX((int)Math.round(origin.getX()));
+ objectAreaInfo.setY((int)Math.round(origin.getY()));
+ AFPUnitConverter unitConv = paintingState.getUnitConverter();
+ float width = unitConv.pt2units(rectanglePaintInfo.getWidth());
+ float height = unitConv.pt2units(rectanglePaintInfo.getHeight());
+ objectAreaInfo.setWidth(Math.round(width));
+ objectAreaInfo.setHeight(Math.round(height));
+ objectAreaInfo.setHeightRes(resolution);
+ objectAreaInfo.setWidthRes(resolution);
+ objectAreaInfo.setRotation(rotation);
+ imageObjectInfo.setObjectAreaInfo(objectAreaInfo);
+
+ //Create rectangle
+ resourceManager.createObject(imageObjectInfo);
+ }
+
+}
diff --git a/src/java/org/apache/fop/afp/AFPEventProducer.java b/src/java/org/apache/fop/afp/AFPEventProducer.java
index 49792183f..b56250fc2 100644
--- a/src/java/org/apache/fop/afp/AFPEventProducer.java
+++ b/src/java/org/apache/fop/afp/AFPEventProducer.java
@@ -80,4 +80,14 @@ public interface AFPEventProducer extends EventProducer {
* @event.severity ERROR
*/
void characterSetEncodingError(Object source, String charSetName, String encoding);
+
+ /**
+ * Triggered when an external resource fails to be embedded.
+ *
+ * @param source the event source
+ * @param resourceName the name of the resource where the error occurred
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void resourceEmbeddingError(Object source, String resourceName, Exception e);
}
diff --git a/src/java/org/apache/fop/afp/AFPEventProducer.xml b/src/java/org/apache/fop/afp/AFPEventProducer.xml
index 8e6bb5429..364cd92ff 100644
--- a/src/java/org/apache/fop/afp/AFPEventProducer.xml
+++ b/src/java/org/apache/fop/afp/AFPEventProducer.xml
@@ -3,4 +3,5 @@
<message key="org.apache.fop.afp.AFPEventProducer.warnDefaultFontSetup">No AFP fonts configured. Using default setup.</message>
<message key="org.apache.fop.afp.AFPEventProducer.warnMissingDefaultFont">No AFP default "any", {style}, {weight} font configured.</message>
<message key="org.apache.fop.afp.AFPEventProducer.characterSetEncodingError">An error occurred when attempting to encode character set {charSetName} with encoding scheme {encoding}.</message>
+ <message key="org.apache.fop.afp.AFPEventProducer.resourceEmbeddingError">An error occurs while embedding the resource named "{resourceName}".[ Reason: {e}]</message>
</catalogue>
diff --git a/src/java/org/apache/fop/afp/AFPGraphics2D.java b/src/java/org/apache/fop/afp/AFPGraphics2D.java
index 2e176e5ab..0e90c821f 100644
--- a/src/java/org/apache/fop/afp/AFPGraphics2D.java
+++ b/src/java/org/apache/fop/afp/AFPGraphics2D.java
@@ -62,6 +62,7 @@ import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.afp.goca.GraphicsSetLineType;
import org.apache.fop.afp.modca.GraphicsObject;
import org.apache.fop.afp.svg.AFPGraphicsConfiguration;
+import org.apache.fop.afp.util.CubicBezierApproximator;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.svg.NativeImageHandler;
@@ -437,6 +438,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
*/
private void processPathIterator(PathIterator iter) {
double[] dstPts = new double[6];
+ double[] currentPosition = new double[2];
for (int[] openingCoords = new int[2]; !iter.isDone(); iter.next()) {
switch (iter.currentSegment(dstPts)) {
case PathIterator.SEG_LINETO:
@@ -444,6 +446,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
(int)Math.round(dstPts[X]),
(int)Math.round(dstPts[Y])
}, true);
+ currentPosition = new double[]{dstPts[X], dstPts[Y]};
break;
case PathIterator.SEG_QUADTO:
graphicsObj.addFillet(new int[] {
@@ -452,26 +455,39 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
(int)Math.round(dstPts[X2]),
(int)Math.round(dstPts[Y2])
}, true);
+ currentPosition = new double[]{dstPts[X2], dstPts[Y2]};
break;
case PathIterator.SEG_CUBICTO:
- graphicsObj.addFillet(new int[] {
- (int)Math.round(dstPts[X1]),
- (int)Math.round(dstPts[Y1]),
- (int)Math.round(dstPts[X2]),
- (int)Math.round(dstPts[Y2]),
- (int)Math.round(dstPts[X3]),
- (int)Math.round(dstPts[Y3])
- }, true);
+ double[] cubicCoords = new double[] {currentPosition[0], currentPosition[1],
+ dstPts[X1], dstPts[Y1], dstPts[X2], dstPts[Y2], dstPts[X3], dstPts[Y3]};
+ double[][] quadParts = CubicBezierApproximator.fixedMidPointApproximation(
+ cubicCoords);
+ if (quadParts.length >= 4) {
+ for (int segIndex = 0; segIndex < quadParts.length; segIndex++) {
+ double[] quadPts = quadParts[segIndex];
+ if (quadPts != null && quadPts.length == 4) {
+ graphicsObj.addFillet(new int[]{
+ (int) Math.round(quadPts[X1]),
+ (int) Math.round(quadPts[Y1]),
+ (int) Math.round(quadPts[X2]),
+ (int) Math.round(quadPts[Y2])
+ }, true);
+ currentPosition = new double[]{quadPts[X2], quadPts[Y2]};
+ }
+ }
+ }
break;
case PathIterator.SEG_MOVETO:
openingCoords = new int[] {
(int)Math.round(dstPts[X]),
(int)Math.round(dstPts[Y])
};
+ currentPosition = new double[]{dstPts[X], dstPts[Y]};
graphicsObj.setCurrentPosition(openingCoords);
break;
case PathIterator.SEG_CLOSE:
graphicsObj.addLine(openingCoords, true);
+ currentPosition = new double[]{openingCoords[0], openingCoords[1]};
break;
default:
log.debug("Unrecognised path iterator type");
diff --git a/src/java/org/apache/fop/afp/AFPImageObjectInfo.java b/src/java/org/apache/fop/afp/AFPImageObjectInfo.java
index 45ea5fc1f..7aee3cda8 100644
--- a/src/java/org/apache/fop/afp/AFPImageObjectInfo.java
+++ b/src/java/org/apache/fop/afp/AFPImageObjectInfo.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp;
diff --git a/src/java/org/apache/fop/afp/AFPResourceInfo.java b/src/java/org/apache/fop/afp/AFPResourceInfo.java
index 43ba2a238..64623c3db 100644
--- a/src/java/org/apache/fop/afp/AFPResourceInfo.java
+++ b/src/java/org/apache/fop/afp/AFPResourceInfo.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp;
diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java
index c44698a10..b7e1abc01 100644
--- a/src/java/org/apache/fop/afp/AFPResourceManager.java
+++ b/src/java/org/apache/fop/afp/AFPResourceManager.java
@@ -28,6 +28,8 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.afp.fonts.AFPFont;
+import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.modca.AbstractNamedAFPObject;
import org.apache.fop.afp.modca.AbstractPageObject;
import org.apache.fop.afp.modca.IncludeObject;
@@ -165,39 +167,38 @@ public class AFPResourceManager {
AFPResourceLevel resourceLevel = resourceInfo.getLevel();
ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
+
useInclude &= resourceGroup != null;
if (useInclude) {
+ boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
+
+ // if it is to reside within a resource group at print-file or external level
+ if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
+ if (usePageSegment) {
+ String pageSegmentName = "S10" + namedObj.getName().substring(3);
+ namedObj.setName(pageSegmentName);
+ PageSegment seg = new PageSegment(pageSegmentName);
+ seg.addObject(namedObj);
+ namedObj = seg;
+ }
+
+ // wrap newly created data object in a resource object
+ namedObj = dataObjectFactory.createResource(namedObj, resourceInfo, objectType);
+ }
- boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
+ // add data object into its resource group destination
+ resourceGroup.addObject(namedObj);
- // if it is to reside within a resource group at print-file or external level
- if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
+ // create the include object
+ objectName = namedObj.getName();
if (usePageSegment) {
- String pageSegmentName = "S10" + namedObj.getName().substring(3);
- namedObj.setName(pageSegmentName);
- PageSegment seg = new PageSegment(pageSegmentName);
- seg.addObject(namedObj);
- namedObj = seg;
+ includePageSegment(dataObjectInfo, objectName);
+ pageSegmentMap.put(resourceInfo, objectName);
+ } else {
+ includeObject(dataObjectInfo, objectName);
+ // record mapping of resource info to data object resource name
+ includeNameMap.put(resourceInfo, objectName);
}
-
- // wrap newly created data object in a resource object
- namedObj = dataObjectFactory.createResource(namedObj, resourceInfo, objectType);
- }
-
- // add data object into its resource group destination
- resourceGroup.addObject(namedObj);
-
- // create the include object
- objectName = namedObj.getName();
- if (usePageSegment) {
- includePageSegment(dataObjectInfo, objectName);
- pageSegmentMap.put(resourceInfo, objectName);
- } else {
- includeObject(dataObjectInfo, objectName);
- // record mapping of resource info to data object resource name
- includeNameMap.put(resourceInfo, objectName);
- }
-
} else {
// not to be included so inline data object directly into the current page
dataStream.getCurrentPage().addObject(namedObj);
@@ -218,10 +219,32 @@ public class AFPResourceManager {
private void includeObject(AFPDataObjectInfo dataObjectInfo,
String objectName) {
- IncludeObject includeObject
- = dataObjectFactory.createInclude(objectName, dataObjectInfo);
- dataStream.getCurrentPage().addObject(includeObject);
+ IncludeObject includeObject
+ = dataObjectFactory.createInclude(objectName, dataObjectInfo);
+ dataStream.getCurrentPage().addObject(includeObject);
+ }
+
+ /**
+ * Handles font embedding. If a font is embeddable and has not already been embedded it will be.
+ * @param afpFont the AFP font to be checked for embedding
+ * @param charSet the associated character set
+ * @throws IOException if there's a problem while embedding the external resources
+ */
+ public void embedFont(AFPFont afpFont, CharacterSet charSet)
+ throws IOException {
+ if (afpFont.isEmbeddable()) {
+ //Embed fonts (char sets and code pages)
+ if (charSet.getResourceAccessor() != null) {
+ ResourceAccessor accessor = charSet.getResourceAccessor();
+ createIncludedResource(
+ charSet.getName(), accessor,
+ ResourceObject.TYPE_FONT_CHARACTER_SET);
+ createIncludedResource(
+ charSet.getCodePage(), accessor,
+ ResourceObject.TYPE_CODE_PAGE);
+ }
}
+ }
private void includePageSegment(AFPDataObjectInfo dataObjectInfo,
String pageSegmentName) {
@@ -241,7 +264,6 @@ public class AFPResourceManager {
*/
public void createIncludedResource(String resourceName, ResourceAccessor accessor,
byte resourceObjectType) throws IOException {
- AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
URI uri;
try {
uri = new URI(resourceName.trim());
@@ -250,6 +272,21 @@ public class AFPResourceManager {
+ " (" + e.getMessage() + ")");
}
+ createIncludedResource(resourceName, uri, accessor, resourceObjectType);
+ }
+
+ /**
+ * Creates an included resource object by loading the contained object from a file.
+ * @param resourceName the name of the resource
+ * @param uri the URI for the resource
+ * @param accessor resource accessor to access the resource with
+ * @param resourceObjectType the resource object type ({@link ResourceObject}.*)
+ * @throws IOException if an I/O error occurs while loading the resource
+ */
+ public void createIncludedResource(String resourceName, URI uri, ResourceAccessor accessor,
+ byte resourceObjectType) throws IOException {
+ AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
+
AFPResourceInfo resourceInfo = new AFPResourceInfo();
resourceInfo.setLevel(resourceLevel);
resourceInfo.setName(resourceName);
diff --git a/src/java/org/apache/fop/afp/AbstractAFPPainter.java b/src/java/org/apache/fop/afp/AbstractAFPPainter.java
index 576b8bb11..1358f8072 100644
--- a/src/java/org/apache/fop/afp/AbstractAFPPainter.java
+++ b/src/java/org/apache/fop/afp/AbstractAFPPainter.java
@@ -19,6 +19,8 @@
package org.apache.fop.afp;
+import java.io.IOException;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -48,6 +50,7 @@ public abstract class AbstractAFPPainter {
* Paints the painting item
*
* @param paintInfo the painting information
+ * @throws IOException if an I/O error occurs
*/
- public abstract void paint(PaintingInfo paintInfo);
+ public abstract void paint(PaintingInfo paintInfo) throws IOException;
}
diff --git a/src/java/org/apache/fop/afp/DataStream.java b/src/java/org/apache/fop/afp/DataStream.java
index b1ff96859..cb68af94e 100644
--- a/src/java/org/apache/fop/afp/DataStream.java
+++ b/src/java/org/apache/fop/afp/DataStream.java
@@ -30,8 +30,9 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.afp.fonts.AFPFont;
import org.apache.fop.afp.fonts.AFPFontAttributes;
+import org.apache.fop.afp.fonts.AFPFont;
+import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.modca.AbstractPageObject;
import org.apache.fop.afp.modca.Document;
import org.apache.fop.afp.modca.InterchangeSet;
@@ -41,6 +42,10 @@ import org.apache.fop.afp.modca.PageObject;
import org.apache.fop.afp.modca.ResourceGroup;
import org.apache.fop.afp.modca.TagLogicalElementBean;
import org.apache.fop.afp.modca.triplets.FullyQualifiedNameTriplet;
+import org.apache.fop.afp.ptoca.PtocaProducer;
+import org.apache.fop.afp.ptoca.PtocaBuilder;
+import org.apache.fop.util.CharUtilities;
+import org.apache.fop.fonts.Font;
/**
* A data stream is a continuous ordered stream of data elements and objects
@@ -347,11 +352,15 @@ public class DataStream {
* Helper method to create text on the current page, this method delegates
* to the current presentation text object in order to construct the text.
*
- * @param textDataInfo
- * the afp text data
+ * @param textDataInfo the afp text data
+ * @param letterSpacing letter spacing to draw text with
+ * @param wordSpacing word Spacing to draw text with
+ * @param font is the font to draw text with
+ * @param charSet is the AFP Character Set to use with the text
* @throws UnsupportedEncodingException thrown if character encoding is not supported
*/
- public void createText(AFPTextDataInfo textDataInfo) throws UnsupportedEncodingException {
+ public void createText(final AFPTextDataInfo textDataInfo, final int letterSpacing, final int wordSpacing,
+ final Font font, final CharacterSet charSet) throws UnsupportedEncodingException {
int rotation = paintingState.getRotation();
if (rotation != 0) {
textDataInfo.setRotation(rotation);
@@ -359,7 +368,86 @@ public class DataStream {
textDataInfo.setX(p.x);
textDataInfo.setY(p.y);
}
- currentPage.createText(textDataInfo);
+ // use PtocaProducer to create PTX records
+ PtocaProducer producer = new PtocaProducer() {
+
+ public void produce(PtocaBuilder builder) throws IOException {
+ builder.setTextOrientation(textDataInfo.getRotation());
+ builder.absoluteMoveBaseline(textDataInfo.getY());
+ builder.absoluteMoveInline(textDataInfo.getX());
+
+ builder.setExtendedTextColor(textDataInfo.getColor());
+ builder.setCodedFont((byte)textDataInfo.getFontReference());
+
+ int l = textDataInfo.getString().length();
+ StringBuffer sb = new StringBuffer();
+
+ int interCharacterAdjustment = 0;
+ AFPUnitConverter unitConv = paintingState.getUnitConverter();
+ if (letterSpacing != 0) {
+ interCharacterAdjustment = Math.round(unitConv.mpt2units(letterSpacing));
+ }
+ builder.setInterCharacterAdjustment(interCharacterAdjustment);
+
+ int spaceWidth = font.getCharWidth(CharUtilities.SPACE);
+ int spacing = spaceWidth + letterSpacing;
+ int fixedSpaceCharacterIncrement = Math.round(unitConv.mpt2units(spacing));
+ int varSpaceCharacterIncrement = fixedSpaceCharacterIncrement;
+ if (wordSpacing != 0) {
+ varSpaceCharacterIncrement = Math.round(unitConv.mpt2units(
+ spaceWidth + wordSpacing + letterSpacing));
+ }
+ builder.setVariableSpaceCharacterIncrement(varSpaceCharacterIncrement);
+
+ boolean fixedSpaceMode = false;
+
+ for (int i = 0; i < l; i++) {
+ char orgChar = textDataInfo.getString().charAt(i);
+ float glyphAdjust = 0;
+ if (CharUtilities.isFixedWidthSpace(orgChar)) {
+ flushText(builder, sb, charSet);
+ builder.setVariableSpaceCharacterIncrement(
+ fixedSpaceCharacterIncrement);
+ fixedSpaceMode = true;
+ sb.append(CharUtilities.SPACE);
+ int charWidth = font.getCharWidth(orgChar);
+ glyphAdjust += (charWidth - spaceWidth);
+ } else {
+ if (fixedSpaceMode) {
+ flushText(builder, sb, charSet);
+ builder.setVariableSpaceCharacterIncrement(
+ varSpaceCharacterIncrement);
+ fixedSpaceMode = false;
+ }
+ char ch;
+ if (orgChar == CharUtilities.NBSPACE) {
+ ch = ' '; //converted to normal space to allow word spacing
+ } else {
+ ch = orgChar;
+ }
+ sb.append(ch);
+ }
+
+ if (glyphAdjust != 0) {
+ flushText(builder, sb, charSet);
+ int increment = Math.round(unitConv.mpt2units(glyphAdjust));
+ builder.relativeMoveInline(increment);
+ }
+ }
+ flushText(builder, sb, charSet);
+ }
+
+ private void flushText(PtocaBuilder builder, StringBuffer sb,
+ final CharacterSet charSet) throws IOException {
+ if (sb.length() > 0) {
+ builder.addTransparentData(charSet.encodeChars(sb));
+ sb.setLength(0);
+ }
+ }
+
+ };
+
+ currentPage.createText(producer);
}
/**
diff --git a/src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java b/src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java
index e607bef5f..417250df1 100644
--- a/src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java
+++ b/src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java
@@ -49,7 +49,7 @@ public class AFPBase12FontCollection implements FontCollection {
private void addCharacterSet(RasterFont font, String charsetName, Base14Font base14) {
for (int i = 0; i < RASTER_SIZES.length; i++) {
- int size = RASTER_SIZES[i];
+ int size = RASTER_SIZES[i] * 1000;
FopCharacterSet characterSet = new FopCharacterSet(
CharacterSet.DEFAULT_CODEPAGE, CharacterSet.DEFAULT_ENCODING,
charsetName + CHARSET_REF[i], base14);
@@ -80,22 +80,22 @@ public class AFPBase12FontCollection implements FontCollection {
/** standard font family reference names for Helvetica font */
final String[] helveticaNames = {"Helvetica", "Arial", "sans-serif"};
- font = new RasterFont("Helvetica");
+ font = createReferencedRasterFont("Helvetica");
addCharacterSet(font, "C0H200", new Helvetica());
num = addFontProperties(fontInfo, font, helveticaNames,
Font.STYLE_NORMAL, Font.WEIGHT_NORMAL, num);
- font = new RasterFont("Helvetica Italic");
+ font = createReferencedRasterFont("Helvetica Italic");
addCharacterSet(font, "C0H300", new HelveticaOblique());
num = addFontProperties(fontInfo, font, helveticaNames,
Font.STYLE_ITALIC, Font.WEIGHT_NORMAL, num);
- font = new RasterFont("Helvetica (Semi) Bold");
+ font = createReferencedRasterFont("Helvetica (Semi) Bold");
addCharacterSet(font, "C0H400", new HelveticaBold());
num = addFontProperties(fontInfo, font, helveticaNames,
Font.STYLE_NORMAL, Font.WEIGHT_BOLD, num);
- font = new RasterFont("Helvetica Italic (Semi) Bold");
+ font = createReferencedRasterFont("Helvetica Italic (Semi) Bold");
addCharacterSet(font, "C0H500", new HelveticaOblique());
num = addFontProperties(fontInfo, font, helveticaNames,
Font.STYLE_ITALIC, Font.WEIGHT_BOLD, num);
@@ -107,22 +107,22 @@ public class AFPBase12FontCollection implements FontCollection {
final String[] timesNames = {"Times", "TimesRoman", "Times Roman", "Times-Roman",
"Times New Roman", "TimesNewRoman", "serif", "any"};
- font = new RasterFont("Times Roman");
+ font = createReferencedRasterFont("Times Roman");
addCharacterSet(font, "CON200", new TimesRoman());
num = addFontProperties(fontInfo, font, timesNames,
Font.STYLE_NORMAL, Font.WEIGHT_NORMAL, num);
- font = new RasterFont("Times Roman Italic");
+ font = createReferencedRasterFont("Times Roman Italic");
addCharacterSet(font, "CON300", new TimesItalic());
num = addFontProperties(fontInfo, font, timesNames,
Font.STYLE_ITALIC, Font.WEIGHT_NORMAL, num);
- font = new RasterFont("Times Roman Bold");
+ font = createReferencedRasterFont("Times Roman Bold");
addCharacterSet(font, "CON400", new TimesBold());
num = addFontProperties(fontInfo, font, timesNames,
Font.STYLE_NORMAL, Font.WEIGHT_BOLD, num);
- font = new RasterFont("Times Roman Italic Bold");
+ font = createReferencedRasterFont("Times Roman Italic Bold");
addCharacterSet(font, "CON500", new TimesBoldItalic());
num = addFontProperties(fontInfo, font, timesNames,
Font.STYLE_ITALIC, Font.WEIGHT_BOLD, num);
@@ -131,22 +131,22 @@ public class AFPBase12FontCollection implements FontCollection {
/** standard font family reference names for Courier font */
final String[] courierNames = {"Courier", "monospace"};
- font = new RasterFont("Courier");
+ font = createReferencedRasterFont("Courier");
addCharacterSet(font, "C04200", new Courier());
num = addFontProperties(fontInfo, font, courierNames,
Font.STYLE_NORMAL, Font.WEIGHT_NORMAL, num);
- font = new RasterFont("Courier Italic");
+ font = createReferencedRasterFont("Courier Italic");
addCharacterSet(font, "C04300", new CourierOblique());
num = addFontProperties(fontInfo, font, courierNames,
Font.STYLE_ITALIC, Font.WEIGHT_NORMAL, num);
- font = new RasterFont("Courier Bold");
+ font = createReferencedRasterFont("Courier Bold");
addCharacterSet(font, "C04400", new CourierBold());
num = addFontProperties(fontInfo, font, courierNames,
Font.STYLE_NORMAL, Font.WEIGHT_BOLD, num);
- font = new RasterFont("Courier Italic Bold");
+ font = createReferencedRasterFont("Courier Italic Bold");
addCharacterSet(font, "C04500", new CourierBoldOblique());
num = addFontProperties(fontInfo, font, courierNames,
Font.STYLE_ITALIC, Font.WEIGHT_BOLD, num);
@@ -154,4 +154,10 @@ public class AFPBase12FontCollection implements FontCollection {
return num;
}
+ private RasterFont createReferencedRasterFont(String fontFamily) {
+ RasterFont font = new RasterFont(fontFamily);
+ font.setEmbeddable(false); //Font is assumed to be available on the target platform
+ return font;
+ }
+
}
diff --git a/src/java/org/apache/fop/afp/fonts/AFPFont.java b/src/java/org/apache/fop/afp/fonts/AFPFont.java
index f56611087..a1c257d3e 100644
--- a/src/java/org/apache/fop/afp/fonts/AFPFont.java
+++ b/src/java/org/apache/fop/afp/fonts/AFPFont.java
@@ -36,6 +36,8 @@ public abstract class AFPFont extends Typeface {
/** The font name */
protected String name;
+ private boolean embeddable = true;
+
/**
* Constructor for the base font requires the name.
* @param name the name of the font
@@ -98,11 +100,19 @@ public abstract class AFPFont extends Typeface {
public abstract CharacterSet getCharacterSet(int size);
/**
+ * Controls whether this font is embeddable or not.
+ * @param value true to enable embedding, false otherwise.
+ */
+ public void setEmbeddable(boolean value) {
+ this.embeddable = value;
+ }
+
+ /**
* Indicates if this font may be embedded.
* @return True, if embedding is possible/permitted
*/
public boolean isEmbeddable() {
- return false; //TODO Complete AFP font embedding
+ return this.embeddable;
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java b/src/java/org/apache/fop/afp/fonts/AFPFontReader.java
index 4e6a03259..25ea15278 100644
--- a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java
+++ b/src/java/org/apache/fop/afp/fonts/AFPFontReader.java
@@ -177,7 +177,8 @@ public final class AFPFontReader {
StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
// Process D3A689 Font Descriptor
- int pointSize = processFontDescriptor(structuredFieldReader);
+ FontDescriptor fontDescriptor = processFontDescriptor(structuredFieldReader);
+ characterSet.setNominalVerticalSize(fontDescriptor.getNominalFontSizeInMillipoints());
// Process D3A789 Font Control
FontControl fontControl = processFontControl(structuredFieldReader);
@@ -187,12 +188,13 @@ public final class AFPFontReader {
CharacterSetOrientation[] characterSetOrientations
= processFontOrientation(structuredFieldReader);
- int dpi = fontControl.getDpi();
- int metricNormalizationFactor = 0;
+ int metricNormalizationFactor;
if (fontControl.isRelative()) {
metricNormalizationFactor = 1;
} else {
- metricNormalizationFactor = 72000 / dpi / pointSize;
+ int dpi = fontControl.getDpi();
+ metricNormalizationFactor = 1000 * 72000
+ / fontDescriptor.getNominalFontSizeInMillipoints() / dpi;
}
//process D3AC89 Font Position
@@ -274,15 +276,13 @@ public final class AFPFontReader {
* Process the font descriptor details using the structured field reader.
*
* @param structuredFieldReader the structured field reader
- * @return the nominal size of the font (in points)
+ * @return a class representing the font descriptor
*/
- private static int processFontDescriptor(StructuredFieldReader structuredFieldReader)
+ private static FontDescriptor processFontDescriptor(StructuredFieldReader structuredFieldReader)
throws IOException {
byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF);
-
- int nominalPointSize = (((fndData[39] & 0xFF) << 8) + (fndData[40] & 0xFF)) / 10;
- return nominalPointSize;
+ return new FontDescriptor(fndData);
}
/**
@@ -303,8 +303,13 @@ public final class AFPFontReader {
if (fncData[7] == (byte) 0x02) {
fontControl.setRelative(true);
}
- int metricResolution = (((fncData[9] & 0xFF) << 8) + (fncData[10] & 0xFF)) / 10;
- fontControl.setDpi(metricResolution);
+ int metricResolution = getUBIN(fncData, 9);
+ if (metricResolution == 1000) {
+ //Special case: 1000 units per em (rather than dpi)
+ fontControl.setUnitsPerEm(1000);
+ } else {
+ fontControl.setDpi(metricResolution / 10);
+ }
}
return fontControl;
}
@@ -378,7 +383,7 @@ public final class AFPFontReader {
* font metric values
*/
private void processFontPosition(StructuredFieldReader structuredFieldReader,
- CharacterSetOrientation[] characterSetOrientations, int metricNormalizationFactor)
+ CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor)
throws IOException {
byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF);
@@ -397,17 +402,21 @@ public final class AFPFontReader {
CharacterSetOrientation characterSetOrientation
= characterSetOrientations[characterSetOrientationIndex];
- int xHeight = ((fpData[2] & 0xFF) << 8) + (fpData[3] & 0xFF);
- int capHeight = ((fpData[4] & 0xFF) << 8) + (fpData[5] & 0xFF);
- int ascHeight = ((fpData[6] & 0xFF) << 8) + (fpData[7] & 0xFF);
- int dscHeight = ((fpData[8] & 0xFF) << 8) + (fpData[9] & 0xFF);
+ int xHeight = getSBIN(fpData, 2);
+ int capHeight = getSBIN(fpData, 4);
+ int ascHeight = getSBIN(fpData, 6);
+ int dscHeight = getSBIN(fpData, 8);
dscHeight = dscHeight * -1;
- characterSetOrientation.setXHeight(xHeight * metricNormalizationFactor);
- characterSetOrientation.setCapHeight(capHeight * metricNormalizationFactor);
- characterSetOrientation.setAscender(ascHeight * metricNormalizationFactor);
- characterSetOrientation.setDescender(dscHeight * metricNormalizationFactor);
+ characterSetOrientation.setXHeight(
+ (int)Math.round(xHeight * metricNormalizationFactor));
+ characterSetOrientation.setCapHeight(
+ (int)Math.round(capHeight * metricNormalizationFactor));
+ characterSetOrientation.setAscender(
+ (int)Math.round(ascHeight * metricNormalizationFactor));
+ characterSetOrientation.setDescender(
+ (int)Math.round(dscHeight * metricNormalizationFactor));
}
} else if (position == 22) {
position = 0;
@@ -430,7 +439,8 @@ public final class AFPFontReader {
* font metric values
*/
private void processFontIndex(StructuredFieldReader structuredFieldReader,
- CharacterSetOrientation cso, Map/*<String,String>*/ codepage, int metricNormalizationFactor)
+ CharacterSetOrientation cso, Map/*<String,String>*/ codepage,
+ double metricNormalizationFactor)
throws IOException {
byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF);
@@ -442,6 +452,7 @@ public final class AFPFontReader {
int lowest = 255;
int highest = 0;
+ String firstABCMismatch = null;
// Read data, ignoring bytes 0 - 2
for (int index = 3; index < data.length; index++) {
@@ -464,7 +475,26 @@ public final class AFPFontReader {
if (idx != null) {
int cidx = idx.charAt(0);
- int width = ((fiData[0] & 0xFF) << 8) + (fiData[1] & 0xFF);
+ int width = getUBIN(fiData, 0);
+ int a = getSBIN(fiData, 10);
+ int b = getUBIN(fiData, 12);
+ int c = getSBIN(fiData, 14);
+ int abc = a + b + c;
+ int diff = Math.abs(abc - width);
+ if (diff != 0 && width != 0) {
+ double diffPercent = 100 * diff / (double)width;
+ //if difference > 2%
+ if (diffPercent > 2) {
+ if (log.isTraceEnabled()) {
+ log.trace(gcgiString + ": "
+ + a + " + " + b + " + " + c + " = " + (a + b + c)
+ + " but found: " + width);
+ }
+ if (firstABCMismatch == null) {
+ firstABCMismatch = gcgiString;
+ }
+ }
+ }
if (cidx < lowest) {
lowest = cidx;
@@ -474,9 +504,9 @@ public final class AFPFontReader {
highest = cidx;
}
- int a = (width * metricNormalizationFactor);
+ int normalizedWidth = (int)Math.round(width * metricNormalizationFactor);
- cso.setWidth(cidx, a);
+ cso.setWidth(cidx, normalizedWidth);
}
@@ -486,11 +516,32 @@ public final class AFPFontReader {
cso.setFirstChar(lowest);
cso.setLastChar(highest);
+ if (log.isDebugEnabled() && firstABCMismatch != null) {
+ //Debug level because it usually is no problem.
+ log.debug("Font has metrics inconsitencies where A+B+C doesn't equal the"
+ + " character increment. The first such character found: "
+ + firstABCMismatch);
+ }
+ }
+
+ private static int getUBIN(byte[] data, int start) {
+ return ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
+ }
+
+ private static int getSBIN(byte[] data, int start) {
+ int ubin = ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
+ if ((ubin & 0x8000) != 0) {
+ //extend sign
+ return ubin | 0xFFFF0000;
+ } else {
+ return ubin;
+ }
}
private class FontControl {
private int dpi;
+ private int unitsPerEm;
private boolean isRelative = false;
@@ -502,6 +553,14 @@ public final class AFPFontReader {
dpi = i;
}
+ public int getUnitsPerEm() {
+ return this.unitsPerEm;
+ }
+
+ public void setUnitsPerEm(int value) {
+ this.unitsPerEm = value;
+ }
+
public boolean isRelative() {
return isRelative;
}
@@ -511,4 +570,18 @@ public final class AFPFontReader {
}
}
+ private static class FontDescriptor {
+
+ private byte[] data;
+
+ public FontDescriptor(byte[] data) {
+ this.data = data;
+ }
+
+ public int getNominalFontSizeInMillipoints() {
+ int nominalFontSize = 100 * getUBIN(data, 39);
+ return nominalFontSize;
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSet.java b/src/java/org/apache/fop/afp/fonts/CharacterSet.java
index 9573506b3..48d5f4f30 100644
--- a/src/java/org/apache/fop/afp/fonts/CharacterSet.java
+++ b/src/java/org/apache/fop/afp/fonts/CharacterSet.java
@@ -95,6 +95,9 @@ public class CharacterSet {
/** The collection of objects for each orientation */
private Map characterSetOrientations = null;
+ /** The nominal vertical size (in millipoints) for bitmap fonts. 0 for outline fonts. */
+ private int nominalVerticalSize = 0;
+
/**
* Constructor for the CharacterSetMetric object, the character set is used
* to load the font information from the actual AFP font.
@@ -159,6 +162,23 @@ public class CharacterSet {
}
/**
+ * Sets the nominal vertical size of the font in the case of bitmap fonts.
+ * @param nominalVerticalSize the nominal vertical size (in millipoints)
+ */
+ public void setNominalVerticalSize(int nominalVerticalSize) {
+ this.nominalVerticalSize = nominalVerticalSize;
+ }
+
+ /**
+ * Returns the nominal vertical size of the font in the case of bitmap fonts. For outline fonts,
+ * zero is returned, because these are scalable fonts.
+ * @return the nominal vertical size (in millipoints) for bitmap fonts, or 0 for outline fonts.
+ */
+ public int getNominalVerticalSize() {
+ return this.nominalVerticalSize;
+ }
+
+ /**
* Ascender height is the distance from the character baseline to the
* top of the character box. A negative ascender height signifies that
* all of the graphic character is below the character baseline. For
diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java b/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java
index 8ced8e356..1946fd4a4 100644
--- a/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java
+++ b/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java
@@ -191,7 +191,7 @@ public class CharacterSetOrientation {
* a character rotation other than 0, ascender height loses its
* meaning when the character is lying on its side or is upside down
* with respect to normal viewing orientation. For the general case,
- * Ascender Height is the character�s most positive y-axis value.
+ * Ascender Height is the character's most positive y-axis value.
* For bounded character boxes, for a given character having an
* ascender, ascender height and baseline offset are equal.
* @param ascender the ascender to set
diff --git a/src/java/org/apache/fop/afp/fonts/OutlineFont.java b/src/java/org/apache/fop/afp/fonts/OutlineFont.java
index b97d5f3ab..8dca69f9c 100644
--- a/src/java/org/apache/fop/afp/fonts/OutlineFont.java
+++ b/src/java/org/apache/fop/afp/fonts/OutlineFont.java
@@ -87,8 +87,7 @@ public class OutlineFont extends AFPFont {
* "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also
* used to denote the part of the letter extending above the x-height.
*
- * @param size
- * the point size
+ * @param size the font size (in mpt)
* @return the ascender for the given size
*/
public int getAscender(int size) {
@@ -98,8 +97,7 @@ public class OutlineFont extends AFPFont {
/**
* Obtains the height of capital letters for the specified point size.
*
- * @param size
- * the point size
+ * @param size the font size (in mpt)
* @return the cap height for the given size
*/
public int getCapHeight(int size) {
@@ -111,8 +109,7 @@ public class OutlineFont extends AFPFont {
* base line, such as "g", "j", or "p". Also used to denote the part of the
* letter extending below the base line.
*
- * @param size
- * the point size
+ * @param size the font size (in mpt)
* @return the descender for the given size
*/
public int getDescender(int size) {
@@ -122,8 +119,7 @@ public class OutlineFont extends AFPFont {
/**
* The "x-height" (the height of the letter "x").
*
- * @param size
- * the point size
+ * @param size the font size (in mpt)
* @return the x height for the given size
*/
public int getXHeight(int size) {
@@ -133,7 +129,7 @@ public class OutlineFont extends AFPFont {
/**
* Obtain the width of the character for the specified point size.
* @param character the character
- * @param size point size
+ * @param size the font size (in mpt)
* @return the width of the character for the specified point size
*/
public int getWidth(int character, int size) {
@@ -144,8 +140,7 @@ public class OutlineFont extends AFPFont {
* Get the getWidth (in 1/1000ths of a point size) of all characters in this
* character set.
*
- * @param size
- * the point size
+ * @param size the font size (in mpt)
* @return the widths of all characters
*/
public int[] getWidths(int size) {
diff --git a/src/java/org/apache/fop/afp/fonts/RasterFont.java b/src/java/org/apache/fop/afp/fonts/RasterFont.java
index 6288dadbb..115773214 100644
--- a/src/java/org/apache/fop/afp/fonts/RasterFont.java
+++ b/src/java/org/apache/fop/afp/fonts/RasterFont.java
@@ -19,8 +19,10 @@
package org.apache.fop.afp.fonts;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.SortedMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -37,8 +39,9 @@ public class RasterFont extends AFPFont {
/** Static logging instance */
protected static final Log log = LogFactory.getLog("org.apache.fop.afp.fonts");
- private final Map/*<String,CharacterSet>*/ charSets
- = new java.util.HashMap/*<String,CharacterSet>*/();
+ private final SortedMap/*<Integer,CharacterSet>*/ charSets
+ = new java.util.TreeMap/*<Integer,CharacterSet>*/();
+ private Map/*<Integer,CharacterSet>*/ substitutionCharSets;
private CharacterSet charSet = null;
@@ -55,58 +58,77 @@ public class RasterFont extends AFPFont {
/**
* Adds the character set for the given point size
- * @param size point size
+ * @param size point size (in mpt)
* @param characterSet character set
*/
public void addCharacterSet(int size, CharacterSet characterSet) {
- this.charSets.put(String.valueOf(size), characterSet);
+ //TODO: replace with Integer.valueOf() once we switch to Java 5
+ this.charSets.put(new Integer(size), characterSet);
this.charSet = characterSet;
}
- /** Describes the unit millipoint. */
- public static final String MPT = "mpt";
-
/**
* Get the character set metrics for the specified point size.
*
- * @param size the point size
+ * @param size the point size (in mpt)
* @return the character set metrics
*/
public CharacterSet getCharacterSet(int size) {
- String pointsize = String.valueOf(size / 1000);
- CharacterSet csm = (CharacterSet) charSets.get(pointsize);
- if (csm == null) {
- csm = (CharacterSet) charSets.get(size + MPT);
+ //TODO: replace with Integer.valueOf() once we switch to Java 5
+ Integer requestedSize = new Integer(size);
+ CharacterSet csm = (CharacterSet) charSets.get(requestedSize);
+
+ if (csm != null) {
+ return csm;
}
- if (csm == null) {
- // Get char set with nearest font size
- int distance = Integer.MAX_VALUE;
- for (Iterator it = charSets.entrySet().iterator(); it.hasNext();) {
- Map.Entry me = (Map.Entry)it.next();
- String key = (String)me.getKey();
- if (!key.endsWith(MPT)) {
- int mpt = Integer.parseInt(key) * 1000;
- if (Math.abs(size - mpt) < distance) {
- distance = Math.abs(size - mpt);
- pointsize = (String)me.getKey();
- csm = (CharacterSet)me.getValue();
- }
- }
+
+ if (substitutionCharSets != null) {
+ //Check first if a substitution has already been added
+ csm = (CharacterSet) substitutionCharSets.get(requestedSize);
+ }
+
+ if (csm == null && !charSets.isEmpty()) {
+ // No match or substitution found, but there exist entries
+ // for other sizes
+ // Get char set with nearest, smallest font size
+ SortedMap smallerSizes = charSets.headMap(requestedSize);
+ SortedMap largerSizes = charSets.tailMap(requestedSize);
+ int smallerSize = smallerSizes.isEmpty() ? 0
+ : ((Integer)smallerSizes.lastKey()).intValue();
+ int largerSize = largerSizes.isEmpty() ? Integer.MAX_VALUE
+ : ((Integer)largerSizes.firstKey()).intValue();
+
+ Integer fontSize;
+ if (!smallerSizes.isEmpty()
+ && (size - smallerSize) <= (largerSize - size)) {
+ fontSize = new Integer(smallerSize);
+ } else {
+ fontSize = new Integer(largerSize);
}
+ csm = (CharacterSet) charSets.get(fontSize);
+
if (csm != null) {
- charSets.put(size + MPT, csm);
- String msg = "No " + (size / 1000) + "pt font " + getFontName()
- + " found, substituted with " + pointsize + "pt font";
+ // Add the substitute mapping, so subsequent calls will
+ // find it immediately
+ if (substitutionCharSets == null) {
+ substitutionCharSets = new HashMap();
+ }
+ substitutionCharSets.put(requestedSize, csm);
+ String msg = "No " + (size / 1000f) + "pt font " + getFontName()
+ + " found, substituted with " + fontSize.intValue() / 1000f + "pt font";
log.warn(msg);
}
}
+
if (csm == null) {
+ // Still no match -> error
String msg = "No font found for font " + getFontName()
- + " with point size " + pointsize;
+ + " with point size " + size / 1000f;
log.error(msg);
throw new FontRuntimeException(msg);
}
+
return csm;
}
@@ -145,26 +167,37 @@ public class RasterFont extends AFPFont {
}
+ private int metricsToAbsoluteSize(CharacterSet cs, int value, int givenSize) {
+ int nominalVerticalSize = cs.getNominalVerticalSize();
+ if (nominalVerticalSize != 0) {
+ return value * nominalVerticalSize;
+ } else {
+ return value * givenSize;
+ }
+ }
+
/**
* The ascender is the part of a lowercase letter that extends above the
* "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also
* used to denote the part of the letter extending above the x-height.
*
- * @param size the point size
+ * @param size the font size (in mpt)
* @return the ascender for the given point size
*/
public int getAscender(int size) {
- return getCharacterSet(size).getAscender() * size;
+ CharacterSet cs = getCharacterSet(size);
+ return metricsToAbsoluteSize(cs, cs.getAscender(), size);
}
/**
* Obtains the height of capital letters for the specified point size.
*
- * @param size the point size
+ * @param size the font size (in mpt)
* @return the cap height for the specified point size
*/
public int getCapHeight(int size) {
- return getCharacterSet(size).getCapHeight() * size;
+ CharacterSet cs = getCharacterSet(size);
+ return metricsToAbsoluteSize(cs, cs.getCapHeight(), size);
}
/**
@@ -172,43 +205,50 @@ public class RasterFont extends AFPFont {
* base line, such as "g", "j", or "p". Also used to denote the part of the
* letter extending below the base line.
*
- * @param size the point size
+ * @param size the font size (in mpt)
* @return the descender for the specified point size
*/
public int getDescender(int size) {
- return getCharacterSet(size).getDescender() * size;
+ CharacterSet cs = getCharacterSet(size);
+ return metricsToAbsoluteSize(cs, cs.getDescender(), size);
}
/**
* The "x-height" (the height of the letter "x").
*
- * @param size the point size
+ * @param size the font size (in mpt)
* @return the x height for the given point size
*/
public int getXHeight(int size) {
- return getCharacterSet(size).getXHeight() * size;
+ CharacterSet cs = getCharacterSet(size);
+ return metricsToAbsoluteSize(cs, cs.getXHeight(), size);
}
/**
* Obtain the width of the character for the specified point size.
* @param character the character
- * @param size the point size
+ * @param size the font size (in mpt)
* @return the width for the given point size
*/
public int getWidth(int character, int size) {
- return getCharacterSet(size).getWidth(character) * size;
+ CharacterSet cs = getCharacterSet(size);
+ return metricsToAbsoluteSize(cs, cs.getWidth(character), size);
}
/**
* Get the getWidth (in 1/1000ths of a point size) of all characters in this
* character set.
*
- * @param size
- * the point size
+ * @param size the font size (in mpt)
* @return the widths of all characters
*/
public int[] getWidths(int size) {
- return getCharacterSet(size).getWidths();
+ CharacterSet cs = getCharacterSet(size);
+ int[] widths = cs.getWidths();
+ for (int i = 0, c = widths.length; i < c; i++) {
+ widths[i] = metricsToAbsoluteSize(cs, widths[i], size);
+ }
+ return widths;
}
/**
@@ -239,5 +279,4 @@ public class RasterFont extends AFPFont {
public String getEncodingName() {
return charSet.getEncoding();
}
-
}
diff --git a/src/java/org/apache/fop/afp/goca/AbstractGraphicsCoord.java b/src/java/org/apache/fop/afp/goca/AbstractGraphicsCoord.java
index 3d8495667..3c3442def 100644
--- a/src/java/org/apache/fop/afp/goca/AbstractGraphicsCoord.java
+++ b/src/java/org/apache/fop/afp/goca/AbstractGraphicsCoord.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/AbstractGraphicsDrawingOrderContainer.java b/src/java/org/apache/fop/afp/goca/AbstractGraphicsDrawingOrderContainer.java
index 34398b094..dbeaa26d0 100644
--- a/src/java/org/apache/fop/afp/goca/AbstractGraphicsDrawingOrderContainer.java
+++ b/src/java/org/apache/fop/afp/goca/AbstractGraphicsDrawingOrderContainer.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
@@ -46,6 +46,8 @@ implements StructuredData, Completable, Startable {
/** object has started */
private boolean started = false;
+ private int dataLength = 0;
+
/**
* Default constructor
*/
@@ -78,6 +80,7 @@ implements StructuredData, Completable, Startable {
*/
public void addObject(StructuredData object) {
objects.add(object);
+ dataLength += object.getDataLength();
}
/**
@@ -88,6 +91,7 @@ implements StructuredData, Completable, Startable {
public void addAll(AbstractGraphicsDrawingOrderContainer graphicsContainer) {
Collection/*<StructuredDataObject>*/ objects = graphicsContainer.getObjects();
objects.addAll(objects);
+ dataLength += graphicsContainer.getDataLength();
}
/**
@@ -107,9 +111,11 @@ implements StructuredData, Completable, Startable {
public StructuredData removeLast() {
int lastIndex = objects.size() - 1;
StructuredData object = null;
- if (lastIndex > -1) {
- object = (StructuredData)objects.get(lastIndex);
- objects.remove(lastIndex);
+ if (lastIndex >= 0) {
+ object = (StructuredData)objects.remove(lastIndex);
+ }
+ if (object != null) {
+ dataLength -= object.getDataLength();
}
return object;
}
@@ -121,12 +127,7 @@ implements StructuredData, Completable, Startable {
* all enclosed objects (and their containers)
*/
public int getDataLength() {
- int dataLen = 0;
- Iterator it = objects.iterator();
- while (it.hasNext()) {
- dataLen += ((StructuredData)it.next()).getDataLength();
- }
- return dataLen;
+ return this.dataLength;
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsBox.java b/src/java/org/apache/fop/afp/goca/GraphicsBox.java
index 945697ec2..97c08b9ee 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsBox.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsBox.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java b/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java
index 8a92db296..1162e83aa 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsChainedSegment.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java b/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java
index 70039d167..4094314a2 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsData.java b/src/java/org/apache/fop/afp/goca/GraphicsData.java
index c75057dc5..1ba757e4b 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsData.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsData.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsFillet.java b/src/java/org/apache/fop/afp/goca/GraphicsFillet.java
index 294be6d9b..9dad2fe1c 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsFillet.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsFillet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsFullArc.java b/src/java/org/apache/fop/afp/goca/GraphicsFullArc.java
index a4b6916ae..47bf53079 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsFullArc.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsFullArc.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsLine.java b/src/java/org/apache/fop/afp/goca/GraphicsLine.java
index 17bd43ce0..dea03960a 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsLine.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsLine.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetArcParameters.java b/src/java/org/apache/fop/afp/goca/GraphicsSetArcParameters.java
index 693cf21a9..6b3dc98ec 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetArcParameters.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetArcParameters.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetCharacterSet.java b/src/java/org/apache/fop/afp/goca/GraphicsSetCharacterSet.java
index b3d1158fe..f2a450516 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetCharacterSet.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetCharacterSet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetCurrentPosition.java b/src/java/org/apache/fop/afp/goca/GraphicsSetCurrentPosition.java
index 675c2f034..1335f2473 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetCurrentPosition.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetCurrentPosition.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetLineType.java b/src/java/org/apache/fop/afp/goca/GraphicsSetLineType.java
index b6512f57c..b4224b3c9 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetLineType.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetLineType.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java b/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java
index 96eac0677..4ba208bb0 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetPatternSymbol.java b/src/java/org/apache/fop/afp/goca/GraphicsSetPatternSymbol.java
index 3d6cf7cd6..e2cc081ce 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetPatternSymbol.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetPatternSymbol.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java b/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java
index 05a6ee5d1..f0c4aa321 100644
--- a/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java
+++ b/src/java/org/apache/fop/afp/goca/GraphicsSetProcessColor.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.goca;
diff --git a/src/java/org/apache/fop/afp/modca/AbstractDataObject.java b/src/java/org/apache/fop/afp/modca/AbstractDataObject.java
index fcab3cb4a..d76de9259 100644
--- a/src/java/org/apache/fop/afp/modca/AbstractDataObject.java
+++ b/src/java/org/apache/fop/afp/modca/AbstractDataObject.java
@@ -34,7 +34,8 @@ import org.apache.fop.afp.Startable;
* Abstract base class used by the ImageObject and GraphicsObject which both
* have define an ObjectEnvironmentGroup
*/
-public abstract class AbstractDataObject extends AbstractNamedAFPObject implements Startable, Completable {
+public abstract class AbstractDataObject extends AbstractNamedAFPObject
+ implements Startable, Completable {
/** the object environment group */
protected ObjectEnvironmentGroup objectEnvironmentGroup = null;
@@ -81,14 +82,14 @@ public abstract class AbstractDataObject extends AbstractNamedAFPObject implemen
AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
AFPResourceLevel resourceLevel = resourceInfo.getLevel();
ObjectAreaPosition objectAreaPosition = null;
+ int rotation = objectAreaInfo.getRotation();
if (resourceLevel.isInline()) {
int x = objectAreaInfo.getX();
int y = objectAreaInfo.getY();
- int rotation = objectAreaInfo.getRotation();
objectAreaPosition = factory.createObjectAreaPosition(x, y, rotation);
} else {
// positional values are specified in the oaOffset of the include object
- objectAreaPosition = factory.createObjectAreaPosition(0, 0, 0);
+ objectAreaPosition = factory.createObjectAreaPosition(0, 0, rotation);
}
objectAreaPosition.setReferenceCoordinateSystem(
ObjectAreaPosition.REFCSYS_PAGE_SEGMENT_RELATIVE);
diff --git a/src/java/org/apache/fop/afp/modca/AbstractEnvironmentGroup.java b/src/java/org/apache/fop/afp/modca/AbstractEnvironmentGroup.java
index 4ba9abff8..abc3aea87 100644
--- a/src/java/org/apache/fop/afp/modca/AbstractEnvironmentGroup.java
+++ b/src/java/org/apache/fop/afp/modca/AbstractEnvironmentGroup.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/AbstractPageObject.java b/src/java/org/apache/fop/afp/modca/AbstractPageObject.java
index c043faf2e..af676410f 100644
--- a/src/java/org/apache/fop/afp/modca/AbstractPageObject.java
+++ b/src/java/org/apache/fop/afp/modca/AbstractPageObject.java
@@ -25,9 +25,9 @@ import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.fop.afp.AFPLineDataInfo;
-import org.apache.fop.afp.AFPTextDataInfo;
import org.apache.fop.afp.Completable;
import org.apache.fop.afp.Factory;
+import org.apache.fop.afp.ptoca.PtocaProducer;
import org.apache.fop.afp.fonts.AFPFont;
/**
@@ -170,8 +170,10 @@ public abstract class AbstractPageObject extends AbstractNamedAFPObject implemen
* the afp text data
* @throws UnsupportedEncodingException thrown if character encoding is not supported
*/
- public void createText(AFPTextDataInfo textDataInfo) throws UnsupportedEncodingException {
- getPresentationTextObject().createTextData(textDataInfo);
+ public void createText(PtocaProducer producer) throws UnsupportedEncodingException {
+ //getPresentationTextObject().createTextData(textDataInfo);
+ getPresentationTextObject().createControlSequences(producer);
+
}
/**
@@ -212,6 +214,17 @@ public abstract class AbstractPageObject extends AbstractNamedAFPObject implemen
}
/**
+ * Returns the list of {@link TagLogicalElement}s.
+ * @return the TLEs
+ */
+ protected List getTagLogicalElements() {
+ if (tagLogicalElements == null) {
+ this.tagLogicalElements = new java.util.ArrayList/*<TagLogicalElement>*/();
+ }
+ return this.tagLogicalElements;
+ }
+
+ /**
* Creates a TagLogicalElement on the page.
*
* @param name
@@ -223,10 +236,8 @@ public abstract class AbstractPageObject extends AbstractNamedAFPObject implemen
*/
public void createTagLogicalElement(String name, String value, int tleID) {
TagLogicalElement tle = new TagLogicalElement(name, value, tleID);
- if (tagLogicalElements == null) {
- tagLogicalElements = new java.util.ArrayList/*<TagLogicalElement>*/();
- }
- tagLogicalElements.add(tle);
+ List list = getTagLogicalElements();
+ list.add(tle);
}
/**
diff --git a/src/java/org/apache/fop/afp/modca/AbstractResourceEnvironmentGroupContainer.java b/src/java/org/apache/fop/afp/modca/AbstractResourceEnvironmentGroupContainer.java
index baba170f7..1319b3cdb 100644
--- a/src/java/org/apache/fop/afp/modca/AbstractResourceEnvironmentGroupContainer.java
+++ b/src/java/org/apache/fop/afp/modca/AbstractResourceEnvironmentGroupContainer.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/AbstractResourceGroupContainer.java b/src/java/org/apache/fop/afp/modca/AbstractResourceGroupContainer.java
index 6546fa978..2c5e02328 100644
--- a/src/java/org/apache/fop/afp/modca/AbstractResourceGroupContainer.java
+++ b/src/java/org/apache/fop/afp/modca/AbstractResourceGroupContainer.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
@@ -141,12 +141,25 @@ implements Streamable {
/** {@inheritDoc} */
protected void writeObjects(Collection/*<AbstractAFPObject>*/ objects, OutputStream os)
- throws IOException {
+ throws IOException {
+ writeObjects(objects, os, false);
+ }
+
+ /**
+ * Writes a collection of {@link AbstractAFPObject}s to the AFP Datastream.
+ *
+ * @param objects a list of AFPObjects
+ * @param os The stream to write to
+ * @param forceWrite true if writing should happen in any case
+ * @throws java.io.IOException an I/O exception of some sort has occurred.
+ */
+ protected void writeObjects(Collection/*<AbstractAFPObject>*/ objects, OutputStream os,
+ boolean forceWrite) throws IOException {
if (objects != null && objects.size() > 0) {
Iterator it = objects.iterator();
while (it.hasNext()) {
AbstractAFPObject ao = (AbstractAFPObject)it.next();
- if (canWrite(ao)) {
+ if (forceWrite || canWrite(ao)) {
ao.writeToStream(os);
it.remove();
} else {
diff --git a/src/java/org/apache/fop/afp/modca/ImageObject.java b/src/java/org/apache/fop/afp/modca/ImageObject.java
index bbbc25bea..65802f6ca 100644
--- a/src/java/org/apache/fop/afp/modca/ImageObject.java
+++ b/src/java/org/apache/fop/afp/modca/ImageObject.java
@@ -28,7 +28,6 @@ import org.apache.fop.afp.AFPDataObjectInfo;
import org.apache.fop.afp.AFPImageObjectInfo;
import org.apache.fop.afp.Factory;
import org.apache.fop.afp.ioca.ImageSegment;
-import org.apache.fop.afp.modca.triplets.MappingOptionTriplet;
/**
* An IOCA Image Data Object
@@ -66,10 +65,6 @@ public class ImageObject extends AbstractDataObject {
int dataWidth = imageObjectInfo.getDataWidth();
int dataHeight = imageObjectInfo.getDataHeight();
-// AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo();
-// int widthRes = objectAreaInfo.getWidthRes();
-// int heightRes = objectAreaInfo.getHeightRes();
-
int dataWidthRes = imageObjectInfo.getDataWidthRes();
int dataHeightRes = imageObjectInfo.getDataWidthRes();
ImageDataDescriptor imageDataDescriptor
@@ -79,7 +74,7 @@ public class ImageObject extends AbstractDataObject {
}
getObjectEnvironmentGroup().setDataDescriptor(imageDataDescriptor);
getObjectEnvironmentGroup().setMapImageObject(
- new MapImageObject(MappingOptionTriplet.SCALE_TO_FILL));
+ new MapImageObject(dataObjectInfo.getMappingOption()));
getImageSegment().setImageSize(dataWidth, dataHeight, dataWidthRes, dataHeightRes);
}
diff --git a/src/java/org/apache/fop/afp/modca/InterchangeSet.java b/src/java/org/apache/fop/afp/modca/InterchangeSet.java
index 28a4da42b..f4b020239 100644
--- a/src/java/org/apache/fop/afp/modca/InterchangeSet.java
+++ b/src/java/org/apache/fop/afp/modca/InterchangeSet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/MapCodedFont.java b/src/java/org/apache/fop/afp/modca/MapCodedFont.java
index 54b4d1796..e732a8bb7 100644
--- a/src/java/org/apache/fop/afp/modca/MapCodedFont.java
+++ b/src/java/org/apache/fop/afp/modca/MapCodedFont.java
@@ -206,7 +206,7 @@ public class MapCodedFont extends AbstractStructuredObject {
// There are approximately 72 points to 1 inch or 20 1440ths per point.
- fontDefinition.scale = ((size / 1000) * 20);
+ fontDefinition.scale = 20 * size / 1000;
fontDefinition.codePage = cs.getCodePage().getBytes(
AFPConstants.EBCIDIC_ENCODING);
diff --git a/src/java/org/apache/fop/afp/modca/MapDataResource.java b/src/java/org/apache/fop/afp/modca/MapDataResource.java
index 566f60ce5..0bac920bd 100644
--- a/src/java/org/apache/fop/afp/modca/MapDataResource.java
+++ b/src/java/org/apache/fop/afp/modca/MapDataResource.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/ObjectContainer.java b/src/java/org/apache/fop/afp/modca/ObjectContainer.java
index 39b935d01..e5a57ebe9 100644
--- a/src/java/org/apache/fop/afp/modca/ObjectContainer.java
+++ b/src/java/org/apache/fop/afp/modca/ObjectContainer.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/PageGroup.java b/src/java/org/apache/fop/afp/modca/PageGroup.java
index 4e578718b..f70b6fc52 100644
--- a/src/java/org/apache/fop/afp/modca/PageGroup.java
+++ b/src/java/org/apache/fop/afp/modca/PageGroup.java
@@ -21,7 +21,6 @@ package org.apache.fop.afp.modca;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.List;
import org.apache.fop.afp.Factory;
@@ -36,9 +35,6 @@ import org.apache.fop.afp.Factory;
*/
public class PageGroup extends AbstractResourceEnvironmentGroupContainer {
- /** The tag logical elements contained within this group */
- private List tagLogicalElements = null;
-
/**
* Sequence number for TLE's.
*/
@@ -56,13 +52,6 @@ public class PageGroup extends AbstractResourceEnvironmentGroupContainer {
this.tleSequence = tleSequence;
}
- private List getTagLogicalElements() {
- if (tagLogicalElements == null) {
- this.tagLogicalElements = new java.util.ArrayList();
- }
- return this.tagLogicalElements;
- }
-
/**
* Creates a TagLogicalElement on the page.
*
@@ -88,7 +77,7 @@ public class PageGroup extends AbstractResourceEnvironmentGroupContainer {
/** {@inheritDoc} */
protected void writeContent(OutputStream os) throws IOException {
- writeObjects(tagLogicalElements, os);
+ writeObjects(tagLogicalElements, os, true);
super.writeContent(os);
}
diff --git a/src/java/org/apache/fop/afp/modca/PageSegment.java b/src/java/org/apache/fop/afp/modca/PageSegment.java
index ab1388efb..b765d6c2f 100644
--- a/src/java/org/apache/fop/afp/modca/PageSegment.java
+++ b/src/java/org/apache/fop/afp/modca/PageSegment.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java b/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java
index 72e261662..ff16e89b6 100644
--- a/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java
+++ b/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/Registry.java b/src/java/org/apache/fop/afp/modca/Registry.java
index 481a72afd..eade967ec 100644
--- a/src/java/org/apache/fop/afp/modca/Registry.java
+++ b/src/java/org/apache/fop/afp/modca/Registry.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/ResourceEnvironmentGroup.java b/src/java/org/apache/fop/afp/modca/ResourceEnvironmentGroup.java
index 9a898ef4d..3f9258741 100644
--- a/src/java/org/apache/fop/afp/modca/ResourceEnvironmentGroup.java
+++ b/src/java/org/apache/fop/afp/modca/ResourceEnvironmentGroup.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/ResourceObject.java b/src/java/org/apache/fop/afp/modca/ResourceObject.java
index 0f555a42e..6f97bed93 100644
--- a/src/java/org/apache/fop/afp/modca/ResourceObject.java
+++ b/src/java/org/apache/fop/afp/modca/ResourceObject.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca;
diff --git a/src/java/org/apache/fop/afp/modca/TagLogicalElement.java b/src/java/org/apache/fop/afp/modca/TagLogicalElement.java
index 9ccd58bfb..5c1f7bbbb 100644
--- a/src/java/org/apache/fop/afp/modca/TagLogicalElement.java
+++ b/src/java/org/apache/fop/afp/modca/TagLogicalElement.java
@@ -64,7 +64,7 @@ public class TagLogicalElement extends AbstractAFPObject {
/**
* Construct a tag logical element with the name and value specified.
- *
+ *
* @param name the name of the tag logical element
* @param value the value of the tag logical element
* @param tleID unique identifier for TLE within AFP stream
@@ -135,7 +135,7 @@ public class TagLogicalElement extends AbstractAFPObject {
data[pos++] = tleByteValue[i];
}
// attribute qualifier
- data[pos++] = 0x10;
+ data[pos++] = 0x0A;
data[pos++] = (byte)0x80;
byte[] id = BinaryUtils.convert(tleID, 4);
for (int i = 0; i < id.length; i++) {
diff --git a/src/java/org/apache/fop/afp/modca/triplets/AbstractTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/AbstractTriplet.java
index 4e75d4204..598df1b98 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/AbstractTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/AbstractTriplet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca.triplets;
diff --git a/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java
index 55653457c..65c438199 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca.triplets;
diff --git a/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java
index 0d20d0227..2f19eca83 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/MappingOptionTriplet.java
@@ -50,9 +50,14 @@ public class MappingOptionTriplet extends AbstractTriplet {
*/
public static final byte CENTER_AND_TRIM = 0x30;
-// public static final byte MIGRATION_MAPPING_1 = 0x41;
-// public static final byte MIGRATION_MAPPING_2 = 0x42;
-// public static final byte MIGRATION_MAPPING_3 = 0x50;
+ /** Migration mapping option: Image point-to-pel. */
+ public static final byte IMAGE_POINT_TO_PEL = 0x41;
+
+ /** Migration mapping option: Image point-to-pel with double dot. */
+ public static final byte IMAGE_POINT_TO_PEL_DOUBLE_DOT = 0x42;
+
+ /** Migration mapping option: Replicate and trim. */
+ public static final byte REPLICATE_AND_TRIM = 0x50;
/** the data object is centred, aspect ratio is not always preserved */
public static final byte SCALE_TO_FILL = 0x60;
diff --git a/src/java/org/apache/fop/afp/modca/triplets/MeasurementUnitsTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/MeasurementUnitsTriplet.java
index 68d3fc40c..b04c6d43c 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/MeasurementUnitsTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/MeasurementUnitsTriplet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca.triplets;
diff --git a/src/java/org/apache/fop/afp/modca/triplets/ObjectAreaSizeTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/ObjectAreaSizeTriplet.java
index 3d408639e..1b1aa5b88 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/ObjectAreaSizeTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/ObjectAreaSizeTriplet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca.triplets;
diff --git a/src/java/org/apache/fop/afp/modca/triplets/ObjectClassificationTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/ObjectClassificationTriplet.java
index 9c2ab7bc4..8430a47ee 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/ObjectClassificationTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/ObjectClassificationTriplet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca.triplets;
diff --git a/src/java/org/apache/fop/afp/modca/triplets/ResourceObjectTypeTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/ResourceObjectTypeTriplet.java
index e4b13177d..a6d83f83b 100644
--- a/src/java/org/apache/fop/afp/modca/triplets/ResourceObjectTypeTriplet.java
+++ b/src/java/org/apache/fop/afp/modca/triplets/ResourceObjectTypeTriplet.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.afp.modca.triplets;
diff --git a/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java b/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java
index 3a6507252..40adb5ed8 100644
--- a/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java
+++ b/src/java/org/apache/fop/afp/ptoca/PtocaBuilder.java
@@ -20,6 +20,7 @@
package org.apache.fop.afp.ptoca;
import java.awt.Color;
+import java.awt.color.ColorSpace;
import java.io.IOException;
import java.io.OutputStream;
@@ -314,19 +315,38 @@ public abstract class PtocaBuilder implements PtocaConstants {
return;
}
newControlSequence();
- writeByte(0x00); // Reserved; must be zero
- writeByte(0x01); // Color space - 0x01 = RGB
- writeByte(0x00); // Reserved; must be zero
- writeByte(0x00); // Reserved; must be zero
- writeByte(0x00); // Reserved; must be zero
- writeByte(0x00); // Reserved; must be zero
- writeByte(8); // Number of bits in component 1
- writeByte(8); // Number of bits in component 2
- writeByte(8); // Number of bits in component 3
- writeByte(0); // Number of bits in component 4
- writeByte(col.getRed()); // Red intensity
- writeByte(col.getGreen()); // Green intensity
- writeByte(col.getBlue()); // Blue intensity
+ if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x04); // Color space - 0x04 = CMYK
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(8); // Number of bits in component 1
+ writeByte(8); // Number of bits in component 2
+ writeByte(8); // Number of bits in component 3
+ writeByte(8); // Number of bits in component 4
+ float[] comps = col.getColorComponents(null);
+ assert comps.length == 4;
+ for (int i = 0; i < 4; i++) {
+ int component = Math.round(comps[i] * 256);
+ writeByte(component);
+ }
+ } else {
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x01); // Color space - 0x01 = RGB
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(0x00); // Reserved; must be zero
+ writeByte(8); // Number of bits in component 1
+ writeByte(8); // Number of bits in component 2
+ writeByte(8); // Number of bits in component 3
+ writeByte(0); // Number of bits in component 4
+ writeByte(col.getRed()); // Red intensity
+ writeByte(col.getGreen()); // Green intensity
+ writeByte(col.getBlue()); // Blue intensity
+ }
commit(chained(SEC));
this.currentColor = col;
}
diff --git a/src/java/org/apache/fop/afp/util/CubicBezierApproximator.java b/src/java/org/apache/fop/afp/util/CubicBezierApproximator.java
new file mode 100644
index 000000000..d3ed41c76
--- /dev/null
+++ b/src/java/org/apache/fop/afp/util/CubicBezierApproximator.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.util;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Point2D.Double;
+
+/**
+ * This class can be used to convert a cubic bezier curve within
+ * a path into multiple quadratic bezier curves which will approximate
+ * the original cubic curve.
+ * The various techniques are described here:
+ * http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm
+ */
+public class CubicBezierApproximator {
+
+ /**
+ * This method will take in an array containing the x and y coordinates of the four control
+ * points that describe the cubic bezier curve to be approximated using the fixed mid point
+ * approximation. The curve will be approximated using four quadratic bezier curves the points
+ * for which will be returned in a two dimensional array, with each array within that containing
+ * the points for a single quadratic curve. The returned data will not include the start point
+ * for any of the curves; the first point passed in to this method should already have been
+ * set as the current position and will be the assumed start of the first curve.
+ *
+ * @param cubicControlPointCoords an array containing the x and y coordinates of the
+ * four control points.
+ * @return an array of arrays containing the x and y coordinates of the quadratic curves
+ * that approximate the original supplied cubic bezier curve.
+ */
+ public static double[][] fixedMidPointApproximation(double[] cubicControlPointCoords) {
+ if (cubicControlPointCoords.length < 8) {
+ throw new IllegalArgumentException("Must have at least 8 coordinates");
+ }
+
+ //extract point objects from source array
+ Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]);
+ Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]);
+ Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]);
+ Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]);
+
+ //calculates the useful base points
+ Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0);
+ Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0);
+
+ //get 1/16 of the [P3, P0] segment
+ double dx = (p3.getX() - p0.getX()) / 16.0;
+ double dy = (p3.getY() - p0.getY()) / 16.0;
+
+ //calculates control point 1
+ Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0);
+
+ //calculates control point 2
+ Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0);
+ pc2 = movePoint(pc2, -dx, -dy);
+
+ //calculates control point 3
+ Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0);
+ pc3 = movePoint(pc3, dx, dy);
+
+ //calculates control point 4
+ Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0);
+
+ //calculates the 3 anchor points
+ Point2D pa1 = getMidPoint(pc1, pc2);
+ Point2D pa2 = getMidPoint(pa, pb);
+ Point2D pa3 = getMidPoint(pc3, pc4);
+
+ //return the points for the four quadratic curves
+ return new double[][] {
+ {pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()},
+ {pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()},
+ {pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()},
+ {pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}};
+ }
+
+ private static Double movePoint(Point2D point, double dx, double dy) {
+ return new Point2D.Double(point.getX() + dx, point.getY() + dy);
+ }
+
+ /**
+ * This method will calculate the coordinates of a point half way along a segment [P0, P1]
+ *
+ * @param p0 - The point describing the start of the segment.
+ * @param p1 - The point describing the end of the segment.
+ * @return a Point object describing the coordinates of the calculated point on the segment.
+ */
+ private static Point2D getMidPoint(Point2D p0, Point2D p1) {
+ return getPointOnSegment(p0, p1, 0.5);
+ }
+
+ /**
+ * This method will calculate the coordinates of a point on a segment [P0, P1]
+ * whose distance along the segment [P0, P1] from P0, is the given ratio
+ * of the length the [P0, P1] segment.
+ *
+ * @param p0 The point describing the start of the segment.
+ * @param p1 The point describing the end of the segment.
+ * @param ratio The distance of the point being calculated from P0 as a ratio of
+ * the segment length.
+ * @return a Point object describing the coordinates of the calculated point on the segment.
+ */
+ private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) {
+ double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio);
+ double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio);
+ return new Point2D.Double(x, y);
+ }
+
+}
diff --git a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
index 97646542b..053db01af 100644
--- a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
+++ b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
@@ -63,8 +63,10 @@ public class DefaultFOPResourceAccessor extends SimpleResourceAccessor {
URI resolved = resolveAgainstBase(uri);
//Step 2: resolve against the user agent --> stream
- Source src;
- src = userAgent.resolveURI(resolved.toASCIIString(), this.categoryBaseURI);
+ String base = (this.categoryBaseURI != null
+ ? this.categoryBaseURI
+ : this.userAgent.getBaseURL());
+ Source src = userAgent.resolveURI(resolved.toASCIIString(), base);
if (src == null) {
throw new FileNotFoundException("Resource not found: " + uri.toASCIIString());
diff --git a/src/java/org/apache/fop/apps/FOURIResolver.java b/src/java/org/apache/fop/apps/FOURIResolver.java
index f96711d31..c7d564ea3 100644
--- a/src/java/org/apache/fop/apps/FOURIResolver.java
+++ b/src/java/org/apache/fop/apps/FOURIResolver.java
@@ -68,6 +68,8 @@ public class FOURIResolver implements javax.xml.transform.URIResolver {
* @throws MalformedURLException if there's a problem with a file URL
*/
public String checkBaseURL(String base) throws MalformedURLException {
+ // replace back slash with forward slash to ensure windows file:/// URLS are supported
+ base = base.replace('\\', '/');
if (!base.endsWith("/")) {
// The behavior described by RFC 3986 regarding resolution of relative
// references may be misleading for normal users:
diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java
index d2af24a01..25d110086 100644
--- a/src/java/org/apache/fop/area/AreaTreeParser.java
+++ b/src/java/org/apache/fop/area/AreaTreeParser.java
@@ -20,6 +20,7 @@
package org.apache.fop.area;
import java.awt.Color;
+import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -38,9 +39,10 @@ import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
-
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
@@ -48,9 +50,6 @@ import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
@@ -404,7 +403,7 @@ public class AreaTreeParser {
if (currentPageViewport != null) {
throw new IllegalStateException("currentPageViewport must be null");
}
- Rectangle2D viewArea = XMLUtil.getAttributeAsRectangle2D(attributes, "bounds");
+ Rectangle viewArea = XMLUtil.getAttributeAsRectangle(attributes, "bounds");
int pageNumber = XMLUtil.getAttributeAsInt(attributes, "nr", -1);
String key = attributes.getValue("key");
String pageNumberString = attributes.getValue("formatted-nr");
diff --git a/src/java/org/apache/fop/area/PageViewport.java b/src/java/org/apache/fop/area/PageViewport.java
index 321ca4c03..63740386e 100644
--- a/src/java/org/apache/fop/area/PageViewport.java
+++ b/src/java/org/apache/fop/area/PageViewport.java
@@ -20,7 +20,6 @@
package org.apache.fop.area;
import java.awt.Rectangle;
-import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -48,7 +47,7 @@ import org.apache.fop.fo.pagination.SimplePageMaster;
public class PageViewport extends AreaTreeObject implements Resolvable, Cloneable {
private Page page;
- private Rectangle2D viewArea;
+ private Rectangle viewArea;
private String simplePageMasterName;
/**
@@ -100,6 +99,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl
public PageViewport(SimplePageMaster spm, int pageNumber, String pageStr, boolean blank) {
this.simplePageMasterName = spm.getMasterName();
setExtensionAttachments(spm.getExtensionAttachments());
+ setForeignAttributes(spm.getForeignAttributes());
this.blank = blank;
int pageWidth = spm.getPageWidth().getValue();
int pageHeight = spm.getPageHeight().getValue();
@@ -118,11 +118,14 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl
if (original.extensionAttachments != null) {
setExtensionAttachments(original.extensionAttachments);
}
+ if (original.foreignAttributes != null) {
+ setForeignAttributes(original.foreignAttributes);
+ }
this.pageIndex = original.pageIndex;
this.pageNumber = original.pageNumber;
this.pageNumberString = original.pageNumberString;
this.page = (Page)original.page.clone();
- this.viewArea = (Rectangle2D)original.viewArea.clone();
+ this.viewArea = new Rectangle(original.viewArea);
this.simplePageMasterName = original.simplePageMasterName;
this.blank = original.blank;
}
@@ -135,7 +138,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl
* @param simplePageMasterName name of the original simple-page-master that generated this page
* @param blank true if this is a blank page
*/
- public PageViewport(Rectangle2D viewArea, int pageNumber, String pageStr,
+ public PageViewport(Rectangle viewArea, int pageNumber, String pageStr,
String simplePageMasterName, boolean blank) {
this.viewArea = viewArea;
this.pageNumber = pageNumber;
@@ -161,7 +164,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl
* Get the view area rectangle of this viewport.
* @return the rectangle for this viewport
*/
- public Rectangle2D getViewArea() {
+ public Rectangle getViewArea() {
return viewArea;
}
diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java
index e82ce13a7..ece99d1de 100644
--- a/src/java/org/apache/fop/cli/CommandLineOptions.java
+++ b/src/java/org/apache/fop/cli/CommandLineOptions.java
@@ -30,10 +30,9 @@ import java.util.Vector;
import javax.swing.UIManager;
-import org.xml.sax.SAXException;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.xml.sax.SAXException;
import org.apache.fop.Version;
import org.apache.fop.accessibility.AccessibilityUtil;
@@ -336,8 +335,8 @@ public class CommandLineOptions {
} else if (args[i].equals("-a")) {
this.renderingOptions.put(AccessibilityUtil.ACCESSIBILITY, Boolean.TRUE);
} else if (args[i].equals("-v")) {
+ /* Currently just print the version */
printVersion();
- return false;
} else if (args[i].equals("-param")) {
if (i + 2 < args.length) {
String name = args[++i];
@@ -360,6 +359,9 @@ public class CommandLineOptions {
getPDFEncryptionParams().setAllowEditContent(false);
} else if (args[i].equals("-noannotations")) {
getPDFEncryptionParams().setAllowEditAnnotations(false);
+ } else if (args[i].equals("-version")) {
+ printVersion();
+ return false;
} else if (!isOption(args[i])) {
i = i + parseUnknownOption(args, i);
} else {
@@ -1122,6 +1124,7 @@ public class CommandLineOptions {
"\nUSAGE\nfop [options] [-fo|-xml] infile [-xsl file] "
+ "[-awt|-pdf|-mif|-rtf|-tiff|-png|-pcl|-ps|-txt|-at [mime]|-print] <outfile>\n"
+ " [OPTIONS] \n"
+ + " -version print FOP version and exit\n"
+ " -d debug mode \n"
+ " -x dump configuration settings \n"
+ " -q quiet mode \n"
@@ -1130,7 +1133,7 @@ public class CommandLineOptions {
+ " -r relaxed/less strict validation (where available)\n"
+ " -dpi xxx target resolution in dots per inch (dpi) where xxx is a number\n"
+ " -s for area tree XML, down to block areas only\n"
- + " -v to show FOP version being used\n\n"
+ + " -v run in verbose mode (currently simply print FOP version and continue)\n\n"
+ " -o [password] PDF file will be encrypted with option owner password\n"
+ " -u [password] PDF file will be encrypted with option user password\n"
+ " -noprint PDF file will be encrypted without printing permission\n"
@@ -1140,8 +1143,8 @@ public class CommandLineOptions {
+ " -a enables accessibility features (Tagged PDF etc., default off)\n"
+ " -pdfprofile prof PDF file will be generated with the specified profile\n"
+ " (Examples for prof: PDF/A-1b or PDF/X-3:2003)\n\n"
- + " -conserve Enable memory-conservation policy (trades memory-consumption for disk I/O)"
- + " (Note: currently only influences whether the area tree is serialized.)"
+ + " -conserve Enable memory-conservation policy (trades memory-consumption for disk I/O)\n"
+ + " (Note: currently only influences whether the area tree is serialized.)\n\n"
+ " [INPUT] \n"
+ " infile xsl:fo input file (the same as the next) \n"
+ " (use '-' for infile to pipe input from stdin)\n"
diff --git a/src/java/org/apache/fop/events/EventFormatter.xml b/src/java/org/apache/fop/events/EventFormatter.xml
index d26fbeb93..147744a0d 100644
--- a/src/java/org/apache/fop/events/EventFormatter.xml
+++ b/src/java/org/apache/fop/events/EventFormatter.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
@@ -13,7 +14,9 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---><!-- $Id$ --><catalogue xml:lang="en">
+-->
+<!-- $Id$ -->
+<catalogue xml:lang="en">
<message key="locator">[ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]</message>
<message key="rule.markerDescendantOfFlow">An fo:marker is permitted only as the descendant of an fo:flow.</message>
<message key="rule.retrieveMarkerDescendantOfStaticContent">An fo:retrieve-marker is permitted only as the descendant of an fo:static-content.</message>
@@ -102,7 +105,4 @@ Any reference to it will be considered a reference to the first occurrence in th
<message key="org.apache.fop.fonts.FontEventAdapter.fontSubstituted">Font "{requested}" not found. Substituting with "{effective}".</message>
<message key="org.apache.fop.fonts.FontEventAdapter.fontLoadingErrorAtAutoDetection">Unable to load font file: {fontURL}.[ Reason: {e}]</message>
<message key="org.apache.fop.fonts.FontEventAdapter.glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message>
-<message key="org.apache.fop.afp.AFPEventProducer.warnDefaultFontSetup"/>
-<message key="org.apache.fop.afp.AFPEventProducer.warnMissingDefaultFont"/>
-<message key="org.apache.fop.afp.AFPEventProducer.characterSetEncodingError"/>
</catalogue>
diff --git a/src/java/org/apache/fop/events/EventFormatter_de.xml b/src/java/org/apache/fop/events/EventFormatter_de.xml
index c65d24f73..0e2bd5c44 100644
--- a/src/java/org/apache/fop/events/EventFormatter_de.xml
+++ b/src/java/org/apache/fop/events/EventFormatter_de.xml
@@ -1,23 +1,23 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- $Id$ -->
-<catalogue xml:lang="de">
- <message key="locator">[ (Siehe Position {loc})| (Siehe {#gatherContextInfo})| (Keine Kontextinformationen verfügbar)]</message>
- <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">In "{elementName}" darf nur ein einziges "{offendingNode}" vorkommen!{{locator}}</message>
- <message key="org.apache.fop.fo.FOValidationEventProducer.missingProperty">Dem Element "{elementName}" fehlt ein verlangtes Property "{propertyName}"!{{locator}}</message>
-</catalogue>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<catalogue xml:lang="de">
+ <message key="locator">[ (Siehe Position {loc})| (Siehe {#gatherContextInfo})| (Keine Kontextinformationen verfügbar)]</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">In "{elementName}" darf nur ein einziges "{offendingNode}" vorkommen!{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.missingProperty">Dem Element "{elementName}" fehlt ein verlangtes Property "{propertyName}"!{{locator}}</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java
index 8c4e3416d..67f5d1d30 100644
--- a/src/java/org/apache/fop/fo/FONode.java
+++ b/src/java/org/apache/fop/fo/FONode.java
@@ -23,13 +23,12 @@ package org.apache.fop.fo;
import java.util.ListIterator;
import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.helpers.LocatorImpl;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.xmlgraphics.util.QName;
import org.apache.fop.apps.FOPException;
@@ -91,23 +90,19 @@ public abstract class FONode implements Cloneable {
*/
public FONode clone(FONode cloneparent, boolean removeChildren)
throws FOPException {
- try {
- FONode foNode = (FONode) clone();
- foNode.parent = cloneparent;
- foNode.siblings = null;
- return foNode;
- } catch (CloneNotSupportedException cnse) {
- return null;
- }
+ FONode foNode = (FONode) clone();
+ foNode.parent = cloneparent;
+ foNode.siblings = null;
+ return foNode;
}
- /**
- * Perform a shallow cloning operation
- *
- * {@inheritDoc}
- */
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
+ /** {@inheritDoc} */
+ protected Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // Can't happen
+ }
}
/**
diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java
index 7c77f0697..c848eb4f1 100644
--- a/src/java/org/apache/fop/fo/FOTreeBuilder.java
+++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java
@@ -288,6 +288,9 @@ public class FOTreeBuilder extends DefaultHandler {
builderContext.switchMarkerContext(true);
}
}
+ if (foNode.getNameId() == Constants.FO_PAGE_SEQUENCE) {
+ builderContext.getXMLWhiteSpaceHandler().reset();
+ }
} catch (IllegalArgumentException e) {
throw new SAXException(e);
}
diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java
index 55844e6db..2ebfaaf95 100644
--- a/src/java/org/apache/fop/fo/FObj.java
+++ b/src/java/org/apache/fop/fo/FObj.java
@@ -53,7 +53,7 @@ public abstract class FObj extends FONode implements Constants {
protected FONode firstChild;
/** The list of extension attachments, null if none */
- private List extensionAttachments = null;
+ private List/*<ExtensionAttachment>*/ extensionAttachments = null;
/** The map of foreign attributes, null if none */
private Map foreignAttributes = null;
@@ -554,7 +554,7 @@ public abstract class FObj extends FONode implements Constants {
"Parameter attachment must not be null");
}
if (extensionAttachments == null) {
- extensionAttachments = new java.util.ArrayList();
+ extensionAttachments = new java.util.ArrayList/*<ExtensionAttachment>*/();
}
if (log.isDebugEnabled()) {
log.debug("ExtensionAttachment of category "
@@ -565,7 +565,7 @@ public abstract class FObj extends FONode implements Constants {
}
/** @return the extension attachments of this FObj. */
- public List getExtensionAttachments() {
+ public List/*<ExtensionAttachment>*/ getExtensionAttachments() {
if (extensionAttachments == null) {
return Collections.EMPTY_LIST;
} else {
diff --git a/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java b/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java
index cad9fb729..51e84551c 100644
--- a/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java
+++ b/src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java
@@ -234,6 +234,18 @@ public class XMLWhiteSpaceHandler {
}
/**
+ * Reset the handler, release all references
+ */
+ protected final void reset() {
+ if (pendingInlines != null) {
+ pendingInlines.clear();
+ }
+ nestedBlockStack.clear();
+ charIter = null;
+ firstWhiteSpaceInSeq = null;
+ }
+
+ /**
* Handle white-space for the fo that is passed in, starting at
* firstTextNode (when a nested FO is encountered)
* @param fo the FO for which to handle white-space
diff --git a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java
index 2069945f1..9e2ef2e7d 100644
--- a/src/java/org/apache/fop/fo/expr/ICCColorFunction.java
+++ b/src/java/org/apache/fop/fo/expr/ICCColorFunction.java
@@ -24,6 +24,7 @@ import org.apache.fop.fo.pagination.ColorProfile;
import org.apache.fop.fo.pagination.Declarations;
import org.apache.fop.fo.properties.ColorProperty;
import org.apache.fop.fo.properties.Property;
+import org.apache.fop.util.ColorUtil;
/**
* Implements the rgb-icc() function.
@@ -63,13 +64,15 @@ class ICCColorFunction extends FunctionBase {
} else {
cp = decls.getColorProfile(colorProfileName);
if (cp == null) {
- PropertyException pe = new PropertyException("The " + colorProfileName
- + " color profile was not declared");
- pe.setPropertyInfo(pInfo);
- throw pe;
+ if (!ColorUtil.isPseudoProfile(colorProfileName)) {
+ PropertyException pe = new PropertyException("The " + colorProfileName
+ + " color profile was not declared");
+ pe.setPropertyInfo(pInfo);
+ throw pe;
+ }
}
}
- String src = cp.getSrc();
+ String src = (cp != null ? cp.getSrc() : "");
float red = 0, green = 0, blue = 0;
red = args[0].getNumber().floatValue();
diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java
index 7ae1db7f3..87f640651 100644
--- a/src/java/org/apache/fop/fo/expr/PropertyParser.java
+++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java
@@ -19,6 +19,12 @@
package org.apache.fop.fo.expr;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.xmlgraphics.util.UnitConv;
+
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.Numeric;
@@ -31,10 +37,6 @@ import org.apache.fop.fo.properties.PercentLength;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.StringProperty;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
/**
* Class to parse XSL-FO property expressions.
* This class is heavily based on the epxression parser in James Clark's
@@ -99,7 +101,7 @@ public final class PropertyParser extends PropertyTokenizer {
/**
* Private constructor. Called by the static parse() method.
* @param propExpr The specified value (attribute on the xml element).
- * @param propInfo A PropertyInfo object representing the context in
+ * @param pInfo A PropertyInfo object representing the context in
* which the property expression is to be evaluated.
*/
private PropertyParser(String propExpr, PropertyInfo pInfo) {
@@ -310,12 +312,13 @@ public final class PropertyParser extends PropertyTokenizer {
propInfo.currentFontSize());
} else {
if ("px".equals(unitPart)) {
- //pass the ratio between source-resolution and
+ //pass the ratio between target-resolution and
//the default resolution of 72dpi
+ float resolution = propInfo.getPropertyList().getFObj()
+ .getUserAgent().getSourceResolution();
prop = FixedLength.getInstance(
numPart, unitPart,
- propInfo.getPropertyList().getFObj()
- .getUserAgent().getSourceResolution() / 72.0f);
+ UnitConv.IN2PT / resolution);
} else {
//use default resolution of 72dpi
prop = FixedLength.getInstance(numPart, unitPart);
diff --git a/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java b/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java
index 2ddcd0922..5baa0c4d8 100644
--- a/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java
+++ b/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java
@@ -243,15 +243,20 @@ class PropertyTokenizer {
}
- private void nextColor () throws PropertyException {
+ private void nextColor() throws PropertyException {
if (exprIndex < exprLength
&& isHexDigit(expr.charAt(exprIndex))) {
++exprIndex;
scanHexDigits();
- currentToken = TOK_COLORSPEC;
+ int len = exprIndex - currentTokenStartIndex - 1;
+ if (len % 3 == 0) {
+ currentToken = TOK_COLORSPEC;
+ } else {
+ scanRestOfName();
+ currentToken = TOK_NCNAME;
+ }
currentTokenValue = expr.substring(currentTokenStartIndex,
exprIndex);
- // Probably should have some multiple of 3 for length!
return;
} else {
throw new PropertyException("illegal character '#'");
@@ -263,11 +268,15 @@ class PropertyTokenizer {
*/
private void scanName() {
if (exprIndex < exprLength && isNameStartChar(expr.charAt(exprIndex))) {
- while (++exprIndex < exprLength
- && isNameChar(expr.charAt(exprIndex))) { }
+ scanRestOfName();
}
}
+ private void scanRestOfName() {
+ while (++exprIndex < exprLength
+ && isNameChar(expr.charAt(exprIndex))) { }
+ }
+
/**
* Attempt to recognize a valid sequence of decimal DIGITS in the
* input expression.
diff --git a/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java b/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java
index 1432c9381..51ae7441d 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java
@@ -155,11 +155,6 @@ public abstract class AbstractRetrieveMarker extends FObjMixed {
private void cloneFromMarker(Marker marker)
throws FOPException {
- // clean up remnants from a possible earlier layout
- if (firstChild != null) {
- currentTextNode = null;
- firstChild = null;
- }
cloneSubtree(marker.getChildNodes(), this,
marker, propertyList);
handleWhiteSpaceFor(this, null);
@@ -171,6 +166,11 @@ public abstract class AbstractRetrieveMarker extends FObjMixed {
* @param marker the marker that is to be cloned
*/
public void bindMarker(Marker marker) {
+ // clean up remnants from a possible earlier layout
+ if (firstChild != null) {
+ currentTextNode = null;
+ firstChild = null;
+ }
if (marker.getChildNodes() != null) {
try {
cloneFromMarker(marker);
diff --git a/src/java/org/apache/fop/fo/flow/table/EffRow.java b/src/java/org/apache/fop/fo/flow/table/EffRow.java
index 16d507303..a5853cd91 100644
--- a/src/java/org/apache/fop/fo/flow/table/EffRow.java
+++ b/src/java/org/apache/fop/fo/flow/table/EffRow.java
@@ -23,8 +23,7 @@ import java.util.Iterator;
import java.util.List;
import org.apache.fop.fo.Constants;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.table.TableRowIterator;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.util.BreakUtil;
@@ -170,20 +169,19 @@ public class EffRow {
*
* @return the strength of the keep-with-previous constraint
*/
- public int getKeepWithPreviousStrength() {
- int strength = BlockLevelLayoutManager.KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ Keep keep = Keep.KEEP_AUTO;
TableRow row = getTableRow();
if (row != null) {
- strength = Math.max(strength,
- KeepUtil.getCombinedBlockLevelKeepStrength(row.getKeepWithPrevious()));
+ keep = Keep.getKeep(row.getKeepWithPrevious());
}
for (Iterator iter = gridUnits.iterator(); iter.hasNext();) {
GridUnit gu = (GridUnit) iter.next();
if (gu.isPrimary()) {
- strength = Math.max(strength, gu.getPrimary().getKeepWithPreviousStrength());
+ keep = keep.compare(gu.getPrimary().getKeepWithPrevious());
}
}
- return strength;
+ return keep;
}
/**
@@ -192,20 +190,19 @@ public class EffRow {
*
* @return the strength of the keep-with-next constraint
*/
- public int getKeepWithNextStrength() {
- int strength = BlockLevelLayoutManager.KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ Keep keep = Keep.KEEP_AUTO;
TableRow row = getTableRow();
if (row != null) {
- strength = Math.max(strength,
- KeepUtil.getCombinedBlockLevelKeepStrength(row.getKeepWithNext()));
+ keep = Keep.getKeep(row.getKeepWithNext());
}
for (Iterator iter = gridUnits.iterator(); iter.hasNext();) {
GridUnit gu = (GridUnit) iter.next();
if (!gu.isEmpty() && gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()) {
- strength = Math.max(strength, gu.getPrimary().getKeepWithNextStrength());
+ keep = keep.compare(gu.getPrimary().getKeepWithNext());
}
}
- return strength;
+ return keep;
}
/**
@@ -213,16 +210,13 @@ public class EffRow {
* not take the parent table's keeps into account!
* @return the keep-together strength
*/
- public int getKeepTogetherStrength() {
+ public Keep getKeepTogether() {
TableRow row = getTableRow();
- int strength = BlockLevelLayoutManager.KEEP_AUTO;
+ Keep keep = Keep.KEEP_AUTO;
if (row != null) {
- strength = Math.max(strength, KeepUtil.getKeepStrength(
- row.getKeepTogether().getWithinPage()));
- strength = Math.max(strength, KeepUtil.getKeepStrength(
- row.getKeepTogether().getWithinColumn()));
+ keep = Keep.getKeep(row.getKeepTogether());
}
- return strength;
+ return keep;
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java b/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java
index 3254e928b..9326d6cd4 100644
--- a/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java
+++ b/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java
@@ -19,14 +19,13 @@
package org.apache.fop.fo.flow.table;
-import java.util.LinkedList;
import java.util.List;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.table.TableCellLayoutManager;
/**
@@ -54,8 +53,8 @@ public class PrimaryGridUnit extends GridUnit {
private boolean isSeparateBorderModel;
private int halfBorderSeparationBPD;
- private int keepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO;
- private int keepWithNext = BlockLevelLayoutManager.KEEP_AUTO;
+ private Keep keepWithPrevious = Keep.KEEP_AUTO;
+ private Keep keepWithNext = Keep.KEEP_AUTO;
private int breakBefore = Constants.EN_AUTO;
private int breakAfter = Constants.EN_AUTO;
@@ -334,16 +333,16 @@ public class PrimaryGridUnit extends GridUnit {
*
* @return the keep-with-previous strength
*/
- public int getKeepWithPreviousStrength() {
+ public Keep getKeepWithPrevious() {
return keepWithPrevious;
}
/**
* Don't use, reserved for TableCellLM. TODO
- * @param strength the keep strength
+ * @param keep the keep strength
*/
- public void setKeepWithPreviousStrength(int strength) {
- this.keepWithPrevious = strength;
+ public void setKeepWithPrevious(Keep keep) {
+ this.keepWithPrevious = keep;
}
/**
@@ -352,16 +351,16 @@ public class PrimaryGridUnit extends GridUnit {
*
* @return the keep-with-next strength
*/
- public int getKeepWithNextStrength() {
+ public Keep getKeepWithNext() {
return keepWithNext;
}
/**
* Don't use, reserved for TableCellLM. TODO
- * @param strength the keep strength
+ * @param keep the keep strength
*/
- public void setKeepWithNextStrength(int strength) {
- this.keepWithNext = strength;
+ public void setKeepWithNext(Keep keep) {
+ this.keepWithNext = keep;
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/table/TableBody.java b/src/java/org/apache/fop/fo/flow/table/TableBody.java
index 0ddfa97e3..0b42fd837 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableBody.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableBody.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.fo.flow.table;
diff --git a/src/java/org/apache/fop/fo/flow/table/TablePart.java b/src/java/org/apache/fop/fo/flow/table/TablePart.java
index b1db59d91..5b04cddc7 100644
--- a/src/java/org/apache/fop/fo/flow/table/TablePart.java
+++ b/src/java/org/apache/fop/fo/flow/table/TablePart.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: TableBody.java 655614 2008-05-12 19:37:39Z vhennebert $ */
+/* $Id$ */
package org.apache.fop.fo.flow.table;
@@ -70,6 +70,13 @@ public abstract class TablePart extends TableCellContainer {
}
/** {@inheritDoc} */
+ protected Object clone() {
+ TablePart clone = (TablePart) super.clone();
+ clone.rowGroups = new LinkedList(rowGroups);
+ return clone;
+ }
+
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
super.bind(pList);
@@ -103,13 +110,13 @@ public abstract class TablePart extends TableCellContainer {
pendingSpans = null;
columnNumberManager = null;
}
-
if (!(tableRowsFound || tableCellsFound)) {
missingChildElementError("marker* (table-row+|table-cell+)", true);
getParent().removeChild(this);
} else {
finishLastRowGroup();
}
+
}
/** {@inheritDoc} */
@@ -197,6 +204,9 @@ public abstract class TablePart extends TableCellContainer {
//nop
}
}
+ //TODO: possible performance problems in case of large tables...
+ //If the number of children grows significantly large, the default
+ //implementation in FObj will get slower and slower...
super.addChildNode(child);
}
diff --git a/src/java/org/apache/fop/fo/properties/LengthProperty.java b/src/java/org/apache/fop/fo/properties/LengthProperty.java
index 4ffe38074..3f569054e 100644
--- a/src/java/org/apache/fop/fo/properties/LengthProperty.java
+++ b/src/java/org/apache/fop/fo/properties/LengthProperty.java
@@ -19,6 +19,8 @@
package org.apache.fop.fo.properties;
+import org.apache.xmlgraphics.util.UnitConv;
+
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.FObj;
@@ -57,9 +59,10 @@ public abstract class LengthProperty extends Property
}
if (p instanceof NumberProperty) {
//Assume pixels (like in HTML) when there's no unit
+ float resolution = propertyList.getFObj().getUserAgent().getSourceResolution();
return FixedLength.getInstance(
p.getNumeric().getNumericValue(), "px",
- propertyList.getFObj().getUserAgent().getSourceResolution() / 72.0f);
+ UnitConv.IN2PT / resolution);
}
Length val = p.getLength();
if (val != null) {
diff --git a/src/java/org/apache/fop/fonts/FontAdder.java b/src/java/org/apache/fop/fonts/FontAdder.java
index 0d6a730cf..f0e511c42 100644
--- a/src/java/org/apache/fop/fonts/FontAdder.java
+++ b/src/java/org/apache/fop/fonts/FontAdder.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.fonts;
diff --git a/src/java/org/apache/fop/fonts/FontDetector.java b/src/java/org/apache/fop/fonts/FontDetector.java
index 09671f1f8..828cad2b5 100644
--- a/src/java/org/apache/fop/fonts/FontDetector.java
+++ b/src/java/org/apache/fop/fonts/FontDetector.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.fonts;
diff --git a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
index c97901163..208c32803 100644
--- a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
+++ b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.fonts;
diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java
index 8739b42a4..fb4725bd4 100644
--- a/src/java/org/apache/fop/fonts/SingleByteFont.java
+++ b/src/java/org/apache/fop/fonts/SingleByteFont.java
@@ -94,7 +94,7 @@ public class SingleByteFont extends CustomFont {
/** {@inheritDoc} */
public int[] getWidths() {
int[] arr = new int[width.length];
- System.arraycopy(width, 0, arr, 0, width.length - 1);
+ System.arraycopy(width, 0, arr, 0, width.length);
return arr;
}
diff --git a/src/java/org/apache/fop/fonts/apps/PFMReader.java b/src/java/org/apache/fop/fonts/apps/PFMReader.java
index 90dd1fd28..e5e8ca524 100644
--- a/src/java/org/apache/fop/fonts/apps/PFMReader.java
+++ b/src/java/org/apache/fop/fonts/apps/PFMReader.java
@@ -171,7 +171,7 @@ public class PFMReader extends AbstractFontReader {
* @param pfm The PFM file to preview.
*/
public void preview(PFMFile pfm) {
- if (log != null & log.isInfoEnabled()) {
+ if (log != null && log.isInfoEnabled()) {
log.info("Font: " + pfm.getWindowsName());
log.info("Name: " + pfm.getPostscriptName());
log.info("CharSet: " + pfm.getCharSetName());
@@ -219,14 +219,15 @@ public class PFMReader extends AbstractFontReader {
root.appendChild(el);
el.appendChild(doc.createTextNode(pfm.getPostscriptName()));
- String s = pfm.getPostscriptName();
- int pos = s.indexOf("-");
- if (pos >= 0) {
- char[] sb = new char[s.length() - 1];
- s.getChars(0, pos, sb, 0);
- s.getChars(pos + 1, s.length(), sb, pos);
- s = new String(sb);
- }
+ // Currently unused.
+ // String s = pfm.getPostscriptName();
+ // int pos = s.indexOf("-");
+ // if (pos >= 0) {
+ // char[] sb = new char[s.length() - 1];
+ // s.getChars(0, pos, sb, 0);
+ // s.getChars(pos + 1, s.length(), sb, pos);
+ // s = new String(sb);
+ // }
el = doc.createElement("embed");
root.appendChild(el);
@@ -304,8 +305,7 @@ public class PFMReader extends AbstractFontReader {
el = doc.createElement("char");
widths.appendChild(el);
el.setAttribute("idx", Integer.toString(i));
- el.setAttribute("wdt",
- new Integer(pfm.getCharWidth(i)).toString());
+ el.setAttribute("wdt", Integer.toString(pfm.getCharWidth(i)));
}
@@ -318,13 +318,14 @@ public class PFMReader extends AbstractFontReader {
root.appendChild(el);
Element el2 = null;
- Map h2 = (Map)pfm.getKerning().get(kpx1);
- Iterator enum2 = h2.keySet().iterator();
+ Map h2 = (Map) pfm.getKerning().get(kpx1);
+ Iterator enum2 = h2.entrySet().iterator();
while (enum2.hasNext()) {
- Integer kpx2 = (Integer)enum2.next();
+ Map.Entry entry = (Map.Entry) enum2.next();
+ Integer kpx2 = (Integer) entry.getKey();
el2 = doc.createElement("pair");
el2.setAttribute("kpx2", kpx2.toString());
- Integer val = (Integer)h2.get(kpx2);
+ Integer val = (Integer) entry.getValue();
el2.setAttribute("kern", val.toString());
el.appendChild(el2);
}
diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
index 03a3e1018..e2858e2f7 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -212,7 +212,8 @@ public class FontInfoFinder {
IOUtils.closeQuietly(in);
}
- List embedFontInfoList = new java.util.ArrayList(); //List<EmbedFontInfo>
+ List/*<EmbedFontInfo>*/ embedFontInfoList
+ = new java.util.ArrayList/*<EmbedFontInfo>*/();
// For each font name ...
//for (String fontName : ttcNames) {
diff --git a/src/java/org/apache/fop/fonts/type1/PFMFile.java b/src/java/org/apache/fop/fonts/type1/PFMFile.java
index d2d587d90..d1a3d79ff 100644
--- a/src/java/org/apache/fop/fonts/type1/PFMFile.java
+++ b/src/java/org/apache/fop/fonts/type1/PFMFile.java
@@ -481,7 +481,13 @@ public class PFMFile {
* @return The width of a character.
*/
public int getCharWidth(short which) {
- return extentTable[which - dfFirstChar];
+ if (extentTable != null) {
+ return extentTable[which - dfFirstChar];
+ } else {
+ //Fixed-width font (PFM may have no extent table)
+ //we'll just use the average width
+ return this.dfAvgWidth;
+ }
}
}
diff --git a/src/java/org/apache/fop/hyphenation/PatternParser.java b/src/java/org/apache/fop/hyphenation/PatternParser.java
index 6bd423a26..b34ab7ec8 100644
--- a/src/java/org/apache/fop/hyphenation/PatternParser.java
+++ b/src/java/org/apache/fop/hyphenation/PatternParser.java
@@ -30,7 +30,10 @@ import org.xml.sax.Attributes;
// Java
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
@@ -44,13 +47,14 @@ import javax.xml.parsers.SAXParserFactory;
*/
public class PatternParser extends DefaultHandler implements PatternConsumer {
- XMLReader parser;
- int currElement;
- PatternConsumer consumer;
- StringBuffer token;
- ArrayList exception;
- char hyphenChar;
- String errMsg;
+ private XMLReader parser;
+ private int currElement;
+ private PatternConsumer consumer;
+ private StringBuffer token;
+ private ArrayList exception;
+ private char hyphenChar;
+ private String errMsg;
+ private boolean hasClasses = false;
static final int ELEM_CLASSES = 1;
static final int ELEM_EXCEPTIONS = 2;
@@ -58,24 +62,19 @@ public class PatternParser extends DefaultHandler implements PatternConsumer {
static final int ELEM_HYPHEN = 4;
public PatternParser() throws HyphenationException {
+ this.consumer = this;
token = new StringBuffer();
parser = createParser();
parser.setContentHandler(this);
parser.setErrorHandler(this);
hyphenChar = '-'; // default
-
}
- public PatternParser(PatternConsumer consumer)
- throws HyphenationException {
+ public PatternParser(PatternConsumer consumer) throws HyphenationException {
this();
this.consumer = consumer;
}
- public void setConsumer(PatternConsumer consumer) {
- this.consumer = consumer;
- }
-
/**
* Parses a hyphenation pattern file.
* @param filename the filename
@@ -249,15 +248,32 @@ public class PatternParser extends DefaultHandler implements PatternConsumer {
return il.toString();
}
+ protected void getExternalClasses() throws SAXException {
+ XMLReader mainParser = parser;
+ parser = createParser();
+ parser.setContentHandler(this);
+ parser.setErrorHandler(this);
+ InputStream stream = this.getClass().getResourceAsStream("classes.xml");
+ InputSource source = new InputSource(stream);
+ try {
+ parser.parse(source);
+ } catch (IOException ioe) {
+ throw new SAXException(ioe.getMessage());
+ } finally {
+ parser = mainParser;
+ }
+ }
+
//
// ContentHandler methods
//
/**
* {@inheritDoc}
+ * @throws SAXException
*/
public void startElement(String uri, String local, String raw,
- Attributes attrs) {
+ Attributes attrs) throws SAXException {
if (local.equals("hyphen-char")) {
String h = attrs.getValue("value");
if (h != null && h.length() == 1) {
@@ -266,8 +282,14 @@ public class PatternParser extends DefaultHandler implements PatternConsumer {
} else if (local.equals("classes")) {
currElement = ELEM_CLASSES;
} else if (local.equals("patterns")) {
+ if (!hasClasses) {
+ getExternalClasses();
+ }
currElement = ELEM_PATTERNS;
} else if (local.equals("exceptions")) {
+ if (!hasClasses) {
+ getExternalClasses();
+ }
currElement = ELEM_EXCEPTIONS;
exception = new ArrayList();
} else if (local.equals("hyphen")) {
@@ -311,6 +333,9 @@ public class PatternParser extends DefaultHandler implements PatternConsumer {
token.setLength(0);
}
}
+ if (currElement == ELEM_CLASSES) {
+ hasClasses = true;
+ }
if (currElement == ELEM_HYPHEN) {
currElement = ELEM_EXCEPTIONS;
} else {
@@ -403,23 +428,46 @@ public class PatternParser extends DefaultHandler implements PatternConsumer {
// PatternConsumer implementation for testing purposes
public void addClass(String c) {
- System.out.println("class: " + c);
+ testOut.println("class: " + c);
}
public void addException(String w, ArrayList e) {
- System.out.println("exception: " + w + " : " + e.toString());
+ testOut.println("exception: " + w + " : " + e.toString());
}
public void addPattern(String p, String v) {
- System.out.println("pattern: " + p + " : " + v);
+ testOut.println("pattern: " + p + " : " + v);
+ }
+
+ private PrintStream testOut = System.out;
+
+ /**
+ * @param testOut the testOut to set
+ */
+ public void setTestOut(PrintStream testOut) {
+ this.testOut = testOut;
+ }
+
+ public void closeTestOut() {
+ testOut.flush();
+ testOut.close();
}
public static void main(String[] args) throws Exception {
if (args.length > 0) {
PatternParser pp = new PatternParser();
- pp.setConsumer(pp);
+ PrintStream p = null;
+ if (args.length > 1) {
+ FileOutputStream f = new FileOutputStream(args[1]);
+ p = new PrintStream(f, false, "utf-8");
+ pp.setTestOut(p);
+ }
pp.parse(args[0]);
+ if (pp != null) {
+ pp.closeTestOut();
+ }
}
}
+
}
diff --git a/src/java/org/apache/fop/hyphenation/SerializeHyphPattern.java b/src/java/org/apache/fop/hyphenation/SerializeHyphPattern.java
new file mode 100644
index 000000000..d2a259db0
--- /dev/null
+++ b/src/java/org/apache/fop/hyphenation/SerializeHyphPattern.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.hyphenation;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
+/**
+ * Serialize hyphenation patterns
+ * For all xml files in the source directory a pattern file is built in the target directory
+ * This class may be called from the ant build file in a java task
+ */
+public class SerializeHyphPattern {
+
+ private boolean errorDump = false;
+
+ /**
+ * Controls the amount of error information dumped.
+ * @param errorDump True if more error info should be provided
+ */
+ public void setErrorDump(boolean errorDump) {
+ this.errorDump = errorDump;
+ }
+
+ /**
+ * Compile all xml files in sourceDir, and write output hyp files in targetDir
+ * @param sourceDir Directory with pattern xml files
+ * @param targetDir Directory to which compiled pattern hyp files should be written
+ */
+ public void serializeDir(File sourceDir, File targetDir) {
+ final String extension = ".xml";
+ String[] sourceFiles = sourceDir.list(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(extension);
+ }
+ });
+ for (int j = 0; j < sourceFiles.length; j++) {
+ File infile = new File(sourceDir, sourceFiles[j]);
+ String outfilename = sourceFiles[j].substring(0, sourceFiles[j].length()
+ - extension.length()) + ".hyp";
+ File outfile = new File(targetDir, outfilename);
+ serializeFile(infile, outfile);
+ }
+ }
+
+ /*
+ * checks whether input or output files exists or the latter is older than input file
+ * and start build if necessary
+ */
+ private void serializeFile(File infile, File outfile) {
+ boolean startProcess;
+ startProcess = rebuild(infile, outfile);
+ if (startProcess) {
+ HyphenationTree hTree = buildPatternFile(infile);
+ // serialize class
+ try {
+ ObjectOutputStream out = new ObjectOutputStream(
+ new java.io.BufferedOutputStream(
+ new java.io.FileOutputStream(outfile)));
+ out.writeObject(hTree);
+ out.close();
+ } catch (IOException ioe) {
+ System.err.println("Can't write compiled pattern file: "
+ + outfile);
+ System.err.println(ioe);
+ }
+ }
+ }
+
+ /*
+ * serializes pattern files
+ */
+ private HyphenationTree buildPatternFile(File infile) {
+ System.out.println("Processing " + infile);
+ HyphenationTree hTree = new HyphenationTree();
+ try {
+ hTree.loadPatterns(infile.toString());
+ if (errorDump) {
+ System.out.println("Stats: ");
+ hTree.printStats();
+ }
+ } catch (HyphenationException ex) {
+ System.err.println("Can't load patterns from xml file " + infile
+ + " - Maybe hyphenation.dtd is missing?");
+ if (errorDump) {
+ System.err.println(ex.toString());
+ }
+ }
+ return hTree;
+ }
+
+ /**
+ * Checks for existence of output file and compares
+ * dates with input and stylesheet file
+ */
+ private boolean rebuild(File infile, File outfile) {
+ if (outfile.exists()) {
+ // checks whether output file is older than input file
+ if (outfile.lastModified() < infile.lastModified()) {
+ return true;
+ }
+ } else {
+ // if output file does not exist, start process
+ return true;
+ }
+ return false;
+ } // end rebuild
+
+
+ /**
+ * Entry point for ant java task
+ * @param args sourceDir, targetDir
+ */
+ public static void main (String[] args) {
+ SerializeHyphPattern ser = new SerializeHyphPattern();
+ ser.serializeDir(new File(args[0]), new File(args[1]));
+ }
+
+}
diff --git a/src/java/org/apache/fop/hyphenation/classes.xml b/src/java/org/apache/fop/hyphenation/classes.xml
new file mode 100644
index 000000000..056a533a8
--- /dev/null
+++ b/src/java/org/apache/fop/hyphenation/classes.xml
@@ -0,0 +1,7652 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more -->
+<!-- contributor license agreements. See the NOTICE file distributed with -->
+<!-- this work for additional information regarding copyright ownership. -->
+<!-- The ASF licenses this file to You under the Apache License, Version 2.0 -->
+<!-- (the "License"); you may not use this file except in compliance with -->
+<!-- the License. You may obtain a copy of the License at -->
+<!-- -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0 -->
+<!-- -->
+<!-- Unless required by applicable law or agreed to in writing, software -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS, -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -->
+<!-- See the License for the specific language governing permissions and -->
+<!-- limitations under the License. -->
+
+<!-- $Id$ -->
+
+<!-- !!! THIS IS A GENERATED FILE !!! -->
+<!-- If updates are needed, then: -->
+<!-- * run 'ant codegen-hyphenation-classes', -->
+<!-- which will generate a new file classes.xml -->
+<!-- in 'src/java/org/apache/fop/hyphenation' -->
+<!-- * commit the changed file -->
+
+<classes>
+aA
+bB
+cC
+dD
+eE
+fF
+gG
+hH
+iI
+jJ
+kK
+lL
+mM
+nN
+oO
+pP
+qQ
+rR
+sS
+tT
+uU
+vV
+wW
+xX
+yY
+zZ
+ª
+µΜ
+º
+ß
+àÀ
+áÃ
+âÂ
+ãÃ
+äÄ
+åÅ
+æÆ
+çÇ
+èÈ
+éÉ
+êÊ
+ëË
+ìÌ
+íÃ
+îÎ
+ïÃ
+ðÃ
+ñÑ
+òÒ
+óÓ
+ôÔ
+õÕ
+öÖ
+øØ
+ùÙ
+úÚ
+ûÛ
+üÜ
+ýÃ
+þÞ
+ÿŸ
+ÄÄ€
+ăĂ
+Ä…Ä„
+ćĆ
+ĉĈ
+Ä‹ÄŠ
+ÄÄŒ
+ÄÄŽ
+Ä‘Ä
+Ä“Ä’
+Ä•Ä”
+Ä—Ä–
+ęĘ
+ěĚ
+ÄÄœ
+ÄŸÄž
+Ä¡Ä 
+ģĢ
+ĥĤ
+ħĦ
+ĩĨ
+īĪ
+ĭĬ
+įĮ
+ıI
+ijIJ
+ĵĴ
+ķĶ
+ĸ
+ĺĹ
+ļĻ
+ľĽ
+ŀĿ
+Å‚Å
+ńŃ
+ņŅ
+ňŇ
+ʼn
+Å‹ÅŠ
+ÅÅŒ
+ÅÅŽ
+Å‘Å
+Å“Å’
+Å•Å”
+Å—Å–
+řŘ
+śŚ
+ÅÅœ
+ÅŸÅž
+Å¡Å 
+ţŢ
+ťŤ
+ŧŦ
+ũŨ
+ūŪ
+ŭŬ
+ůŮ
+űŰ
+ųŲ
+ŵŴ
+ŷŶ
+źŹ
+żŻ
+žŽ
+Å¿S
+ƀɃ
+ƃƂ
+Æ…Æ„
+ƈƇ
+ƌƋ
+Æ’Æ‘
+ƕǶ
+ƙƘ
+ƚȽ
+Æ›
+ƞȠ
+Æ¡Æ 
+ƣƢ
+ƥƤ
+ƨƧ
+ƪ
+Æ«
+ƭƬ
+ưƯ
+ƴƳ
+ƶƵ
+ƹƸ
+ƺ
+Æ»
+ƽƼ
+ƾ
+Æ¿Ç·
+Ç€
+Ç‚
+ǃ
+džDŽDž
+ljLJLj
+njNJNj
+ÇŽÇ
+ÇÇ
+Ç’Ç‘
+ǔǓ
+Ç–Ç•
+ǘǗ
+ǚǙ
+ǜǛ
+ÇÆŽ
+ÇŸÇž
+Ç¡Ç 
+ǣǢ
+ǥǤ
+ǧǦ
+ǩǨ
+ǫǪ
+ǭǬ
+ǯǮ
+Ç°
+dzDZDz
+ǵǴ
+ǹǸ
+ǻǺ
+ǽǼ
+ǿǾ
+ÈÈ€
+ȃȂ
+È…È„
+ȇȆ
+ȉȈ
+È‹ÈŠ
+ÈÈŒ
+ÈÈŽ
+È‘È
+È“È’
+È•È”
+È—È–
+șȘ
+țȚ
+ÈÈœ
+ÈŸÈž
+È¡
+ȣȢ
+ȥȤ
+ȧȦ
+ȩȨ
+ȫȪ
+ȭȬ
+ȯȮ
+ȱȰ
+ȳȲ
+È´
+ȵ
+ȶ
+È·
+ȸ
+ȹ
+ȼȻ
+È¿
+É€
+É‚É
+ɇɆ
+ɉɈ
+É‹ÉŠ
+ÉÉŒ
+ÉÉŽ
+ÉⱯ
+É‘â±­
+É’
+É“Æ
+ɔƆ
+É•
+ɖƉ
+É—ÆŠ
+ɘ
+É™Æ
+Éš
+É›Æ
+ɜ
+Éž
+ÉŸ
+É Æ“
+É¡
+É¢
+ɣƔ
+ɤ
+É¥
+ɦ
+ɧ
+ɨƗ
+É©Æ–
+ɪ
+É«â±¢
+ɬ
+É­
+É®
+ɯƜ
+É°
+ɱⱮ
+ɲÆ
+ɳ
+É´
+ɵƟ
+ɶ
+É·
+ɸ
+ɹ
+ɺ
+É»
+ɼ
+ɽⱤ
+ɾ
+É¿
+ʀƦ
+Ê‚
+ʃƩ
+Ê„
+Ê…
+ʆ
+ʇ
+ʈƮ
+ʉɄ
+ʊƱ
+ʋƲ
+ʌɅ
+ÊŽ
+Ê‘
+Ê’Æ·
+Ê“
+Ê”
+Ê•
+Ê–
+Ê—
+ʘ
+Ê™
+Êš
+Ê›
+ʜ
+Êž
+ÊŸ
+Ê 
+Ê¡
+Ê¢
+Ê£
+ʤ
+Ê¥
+ʦ
+ʧ
+ʨ
+Ê©
+ʪ
+Ê«
+ʬ
+Ê­
+Ê®
+ʯ
+ͱͰ
+ͳͲ
+ͷͶ
+ͻϽ
+ͼϾ
+ͽϿ
+άΆ
+έΈ
+ήΉ
+ίΊ
+ΰ
+αΑ
+βΒ
+γΓ
+δΔ
+εΕ
+ζΖ
+ηΗ
+θΘ
+ιΙ
+κΚ
+λΛ
+μΜ
+νÎ
+ξΞ
+οΟ
+πΠ
+ÏΡ
+ςΣ
+σΣ
+τΤ
+Ï…Î¥
+φΦ
+χΧ
+ψΨ
+ωΩ
+ϊΪ
+ϋΫ
+όΌ
+ÏÎŽ
+ÏŽÎ
+ÏÎ’
+ϑΘ
+Ï’
+Ï“
+Ï”
+ϕΦ
+ϖΠ
+Ï—Ï
+ϙϘ
+ϛϚ
+ÏÏœ
+ÏŸÏž
+Ï¡Ï 
+ϣϢ
+ϥϤ
+ϧϦ
+ϩϨ
+ϫϪ
+ϭϬ
+ϯϮ
+ϰΚ
+ϱΡ
+ϲϹ
+ϳ
+ϵΕ
+ϸϷ
+ϻϺ
+ϼ
+аÐ
+бБ
+вВ
+гГ
+дД
+еЕ
+жЖ
+зЗ
+иИ
+йЙ
+кК
+лЛ
+мМ
+нÐ
+оО
+пП
+рР
+ÑС
+тТ
+уУ
+фФ
+Ñ…Ð¥
+цЦ
+чЧ
+шШ
+щЩ
+ъЪ
+ыЫ
+ьЬ
+ÑЭ
+юЮ
+ÑЯ
+ÑЀ
+Ñ‘Ð
+ђЂ
+ѓЃ
+єЄ
+Ñ•Ð…
+іІ
+їЇ
+јЈ
+љЉ
+њЊ
+ћЋ
+ќЌ
+ÑÐ
+ўЎ
+ÑŸÐ
+Ñ¡Ñ 
+ѣѢ
+ѥѤ
+ѧѦ
+ѩѨ
+ѫѪ
+ѭѬ
+ѯѮ
+ѱѰ
+ѳѲ
+ѵѴ
+ѷѶ
+ѹѸ
+ѻѺ
+ѽѼ
+ѿѾ
+ÒÒ€
+Ò‹ÒŠ
+ÒÒŒ
+ÒÒŽ
+Ò‘Ò
+Ò“Ò’
+Ò•Ò”
+Ò—Ò–
+Ò™Ò˜
+Ò›Òš
+ÒÒœ
+ÒŸÒž
+Ò¡Ò 
+Ò£Ò¢
+Ò¥Ò¤
+Ò§Ò¦
+Ò©Ò¨
+Ò«Òª
+Ò­Ò¬
+Ò¯Ò®
+Ò±Ò°
+Ò³Ò²
+ÒµÒ´
+Ò·Ò¶
+Ò¹Ò¸
+Ò»Òº
+Ò½Ò¼
+Ò¿Ò¾
+Ó‚Ó
+Ó„Óƒ
+Ó†Ó…
+ÓˆÓ‡
+ÓŠÓ‰
+ӌӋ
+ÓŽÓ
+ÓÓ€
+Ó‘Ó
+Ó“Ó’
+Ó•Ó”
+Ó—Ó–
+Ó™Ó˜
+Ó›Óš
+ÓÓœ
+ÓŸÓž
+Ó¡Ó 
+Ó£Ó¢
+Ó¥Ó¤
+Ó§Ó¦
+Ó©Ó¨
+Ó«Óª
+Ó­Ó¬
+Ó¯Ó®
+Ó±Ó°
+Ó³Ó²
+ÓµÓ´
+Ó·Ó¶
+Ó¹Ó¸
+Ó»Óº
+Ó½Ó¼
+Ó¿Ó¾
+ÔÔ€
+ÔƒÔ‚
+Ô…Ô„
+Ô‡Ô†
+Ô‰Ôˆ
+Ô‹ÔŠ
+ÔÔŒ
+ÔÔŽ
+Ô‘Ô
+Ô“Ô’
+Ô•Ô”
+Ô—Ô–
+Ô™Ô˜
+Ô›Ôš
+ÔÔœ
+ÔŸÔž
+Ô¡Ô 
+Ô£Ô¢
+Õ¡Ô±
+Õ¢Ô²
+Õ£Ô³
+Õ¤Ô´
+Õ¥Ôµ
+Õ¦Ô¶
+Õ§Ô·
+Õ¨Ô¸
+Õ©Ô¹
+ÕªÔº
+Õ«Ô»
+Õ¬Ô¼
+Õ­Ô½
+Õ®Ô¾
+Õ¯Ô¿
+Õ°Õ€
+Õ±Õ
+Õ²Õ‚
+Õ³Õƒ
+Õ´Õ„
+ÕµÕ…
+Õ¶Õ†
+Õ·Õ‡
+Õ¸Õˆ
+Õ¹Õ‰
+ÕºÕŠ
+Õ»Õ‹
+ռՌ
+Õ½Õ
+Õ¾ÕŽ
+Õ¿Õ
+Ö€Õ
+ÖÕ‘
+Ö‚Õ’
+ÖƒÕ“
+Ö„Õ”
+Ö…Õ•
+Ö†Õ–
+Ö‡
+ב
+×’
+ד
+×”
+ו
+×–
+×—
+ט
+×™
+ך
+×›
+ל
+מ
+ן
+× 
+ס
+×¢
+×£
+פ
+×¥
+צ
+ק
+ר
+ש
+ת
+×°
+×±
+ײ
+Ø¡
+Ø¢
+Ø£
+ؤ
+Ø¥
+ئ
+ا
+ب
+Ø©
+ت
+Ø«
+ج
+Ø­
+Ø®
+د
+Ø°
+ر
+ز
+س
+Ø´
+ص
+ض
+Ø·
+ظ
+ع
+غ
+Ø»
+ؼ
+ؽ
+ؾ
+Ø¿
+Ù‚
+Ùƒ
+Ù„
+Ù…
+Ù†
+Ù‡
+Ùˆ
+Ù‰
+ÙŠ
+Ù®
+Ù¯
+Ù±
+Ù²
+Ù³
+Ù´
+Ùµ
+Ù¶
+Ù·
+Ù¸
+Ù¹
+Ùº
+Ù»
+Ù¼
+Ù½
+Ù¾
+Ù¿
+Ú€
+Ú‚
+Úƒ
+Ú„
+Ú…
+Ú†
+Ú‡
+Úˆ
+Ú‰
+ÚŠ
+Ú‹
+ڌ
+ÚŽ
+Ú‘
+Ú’
+Ú“
+Ú”
+Ú•
+Ú–
+Ú—
+Ú˜
+Ú™
+Úš
+Ú›
+ڜ
+Úž
+ÚŸ
+Ú 
+Ú¡
+Ú¢
+Ú£
+Ú¤
+Ú¥
+Ú¦
+Ú§
+Ú¨
+Ú©
+Úª
+Ú«
+Ú¬
+Ú­
+Ú®
+Ú¯
+Ú°
+Ú±
+Ú²
+Ú³
+Ú´
+Úµ
+Ú¶
+Ú·
+Ú¸
+Ú¹
+Úº
+Ú»
+Ú¼
+Ú½
+Ú¾
+Ú¿
+Û€
+Û‚
+Ûƒ
+Û„
+Û…
+Û†
+Û‡
+Ûˆ
+Û‰
+ÛŠ
+Û‹
+ی
+ÛŽ
+Û‘
+Û’
+Û“
+Û•
+Û®
+Û¯
+Ûº
+Û»
+Û¼
+Û¿
+Ü’
+Ü“
+Ü”
+Ü•
+Ü–
+Ü—
+ܘ
+Ü™
+Üš
+Ü›
+ܜ
+Üž
+ÜŸ
+Ü 
+Ü¡
+Ü¢
+Ü£
+ܤ
+Ü¥
+ܦ
+ܧ
+ܨ
+Ü©
+ܪ
+Ü«
+ܬ
+Ü­
+Ü®
+ܯ
+ÝŽ
+Ý‘
+Ý’
+Ý“
+Ý”
+Ý•
+Ý–
+Ý—
+ݘ
+Ý™
+Ýš
+Ý›
+ݜ
+Ýž
+ÝŸ
+Ý 
+Ý¡
+Ý¢
+Ý£
+ݤ
+Ý¥
+ݦ
+ݧ
+ݨ
+Ý©
+ݪ
+Ý«
+ݬ
+Ý­
+Ý®
+ݯ
+Ý°
+ݱ
+ݲ
+ݳ
+Ý´
+ݵ
+ݶ
+Ý·
+ݸ
+ݹ
+ݺ
+Ý»
+ݼ
+ݽ
+ݾ
+Ý¿
+Þ€
+Þ‚
+Þƒ
+Þ„
+Þ…
+Þ†
+Þ‡
+Þˆ
+Þ‰
+ÞŠ
+Þ‹
+ތ
+ÞŽ
+Þ‘
+Þ’
+Þ“
+Þ”
+Þ•
+Þ–
+Þ—
+Þ˜
+Þ™
+Þš
+Þ›
+ޜ
+Þž
+ÞŸ
+Þ 
+Þ¡
+Þ¢
+Þ£
+Þ¤
+Þ¥
+Þ±
+ߊ
+ß‹
+ߌ
+ߎ
+ß‘
+ß’
+ß“
+ß”
+ß•
+ß–
+ß—
+ߘ
+ß™
+ßš
+ß›
+ߜ
+ßž
+ߟ
+ß 
+ß¡
+ߢ
+ߣ
+ߤ
+ߥ
+ߦ
+ߧ
+ߨ
+ß©
+ߪ
+ऄ
+अ
+आ
+इ
+ई
+उ
+ऊ
+ऋ
+ऌ
+à¤
+ऎ
+à¤
+à¤
+ऑ
+ऒ
+ओ
+औ
+क
+ख
+ग
+घ
+ङ
+च
+छ
+ज
+à¤
+ञ
+ट
+ठ
+ड
+ढ
+ण
+त
+थ
+द
+ध
+न
+ऩ
+प
+फ
+ब
+भ
+म
+य
+र
+ऱ
+ल
+ळ
+ऴ
+व
+श
+ष
+स
+ह
+ऽ
+à¥
+क़
+ख़
+ग़
+ज़
+ड़
+à¥
+फ़
+य़
+ॠ
+ॡ
+ॲ
+ॻ
+ॼ
+ॽ
+ॾ
+ॿ
+অ
+আ
+ই
+ঈ
+উ
+ঊ
+ঋ
+ঌ
+à¦
+à¦
+ও
+ঔ
+ক
+খ
+গ
+ঘ
+ঙ
+চ
+ছ
+জ
+à¦
+ঞ
+ট
+ঠ
+ড
+ঢ
+ণ
+ত
+থ
+দ
+ধ
+ন
+প
+ফ
+ব
+ভ
+ম
+য
+র
+ল
+শ
+ষ
+স
+হ
+ঽ
+ৎ
+ড়
+à§
+য়
+ৠ
+ৡ
+ৰ
+ৱ
+ਅ
+ਆ
+ਇ
+ਈ
+ਉ
+ਊ
+à¨
+à¨
+ਓ
+ਔ
+ਕ
+ਖ
+ਗ
+ਘ
+ਙ
+ਚ
+ਛ
+ਜ
+à¨
+ਞ
+ਟ
+ਠ
+ਡ
+ਢ
+ਣ
+ਤ
+ਥ
+ਦ
+ਧ
+ਨ
+ਪ
+ਫ
+ਬ
+ਭ
+ਮ
+ਯ
+ਰ
+ਲ
+ਲ਼
+ਵ
+ਸ਼
+ਸ
+ਹ
+à©™
+à©š
+à©›
+ੜ
+à©ž
+ੲ
+ੳ
+à©´
+અ
+આ
+ઇ
+ઈ
+ઉ
+ઊ
+ઋ
+ઌ
+àª
+àª
+àª
+ઑ
+ઓ
+ઔ
+ક
+ખ
+ગ
+ઘ
+ઙ
+ચ
+છ
+જ
+àª
+ઞ
+ટ
+ઠ
+ડ
+ઢ
+ણ
+ત
+થ
+દ
+ધ
+ન
+પ
+ફ
+બ
+ભ
+મ
+ય
+ર
+લ
+ળ
+વ
+શ
+ષ
+સ
+હ
+ઽ
+à«
+à« 
+à«¡
+ଅ
+ଆ
+ଇ
+ଈ
+ଉ
+ଊ
+ଋ
+ଌ
+à¬
+à¬
+ଓ
+ଔ
+କ
+ଖ
+ଗ
+ଘ
+ଙ
+ଚ
+ଛ
+ଜ
+à¬
+ଞ
+ଟ
+ଠ
+ଡ
+ଢ
+ଣ
+ତ
+ଥ
+ଦ
+ଧ
+ନ
+ପ
+ଫ
+ବ
+ଭ
+ମ
+ଯ
+ର
+ଲ
+ଳ
+ଵ
+ଶ
+ଷ
+ସ
+ହ
+ଽ
+ଡ଼
+à­
+à­Ÿ
+à­ 
+à­¡
+à­±
+ஃ
+à®…
+ஆ
+இ
+ஈ
+உ
+ஊ
+எ
+à®
+à®
+à®’
+ஓ
+à®”
+க
+à®™
+ச
+ஜ
+ஞ
+ட
+ண
+த
+ந
+ன
+ப
+à®®
+ய
+à®°
+à®±
+ல
+ள
+à®´
+வ
+ஶ
+à®·
+ஸ
+ஹ
+à¯
+à°…
+à°†
+à°‡
+à°ˆ
+à°‰
+à°Š
+à°‹
+ఌ
+à°Ž
+à°
+à°
+à°’
+à°“
+à°”
+à°•
+à°–
+à°—
+à°˜
+à°™
+à°š
+à°›
+జ
+à°
+à°ž
+à°Ÿ
+à° 
+à°¡
+à°¢
+à°£
+à°¤
+à°¥
+à°¦
+à°§
+à°¨
+à°ª
+à°«
+à°¬
+à°­
+à°®
+à°¯
+à°°
+à°±
+à°²
+à°³
+à°µ
+à°¶
+à°·
+à°¸
+à°¹
+à°½
+ౘ
+à±™
+à± 
+ౡ
+ಅ
+ಆ
+ಇ
+ಈ
+ಉ
+ಊ
+ಋ
+ಌ
+ಎ
+à²
+à²
+ಒ
+ಓ
+ಔ
+ಕ
+ಖ
+ಗ
+ಘ
+ಙ
+ಚ
+ಛ
+ಜ
+à²
+ಞ
+ಟ
+ಠ
+ಡ
+ಢ
+ಣ
+ತ
+ಥ
+ದ
+ಧ
+ನ
+ಪ
+ಫ
+ಬ
+ಭ
+ಮ
+ಯ
+ರ
+ಱ
+ಲ
+ಳ
+ವ
+ಶ
+ಷ
+ಸ
+ಹ
+ಽ
+ೞ
+à³ 
+ೡ
+à´…
+à´†
+à´‡
+à´ˆ
+à´‰
+à´Š
+à´‹
+ഌ
+à´Ž
+à´
+à´
+à´’
+à´“
+à´”
+à´•
+à´–
+à´—
+à´˜
+à´™
+à´š
+à´›
+ജ
+à´
+à´ž
+à´Ÿ
+à´ 
+à´¡
+à´¢
+à´£
+à´¤
+à´¥
+à´¦
+à´§
+à´¨
+à´ª
+à´«
+à´¬
+à´­
+à´®
+à´¯
+à´°
+à´±
+à´²
+à´³
+à´´
+à´µ
+à´¶
+à´·
+à´¸
+à´¹
+à´½
+ൠ
+ൡ
+ൺ
+ൻ
+ർ
+ൽ
+ൾ
+ൿ
+අ
+ආ
+ඇ
+ඈ
+ඉ
+ඊ
+උ
+ඌ
+à¶
+ඎ
+à¶
+à¶
+එ
+ඒ
+ඓ
+ඔ
+ඕ
+ඖ
+ක
+ඛ
+ග
+à¶
+ඞ
+ඟ
+ච
+ඡ
+ජ
+ඣ
+ඤ
+ඥ
+ඦ
+ට
+ඨ
+ඩ
+ඪ
+ණ
+ඬ
+ත
+ථ
+ද
+ධ
+න
+ඳ
+ප
+ඵ
+බ
+භ
+ම
+ඹ
+ය
+ර
+ල
+à·€
+à·
+à·‚
+à·ƒ
+à·„
+à·…
+à·†
+à¸
+ข
+ฃ
+ค
+ฅ
+ฆ
+ง
+จ
+ฉ
+ช
+ซ
+ฌ
+à¸
+ฎ
+à¸
+à¸
+ฑ
+ฒ
+ณ
+ด
+ต
+ถ
+ท
+ธ
+น
+บ
+ป
+ผ
+à¸
+พ
+ฟ
+ภ
+ม
+ย
+ร
+ฤ
+ล
+ฦ
+ว
+ศ
+ษ
+ส
+ห
+ฬ
+อ
+ฮ
+ฯ
+ะ
+า
+ำ
+เ
+à¹
+โ
+ใ
+ไ
+ๅ
+àº
+ຂ
+ຄ
+ງ
+ຈ
+ຊ
+àº
+ດ
+ຕ
+ຖ
+ທ
+ນ
+ບ
+ປ
+ຜ
+àº
+ພ
+ຟ
+ມ
+ຢ
+ຣ
+ລ
+ວ
+ສ
+ຫ
+ອ
+ຮ
+ຯ
+ະ
+າ
+ຳ
+ຽ
+ເ
+à»
+ໂ
+ໃ
+ໄ
+ໜ
+à»
+ༀ
+ཀ
+à½
+ག
+གྷ
+ང
+ཅ
+ཆ
+ཇ
+ཉ
+ཊ
+ཋ
+ཌ
+à½
+ཎ
+à½
+à½
+ད
+དྷ
+ན
+པ
+ཕ
+བ
+བྷ
+མ
+ཙ
+ཚ
+ཛ
+ཛྷ
+à½
+ཞ
+ཟ
+འ
+ཡ
+ར
+ལ
+ཤ
+ཥ
+ས
+ཧ
+ཨ
+ཀྵ
+ཪ
+ཫ
+ཬ
+ྈ
+ྉ
+ྊ
+ྋ
+က
+á€
+ဂ
+ဃ
+င
+စ
+ဆ
+ဇ
+ဈ
+ဉ
+ည
+ဋ
+ဌ
+á€
+ဎ
+á€
+á€
+ထ
+ဒ
+ဓ
+န
+ပ
+ဖ
+ဗ
+ဘ
+မ
+ယ
+ရ
+လ
+á€
+သ
+ဟ
+ဠ
+အ
+ဢ
+ဣ
+ဤ
+ဥ
+ဦ
+ဧ
+ဨ
+ဩ
+ဪ
+ဿ
+á‘
+á’
+á“
+á”
+á•
+áš
+á›
+áœ
+á¡
+á¥
+á¦
+á®
+á¯
+á°
+áµ
+á¶
+á·
+á¸
+á¹
+áº
+á»
+á¼
+á½
+á¾
+á¿
+á‚€
+á‚
+á‚Ž
+áƒ
+ბ
+გ
+დ
+ე
+ვ
+ზ
+თ
+ი
+კ
+ლ
+მ
+ნ
+áƒ
+პ
+ჟ
+რ
+ს
+ტ
+უ
+ფ
+ქ
+ღ
+ყ
+შ
+ჩ
+ც
+ძ
+წ
+ჭ
+ხ
+ჯ
+ჰ
+ჱ
+ჲ
+ჳ
+ჴ
+ჵ
+ჶ
+ჷ
+ჸ
+ჹ
+ჺ
+á„€
+á„
+á„‚
+ᄃ
+á„„
+á„…
+ᄆ
+ᄇ
+ᄈ
+ᄉ
+á„Š
+á„‹
+ᄌ
+á„
+á„Ž
+á„
+á„
+á„‘
+á„’
+á„“
+á„”
+á„•
+á„–
+á„—
+ᄘ
+á„™
+á„š
+á„›
+ᄜ
+á„
+á„ž
+á„Ÿ
+á„ 
+á„¡
+á„¢
+á„£
+ᄤ
+á„¥
+ᄦ
+ᄧ
+ᄨ
+á„©
+ᄪ
+á„«
+ᄬ
+á„­
+á„®
+ᄯ
+á„°
+ᄱ
+ᄲ
+ᄳ
+á„´
+ᄵ
+ᄶ
+á„·
+ᄸ
+ᄹ
+ᄺ
+á„»
+ᄼ
+ᄽ
+ᄾ
+á„¿
+á…€
+á…
+á…‚
+á…ƒ
+á…„
+á……
+á…†
+á…‡
+á…ˆ
+á…‰
+á…Š
+á…‹
+ᅌ
+á…
+á…Ž
+á…
+á…
+á…‘
+á…’
+á…“
+á…”
+á…•
+á…–
+á…—
+á…˜
+á…™
+á…Ÿ
+á… 
+á…¡
+á…¢
+á…£
+á…¤
+á…¥
+á…¦
+á…§
+á…¨
+á…©
+á…ª
+á…«
+á…¬
+á…­
+á…®
+á…¯
+á…°
+á…±
+á…²
+á…³
+á…´
+á…µ
+á…¶
+á…·
+á…¸
+á…¹
+á…º
+á…»
+á…¼
+á…½
+á…¾
+á…¿
+ᆀ
+á†
+ᆂ
+ᆃ
+ᆄ
+ᆅ
+ᆆ
+ᆇ
+ᆈ
+ᆉ
+ᆊ
+ᆋ
+ᆌ
+á†
+ᆎ
+á†
+á†
+ᆑ
+ᆒ
+ᆓ
+ᆔ
+ᆕ
+ᆖ
+ᆗ
+ᆘ
+ᆙ
+ᆚ
+ᆛ
+ᆜ
+á†
+ᆞ
+ᆟ
+ᆠ
+ᆡ
+ᆢ
+ᆨ
+ᆩ
+ᆪ
+ᆫ
+ᆬ
+ᆭ
+ᆮ
+ᆯ
+ᆰ
+ᆱ
+ᆲ
+ᆳ
+ᆴ
+ᆵ
+ᆶ
+ᆷ
+ᆸ
+ᆹ
+ᆺ
+ᆻ
+ᆼ
+ᆽ
+ᆾ
+ᆿ
+ᇀ
+á‡
+ᇂ
+ᇃ
+ᇄ
+ᇅ
+ᇆ
+ᇇ
+ᇈ
+ᇉ
+ᇊ
+ᇋ
+ᇌ
+á‡
+ᇎ
+á‡
+á‡
+ᇑ
+ᇒ
+ᇓ
+ᇔ
+ᇕ
+ᇖ
+ᇗ
+ᇘ
+ᇙ
+ᇚ
+ᇛ
+ᇜ
+á‡
+ᇞ
+ᇟ
+ᇠ
+ᇡ
+ᇢ
+ᇣ
+ᇤ
+ᇥ
+ᇦ
+ᇧ
+ᇨ
+ᇩ
+ᇪ
+ᇫ
+ᇬ
+ᇭ
+ᇮ
+ᇯ
+ᇰ
+ᇱ
+ᇲ
+ᇳ
+ᇴ
+ᇵ
+ᇶ
+ᇷ
+ᇸ
+ᇹ
+ሀ
+áˆ
+ሂ
+ሃ
+ሄ
+ህ
+ሆ
+ሇ
+ለ
+ሉ
+ሊ
+ላ
+ሌ
+áˆ
+ሎ
+áˆ
+áˆ
+ሑ
+ሒ
+ሓ
+ሔ
+ሕ
+ሖ
+ሗ
+መ
+ሙ
+ሚ
+ማ
+ሜ
+áˆ
+ሞ
+ሟ
+ሠ
+ሡ
+ሢ
+ሣ
+ሤ
+ሥ
+ሦ
+ሧ
+ረ
+ሩ
+ሪ
+ራ
+ሬ
+ር
+ሮ
+ሯ
+ሰ
+ሱ
+ሲ
+ሳ
+ሴ
+ስ
+ሶ
+ሷ
+ሸ
+ሹ
+ሺ
+ሻ
+ሼ
+ሽ
+ሾ
+ሿ
+ቀ
+á‰
+ቂ
+ቃ
+ቄ
+ቅ
+ቆ
+ቇ
+ቈ
+ቊ
+ቋ
+ቌ
+á‰
+á‰
+ቑ
+ቒ
+ቓ
+ቔ
+ቕ
+ቖ
+ቘ
+ቚ
+ቛ
+ቜ
+á‰
+በ
+ቡ
+ቢ
+ባ
+ቤ
+ብ
+ቦ
+ቧ
+ቨ
+ቩ
+ቪ
+ቫ
+ቬ
+ቭ
+ቮ
+ቯ
+ተ
+ቱ
+ቲ
+ታ
+ቴ
+ት
+ቶ
+ቷ
+ቸ
+ቹ
+ቺ
+ቻ
+ቼ
+ች
+ቾ
+ቿ
+ኀ
+áŠ
+ኂ
+ኃ
+ኄ
+ኅ
+ኆ
+ኇ
+ኈ
+ኊ
+ኋ
+ኌ
+áŠ
+áŠ
+ኑ
+ኒ
+ና
+ኔ
+ን
+ኖ
+ኗ
+ኘ
+ኙ
+ኚ
+ኛ
+ኜ
+áŠ
+ኞ
+ኟ
+አ
+ኡ
+ኢ
+ኣ
+ኤ
+እ
+ኦ
+ኧ
+ከ
+ኩ
+ኪ
+ካ
+ኬ
+ክ
+ኮ
+ኯ
+ኰ
+ኲ
+ኳ
+ኴ
+ኵ
+ኸ
+ኹ
+ኺ
+ኻ
+ኼ
+ኽ
+ኾ
+á‹€
+á‹‚
+ዃ
+á‹„
+á‹…
+ወ
+ዉ
+á‹Š
+á‹‹
+ዌ
+á‹
+á‹Ž
+á‹
+á‹
+á‹‘
+á‹’
+á‹“
+á‹”
+á‹•
+á‹–
+ዘ
+á‹™
+á‹š
+á‹›
+ዜ
+á‹
+á‹ž
+á‹Ÿ
+á‹ 
+á‹¡
+á‹¢
+á‹£
+ዤ
+á‹¥
+ዦ
+ዧ
+የ
+á‹©
+ዪ
+á‹«
+ዬ
+á‹­
+á‹®
+ዯ
+á‹°
+ዱ
+ዲ
+ዳ
+á‹´
+ድ
+ዶ
+á‹·
+ዸ
+ዹ
+ዺ
+á‹»
+ዼ
+ዽ
+ዾ
+á‹¿
+ጀ
+áŒ
+ጂ
+ጃ
+ጄ
+ጅ
+ጆ
+ጇ
+ገ
+ጉ
+ጊ
+ጋ
+ጌ
+áŒ
+ጎ
+áŒ
+áŒ
+ጒ
+ጓ
+ጔ
+ጕ
+ጘ
+ጙ
+ጚ
+ጛ
+ጜ
+áŒ
+ጞ
+ጟ
+ጠ
+ጡ
+ጢ
+ጣ
+ጤ
+ጥ
+ጦ
+ጧ
+ጨ
+ጩ
+ጪ
+ጫ
+ጬ
+ጭ
+ጮ
+ጯ
+ጰ
+ጱ
+ጲ
+ጳ
+ጴ
+ጵ
+ጶ
+ጷ
+ጸ
+ጹ
+ጺ
+ጻ
+ጼ
+ጽ
+ጾ
+ጿ
+á€
+á‚
+áƒ
+á„
+á…
+á†
+á‡
+áˆ
+á‰
+áŠ
+á‹
+áŒ
+áŽ
+á‘
+á’
+á“
+á”
+á•
+á–
+á—
+á˜
+á™
+áš
+ᎀ
+áŽ
+ᎂ
+ᎃ
+ᎄ
+ᎅ
+ᎆ
+ᎇ
+ᎈ
+ᎉ
+ᎊ
+ᎋ
+ᎌ
+áŽ
+ᎎ
+áŽ
+Ꭰ
+Ꭱ
+Ꭲ
+Ꭳ
+Ꭴ
+Ꭵ
+Ꭶ
+Ꭷ
+Ꭸ
+Ꭹ
+Ꭺ
+Ꭻ
+Ꭼ
+Ꭽ
+Ꭾ
+Ꭿ
+Ꮀ
+Ꮁ
+Ꮂ
+Ꮃ
+Ꮄ
+Ꮅ
+Ꮆ
+Ꮇ
+Ꮈ
+Ꮉ
+Ꮊ
+Ꮋ
+Ꮌ
+Ꮍ
+Ꮎ
+Ꮏ
+á€
+á‚
+áƒ
+á„
+á…
+á†
+á‡
+áˆ
+á‰
+áŠ
+á‹
+áŒ
+áŽ
+á‘
+á’
+á“
+á”
+á•
+á–
+á—
+á˜
+á™
+áš
+á›
+áœ
+áž
+áŸ
+á 
+á¡
+á¢
+á£
+á¤
+á¥
+á¦
+á§
+á¨
+á©
+áª
+á«
+á¬
+á­
+á®
+á¯
+á°
+á±
+á²
+á³
+á´
+á‚
+áƒ
+á„
+á…
+á†
+á‡
+áˆ
+á‰
+áŠ
+á‹
+áŒ
+áŽ
+á‘
+á’
+á“
+á”
+á•
+á–
+á—
+á˜
+á™
+áš
+á›
+áœ
+áž
+áŸ
+á 
+á¡
+á¢
+á£
+á¤
+á¥
+á¦
+á§
+á¨
+á©
+áª
+á«
+á¬
+á­
+á®
+á¯
+á°
+á±
+á²
+á³
+á´
+áµ
+á¶
+á·
+á¸
+á¹
+áº
+á»
+á¼
+á½
+á¾
+á¿
+á‘€
+á‘
+á‘‚
+ᑃ
+á‘„
+á‘…
+ᑆ
+ᑇ
+ᑈ
+ᑉ
+á‘Š
+á‘‹
+ᑌ
+á‘
+á‘Ž
+á‘
+á‘
+á‘‘
+á‘’
+á‘“
+á‘”
+á‘•
+á‘–
+á‘—
+ᑘ
+á‘™
+á‘š
+á‘›
+ᑜ
+á‘
+á‘ž
+á‘Ÿ
+á‘ 
+á‘¡
+á‘¢
+á‘£
+ᑤ
+á‘¥
+ᑦ
+ᑧ
+ᑨ
+á‘©
+ᑪ
+á‘«
+ᑬ
+á‘­
+á‘®
+ᑯ
+á‘°
+ᑱ
+ᑲ
+ᑳ
+á‘´
+ᑵ
+ᑶ
+á‘·
+ᑸ
+ᑹ
+ᑺ
+á‘»
+ᑼ
+ᑽ
+ᑾ
+á‘¿
+á’€
+á’
+á’‚
+á’ƒ
+á’„
+á’…
+á’†
+á’‡
+á’ˆ
+á’‰
+á’Š
+á’‹
+ᒌ
+á’
+á’Ž
+á’
+á’
+á’‘
+á’’
+á’“
+á’”
+á’•
+á’–
+á’—
+á’˜
+á’™
+á’š
+á’›
+ᒜ
+á’
+á’ž
+á’Ÿ
+á’ 
+á’¡
+á’¢
+á’£
+á’¤
+á’¥
+á’¦
+á’§
+á’¨
+á’©
+á’ª
+á’«
+á’¬
+á’­
+á’®
+á’¯
+á’°
+á’±
+á’²
+á’³
+á’´
+á’µ
+á’¶
+á’·
+á’¸
+á’¹
+á’º
+á’»
+á’¼
+á’½
+á’¾
+á’¿
+á“€
+á“
+á“‚
+ᓃ
+á“„
+á“…
+ᓆ
+ᓇ
+ᓈ
+ᓉ
+á“Š
+á“‹
+ᓌ
+á“
+á“Ž
+á“
+á“
+á“‘
+á“’
+á““
+á“”
+á“•
+á“–
+á“—
+ᓘ
+á“™
+á“š
+á“›
+ᓜ
+á“
+á“ž
+á“Ÿ
+á“ 
+á“¡
+á“¢
+á“£
+ᓤ
+á“¥
+ᓦ
+ᓧ
+ᓨ
+á“©
+ᓪ
+á“«
+ᓬ
+á“­
+á“®
+ᓯ
+á“°
+ᓱ
+ᓲ
+ᓳ
+á“´
+ᓵ
+ᓶ
+á“·
+ᓸ
+ᓹ
+ᓺ
+á“»
+ᓼ
+ᓽ
+ᓾ
+á“¿
+ᔀ
+á”
+ᔂ
+ᔃ
+ᔄ
+á”…
+ᔆ
+ᔇ
+ᔈ
+ᔉ
+ᔊ
+ᔋ
+ᔌ
+á”
+ᔎ
+á”
+á”
+ᔑ
+á”’
+ᔓ
+á””
+ᔕ
+á”–
+á”—
+ᔘ
+á”™
+ᔚ
+á”›
+ᔜ
+á”
+ᔞ
+ᔟ
+á” 
+ᔡ
+ᔢ
+ᔣ
+ᔤ
+ᔥ
+ᔦ
+ᔧ
+ᔨ
+ᔩ
+ᔪ
+ᔫ
+ᔬ
+á”­
+á”®
+ᔯ
+á”°
+á”±
+ᔲ
+ᔳ
+á”´
+ᔵ
+ᔶ
+á”·
+ᔸ
+ᔹ
+ᔺ
+á”»
+ᔼ
+ᔽ
+ᔾ
+ᔿ
+á•€
+á•
+á•‚
+ᕃ
+á•„
+á•…
+ᕆ
+ᕇ
+ᕈ
+ᕉ
+á•Š
+á•‹
+ᕌ
+á•
+á•Ž
+á•
+á•
+á•‘
+á•’
+á•“
+á•”
+á••
+á•–
+á•—
+ᕘ
+á•™
+á•š
+á•›
+ᕜ
+á•
+á•ž
+á•Ÿ
+á• 
+á•¡
+á•¢
+á•£
+ᕤ
+á•¥
+ᕦ
+ᕧ
+ᕨ
+á•©
+ᕪ
+á•«
+ᕬ
+á•­
+á•®
+ᕯ
+á•°
+ᕱ
+ᕲ
+ᕳ
+á•´
+ᕵ
+ᕶ
+á•·
+ᕸ
+ᕹ
+ᕺ
+á•»
+ᕼ
+ᕽ
+ᕾ
+á•¿
+á–€
+á–
+á–‚
+á–ƒ
+á–„
+á–…
+á–†
+á–‡
+á–ˆ
+á–‰
+á–Š
+á–‹
+ᖌ
+á–
+á–Ž
+á–
+á–
+á–‘
+á–’
+á–“
+á–”
+á–•
+á––
+á–—
+á–˜
+á–™
+á–š
+á–›
+ᖜ
+á–
+á–ž
+á–Ÿ
+á– 
+á–¡
+á–¢
+á–£
+á–¤
+á–¥
+á–¦
+á–§
+á–¨
+á–©
+á–ª
+á–«
+á–¬
+á–­
+á–®
+á–¯
+á–°
+á–±
+á–²
+á–³
+á–´
+á–µ
+á–¶
+á–·
+á–¸
+á–¹
+á–º
+á–»
+á–¼
+á–½
+á–¾
+á–¿
+á—€
+á—
+á—‚
+á—ƒ
+á—„
+á—…
+á—†
+á—‡
+á—ˆ
+á—‰
+á—Š
+á—‹
+ᗌ
+á—
+á—Ž
+á—
+á—
+á—‘
+á—’
+á—“
+á—”
+á—•
+á—–
+á——
+á—˜
+á—™
+á—š
+á—›
+ᗜ
+á—
+á—ž
+á—Ÿ
+á— 
+á—¡
+á—¢
+á—£
+á—¤
+á—¥
+á—¦
+á—§
+á—¨
+á—©
+á—ª
+á—«
+á—¬
+á—­
+á—®
+á—¯
+á—°
+á—±
+á—²
+á—³
+á—´
+á—µ
+á—¶
+á—·
+á—¸
+á—¹
+á—º
+á—»
+á—¼
+á—½
+á—¾
+á—¿
+ᘀ
+á˜
+ᘂ
+ᘃ
+ᘄ
+ᘅ
+ᘆ
+ᘇ
+ᘈ
+ᘉ
+ᘊ
+ᘋ
+ᘌ
+á˜
+ᘎ
+á˜
+á˜
+ᘑ
+ᘒ
+ᘓ
+ᘔ
+ᘕ
+ᘖ
+ᘗ
+ᘘ
+ᘙ
+ᘚ
+ᘛ
+ᘜ
+á˜
+ᘞ
+ᘟ
+ᘠ
+ᘡ
+ᘢ
+ᘣ
+ᘤ
+ᘥ
+ᘦ
+ᘧ
+ᘨ
+ᘩ
+ᘪ
+ᘫ
+ᘬ
+ᘭ
+ᘮ
+ᘯ
+ᘰ
+ᘱ
+ᘲ
+ᘳ
+ᘴ
+ᘵ
+ᘶ
+ᘷ
+ᘸ
+ᘹ
+ᘺ
+ᘻ
+ᘼ
+ᘽ
+ᘾ
+ᘿ
+ᙀ
+á™
+ᙂ
+ᙃ
+ᙄ
+á™…
+ᙆ
+ᙇ
+ᙈ
+ᙉ
+ᙊ
+ᙋ
+ᙌ
+á™
+ᙎ
+á™
+á™
+ᙑ
+á™’
+ᙓ
+á™”
+ᙕ
+á™–
+á™—
+ᙘ
+á™™
+ᙚ
+á™›
+ᙜ
+á™
+ᙞ
+ᙟ
+á™ 
+ᙡ
+ᙢ
+ᙣ
+ᙤ
+ᙥ
+ᙦ
+ᙧ
+ᙨ
+ᙩ
+ᙪ
+ᙫ
+ᙬ
+ᙯ
+á™°
+á™±
+ᙲ
+ᙳ
+á™´
+ᙵ
+ᙶ
+áš
+áš‚
+ᚃ
+áš„
+áš…
+ᚆ
+ᚇ
+ᚈ
+ᚉ
+ᚊ
+áš‹
+ᚌ
+áš
+ᚎ
+áš
+áš
+áš‘
+áš’
+áš“
+áš”
+áš•
+áš–
+áš—
+ᚘ
+áš™
+ášš
+áš 
+áš¡
+ᚢ
+ᚣ
+ᚤ
+ᚥ
+ᚦ
+ᚧ
+ᚨ
+áš©
+ᚪ
+áš«
+ᚬ
+áš­
+áš®
+ᚯ
+áš°
+áš±
+áš²
+áš³
+áš´
+ášµ
+ᚶ
+áš·
+ᚸ
+áš¹
+ᚺ
+áš»
+áš¼
+áš½
+áš¾
+áš¿
+ᛀ
+á›
+ᛂ
+ᛃ
+ᛄ
+á›…
+ᛆ
+ᛇ
+ᛈ
+ᛉ
+ᛊ
+ᛋ
+ᛌ
+á›
+ᛎ
+á›
+á›
+ᛑ
+á›’
+ᛓ
+á›”
+ᛕ
+á›–
+á›—
+ᛘ
+á›™
+ᛚ
+á››
+ᛜ
+á›
+ᛞ
+ᛟ
+á› 
+ᛡ
+ᛢ
+ᛣ
+ᛤ
+ᛥ
+ᛦ
+ᛧ
+ᛨ
+ᛩ
+ᛪ
+ᜀ
+áœ
+ᜂ
+ᜃ
+ᜄ
+ᜅ
+ᜆ
+ᜇ
+ᜈ
+ᜉ
+ᜊ
+ᜋ
+ᜌ
+ᜎ
+áœ
+áœ
+ᜑ
+ᜠ
+ᜡ
+ᜢ
+ᜣ
+ᜤ
+ᜥ
+ᜦ
+ᜧ
+ᜨ
+ᜩ
+ᜪ
+ᜫ
+ᜬ
+ᜭ
+ᜮ
+ᜯ
+ᜰ
+ᜱ
+á€
+á‚
+áƒ
+á„
+á…
+á†
+á‡
+áˆ
+á‰
+áŠ
+á‹
+áŒ
+áŽ
+á‘
+á 
+á¡
+á¢
+á£
+á¤
+á¥
+á¦
+á§
+á¨
+á©
+áª
+á«
+á¬
+á®
+á¯
+á°
+ក
+áž
+áž‚
+ឃ
+áž„
+áž…
+ឆ
+ជ
+ឈ
+ញ
+ដ
+áž‹
+ឌ
+áž
+ណ
+áž
+áž
+áž‘
+áž’
+áž“
+áž”
+áž•
+áž–
+áž—
+ម
+áž™
+ážš
+áž›
+វ
+áž
+ážž
+ស
+áž 
+áž¡
+អ
+ឣ
+ឤ
+ឥ
+ឦ
+ឧ
+ឨ
+áž©
+ឪ
+áž«
+ឬ
+áž­
+áž®
+ឯ
+áž°
+áž±
+áž²
+áž³
+ៜ
+á  
+á ¡
+á ¢
+á £
+á ¤
+á ¥
+á ¦
+á §
+á ¨
+á ©
+á ª
+á «
+á ¬
+á ­
+á ®
+á ¯
+á °
+á ±
+á ²
+á ³
+á ´
+á µ
+á ¶
+á ·
+á ¸
+á ¹
+á º
+á »
+á ¼
+á ½
+á ¾
+á ¿
+á¡€
+á¡
+á¡‚
+á¡„
+á¡…
+ᡆ
+ᡇ
+ᡈ
+ᡉ
+á¡Š
+á¡‹
+ᡌ
+á¡
+á¡Ž
+á¡
+á¡
+á¡‘
+á¡’
+á¡“
+á¡”
+á¡•
+á¡–
+á¡—
+ᡘ
+á¡™
+á¡š
+á¡›
+ᡜ
+á¡
+á¡ž
+á¡Ÿ
+á¡ 
+á¡¡
+á¡¢
+á¡£
+ᡤ
+á¡¥
+ᡦ
+ᡧ
+ᡨ
+á¡©
+ᡪ
+á¡«
+ᡬ
+á¡­
+á¡®
+ᡯ
+á¡°
+ᡱ
+ᡲ
+ᡳ
+á¡´
+ᡵ
+ᡶ
+á¡·
+ᢀ
+á¢
+ᢂ
+ᢃ
+ᢄ
+ᢅ
+ᢆ
+ᢇ
+ᢈ
+ᢉ
+ᢊ
+ᢋ
+ᢌ
+á¢
+ᢎ
+á¢
+á¢
+ᢑ
+ᢒ
+ᢓ
+ᢔ
+ᢕ
+ᢖ
+ᢗ
+ᢘ
+ᢙ
+ᢚ
+ᢛ
+ᢜ
+á¢
+ᢞ
+ᢟ
+ᢠ
+ᢡ
+ᢢ
+ᢣ
+ᢤ
+ᢥ
+ᢦ
+ᢧ
+ᢨ
+ᢪ
+ᤀ
+á¤
+ᤂ
+ᤃ
+ᤄ
+ᤅ
+ᤆ
+ᤇ
+ᤈ
+ᤉ
+ᤊ
+ᤋ
+ᤌ
+á¤
+ᤎ
+á¤
+á¤
+ᤑ
+ᤒ
+ᤓ
+ᤔ
+ᤕ
+ᤖ
+ᤗ
+ᤘ
+ᤙ
+ᤚ
+ᤛ
+ᤜ
+á¥
+ᥑ
+ᥒ
+ᥓ
+ᥔ
+ᥕ
+ᥖ
+ᥗ
+ᥘ
+ᥙ
+ᥚ
+ᥛ
+ᥜ
+á¥
+ᥞ
+ᥟ
+ᥠ
+ᥡ
+ᥢ
+ᥣ
+ᥤ
+ᥥ
+ᥦ
+ᥧ
+ᥨ
+ᥩ
+ᥪ
+ᥫ
+ᥬ
+ᥭ
+ᥰ
+ᥱ
+ᥲ
+ᥳ
+ᥴ
+ᦀ
+á¦
+ᦂ
+ᦃ
+ᦄ
+ᦅ
+ᦆ
+ᦇ
+ᦈ
+ᦉ
+ᦊ
+ᦋ
+ᦌ
+á¦
+ᦎ
+á¦
+á¦
+ᦑ
+ᦒ
+ᦓ
+ᦔ
+ᦕ
+ᦖ
+ᦗ
+ᦘ
+ᦙ
+ᦚ
+ᦛ
+ᦜ
+á¦
+ᦞ
+ᦟ
+ᦠ
+ᦡ
+ᦢ
+ᦣ
+ᦤ
+ᦥ
+ᦦ
+ᦧ
+ᦨ
+ᦩ
+á§
+ᧂ
+ᧃ
+ᧄ
+ᧅ
+ᧆ
+ᧇ
+ᨀ
+á¨
+ᨂ
+ᨃ
+ᨄ
+ᨅ
+ᨆ
+ᨇ
+ᨈ
+ᨉ
+ᨊ
+ᨋ
+ᨌ
+á¨
+ᨎ
+á¨
+á¨
+ᨑ
+ᨒ
+ᨓ
+ᨔ
+ᨕ
+ᨖ
+ᬅ
+ᬆ
+ᬇ
+ᬈ
+ᬉ
+ᬊ
+ᬋ
+ᬌ
+á¬
+ᬎ
+á¬
+á¬
+ᬑ
+ᬒ
+ᬓ
+ᬔ
+ᬕ
+ᬖ
+ᬗ
+ᬘ
+ᬙ
+ᬚ
+ᬛ
+ᬜ
+á¬
+ᬞ
+ᬟ
+ᬠ
+ᬡ
+ᬢ
+ᬣ
+ᬤ
+ᬥ
+ᬦ
+ᬧ
+ᬨ
+ᬩ
+ᬪ
+ᬫ
+ᬬ
+ᬭ
+ᬮ
+ᬯ
+ᬰ
+ᬱ
+ᬲ
+ᬳ
+á­…
+á­†
+á­‡
+á­ˆ
+á­‰
+á­Š
+á­‹
+ᮃ
+ᮄ
+á®…
+ᮆ
+ᮇ
+ᮈ
+ᮉ
+ᮊ
+ᮋ
+ᮌ
+á®
+ᮎ
+á®
+á®
+ᮑ
+á®’
+ᮓ
+á®”
+ᮕ
+á®–
+á®—
+ᮘ
+á®™
+ᮚ
+á®›
+ᮜ
+á®
+ᮞ
+ᮟ
+á® 
+á®®
+ᮯ
+á°€
+á°
+á°‚
+á°ƒ
+á°„
+á°…
+á°†
+á°‡
+á°ˆ
+á°‰
+á°Š
+á°‹
+ᰌ
+á°
+á°Ž
+á°
+á°
+á°‘
+á°’
+á°“
+á°”
+á°•
+á°–
+á°—
+á°˜
+á°™
+á°š
+á°›
+ᰜ
+á°
+á°ž
+á°Ÿ
+á° 
+á°¡
+á°¢
+á°£
+á±
+ᱎ
+á±
+ᱚ
+á±›
+ᱜ
+á±
+ᱞ
+ᱟ
+á± 
+ᱡ
+á±¢
+á±£
+ᱤ
+á±¥
+ᱦ
+ᱧ
+ᱨ
+ᱩ
+ᱪ
+ᱫ
+ᱬ
+á±­
+á±®
+ᱯ
+á±°
+á±±
+á±²
+á±³
+á±´
+á±µ
+ᱶ
+á±·
+á´€
+á´
+á´‚
+á´ƒ
+á´„
+á´…
+á´†
+á´‡
+á´ˆ
+á´‰
+á´Š
+á´‹
+ᴌ
+á´
+á´Ž
+á´
+á´
+á´‘
+á´’
+á´“
+á´”
+á´•
+á´–
+á´—
+á´˜
+á´™
+á´š
+á´›
+ᴜ
+á´
+á´ž
+á´Ÿ
+á´ 
+á´¡
+á´¢
+á´£
+á´¤
+á´¥
+á´¦
+á´§
+á´¨
+á´©
+á´ª
+á´«
+áµ¢
+áµ£
+ᵤ
+áµ¥
+ᵦ
+ᵧ
+ᵨ
+ᵩ
+ᵪ
+ᵫ
+ᵬ
+áµ­
+áµ®
+ᵯ
+áµ°
+áµ±
+áµ²
+áµ³
+áµ´
+áµµ
+ᵶ
+áµ·
+áµ¹ê½
+ᵺ
+áµ»
+áµ¼
+ᵽⱣ
+áµ¾
+ᵿ
+ᶀ
+á¶
+ᶂ
+ᶃ
+ᶄ
+ᶅ
+ᶆ
+ᶇ
+ᶈ
+ᶉ
+ᶊ
+ᶋ
+ᶌ
+á¶
+ᶎ
+á¶
+á¶
+ᶑ
+ᶒ
+ᶓ
+ᶔ
+ᶕ
+ᶖ
+ᶗ
+ᶘ
+ᶙ
+ᶚ
+á¸á¸€
+ḃḂ
+ḅḄ
+ḇḆ
+ḉḈ
+ḋḊ
+á¸á¸Œ
+á¸á¸Ž
+ḑá¸
+ḓḒ
+ḕḔ
+ḗḖ
+ḙḘ
+ḛḚ
+á¸á¸œ
+ḟḞ
+ḡḠ
+ḣḢ
+ḥḤ
+ḧḦ
+ḩḨ
+ḫḪ
+ḭḬ
+ḯḮ
+ḱḰ
+ḳḲ
+ḵḴ
+ḷḶ
+ḹḸ
+ḻḺ
+ḽḼ
+ḿḾ
+á¹á¹€
+ṃṂ
+ṅṄ
+ṇṆ
+ṉṈ
+ṋṊ
+á¹á¹Œ
+á¹á¹Ž
+ṑá¹
+ṓṒ
+ṕṔ
+á¹—á¹–
+ṙṘ
+ṛṚ
+á¹á¹œ
+ṟṞ
+ṡṠ
+ṣṢ
+ṥṤ
+ṧṦ
+ṩṨ
+ṫṪ
+ṭṬ
+ṯṮ
+ṱṰ
+ṳṲ
+ṵṴ
+ṷṶ
+ṹṸ
+ṻṺ
+ṽṼ
+ṿṾ
+áºáº€
+ẃẂ
+ẅẄ
+ẇẆ
+ẉẈ
+ẋẊ
+áºáºŒ
+áºáºŽ
+ẑáº
+ẓẒ
+ẕẔ
+ẖ
+ẗ
+ẘ
+ẙ
+ẚ
+ẛṠ
+ẜ
+áº
+ẟ
+ạẠ
+ảẢ
+ấẤ
+ầẦ
+ẩẨ
+ẫẪ
+ậẬ
+ắẮ
+ằẰ
+ẳẲ
+ẵẴ
+ặẶ
+ẹẸ
+ẻẺ
+ẽẼ
+ếẾ
+á»á»€
+ểỂ
+ễỄ
+ệỆ
+ỉỈ
+ịỊ
+á»á»Œ
+á»á»Ž
+ốá»
+ồỒ
+ổỔ
+á»—á»–
+ộỘ
+ớỚ
+á»á»œ
+ởỞ
+ỡỠ
+ợỢ
+ụỤ
+ủỦ
+ứỨ
+ừỪ
+ửỬ
+ữỮ
+ựỰ
+ỳỲ
+ỵỴ
+ỷỶ
+ỹỸ
+ỻỺ
+ỽỼ
+ỿỾ
+ἀἈ
+á¼á¼‰
+ἂἊ
+ἃἋ
+ἄἌ
+á¼…á¼
+ἆἎ
+ἇá¼
+á¼á¼˜
+ἑἙ
+ἒἚ
+ἓἛ
+ἔἜ
+ἕá¼
+ἠἨ
+ἡἩ
+ἢἪ
+ἣἫ
+ἤἬ
+ἥἭ
+ἦἮ
+ἧἯ
+ἰἸ
+ἱἹ
+ἲἺ
+ἳἻ
+á¼´á¼¼
+ἵἽ
+ἶἾ
+ἷἿ
+ὀὈ
+á½á½‰
+ὂὊ
+ὃὋ
+ὄὌ
+á½…á½
+á½
+ὑὙ
+á½’
+ὓὛ
+á½”
+ὕá½
+á½–
+ὗὟ
+ὠὨ
+ὡὩ
+ὢὪ
+ὣὫ
+ὤὬ
+ὥὭ
+ὦὮ
+ὧὯ
+ὰᾺ
+άΆ
+ὲῈ
+έΈ
+á½´á¿Š
+ήΉ
+ὶῚ
+á½·á¿›
+ὸῸ
+όΌ
+ὺῪ
+ύΎ
+ὼῺ
+ώΏ
+ᾀᾈ
+á¾á¾‰
+ᾂᾊ
+ᾃᾋ
+ᾄᾌ
+á¾…á¾
+ᾆᾎ
+ᾇá¾
+á¾á¾˜
+ᾑᾙ
+ᾒᾚ
+ᾓᾛ
+ᾔᾜ
+ᾕá¾
+ᾖᾞ
+ᾗᾟ
+ᾠᾨ
+ᾡᾩ
+ᾢᾪ
+ᾣᾫ
+ᾤᾬ
+ᾥᾭ
+ᾦᾮ
+ᾧᾯ
+ᾰᾸ
+ᾱᾹ
+á¾²
+ᾳᾼ
+á¾´
+ᾶ
+á¾·
+ιΙ
+á¿‚
+ῃῌ
+á¿„
+ῆ
+ῇ
+á¿á¿˜
+á¿‘á¿™
+á¿’
+á¿“
+á¿–
+á¿—
+ῠῨ
+á¿¡á¿©
+á¿¢
+á¿£
+ῤ
+ῥῬ
+ῦ
+ῧ
+ῲ
+ῳῼ
+á¿´
+ῶ
+á¿·
+ↄↃ
+â°°â°€
+â°±â°
+ⰲⰂ
+ⰳⰃ
+â°´â°„
+ⰵⰅ
+ⰶⰆ
+â°·â°‡
+ⰸⰈ
+ⰹⰉ
+ⰺⰊ
+ⰻⰋ
+ⰼⰌ
+â°½â°
+ⰾⰎ
+â°¿â°
+â±€â°
+â±â°‘
+ⱂⰒ
+ⱃⰓ
+ⱄⰔ
+â±…â°•
+ⱆⰖ
+ⱇⰗ
+ⱈⰘ
+ⱉⰙ
+ⱊⰚ
+ⱋⰛ
+ⱌⰜ
+â±â°
+ⱎⰞ
+â±â°Ÿ
+â±â° 
+ⱑⰡ
+â±’â°¢
+ⱓⰣ
+ⱔⰤ
+ⱕⰥ
+â±–â°¦
+â±—â°§
+ⱘⰨ
+ⱙⰩ
+ⱚⰪ
+ⱛⰫ
+ⱜⰬ
+â±â°­
+ⱞⰮ
+ⱡⱠ
+ⱥȺ
+ⱦȾ
+ⱨⱧ
+ⱪⱩ
+ⱬⱫ
+â±±
+ⱳⱲ
+â±´
+ⱶⱵ
+â±·
+ⱸ
+â±¹
+ⱺ
+â±»
+â±¼
+â²â²€
+ⲃⲂ
+ⲅⲄ
+ⲇⲆ
+ⲉⲈ
+ⲋⲊ
+â²â²Œ
+â²â²Ž
+ⲑâ²
+ⲓⲒ
+ⲕⲔ
+â²—â²–
+ⲙⲘ
+ⲛⲚ
+â²â²œ
+ⲟⲞ
+ⲡⲠ
+ⲣⲢ
+ⲥⲤ
+ⲧⲦ
+ⲩⲨ
+ⲫⲪ
+ⲭⲬ
+ⲯⲮ
+ⲱⲰ
+ⲳⲲ
+ⲵⲴ
+ⲷⲶ
+ⲹⲸ
+ⲻⲺ
+ⲽⲼ
+ⲿⲾ
+â³â³€
+ⳃⳂ
+ⳅⳄ
+ⳇⳆ
+ⳉⳈ
+ⳋⳊ
+â³â³Œ
+â³â³Ž
+ⳑâ³
+ⳓⳒ
+ⳕⳔ
+â³—â³–
+ⳙⳘ
+ⳛⳚ
+â³â³œ
+ⳟⳞ
+ⳡⳠ
+ⳣⳢ
+ⳤ
+ⴀႠ
+â´á‚¡
+â´‚á‚¢
+ⴃႣ
+ⴄႤ
+â´…á‚¥
+ⴆႦ
+ⴇႧ
+ⴈႨ
+ⴉႩ
+ⴊႪ
+â´‹á‚«
+ⴌႬ
+â´á‚­
+â´Žá‚®
+â´á‚¯
+â´á‚°
+ⴑႱ
+ⴒႲ
+ⴓႳ
+ⴔႴ
+ⴕႵ
+ⴖႶ
+â´—á‚·
+ⴘႸ
+ⴙႹ
+ⴚႺ
+ⴛႻ
+ⴜႼ
+â´á‚½
+ⴞႾ
+â´Ÿá‚¿
+ⴠჀ
+â´¡áƒ
+ⴢჂ
+ⴣჃ
+ⴤჄ
+ⴥჅ
+â´°
+â´±
+â´²
+â´³
+â´´
+â´µ
+â´¶
+â´·
+â´¸
+â´¹
+â´º
+â´»
+â´¼
+â´½
+â´¾
+â´¿
+âµ€
+âµ
+ⵂ
+ⵃ
+ⵄ
+âµ…
+ⵆ
+ⵇ
+ⵈ
+ⵉ
+ⵊ
+ⵋ
+ⵌ
+âµ
+ⵎ
+âµ
+âµ
+ⵑ
+âµ’
+ⵓ
+âµ”
+ⵕ
+âµ–
+âµ—
+ⵘ
+âµ™
+ⵚ
+âµ›
+ⵜ
+âµ
+ⵞ
+ⵟ
+âµ 
+ⵡ
+âµ¢
+âµ£
+ⵤ
+âµ¥
+ⶀ
+â¶
+ⶂ
+ⶃ
+ⶄ
+ⶅ
+ⶆ
+ⶇ
+ⶈ
+ⶉ
+ⶊ
+ⶋ
+ⶌ
+â¶
+ⶎ
+â¶
+â¶
+ⶑ
+ⶒ
+ⶓ
+ⶔ
+ⶕ
+ⶖ
+ⶠ
+ⶡ
+ⶢ
+ⶣ
+ⶤ
+ⶥ
+ⶦ
+ⶨ
+ⶩ
+ⶪ
+ⶫ
+ⶬ
+ⶭ
+ⶮ
+ⶰ
+ⶱ
+ⶲ
+ⶳ
+ⶴ
+ⶵ
+ⶶ
+ⶸ
+ⶹ
+ⶺ
+ⶻ
+ⶼ
+ⶽ
+ⶾ
+â·€
+â·
+â·‚
+â·ƒ
+â·„
+â·…
+â·†
+â·ˆ
+â·‰
+â·Š
+â·‹
+ⷌ
+â·
+â·Ž
+â·
+â·‘
+â·’
+â·“
+â·”
+â·•
+â·–
+â·˜
+â·™
+â·š
+â·›
+ⷜ
+â·
+â·ž
+〆
+〼
+ã‚
+ãƒ
+ã„
+ã…
+ã†
+ã‡
+ãˆ
+ã‰
+ãŠ
+ã‹
+ãŒ
+ãŽ
+ã‘
+ã’
+ã“
+ã”
+ã•
+ã–
+ã—
+ã˜
+ã™
+ãš
+ã›
+ãœ
+ãž
+ãŸ
+ã 
+ã¡
+ã¢
+ã£
+ã¤
+ã¥
+ã¦
+ã§
+ã¨
+ã©
+ãª
+ã«
+ã¬
+ã­
+ã®
+ã¯
+ã°
+ã±
+ã²
+ã³
+ã´
+ãµ
+ã¶
+ã·
+ã¸
+ã¹
+ãº
+ã»
+ã¼
+ã½
+ã¾
+ã¿
+ã‚€
+ã‚
+ã‚‚
+ゃ
+ã‚„
+ã‚…
+ゆ
+ょ
+よ
+ら
+ã‚Š
+ã‚‹
+れ
+ã‚
+ã‚Ž
+ã‚
+ã‚
+ã‚‘
+ã‚’
+ã‚“
+ã‚”
+ã‚•
+ã‚–
+ã‚Ÿ
+ã‚¡
+ã‚¢
+ã‚£
+イ
+ã‚¥
+ウ
+ェ
+エ
+ã‚©
+オ
+ã‚«
+ガ
+ã‚­
+ã‚®
+ク
+ã‚°
+ケ
+ゲ
+コ
+ã‚´
+サ
+ザ
+ã‚·
+ジ
+ス
+ズ
+ã‚»
+ゼ
+ソ
+ゾ
+ã‚¿
+ダ
+ãƒ
+ヂ
+ッ
+ツ
+ヅ
+テ
+デ
+ト
+ド
+ナ
+ニ
+ヌ
+ãƒ
+ノ
+ãƒ
+ãƒ
+パ
+ヒ
+ビ
+ピ
+フ
+ブ
+プ
+ヘ
+ベ
+ペ
+ホ
+ボ
+ãƒ
+マ
+ミ
+ム
+メ
+モ
+ャ
+ヤ
+ュ
+ユ
+ョ
+ヨ
+ラ
+リ
+ル
+レ
+ロ
+ヮ
+ワ
+ヰ
+ヱ
+ヲ
+ン
+ヴ
+ヵ
+ヶ
+ヷ
+ヸ
+ヹ
+ヺ
+ヿ
+ã„…
+ㄆ
+ㄇ
+ㄈ
+ㄉ
+ã„Š
+ã„‹
+ㄌ
+ã„
+ã„Ž
+ã„
+ã„
+ã„‘
+ã„’
+ã„“
+ã„”
+ã„•
+ã„–
+ã„—
+ㄘ
+ã„™
+ã„š
+ã„›
+ㄜ
+ã„
+ã„ž
+ã„Ÿ
+ã„ 
+ã„¡
+ã„¢
+ã„£
+ㄤ
+ã„¥
+ㄦ
+ㄧ
+ㄨ
+ã„©
+ㄪ
+ã„«
+ㄬ
+ã„­
+ㄱ
+ㄲ
+ㄳ
+ã„´
+ㄵ
+ㄶ
+ã„·
+ㄸ
+ㄹ
+ㄺ
+ã„»
+ㄼ
+ㄽ
+ㄾ
+ã„¿
+ã…€
+ã…
+ã…‚
+ã…ƒ
+ã…„
+ã……
+ã…†
+ã…‡
+ã…ˆ
+ã…‰
+ã…Š
+ã…‹
+ㅌ
+ã…
+ã…Ž
+ã…
+ã…
+ã…‘
+ã…’
+ã…“
+ã…”
+ã…•
+ã…–
+ã…—
+ã…˜
+ã…™
+ã…š
+ã…›
+ㅜ
+ã…
+ã…ž
+ã…Ÿ
+ã… 
+ã…¡
+ã…¢
+ã…£
+ã…¤
+ã…¥
+ã…¦
+ã…§
+ã…¨
+ã…©
+ã…ª
+ã…«
+ã…¬
+ã…­
+ã…®
+ã…¯
+ã…°
+ã…±
+ã…²
+ã…³
+ã…´
+ã…µ
+ã…¶
+ã…·
+ã…¸
+ã…¹
+ã…º
+ã…»
+ã…¼
+ã…½
+ã…¾
+ã…¿
+ㆀ
+ã†
+ㆂ
+ㆃ
+ㆄ
+ㆅ
+ㆆ
+ㆇ
+ㆈ
+ㆉ
+ㆊ
+ㆋ
+ㆌ
+ã†
+ㆎ
+ㆠ
+ㆡ
+ㆢ
+ㆣ
+ㆤ
+ㆥ
+ㆦ
+ㆧ
+ㆨ
+ㆩ
+ㆪ
+ㆫ
+ㆬ
+ㆭ
+ㆮ
+ㆯ
+ㆰ
+ㆱ
+ㆲ
+ㆳ
+ㆴ
+ㆵ
+ㆶ
+ㆷ
+ㇰ
+ㇱ
+ㇲ
+ㇳ
+ㇴ
+ㇵ
+ㇶ
+ㇷ
+ㇸ
+ㇹ
+ㇺ
+ㇻ
+ㇼ
+ㇽ
+ㇾ
+ㇿ
+ꀀ
+ê€
+ꀂ
+ꀃ
+ꀄ
+ꀅ
+ꀆ
+ꀇ
+ꀈ
+ꀉ
+ꀊ
+ꀋ
+ꀌ
+ê€
+ꀎ
+ê€
+ê€
+ꀑ
+ꀒ
+ꀓ
+ꀔ
+ꀖ
+ꀗ
+ꀘ
+ꀙ
+ꀚ
+ꀛ
+ꀜ
+ê€
+ꀞ
+ꀟ
+ꀠ
+ꀡ
+ꀢ
+ꀣ
+ꀤ
+ꀥ
+ꀦ
+ꀧ
+ꀨ
+ꀩ
+ꀪ
+ꀫ
+ꀬ
+ꀭ
+ꀮ
+ꀯ
+ꀰ
+ꀱ
+ꀲ
+ꀳ
+ꀴ
+ꀵ
+ꀶ
+ꀷ
+ꀸ
+ꀹ
+ꀺ
+ꀻ
+ꀼ
+ꀽ
+ꀾ
+ꀿ
+ê€
+ê‚
+êƒ
+ê„
+ê…
+ê†
+ê‡
+êˆ
+ê‰
+êŠ
+ê‹
+êŒ
+êŽ
+ê‘
+ê’
+ê“
+ê”
+ê•
+ê–
+ê—
+ê˜
+ê™
+êš
+ê›
+êœ
+êž
+êŸ
+ê 
+ê¡
+ê¢
+ê£
+ê¤
+ê¥
+ê¦
+ê§
+ê¨
+ê©
+êª
+ê«
+ê¬
+ê­
+ê®
+ê¯
+ê°
+ê±
+ê²
+ê³
+ê´
+êµ
+ê¶
+ê·
+ê¸
+ê¹
+êº
+ê»
+ê¼
+ê½
+ê¾
+ê¿
+ê‚€
+ê‚
+ê‚‚
+ꂃ
+ê‚„
+ê‚…
+ꂆ
+ꂇ
+ꂈ
+ꂉ
+ê‚Š
+ê‚‹
+ꂌ
+ê‚
+ê‚Ž
+ê‚
+ê‚
+ê‚‘
+ê‚’
+ê‚“
+ê‚”
+ê‚•
+ê‚–
+ê‚—
+ꂘ
+ê‚™
+ê‚š
+ê‚›
+ꂜ
+ê‚
+ê‚ž
+ê‚Ÿ
+ê‚ 
+ê‚¡
+ê‚¢
+ê‚£
+ꂤ
+ê‚¥
+ꂦ
+ꂧ
+ꂨ
+ê‚©
+ꂪ
+ê‚«
+ꂬ
+ê‚­
+ê‚®
+ꂯ
+ê‚°
+ꂱ
+ꂲ
+ꂳ
+ê‚´
+ꂵ
+ꂶ
+ê‚·
+ꂸ
+ꂹ
+ꂺ
+ê‚»
+ꂼ
+ꂽ
+ꂾ
+ê‚¿
+ꃀ
+êƒ
+ꃂ
+ꃃ
+ꃄ
+ꃅ
+ꃆ
+ꃇ
+ꃈ
+ꃉ
+ꃊ
+ꃋ
+ꃌ
+êƒ
+ꃎ
+êƒ
+êƒ
+ꃑ
+ꃒ
+ꃓ
+ꃔ
+ꃕ
+ꃖ
+ꃗ
+ꃘ
+ꃙ
+ꃚ
+ꃛ
+ꃜ
+êƒ
+ꃞ
+ꃟ
+ꃠ
+ꃡ
+ꃢ
+ꃣ
+ꃤ
+ꃥ
+ꃦ
+ꃧ
+ꃨ
+ꃩ
+ꃪ
+ꃫ
+ꃬ
+ꃭ
+ꃮ
+ꃯ
+ꃰ
+ꃱ
+ꃲ
+ꃳ
+ꃴ
+ꃵ
+ꃶ
+ꃷ
+ꃸ
+ꃹ
+ꃺ
+ꃻ
+ꃼ
+ꃽ
+ꃾ
+ꃿ
+ê„€
+ê„
+ê„‚
+ꄃ
+ê„„
+ê„…
+ꄆ
+ꄇ
+ꄈ
+ꄉ
+ê„Š
+ê„‹
+ꄌ
+ê„
+ê„Ž
+ê„
+ê„
+ê„‘
+ê„’
+ê„“
+ê„”
+ê„•
+ê„–
+ê„—
+ꄘ
+ê„™
+ê„š
+ê„›
+ꄜ
+ê„
+ê„ž
+ê„Ÿ
+ê„ 
+ê„¡
+ê„¢
+ê„£
+ꄤ
+ê„¥
+ꄦ
+ꄧ
+ꄨ
+ê„©
+ꄪ
+ê„«
+ꄬ
+ê„­
+ê„®
+ꄯ
+ê„°
+ꄱ
+ꄲ
+ꄳ
+ê„´
+ꄵ
+ꄶ
+ê„·
+ꄸ
+ꄹ
+ꄺ
+ê„»
+ꄼ
+ꄽ
+ꄾ
+ê„¿
+ê…€
+ê…
+ê…‚
+ê…ƒ
+ê…„
+ê……
+ê…†
+ê…‡
+ê…ˆ
+ê…‰
+ê…Š
+ê…‹
+ꅌ
+ê…
+ê…Ž
+ê…
+ê…
+ê…‘
+ê…’
+ê…“
+ê…”
+ê…•
+ê…–
+ê…—
+ê…˜
+ê…™
+ê…š
+ê…›
+ꅜ
+ê…
+ê…ž
+ê…Ÿ
+ê… 
+ê…¡
+ê…¢
+ê…£
+ê…¤
+ê…¥
+ê…¦
+ê…§
+ê…¨
+ê…©
+ê…ª
+ê…«
+ê…¬
+ê…­
+ê…®
+ê…¯
+ê…°
+ê…±
+ê…²
+ê…³
+ê…´
+ê…µ
+ê…¶
+ê…·
+ê…¸
+ê…¹
+ê…º
+ê…»
+ê…¼
+ê…½
+ê…¾
+ê…¿
+ꆀ
+ê†
+ꆂ
+ꆃ
+ꆄ
+ꆅ
+ꆆ
+ꆇ
+ꆈ
+ꆉ
+ꆊ
+ꆋ
+ꆌ
+ê†
+ꆎ
+ê†
+ê†
+ꆑ
+ꆒ
+ꆓ
+ꆔ
+ꆕ
+ꆖ
+ꆗ
+ꆘ
+ꆙ
+ꆚ
+ꆛ
+ꆜ
+ê†
+ꆞ
+ꆟ
+ꆠ
+ꆡ
+ꆢ
+ꆣ
+ꆤ
+ꆥ
+ꆦ
+ꆧ
+ꆨ
+ꆩ
+ꆪ
+ꆫ
+ꆬ
+ꆭ
+ꆮ
+ꆯ
+ꆰ
+ꆱ
+ꆲ
+ꆳ
+ꆴ
+ꆵ
+ꆶ
+ꆷ
+ꆸ
+ꆹ
+ꆺ
+ꆻ
+ꆼ
+ꆽ
+ꆾ
+ꆿ
+ꇀ
+ê‡
+ꇂ
+ꇃ
+ꇄ
+ꇅ
+ꇆ
+ꇇ
+ꇈ
+ꇉ
+ꇊ
+ꇋ
+ꇌ
+ê‡
+ꇎ
+ê‡
+ê‡
+ꇑ
+ꇒ
+ꇓ
+ꇔ
+ꇕ
+ꇖ
+ꇗ
+ꇘ
+ꇙ
+ꇚ
+ꇛ
+ꇜ
+ê‡
+ꇞ
+ꇟ
+ꇠ
+ꇡ
+ꇢ
+ꇣ
+ꇤ
+ꇥ
+ꇦ
+ꇧ
+ꇨ
+ꇩ
+ꇪ
+ꇫ
+ꇬ
+ꇭ
+ꇮ
+ꇯ
+ꇰ
+ꇱ
+ꇲ
+ꇳ
+ꇴ
+ꇵ
+ꇶ
+ꇷ
+ꇸ
+ꇹ
+ꇺ
+ꇻ
+ꇼ
+ꇽ
+ꇾ
+ꇿ
+ꈀ
+êˆ
+ꈂ
+ꈃ
+ꈄ
+ꈅ
+ꈆ
+ꈇ
+ꈈ
+ꈉ
+ꈊ
+ꈋ
+ꈌ
+êˆ
+ꈎ
+êˆ
+êˆ
+ꈑ
+ꈒ
+ꈓ
+ꈔ
+ꈕ
+ꈖ
+ꈗ
+ꈘ
+ꈙ
+ꈚ
+ꈛ
+ꈜ
+êˆ
+ꈞ
+ꈟ
+ꈠ
+ꈡ
+ꈢ
+ꈣ
+ꈤ
+ꈥ
+ꈦ
+ꈧ
+ꈨ
+ꈩ
+ꈪ
+ꈫ
+ꈬ
+ꈭ
+ꈮ
+ꈯ
+ꈰ
+ꈱ
+ꈲ
+ꈳ
+ꈴ
+ꈵ
+ꈶ
+ꈷ
+ꈸ
+ꈹ
+ꈺ
+ꈻ
+ꈼ
+ꈽ
+ꈾ
+ꈿ
+ꉀ
+ê‰
+ꉂ
+ꉃ
+ꉄ
+ꉅ
+ꉆ
+ꉇ
+ꉈ
+ꉉ
+ꉊ
+ꉋ
+ꉌ
+ê‰
+ꉎ
+ê‰
+ê‰
+ꉑ
+ꉒ
+ꉓ
+ꉔ
+ꉕ
+ꉖ
+ꉗ
+ꉘ
+ꉙ
+ꉚ
+ꉛ
+ꉜ
+ê‰
+ꉞ
+ꉟ
+ꉠ
+ꉡ
+ꉢ
+ꉣ
+ꉤ
+ꉥ
+ꉦ
+ꉧ
+ꉨ
+ꉩ
+ꉪ
+ꉫ
+ꉬ
+ꉭ
+ꉮ
+ꉯ
+ꉰ
+ꉱ
+ꉲ
+ꉳ
+ꉴ
+ꉵ
+ꉶ
+ꉷ
+ꉸ
+ꉹ
+ꉺ
+ꉻ
+ꉼ
+ꉽ
+ꉾ
+ꉿ
+ꊀ
+êŠ
+ꊂ
+ꊃ
+ꊄ
+ꊅ
+ꊆ
+ꊇ
+ꊈ
+ꊉ
+ꊊ
+ꊋ
+ꊌ
+êŠ
+ꊎ
+êŠ
+êŠ
+ꊑ
+ꊒ
+ꊓ
+ꊔ
+ꊕ
+ꊖ
+ꊗ
+ꊘ
+ꊙ
+ꊚ
+ꊛ
+ꊜ
+êŠ
+ꊞ
+ꊟ
+ꊠ
+ꊡ
+ꊢ
+ꊣ
+ꊤ
+ꊥ
+ꊦ
+ꊧ
+ꊨ
+ꊩ
+ꊪ
+ꊫ
+ꊬ
+ꊭ
+ꊮ
+ꊯ
+ꊰ
+ꊱ
+ꊲ
+ꊳ
+ꊴ
+ꊵ
+ꊶ
+ꊷ
+ꊸ
+ꊹ
+ꊺ
+ꊻ
+ꊼ
+ꊽ
+ꊾ
+ꊿ
+ê‹€
+ê‹
+ê‹‚
+ꋃ
+ê‹„
+ê‹…
+ꋆ
+ꋇ
+ꋈ
+ꋉ
+ê‹Š
+ê‹‹
+ꋌ
+ê‹
+ê‹Ž
+ê‹
+ê‹
+ê‹‘
+ê‹’
+ê‹“
+ê‹”
+ê‹•
+ê‹–
+ê‹—
+ꋘ
+ê‹™
+ê‹š
+ê‹›
+ꋜ
+ê‹
+ê‹ž
+ê‹Ÿ
+ê‹ 
+ê‹¡
+ê‹¢
+ê‹£
+ꋤ
+ê‹¥
+ꋦ
+ꋧ
+ꋨ
+ê‹©
+ꋪ
+ê‹«
+ꋬ
+ê‹­
+ê‹®
+ꋯ
+ê‹°
+ꋱ
+ꋲ
+ꋳ
+ê‹´
+ꋵ
+ꋶ
+ê‹·
+ꋸ
+ꋹ
+ꋺ
+ê‹»
+ꋼ
+ꋽ
+ꋾ
+ê‹¿
+ꌀ
+êŒ
+ꌂ
+ꌃ
+ꌄ
+ꌅ
+ꌆ
+ꌇ
+ꌈ
+ꌉ
+ꌊ
+ꌋ
+ꌌ
+êŒ
+ꌎ
+êŒ
+êŒ
+ꌑ
+ꌒ
+ꌓ
+ꌔ
+ꌕ
+ꌖ
+ꌗ
+ꌘ
+ꌙ
+ꌚ
+ꌛ
+ꌜ
+êŒ
+ꌞ
+ꌟ
+ꌠ
+ꌡ
+ꌢ
+ꌣ
+ꌤ
+ꌥ
+ꌦ
+ꌧ
+ꌨ
+ꌩ
+ꌪ
+ꌫ
+ꌬ
+ꌭ
+ꌮ
+ꌯ
+ꌰ
+ꌱ
+ꌲ
+ꌳ
+ꌴ
+ꌵ
+ꌶ
+ꌷ
+ꌸ
+ꌹ
+ꌺ
+ꌻ
+ꌼ
+ꌽ
+ꌾ
+ꌿ
+ê€
+ê‚
+êƒ
+ê„
+ê…
+ê†
+ê‡
+êˆ
+ê‰
+êŠ
+ê‹
+êŒ
+êŽ
+ê‘
+ê’
+ê“
+ê”
+ê•
+ê–
+ê—
+ê˜
+ê™
+êš
+ê›
+êœ
+êž
+êŸ
+ê 
+ê¡
+ê¢
+ê£
+ê¤
+ê¥
+ê¦
+ê§
+ê¨
+ê©
+êª
+ê«
+ê¬
+ê­
+ê®
+ê¯
+ê°
+ê±
+ê²
+ê³
+ê´
+êµ
+ê¶
+ê·
+ê¸
+ê¹
+êº
+ê»
+ê¼
+ê½
+ê¾
+ê¿
+ꎀ
+êŽ
+ꎂ
+ꎃ
+ꎄ
+ꎅ
+ꎆ
+ꎇ
+ꎈ
+ꎉ
+ꎊ
+ꎋ
+ꎌ
+êŽ
+ꎎ
+êŽ
+êŽ
+ꎑ
+ꎒ
+ꎓ
+ꎔ
+ꎕ
+ꎖ
+ꎗ
+ꎘ
+ꎙ
+ꎚ
+ꎛ
+ꎜ
+êŽ
+ꎞ
+ꎟ
+ꎠ
+ꎡ
+ꎢ
+ꎣ
+ꎤ
+ꎥ
+ꎦ
+ꎧ
+ꎨ
+ꎩ
+ꎪ
+ꎫ
+ꎬ
+ꎭ
+ꎮ
+ꎯ
+ꎰ
+ꎱ
+ꎲ
+ꎳ
+ꎴ
+ꎵ
+ꎶ
+ꎷ
+ꎸ
+ꎹ
+ꎺ
+ꎻ
+ꎼ
+ꎽ
+ꎾ
+ꎿ
+ê€
+ê‚
+êƒ
+ê„
+ê…
+ê†
+ê‡
+êˆ
+ê‰
+êŠ
+ê‹
+êŒ
+êŽ
+ê‘
+ê’
+ê“
+ê”
+ê•
+ê–
+ê—
+ê˜
+ê™
+êš
+ê›
+êœ
+êž
+êŸ
+ê 
+ê¡
+ê¢
+ê£
+ê¤
+ê¥
+ê¦
+ê§
+ê¨
+ê©
+êª
+ê«
+ê¬
+ê­
+ê®
+ê¯
+ê°
+ê±
+ê²
+ê³
+ê´
+êµ
+ê¶
+ê·
+ê¸
+ê¹
+êº
+ê»
+ê¼
+ê½
+ê¾
+ê¿
+ê€
+ê‚
+êƒ
+ê„
+ê…
+ê†
+ê‡
+êˆ
+ê‰
+êŠ
+ê‹
+êŒ
+êŽ
+ê‘
+ê’
+ê“
+ê”
+ê•
+ê–
+ê—
+ê˜
+ê™
+êš
+ê›
+êœ
+êž
+êŸ
+ê 
+ê¡
+ê¢
+ê£
+ê¤
+ê¥
+ê¦
+ê§
+ê¨
+ê©
+êª
+ê«
+ê¬
+ê­
+ê®
+ê¯
+ê°
+ê±
+ê²
+ê³
+ê´
+êµ
+ê¶
+ê·
+ê¸
+ê¹
+êº
+ê»
+ê¼
+ê½
+ê¾
+ê¿
+ê‘€
+ê‘
+ê‘‚
+ꑃ
+ê‘„
+ê‘…
+ꑆ
+ꑇ
+ꑈ
+ꑉ
+ê‘Š
+ê‘‹
+ꑌ
+ê‘
+ê‘Ž
+ê‘
+ê‘
+ê‘‘
+ê‘’
+ê‘“
+ê‘”
+ê‘•
+ê‘–
+ê‘—
+ꑘ
+ê‘™
+ê‘š
+ê‘›
+ꑜ
+ê‘
+ê‘ž
+ê‘Ÿ
+ê‘ 
+ê‘¡
+ê‘¢
+ê‘£
+ꑤ
+ê‘¥
+ꑦ
+ꑧ
+ꑨ
+ê‘©
+ꑪ
+ê‘«
+ꑬ
+ê‘­
+ê‘®
+ꑯ
+ê‘°
+ꑱ
+ꑲ
+ꑳ
+ê‘´
+ꑵ
+ꑶ
+ê‘·
+ꑸ
+ꑹ
+ꑺ
+ê‘»
+ꑼ
+ꑽ
+ꑾ
+ê‘¿
+ê’€
+ê’
+ê’‚
+ê’ƒ
+ê’„
+ê’…
+ê’†
+ê’‡
+ê’ˆ
+ê’‰
+ê’Š
+ê’‹
+ꒌ
+ꔀ
+ê”
+ꔂ
+ꔃ
+ꔄ
+ê”…
+ꔆ
+ꔇ
+ꔈ
+ꔉ
+ꔊ
+ꔋ
+ꔌ
+ê”
+ꔎ
+ê”
+ê”
+ꔑ
+ê”’
+ꔓ
+ê””
+ꔕ
+ê”–
+ê”—
+ꔘ
+ê”™
+ꔚ
+ê”›
+ꔜ
+ê”
+ꔞ
+ꔟ
+ê” 
+ꔡ
+ꔢ
+ꔣ
+ꔤ
+ꔥ
+ꔦ
+ꔧ
+ꔨ
+ꔩ
+ꔪ
+ꔫ
+ꔬ
+ê”­
+ê”®
+ꔯ
+ê”°
+ê”±
+ꔲ
+ꔳ
+ê”´
+ꔵ
+ꔶ
+ê”·
+ꔸ
+ꔹ
+ꔺ
+ê”»
+ꔼ
+ꔽ
+ꔾ
+ꔿ
+ê•€
+ê•
+ê•‚
+ꕃ
+ê•„
+ê•…
+ꕆ
+ꕇ
+ꕈ
+ꕉ
+ê•Š
+ê•‹
+ꕌ
+ê•
+ê•Ž
+ê•
+ê•
+ê•‘
+ê•’
+ê•“
+ê•”
+ê••
+ê•–
+ê•—
+ꕘ
+ê•™
+ê•š
+ê•›
+ꕜ
+ê•
+ê•ž
+ê•Ÿ
+ê• 
+ê•¡
+ê•¢
+ê•£
+ꕤ
+ê•¥
+ꕦ
+ꕧ
+ꕨ
+ê•©
+ꕪ
+ê•«
+ꕬ
+ê•­
+ê•®
+ꕯ
+ê•°
+ꕱ
+ꕲ
+ꕳ
+ê•´
+ꕵ
+ꕶ
+ê•·
+ꕸ
+ꕹ
+ꕺ
+ê•»
+ꕼ
+ꕽ
+ꕾ
+ê•¿
+ê–€
+ê–
+ê–‚
+ê–ƒ
+ê–„
+ê–…
+ê–†
+ê–‡
+ê–ˆ
+ê–‰
+ê–Š
+ê–‹
+ꖌ
+ê–
+ê–Ž
+ê–
+ê–
+ê–‘
+ê–’
+ê–“
+ê–”
+ê–•
+ê––
+ê–—
+ê–˜
+ê–™
+ê–š
+ê–›
+ꖜ
+ê–
+ê–ž
+ê–Ÿ
+ê– 
+ê–¡
+ê–¢
+ê–£
+ê–¤
+ê–¥
+ê–¦
+ê–§
+ê–¨
+ê–©
+ê–ª
+ê–«
+ê–¬
+ê–­
+ê–®
+ê–¯
+ê–°
+ê–±
+ê–²
+ê–³
+ê–´
+ê–µ
+ê–¶
+ê–·
+ê–¸
+ê–¹
+ê–º
+ê–»
+ê–¼
+ê–½
+ê–¾
+ê–¿
+ê—€
+ê—
+ê—‚
+ê—ƒ
+ê—„
+ê—…
+ê—†
+ê—‡
+ê—ˆ
+ê—‰
+ê—Š
+ê—‹
+ꗌ
+ê—
+ê—Ž
+ê—
+ê—
+ê—‘
+ê—’
+ê—“
+ê—”
+ê—•
+ê—–
+ê——
+ê—˜
+ê—™
+ê—š
+ê—›
+ꗜ
+ê—
+ê—ž
+ê—Ÿ
+ê— 
+ê—¡
+ê—¢
+ê—£
+ê—¤
+ê—¥
+ê—¦
+ê—§
+ê—¨
+ê—©
+ê—ª
+ê—«
+ê—¬
+ê—­
+ê—®
+ê—¯
+ê—°
+ê—±
+ê—²
+ê—³
+ê—´
+ê—µ
+ê—¶
+ê—·
+ê—¸
+ê—¹
+ê—º
+ê—»
+ê—¼
+ê—½
+ê—¾
+ê—¿
+ꘀ
+ê˜
+ꘂ
+ꘃ
+ꘄ
+ꘅ
+ꘆ
+ꘇ
+ꘈ
+ꘉ
+ꘊ
+ꘋ
+ê˜
+ꘑ
+ꘒ
+ꘓ
+ꘔ
+ꘕ
+ꘖ
+ꘗ
+ꘘ
+ꘙ
+ꘚ
+ꘛ
+ꘜ
+ê˜
+ꘞ
+ꘟ
+ꘪ
+ꘫ
+ê™ê™€
+ꙃꙂ
+ꙅꙄ
+ꙇꙆ
+ꙉꙈ
+ꙋꙊ
+ê™ê™Œ
+ê™ê™Ž
+ꙑê™
+ꙓꙒ
+ꙕꙔ
+ê™—ê™–
+ꙙꙘ
+ꙛꙚ
+ê™ê™œ
+ꙟꙞ
+ꙣꙢ
+ꙥꙤ
+ꙧꙦ
+ꙩꙨ
+ꙫꙪ
+ꙭꙬ
+ê™®
+êšêš€
+ꚃꚂ
+êš…êš„
+ꚇꚆ
+ꚉꚈ
+ꚋꚊ
+êšêšŒ
+êšêšŽ
+êš‘êš
+êš“êš’
+êš•êš”
+êš—êš–
+ꜣꜢ
+ꜥꜤ
+ꜧꜦ
+ꜩꜨ
+ꜫꜪ
+ꜭꜬ
+ꜯꜮ
+ꜰ
+ꜱ
+ꜳꜲ
+ꜵꜴ
+ꜷꜶ
+ꜹꜸ
+ꜻꜺ
+ꜽꜼ
+ꜿꜾ
+êê€
+êƒê‚
+ê…ê„
+ê‡ê†
+ê‰êˆ
+ê‹êŠ
+êêŒ
+êêŽ
+ê‘ê
+ê“ê’
+ê•ê”
+ê—ê–
+ê™ê˜
+ê›êš
+êêœ
+êŸêž
+ê¡ê 
+ê£ê¢
+ê¥ê¤
+ê§ê¦
+ê©ê¨
+ê«êª
+ê­ê¬
+ê¯ê®
+ê±
+ê²
+ê³
+ê´
+êµ
+ê¶
+ê·
+ê¸
+êºê¹
+ê¼ê»
+ê¿ê¾
+êžêž€
+ꞃꞂ
+êž…êž„
+ꞇꞆ
+ꞌꞋ
+ꟻ
+ꟼ
+ꟽ
+ꟾ
+ꟿ
+ê €
+ê 
+ê ƒ
+ê „
+ê …
+ê ‡
+ê ˆ
+ê ‰
+ê Š
+ꠌ
+ê 
+ê Ž
+ê 
+ê 
+ê ‘
+ê ’
+ê “
+ê ”
+ê •
+ê –
+ê —
+ê ˜
+ê ™
+ê š
+ê ›
+ꠜ
+ê 
+ê ž
+ê Ÿ
+ê  
+ê ¡
+ê ¢
+ê¡€
+ê¡
+ê¡‚
+ꡃ
+ê¡„
+ê¡…
+ꡆ
+ꡇ
+ꡈ
+ꡉ
+ê¡Š
+ê¡‹
+ꡌ
+ê¡
+ê¡Ž
+ê¡
+ê¡
+ê¡‘
+ê¡’
+ê¡“
+ê¡”
+ê¡•
+ê¡–
+ê¡—
+ꡘ
+ê¡™
+ê¡š
+ê¡›
+ꡜ
+ê¡
+ê¡ž
+ê¡Ÿ
+ê¡ 
+ê¡¡
+ê¡¢
+ê¡£
+ꡤ
+ê¡¥
+ꡦ
+ꡧ
+ꡨ
+ê¡©
+ꡪ
+ê¡«
+ꡬ
+ê¡­
+ê¡®
+ꡯ
+ê¡°
+ꡱ
+ꡲ
+ꡳ
+ꢂ
+ꢃ
+ꢄ
+ꢅ
+ꢆ
+ꢇ
+ꢈ
+ꢉ
+ꢊ
+ꢋ
+ꢌ
+ê¢
+ꢎ
+ê¢
+ê¢
+ꢑ
+ꢒ
+ꢓ
+ꢔ
+ꢕ
+ꢖ
+ꢗ
+ꢘ
+ꢙ
+ꢚ
+ꢛ
+ꢜ
+ê¢
+ꢞ
+ꢟ
+ꢠ
+ꢡ
+ꢢ
+ꢣ
+ꢤ
+ꢥ
+ꢦ
+ꢧ
+ꢨ
+ꢩ
+ꢪ
+ꢫ
+ꢬ
+ꢭ
+ꢮ
+ꢯ
+ꢰ
+ꢱ
+ꢲ
+ꢳ
+ꤊ
+ꤋ
+ꤌ
+ê¤
+ꤎ
+ê¤
+ê¤
+ꤑ
+ꤒ
+ꤓ
+ꤔ
+ꤕ
+ꤖ
+ꤗ
+ꤘ
+ꤙ
+ꤚ
+ꤛ
+ꤜ
+ê¤
+ꤞ
+ꤟ
+ꤠ
+ꤡ
+ꤢ
+ꤣ
+ꤤ
+ꤥ
+ꤰ
+ꤱ
+ꤲ
+ꤳ
+ꤴ
+ꤵ
+ꤶ
+ꤷ
+ꤸ
+ꤹ
+ꤺ
+ꤻ
+ꤼ
+ꤽ
+ꤾ
+ꤿ
+ꥀ
+ê¥
+ꥂ
+ꥃ
+ꥄ
+ꥅ
+ꥆ
+ꨀ
+ê¨
+ꨂ
+ꨃ
+ꨄ
+ꨅ
+ꨆ
+ꨇ
+ꨈ
+ꨉ
+ꨊ
+ꨋ
+ꨌ
+ê¨
+ꨎ
+ê¨
+ê¨
+ꨑ
+ꨒ
+ꨓ
+ꨔ
+ꨕ
+ꨖ
+ꨗ
+ꨘ
+ꨙ
+ꨚ
+ꨛ
+ꨜ
+ê¨
+ꨞ
+ꨟ
+ꨠ
+ꨡ
+ꨢ
+ꨣ
+ꨤ
+ꨥ
+ꨦ
+ꨧ
+ꨨ
+ê©€
+ê©
+ê©‚
+ê©„
+ê©…
+ꩆ
+ꩇ
+ꩈ
+ꩉ
+ê©Š
+ê©‹
+豈
+ï¤
+車
+賈
+滑
+串
+句
+龜
+龜
+契
+金
+喇
+奈
+ï¤
+癩
+ï¤
+ï¤
+螺
+裸
+邏
+樂
+洛
+烙
+珞
+落
+酪
+駱
+亂
+卵
+ï¤
+爛
+蘭
+鸞
+嵐
+濫
+藍
+襤
+拉
+臘
+蠟
+廊
+朗
+浪
+狼
+郎
+來
+冷
+勞
+擄
+櫓
+爐
+盧
+老
+蘆
+虜
+路
+露
+魯
+鷺
+碌
+祿
+綠
+菉
+錄
+鹿
+ï¥
+壟
+弄
+籠
+聾
+牢
+磊
+賂
+雷
+壘
+屢
+樓
+ï¥
+漏
+ï¥
+ï¥
+陋
+勒
+肋
+凜
+凌
+稜
+綾
+菱
+陵
+讀
+拏
+樂
+ï¥
+丹
+寧
+怒
+率
+異
+北
+磻
+便
+復
+不
+泌
+數
+索
+參
+塞
+省
+葉
+說
+殺
+辰
+沈
+拾
+若
+掠
+略
+亮
+兩
+凉
+梁
+糧
+良
+諒
+量
+勵
+呂
+ï¦
+廬
+旅
+濾
+礪
+閭
+驪
+麗
+黎
+力
+曆
+歷
+ï¦
+年
+ï¦
+ï¦
+撚
+漣
+煉
+璉
+秊
+練
+聯
+輦
+蓮
+連
+鍊
+列
+ï¦
+咽
+烈
+裂
+說
+廉
+念
+捻
+殮
+簾
+獵
+令
+囹
+寧
+嶺
+怜
+玲
+瑩
+羚
+聆
+鈴
+零
+靈
+領
+例
+禮
+醴
+隸
+惡
+了
+僚
+寮
+尿
+料
+樂
+燎
+ï§
+蓼
+遼
+龍
+暈
+阮
+劉
+杻
+柳
+流
+溜
+琉
+ï§
+硫
+ï§
+ï§
+六
+戮
+陸
+倫
+崙
+淪
+輪
+律
+慄
+栗
+率
+隆
+ï§
+吏
+履
+易
+李
+梨
+泥
+理
+痢
+罹
+裏
+裡
+里
+離
+匿
+溺
+吝
+燐
+璘
+藺
+隣
+鱗
+麟
+林
+淋
+臨
+立
+笠
+粒
+狀
+炙
+識
+什
+茶
+刺
+切
+ï¨
+拓
+糖
+宅
+洞
+暴
+輻
+行
+降
+見
+廓
+兀
+ï¨
+﨎
+ï¨
+ï¨
+﨑
+晴
+﨓
+﨔
+凞
+猪
+益
+礼
+神
+祥
+福
+靖
+ï¨
+羽
+﨟
+蘒
+﨡
+諸
+﨣
+﨤
+逸
+都
+﨧
+﨨
+﨩
+飯
+飼
+館
+鶴
+侮
+僧
+免
+勉
+勤
+卑
+喝
+嘆
+器
+塀
+墨
+層
+屮
+悔
+慨
+憎
+ï©€
+ï©
+ï©‚
+暑
+ï©„
+ï©…
+渚
+漢
+煮
+爫
+ï©Š
+ï©‹
+社
+ï©
+ï©Ž
+ï©
+ï©
+ï©‘
+ï©’
+ï©“
+ï©”
+ï©•
+ï©–
+ï©—
+縉
+ï©™
+ï©š
+ï©›
+臭
+ï©
+ï©ž
+ï©Ÿ
+ï© 
+ï©¡
+ï©¢
+ï©£
+賓
+ï©¥
+辶
+逸
+難
+ï©©
+頻
+ï©°
+况
+全
+侀
+ï©´
+冀
+勇
+ï©·
+喝
+啕
+喙
+ï©»
+塚
+墳
+奄
+ï©¿
+婢
+ïª
+廒
+廙
+彩
+徭
+惘
+慎
+愈
+憎
+慠
+懲
+戴
+ïª
+搜
+ïª
+ïª
+晴
+朗
+望
+杖
+歹
+殺
+流
+滛
+滋
+漢
+瀞
+煮
+ïª
+爵
+犯
+猪
+瑱
+甆
+画
+瘝
+瘟
+益
+盛
+直
+睊
+着
+磌
+窱
+節
+类
+絛
+練
+缾
+者
+荒
+華
+蝹
+襁
+覆
+視
+調
+諸
+請
+謁
+諾
+諭
+謹
+ï«€
+ï«
+ï«‚
+遲
+ï«„
+ï«…
+陼
+難
+靖
+韛
+ï«Š
+ï«‹
+頻
+ï«
+ï«Ž
+ï«
+ï«
+ï«‘
+ï«’
+ï«“
+ï«”
+ï«•
+ï«–
+ï«—
+齃
+ï«™
+ï­
+ï­‘
+ï­’
+ï­“
+ï­”
+ï­•
+ï­–
+ï­—
+ï­˜
+ï­™
+ï­š
+ï­›
+ﭜ
+ï­
+ï­ž
+ï­Ÿ
+ï­ 
+ï­¡
+ï­¢
+ï­£
+ï­¤
+ï­¥
+ï­¦
+ï­§
+ï­¨
+ï­©
+ï­ª
+ï­«
+ï­¬
+ï­­
+ï­®
+ï­¯
+ï­°
+ï­±
+ï­²
+ï­³
+ï­´
+ï­µ
+ï­¶
+ï­·
+ï­¸
+ï­¹
+ï­º
+ï­»
+ï­¼
+ï­½
+ï­¾
+ï­¿
+ﮀ
+ï®
+ﮂ
+ﮃ
+ﮄ
+ï®…
+ﮆ
+ﮇ
+ﮈ
+ﮉ
+ﮊ
+ﮋ
+ﮌ
+ï®
+ﮎ
+ï®
+ï®
+ﮑ
+ï®’
+ﮓ
+ï®”
+ﮕ
+ï®–
+ï®—
+ﮘ
+ï®™
+ﮚ
+ï®›
+ﮜ
+ï®
+ﮞ
+ﮟ
+ï® 
+ﮡ
+ﮢ
+ﮣ
+ﮤ
+ﮥ
+ﮦ
+ﮧ
+ﮨ
+ﮩ
+ﮪ
+ﮫ
+ﮬ
+ï®­
+ï®®
+ﮯ
+ï®°
+ï®±
+ﯓ
+ﯔ
+ﯕ
+ﯖ
+ﯗ
+ﯘ
+ﯙ
+ﯚ
+ﯛ
+ﯜ
+ï¯
+ﯞ
+ﯟ
+ﯠ
+ﯡ
+ﯢ
+ﯣ
+ﯤ
+ﯥ
+ﯦ
+ﯧ
+ﯨ
+ﯩ
+ﯪ
+ﯫ
+ﯬ
+ﯭ
+ﯮ
+ﯯ
+ﯰ
+ﯱ
+ﯲ
+ﯳ
+ﯴ
+ﯵ
+ﯶ
+ﯷ
+ﯸ
+ﯹ
+ﯺ
+ﯻ
+ﯼ
+ﯽ
+ﯾ
+ﯿ
+ï°€
+ï°
+ï°‚
+ï°ƒ
+ï°„
+ï°…
+ï°†
+ï°‡
+ï°ˆ
+ï°‰
+ï°Š
+ï°‹
+ﰌ
+ï°
+ï°Ž
+ï°
+ï°
+ï°‘
+ï°’
+ï°“
+ï°”
+ï°•
+ï°–
+ï°—
+ï°˜
+ï°™
+ï°š
+ï°›
+ﰜ
+ï°
+ï°ž
+ï°Ÿ
+ï° 
+ï°¡
+ï°¢
+ï°£
+ï°¤
+ï°¥
+ï°¦
+ï°§
+ï°¨
+ï°©
+ï°ª
+ï°«
+ï°¬
+ï°­
+ï°®
+ï°¯
+ï°°
+ï°±
+ï°²
+ï°³
+ï°´
+ï°µ
+ï°¶
+ï°·
+ï°¸
+ï°¹
+ï°º
+ï°»
+ï°¼
+ï°½
+ï°¾
+ï°¿
+ï±€
+ï±
+ﱂ
+ﱃ
+ﱄ
+ï±…
+ﱆ
+ﱇ
+ﱈ
+ﱉ
+ﱊ
+ﱋ
+ﱌ
+ï±
+ﱎ
+ï±
+ï±
+ﱑ
+ï±’
+ﱓ
+ï±”
+ﱕ
+ï±–
+ï±—
+ﱘ
+ï±™
+ﱚ
+ï±›
+ﱜ
+ï±
+ﱞ
+ﱟ
+ï± 
+ﱡ
+ï±¢
+ï±£
+ﱤ
+ï±¥
+ﱦ
+ﱧ
+ﱨ
+ﱩ
+ﱪ
+ﱫ
+ﱬ
+ï±­
+ï±®
+ﱯ
+ï±°
+ï±±
+ï±²
+ï±³
+ï±´
+ï±µ
+ﱶ
+ï±·
+ﱸ
+ï±¹
+ﱺ
+ï±»
+ï±¼
+ï±½
+ï±¾
+ﱿ
+ï²€
+ï²
+ﲂ
+ﲃ
+ﲄ
+ï²…
+ﲆ
+ﲇ
+ﲈ
+ﲉ
+ﲊ
+ﲋ
+ﲌ
+ï²
+ﲎ
+ï²
+ï²
+ﲑ
+ï²’
+ﲓ
+ï²”
+ﲕ
+ï²–
+ï²—
+ﲘ
+ï²™
+ﲚ
+ï²›
+ﲜ
+ï²
+ﲞ
+ﲟ
+ï² 
+ﲡ
+ï²¢
+ï²£
+ﲤ
+ï²¥
+ﲦ
+ﲧ
+ﲨ
+ﲩ
+ﲪ
+ﲫ
+ﲬ
+ï²­
+ï²®
+ﲯ
+ï²°
+ï²±
+ï²²
+ï²³
+ï²´
+ï²µ
+ﲶ
+ï²·
+ﲸ
+ï²¹
+ﲺ
+ï²»
+ï²¼
+ï²½
+ï²¾
+ﲿ
+ï³€
+ï³
+ﳂ
+ﳃ
+ﳄ
+ï³…
+ﳆ
+ﳇ
+ﳈ
+ﳉ
+ﳊ
+ﳋ
+ﳌ
+ï³
+ﳎ
+ï³
+ï³
+ﳑ
+ï³’
+ﳓ
+ï³”
+ﳕ
+ï³–
+ï³—
+ﳘ
+ï³™
+ﳚ
+ï³›
+ﳜ
+ï³
+ﳞ
+ﳟ
+ï³ 
+ﳡ
+ï³¢
+ï³£
+ﳤ
+ï³¥
+ﳦ
+ﳧ
+ﳨ
+ﳩ
+ﳪ
+ﳫ
+ﳬ
+ï³­
+ï³®
+ﳯ
+ï³°
+ï³±
+ï³²
+ï³³
+ï³´
+ï³µ
+ﳶ
+ï³·
+ﳸ
+ï³¹
+ﳺ
+ï³»
+ï³¼
+ï³½
+ï³¾
+ﳿ
+ï´€
+ï´
+ï´‚
+ï´ƒ
+ï´„
+ï´…
+ï´†
+ï´‡
+ï´ˆ
+ï´‰
+ï´Š
+ï´‹
+ﴌ
+ï´
+ï´Ž
+ï´
+ï´
+ï´‘
+ï´’
+ï´“
+ï´”
+ï´•
+ï´–
+ï´—
+ï´˜
+ï´™
+ï´š
+ï´›
+ﴜ
+ï´
+ï´ž
+ï´Ÿ
+ï´ 
+ï´¡
+ï´¢
+ï´£
+ï´¤
+ï´¥
+ï´¦
+ï´§
+ï´¨
+ï´©
+ï´ª
+ï´«
+ï´¬
+ï´­
+ï´®
+ï´¯
+ï´°
+ï´±
+ï´²
+ï´³
+ï´´
+ï´µ
+ï´¶
+ï´·
+ï´¸
+ï´¹
+ï´º
+ï´»
+ï´¼
+ï´½
+ïµ
+ﵑ
+ïµ’
+ﵓ
+ïµ”
+ﵕ
+ïµ–
+ïµ—
+ﵘ
+ïµ™
+ﵚ
+ïµ›
+ﵜ
+ïµ
+ﵞ
+ﵟ
+ïµ 
+ﵡ
+ïµ¢
+ïµ£
+ﵤ
+ïµ¥
+ﵦ
+ﵧ
+ﵨ
+ﵩ
+ﵪ
+ﵫ
+ﵬ
+ïµ­
+ïµ®
+ﵯ
+ïµ°
+ïµ±
+ïµ²
+ïµ³
+ïµ´
+ïµµ
+ﵶ
+ïµ·
+ﵸ
+ïµ¹
+ﵺ
+ïµ»
+ïµ¼
+ïµ½
+ïµ¾
+ﵿ
+ﶀ
+ï¶
+ﶂ
+ﶃ
+ﶄ
+ﶅ
+ﶆ
+ﶇ
+ﶈ
+ﶉ
+ﶊ
+ﶋ
+ﶌ
+ï¶
+ﶎ
+ï¶
+ﶒ
+ﶓ
+ﶔ
+ﶕ
+ﶖ
+ﶗ
+ﶘ
+ﶙ
+ﶚ
+ﶛ
+ﶜ
+ï¶
+ﶞ
+ﶟ
+ﶠ
+ﶡ
+ﶢ
+ﶣ
+ﶤ
+ﶥ
+ﶦ
+ﶧ
+ﶨ
+ﶩ
+ﶪ
+ﶫ
+ﶬ
+ﶭ
+ﶮ
+ﶯ
+ﶰ
+ﶱ
+ﶲ
+ﶳ
+ﶴ
+ﶵ
+ﶶ
+ﶷ
+ﶸ
+ﶹ
+ﶺ
+ﶻ
+ﶼ
+ﶽ
+ﶾ
+ﶿ
+ï·€
+ï·
+ï·‚
+ï·ƒ
+ï·„
+ï·…
+ï·†
+ï·‡
+ï·°
+ï·±
+ï·²
+ï·³
+ï·´
+ï·µ
+ï·¶
+ï··
+ï·¸
+ï·¹
+ï·º
+ï·»
+ï¹°
+ï¹±
+ï¹²
+ï¹³
+ï¹´
+ﹶ
+ï¹·
+ﹸ
+ï¹¹
+ﹺ
+ï¹»
+ï¹¼
+ï¹½
+ï¹¾
+ﹿ
+ﺀ
+ïº
+ﺂ
+ﺃ
+ﺄ
+ﺅ
+ﺆ
+ﺇ
+ﺈ
+ﺉ
+ﺊ
+ﺋ
+ﺌ
+ïº
+ﺎ
+ïº
+ïº
+ﺑ
+ﺒ
+ﺓ
+ﺔ
+ﺕ
+ﺖ
+ﺗ
+ﺘ
+ﺙ
+ﺚ
+ﺛ
+ﺜ
+ïº
+ﺞ
+ﺟ
+ﺠ
+ﺡ
+ﺢ
+ﺣ
+ﺤ
+ﺥ
+ﺦ
+ﺧ
+ﺨ
+ﺩ
+ﺪ
+ﺫ
+ﺬ
+ﺭ
+ﺮ
+ﺯ
+ﺰ
+ﺱ
+ﺲ
+ﺳ
+ﺴ
+ﺵ
+ﺶ
+ﺷ
+ﺸ
+ﺹ
+ﺺ
+ﺻ
+ﺼ
+ﺽ
+ﺾ
+ﺿ
+ﻀ
+ï»
+ﻂ
+ﻃ
+ﻄ
+ï»…
+ﻆ
+ﻇ
+ﻈ
+ﻉ
+ﻊ
+ﻋ
+ﻌ
+ï»
+ﻎ
+ï»
+ï»
+ﻑ
+ï»’
+ﻓ
+ï»”
+ﻕ
+ï»–
+ï»—
+ﻘ
+ï»™
+ﻚ
+ï»›
+ﻜ
+ï»
+ﻞ
+ﻟ
+ï» 
+ﻡ
+ﻢ
+ﻣ
+ﻤ
+ﻥ
+ﻦ
+ﻧ
+ﻨ
+ﻩ
+ﻪ
+ﻫ
+ﻬ
+ï»­
+ï»®
+ﻯ
+ï»°
+ï»±
+ﻲ
+ﻳ
+ï»´
+ﻵ
+ﻶ
+ï»·
+ﻸ
+ﻹ
+ﻺ
+ï»»
+ﻼ
+</classes>
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
index f7acf3eb5..8c213d7d5 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
@@ -19,8 +19,12 @@
package org.apache.fop.layoutmgr;
+import java.util.List;
+import java.util.Stack;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.FObj;
@@ -253,4 +257,20 @@ public abstract class AbstractBaseLayoutManager
return fobj;
}
+ /** {@inheritDoc} */
+ public void reset() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+ Position positionAtIPDChange, LayoutManager restartAtLM) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
index 624bc907a..1a6f7cfb9 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
@@ -19,6 +19,8 @@
package org.apache.fop.layoutmgr;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -27,6 +29,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.Constants;
+import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.util.ListUtil;
@@ -46,10 +49,10 @@ public abstract class AbstractBreaker {
int footnoteLastListIndex;
int footnoteLastElementIndex;
- PageBreakPosition(LayoutManager lm, int iBreakIndex,
+ PageBreakPosition(LayoutManager lm, int breakIndex,
int ffli, int ffei, int flli, int flei,
double bpdA, int diff) {
- super(lm, iBreakIndex);
+ super(lm, breakIndex);
bpdAdjust = bpdA;
difference = diff;
footnoteFirstListIndex = ffli;
@@ -59,6 +62,30 @@ public abstract class AbstractBreaker {
}
}
+ /**
+ * Helper method, mainly used to improve debug/trace output
+ * @param breakClassId the {@link Constants} enum value.
+ * @return the break class name
+ */
+ static String getBreakClassName(int breakClassId) {
+ switch (breakClassId) {
+ case Constants.EN_ALL: return "ALL";
+ case Constants.EN_ANY: return "ANY";
+ case Constants.EN_AUTO: return "AUTO";
+ case Constants.EN_COLUMN: return "COLUMN";
+ case Constants.EN_EVEN_PAGE: return "EVEN PAGE";
+ case Constants.EN_LINE: return "LINE";
+ case Constants.EN_NONE: return "NONE";
+ case Constants.EN_ODD_PAGE: return "ODD PAGE";
+ case Constants.EN_PAGE: return "PAGE";
+ default: return "??? (" + String.valueOf(breakClassId) + ")";
+ }
+ }
+
+ /**
+ * Helper class, extending the functionality of the
+ * basic {@link BlockKnuthSequence}.
+ */
public class BlockSequence extends BlockKnuthSequence {
/** Number of elements to ignore at the beginning of the list. */
@@ -79,19 +106,21 @@ public abstract class AbstractBreaker {
/**
* Creates a new BlockSequence.
- * @param iStartOn the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
- * EN_ODD_PAGE, EN_EVEN_PAGE.
+ * @param startOn the kind of page the sequence should start on.
+ * One of {@link Constants#EN_ANY}, {@link Constants#EN_COLUMN},
+ * {@link Constants#EN_ODD_PAGE}, or {@link Constants#EN_EVEN_PAGE}.
* @param displayAlign the value for the display-align property
*/
- public BlockSequence(int iStartOn, int displayAlign) {
+ public BlockSequence(int startOn, int displayAlign) {
super();
- startOn = iStartOn;
+ this.startOn = startOn;
this.displayAlign = displayAlign;
}
/**
- * @return the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
- * EN_ODD_PAGE, EN_EVEN_PAGE.
+ * @return the kind of page the sequence should start on.
+ * One of {@link Constants#EN_ANY}, {@link Constants#EN_COLUMN},
+ * {@link Constants#EN_ODD_PAGE}, or {@link Constants#EN_EVEN_PAGE}.
*/
public int getStartOn() {
return this.startOn;
@@ -101,6 +130,7 @@ public abstract class AbstractBreaker {
public int getDisplayAlign() {
return this.displayAlign;
}
+
/**
* Finalizes a Knuth sequence.
* @return a finalized sequence.
@@ -142,6 +172,12 @@ public abstract class AbstractBreaker {
}
}
+ /**
+ * Finalizes a this {@link BlockSequence}, adding a terminating
+ * penalty-glue-penalty sequence
+ * @param breakPosition a Position instance pointing to the last penalty
+ * @return the finalized {@link BlockSequence}
+ */
public BlockSequence endBlockSequence(Position breakPosition) {
KnuthSequence temp = endSequence(breakPosition);
if (temp != null) {
@@ -214,6 +250,11 @@ public abstract class AbstractBreaker {
*/
protected abstract List getNextKnuthElements(LayoutContext context, int alignment);
+ protected List getNextKnuthElements(LayoutContext context, int alignment,
+ Position positionAtIPDChange, LayoutManager restartAtLM) {
+ throw new UnsupportedOperationException("TODO: implement acceptable fallback");
+ }
+
/** @return true if there's no content that could be handled. */
public boolean isEmpty() {
return (this.blockLists.isEmpty());
@@ -260,14 +301,6 @@ public abstract class AbstractBreaker {
/**
* Starts the page breaking process.
* @param flowBPD the constant available block-progression-dimension (used for every part)
- */
- public void doLayout(int flowBPD) {
- doLayout(flowBPD, false);
- }
-
- /**
- * Starts the page breaking process.
- * @param flowBPD the constant available block-progression-dimension (used for every part)
* @param autoHeight true if warnings about overflows should be disabled because the
* the BPD is really undefined (for footnote-separators, for example)
*/
@@ -310,10 +343,7 @@ public abstract class AbstractBreaker {
//debug code start
if (log.isDebugEnabled()) {
log.debug(" blockListIndex = " + blockListIndex);
- String pagina = (blockList.startOn == Constants.EN_ANY) ? "any page"
- : (blockList.startOn == Constants.EN_ODD_PAGE) ? "odd page"
- : "even page";
- log.debug(" sequence starts on " + pagina);
+ log.debug(" sequence starts on " + getBreakClassName(blockList.startOn));
}
observeElementList(blockList);
//debug code end
@@ -324,7 +354,6 @@ public abstract class AbstractBreaker {
getPageProvider(), createLayoutListener(),
alignment, alignmentLast, footnoteSeparatorLength,
isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
- int iOptPageCount;
BlockSequence effectiveList;
if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
@@ -335,22 +364,108 @@ public abstract class AbstractBreaker {
effectiveList = blockList;
}
- //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true);
alg.setConstantLineWidth(flowBPD);
- iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/
- 1, true, BreakingAlgorithm.ALL_BREAKS);
- log.debug("PLM> iOptPageCount= " + iOptPageCount
- + " pageBreaks.size()= " + alg.getPageBreaks().size());
+ int optimalPageCount = alg.findBreakingPoints(effectiveList, 1, true,
+ BreakingAlgorithm.ALL_BREAKS);
+ if (alg.ipdChanged()) {
+ KnuthNode optimalBreak = alg.getBestNodeBeforeIPDChange();
+ int positionIndex = optimalBreak.position;
+ KnuthElement elementAtBreak = alg.getElement(positionIndex);
+ Position positionAtBreak = elementAtBreak.getPosition();
+ if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
+ throw new UnsupportedOperationException(
+ "Don't know how to restart at position" + positionAtBreak);
+ }
+ /* Retrieve the original position wrapped into this space position */
+ positionAtBreak = positionAtBreak.getPosition();
+ LayoutManager restartAtLM = null;
+ List firstElements = Collections.EMPTY_LIST;
+ if (containsNonRestartableLM(positionAtBreak)) {
+ firstElements = new LinkedList();
+ boolean boxFound = false;
+ Iterator iter = effectiveList.listIterator(++positionIndex);
+ Position position = null;
+ while (iter.hasNext()
+ && (position == null || containsNonRestartableLM(position))) {
+ KnuthElement element = (KnuthElement) iter.next();
+ positionIndex++;
+ position = element.getPosition();
+ if (element.isBox()) {
+ boxFound = true;
+ firstElements.add(element);
+ } else if (boxFound) {
+ firstElements.add(element);
+ }
+ }
+ if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) {
+ /* Retrieve the original position wrapped into this space position */
+ positionAtBreak = position.getPosition();
+ }
+ }
+ if (positionAtBreak.getIndex() == -1) {
+ /*
+ * This is an indication that we are between two blocks
+ * (possibly surrounded by another block), not inside a
+ * paragraph.
+ */
+ Position position;
+ Iterator iter = effectiveList.listIterator(positionIndex + 1);
+ do {
+ KnuthElement nextElement = (KnuthElement) iter.next();
+ position = nextElement.getPosition();
+ } while (position == null
+ || position instanceof SpaceResolver.SpaceHandlingPosition
+ || position instanceof SpaceResolver.SpaceHandlingBreakPosition
+ && position.getPosition().getIndex() == -1);
+ LayoutManager surroundingLM = positionAtBreak.getLM();
+ while (position.getLM() != surroundingLM) {
+ position = position.getPosition();
+ }
+ restartAtLM = position.getPosition().getLM();
+ }
+ log.trace("IPD changes after page " + optimalPageCount + " at index "
+ + optimalBreak.position);
+ doPhase3(alg, optimalPageCount, blockList, effectiveList);
+
+ blockLists.clear();
+ blockListIndex = -1;
+ nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN,
+ positionAtBreak, restartAtLM, firstElements);
+ } else {
+ log.debug("PLM> iOptPageCount= " + optimalPageCount
+ + " pageBreaks.size()= " + alg.getPageBreaks().size());
- //*** Phase 3: Add areas ***
- doPhase3(alg, iOptPageCount, blockList, effectiveList);
+ //*** Phase 3: Add areas ***
+ doPhase3(alg, optimalPageCount, blockList, effectiveList);
+ }
}
}
}
/**
+ * Returns {@code true} if the given position or one of its descendants
+ * corresponds to a non-restartable LM.
+ *
+ * @param position a position
+ * @return {@code true} if there is a non-restartable LM in the hierarchy
+ */
+ private boolean containsNonRestartableLM(Position position) {
+ LayoutManager lm = position.getLM();
+ if (lm != null && !lm.isRestartable()) {
+ return true;
+ } else {
+ Position subPosition = position.getPosition();
+ if (subPosition == null) {
+ return false;
+ } else {
+ return containsNonRestartableLM(subPosition);
+ }
+ }
+ }
+
+ /**
* Phase 3 of Knuth algorithm: Adds the areas
* @param alg PageBreakingAlgorithm instance which determined the breaks
* @param partCount number of parts (pages) to be rendered
@@ -417,7 +532,7 @@ public abstract class AbstractBreaker {
log.debug("PLM> part: " + (p + 1)
+ ", start at pos " + startElementIndex
+ ", break at pos " + endElementIndex
- + ", break class = " + lastBreakClass);
+ + ", break class = " + getBreakClassName(lastBreakClass));
startPart(effectiveList, lastBreakClass);
@@ -444,17 +559,9 @@ public abstract class AbstractBreaker {
// at the beginning of the line
effectiveListIterator = effectiveList
.listIterator(startElementIndex);
- KnuthElement firstElement;
while (effectiveListIterator.hasNext()
- && !(firstElement = (KnuthElement) effectiveListIterator.next())
+ && !((KnuthElement) effectiveListIterator.next())
.isBox()) {
- /*
- if (firstElement.isGlue() && firstElement.getLayoutManager() != null) {
- // discard the space representd by the glue element
- ((BlockLevelLayoutManager) firstElement
- .getLayoutManager())
- .discardSpace((KnuthGlue) firstElement);
- }*/
startElementIndex++;
}
@@ -537,6 +644,7 @@ public abstract class AbstractBreaker {
protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
return nextSequenceStartsOn;
}
+
/**
* Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
* @param childLC LayoutContext to use
@@ -545,12 +653,38 @@ public abstract class AbstractBreaker {
*/
protected int getNextBlockList(LayoutContext childLC,
int nextSequenceStartsOn) {
+ return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
+ }
+
+ /**
+ * Gets the next block list (sequence) and adds it to a list of block lists
+ * if it's not empty.
+ *
+ * @param childLC LayoutContext to use
+ * @param nextSequenceStartsOn indicates on what page the next sequence
+ * should start
+ * @param positionAtIPDChange last element on the part before an IPD change
+ * @param restartAtLM the layout manager from which to restart, if IPD
+ * change occurs between two LMs
+ * @param firstElements elements from non-restartable LMs on the new page
+ * @return the page on which the next content should appear after a hard
+ * break
+ */
+ protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
+ Position positionAtIPDChange, LayoutManager restartAtLM, List firstElements) {
updateLayoutContext(childLC);
//Make sure the span change signal is reset
childLC.signalSpanChange(Constants.NOT_SET);
BlockSequence blockList;
- List returnedList = getNextKnuthElements(childLC, alignment);
+ List returnedList;
+ if (positionAtIPDChange == null) {
+ returnedList = getNextKnuthElements(childLC, alignment);
+ } else {
+ returnedList = getNextKnuthElements(childLC, alignment, positionAtIPDChange,
+ restartAtLM);
+ returnedList.addAll(0, firstElements);
+ }
if (returnedList != null) {
if (returnedList.isEmpty()) {
nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
@@ -562,26 +696,23 @@ public abstract class AbstractBreaker {
nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
Position breakPosition = null;
- if (((KnuthElement) ListUtil.getLast(returnedList)).isForcedBreak()) {
+ if (ElementListUtils.endsWithForcedBreak(returnedList)) {
KnuthPenalty breakPenalty = (KnuthPenalty) ListUtil
.removeLast(returnedList);
breakPosition = breakPenalty.getPosition();
+ log.debug("PLM> break - " + getBreakClassName(breakPenalty.getBreakClass()));
switch (breakPenalty.getBreakClass()) {
case Constants.EN_PAGE:
- log.debug("PLM> break - PAGE");
nextSequenceStartsOn = Constants.EN_ANY;
break;
case Constants.EN_COLUMN:
- log.debug("PLM> break - COLUMN");
//TODO Fix this when implementing multi-column layout
nextSequenceStartsOn = Constants.EN_COLUMN;
break;
case Constants.EN_ODD_PAGE:
- log.debug("PLM> break - ODD PAGE");
nextSequenceStartsOn = Constants.EN_ODD_PAGE;
break;
case Constants.EN_EVEN_PAGE:
- log.debug("PLM> break - EVEN PAGE");
nextSequenceStartsOn = Constants.EN_EVEN_PAGE;
break;
default:
@@ -590,7 +721,7 @@ public abstract class AbstractBreaker {
}
}
blockList.addAll(returnedList);
- BlockSequence seq = null;
+ BlockSequence seq;
seq = blockList.endBlockSequence(breakPosition);
if (seq != null) {
this.blockLists.add(seq);
@@ -602,8 +733,8 @@ public abstract class AbstractBreaker {
/**
* Returns the average width of all the lines in the given range.
* @param effectiveList effective block list to work on
- * @param startElementIndex
- * @param endElementIndex
+ * @param startElementIndex index of the element starting the range
+ * @param endElementIndex index of the element ending the range
* @return the average line length, 0 if there's no content
*/
private int optimizeLineLength(KnuthSequence effectiveList, int startElementIndex, int endElementIndex) {
@@ -854,29 +985,6 @@ public abstract class AbstractBreaker {
log.debug("AdjustLineNumbers: difference " + difference + " / " + total + " on " + lineList.size() + " elements");
}
-// int adjustedDiff = 0;
-// int partial = 0;
-// KnuthGlue prevLine = null;
-// KnuthGlue currLine = null;
-// ListIterator lineListIterator = lineList.listIterator();
-// while (lineListIterator.hasNext()) {
-// currLine = (KnuthGlue)lineListIterator.next();
-// if (prevLine != null
-// && prevLine.getLayoutManager() != currLine.getLayoutManager()) {
-// int newAdjust = ((BlockLevelLayoutManager) prevLine.getLayoutManager())
-// .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, prevLine);
-// adjustedDiff += newAdjust;
-// }
-// partial += (difference > 0 ? currLine.getY() : currLine.getZ());
-// prevLine = currLine;
-// }
-// if (currLine != null) {
-// int newAdjust = ((BlockLevelLayoutManager) currLine.getLayoutManager())
-// .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, currLine);
-// adjustedDiff += newAdjust;
-// }
-// return adjustedDiff;
-
ListIterator lineListIterator = lineList.listIterator();
int adjustedDiff = 0;
int partial = 0;
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
index 8dca1c749..82f0599eb 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
@@ -47,22 +47,22 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
private static Log log = LogFactory.getLog(AbstractLayoutManager.class);
/** Parent LayoutManager for this LayoutManager */
- protected LayoutManager parentLM = null;
+ protected LayoutManager parentLM;
/** List of child LayoutManagers */
- protected List childLMs = null;
+ protected List childLMs;
/** Iterator for child LayoutManagers */
- protected ListIterator fobjIter = null;
+ protected ListIterator fobjIter;
/** Marker map for markers related to this LayoutManager */
- private Map markers = null;
+ private Map markers;
/** True if this LayoutManager has handled all of its content. */
- private boolean isFinished = false;
+ private boolean isFinished;
/** child LM during getNextKnuthElement phase */
- protected LayoutManager curChildLM = null;
+ protected LayoutManager curChildLM;
/** child LM iterator during getNextKnuthElement phase */
- protected ListIterator childLMiter = null;
+ protected ListIterator childLMiter;
private int lastGeneratedPosition = -1;
private int smallestPosNumberChecked = Integer.MAX_VALUE;
@@ -122,6 +122,14 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
return null;
}
+ protected void setCurrentChildLM(LayoutManager childLM) {
+ curChildLM = childLM;
+ childLMiter = new LMiter(this);
+ do {
+ curChildLM = (LayoutManager) childLMiter.next();
+ } while (curChildLM != childLM);
+ }
+
/**
* Return indication if getChildLM will return another LM.
* @return true if another child LM is still available
@@ -450,4 +458,22 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : ""));
}
+ /** {@inheritDoc} */
+ public void reset() {
+ isFinished = false;
+ curChildLM = null;
+ childLMiter = new LMiter(this);
+ /*
+ * Reset the children LM. Can't rely on childLMiter since it may have
+ * been set to null in checkEndOfLayout.
+ */
+ for (LMiter iter = new LMiter(this); iter.hasNext();) {
+ ((LayoutManager) iter.next()).reset();
+ }
+ if (fobj != null) {
+ markers = fobj.getMarkers();
+ }
+ lastGeneratedPosition = -1;
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
index 758761303..0fa046aee 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
@@ -382,4 +382,9 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
}
}
+ /** {@inheritDoc} */
+ public void reset() {
+ throw new IllegalStateException();
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
index a429359ad..14183c52e 100644
--- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
+++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
@@ -117,7 +117,7 @@ public class AreaAdditionUtil {
// set space after for each LM, in order to implement
// display-align = distribute
lc.setSpaceAfter(layoutContext.getSpaceAfter());
- lc.setStackLimitsFrom(layoutContext);
+ lc.setStackLimitBP(layoutContext.getStackLimitBP());
childLM.addAreas(childPosIter, lc);
}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
index b6dd4d082..e86c5feaf 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
@@ -24,6 +24,7 @@ import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -37,6 +38,7 @@ import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
import org.apache.fop.util.ListUtil;
@@ -261,7 +263,215 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
if (!firstVisibleMarkServed) {
addKnuthElementsForSpaceBefore(returnList, alignment);
- context.updateKeepWithPreviousPending(getKeepWithPreviousStrength());
+ context.updateKeepWithPreviousPending(getKeepWithPrevious());
+ }
+
+ addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
+ firstVisibleMarkServed = true;
+
+ if (autoHeight && inlineElementList) {
+ //Spaces, border and padding to be repeated at each break
+ addPendingMarks(context);
+
+ LayoutManager curLM; // currently active LM
+ LayoutManager prevLM = null; // previously active LM
+ while ((curLM = getChildLM()) != null) {
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.copyPendingMarksFrom(context);
+ // curLM is a ?
+ childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
+ childLC.setRefIPD(relDims.ipd);
+ childLC.setWritingMode(getBlockContainerFO().getWritingMode());
+ if (curLM == this.childLMs.get(0)) {
+ childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+ //Handled already by the parent (break collapsing, see above)
+ }
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
+ //Propagate keep-with-previous up from the first child
+ context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+ childLC.clearKeepWithPreviousPending();
+ }
+ if (returnedList.size() == 1
+ && ((ListElement)returnedList.get(0)).isForcedBreak()) {
+ // a descendant of this block has break-before
+ /*
+ if (returnList.size() == 0) {
+ // the first child (or its first child ...) has
+ // break-before;
+ // all this block, including space before, will be put in
+ // the
+ // following page
+ bSpaceBeforeServed = false;
+ }*/
+ contentList.addAll(returnedList);
+
+ // "wrap" the Position inside each element
+ // moving the elements from contentList to returnList
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ } else {
+ if (prevLM != null) {
+ // there is a block handled by prevLM
+ // before the one handled by curLM
+ addInBetweenBreak(contentList, context, childLC);
+ }
+ contentList.addAll(returnedList);
+ if (returnedList.isEmpty()) {
+ //Avoid NoSuchElementException below (happens with empty blocks)
+ continue;
+ }
+ if (ElementListUtils.endsWithForcedBreak(returnedList)) {
+ // a descendant of this block has break-after
+ if (curLM.isFinished()) {
+ // there is no other content in this block;
+ // it's useless to add space after before a page break
+ setFinished(true);
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ }
+ }
+ // propagate and clear
+ context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+ childLC.clearKeepsPending();
+ prevLM = curLM;
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ } else {
+ MinOptMax range = new MinOptMax(relDims.ipd);
+ BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+ breaker.doLayout(relDims.bpd, autoHeight);
+ boolean contentOverflows = breaker.isOverflow();
+ if (autoHeight) {
+ //Update content BPD now that it is known
+ int newHeight = breaker.deferredAlg.totalWidth;
+ boolean switchedProgressionDirection
+ = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
+ if (switchedProgressionDirection) {
+ setContentAreaIPD(newHeight);
+ } else {
+ vpContentBPD = newHeight;
+ }
+ updateRelDims(contentRectOffsetX, contentRectOffsetY, false);
+ }
+
+ Position bcPosition = new BlockContainerPosition(this, breaker);
+ returnList.add(new KnuthBox(vpContentBPD, notifyPos(bcPosition), false));
+ //TODO Handle min/opt/max for block-progression-dimension
+ /* These two elements will be used to add stretchability to the above box
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+ false, returnPosition, false));
+ returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+ LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+ */
+
+ if (contentOverflows) {
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getBlockContainerFO().getUserAgent().getEventBroadcaster());
+ boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW);
+ eventProducer.viewportOverflow(this, getBlockContainerFO().getName(),
+ breaker.getOverflowAmount(), needClip(), canRecover,
+ getBlockContainerFO().getLocator());
+ }
+ }
+ addKnuthElementsForBorderPaddingAfter(returnList, true);
+ addKnuthElementsForSpaceAfter(returnList, alignment);
+
+ //All child content is processed. Only break-after can occur now, so...
+ context.clearPendingMarks();
+ addKnuthElementsForBreakAfter(returnList, context);
+
+ context.updateKeepWithNextPending(getKeepWithNext());
+
+ setFinished(true);
+ return returnList;
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+ Position restartPosition, LayoutManager restartAtLM) {
+ resetSpaces();
+ if (isAbsoluteOrFixed()) {
+ return getNextKnuthElementsAbsolute(context, alignment);
+ }
+
+ autoHeight = false;
+ //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
+ int maxbpd = context.getStackLimitBP().opt;
+ int allocBPD;
+ if (height.getEnum() == EN_AUTO
+ || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
+ //auto height when height="auto" or "if that dimension is not specified explicitly
+ //(i.e., it depends on content's block-progression-dimension)" (XSL 1.0, 7.14.1)
+ allocBPD = maxbpd;
+ autoHeight = true;
+ if (getBlockContainerFO().getReferenceOrientation() == 0) {
+ //Cannot easily inline element list when ref-or="180"
+ inlineElementList = true;
+ }
+ } else {
+ allocBPD = height.getValue(this); //this is the content-height
+ allocBPD += getBPIndents();
+ }
+ vpContentBPD = allocBPD - getBPIndents();
+
+ referenceIPD = context.getRefIPD();
+ if (width.getEnum() == EN_AUTO) {
+ updateContentAreaIPDwithOverconstrainedAdjust();
+ } else {
+ int contentWidth = width.getValue(this);
+ updateContentAreaIPDwithOverconstrainedAdjust(contentWidth);
+ }
+
+ double contentRectOffsetX = 0;
+ contentRectOffsetX += getBlockContainerFO()
+ .getCommonMarginBlock().startIndent.getValue(this);
+ double contentRectOffsetY = 0;
+ contentRectOffsetY += getBlockContainerFO()
+ .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+ contentRectOffsetY += getBlockContainerFO()
+ .getCommonBorderPaddingBackground().getPaddingBefore(false, this);
+
+ updateRelDims(contentRectOffsetX, contentRectOffsetY, autoHeight);
+
+ int availableIPD = referenceIPD - getIPIndents();
+ if (getContentAreaIPD() > availableIPD) {
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getBlockContainerFO().getUserAgent().getEventBroadcaster());
+ eventProducer.objectTooWide(this, getBlockContainerFO().getName(),
+ getContentAreaIPD(), context.getRefIPD(),
+ getBlockContainerFO().getLocator());
+ }
+
+ MinOptMax stackLimit = new MinOptMax(relDims.bpd);
+
+ List returnedList;
+ List contentList = new LinkedList();
+ List returnList = new LinkedList();
+
+ if (!breakBeforeServed) {
+ breakBeforeServed = true;
+ if (!context.suppressBreakBefore()) {
+ if (addKnuthElementsForBreakBefore(returnList, context)) {
+ return returnList;
+ }
+ }
+ }
+
+ if (!firstVisibleMarkServed) {
+ addKnuthElementsForSpaceBefore(returnList, alignment);
+ context.updateKeepWithPreviousPending(getKeepWithPrevious());
}
addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
@@ -273,8 +483,99 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
BlockLevelLayoutManager curLM; // currently active LM
BlockLevelLayoutManager prevLM = null; // previously active LM
+
+ LayoutContext childLC = new LayoutContext(0);
+ if (lmStack.isEmpty()) {
+ assert restartAtLM != null && restartAtLM.getParent() == this;
+ curLM = (BlockLevelLayoutManager) restartAtLM;
+ curLM.reset();
+ setCurrentChildLM(curLM);
+
+ childLC.copyPendingMarksFrom(context);
+ childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
+ childLC.setRefIPD(relDims.ipd);
+ childLC.setWritingMode(getBlockContainerFO().getWritingMode());
+ if (curLM == this.childLMs.get(0)) {
+ childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+ //Handled already by the parent (break collapsing, see above)
+ }
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ } else {
+ curLM = (BlockLevelLayoutManager) lmStack.pop();
+ setCurrentChildLM(curLM);
+
+ childLC.copyPendingMarksFrom(context);
+ childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
+ childLC.setRefIPD(relDims.ipd);
+ childLC.setWritingMode(getBlockContainerFO().getWritingMode());
+ if (curLM == this.childLMs.get(0)) {
+ childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+ //Handled already by the parent (break collapsing, see above)
+ }
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment, lmStack,
+ restartPosition, restartAtLM);
+ }
+ if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
+ //Propagate keep-with-previous up from the first child
+ context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+ childLC.clearKeepWithPreviousPending();
+ }
+ if (returnedList.size() == 1
+ && ((ListElement)returnedList.get(0)).isForcedBreak()) {
+ // a descendant of this block has break-before
+ /*
+ if (returnList.size() == 0) {
+ // the first child (or its first child ...) has
+ // break-before;
+ // all this block, including space before, will be put in
+ // the
+ // following page
+ bSpaceBeforeServed = false;
+ }*/
+ contentList.addAll(returnedList);
+
+ // "wrap" the Position inside each element
+ // moving the elements from contentList to returnList
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ } else {
+ if (prevLM != null) {
+ // there is a block handled by prevLM
+ // before the one handled by curLM
+ addInBetweenBreak(contentList, context, childLC);
+ }
+ contentList.addAll(returnedList);
+ if (!returnedList.isEmpty()) {
+ if (((ListElement) ListUtil.getLast(returnedList))
+ .isForcedBreak()) {
+ // a descendant of this block has break-after
+ if (curLM.isFinished()) {
+ // there is no other content in this block;
+ // it's useless to add space after before a page break
+ setFinished(true);
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ }
+ }
+ }
+ // propagate and clear
+ context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+ childLC.clearKeepsPending();
+ prevLM = curLM;
+
while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
- LayoutContext childLC = new LayoutContext(0);
+ curLM.reset();
+ childLC = new LayoutContext(0);
childLC.copyPendingMarksFrom(context);
// curLM is a ?
childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
@@ -391,12 +692,17 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
context.clearPendingMarks();
addKnuthElementsForBreakAfter(returnList, context);
- context.updateKeepWithNextPending(getKeepWithNextStrength());
+ context.updateKeepWithNextPending(getKeepWithNext());
setFinished(true);
return returnList;
}
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return true;
+ }
+
private List getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
autoHeight = false;
@@ -1011,23 +1317,18 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(
- getBlockContainerFO().getKeepTogether());
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ public KeepProperty getKeepTogetherProperty() {
+ return getBlockContainerFO().getKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(
- getBlockContainerFO().getKeepWithNext());
+ public KeepProperty getKeepWithPreviousProperty() {
+ return getBlockContainerFO().getKeepWithPrevious();
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(
- getBlockContainerFO().getKeepWithPrevious());
+ public KeepProperty getKeepWithNextProperty() {
+ return getBlockContainerFO().getKeepWithNext();
}
/**
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
index acfcbe3f0..53dc5b38c 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
@@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -67,9 +68,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
private MinOptMax effSpaceBefore;
private MinOptMax effSpaceAfter;
- /** The list of child BreakPoss instances. */
- protected List childBreaks = new java.util.ArrayList();
-
/**
* Creates a new BlockLayoutManager.
* @param inBlock the block FO object to create the layout manager for.
@@ -114,8 +112,19 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
/** {@inheritDoc} */
public List getNextKnuthElements(LayoutContext context, int alignment) {
+ return getNextKnuthElements(context, alignment, null, null, null);
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+ Position restartPosition, LayoutManager restartAtLM) {
resetSpaces();
- return super.getNextKnuthElements(context, alignment);
+ if (lmStack == null) {
+ return super.getNextKnuthElements(context, alignment);
+ } else {
+ return super.getNextKnuthElements(context, alignment, lmStack, restartPosition,
+ restartAtLM);
+ }
}
private void resetSpaces() {
@@ -210,21 +219,18 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- KeepProperty keep = getBlockFO().getKeepTogether();
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(keep);
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ public KeepProperty getKeepTogetherProperty() {
+ return getBlockFO().getKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithNext());
+ public KeepProperty getKeepWithPreviousProperty() {
+ return getBlockFO().getKeepWithPrevious();
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithPrevious());
+ public KeepProperty getKeepWithNextProperty() {
+ return getBlockFO().getKeepWithNext();
}
/** {@inheritDoc} */
@@ -252,8 +258,8 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
// and put them in a new list;
LinkedList positionList = new LinkedList();
Position pos;
- boolean bSpaceBefore = false;
- boolean bSpaceAfter = false;
+ boolean spaceBefore = false;
+ boolean spaceAfter = false;
Position firstPos = null;
Position lastPos = null;
while (parentIter.hasNext()) {
@@ -276,11 +282,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
// this means the space was not discarded
if (positionList.size() == 0) {
// pos was in the element representing space-before
- bSpaceBefore = true;
+ spaceBefore = true;
//log.trace(" space before");
} else {
// pos was in the element representing space-after
- bSpaceAfter = true;
+ spaceAfter = true;
//log.trace(" space-after");
}
} else if (innerPosition.getLM() == this
@@ -305,7 +311,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
// the Positions in positionList were inside the elements
// created by the LineLM
childPosIter = new StackingIter(positionList.listIterator());
- } else {
+ } else {
// the Positions in positionList were inside the elements
// created by the BlockLM in the createUnitElements() method
//if (((Position) positionList.getLast()) instanceof
@@ -344,7 +350,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
// + " spacing");
// add space before and / or after the paragraph
// to reach a multiple of bpUnit
- if (bSpaceBefore && bSpaceAfter) {
+ if (spaceBefore && spaceAfter) {
foSpaceBefore = new SpaceVal(getBlockFO()
.getCommonMarginBlock().spaceBefore, this).getSpace();
foSpaceAfter = new SpaceVal(getBlockFO()
@@ -357,7 +363,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
+ foSpaceBefore.min
+ foSpaceAfter.min)
* bpUnit - splitLength - adjustedSpaceBefore;
- } else if (bSpaceBefore) {
+ } else if (spaceBefore) {
adjustedSpaceBefore = neededUnits(splitLength
+ foSpaceBefore.min)
* bpUnit - splitLength;
@@ -551,5 +557,10 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
}
}
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return true;
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java
index 9163193a2..3d30abde0 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java
@@ -19,6 +19,8 @@
package org.apache.fop.layoutmgr;
+import org.apache.fop.fo.properties.KeepProperty;
+
/**
* The interface for LayoutManagers which generate block areas
*/
@@ -35,11 +37,6 @@ public interface BlockLevelLayoutManager extends LayoutManager {
/** Adjustment class: adjustment for line height */
int LINE_HEIGHT_ADJUSTMENT = 3;
- /** The integer value for "auto" keep strength */
- int KEEP_AUTO = Integer.MIN_VALUE;
- /** The integer value for "always" keep strength */
- int KEEP_ALWAYS = Integer.MAX_VALUE;
-
int negotiateBPDAdjustment(int adj, KnuthElement lastElement);
void discardSpace(KnuthGlue spaceGlue);
@@ -48,7 +45,7 @@ public interface BlockLevelLayoutManager extends LayoutManager {
* Returns the keep-together strength for this element.
* @return the keep-together strength
*/
- int getKeepTogetherStrength();
+ Keep getKeepTogether();
/**
* @return true if this element must be kept together
@@ -59,7 +56,7 @@ public interface BlockLevelLayoutManager extends LayoutManager {
* Returns the keep-with-previous strength for this element.
* @return the keep-with-previous strength
*/
- int getKeepWithPreviousStrength();
+ Keep getKeepWithPrevious();
/**
* @return true if this element must be kept with the previous element.
@@ -70,11 +67,28 @@ public interface BlockLevelLayoutManager extends LayoutManager {
* Returns the keep-with-next strength for this element.
* @return the keep-with-next strength
*/
- int getKeepWithNextStrength();
+ Keep getKeepWithNext();
/**
* @return true if this element must be kept with the next element.
*/
boolean mustKeepWithNext();
+ /**
+ * Returns the keep-together property specified on the FObj.
+ * @return the keep-together property
+ */
+ KeepProperty getKeepTogetherProperty();
+
+ /**
+ * Returns the keep-with-previous property specified on the FObj.
+ * @return the keep-together property
+ */
+ KeepProperty getKeepWithPreviousProperty();
+
+ /**
+ * Returns the keep-with-next property specified on the FObj.
+ * @return the keep-together property
+ */
+ KeepProperty getKeepWithNextProperty();
}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
index 5a44c8391..53c529eaa 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
@@ -23,6 +23,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -30,9 +31,11 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.BlockParent;
+import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.properties.BreakPropertySet;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.SpaceProperty;
import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
import org.apache.fop.layoutmgr.inline.LineLayoutManager;
@@ -52,31 +55,26 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
*/
private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class);
- /**
- * Reference to FO whose areas it's managing or to the traits
- * of the FO.
- */
- //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
- protected BlockParent parentArea = null;
+ protected BlockParent parentArea;
/** Value of the block-progression-unit (non-standard property) */
- protected int bpUnit = 0;
+ protected int bpUnit;
/** space-before value adjusted for block-progression-unit handling */
- protected int adjustedSpaceBefore = 0;
+ protected int adjustedSpaceBefore;
/** space-after value adjusted for block-progression-unit handling */
- protected int adjustedSpaceAfter = 0;
+ protected int adjustedSpaceAfter;
/** Only used to store the original list when createUnitElements is called */
- protected List storedList = null;
+ protected List storedList;
/** Indicates whether break before has been served or not */
- protected boolean breakBeforeServed = false;
+ protected boolean breakBeforeServed;
/** Indicates whether the first visible mark has been returned by this LM, yet */
- protected boolean firstVisibleMarkServed = false;
+ protected boolean firstVisibleMarkServed;
/** Reference IPD available */
- protected int referenceIPD = 0;
+ protected int referenceIPD;
/** the effective start-indent value */
- protected int startIndent = 0;
+ protected int startIndent;
/** the effective end-indent value */
- protected int endIndent = 0;
+ protected int endIndent;
/**
* Holds the (one-time use) fo:block space-before
* and -after properties. Large fo:blocks are split
@@ -86,13 +84,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
* Block and space-after at the end of the last Block
* used in rendering the fo:block.
*/
- protected MinOptMax foSpaceBefore = null;
+ protected MinOptMax foSpaceBefore;
/** see foSpaceBefore */
- protected MinOptMax foSpaceAfter = null;
+ protected MinOptMax foSpaceAfter;
private Position auxiliaryPosition;
- private int contentAreaIPD = 0;
+ private int contentAreaIPD;
/**
* @param node the fo this LM deals with
@@ -246,38 +244,27 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
/** {@inheritDoc} */
public List getNextKnuthElements(LayoutContext context, int alignment) {
- //log.debug("BLM.getNextKnuthElements> keep-together = "
- // + layoutProps.keepTogether.getType());
- //log.debug(" keep-with-previous = " +
- // layoutProps.keepWithPrevious.getType());
- //log.debug(" keep-with-next = " +
- // layoutProps.keepWithNext.getType());
- BlockLevelLayoutManager curLM; // currently active LM
- BlockLevelLayoutManager prevLM = null; // previously active LM
-
referenceIPD = context.getRefIPD();
-
updateContentAreaIPDwithOverconstrainedAdjust();
- List returnedList = null;
List contentList = new LinkedList();
- List returnList = new LinkedList();
+ List elements = new LinkedList();
if (!breakBeforeServed) {
breakBeforeServed = true;
if (!context.suppressBreakBefore()) {
- if (addKnuthElementsForBreakBefore(returnList, context)) {
- return returnList;
+ if (addKnuthElementsForBreakBefore(elements, context)) {
+ return elements;
}
}
}
if (!firstVisibleMarkServed) {
- addKnuthElementsForSpaceBefore(returnList, alignment);
- context.updateKeepWithPreviousPending(getKeepWithPreviousStrength());
+ addKnuthElementsForSpaceBefore(elements, alignment);
+ context.updateKeepWithPreviousPending(getKeepWithPrevious());
}
- addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
+ addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed);
firstVisibleMarkServed = true;
//Spaces, border and padding to be repeated at each break
@@ -286,171 +273,340 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
//Used to indicate a special break-after case when all content has already been generated.
BreakElement forcedBreakAfterLast = null;
- while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+ LayoutManager currentChildLM;
+ while ((currentChildLM = (LayoutManager) getChildLM()) != null) {
LayoutContext childLC = new LayoutContext(0);
- childLC.copyPendingMarksFrom(context);
- if (curLM instanceof LineLayoutManager) {
- // curLM is a LineLayoutManager
- // set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!)
- childLC.setStackLimitBP(context.getStackLimitBP());
- childLC.setStackLimitIP(new MinOptMax(getContentAreaIPD()));
- childLC.setRefIPD(getContentAreaIPD());
- } else {
- // curLM is a ?
- //childLC.setStackLimit(MinOptMax.subtract(context
- // .getStackLimit(), stackSize));
- childLC.setStackLimitBP(context.getStackLimitBP());
- childLC.setRefIPD(referenceIPD);
- }
- if (curLM == this.childLMs.get(0)) {
- childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
- //Handled already by the parent (break collapsing, see above)
- }
- // get elements from curLM
- returnedList = curLM.getNextKnuthElements(childLC, alignment);
- if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
+ List childrenElements = getNextChildElements(currentChildLM, context, childLC,
+ alignment);
+
+ if (contentList.isEmpty()) {
//Propagate keep-with-previous up from the first child
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- childLC.clearKeepWithPreviousPending();
}
- if (returnedList != null
- && returnedList.size() == 1
- && ((ListElement) returnedList.get(0)).isForcedBreak()) {
+ if (childrenElements != null && !childrenElements.isEmpty()) {
+ if (!contentList.isEmpty()
+ && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
+ // there is a block handled by prevLM before the one
+ // handled by curLM, and the one handled
+ // by the current LM does not begin with a break
+ addInBetweenBreak(contentList, context, childLC);
+ }
+ if (childrenElements.size() == 1
+ && ElementListUtils.startsWithForcedBreak(childrenElements)) {
- if (curLM.isFinished() && !hasNextChildLM()) {
+ if (currentChildLM.isFinished() && !hasNextChildLM()) {
+ // a descendant of this block has break-before
+ forcedBreakAfterLast = (BreakElement) childrenElements.get(0);
+ context.clearPendingMarks();
+ break;
+ }
+
+ if (contentList.isEmpty()) {
+ // Empty fo:block, zero-length box makes sure the IDs and/or markers
+ // are registered and borders/padding are painted.
+ elements.add(new KnuthBox(0, notifyPos(new Position(this)), false));
+ }
// a descendant of this block has break-before
- forcedBreakAfterLast = (BreakElement) returnedList.get(0);
+ contentList.addAll(childrenElements);
+
+ wrapPositionElements(contentList, elements);
+
+ return elements;
+ } else {
+ contentList.addAll(childrenElements);
+ if (ElementListUtils.endsWithForcedBreak(childrenElements)) {
+ // a descendant of this block has break-after
+ if (currentChildLM.isFinished() && !hasNextChildLM()) {
+ forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
+ context.clearPendingMarks();
+ break;
+ }
+
+ wrapPositionElements(contentList, elements);
+
+ return elements;
+ }
+ }
+ context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+ }
+ }
+
+ if (!contentList.isEmpty()) {
+ wrapPositionElements(contentList, elements);
+ } else if (forcedBreakAfterLast == null) {
+ // Empty fo:block, zero-length box makes sure the IDs and/or markers
+ // are registered.
+ elements.add(new KnuthBox(0, notifyPos(new Position(this)), true));
+ }
+
+ addKnuthElementsForBorderPaddingAfter(elements, true);
+ addKnuthElementsForSpaceAfter(elements, alignment);
+
+ //All child content is processed. Only break-after can occur now, so...
+ context.clearPendingMarks();
+ if (forcedBreakAfterLast == null) {
+ addKnuthElementsForBreakAfter(elements, context);
+ } else {
+ forcedBreakAfterLast.clearPendingMarks();
+ elements.add(forcedBreakAfterLast);
+ }
+
+ context.updateKeepWithNextPending(getKeepWithNext());
+
+ setFinished(true);
+
+ return elements;
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+ Position restartPosition, LayoutManager restartAtLM) {
+ referenceIPD = context.getRefIPD();
+ updateContentAreaIPDwithOverconstrainedAdjust();
+
+ List contentList = new LinkedList();
+ List elements = new LinkedList();
+
+ if (!breakBeforeServed) {
+ breakBeforeServed = true;
+ if (!context.suppressBreakBefore()) {
+ if (addKnuthElementsForBreakBefore(elements, context)) {
+ return elements;
+ }
+ }
+ }
+
+ if (!firstVisibleMarkServed) {
+ addKnuthElementsForSpaceBefore(elements, alignment);
+ context.updateKeepWithPreviousPending(getKeepWithPrevious());
+ }
+
+ addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed);
+ firstVisibleMarkServed = true;
+
+ //Spaces, border and padding to be repeated at each break
+ addPendingMarks(context);
+
+ //Used to indicate a special break-after case when all content has already been generated.
+ BreakElement forcedBreakAfterLast = null;
+
+ LayoutContext childLC = new LayoutContext(0);
+ List childrenElements;
+ LayoutManager currentChildLM;
+ if (lmStack.isEmpty()) {
+ assert restartAtLM != null && restartAtLM.getParent() == this;
+ currentChildLM = restartAtLM;
+ currentChildLM.reset();
+ setCurrentChildLM(currentChildLM);
+
+ childrenElements = getNextChildElements(currentChildLM, context, childLC,
+ alignment);
+ } else {
+ currentChildLM = (BlockLevelLayoutManager) lmStack.pop();
+ setCurrentChildLM(currentChildLM);
+ childrenElements = getNextChildElements(currentChildLM, context, childLC, alignment,
+ lmStack, restartPosition, restartAtLM);
+ }
+
+ if (contentList.isEmpty()) {
+ //Propagate keep-with-previous up from the first child
+ context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+ }
+ if (childrenElements != null && !childrenElements.isEmpty()) {
+ if (!contentList.isEmpty()
+ && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
+ // there is a block handled by prevLM before the one
+ // handled by curLM, and the one handled
+ // by the current LM does not begin with a break
+ addInBetweenBreak(contentList, context, childLC);
+ }
+ if (childrenElements.size() == 1
+ && ElementListUtils.startsWithForcedBreak(childrenElements)) {
+
+ if (currentChildLM.isFinished() && !hasNextChildLM()) {
+ // a descendant of this block has break-before
+ forcedBreakAfterLast = (BreakElement) childrenElements.get(0);
context.clearPendingMarks();
- break;
+// break; TODO
}
if (contentList.isEmpty()) {
// Empty fo:block, zero-length box makes sure the IDs and/or markers
// are registered and borders/padding are painted.
- returnList.add(new KnuthBox(0, notifyPos(new Position(this)), false));
+ elements.add(new KnuthBox(0, notifyPos(new Position(this)), false));
}
// a descendant of this block has break-before
- contentList.addAll(returnedList);
-
- /* extension: conversione di tutta la sequenza fin'ora ottenuta */
- if (bpUnit > 0) {
- storedList = contentList;
- contentList = createUnitElements(contentList);
- }
- /* end of extension */
+ contentList.addAll(childrenElements);
- // "wrap" the Position inside each element
- // moving the elements from contentList to returnList
- returnedList = new LinkedList();
- wrapPositionElements(contentList, returnList);
+ wrapPositionElements(contentList, elements);
- return returnList;
+ return elements;
} else {
- if (returnedList == null || returnedList.isEmpty()) {
- //Avoid NoSuchElementException below (happens with empty blocks)
- continue;
+ contentList.addAll(childrenElements);
+ if (ElementListUtils.endsWithForcedBreak(childrenElements)) {
+ // a descendant of this block has break-after
+ if (currentChildLM.isFinished() && !hasNextChildLM()) {
+ forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
+ context.clearPendingMarks();
+// break; TODO
+ }
+
+ wrapPositionElements(contentList, elements);
+
+ return elements;
}
- if (prevLM != null
- && !ElementListUtils.startsWithForcedBreak(returnedList)) {
+ }
+ context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+ }
+
+ while ((currentChildLM = (LayoutManager) getChildLM()) != null) {
+ currentChildLM.reset(); // TODO won't work with forced breaks
+
+ childLC = new LayoutContext(0);
+
+ childrenElements = getNextChildElements(currentChildLM, context, childLC,
+ alignment);
+
+ if (contentList.isEmpty()) {
+ //Propagate keep-with-previous up from the first child
+ context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+ }
+ if (childrenElements != null && !childrenElements.isEmpty()) {
+ if (!contentList.isEmpty()
+ && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
// there is a block handled by prevLM before the one
// handled by curLM, and the one handled
// by the current LM does not begin with a break
addInBetweenBreak(contentList, context, childLC);
}
- contentList.addAll(returnedList);
- if (ElementListUtils.endsWithForcedBreak(returnedList)) {
- // a descendant of this block has break-after
- if (curLM.isFinished() && !hasNextChildLM()) {
- forcedBreakAfterLast = (BreakElement) ListUtil
- .removeLast(contentList);
+ if (childrenElements.size() == 1
+ && ElementListUtils.startsWithForcedBreak(childrenElements)) {
+
+ if (currentChildLM.isFinished() && !hasNextChildLM()) {
+ // a descendant of this block has break-before
+ forcedBreakAfterLast = (BreakElement) childrenElements.get(0);
context.clearPendingMarks();
break;
}
- /* extension: conversione di tutta la sequenza fin'ora ottenuta */
- if (bpUnit > 0) {
- storedList = contentList;
- contentList = createUnitElements(contentList);
+ if (contentList.isEmpty()) {
+ // Empty fo:block, zero-length box makes sure the IDs and/or markers
+ // are registered and borders/padding are painted.
+ elements.add(new KnuthBox(0, notifyPos(new Position(this)), false));
}
- /* end of extension */
+ // a descendant of this block has break-before
+ contentList.addAll(childrenElements);
- returnedList = new LinkedList();
- wrapPositionElements(contentList, returnList);
+ wrapPositionElements(contentList, elements);
- return returnList;
+ return elements;
+ } else {
+ contentList.addAll(childrenElements);
+ if (ElementListUtils.endsWithForcedBreak(childrenElements)) {
+ // a descendant of this block has break-after
+ if (currentChildLM.isFinished() && !hasNextChildLM()) {
+ forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
+ context.clearPendingMarks();
+ break;
+ }
+
+ wrapPositionElements(contentList, elements);
+
+ return elements;
+ }
}
+ context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
}
- // propagate and clear
- context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
- childLC.clearKeepsPending();
- prevLM = curLM;
- }
-
- /* Extension: conversione di tutta la sequenza fin'ora ottenuta */
- if (bpUnit > 0) {
- storedList = contentList;
- contentList = createUnitElements(contentList);
}
- /* end of extension */
- returnedList = new LinkedList();
if (!contentList.isEmpty()) {
- wrapPositionElements(contentList, returnList);
+ wrapPositionElements(contentList, elements);
} else if (forcedBreakAfterLast == null) {
// Empty fo:block, zero-length box makes sure the IDs and/or markers
// are registered.
- returnList.add(new KnuthBox(0, notifyPos(new Position(this)), true));
+ elements.add(new KnuthBox(0, notifyPos(new Position(this)), true));
}
- addKnuthElementsForBorderPaddingAfter(returnList, true);
- addKnuthElementsForSpaceAfter(returnList, alignment);
+ addKnuthElementsForBorderPaddingAfter(elements, true);
+ addKnuthElementsForSpaceAfter(elements, alignment);
//All child content is processed. Only break-after can occur now, so...
context.clearPendingMarks();
if (forcedBreakAfterLast == null) {
- addKnuthElementsForBreakAfter(returnList, context);
- }
-
- if (forcedBreakAfterLast != null) {
+ addKnuthElementsForBreakAfter(elements, context);
+ } else {
forcedBreakAfterLast.clearPendingMarks();
- returnList.add(forcedBreakAfterLast);
+ elements.add(forcedBreakAfterLast);
}
- context.updateKeepWithNextPending(getKeepWithNextStrength());
+ context.updateKeepWithNextPending(getKeepWithNext());
setFinished(true);
- return returnList;
+ return elements;
+ }
+
+ private List getNextChildElements(LayoutManager childLM, LayoutContext context,
+ LayoutContext childLC, int alignment) {
+ return getNextChildElements(childLM, context, childLC, alignment, null, null, null);
+ }
+
+ private List getNextChildElements(LayoutManager childLM, LayoutContext context,
+ LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition,
+ LayoutManager restartAtLM) {
+ childLC.copyPendingMarksFrom(context);
+ childLC.setStackLimitBP(context.getStackLimitBP());
+ if (childLM instanceof LineLayoutManager) {
+ childLC.setRefIPD(getContentAreaIPD());
+ } else {
+ childLC.setRefIPD(referenceIPD);
+ }
+ if (childLM == this.childLMs.get(0)) {
+ childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+ //Handled already by the parent (break collapsing, see above)
+ }
+
+ if (lmStack == null) {
+ return childLM.getNextKnuthElements(childLC, alignment);
+ } else {
+ if (childLM instanceof LineLayoutManager) {
+ return ((LineLayoutManager) childLM).getNextKnuthElements(childLC, alignment,
+ (LeafPosition) restartPosition);
+ } else {
+ return childLM.getNextKnuthElements(childLC, alignment,
+ lmStack, restartPosition, restartAtLM);
+ }
+ }
}
/**
* Adds a break element to the content list between individual child elements.
- * @param contentList the content list to populate
- * @param context the current layout context
+ * @param contentList
+ * @param parentLC
* @param childLC the currently active child layout context
*/
- protected void addInBetweenBreak(List contentList, LayoutContext context,
- LayoutContext childLC) {
+ protected void addInBetweenBreak(List contentList, LayoutContext parentLC,
+ LayoutContext childLC) {
+
if (mustKeepTogether()
- || context.isKeepWithNextPending()
+ || parentLC.isKeepWithNextPending()
|| childLC.isKeepWithPreviousPending()) {
- int strength = getKeepTogetherStrength();
+ Keep keep = getKeepTogether();
//Handle pending keep-with-next
- strength = Math.max(strength, context.getKeepWithNextPending());
- context.clearKeepWithNextPending();
+ keep = keep.compare(parentLC.getKeepWithNextPending());
+ parentLC.clearKeepWithNextPending();
//Handle pending keep-with-previous from child LM
- strength = Math.max(strength, childLC.getKeepWithPreviousPending());
+ keep = keep.compare(childLC.getKeepWithPreviousPending());
childLC.clearKeepWithPreviousPending();
- int penalty = KeepUtil.getPenaltyForKeep(strength);
-
// add a penalty to forbid or discourage a break between blocks
contentList.add(new BreakElement(
- new Position(this), penalty, context));
+ new Position(this), keep.getPenalty(),
+ keep.getContext(), parentLC));
return;
}
@@ -481,7 +637,7 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
// add a null penalty to allow a break between blocks
contentList.add(new BreakElement(
- new Position(this), 0, context));
+ new Position(this), 0, Constants.EN_AUTO, parentLC));
}
}
@@ -817,33 +973,77 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
* Retrieves and returns the keep-together strength from the parent element.
* @return the keep-together strength
*/
- protected int getParentKeepTogetherStrength() {
- int strength = KEEP_AUTO;
+ protected Keep getParentKeepTogether() {
+ Keep keep = Keep.KEEP_AUTO;
if (getParent() instanceof BlockLevelLayoutManager) {
- strength = ((BlockLevelLayoutManager)getParent()).getKeepTogetherStrength();
+ keep = ((BlockLevelLayoutManager)getParent()).getKeepTogether();
} else if (getParent() instanceof InlineLayoutManager) {
if (((InlineLayoutManager) getParent()).mustKeepTogether()) {
- strength = KEEP_ALWAYS;
+ keep = Keep.KEEP_ALWAYS;
}
//TODO Fix me
//strength = ((InlineLayoutManager) getParent()).getKeepTogetherStrength();
}
- return strength;
+ return keep;
}
/** {@inheritDoc} */
public boolean mustKeepTogether() {
- return getKeepTogetherStrength() > KEEP_AUTO;
+ return !getKeepTogether().isAuto();
}
/** {@inheritDoc} */
public boolean mustKeepWithPrevious() {
- return getKeepWithPreviousStrength() > KEEP_AUTO;
+ return !getKeepWithPrevious().isAuto();
}
/** {@inheritDoc} */
public boolean mustKeepWithNext() {
- return getKeepWithNextStrength() > KEEP_AUTO;
+ return !getKeepWithNext().isAuto();
+ }
+
+ /** {@inheritDoc} */
+ public Keep getKeepTogether() {
+ Keep keep = Keep.getKeep(getKeepTogetherProperty());
+ keep = keep.compare(getParentKeepTogether());
+ return keep;
+ }
+
+ /** {@inheritDoc} */
+ public Keep getKeepWithPrevious() {
+ return Keep.getKeep(getKeepWithPreviousProperty());
+ }
+
+ /** {@inheritDoc} */
+ public Keep getKeepWithNext() {
+ return Keep.getKeep(getKeepWithNextProperty());
+ }
+
+ /**
+ * {@inheritDoc}
+ * Default implementation throws {@code IllegalStateException}
+ * Must be implemented by the subclass, if applicable.
+ */
+ public KeepProperty getKeepTogetherProperty() {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * {@inheritDoc}
+ * Default implementation throws {@code IllegalStateException}
+ * Must be implemented by the subclass, if applicable.
+ */
+ public KeepProperty getKeepWithPreviousProperty() {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * {@inheritDoc}
+ * Default implementation throws {@code IllegalStateException}
+ * Must be implemented by the subclass, if applicable.
+ */
+ public KeepProperty getKeepWithNextProperty() {
+ throw new IllegalStateException();
}
/**
@@ -1608,5 +1808,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
return -1;
}
+ /** {@inheritDoc} */
+ public void reset() {
+ super.reset();
+ breakBeforeServed = false;
+ firstVisibleMarkServed = false;
+ // TODO startIndent, endIndent
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/BreakElement.java b/src/java/org/apache/fop/layoutmgr/BreakElement.java
index 98e31dbf8..3eb96e9cd 100644
--- a/src/java/org/apache/fop/layoutmgr/BreakElement.java
+++ b/src/java/org/apache/fop/layoutmgr/BreakElement.java
@@ -41,7 +41,22 @@ public class BreakElement extends UnresolvedListElement {
* @param context the layout context which contains the pending conditional elements
*/
public BreakElement(Position position, int penaltyValue, LayoutContext context) {
- this(position, 0, penaltyValue, -1, context);
+ this(position, penaltyValue, -1, context);
+ }
+
+ /**
+ * Create a new BreakElement for the given {@code position}, {@code penaltyValue}
+ * and {@code breakClass}. (Used principally to generate break-possibilities in
+ * ranges of content that must be kept together within the context corresponding
+ * to the {@code breakClass}; expected to be one of {@link Constants#EN_AUTO},
+ * {@link Constants#EN_LINE}, {@link Constants#EN_COLUMN} or {@link Constants#EN_PAGE})
+ * @param position the corresponding {@link Position}
+ * @param penaltyValue the penalty value
+ * @param breakClass the break class
+ * @param context the {@link LayoutContext}
+ */
+ public BreakElement(Position position, int penaltyValue, int breakClass, LayoutContext context) {
+ this(position, 0, penaltyValue, breakClass, context);
}
/**
@@ -65,6 +80,10 @@ public class BreakElement extends UnresolvedListElement {
this.pendingAfterMarks = context.getPendingAfterMarks();
}
+ private static String getBreakClassName(int breakClass) {
+ return AbstractBreaker.getBreakClassName(breakClass);
+ }
+
/** {@inheritDoc} */
public boolean isConditional() {
return false; //Does not really apply here
@@ -143,27 +162,17 @@ public class BreakElement extends UnresolvedListElement {
/** {@inheritDoc} */
public String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuffer sb = new StringBuffer(64);
sb.append("BreakPossibility[p:");
- sb.append(this.penaltyValue);
+ sb.append(KnuthPenalty.valueOf(this.penaltyValue));
if (isForcedBreak()) {
- sb.append(" (forced break");
- switch (getBreakClass()) {
- case Constants.EN_PAGE:
- sb.append(", page");
- break;
- case Constants.EN_COLUMN:
- sb.append(", column");
- break;
- case Constants.EN_EVEN_PAGE:
- sb.append(", even page");
- break;
- case Constants.EN_ODD_PAGE:
- sb.append(", odd page");
- break;
- default:
- }
- sb.append(")");
+ sb.append(" (forced break, ")
+ .append(getBreakClassName(this.breakClass))
+ .append(")");
+ } else if (this.penaltyValue >= 0 && this.breakClass != -1) {
+ sb.append(" (keep constraint, ")
+ .append(getBreakClassName(this.breakClass))
+ .append(")");
}
sb.append("; w:");
sb.append(penaltyWidth);
diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
index 0bf228e7e..3a688cce8 100644
--- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
@@ -22,12 +22,12 @@ package org.apache.fop.layoutmgr;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.Constants;
/**
* The set of nodes is sorted into lines indexed into activeLines.
* The nodes in each line are linked together in a single linked list by the
- * KnuthNode.next field. The activeLines array contains a link to the head of
+ * {@link KnuthNode#next} field. The activeLines array contains a link to the head of
* the linked list in index 'line*2' and a link to the tail at index 'line*2+1'.
* <p>
* The set of active nodes can be traversed by
@@ -57,13 +57,42 @@ public abstract class BreakingAlgorithm {
/** wrap-option = "no-wrap". */
public static final int ONLY_FORCED_BREAKS = 2;
+ /** Holder for symbolic literals for the fitness classes */
+ static final class FitnessClasses {
+ static final int VERY_TIGHT = 0;
+ static final int TIGHT = 1;
+ static final int LOOSE = 2;
+ static final int VERY_LOOSE = 3;
+
+ static final String[] NAMES = { "VERY TIGHT", "TIGHT", "LOOSE", "VERY LOOSE" };
+
+ /**
+ * Figure out the fitness class of this line (tight, loose,
+ * very tight or very loose).
+ * See the section on "More Bells and Whistles" in Knuth's
+ * "Breaking Paragraphs Into Lines".
+ *
+ * @param adjustRatio the adjustment ratio
+ * @return the fitness class
+ */
+ static int computeFitness(double adjustRatio) {
+ if (adjustRatio < -0.5) {
+ return FitnessClasses.VERY_TIGHT;
+ } else if (adjustRatio <= 0.5) {
+ return FitnessClasses.TIGHT;
+ } else if (adjustRatio <= 1.0) {
+ return FitnessClasses.LOOSE;
+ } else {
+ return FitnessClasses.VERY_LOOSE;
+ }
+ }
+ }
+
// parameters of Knuth's algorithm:
- /** Penalty value for flagged penalties. */
- private int flaggedPenalty = 50;
/** Demerit for consecutive lines ending at flagged penalties. */
- protected int repeatedFlaggedDemerit = 50;
+ protected int repeatedFlaggedDemerit = KnuthPenalty.FLAGGED_PENALTY;
/** Demerit for consecutive lines belonging to incompatible fitness classes . */
- protected int incompatibleFitnessDemerit = 50;
+ protected int incompatibleFitnessDemerit = KnuthPenalty.FLAGGED_PENALTY;
/** Maximum number of consecutive lines ending with a flagged penalty.
* Only a value >= 1 is a significant limit.
*/
@@ -110,7 +139,7 @@ public abstract class BreakingAlgorithm {
/** Alignment of the paragraph's last line. */
protected int alignmentLast;
/** Used to handle the text-indent property (indent the first line of a paragraph). */
- protected boolean bFirst;
+ protected boolean indentFirstPart;
/**
* The set of active nodes in ascending line order. For each line l, activeLines[2l] contains a
@@ -151,30 +180,35 @@ public abstract class BreakingAlgorithm {
protected BestRecords best;
- /** {@inheritDoc} */
private boolean partOverflowRecoveryActivated = true;
private KnuthNode lastRecovered;
/**
* Create a new instance.
- * @param align alignment of the paragraph/page. One of EN_START, EN_JUSTIFY, etc. For
- * pages EN_BEFORE, EN_AFTER are mapped to the corresponding inline properties
- * (EN_START, EN_END)
+ *
+ * @param align alignment of the paragraph/page. One of {@link Constants#EN_START},
+ * {@link Constants#EN_JUSTIFY}, {@link Constants#EN_CENTER},
+ * {@link Constants#EN_END}.
+ * For pages, {@link Constants#EN_BEFORE} and {@link Constants#EN_AFTER}
+ * are mapped to the corresponding inline properties,
+ * {@link Constants#EN_START} and {@link Constants#EN_END}.
* @param alignLast alignment of the paragraph's last line
- * @param first for the text-indent property (indent the first line of a paragraph)
- * @param partOverflowRecovery true if too long elements should be moved to the next line/part
- * @param maxFlagCount maximum allowed number of consecutive lines ending at a flagged penalty
- * item
+ * @param first for the text-indent property ({@code true} if the first line
+ * of a paragraph should be indented)
+ * @param partOverflowRecovery {@code true} if too long elements should be moved to
+ * the next line/part
+ * @param maxFlagCount maximum allowed number of consecutive lines ending at a flagged penalty
+ * item
*/
public BreakingAlgorithm(int align, int alignLast,
boolean first, boolean partOverflowRecovery,
int maxFlagCount) {
- alignment = align;
- alignmentLast = alignLast;
- bFirst = first;
+ this.alignment = align;
+ this.alignmentLast = alignLast;
+ this.indentFirstPart = first;
this.partOverflowRecoveryActivated = partOverflowRecovery;
this.best = new BestRecords();
- maxFlaggedPenaltiesCount = maxFlagCount;
+ this.maxFlaggedPenaltiesCount = maxFlagCount;
}
@@ -183,34 +217,34 @@ public abstract class BreakingAlgorithm {
*/
public class KnuthNode {
/** index of the breakpoint represented by this node */
- public int position;
+ public final int position;
/** number of the line ending at this breakpoint */
- public int line;
+ public final int line;
/** fitness class of the line ending at this breakpoint. One of 0, 1, 2, 3. */
- public int fitness;
+ public final int fitness;
/** accumulated width of the KnuthElements up to after this breakpoint. */
- public int totalWidth;
+ public final int totalWidth;
/** accumulated stretchability of the KnuthElements up to after this breakpoint. */
- public int totalStretch;
+ public final int totalStretch;
/** accumulated shrinkability of the KnuthElements up to after this breakpoint. */
- public int totalShrink;
+ public final int totalShrink;
/** adjustment ratio if the line ends at this breakpoint */
- public double adjustRatio;
+ public final double adjustRatio;
/** available stretch of the line ending at this breakpoint */
- public int availableShrink;
+ public final int availableShrink;
/** available shrink of the line ending at this breakpoint */
- public int availableStretch;
+ public final int availableStretch;
/** difference between target and actual line width */
- public int difference;
+ public final int difference;
/** minimum total demerits up to this breakpoint */
public double totalDemerits;
@@ -249,7 +283,8 @@ public abstract class BreakingAlgorithm {
return "<KnuthNode at " + position + " "
+ totalWidth + "+" + totalStretch + "-" + totalShrink
+ " line:" + line + " prev:" + (previous != null ? previous.position : -1)
- + " dem:" + totalDemerits + ">";
+ + " dem:" + totalDemerits
+ + " fitness:" + FitnessClasses.NAMES[fitness] + ">";
}
}
@@ -258,7 +293,6 @@ public abstract class BreakingAlgorithm {
*/
protected class BestRecords {
private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
- //private static final double INFINITE_DEMERITS = 1E11;
private double[] bestDemerits = new double[4];
private KnuthNode[] bestNode = new KnuthNode[4];
@@ -333,7 +367,7 @@ public abstract class BreakingAlgorithm {
return bestAvailableStretch[fitness];
}
- public int getDifference(int fitness) {
+ public int getDifference(int fitness) {
return bestDifference[fitness];
}
@@ -373,20 +407,21 @@ public abstract class BreakingAlgorithm {
return this.partOverflowRecoveryActivated;
}
- /** Empty method, hook for subclasses. Called before determining the optimal
+ /**
+ * Empty method, hook for subclasses. Called before determining the optimal
* breakpoints corresponding to a given active node.
* @param total number of lines for the active node
* @param demerits total demerits of the paragraph for the active node
*/
public abstract void updateData1(int total, double demerits);
- /** Empty method, hook for subclasses. Called when determining the optimal breakpoints
+ /**
+ * Empty method, hook for subclasses. Called when determining the optimal breakpoints
* for a given active node.
* @param bestActiveNode a node in the chain of best active nodes, corresponding to
* one of the optimal breakpoints
* @param sequence the corresponding paragraph
* @param total the number of lines into which the paragraph will be broken
- * @see #calculateBreakPoints(KnuthNode, KnuthSequence, int)
*/
public abstract void updateData2(KnuthNode bestActiveNode,
KnuthSequence sequence,
@@ -404,13 +439,18 @@ public abstract class BreakingAlgorithm {
return findBreakingPoints(par, 0, threshold, force, allowedBreaks);
}
- /** Finds an optimal set of breakpoints for the given paragraph.
- * @param par the paragraph to break
- * @param startIndex index of the Knuth element at which the breaking must start
- * @param threshold upper bound of the adjustment ratio
- * @param force true if a set of breakpoints must be found even if there are no
- * feasible ones
- * @param allowedBreaks one of ONLY_FORCED_BREAKS, NO_FLAGGED_PENALTIES, ALL_BREAKS
+ /**
+ * Finds an optimal set of breakpoints for the given paragraph.
+ *
+ * @param par the paragraph to break
+ * @param startIndex index of the Knuth element at which the breaking must start
+ * @param threshold upper bound of the adjustment ratio
+ * @param force {@code true} if a set of breakpoints must be found, even
+ * if there are no feasible ones
+ * @param allowedBreaks the type(s) of breaks allowed. One of {@link #ONLY_FORCED_BREAKS},
+ * {@link #NO_FLAGGED_PENALTIES} or {@link #ALL_BREAKS}.
+ *
+ * @return the number of effective breaks
*/
public int findBreakingPoints(KnuthSequence par, int startIndex,
double threshold, boolean force,
@@ -418,142 +458,69 @@ public abstract class BreakingAlgorithm {
this.par = par;
this.threshold = threshold;
this.force = force;
- //this.lineWidth = lineWidth;
- initialize();
- activeLines = new KnuthNode[20];
+ // initialize the algorithm
+ initialize();
- // reset lastTooShort and lastTooLong, as they could be not null
- // because of previous calls to findBreakingPoints
- lastTooShort = lastTooLong = null;
- // reset startLine and endLine
- startLine = endLine = 0;
- // current element in the paragraph
- KnuthElement thisElement = null;
// previous element in the paragraph is a KnuthBox?
boolean previousIsBox = false;
- // index of the first KnuthBox in the sequence
+ // index of the first KnuthBox in the sequence, in case of non-centered
+ // alignment. For centered alignment, we need to take into account preceding
+ // penalties+glues used for the filler spaces
int firstBoxIndex = startIndex;
- if (alignment != org.apache.fop.fo.Constants.EN_CENTER) {
- while (par.size() > firstBoxIndex
- && !((KnuthElement) par.get(firstBoxIndex)).isBox()) {
- firstBoxIndex++;
- }
+ if (alignment != Constants.EN_CENTER) {
+ firstBoxIndex = par.getFirstBoxIndex(startIndex);
}
+ firstBoxIndex = (firstBoxIndex < 0) ? 0 : firstBoxIndex;
// create an active node representing the starting point
- activeLines = new KnuthNode[20];
addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
+ KnuthNode lastForced = getNode(0);
+
if (log.isTraceEnabled()) {
log.trace("Looping over " + (par.size() - startIndex) + " elements");
+ log.trace(par);
}
- KnuthNode lastForced = getNode(0);
-
// main loop
- for (int i = startIndex; i < par.size(); i++) {
- thisElement = getElement(i);
- if (thisElement.isBox()) {
- // a KnuthBox object is not a legal line break
- totalWidth += thisElement.getW();
- previousIsBox = true;
- handleBox((KnuthBox) thisElement);
- } else if (thisElement.isGlue()) {
- // a KnuthGlue object is a legal line break
- // only if the previous object is a KnuthBox
- // consider these glues according to the value of allowedBreaks
- if (previousIsBox
- && !(allowedBreaks == ONLY_FORCED_BREAKS)) {
- considerLegalBreak(thisElement, i);
- }
- totalWidth += thisElement.getW();
- totalStretch += thisElement.getY();
- totalShrink += thisElement.getZ();
- previousIsBox = false;
- } else {
- // a KnuthPenalty is a legal line break
- // only if its penalty is not infinite;
- // consider all penalties, non-flagged penalties or non-forcing penalties
- // according to the value of allowedBreaks
- if (((KnuthPenalty) thisElement).getP() < KnuthElement.INFINITE
- && (!(allowedBreaks == NO_FLAGGED_PENALTIES)
- || !(((KnuthPenalty) thisElement).isFlagged()))
- && (!(allowedBreaks == ONLY_FORCED_BREAKS)
- || ((KnuthPenalty) thisElement).getP() == -KnuthElement.INFINITE)) {
- considerLegalBreak(thisElement, i);
- }
- previousIsBox = false;
- }
+ for (int elementIndex = startIndex; elementIndex < par.size(); elementIndex++) {
+
+ previousIsBox = handleElementAt(
+ elementIndex, previousIsBox, allowedBreaks).isBox();
+
if (activeNodeCount == 0) {
+ if (ipdChanged()) {
+ return handleIpdChange();
+ }
if (!force) {
log.debug("Could not find a set of breaking points " + threshold);
return 0;
}
+
// lastDeactivated was a "good" break, while lastTooShort and lastTooLong
// were "bad" breaks since the beginning;
// if it is not the node we just restarted from, lastDeactivated can
// replace either lastTooShort or lastTooLong
- if (lastDeactivated != null && lastDeactivated != lastForced) {
- if (lastDeactivated.adjustRatio > 0) {
- lastTooShort = lastDeactivated;
- } else {
- lastTooLong = lastDeactivated;
- }
+ if (lastDeactivated != null
+ && lastDeactivated != lastForced) {
+ replaceLastDeactivated();
}
- if (lastTooShort == null || lastForced.position == lastTooShort.position) {
- if (isPartOverflowRecoveryActivated()) {
- if (this.lastRecovered == null) {
- this.lastRecovered = lastTooLong;
- if (log.isDebugEnabled()) {
- log.debug("Recovery point: " + lastRecovered);
- }
- }
- // content would overflow, insert empty line/page and try again
- KnuthNode node = createNode(
- lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
- 0, 0, 0,
- 0, 0, 0,
- 0, 0, lastTooLong.previous);
- lastForced = node;
- node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1;
- if (log.isDebugEnabled()) {
- log.debug("first part doesn't fit into line, recovering: "
- + node.fitRecoveryCounter);
- }
- if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) {
- while (lastForced.fitRecoveryCounter > 0) {
- lastForced = lastForced.previous;
- lastDeactivated = lastForced.previous;
- startLine--;
- endLine--;
- }
- lastForced = this.lastRecovered;
- this.lastRecovered = null;
- startLine = lastForced.line;
- endLine = lastForced.line;
- log.debug("rolled back...");
- }
- } else {
- lastForced = lastTooLong;
- }
+
+ if (lastTooShort == null
+ || lastForced.position == lastTooShort.position) {
+ lastForced = recoverFromOverflow();
} else {
lastForced = lastTooShort;
this.lastRecovered = null;
}
-
- if (log.isDebugEnabled()) {
- log.debug("Restarting at node " + lastForced);
- }
- i = restartFrom(lastForced, i);
+ elementIndex = restartFrom(lastForced, elementIndex);
}
+
}
+
finish();
- if (log.isTraceEnabled()) {
- log.trace("Main loop completed " + activeNodeCount);
- log.trace("Active nodes=" + toString(""));
- }
// there is at least one set of breaking points
// select one or more active nodes, removing the others from the list
@@ -571,42 +538,49 @@ public abstract class BreakingAlgorithm {
return line;
}
+ protected boolean ipdChanged() {
+ return false;
+ }
+
+ protected int handleIpdChange() {
+ throw new IllegalStateException();
+ }
+
/**
- * This method tries to find the context FO for a position in a KnuthSequence.
- * @param seq the KnuthSequence to inspect
- * @param position the index of the position in the KnuthSequence
- * @return the requested context FO note or null, if no context node could be determined
+ * Recover from a {@link KnuthNode} leading to a line that is too long.
+ * The default implementation creates a new node corresponding to a break
+ * point after the previous node that led to a line that was too short.
+ *
+ * @param lastTooLong the node that leads to a "too long" line
+ * @return node corresponding to a breakpoint after the previous "too short" line
*/
- private FONode findContextFO(KnuthSequence seq, int position) {
- ListElement el = seq.getElement(position);
- while (el.getLayoutManager() == null && position < seq.size() - 1) {
- position++;
- el = seq.getElement(position);
- }
- Position pos = (el != null ? el.getPosition() : null);
- LayoutManager lm = (pos != null ? pos.getLM() : null);
- while (pos instanceof NonLeafPosition) {
- pos = ((NonLeafPosition)pos).getPosition();
- if (pos != null && pos.getLM() != null) {
- lm = pos.getLM();
- }
- }
- if (lm != null) {
- return lm.getFObj();
- } else {
- return null;
+ protected KnuthNode recoverFromTooLong(KnuthNode lastTooLong) {
+ if (log.isDebugEnabled()) {
+ log.debug("Recovering from too long: " + lastTooLong);
}
+
+ // content would overflow, insert empty line/page and try again
+ return createNode(
+ lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, lastTooLong.previous);
}
- /** Resets the algorithm's variables. */
+ /** Initializes the algorithm's variables. */
protected void initialize() {
this.totalWidth = 0;
this.totalStretch = 0;
this.totalShrink = 0;
+ this.lastTooShort = this.lastTooLong = null;
+ this.startLine = this.endLine = 0;
+ this.activeLines = new KnuthNode[20];
}
- /** Creates a new active node for a feasible breakpoint at the given position. Only
+ /**
+ * Creates a new active node for a feasible breakpoint at the given position. Only
* called in forced mode.
+ *
* @param position index of the element in the Knuth sequence
* @param line number of the line ending at the breakpoint
* @param fitness fitness class of the line ending at the breakpoint. One of 0, 1, 2, 3.
@@ -621,6 +595,7 @@ public abstract class BreakingAlgorithm {
* @param difference difference between target and actual line width
* @param totalDemerits minimum total demerits up to the breakpoint
* @param previous active node for the preceding breakpoint
+ * @return a new node
*/
protected KnuthNode createNode(int position, int line, int fitness,
int totalWidth, int totalStretch, int totalShrink,
@@ -646,11 +621,173 @@ public abstract class BreakingAlgorithm {
best.getNode(fitness));
}
- /** Empty method, hook for subclasses. */
+ /**
+ * Return the last node that yielded a too short line.
+ * @return the node corresponding to the last too short line
+ */
+ protected final KnuthNode getLastTooShort() {
+ return this.lastTooShort;
+ }
+
+ /**
+ * Generic handler for a {@link KnuthElement} at the given {@code position},
+ * taking into account whether the preceding element was a box, and which
+ * type(s) of breaks are allowed.
+ * Non-overridable. This method simply serves to route the call to one of the
+ * more specific handlers ({@link #handleBox(KnuthBox)},
+ * {@link #handleGlueAt(KnuthGlue,int,boolean,int)} or
+ * {@link #handlePenaltyAt(KnuthPenalty,int,int)}. The specialized handlers
+ * can be overridden by subclasses to add to or modify the default behavior
+ * for the different types of elements.
+ *
+ * @param position the position index of the element in the paragraph
+ * @param previousIsBox {@code true} if the previous element is a box
+ * @param allowedBreaks the type(s) of breaks allowed; should be one
+ * of {@link #ALL_BREAKS}, {@link #NO_FLAGGED_PENALTIES}
+ * or {@link #ONLY_FORCED_BREAKS}
+ * @return the handled element
+ */
+ protected final KnuthElement handleElementAt(int position,
+ boolean previousIsBox,
+ int allowedBreaks) {
+ KnuthElement element = getElement(position);
+ if (element.isBox()) {
+ handleBox((KnuthBox) element);
+ } else if (element.isGlue()) {
+ handleGlueAt((KnuthGlue) element, position, previousIsBox, allowedBreaks);
+ } else if (element.isPenalty()){
+ handlePenaltyAt((KnuthPenalty) element, position, allowedBreaks);
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown KnuthElement type: expecting KnuthBox, KnuthGlue or KnuthPenalty");
+ }
+ return element;
+ }
+
+ /**
+ * Handle a {@link KnuthBox}.
+ * <br/><em>Note: default implementation just adds the box's width
+ * to the total content width. Subclasses that do not keep track
+ * of this themselves, but override this method, should remember
+ * to call {@code super.handleBox(box)} to avoid unwanted side-effects.</em>
+ *
+ * @param box the {@link KnuthBox} to handle
+ */
protected void handleBox(KnuthBox box) {
+ // a KnuthBox object is not a legal line break,
+ // just add the width to the total
+ totalWidth += box.getW();
+ }
+
+ /**
+ * Handle a {@link KnuthGlue} at the given position,
+ * taking into account the additional parameters.
+ *
+ * @param glue the {@link KnuthGlue} to handle
+ * @param position the position of the glue in the list
+ * @param previousIsBox {@code true} if the preceding element is a box
+ * @param allowedBreaks the type of breaks that are allowed
+ */
+ protected void handleGlueAt(KnuthGlue glue, int position,
+ boolean previousIsBox, int allowedBreaks) {
+ // a KnuthGlue object is a legal line break
+ // only if the previous object is a KnuthBox
+ // consider these glues according to the value of allowedBreaks
+ if (previousIsBox
+ && !(allowedBreaks == ONLY_FORCED_BREAKS)) {
+ considerLegalBreak(glue, position);
+ }
+ totalWidth += glue.getW();
+ totalStretch += glue.getY();
+ totalShrink += glue.getZ();
+ }
+
+ /**
+ * Handle a {@link KnuthPenalty} at the given position,
+ * taking into account the type of breaks allowed.
+ *
+ * @param penalty the {@link KnuthPenalty} to handle
+ * @param position the position of the penalty in the list
+ * @param allowedBreaks the type of breaks that are allowed
+ */
+ protected void handlePenaltyAt(KnuthPenalty penalty, int position,
+ int allowedBreaks) {
+ // a KnuthPenalty is a legal line break
+ // only if its penalty is not infinite;
+ // consider all penalties, non-flagged penalties or non-forcing penalties
+ // according to the value of allowedBreaks
+ if (((penalty.getP() < KnuthElement.INFINITE)
+ && (!(allowedBreaks == NO_FLAGGED_PENALTIES) || !penalty.isFlagged())
+ && (!(allowedBreaks == ONLY_FORCED_BREAKS)
+ || penalty.isForcedBreak()))) {
+ considerLegalBreak(penalty, position);
+ }
+ }
+
+ /**
+ * Replace the last too-long or too-short node by the last deactivated
+ * node, if applicable.
+ */
+ protected final void replaceLastDeactivated() {
+ if (lastDeactivated.adjustRatio > 0) {
+ //last deactivated was too short
+ lastTooShort = lastDeactivated;
+ } else {
+ //last deactivated was too long or exactly the right width
+ lastTooLong = lastDeactivated;
+ }
+ }
+
+ /**
+ * Recover from an overflow condition.
+ *
+ * @return the new {@code lastForced} node
+ */
+ protected KnuthNode recoverFromOverflow() {
+ KnuthNode lastForced;
+ if (isPartOverflowRecoveryActivated()) {
+ if (lastRecovered == null) {
+ lastRecovered = lastTooLong;
+ if (log.isDebugEnabled()) {
+ log.debug("Recovery point: " + lastRecovered);
+ }
+ }
+ KnuthNode node = recoverFromTooLong(lastTooLong);
+ lastForced = node;
+ node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1;
+ if (log.isDebugEnabled()) {
+ log.debug("first part doesn't fit into line, recovering: "
+ + node.fitRecoveryCounter);
+ }
+ if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) {
+ while (lastForced.fitRecoveryCounter > 0
+ && lastForced.previous != null) {
+ lastForced = lastForced.previous;
+ lastDeactivated = lastForced.previous;
+ }
+ lastForced = lastRecovered;
+ lastRecovered = null;
+ startLine = lastForced.line;
+ endLine = lastForced.line;
+ log.debug("rolled back...");
+ }
+ } else {
+ lastForced = lastTooLong;
+ }
+ return lastForced;
}
+ /**
+ * Restart from the given node at the given index.
+ *
+ * @param restartingNode the {@link KnuthNode} to restart from
+ * @param currentIndex the current position index
+ * @return the index of the restart point
+ */
protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Restarting at node " + restartingNode);
+ }
restartingNode.totalDemerits = 0;
addNode(restartingNode.line, restartingNode);
startLine = restartingNode.line;
@@ -672,7 +809,8 @@ public abstract class BreakingAlgorithm {
return restartingIndex;
}
- /** Determines if the given breakpoint is a feasible breakpoint. That is, if a decent
+ /**
+ * Determines if the given breakpoint is a feasible breakpoint. That is, if a decent
* line may be built between one of the currently active nodes and this breakpoint.
* @param element the paragraph's element to consider
* @param elementIdx the element's index inside the paragraph
@@ -694,9 +832,15 @@ public abstract class BreakingAlgorithm {
continue;
}
int difference = computeDifference(node, element, elementIdx);
+ if (!elementCanEndLine(element, endLine, difference)) {
+ log.trace("Skipping legal break");
+ break;
+ }
+
double r = computeAdjustmentRatio(node, difference);
int availableShrink = totalShrink - node.totalShrink;
int availableStretch = totalStretch - node.totalStretch;
+
if (log.isTraceEnabled()) {
log.trace("\tr=" + r + " difference=" + difference);
log.trace("\tline=" + line);
@@ -704,87 +848,22 @@ public abstract class BreakingAlgorithm {
// The line would be too long.
if (r < -1 || element.isForcedBreak()) {
- // Deactivate node.
- if (log.isTraceEnabled()) {
- log.trace("Removing " + node);
- }
- removeNode(line, node);
- lastDeactivated = compareNodes(lastDeactivated, node);
+ deactivateNode(node, line);
}
+ int fitnessClass = FitnessClasses.computeFitness(r);
+ double demerits = computeDemerits(node, element, fitnessClass, r);
// The line is within the available shrink and the threshold.
if (r >= -1 && r <= threshold) {
- int fitnessClass = computeFitness(r);
- double demerits = computeDemerits(node, element, fitnessClass, r);
-
- if (log.isTraceEnabled()) {
- log.trace("\tDemerits=" + demerits);
- log.trace("\tFitness class=" + fitnessClass);
- }
-
- if (demerits < best.getDemerits(fitnessClass)) {
- // updates best demerits data
- best.addRecord(demerits, node, r, availableShrink, availableStretch,
- difference, fitnessClass);
- lastTooShort = null;
- }
+ activateNode(node, difference, r,
+ demerits, fitnessClass, availableShrink, availableStretch);
}
- // The line is way too short, but we are in forcing mode, so a node is
+ // The line is way too short/long, but we are in forcing mode, so a node is
// calculated and stored in lastValidNode.
if (force && (r <= -1 || r > threshold)) {
- int fitnessClass = computeFitness(r);
- double demerits = computeDemerits(node, element, fitnessClass, r);
- int newWidth = totalWidth;
- int newStretch = totalStretch;
- int newShrink = totalShrink;
-
- // add the width, stretch and shrink of glue elements after
- // the break
- // this does not affect the dimension of the line / page, only
- // the values stored in the node; these would be as if the break
- // was just before the next box element, thus ignoring glues and
- // penalties between the "real" break and the following box
- for (int i = elementIdx; i < par.size(); i++) {
- KnuthElement tempElement = getElement(i);
- if (tempElement.isBox()) {
- break;
- } else if (tempElement.isGlue()) {
- newWidth += tempElement.getW();
- newStretch += tempElement.getY();
- newShrink += tempElement.getZ();
- } else if (tempElement.isForcedBreak() && i != elementIdx) {
- break;
- }
- }
-
- if (r <= -1) {
- if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
- lastTooLong = createNode(elementIdx, line + 1, fitnessClass,
- newWidth, newStretch, newShrink,
- r, availableShrink, availableStretch,
- difference, demerits, node);
- if (log.isTraceEnabled()) {
- log.trace("Picking tooLong " + lastTooLong);
- }
- }
- } else {
- if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
- if (considerTooShort) {
- //consider possibilities which are too short
- best.addRecord(demerits, node, r,
- availableShrink, availableStretch,
- difference, fitnessClass);
- }
- lastTooShort = createNode(elementIdx, line + 1, fitnessClass,
- newWidth, newStretch, newShrink,
- r, availableShrink, availableStretch,
- difference, demerits, node);
- if (log.isTraceEnabled()) {
- log.trace("Picking tooShort " + lastTooShort);
- }
- }
- }
+ forceNode(node, line, elementIdx, difference, r,
+ demerits, fitnessClass, availableShrink, availableStretch);
}
}
addBreaks(line, elementIdx);
@@ -792,6 +871,145 @@ public abstract class BreakingAlgorithm {
}
/**
+ * Check if the given {@link KnuthElement} can end the line with the given
+ * number.
+ * @param element the element
+ * @param line the line number
+ * @param difference
+ * @return {@code true} if the element can end the line
+ */
+ protected boolean elementCanEndLine(KnuthElement element, int line, int difference) {
+ return (!element.isPenalty()
+ || element.getP() < KnuthElement.INFINITE);
+ }
+
+ /**
+ * Force the given {@link KnuthNode}, and register it.
+ *
+ * @param node the node
+ * @param line the line number
+ * @param elementIdx the position index of the element
+ * @param difference the difference between content-length and avaialable width
+ * @param r the adjustment ratio
+ * @param demerits demerits produced by the node
+ * @param fitnessClass the fitness class
+ * @param availableShrink the available amount of shrink
+ * @param availableStretch tha available amount of stretch
+ */
+ protected void forceNode(KnuthNode node,
+ int line,
+ int elementIdx,
+ int difference,
+ double r,
+ double demerits,
+ int fitnessClass,
+ int availableShrink,
+ int availableStretch) {
+
+ int newWidth = totalWidth;
+ int newStretch = totalStretch;
+ int newShrink = totalShrink;
+
+ // add the width, stretch and shrink of glue elements after
+ // the break
+ // this does not affect the dimension of the line / page, only
+ // the values stored in the node; these would be as if the break
+ // was just before the next box element, thus ignoring glues and
+ // penalties between the "real" break and the following box
+ for (int i = elementIdx; i < par.size(); i++) {
+ KnuthElement tempElement = getElement(i);
+ if (tempElement.isBox()) {
+ break;
+ } else if (tempElement.isGlue()) {
+ newWidth += tempElement.getW();
+ newStretch += tempElement.getY();
+ newShrink += tempElement.getZ();
+ } else if (tempElement.isForcedBreak() && i != elementIdx) {
+ break;
+ }
+ }
+
+ if (r <= -1) {
+ log.debug("Considering tooLong, demerits=" + demerits);
+ if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+ lastTooLong = createNode(elementIdx, line + 1, fitnessClass,
+ newWidth, newStretch, newShrink,
+ r, availableShrink, availableStretch,
+ difference, demerits, node);
+ if (log.isTraceEnabled()) {
+ log.trace("Picking tooLong " + lastTooLong);
+ }
+ }
+ } else {
+ if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+ if (considerTooShort) {
+ //consider possibilities which are too short
+ best.addRecord(demerits, node, r,
+ availableShrink, availableStretch,
+ difference, fitnessClass);
+ }
+ lastTooShort = createNode(elementIdx, line + 1, fitnessClass,
+ newWidth, newStretch, newShrink,
+ r, availableShrink, availableStretch,
+ difference, demerits, node);
+ if (log.isTraceEnabled()) {
+ log.trace("Picking tooShort " + lastTooShort);
+ }
+ }
+ }
+ }
+
+ /**
+ * Activate the given node. Will result in the given {@link KnuthNode}
+ * being registered as a feasible breakpoint, if the {@code demerits} are better
+ * than that of the best node registered for the given {@code fitnessClass}.
+ *
+ * @param node the node
+ * @param difference the difference between content-length and available width
+ * @param r the adjustment ratio
+ * @param demerits demerits produced by the node
+ * @param fitnessClass the fitness class
+ * @param availableShrink the available amount of shrink
+ * @param availableStretch the available amount of stretch
+ */
+ protected void activateNode(KnuthNode node,
+ int difference,
+ double r,
+ double demerits,
+ int fitnessClass,
+ int availableShrink,
+ int availableStretch) {
+
+ if (log.isTraceEnabled()) {
+ log.trace("\tDemerits=" + demerits);
+ log.trace("\tFitness class=" + FitnessClasses.NAMES[fitnessClass]);
+ }
+
+ if (demerits < best.getDemerits(fitnessClass)) {
+ // updates best demerits data
+ best.addRecord(demerits, node, r, availableShrink, availableStretch,
+ difference, fitnessClass);
+ lastTooShort = null;
+ }
+ }
+
+ /**
+ * Deactivate the given node
+ *
+ * @param node the node
+ * @param line the line number
+ */
+ protected void deactivateNode(KnuthNode node, int line) {
+ // Deactivate node...
+ if (log.isTraceEnabled()) {
+ log.trace("Removing " + node);
+ }
+ removeNode(line, node);
+ // ... and remember it, if it was a good candidate
+ lastDeactivated = compareNodes(lastDeactivated, node);
+ }
+
+ /**
* Adds new active nodes for breaks at the given element.
* @param line number of the previous line; this element will end line number (line+1)
* @param elementIdx the element's index
@@ -832,7 +1050,7 @@ public abstract class BreakingAlgorithm {
// by line number and position;
if (log.isTraceEnabled()) {
log.trace("\tInsert new break in list of " + activeNodeCount
- + " from fitness class " + i);
+ + " from fitness class " + FitnessClasses.NAMES[i]);
}
KnuthNode newNode = createNode(elementIdx, line + 1, i,
newWidth, newStretch, newShrink);
@@ -846,8 +1064,9 @@ public abstract class BreakingAlgorithm {
* Return the difference between the natural width of a line that would be made
* between the given active node and the given element, and the available width of the
* real line.
- * @param activeNode node for the previous breakpoint
- * @param element currently considered breakpoint
+ * @param activeNode node for the previous breakpoint
+ * @param element currently considered breakpoint
+ * @param elementIndex index of the element that is considered as a breakpoint
* @return The difference in width. Positive numbers mean extra space in the line,
* negative number that the line overflows.
*/
@@ -862,7 +1081,7 @@ public abstract class BreakingAlgorithm {
}
/**
- * Return the adjust ration needed to make up for the difference. A ration of
+ * Return the adjustment ratio needed to make up for the difference. A ratio of
* <ul>
* <li>0 means that the break has the exact right width</li>
* <li>&gt;= -1 &amp;&amp; &lt; 0 means that the break is wider than the line,
@@ -871,9 +1090,9 @@ public abstract class BreakingAlgorithm {
* but within the maximum values of the glues.</li>
* <li>&gt; 1 means that the break is too small to make up for the glues.</li>
* </ul>
- * @param activeNode
- * @param difference
- * @return The ration.
+ * @param activeNode the currently active node
+ * @param difference the difference between content-length and available width
+ * @return The adjustment ratio.
*/
protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
// compute the adjustment ratio
@@ -897,26 +1116,6 @@ public abstract class BreakingAlgorithm {
}
/**
- * Figure out the fitness class of this line (tight, loose,
- * very tight or very loose).
- * See the section on "More Bells and Whistles" in Knuth's
- * "Breaking Paragraphs Into Lines".
- * @param r
- * @return the fitness class
- */
- private int computeFitness(double r) {
- if (r < -0.5) {
- return 0;
- } else if (r <= 0.5) {
- return 1;
- } else if (r <= 1) {
- return 2;
- } else {
- return 3;
- }
- }
-
- /**
* Computes the demerits of the current breaking (that is, up to the given element),
* if the next-to-last chosen breakpoint is the given active node. This adds to the
* total demerits of the given active node, the demerits of a line starting at this
@@ -933,12 +1132,16 @@ public abstract class BreakingAlgorithm {
// compute demerits
double f = Math.abs(r);
f = 1 + 100 * f * f * f;
- if (element.isPenalty() && element.getP() >= 0) {
- f += element.getP();
- demerits = f * f;
- } else if (element.isPenalty() && !element.isForcedBreak()) {
+ if (element.isPenalty()) {
double penalty = element.getP();
- demerits = f * f - penalty * penalty;
+ if (penalty >= 0) {
+ f += penalty;
+ demerits = f * f;
+ } else if (!element.isForcedBreak()) {
+ demerits = f * f - penalty * penalty;
+ } else {
+ demerits = f * f;
+ }
} else {
demerits = f * f;
}
@@ -982,7 +1185,15 @@ public abstract class BreakingAlgorithm {
return demerits;
}
+ /**
+ * Hook for subclasses to trigger special behavior after ending the
+ * main loop in {@link #findBreakingPoints(KnuthSequence,int,double,boolean,int)}
+ */
protected void finish() {
+ if (log.isTraceEnabled()) {
+ log.trace("Main loop completed " + activeNodeCount);
+ log.trace("Active nodes=" + toString(""));
+ }
}
/**
@@ -1083,12 +1294,8 @@ public abstract class BreakingAlgorithm {
* @return the width/length in millipoints
*/
protected int getLineWidth(int line) {
- if (this.lineWidth < 0) {
- throw new IllegalStateException("lineWidth must be set"
- + (this.lineWidth != 0 ? " and positive, but it is: " + this.lineWidth : ""));
- } else {
- return this.lineWidth;
- }
+ assert lineWidth >= 0;
+ return this.lineWidth;
}
/** @return the constant line/part width or -1 if there is no such value */
@@ -1106,10 +1313,10 @@ public abstract class BreakingAlgorithm {
sb.append("[\n");
for (int i = startLine; i < endLine; i++) {
for (KnuthNode node = getNode(i); node != null; node = node.next) {
- sb.append(prepend + "\t" + node + ",\n");
+ sb.append(prepend).append('\t').append(node).append(",\n");
}
}
- sb.append(prepend + "]");
+ sb.append(prepend).append("]");
return sb.toString();
}
@@ -1121,7 +1328,7 @@ public abstract class BreakingAlgorithm {
* @param par the corresponding paragraph
* @param total the number of lines into which the paragraph will be broken
*/
- private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
+ protected void calculateBreakPoints(KnuthNode node, KnuthSequence par,
int total) {
KnuthNode bestActiveNode = node;
// use bestActiveNode to determine the optimum breakpoints
diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
index dd23d2e85..67b9b4254 100644
--- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
@@ -22,14 +22,14 @@ package org.apache.fop.layoutmgr;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockParent;
import org.apache.fop.fo.pagination.Flow;
-import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
-import org.apache.fop.layoutmgr.inline.WrapperLayoutManager;
/**
* LayoutManager for an fo:flow object.
@@ -63,108 +63,151 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
/** {@inheritDoc} */
public List getNextKnuthElements(LayoutContext context, int alignment) {
- // set layout dimensions
- int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth();
- int flowBPD = getCurrentPV().getBodyRegion().getBPD();
-
- // currently active LM
- LayoutManager curLM;
- List returnedList;
- List returnList = new LinkedList();
+ List elements = new LinkedList();
- while ((curLM = getChildLM()) != null) {
- if (!(curLM instanceof WrapperLayoutManager)
- && curLM instanceof InlineLevelLayoutManager) {
- log.error("inline area not allowed under flow - ignoring");
- curLM.setFinished(true);
- continue;
+ LayoutManager currentChildLM;
+ while ((currentChildLM = getChildLM()) != null) {
+ if (addChildElements(elements, currentChildLM, context, alignment) != null) {
+ return elements;
}
+ }
+
+ SpaceResolver.resolveElementList(elements);
+ setFinished(true);
+
+ assert !elements.isEmpty();
+ return elements;
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment,
+ Position positionAtIPDChange, LayoutManager restartAtLM) {
+
+ List elements = new LinkedList();
- int span = EN_NONE;
- int disableColumnBalancing = EN_FALSE;
- if (curLM instanceof BlockLayoutManager) {
- span = ((BlockLayoutManager)curLM).getBlockFO().getSpan();
- disableColumnBalancing = ((BlockLayoutManager) curLM).getBlockFO()
- .getDisableColumnBalancing();
- } else if (curLM instanceof BlockContainerLayoutManager) {
- span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan();
- disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO()
- .getDisableColumnBalancing();
+ LayoutManager currentChildLM = positionAtIPDChange.getLM();
+ if (currentChildLM == null) {
+ throw new IllegalStateException(
+ "Cannot find layout manager from where to re-start layout after IPD change");
+ }
+ if (restartAtLM != null && restartAtLM.getParent() == this) {
+ currentChildLM = restartAtLM;
+ setCurrentChildLM(currentChildLM);
+ currentChildLM.reset();
+ if (addChildElements(elements, currentChildLM, context, alignment) != null) {
+ return elements;
+ }
+ } else {
+ Stack lmStack = new Stack();
+ while (currentChildLM.getParent() != this) {
+ lmStack.push(currentChildLM);
+ currentChildLM = currentChildLM.getParent();
}
+ setCurrentChildLM(currentChildLM);
+ if (addChildElements(elements, currentChildLM, context, alignment, lmStack,
+ positionAtIPDChange, restartAtLM) != null) {
+ return elements;
+ }
+ }
- int currentSpan = context.getCurrentSpan();
- if (currentSpan != span) {
- if (span == EN_ALL) {
- context.setDisableColumnBalancing(disableColumnBalancing);
- }
- log.debug("span change from " + currentSpan + " to " + span);
- context.signalSpanChange(span);
- SpaceResolver.resolveElementList(returnList);
- return returnList;
+ while ((currentChildLM = getChildLM()) != null) {
+ currentChildLM.reset(); // TODO won't work with forced breaks
+ if (addChildElements(elements, currentChildLM, context, alignment) != null) {
+ return elements;
}
+ }
- // Set up a LayoutContext
- //MinOptMax bpd = context.getStackLimit();
+ SpaceResolver.resolveElementList(elements);
+ setFinished(true);
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimitBP(context.getStackLimitBP());
- childLC.setRefIPD(context.getRefIPD());
- childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
+ assert !elements.isEmpty();
+ return elements;
+ }
- // get elements from curLM
- returnedList = curLM.getNextKnuthElements(childLC, alignment);
- //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
- if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) {
- context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- childLC.clearKeepWithPreviousPending();
- }
+ private List addChildElements(List elements, LayoutManager childLM, LayoutContext context,
+ int alignment) {
+ return addChildElements(elements, childLM, context, alignment, null, null, null);
+ }
- // "wrap" the Position inside each element
- List tempList = returnedList;
- returnedList = new LinkedList();
- wrapPositionElements(tempList, returnedList);
-
- if (returnedList.size() == 1
- && ElementListUtils.endsWithForcedBreak(returnedList)) {
- // a descendant of this flow has break-before
- returnList.addAll(returnedList);
- SpaceResolver.resolveElementList(returnList);
- return returnList;
- } else if (returnedList.size() > 0) {
- if (returnList.size() > 0
- && !ElementListUtils.startsWithForcedBreak(returnedList)) {
- addInBetweenBreak(returnList, context, childLC);
- }
- returnList.addAll(returnedList);
- if (ElementListUtils.endsWithForcedBreak(returnList)) {
- if (curLM.isFinished() && !hasNextChildLM()) {
- //If the layout manager is finished at this point, the pending
- //marks become irrelevant.
- childLC.clearPendingMarks();
- //setFinished(true);
- break;
- }
- // a descendant of this flow has break-after
- SpaceResolver.resolveElementList(returnList);
- return returnList;
- }
+ private List addChildElements(List elements, LayoutManager childLM, LayoutContext context,
+ int alignment, Stack lmStack, Position position, LayoutManager restartAtLM) {
+ if (handleSpanChange(childLM, elements, context)) {
+ SpaceResolver.resolveElementList(elements);
+ return elements;
+ }
+
+ LayoutContext childLC = new LayoutContext(0);
+ List childrenElements = getNextChildElements(childLM, context, childLC, alignment, lmStack,
+ position, restartAtLM);
+ if (elements.isEmpty()) {
+ context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+ }
+ if (!elements.isEmpty()
+ && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
+ addInBetweenBreak(elements, context, childLC);
+ }
+ context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+
+ elements.addAll(childrenElements);
+
+ if (ElementListUtils.endsWithForcedBreak(elements)) {
+ // a descendant of this flow has break-before or break-after
+ if (childLM.isFinished() && !hasNextChildLM()) {
+ setFinished(true);
}
+ SpaceResolver.resolveElementList(elements);
+ return elements;
+ }
+ return null;
+ }
- //Propagate and clear
- context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
- childLC.clearKeepWithNextPending();
+ private boolean handleSpanChange(LayoutManager childLM, List elements, LayoutContext context) {
+ int span = EN_NONE;
+ int disableColumnBalancing = EN_FALSE;
+ if (childLM instanceof BlockLayoutManager) {
+ span = ((BlockLayoutManager)childLM).getBlockFO().getSpan();
+ disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO()
+ .getDisableColumnBalancing();
+ } else if (childLM instanceof BlockContainerLayoutManager) {
+ span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan();
+ disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO()
+ .getDisableColumnBalancing();
+ }
- context.updateKeepWithNextPending(getKeepWithNextStrength());
+ int currentSpan = context.getCurrentSpan();
+ if (currentSpan != span) {
+ if (span == EN_ALL) {
+ context.setDisableColumnBalancing(disableColumnBalancing);
+ }
+ log.debug("span change from " + currentSpan + " to " + span);
+ context.signalSpanChange(span);
+ return true;
+ } else {
+ return false;
}
+ }
- SpaceResolver.resolveElementList(returnList);
- setFinished(true);
+ private List getNextChildElements(LayoutManager childLM, LayoutContext context,
+ LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition,
+ LayoutManager restartLM) {
+ childLC.setStackLimitBP(context.getStackLimitBP());
+ childLC.setRefIPD(context.getRefIPD());
+ childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
- if (returnList.size() > 0) {
- return returnList;
+ List childrenElements;
+ if (lmStack == null) {
+ childrenElements = childLM.getNextKnuthElements(childLC, alignment);
} else {
- return null;
+ childrenElements = childLM.getNextKnuthElements(childLC,
+ alignment, lmStack, restartPosition, restartLM);
}
+ assert !childrenElements.isEmpty();
+
+ // "wrap" the Position inside each element
+ List tempList = childrenElements;
+ childrenElements = new LinkedList();
+ wrapPositionElements(tempList, childrenElements);
+ return childrenElements;
}
/**
@@ -203,18 +246,18 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- return KEEP_AUTO;
+ public Keep getKeepTogether() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
@@ -352,5 +395,10 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
return getCurrentPV().getBodyRegion().getBPD();
}
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return true;
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java
index 791008aec..affa75abb 100644
--- a/src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java
@@ -92,18 +92,18 @@ public class FootnoteBodyLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- return getParentKeepTogetherStrength();
+ public Keep getKeepTogether() {
+ return getParentKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
}
}
diff --git a/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java b/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java
index 6d11a3c24..104c71131 100644
--- a/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java
+++ b/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java
@@ -57,16 +57,12 @@ public class InlineKnuthSequence extends KnuthSequence {
return true;
}
- /* (non-Javadoc)
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public boolean canAppendSequence(KnuthSequence sequence) {
return sequence.isInlineSequence() && !isClosed;
}
- /* (non-Javadoc)
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public boolean appendSequence(KnuthSequence sequence) {
if (!canAppendSequence(sequence)) {
return false;
@@ -83,18 +79,14 @@ public class InlineKnuthSequence extends KnuthSequence {
return true;
}
- /* (non-Javadoc)
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public boolean appendSequence(KnuthSequence sequence, boolean keepTogether,
BreakElement breakElement) {
return appendSequence(sequence);
}
- /* (non-Javadoc)
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public KnuthSequence endSequence() {
if (!isClosed) {
add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false));
diff --git a/src/java/org/apache/fop/layoutmgr/Keep.java b/src/java/org/apache/fop/layoutmgr/Keep.java
new file mode 100644
index 000000000..444448ae4
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/Keep.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.properties.KeepProperty;
+import org.apache.fop.fo.properties.Property;
+
+/**
+ * Object representing a keep constraint, corresponding
+ * to the XSL-FO <a href="http://www.w3.org/TR/xsl/#d0e26492">keep properties</a>.
+ */
+public class Keep {
+
+ /** The integer value for "auto" keep strength. */
+ private static final int STRENGTH_AUTO = Integer.MIN_VALUE;
+
+ /** The integer value for "always" keep strength. */
+ private static final int STRENGTH_ALWAYS = Integer.MAX_VALUE;
+
+ public static final Keep KEEP_AUTO = new Keep(STRENGTH_AUTO, Constants.EN_AUTO);
+
+ public static final Keep KEEP_ALWAYS = new Keep(STRENGTH_ALWAYS, Constants.EN_LINE);
+
+ private int strength;
+
+ private int context;
+
+ private Keep(int strength, int context) {
+ this.strength = strength;
+ this.context = context;
+ }
+
+ private static int getKeepStrength(Property keep) {
+ if (keep.isAuto()) {
+ return STRENGTH_AUTO;
+ } else if (keep.getEnum() == Constants.EN_ALWAYS) {
+ return STRENGTH_ALWAYS;
+ } else {
+ return keep.getNumber().intValue();
+ }
+ }
+
+ /**
+ * Obtain a Keep instance corresponding to the given {@link KeepProperty}
+ *
+ * @param keepProperty the {@link KeepProperty}
+ * @return a new instance corresponding to the given property
+ */
+ public static Keep getKeep(KeepProperty keepProperty) {
+ Keep keep = new Keep(STRENGTH_AUTO, Constants.EN_AUTO);
+ keep.update(keepProperty.getWithinPage(), Constants.EN_PAGE);
+ keep.update(keepProperty.getWithinColumn(), Constants.EN_COLUMN);
+ keep.update(keepProperty.getWithinLine(), Constants.EN_LINE);
+ return keep;
+ }
+
+ private void update(Property keep, int context) {
+ if (!keep.isAuto()) {
+ this.strength = getKeepStrength(keep);
+ this.context = context;
+ }
+ }
+
+ /** @return {@code true} if the keep property was specified as "auto" */
+ public boolean isAuto() {
+ return strength == STRENGTH_AUTO;
+ }
+
+ /**
+ * Returns the context of this keep.
+ *
+ * @return one of {@link Constants#EN_LINE}, {@link Constants#EN_COLUMN} or
+ * {@link Constants#EN_PAGE}
+ */
+ public int getContext() {
+ return context;
+ }
+
+ /** @return the penalty value corresponding to the strength of this Keep */
+ public int getPenalty() {
+ if (strength == STRENGTH_AUTO) {
+ return 0;
+ } else if (strength == STRENGTH_ALWAYS) {
+ return KnuthElement.INFINITE;
+ } else {
+ return KnuthElement.INFINITE - 1;
+ }
+ }
+
+ private static int getKeepContextPriority(int context) {
+ switch (context) {
+ case Constants.EN_LINE: return 0;
+ case Constants.EN_COLUMN: return 1;
+ case Constants.EN_PAGE: return 2;
+ case Constants.EN_AUTO: return 3;
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Compare this Keep instance to another one, and return the
+ * stronger one if the context is the same
+ *
+ * @param other the instance to compare to
+ * @return the winning Keep instance
+ */
+ public Keep compare(Keep other) {
+
+ /* check strength "always" first, regardless of priority */
+ if (this.strength == STRENGTH_ALWAYS
+ && this.strength > other.strength) {
+ return this;
+ } else if (other.strength == STRENGTH_ALWAYS
+ && other.strength > this.strength) {
+ return other;
+ }
+
+ int pThis = getKeepContextPriority(this.context);
+ int pOther = getKeepContextPriority(other.context);
+
+ /* equal priority: strongest wins */
+ if (pThis == pOther) {
+ return (strength >= other.strength) ? this : other;
+ }
+
+ /* different priority: lowest priority wins */
+ return (pThis < pOther) ? this : other;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return (strength == STRENGTH_AUTO) ? "auto"
+ : (strength == STRENGTH_ALWAYS) ? "always"
+ : Integer.toString(strength);
+ }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/KeepUtil.java b/src/java/org/apache/fop/layoutmgr/KeepUtil.java
deleted file mode 100644
index 5cc33533f..000000000
--- a/src/java/org/apache/fop/layoutmgr/KeepUtil.java
+++ /dev/null
@@ -1,109 +0,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.
- */
-
-/* $Id$ */
-
-package org.apache.fop.layoutmgr;
-
-import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.properties.KeepProperty;
-import org.apache.fop.fo.properties.Property;
-
-/**
- * Utility class for working with keeps.
- */
-public class KeepUtil {
-
- /**
- * Converts a keep property into an integer value.
- * <p>
- * Note: The conversion restricts the effectively available integer range by two values.
- * Integer.MIN_VALUE is used to represent the value "auto" and
- * Integer.MAX_VALUE is used to represebt the value "always".
- * @param keep the keep property
- * @return the keep value as an integer
- */
- public static int getKeepStrength(Property keep) {
- if (keep.isAuto()) {
- return BlockLevelLayoutManager.KEEP_AUTO;
- } else if (keep.getEnum() == Constants.EN_ALWAYS) {
- return BlockLevelLayoutManager.KEEP_ALWAYS;
- } else {
- return keep.getNumber().intValue();
- }
- }
-
- /**
- * Returns the combined block-level keep strength from a keep property.
- * <p>
- * Note: This is a temporary method to be used until it is possible to differentiate between
- * page and column keeps!
- * @param keep the keep property
- * @return the combined keep strength
- */
- public static int getCombinedBlockLevelKeepStrength(KeepProperty keep) {
- return Math.max(
- getKeepStrength(keep.getWithinPage()),
- getKeepStrength(keep.getWithinColumn()));
- }
-
- /**
- * Indicates whether a keep strength indicates a keep constraint.
- * @param strength the keep strength
- * @return true if the keep is not "auto"
- */
- public static boolean hasKeep(int strength) {
- return strength > BlockLevelLayoutManager.KEEP_AUTO;
- }
-
- /**
- * Returns the penalty value to be used for a certain keep strength.
- * <ul>
- * <li>"auto": returns 0</li>
- * <li>"always": returns KnuthElement.INFINITE</li>
- * <li>otherwise: returns KnuthElement.INFINITE - 1</li>
- * </ul>
- * @param keepStrength the keep strength
- * @return the penalty value
- */
- public static int getPenaltyForKeep(int keepStrength) {
- if (keepStrength == BlockLevelLayoutManager.KEEP_AUTO) {
- return 0;
- }
- int penalty = KnuthElement.INFINITE;
- if (keepStrength < BlockLevelLayoutManager.KEEP_ALWAYS) {
- penalty--;
- }
- return penalty;
- }
-
- /**
- * Returns a string representation of a keep strength value.
- * @param keepStrength the keep strength
- * @return the string representation
- */
- public static String keepStrengthToString(int keepStrength) {
- if (keepStrength == BlockLevelLayoutManager.KEEP_AUTO) {
- return "auto";
- } else if (keepStrength == BlockLevelLayoutManager.KEEP_ALWAYS) {
- return "always";
- } else {
- return Integer.toString(keepStrength);
- }
- }
-
-}
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java b/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
index 6c13fba8a..5e44127df 100644
--- a/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
+++ b/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
@@ -45,7 +45,7 @@ public class KnuthPenalty extends KnuthElement {
public static final int FLAGGED_PENALTY = 50;
private int penalty;
- private boolean bFlagged;
+ private boolean isFlagged;
private int breakClass = -1;
/**
@@ -55,12 +55,12 @@ public class KnuthPenalty extends KnuthElement {
* @param p the penalty value of this penalty
* @param f is this penalty flagged?
* @param pos the Position stored in this penalty
- * @param bAux is this penalty auxiliary?
+ * @param isAuxiliary is this penalty auxiliary?
*/
- public KnuthPenalty(int w, int p, boolean f, Position pos, boolean bAux) {
- super(w, pos, bAux);
+ public KnuthPenalty(int w, int p, boolean f, Position pos, boolean isAuxiliary) {
+ super(w, pos, isAuxiliary);
penalty = p;
- bFlagged = f;
+ isFlagged = f;
}
/**
@@ -69,18 +69,37 @@ public class KnuthPenalty extends KnuthElement {
* @param w the width of this penalty
* @param p the penalty value of this penalty
* @param f is this penalty flagged?
- * @param iBreakClass the break class of this penalty (one of
+ * @param breakClass the break class of this penalty (one of
* {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN}, {@link Constants#EN_PAGE},
* {@link Constants#EN_EVEN_PAGE}, {@link Constants#EN_ODD_PAGE})
* @param pos the Position stored in this penalty
- * @param bAux is this penalty auxiliary?
+ * @param isAuxiliary is this penalty auxiliary?
*/
public KnuthPenalty(int w, int p, boolean f,
- int iBreakClass, Position pos, boolean bAux) {
- super(w, pos, bAux);
- penalty = p;
- bFlagged = f;
- breakClass = iBreakClass;
+ int breakClass, Position pos, boolean isAuxiliary) {
+ this(w, p, f, pos, isAuxiliary);
+ this.breakClass = breakClass;
+ }
+
+ private static String getBreakClassName(int breakClass) {
+ return AbstractBreaker.getBreakClassName(breakClass);
+ }
+
+ /**
+ * Get the penalty's value as a {@code java.lang.String}.
+ * (Mainly used in {@code toString()} methods, to improve readability
+ * of the trace logs.)
+ *
+ * @param penaltyValue the penalty value
+ * @return the penalty value as a {@code java.lang.String}
+ */
+ protected static String valueOf(int penaltyValue) {
+ String result = (penaltyValue < 0) ? "-" : "";
+ int tmpValue = Math.abs(penaltyValue);
+ result += (tmpValue == KnuthElement.INFINITE)
+ ? "INFINITE"
+ : String.valueOf(tmpValue);
+ return result;
}
/** {@inheritDoc} */
@@ -105,7 +124,7 @@ public class KnuthPenalty extends KnuthElement {
/** @return true is this penalty is a flagged one. */
public boolean isFlagged() {
- return bFlagged;
+ return isFlagged;
}
/** {@inheritDoc} */
@@ -121,14 +140,6 @@ public class KnuthPenalty extends KnuthElement {
return breakClass;
}
- /**
- * Sets the break class for this penalty.
- * @param cl the break class (EN_AUTO, EN_COLUMN, EN_PAGE, EN_EVEN_PAGE, EN_ODD_PAGE)
- */
- public void setBreakClass(int cl) {
- this.breakClass = cl;
- }
-
/** {@inheritDoc} */
public String toString() {
StringBuffer sb = new StringBuffer(64);
@@ -137,39 +148,22 @@ public class KnuthPenalty extends KnuthElement {
}
sb.append("penalty");
sb.append(" p=");
- if (getP() < 0) {
- sb.append("-");
- }
- if (Math.abs(getP()) == INFINITE) {
- sb.append("INFINITE");
- } else {
- sb.append(getP());
- }
- if (isFlagged()) {
+ sb.append(valueOf(this.penalty));
+ if (this.isFlagged) {
sb.append(" [flagged]");
}
sb.append(" w=");
sb.append(getW());
if (isForcedBreak()) {
- sb.append(" (forced break");
- switch (getBreakClass()) {
- case Constants.EN_PAGE:
- sb.append(", page");
- break;
- case Constants.EN_COLUMN:
- sb.append(", column");
- break;
- case Constants.EN_EVEN_PAGE:
- sb.append(", even page");
- break;
- case Constants.EN_ODD_PAGE:
- sb.append(", odd page");
- break;
- default:
- }
- sb.append(")");
+ sb.append(" (forced break, ")
+ .append(getBreakClassName(this.breakClass))
+ .append(")");
+ } else if (this.penalty >= 0 && this.breakClass != -1) {
+ //penalty corresponding to a keep constraint
+ sb.append(" (keep constraint, ")
+ .append(getBreakClassName(this.breakClass))
+ .append(")");
}
return sb.toString();
}
-
}
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthSequence.java b/src/java/org/apache/fop/layoutmgr/KnuthSequence.java
index fe9a01498..feb633265 100644
--- a/src/java/org/apache/fop/layoutmgr/KnuthSequence.java
+++ b/src/java/org/apache/fop/layoutmgr/KnuthSequence.java
@@ -19,6 +19,8 @@
package org.apache.fop.layoutmgr;
+import org.apache.fop.util.ListUtil;
+
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
@@ -26,9 +28,6 @@ import java.util.ListIterator;
/**
* Represents a list of Knuth elements.
*/
-/**
- *
- */
public abstract class KnuthSequence extends ArrayList {
/**
* Creates a new and empty list.
@@ -132,11 +131,9 @@ public abstract class KnuthSequence extends ArrayList {
* @return the last element of this sequence.
*/
public ListElement getLast() {
- int idx = size();
- if (idx == 0) {
- return null;
- }
- return (ListElement) get(idx - 1);
+ return (isEmpty()
+ ? null
+ : (ListElement) ListUtil.getLast(this));
}
/**
@@ -144,11 +141,9 @@ public abstract class KnuthSequence extends ArrayList {
* @return the removed element.
*/
public ListElement removeLast() {
- int idx = size();
- if (idx == 0) {
- return null;
- }
- return (ListElement) remove(idx - 1);
+ return (isEmpty()
+ ? null
+ : (ListElement) ListUtil.removeLast(this));
}
/**
@@ -156,7 +151,45 @@ public abstract class KnuthSequence extends ArrayList {
* @return the element at index index.
*/
public ListElement getElement(int index) {
- return (ListElement) get(index);
+ return (index >= size() || index < 0)
+ ? null
+ : (ListElement) get(index);
+ }
+
+ /** @return the position index of the first box in this sequence */
+ protected int getFirstBoxIndex() {
+ if (isEmpty()) {
+ return -1;
+ } else {
+ return getFirstBoxIndex(0);
+ }
+ }
+
+ /**
+ * Get the position index of the first box in this sequence,
+ * starting at the given index. If there is no box after the
+ * passed {@code startIndex}, the starting index itself is returned.
+ * @param startIndex the starting index for the lookup
+ * @return the absolute position index of the next box element
+ */
+ protected int getFirstBoxIndex(int startIndex) {
+ if (isEmpty() || startIndex < 0 || startIndex >= size()) {
+ return -1;
+ } else {
+ ListElement element = null;
+ int posIndex = startIndex;
+ int lastIndex = size();
+ while (posIndex < lastIndex
+ && !(element = getElement(posIndex)).isBox()) {
+ posIndex++;
+ }
+ if (posIndex != startIndex
+ && element.isBox()) {
+ return posIndex - 1;
+ } else {
+ return startIndex;
+ }
+ }
}
/**
@@ -165,4 +198,9 @@ public abstract class KnuthSequence extends ArrayList {
*/
public abstract boolean isInlineSequence();
+ /** {@inheritDoc} */
+ public String toString() {
+ return "<KnuthSequence " + super.toString() + ">";
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutContext.java b/src/java/org/apache/fop/layoutmgr/LayoutContext.java
index 4d56d1657..81726e57b 100644
--- a/src/java/org/apache/fop/layoutmgr/LayoutContext.java
+++ b/src/java/org/apache/fop/layoutmgr/LayoutContext.java
@@ -78,15 +78,6 @@ public class LayoutContext {
* level LM to allow them to optimize returned break possibilities.
*/
private MinOptMax stackLimitBP;
- /**
- * Total available stacking dimension for a "galley-level" layout
- * manager in inline-progression-direction. It is passed by the
- * parent LM. For LineLM, the block LM determines this based on
- * indent properties.
- * These LM <b>may</b> wish to pass this information down to lower
- * level LM to allow them to optimize returned break possibilities.
- */
- private MinOptMax stackLimitIP;
/** to keep track of spanning in multi-column layout */
private int currentSpan = Constants.NOT_SET;
@@ -145,8 +136,8 @@ public class LayoutContext {
private int breakBefore;
private int breakAfter;
- private int pendingKeepWithNext = BlockLevelLayoutManager.KEEP_AUTO;
- private int pendingKeepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO;
+ private Keep pendingKeepWithNext = Keep.KEEP_AUTO;
+ private Keep pendingKeepWithPrevious = Keep.KEEP_AUTO;
private int disableColumnBalancing;
@@ -158,7 +149,7 @@ public class LayoutContext {
this.flags = parentLC.flags;
this.refIPD = parentLC.refIPD;
this.writingMode = parentLC.writingMode;
- setStackLimitsFrom(parentLC);
+ setStackLimitBP(parentLC.getStackLimitBP());
this.leadingSpace = parentLC.leadingSpace; //???
this.trailingSpace = parentLC.trailingSpace; //???
this.hyphContext = parentLC.hyphContext;
@@ -183,7 +174,6 @@ public class LayoutContext {
this.flags = flags;
this.refIPD = 0;
stackLimitBP = new MinOptMax(0);
- stackLimitIP = new MinOptMax(0);
leadingSpace = null;
trailingSpace = null;
}
@@ -237,7 +227,7 @@ public class LayoutContext {
* Returns the strength of a keep-with-next currently pending.
* @return the keep-with-next strength
*/
- public int getKeepWithNextPending() {
+ public Keep getKeepWithNextPending() {
return this.pendingKeepWithNext;
}
@@ -245,7 +235,7 @@ public class LayoutContext {
* Returns the strength of a keep-with-previous currently pending.
* @return the keep-with-previous strength
*/
- public int getKeepWithPreviousPending() {
+ public Keep getKeepWithPreviousPending() {
return this.pendingKeepWithPrevious;
}
@@ -253,14 +243,14 @@ public class LayoutContext {
* Clears any pending keep-with-next strength.
*/
public void clearKeepWithNextPending() {
- this.pendingKeepWithNext = BlockLevelLayoutManager.KEEP_AUTO;
+ this.pendingKeepWithNext = Keep.KEEP_AUTO;
}
/**
* Clears any pending keep-with-previous strength.
*/
public void clearKeepWithPreviousPending() {
- this.pendingKeepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO;
+ this.pendingKeepWithPrevious = Keep.KEEP_AUTO;
}
/**
@@ -273,18 +263,18 @@ public class LayoutContext {
/**
* Updates the currently pending keep-with-next strength.
- * @param strength the new strength to consider
+ * @param keep the new strength to consider
*/
- public void updateKeepWithNextPending(int strength) {
- this.pendingKeepWithNext = Math.max(this.pendingKeepWithNext, strength);
+ public void updateKeepWithNextPending(Keep keep) {
+ this.pendingKeepWithNext = this.pendingKeepWithNext.compare(keep);
}
/**
* Updates the currently pending keep-with-previous strength.
- * @param strength the new strength to consider
+ * @param keep the new strength to consider
*/
- public void updateKeepWithPreviousPending(int strength) {
- this.pendingKeepWithPrevious = Math.max(this.pendingKeepWithPrevious, strength);
+ public void updateKeepWithPreviousPending(Keep keep) {
+ this.pendingKeepWithPrevious = this.pendingKeepWithPrevious.compare(keep);
}
/**
@@ -292,7 +282,7 @@ public class LayoutContext {
* @return true if a keep-with-next constraint is pending
*/
public boolean isKeepWithNextPending() {
- return getKeepWithNextPending() != BlockLevelLayoutManager.KEEP_AUTO;
+ return !getKeepWithNextPending().isAuto();
}
/**
@@ -300,7 +290,7 @@ public class LayoutContext {
* @return true if a keep-with-previous constraint is pending
*/
public boolean isKeepWithPreviousPending() {
- return getKeepWithPreviousPending() != BlockLevelLayoutManager.KEEP_AUTO;
+ return !getKeepWithPreviousPending().isAuto();
}
public void setLeadingSpace(SpaceSpecifier space) {
@@ -398,31 +388,6 @@ public class LayoutContext {
}
/**
- * Sets the stack limit in inline-progression-dimension.
- * @param limit the stack limit
- */
- public void setStackLimitIP(MinOptMax limit) {
- stackLimitIP = limit;
- }
-
- /**
- * Returns the stack limit in inline-progression-dimension.
- * @return the stack limit
- */
- public MinOptMax getStackLimitIP() {
- return stackLimitIP;
- }
-
- /**
- * Sets (Copies) the stack limits in both directions from another layout context.
- * @param context the layout context to take the values from
- */
- public void setStackLimitsFrom(LayoutContext context) {
- setStackLimitBP(context.getStackLimitBP());
- setStackLimitIP(context.getStackLimitIP());
- }
-
- /**
* Sets the inline-progression-dimension of the nearest ancestor reference area.
*/
public void setRefIPD(int ipd) {
@@ -662,8 +627,6 @@ public class LayoutContext {
return "Layout Context:"
+ "\nStack Limit BPD: \t"
+ (getStackLimitBP() == null ? "null" : getStackLimitBP().toString())
- + "\nStack Limit IPD: \t"
- + (getStackLimitIP() == null ? "null" : getStackLimitIP().toString())
+ "\nTrailing Space: \t"
+ (getTrailingSpace() == null ? "null" : getTrailingSpace().toString())
+ "\nLeading Space: \t"
@@ -677,9 +640,8 @@ public class LayoutContext {
+ "\nStarts New Area: \t" + startsNewArea()
+ "\nIs Last Area: \t" + isLastArea()
+ "\nTry Hyphenate: \t" + tryHyphenate()
- + "\nKeeps: \t[keep-with-next=" + KeepUtil.keepStrengthToString(getKeepWithNextPending())
- + "][keep-with-previous="
- + KeepUtil.keepStrengthToString(getKeepWithPreviousPending()) + "] pending"
+ + "\nKeeps: \t[keep-with-next=" + getKeepWithNextPending()
+ + "][keep-with-previous=" + getKeepWithPreviousPending() + "] pending"
+ "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "]["
+ (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]";
}
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java
index f19588a77..454b8b366 100644
--- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java
@@ -20,6 +20,7 @@
package org.apache.fop.layoutmgr;
import java.util.List;
+import java.util.Stack;
import org.apache.fop.area.Area;
import org.apache.fop.datatypes.PercentBaseContext;
@@ -219,4 +220,36 @@ public interface LayoutManager extends PercentBaseContext {
* @return the same Position but with a position index
*/
Position notifyPos(Position pos);
+
+ /**
+ * Re-initializes this layout manager in order to re-generate its Knuth
+ * elements according to a new IPD value.
+ */
+ void reset();
+
+ /**
+ * Returns {@code true} if this layout manager is able to re-generate its
+ * Knuth elements after an IPD change.
+ *
+ * @return {@code true} if this layout manager can be restarted after an IPD
+ * change
+ */
+ boolean isRestartable();
+
+ /**
+ * Returns an updated list of Knuth elements corresponding to this layout
+ * manager, after a change of IPD has been detected.
+ *
+ * @param context the layout context
+ * @param alignment the alignment
+ * @param lmStack the stack of LMs that are active at the IPD change
+ * @param positionAtIPDChange the position corresponding to the element
+ * finishing the page before the IPD change
+ * @param restartAtLM if not null, the layout manager from which to restart.
+ * That is, the IPD change occurs between two block elements and not inside
+ * a paragraph
+ * @return an updated list of elements, taking the new IPD into account
+ */
+ List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+ Position positionAtIPDChange, LayoutManager restartAtLM);
}
diff --git a/src/java/org/apache/fop/layoutmgr/LeafPosition.java b/src/java/org/apache/fop/layoutmgr/LeafPosition.java
index ed8cc94e2..8b2a5f4bc 100644
--- a/src/java/org/apache/fop/layoutmgr/LeafPosition.java
+++ b/src/java/org/apache/fop/layoutmgr/LeafPosition.java
@@ -21,15 +21,20 @@ package org.apache.fop.layoutmgr;
public class LeafPosition extends Position {
- private int iLeafPos;
+ private int leafPos;
public LeafPosition(LayoutManager lm, int pos) {
super(lm);
- iLeafPos = pos;
+ leafPos = pos;
+ }
+
+ public LeafPosition(LayoutManager layoutManager, int pos, int index) {
+ super(layoutManager, index);
+ leafPos = pos;
}
public int getLeafPos() {
- return iLeafPos;
+ return leafPos;
}
public boolean generatesAreas() {
diff --git a/src/java/org/apache/fop/layoutmgr/Page.java b/src/java/org/apache/fop/layoutmgr/Page.java
index 7e22cef67..b183efa01 100644
--- a/src/java/org/apache/fop/layoutmgr/Page.java
+++ b/src/java/org/apache/fop/layoutmgr/Page.java
@@ -19,7 +19,7 @@
package org.apache.fop.layoutmgr;
-import java.awt.geom.Rectangle2D;
+import java.awt.Rectangle;
import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.pagination.SimplePageMaster;
@@ -54,7 +54,7 @@ public class Page {
* @param pageNumberStr the page number (as a String)
* @param blank true if this is a blank page
*/
- public Page(Rectangle2D viewArea, int pageNumber, String pageNumberStr, boolean blank) {
+ public Page(Rectangle viewArea, int pageNumber, String pageNumberStr, boolean blank) {
this.spm = null;
this.pageViewport = new PageViewport(viewArea, pageNumber, pageNumberStr, null, blank);
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java
index 1137065ab..86c3fccf8 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java
@@ -28,7 +28,6 @@ import org.apache.fop.area.Footnote;
import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
-import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Region;
import org.apache.fop.fo.pagination.RegionBody;
import org.apache.fop.fo.pagination.StaticContent;
@@ -78,6 +77,14 @@ public class PageBreaker extends AbstractBreaker {
return pslm.getPageProvider();
}
+ /**
+ * Starts the page breaking process.
+ * @param flowBPD the constant available block-progression-dimension (used for every part)
+ */
+ void doLayout(int flowBPD) {
+ doLayout(flowBPD, false);
+ }
+
/** {@inheritDoc} */
protected PageBreakingLayoutListener createLayoutListener() {
return new PageBreakingLayoutListener() {
@@ -122,6 +129,12 @@ public class PageBreaker extends AbstractBreaker {
/** {@inheritDoc} */
protected int getNextBlockList(LayoutContext childLC,
int nextSequenceStartsOn) {
+ return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
+ }
+
+ /** {@inheritDoc} */
+ protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
+ Position positionAtIPDChange, LayoutManager restartLM, List firstElements) {
if (!firstPart) {
// if this is the first page that will be created by
// the current BlockSequence, it could have a break
@@ -133,19 +146,13 @@ public class PageBreaker extends AbstractBreaker {
pageBreakHandled = true;
pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(),
pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
- return super.getNextBlockList(childLC, nextSequenceStartsOn);
+ return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange,
+ restartLM, firstElements);
}
- /** {@inheritDoc} */
- protected List getNextKnuthElements(LayoutContext context, int alignment) {
- List contentList = null;
-
- while (!childFLM.isFinished() && contentList == null) {
- contentList = childFLM.getNextKnuthElements(context, alignment);
- }
+ private boolean containsFootnotes(List contentList, LayoutContext context) {
- // scan contentList, searching for footnotes
- boolean bFootnotesPresent = false;
+ boolean containsFootnotes = false;
if (contentList != null) {
ListIterator contentListIterator = contentList.listIterator();
while (contentListIterator.hasNext()) {
@@ -153,7 +160,7 @@ public class PageBreaker extends AbstractBreaker {
if (element instanceof KnuthBlockBox
&& ((KnuthBlockBox) element).hasAnchors()) {
// element represents a line with footnote citations
- bFootnotesPresent = true;
+ containsFootnotes = true;
LayoutContext footnoteContext = new LayoutContext(context);
footnoteContext.setStackLimitBP(context.getStackLimitBP());
footnoteContext.setRefIPD(pslm.getCurrentPV()
@@ -173,31 +180,64 @@ public class PageBreaker extends AbstractBreaker {
}
}
}
+ return containsFootnotes;
+ }
+
+ private void handleFootnoteSeparator() {
+ StaticContent footnoteSeparator;
+ footnoteSeparator = pslm.getPageSequence().getStaticContent("xsl-footnote-separator");
+ if (footnoteSeparator != null) {
+ // the footnote separator can contain page-dependent content such as
+ // page numbers or retrieve markers, so its areas cannot simply be
+ // obtained now and repeated in each page;
+ // we need to know in advance the separator bpd: the actual separator
+ // could be different from page to page, but its bpd would likely be
+ // always the same
+
+ // create a Block area that will contain the separator areas
+ separatorArea = new Block();
+ separatorArea.setIPD(pslm.getCurrentPV()
+ .getRegionReference(Constants.FO_REGION_BODY).getIPD());
+ // create a StaticContentLM for the footnote separator
+ footnoteSeparatorLM
+ = pslm.getLayoutManagerMaker().makeStaticContentLayoutManager(
+ pslm, footnoteSeparator, separatorArea);
+ footnoteSeparatorLM.doLayout();
+
+ footnoteSeparatorLength = new MinOptMax(separatorArea.getBPD());
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected List getNextKnuthElements(LayoutContext context, int alignment) {
+ List contentList = null;
+
+ while (!childFLM.isFinished() && contentList == null) {
+ contentList = childFLM.getNextKnuthElements(context, alignment);
+ }
- if (bFootnotesPresent) {
+ // scan contentList, searching for footnotes
+ if (containsFootnotes(contentList, context)) {
// handle the footnote separator
- StaticContent footnoteSeparator;
- footnoteSeparator = pslm.getPageSequence().getStaticContent("xsl-footnote-separator");
- if (footnoteSeparator != null) {
- // the footnote separator can contain page-dependent content such as
- // page numbers or retrieve markers, so its areas cannot simply be
- // obtained now and repeated in each page;
- // we need to know in advance the separator bpd: the actual separator
- // could be different from page to page, but its bpd would likely be
- // always the same
-
- // create a Block area that will contain the separator areas
- separatorArea = new Block();
- separatorArea.setIPD(pslm.getCurrentPV()
- .getRegionReference(Constants.FO_REGION_BODY).getIPD());
- // create a StaticContentLM for the footnote separator
- footnoteSeparatorLM = (StaticContentLayoutManager)
- pslm.getLayoutManagerMaker().makeStaticContentLayoutManager(
- pslm, footnoteSeparator, separatorArea);
- footnoteSeparatorLM.doLayout();
+ handleFootnoteSeparator();
+ }
+ return contentList;
+ }
- footnoteSeparatorLength = new MinOptMax(separatorArea.getBPD());
- }
+ /** {@inheritDoc} */
+ protected List getNextKnuthElements(LayoutContext context, int alignment,
+ Position positionAtIPDChange, LayoutManager restartAtLM) {
+ List contentList = null;
+
+ do {
+ contentList = childFLM.getNextKnuthElements(context, alignment, positionAtIPDChange,
+ restartAtLM);
+ } while (!childFLM.isFinished() && contentList == null);
+
+ // scan contentList, searching for footnotes
+ if (containsFootnotes(contentList, context)) {
+ // handle the footnote separator
+ handleFootnoteSeparator();
}
return contentList;
}
@@ -241,49 +281,61 @@ public class PageBreaker extends AbstractBreaker {
}
/**
- * Performs phase 3 operation
- *
- * @param alg page breaking algorithm
- * @param partCount part count
- * @param originalList the block sequence original list
- * @param effectiveList the block sequence effective list
+ * {@inheritDoc}
+ * This implementation checks whether to trigger column-balancing,
+ * or whether to take into account a 'last-page' condition.
*/
protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
+
if (needColumnBalancing) {
- doPhase3WithColumnBalancing(alg, partCount, originalList, effectiveList);
- } else {
- if (!hasMoreContent() && pslm.getPageSequence().hasPagePositionLast()) {
- //last part is reached and we have a "last page" condition
- doPhase3WithLastPage(alg, partCount, originalList, effectiveList);
- } else {
- //Directly add areas after finding the breaks
- addAreas(alg, partCount, originalList, effectiveList);
+ //column balancing for the last part
+ doPhase3(alg, partCount, originalList, effectiveList, false);
+ return;
+ }
+
+ boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast();
+ if (!hasMoreContent()) {
+ //last part is reached
+ if (lastPageMasterDefined) {
+ //last-page condition
+ doPhase3(alg, partCount, originalList, effectiveList, true);
+ return;
}
}
+
+ //nothing special: just add the areas now
+ addAreas(alg, partCount, originalList, effectiveList);
}
- private void doPhase3WithLastPage(PageBreakingAlgorithm alg, int partCount,
- BlockSequence originalList, BlockSequence effectiveList) {
- int newStartPos;
+ /**
+ * Restart the algorithm at the break corresponding
+ * to the given partCount
+ * (currently only used to redo the part after the
+ * last break in case of column-balancing
+ * and/or a last page-master)
+ */
+ private void doPhase3(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList,
+ boolean isLastPart) {
+
+
+ int newStartPos = 0;
int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
if (restartPoint > 0) {
- //Add definitive areas before last page
+ //Add definitive areas for the parts before the
+ //restarting point
addAreas(alg, restartPoint, originalList, effectiveList);
//Get page break from which we restart
PageBreakPosition pbp = (PageBreakPosition)
alg.getPageBreaks().get(restartPoint - 1);
- //Set starting position to the first element *after* the page-break
newStartPos = pbp.getLeafPos() + 1;
//Handle page break right here to avoid any side-effects
if (newStartPos > 0) {
handleBreakTrait(Constants.EN_PAGE);
}
- } else {
- newStartPos = 0;
}
- AbstractBreaker.log.debug("Last page handling now!!!");
- AbstractBreaker.log.debug("===================================================");
+
AbstractBreaker.log.debug("Restarting at " + restartPoint
+ ", new start position: " + newStartPos);
@@ -292,95 +344,78 @@ public class PageBreaker extends AbstractBreaker {
int currentPageNum = pslm.getCurrentPageNum();
pageProvider.setStartOfNextElementList(currentPageNum,
pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
- pageProvider.setLastPageIndex(currentPageNum);
-
- //Restart last page
- PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
- getTopLevelLM(),
- getPageProvider(), createLayoutListener(),
- alg.getAlignment(), alg.getAlignmentLast(),
- footnoteSeparatorLength,
- isPartOverflowRecoveryActivated(), false, false);
- //alg.setConstantLineWidth(flowBPD);
- int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
- newStartPos,
- 1, true, BreakingAlgorithm.ALL_BREAKS);
- AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount
- + " pageBreaks.size()= " + algRestart.getPageBreaks().size());
+
+ PageBreakingAlgorithm algRestart = null;
+ int optimalPageCount;
//Make sure we only add the areas we haven't added already
effectiveList.ignoreAtStart = newStartPos;
- boolean replaceLastPage
- = iOptPageCount <= pslm.getCurrentPV().getBodyRegion().getColumnCount();
- if (replaceLastPage) {
- //Replace last page
- pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
- addAreas(algRestart, iOptPageCount, originalList, effectiveList);
- } else {
- addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList);
- //Add blank last page
- pageProvider.setLastPageIndex(currentPageNum + 1);
- pslm.setCurrentPage(pslm.makeNewPage(true, true));
- }
- AbstractBreaker.log.debug("===================================================");
- }
- private void doPhase3WithColumnBalancing(PageBreakingAlgorithm alg, int partCount,
- BlockSequence originalList, BlockSequence effectiveList) {
- AbstractBreaker.log.debug("Column balancing now!!!");
- AbstractBreaker.log.debug("===================================================");
- int newStartPos;
- int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
- if (restartPoint > 0) {
- //Add definitive areas
- addAreas(alg, restartPoint, originalList, effectiveList);
- //Get page break from which we restart
- PageBreakPosition pbp = (PageBreakPosition)
- alg.getPageBreaks().get(restartPoint - 1);
- newStartPos = pbp.getLeafPos();
- //Handle page break right here to avoid any side-effects
- if (newStartPos > 0) {
- handleBreakTrait(Constants.EN_PAGE);
- }
- } else {
- newStartPos = 0;
+ if (isLastPart) {
+ pageProvider.setLastPageIndex(currentPageNum);
}
- AbstractBreaker.log.debug("Restarting at " + restartPoint
- + ", new start position: " + newStartPos);
- pageBreakHandled = true;
- //Update so the available BPD is reported correctly
- pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(),
- pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
+ if (needColumnBalancing) {
+ AbstractBreaker.log.debug("Column balancing now!!!");
+ AbstractBreaker.log.debug("===================================================");
+
+ //Restart last page
+ algRestart = new BalancingColumnBreakingAlgorithm(
+ getTopLevelLM(), getPageProvider(), createLayoutListener(),
+ alignment, Constants.EN_START, footnoteSeparatorLength,
+ isPartOverflowRecoveryActivated(),
+ pslm.getCurrentPV().getBodyRegion().getColumnCount());
+ AbstractBreaker.log.debug("===================================================");
+ } else {
+ //plain last page, no column balancing
+ AbstractBreaker.log.debug("Last page handling now!!!");
+ AbstractBreaker.log.debug("===================================================");
+ //Restart last page
+ algRestart = new PageBreakingAlgorithm(
+ getTopLevelLM(), getPageProvider(), createLayoutListener(),
+ alg.getAlignment(), alg.getAlignmentLast(),
+ footnoteSeparatorLength,
+ isPartOverflowRecoveryActivated(), false, false);
+ AbstractBreaker.log.debug("===================================================");
+ }
- //Restart last page
- PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
- getTopLevelLM(),
- getPageProvider(), createLayoutListener(),
- alignment, Constants.EN_START, footnoteSeparatorLength,
- isPartOverflowRecoveryActivated(),
- pslm.getCurrentPV().getBodyRegion().getColumnCount());
- //alg.setConstantLineWidth(flowBPD);
- int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
+ optimalPageCount = algRestart.findBreakingPoints(effectiveList,
newStartPos,
1, true, BreakingAlgorithm.ALL_BREAKS);
- AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount
+ AbstractBreaker.log.debug("restart: optimalPageCount= " + optimalPageCount
+ " pageBreaks.size()= " + algRestart.getPageBreaks().size());
- if (iOptPageCount > pslm.getCurrentPV().getBodyRegion().getColumnCount()) {
- AbstractBreaker.log.warn(
- "Breaking algorithm produced more columns than are available.");
- /* reenable when everything works
- throw new IllegalStateException(
- "Breaking algorithm must not produce more columns than available.");
- */
+
+ boolean fitsOnePage
+ = optimalPageCount <= pslm.getCurrentPV().getBodyRegion().getColumnCount();
+
+ if (isLastPart) {
+ if (fitsOnePage) {
+ //Replace last page
+ pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
+ } else {
+ //Last page-master cannot hold the content.
+ //Add areas now...
+ addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList);
+ //...and add a blank last page
+ pageProvider.setLastPageIndex(currentPageNum + 1);
+ pslm.setCurrentPage(pslm.makeNewPage(true, true));
+ return;
+ }
+ } else {
+ if (!fitsOnePage) {
+ AbstractBreaker.log.warn(
+ "Breaking algorithm produced more columns than are available.");
+ /* reenable when everything works
+ throw new IllegalStateException(
+ "Breaking algorithm must not produce more columns than available.");
+ */
+ }
}
- //Make sure we only add the areas we haven't added already
- effectiveList.ignoreAtStart = newStartPos;
- addAreas(algRestart, iOptPageCount, originalList, effectiveList);
- AbstractBreaker.log.debug("===================================================");
+
+ addAreas(algRestart, optimalPageCount, originalList, effectiveList);
}
protected void startPart(BlockSequence list, int breakClass) {
- AbstractBreaker.log.debug("startPart() breakClass=" + breakClass);
+ AbstractBreaker.log.debug("startPart() breakClass=" + getBreakClassName(breakClass));
if (pslm.getCurrentPage() == null) {
throw new IllegalStateException("curPage must not be null");
}
@@ -416,7 +451,7 @@ public class PageBreaker extends AbstractBreaker {
|| pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex) {
// call addAreas() for each FootnoteBodyLM
for (int i = pbp.footnoteFirstListIndex; i <= pbp.footnoteLastListIndex; i++) {
- LinkedList elementList = alg.getFootnoteList(i);
+ List elementList = alg.getFootnoteList(i);
int firstIndex = (i == pbp.footnoteFirstListIndex
? pbp.footnoteFirstElementIndex : 0);
int lastIndex = (i == pbp.footnoteLastListIndex
@@ -441,9 +476,7 @@ public class PageBreaker extends AbstractBreaker {
pslm.getCurrentPV().getCurrentSpan().notifyFlowsFinished();
}
- /**
- * @return the current child flow layout manager
- */
+ /** @return the current child flow layout manager */
protected LayoutManager getCurrentChildLM() {
return childFLM;
}
@@ -462,45 +495,51 @@ public class PageBreaker extends AbstractBreaker {
*/
private void handleBreakTrait(int breakVal) {
Page curPage = pslm.getCurrentPage();
- if (breakVal == Constants.EN_ALL) {
+ switch (breakVal) {
+ case Constants.EN_ALL:
//break due to span change in multi-column layout
curPage.getPageViewport().createSpan(true);
return;
- } else if (breakVal == Constants.EN_NONE) {
+ case Constants.EN_NONE:
curPage.getPageViewport().createSpan(false);
return;
- } else if (breakVal == Constants.EN_COLUMN
- || breakVal <= 0
- || breakVal == Constants.EN_AUTO) {
+ case Constants.EN_COLUMN:
+ case Constants.EN_AUTO:
+ case Constants.EN_PAGE:
+ case -1:
PageViewport pv = curPage.getPageViewport();
//Check if previous page was spanned
boolean forceNewPageWithSpan = false;
RegionBody rb = (RegionBody)curPage.getSimplePageMaster().getRegion(
Constants.FO_REGION_BODY);
- if (breakVal < 0
- && rb.getColumnCount() > 1
- && pv.getCurrentSpan().getColumnCount() == 1) {
- forceNewPageWithSpan = true;
- }
+ forceNewPageWithSpan
+ = (rb.getColumnCount() > 1
+ && pv.getCurrentSpan().getColumnCount() == 1);
if (forceNewPageWithSpan) {
+ log.trace("Forcing new page with span");
curPage = pslm.makeNewPage(false, false);
curPage.getPageViewport().createSpan(true);
} else if (pv.getCurrentSpan().hasMoreFlows()) {
+ log.trace("Moving to next flow");
pv.getCurrentSpan().moveToNextFlow();
} else {
- curPage = pslm.makeNewPage(false, false);
+ log.trace("Making new page");
+ /*curPage = */pslm.makeNewPage(false, false);
}
return;
- }
- log.debug("handling break-before after page " + pslm.getCurrentPageNum()
- + " breakVal=" + breakVal);
- if (needBlankPageBeforeNew(breakVal)) {
- curPage = pslm.makeNewPage(true, false);
- }
- if (needNewPage(breakVal)) {
- curPage = pslm.makeNewPage(false, false);
+ default:
+ log.debug("handling break-before after page " + pslm.getCurrentPageNum()
+ + " breakVal=" + getBreakClassName(breakVal));
+ if (needBlankPageBeforeNew(breakVal)) {
+ log.trace("Inserting blank page");
+ /*curPage = */pslm.makeNewPage(true, false);
+ }
+ if (needNewPage(breakVal)) {
+ log.trace("Making new page");
+ /*curPage = */pslm.makeNewPage(false, false);
+ }
}
}
@@ -540,4 +579,4 @@ public class PageBreaker extends AbstractBreaker {
return true;
}
}
-} \ No newline at end of file
+}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
index 8095feba1..52238e9be 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
@@ -20,16 +20,19 @@
package org.apache.fop.layoutmgr;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
import org.apache.fop.traits.MinOptMax;
+import org.apache.fop.util.ListUtil;
class PageBreakingAlgorithm extends BreakingAlgorithm {
@@ -47,9 +50,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
* List&lt;List&lt;KnuthElement&gt;&gt;, it contains the sequences of KnuthElement
* representing the footnotes bodies.
*/
- private ArrayList footnotesList = null;
+ private List footnotesList = null;
/** Cumulated bpd of unhandled footnotes. */
- private ArrayList lengthList = null;
+ private List lengthList = null;
/** Length of all the footnotes which will be put on the current page. */
private int totalFootnotesLength = 0;
/**
@@ -58,6 +61,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
* footnotes from its preceding pages.
*/
private int insertedFootnotesLength = 0;
+
/** True if footnote citations have been met since the beginning of the page sequence. */
private boolean footnotesPending = false;
/**
@@ -92,6 +96,14 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
//Controls whether a single part should be forced if possible (ex. block-container)
private boolean favorSinglePart = false;
+ private boolean ipdChange;
+ private KnuthNode bestNodeForIPDChange;
+
+ //Used to keep track of switches in keep-context
+ private int currentKeepContext = Constants.EN_AUTO;
+ private KnuthNode lastBeforeKeepContextSwitch;
+
+
public PageBreakingAlgorithm(LayoutManager topLevelLM,
PageProvider pageProvider,
PageBreakingLayoutListener layoutListener,
@@ -178,6 +190,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
+ /** {@inheritDoc} */
protected void initialize() {
super.initialize();
insertedFootnotesLength = 0;
@@ -185,6 +198,73 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
footnoteElementIndex = -1;
}
+ /**
+ * {@inheritDoc}
+ * Overridden to defer a part to the next page, if it
+ * must be kept within one page, but is too large to fit in
+ * the last column.
+ */
+ protected KnuthNode recoverFromTooLong(KnuthNode lastTooLong) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Recovering from too long: " + lastTooLong);
+ log.debug("\tlastTooShort = " + getLastTooShort());
+ log.debug("\tlastBeforeKeepContextSwitch = " + lastBeforeKeepContextSwitch);
+ log.debug("\tcurrentKeepContext = " + AbstractBreaker.getBreakClassName(currentKeepContext));
+ }
+
+ if (lastBeforeKeepContextSwitch == null
+ || currentKeepContext == Constants.EN_AUTO) {
+ return super.recoverFromTooLong(lastTooLong);
+ }
+
+ KnuthNode node = lastBeforeKeepContextSwitch;
+ lastBeforeKeepContextSwitch = null;
+ // content would overflow, insert empty page/column(s) and try again
+ while (!pageProvider.endPage(node.line - 1)) {
+ log.trace("Adding node for empty column");
+ node = createNode(
+ node.position,
+ node.line + 1, 1,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, node);
+ }
+ return node;
+ }
+
+ /**
+ * Compare two KnuthNodes and return the node with the least demerit.
+ *
+ * @param node1 The first knuth node.
+ * @param node2 The other knuth node.
+ * @return the node with the least demerit.
+ */
+ protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) {
+
+ /* if either node is null, return the other one */
+ if (node1 == null || node2 == null) {
+ return (node1 == null) ? node2 : node1;
+ }
+
+ /* if either one of the nodes corresponds to a mere column-break,
+ * and the other one corresponds to a page-break, return the page-break node
+ */
+ if (pageProvider != null) {
+ if (pageProvider.endPage(node1.line - 1)
+ && !pageProvider.endPage(node2.line - 1)) {
+ return node1;
+ } else if (pageProvider.endPage(node2.line - 1)
+ && !pageProvider.endPage(node1.line - 1)) {
+ return node2;
+ }
+ }
+
+ /* all other cases: use superclass implementation */
+ return super.compareNodes(node1, node2);
+ }
+
+ /** {@inheritDoc} */
protected KnuthNode createNode(int position, int line, int fitness,
int totalWidth, int totalStretch, int totalShrink,
double adjustRatio, int availableShrink, int availableStretch,
@@ -196,6 +276,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
difference, totalDemerits, previous);
}
+ /** {@inheritDoc} */
protected KnuthNode createNode(int position, int line, int fitness,
int totalWidth, int totalStretch, int totalShrink) {
return new KnuthPageNode(position, line, fitness,
@@ -209,11 +290,13 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
/**
+ * {@inheritDoc}
* Page-breaking specific handling of the given box. Currently it adds the footnotes
* cited in the given box to the list of to-be-handled footnotes.
* @param box a block-level element possibly containing foonotes citations
*/
protected void handleBox(KnuthBox box) {
+ super.handleBox(box);
if (box instanceof KnuthBlockBox
&& ((KnuthBlockBox) box).hasAnchors()) {
handleFootnotes(((KnuthBlockBox) box).getElementLists());
@@ -225,6 +308,28 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
/**
+ * {@inheritDoc}
+ * Overridden to consider penalties with value {@link KnuthElement#INFINITE}
+ * as legal break-points, if the current keep-context allows this
+ * (a keep-*.within-page="always" constraint still permits column-breaks)
+ */
+ protected void handlePenaltyAt(KnuthPenalty penalty, int position,
+ int allowedBreaks) {
+ super.handlePenaltyAt(penalty, position, allowedBreaks);
+ /* if the penalty had value INFINITE, default implementation
+ * will not have considered it a legal break, but it could still
+ * be one.
+ */
+ if (penalty.getP() == KnuthPenalty.INFINITE) {
+ int breakClass = penalty.getBreakClass();
+ if (breakClass == Constants.EN_PAGE
+ || breakClass == Constants.EN_COLUMN) {
+ considerLegalBreak(penalty, position);
+ }
+ }
+ }
+
+ /**
* Handles the footnotes cited inside a block-level box. Updates footnotesList and the
* value of totalFootnotesLength with the lengths of the given footnotes.
* @param elementLists list of KnuthElement sequences corresponding to the footnotes
@@ -244,9 +349,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
// compute the total length of the footnotes
- ListIterator elementListsIterator = elementLists.listIterator();
- while (elementListsIterator.hasNext()) {
- LinkedList noteList = (LinkedList) elementListsIterator.next();
+ for (Iterator elementListsIterator = elementLists.iterator();
+ elementListsIterator.hasNext();) {
+ final List noteList = (List) elementListsIterator.next();
//Space resolution (Note: this does not respect possible stacking constraints
//between footnotes!)
@@ -254,21 +359,23 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
int noteLength = 0;
footnotesList.add(noteList);
- ListIterator noteListIterator = noteList.listIterator();
- while (noteListIterator.hasNext()) {
- KnuthElement element = (KnuthElement) noteListIterator.next();
+ for (Iterator noteListIterator = noteList.iterator();
+ noteListIterator.hasNext();) {
+ final KnuthElement element = (KnuthElement) noteListIterator.next();
if (element.isBox() || element.isGlue()) {
noteLength += element.getW();
}
}
- int prevLength = (lengthList.size() == 0
+ int prevLength = (lengthList == null || lengthList.isEmpty())
? 0
- : ((Integer) lengthList.get(lengthList.size() - 1)).intValue());
+ : ((Integer) ListUtil.getLast(lengthList)).intValue();
+ //TODO: replace with Integer.valueOf() once we switch to Java 5
lengthList.add(new Integer(prevLength + noteLength));
totalFootnotesLength += noteLength;
}
}
+ /** {@inheritDoc} */
protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
int returnValue = super.restartFrom(restartingNode, currentIndex);
newFootnotes = false;
@@ -276,10 +383,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// remove from footnotesList the note lists that will be met
// after the restarting point
for (int j = currentIndex; j >= restartingNode.position; j--) {
- KnuthElement resettedElement = getElement(j);
- if (resettedElement instanceof KnuthBlockBox
- && ((KnuthBlockBox) resettedElement).hasAnchors()) {
- resetFootnotes(((KnuthBlockBox) resettedElement).getElementLists());
+ final KnuthElement resetElement = getElement(j);
+ if (resetElement instanceof KnuthBlockBox
+ && ((KnuthBlockBox) resetElement).hasAnchors()) {
+ resetFootnotes(((KnuthBlockBox) resetElement).getElementLists());
}
}
}
@@ -288,12 +395,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
private void resetFootnotes(List elementLists) {
for (int i = 0; i < elementLists.size(); i++) {
- /*LinkedList removedList = (LinkedList)*/footnotesList.remove(footnotesList.size() - 1);
- lengthList.remove(lengthList.size() - 1);
+ /*LinkedList removedList = (LinkedList)*/ListUtil.removeLast(footnotesList);
+ ListUtil.removeLast(lengthList);
// update totalFootnotesLength
- if (lengthList.size() > 0) {
- totalFootnotesLength = ((Integer) lengthList.get(lengthList.size() - 1)).intValue();
+ if (!lengthList.isEmpty()) {
+ totalFootnotesLength = ((Integer) ListUtil.getLast(lengthList)).intValue();
} else {
totalFootnotesLength = 0;
}
@@ -304,16 +411,72 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
+ /** {@inheritDoc} */
protected void considerLegalBreak(KnuthElement element, int elementIdx) {
+ if (element.isPenalty()) {
+ int breakClass = ((KnuthPenalty) element).getBreakClass();
+ switch (breakClass) {
+ case Constants.EN_PAGE:
+ if (this.currentKeepContext != breakClass) {
+ this.lastBeforeKeepContextSwitch = getLastTooShort();
+ }
+ this.currentKeepContext = breakClass;
+ break;
+ case Constants.EN_COLUMN:
+ if (this.currentKeepContext != breakClass) {
+ this.lastBeforeKeepContextSwitch = getLastTooShort();
+ }
+ this.currentKeepContext = breakClass;
+ break;
+ case Constants.EN_AUTO:
+ this.currentKeepContext = breakClass;
+ break;
+ default:
+ //nop
+ }
+ }
super.considerLegalBreak(element, elementIdx);
newFootnotes = false;
}
+ /** {@inheritDoc} */
+ protected boolean elementCanEndLine(KnuthElement element, int line, int difference) {
+ if (!(element.isPenalty()) || pageProvider == null) {
+ return true;
+ } else {
+ KnuthPenalty p = (KnuthPenalty) element;
+ if (p.getP() <= 0) {
+ return true;
+ } else {
+ int context = p.getBreakClass();
+ switch (context) {
+ case Constants.EN_LINE:
+ case Constants.EN_COLUMN:
+ return p.getP() < KnuthPenalty.INFINITE;
+ case Constants.EN_PAGE:
+ return p.getP() < KnuthPenalty.INFINITE
+ || !pageProvider.endPage(line - 1);
+ case Constants.EN_AUTO:
+ log.debug("keep is not auto but context is");
+ return true;
+ default:
+ if (p.getP() < KnuthPenalty.INFINITE) {
+ log.debug("Non recognized keep context:" + context);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
protected int computeDifference(KnuthNode activeNode, KnuthElement element,
int elementIndex) {
KnuthPageNode pageNode = (KnuthPageNode) activeNode;
int actualWidth = totalWidth - pageNode.totalWidth;
- int footnoteSplit;
+ int footnoteSplit = 0;
boolean canDeferOldFootnotes;
if (element.isPenalty()) {
actualWidth += element.getW();
@@ -332,7 +495,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
footnoteListIndex = footnotesList.size() - 1;
footnoteElementIndex
- = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
+ = getFootnoteList(footnoteListIndex).size() - 1;
} else if (((canDeferOldFootnotes
= checkCanDeferOldFootnotes(pageNode, elementIndex))
|| newFootnotes)
@@ -358,7 +521,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
footnoteListIndex = footnotesList.size() - 1;
footnoteElementIndex
- = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
+ = getFootnoteList(footnoteListIndex).size() - 1;
}
} else {
// all footnotes have already been placed on previous pages
@@ -375,7 +538,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
- /** Checks whether footnotes from preceding pages may be deferred to the page after
+ /**
+ * Checks whether footnotes from preceding pages may be deferred to the page after
* the given element.
* @param node active node for the preceding page break
* @param contentElementIndex index of the Knuth element considered for the
@@ -448,7 +612,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
return ((newFootnotes
&& firstNewFootnoteIndex != 0
&& (listIndex < firstNewFootnoteIndex - 1
- || elementIndex < ((LinkedList) footnotesList.get(listIndex)).size() - 1))
+ || elementIndex < getFootnoteList(listIndex).size() - 1))
|| length < totalFootnotesLength);
}
@@ -457,6 +621,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
* @param activeNode currently considered previous page break
* @param availableLength available space for footnotes
* @param canDeferOldFootnotes
+ * @return ...
*/
private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength,
boolean canDeferOldFootnotes) {
@@ -473,6 +638,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
* @param prevLength total length of footnotes inserted so far
* @param availableLength available space for footnotes on this page
* @param canDeferOldFootnotes
+ * @return ...
*/
private int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength,
int availableLength, boolean canDeferOldFootnotes) {
@@ -491,7 +657,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// already placed in a page: advance to the next element
int listIndex = prevListIndex;
int elementIndex = prevElementIndex;
- if (elementIndex == ((LinkedList) footnotesList.get(listIndex)).size() - 1) {
+ if (elementIndex == getFootnoteList(listIndex).size() - 1) {
listIndex++;
elementIndex = 0;
} else {
@@ -501,9 +667,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// try adding whole notes
if (footnotesList.size() - 1 > listIndex) {
// add the previous footnotes: these cannot be broken or deferred
- if (!canDeferOldFootnotes
- && newFootnotes
- && firstNewFootnoteIndex > 0) {
+ if (!canDeferOldFootnotes && newFootnotes && firstNewFootnoteIndex > 0) {
splitLength = ((Integer) lengthList.get(firstNewFootnoteIndex - 1)).intValue()
- prevLength;
listIndex = firstNewFootnoteIndex;
@@ -524,8 +688,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
// try adding a split of the next note
- noteListIterator = ((LinkedList) footnotesList.get(listIndex))
- .listIterator(elementIndex);
+ noteListIterator = getFootnoteList(listIndex).listIterator(elementIndex);
int prevSplitLength = 0;
int prevIndex = -1;
@@ -539,7 +702,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
prevIndex = index;
}
// get a sub-sequence from the note element list
- boolean bPrevIsBox = false;
+ boolean boxPreceding = false;
while (noteListIterator.hasNext()) {
// as this method is called only if it is not possible to insert
// all footnotes, and we have already tried (and failed) to insert
@@ -549,15 +712,15 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
if (element.isBox()) {
// element is a box
splitLength += element.getW();
- bPrevIsBox = true;
+ boxPreceding = true;
} else if (element.isGlue()) {
// element is a glue
- if (bPrevIsBox) {
+ if (boxPreceding) {
// end of the sub-sequence
index = noteListIterator.previousIndex();
break;
}
- bPrevIsBox = false;
+ boxPreceding = false;
splitLength += element.getW();
} else {
// element is a penalty
@@ -569,11 +732,13 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
}
+
// if prevSplitLength is 0, this means that the available length isn't enough
// to insert even the smallest split of the last footnote, so we cannot end a
// page here
// if prevSplitLength is > 0 we can insert some footnote content in this page
// and insert the remaining in the following one
+ //TODO: check this conditional, as the first one is always false...?
if (!somethingAdded) {
// there was not enough space to add a piece of the first new footnote
// this is not a good break
@@ -583,12 +748,13 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
footnoteListIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
footnoteElementIndex = (prevIndex != -1)
? prevIndex
- : ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
+ : getFootnoteList(footnoteListIndex).size() - 1;
}
return prevSplitLength;
}
}
+ /** {@inheritDoc} */
protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
// compute the adjustment ratio
if (difference > 0) {
@@ -618,18 +784,23 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
+ /** {@inheritDoc} */
protected double computeDemerits(KnuthNode activeNode, KnuthElement element,
int fitnessClass, double r) {
double demerits = 0;
// compute demerits
double f = Math.abs(r);
f = 1 + 100 * f * f * f;
- if (element.isPenalty() && element.getP() >= 0) {
- f += element.getP();
- demerits = f * f;
- } else if (element.isPenalty() && !element.isForcedBreak()) {
+ if (element.isPenalty()) {
double penalty = element.getP();
- demerits = f * f - penalty * penalty;
+ if (penalty >= 0) {
+ f += penalty;
+ demerits = f * f;
+ } else if (!element.isForcedBreak()) {
+ demerits = f * f - penalty * penalty;
+ } else {
+ demerits = f * f;
+ }
} else {
demerits = f * f;
}
@@ -654,7 +825,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
if (footnoteListIndex < footnotesList.size()) {
if (footnoteElementIndex
- < ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1) {
+ < getFootnoteList(footnoteListIndex).size() - 1) {
// add demerits for the footnote split between pages
demerits += splitFootnoteDemerits;
}
@@ -680,6 +851,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
private void createFootnotePages(KnuthPageNode lastNode) {
+
insertedFootnotesLength = lastNode.totalFootnotes;
footnoteListIndex = lastNode.footnoteListIndex;
footnoteElementIndex = lastNode.footnoteElementIndex;
@@ -689,18 +861,16 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// create pages containing the remaining footnote bodies
while (insertedFootnotesLength < totalFootnotesLength) {
+ final int tmpLength = ((Integer) lengthList.get(footnoteListIndex)).intValue();
// try adding some more content
- if (((Integer) lengthList.get(footnoteListIndex)).intValue() - insertedFootnotesLength
- <= availableBPD) {
+ if ((tmpLength - insertedFootnotesLength) <= availableBPD) {
// add a whole footnote
- availableBPD -= ((Integer) lengthList.get(footnoteListIndex)).intValue()
- - insertedFootnotesLength;
- insertedFootnotesLength = ((Integer)lengthList.get(footnoteListIndex)).intValue();
+ availableBPD -= tmpLength - insertedFootnotesLength;
+ insertedFootnotesLength = tmpLength;
footnoteElementIndex
- = ((LinkedList)footnotesList.get(footnoteListIndex)).size() - 1;
+ = getFootnoteList(footnoteListIndex).size() - 1;
} else if ((split = getFootnoteSplit(footnoteListIndex, footnoteElementIndex,
- insertedFootnotesLength, availableBPD, true))
- > 0) {
+ insertedFootnotesLength, availableBPD, true)) > 0) {
// add a piece of a footnote
availableBPD -= split;
insertedFootnotesLength += split;
@@ -732,12 +902,19 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
/**
- * @return a list of PageBreakPosition elements
+ * @return a list of {@link PageBreakPosition} elements
+ * corresponding to the computed page- and column-breaks
*/
public LinkedList getPageBreaks() {
return pageBreaks;
}
+ /**
+ * Insert the given {@link PageBreakPosition} as the first
+ * element in the list of page-breaks
+ *
+ * @param pageBreak the position to insert
+ */
public void insertPageBreakAsFirst(PageBreakPosition pageBreak) {
if (pageBreaks == null) {
pageBreaks = new LinkedList();
@@ -759,9 +936,11 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
+ /** {@inheritDoc} */
public void updateData1(int total, double demerits) {
}
+ /** {@inheritDoc} */
public void updateData2(KnuthNode bestActiveNode,
KnuthSequence sequence,
int total) {
@@ -807,7 +986,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex;
int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex;
if (footnotesList != null
- && firstElementIndex == ((LinkedList) footnotesList.get(firstListIndex)).size() - 1) {
+ && firstElementIndex == getFootnoteList(firstListIndex).size() - 1) {
// advance to the next list
firstListIndex++;
firstElementIndex = 0;
@@ -829,6 +1008,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
ratio, difference));
}
+ /** {@inheritDoc} */
protected int filterActiveNodes() {
// leave only the active node with fewest total demerits
KnuthNode bestActiveNode = null;
@@ -848,11 +1028,18 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
}
+ assert (bestActiveNode != null);
return bestActiveNode.line;
}
- public LinkedList getFootnoteList(int index) {
- return (LinkedList) footnotesList.get(index);
+ /**
+ * Obtain the element-list corresponding to the footnote at the given index.
+ *
+ * @param index the index in the list of footnotes
+ * @return the element-list
+ */
+ protected final List getFootnoteList(int index) {
+ return (List) footnotesList.get(index);
}
/** @return the associated top-level formatting object. */
@@ -889,4 +1076,63 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
+ /** {@inheritDoc} */
+ protected boolean ipdChanged() {
+ return ipdChange;
+ }
+
+ /** {@inheritDoc} */
+ protected int handleIpdChange() {
+ log.trace("Best node for ipd change:" + bestNodeForIPDChange);
+ // TODO finish()
+ /*
+ * The third parameter is used to determine if this is the last page, so
+ * if the content must be vertically justified or not. If we are here
+ * this means that there is further content and the next page has a
+ * different ipd. So tweak the parameter to fall into the non-last-page
+ * case.
+ */
+ calculateBreakPoints(bestNodeForIPDChange, par, bestNodeForIPDChange.line + 1);
+ activeLines = null;
+ return bestNodeForIPDChange.line;
+ }
+
+ /**
+ * Add a node at the end of the given line's existing active nodes.
+ * If this is the first node in the line, adjust endLine accordingly.
+ * @param line number of the line ending at the node's corresponding breakpoint
+ * @param node the active node to add
+ */
+ protected void addNode(int line, KnuthNode node) {
+ if (node.position < par.size() - 1 && line > 0 && ipdChange(line - 1)) {
+ log.trace("IPD changes at page " + line);
+ ipdChange = true;
+ if (bestNodeForIPDChange == null
+ || node.totalDemerits < bestNodeForIPDChange.totalDemerits) {
+ bestNodeForIPDChange = node;
+ }
+ } else {
+ if (node.position == par.size() - 1) {
+ /*
+ * The whole sequence could actually fit on the last page before
+ * the IPD change. No need to do any special handling.
+ */
+ ipdChange = false;
+ }
+ super.addNode(line, node);
+ }
+ }
+
+ KnuthNode getBestNodeBeforeIPDChange() {
+ return bestNodeForIPDChange;
+ }
+
+ /** {@inheritDoc} */
+ protected boolean ipdChange(int line) {
+ if (pageProvider == null) {
+ return false;
+ }
+ return pageProvider.ipdChange(line);
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java
index 9073c48bb..bd556366a 100644
--- a/src/java/org/apache/fop/layoutmgr/PageProvider.java
+++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java
@@ -146,6 +146,76 @@ public class PageProvider implements Constants {
return this.lastReportedBPD;
}
+ // Wish there were a more elegant way to do this in Java
+ private int[] getColIndexAndColCount(int index) {
+ int columnCount = 0;
+ int colIndex = startColumnOfCurrentElementList + index;
+ int pageIndex = -1;
+ do {
+ colIndex -= columnCount;
+ pageIndex++;
+ Page page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST);
+ columnCount = page.getPageViewport().getCurrentSpan().getColumnCount();
+ } while (colIndex >= columnCount);
+ return new int[] {colIndex, columnCount};
+ }
+
+ /**
+ * Returns true if the part following the given one has a different IPD.
+ *
+ * @param index index of the current part
+ * @return true if the following part has a different IPD, false otherwise
+ */
+ public boolean ipdChange(int index) {
+ int columnCount = 0;
+ int colIndex = startColumnOfCurrentElementList + index;
+ int pageIndex = -1;
+ Page page;
+ do {
+ colIndex -= columnCount;
+ pageIndex++;
+ page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST);
+ columnCount = page.getPageViewport().getCurrentSpan().getColumnCount();
+ } while (colIndex >= columnCount);
+ if (colIndex + 1 < columnCount) {
+ // Next part is a column on same page => same IPD
+ return false;
+ } else {
+ Page nextPage = getPage(false, pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST);
+ return page.getPageViewport().getBodyRegion().getIPD()
+ != nextPage.getPageViewport().getBodyRegion().getIPD();
+ }
+ }
+
+ /**
+ * Checks if a break at the passed index would start a new page
+ * @param index the index of the element before the break
+ * @return {@code true} if the break starts a new page
+ */
+ boolean startPage(int index) {
+ return getColIndexAndColCount(index)[0] == 0;
+ }
+
+ /**
+ * Checks if a break at the passed index would end a page
+ * @param index the index of the element before the break
+ * @return {@code true} if the break ends a page
+ */
+ boolean endPage(int index) {
+ int[] colIndexAndColCount = getColIndexAndColCount(index);
+ return colIndexAndColCount[0] == colIndexAndColCount[1] - 1;
+ }
+
+ /**
+ * Obtain the applicable column-count for the element at the
+ * passed index
+ * @param index the index of the element
+ * @return the number of columns
+ */
+ int getColumnCount(int index) {
+ return getColIndexAndColCount(index)[1];
+ }
+
/**
* Returns the part index (0<x<partCount) which denotes the first part on the last page
* generated by the current element list.
@@ -272,4 +342,4 @@ public class PageProvider implements Constants {
return page;
}
-} \ No newline at end of file
+}
diff --git a/src/java/org/apache/fop/layoutmgr/Position.java b/src/java/org/apache/fop/layoutmgr/Position.java
index 42034cab4..10ad5878e 100644
--- a/src/java/org/apache/fop/layoutmgr/Position.java
+++ b/src/java/org/apache/fop/layoutmgr/Position.java
@@ -28,6 +28,11 @@ public class Position {
layoutManager = lm;
}
+ public Position(LayoutManager lm, int index) {
+ this(lm);
+ setIndex(index);
+ }
+
public LayoutManager getLM() {
return layoutManager;
}
diff --git a/src/java/org/apache/fop/layoutmgr/SpaceResolver.java b/src/java/org/apache/fop/layoutmgr/SpaceResolver.java
index 416ee924d..ff464ce6a 100644
--- a/src/java/org/apache/fop/layoutmgr/SpaceResolver.java
+++ b/src/java/org/apache/fop/layoutmgr/SpaceResolver.java
@@ -19,7 +19,6 @@
package org.apache.fop.layoutmgr;
-import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -565,6 +564,11 @@ public class SpaceResolver {
public Position getOriginalBreakPosition() {
return this.originalPosition;
}
+
+ public Position getPosition() {
+ return originalPosition;
+ }
+
}
/**
diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
index b4941d418..d5949f4a2 100644
--- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
@@ -21,10 +21,6 @@ package org.apache.fop.layoutmgr;
import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
@@ -35,10 +31,7 @@ import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
-import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
import org.apache.fop.layoutmgr.inline.TextLayoutManager;
-import org.apache.fop.traits.MinOptMax;
-import org.apache.fop.util.ListUtil;
/**
* LayoutManager for an fo:flow object.
@@ -48,11 +41,6 @@ import org.apache.fop.util.ListUtil;
*/
public class StaticContentLayoutManager extends BlockStackingLayoutManager {
- /**
- * logging instance
- */
- private static Log log = LogFactory.getLog(StaticContentLayoutManager.class);
-
private RegionReference targetRegion;
private Block targetBlock;
private SideRegion regionFO;
@@ -89,96 +77,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
/** {@inheritDoc} */
public List getNextKnuthElements(LayoutContext context, int alignment) {
- if (true) {
- throw new UnsupportedOperationException(
- "Shouldn't this method be emptied because it's never called at all?");
- }
- //TODO Empty this method?!?
- // set layout dimensions
- setContentAreaIPD(context.getRefIPD());
- setContentAreaBPD(context.getStackLimitBP().opt);
-
- //TODO Copied from elsewhere. May be worthwhile to factor out the common parts.
- // currently active LM
- BlockLevelLayoutManager curLM;
- BlockLevelLayoutManager prevLM = null;
- MinOptMax stackSize = new MinOptMax();
- List returnedList;
- List returnList = new LinkedList();
-
- while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
- if (curLM instanceof InlineLevelLayoutManager) {
- log.error("inline area not allowed under flow - ignoring");
- curLM.setFinished(true);
- continue;
- }
-
- // Set up a LayoutContext
- MinOptMax bpd = context.getStackLimitBP();
-
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize));
- childLC.setRefIPD(context.getRefIPD());
-
- // get elements from curLM
- returnedList = curLM.getNextKnuthElements(childLC, alignment);
- //log.debug("FLM.getNextKnuthElements> returnedList.size() = "
- // + returnedList.size());
-
- // "wrap" the Position inside each element
- List tempList = returnedList;
- KnuthElement tempElement;
- returnedList = new LinkedList();
- ListIterator listIter = tempList.listIterator();
- while (listIter.hasNext()) {
- tempElement = (KnuthElement)listIter.next();
- tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
- returnedList.add(tempElement);
- }
-
- if (returnedList.size() == 1
- && ((KnuthElement)returnedList.get(0)).isPenalty()
- && ((KnuthPenalty)returnedList.get(0)).getP() == -KnuthElement.INFINITE) {
- // a descendant of this flow has break-before
- returnList.addAll(returnedList);
- return returnList;
- } else {
- if (!returnList.isEmpty()) {
- // there is a block before this one
- if (prevLM.mustKeepWithNext()
- || curLM.mustKeepWithPrevious()) {
- // add an infinite penalty to forbid a break between blocks
- returnList.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false,
- new Position(this), false));
- } else if (!((KnuthElement) ListUtil.getLast(returnList))
- .isGlue()) {
- // add a null penalty to allow a break between blocks
- returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
- }
- }
-/*LF*/ if (!returnedList.isEmpty()) { // controllare!
- returnList.addAll(returnedList);
- final KnuthElement last = (KnuthElement) ListUtil
- .getLast(returnedList);
- if (last.isPenalty()
- && ((KnuthPenalty) last).getP() == -KnuthElement.INFINITE) {
- // a descendant of this flow has break-after
-/*LF*/ //log.debug("FLM - break after!!");
- return returnList;
- }
-/*LF*/ }
- }
- prevLM = curLM;
- }
-
- setFinished(true);
-
- if (returnList.isEmpty()) {
- return null;
- } else {
- return returnList;
- }
+ throw new IllegalStateException();
}
/**
@@ -415,18 +314,18 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- return KEEP_AUTO;
+ public Keep getKeepTogether() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
}
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
index a5247d652..95f798161 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
@@ -27,6 +27,7 @@ import java.util.ListIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.LineArea;
@@ -43,7 +44,6 @@ import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.SpaceSpecifier;
-import org.apache.fop.traits.MinOptMax;
/**
* Content Layout Manager.
@@ -111,8 +111,6 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA);
childLC.setLeadingSpace(new SpaceSpecifier(false));
childLC.setTrailingSpace(new SpaceSpecifier(false));
- // set stackLimit for lines
- childLC.setStackLimitIP(new MinOptMax(ipd));
childLC.setRefIPD(ipd);
int lineHeight = 14000;
@@ -129,8 +127,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
stackSize = 0;
- List contentList =
- getNextKnuthElements(childLC, Constants.EN_START);
+ List contentList = getNextKnuthElements(childLC, Constants.EN_START);
ListIterator contentIter = contentList.listIterator();
while (contentIter.hasNext()) {
KnuthElement element = (KnuthElement) contentIter.next();
@@ -149,8 +146,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
lc.setLeadingSpace(new SpaceSpecifier(false));
lc.setTrailingSpace(new SpaceSpecifier(false));
- KnuthPossPosIter contentPosIter =
- new KnuthPossPosIter(contentList, 0, contentList.size());
+ KnuthPossPosIter contentPosIter = new KnuthPossPosIter(contentList, 0, contentList.size());
curLM.addAreas(contentPosIter, lc);
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java b/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java
index bacf43dbc..338a58d9c 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java
@@ -172,15 +172,15 @@ public class ImageLayout implements Constants {
}
this.clip = false;
- if (cwidth > ipd || cheight > bpd) {
- int overflow = props.getOverflow();
- if (overflow == EN_HIDDEN) {
- this.clip = true;
- } else if (overflow == EN_ERROR_IF_OVERFLOW) {
+ int overflow = props.getOverflow();
+ if (overflow == EN_HIDDEN) {
+ this.clip = true;
+ } else if (overflow == EN_ERROR_IF_OVERFLOW) {
+ if (cwidth > ipd || cheight > bpd) {
//TODO Don't use logging to report error!
log.error("Object overflows the viewport: clipping");
- this.clip = true;
}
+ this.clip = true;
}
int xoffset = computeXOffset(ipd, cwidth);
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
index 0c332281f..6e0c34a82 100755
--- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
@@ -248,8 +248,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
List returnList = new LinkedList();
KnuthSequence lastSequence = null;
- SpaceSpecifier leadingSpace = context.getLeadingSpace();
-
if (fobj instanceof Title) {
alignmentContext = new AlignmentContext(font,
lineHeight.getOptimum(this).getLength().getValue(this),
@@ -274,14 +272,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
if (getSpaceStart() != null) {
context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
}
-
- // Check for "fence"
- if (hasLeadingFence(!context.isFirstArea())) {
- // Reset leading space sequence for child areas
- leadingSpace = new SpaceSpecifier(false);
- }
- // Reset state variables
- clearPrevIPD(); // Clear stored prev content dimensions
}
StringBuffer trace = new StringBuffer("InlineLM:");
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java
index 963b98b37..65e59554f 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java
@@ -19,12 +19,13 @@
package org.apache.fop.layoutmgr.inline;
-import java.util.LinkedList;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
-import java.util.HashMap;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.inline.Space;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.properties.SpaceProperty;
import org.apache.fop.layoutmgr.AbstractLayoutManager;
@@ -34,8 +35,6 @@ import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.inline.Space;
import org.apache.fop.traits.MinOptMax;
/**
@@ -62,12 +61,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
}
}
-
- /**
- * Size of any start or end borders and padding.
- */
- private MinOptMax allocIPD = new MinOptMax(0);
-
/**
* Size of border and padding in BPD (ie, before and after).
*/
@@ -78,9 +71,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
/** The child layout context */
protected LayoutContext childLC;
- /** Used to store previous content IPD for each child LM. */
- private HashMap hmPrevIPD = new HashMap();
-
/**
* Create an inline stacking layout manager.
* This is used for fo's that create areas that
@@ -149,22 +139,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
}
/**
- * TODO: Explain this method
- * @param lm ???
- * @return ???
- */
- protected MinOptMax getPrevIPD(LayoutManager lm) {
- return (MinOptMax) hmPrevIPD.get(lm);
- }
-
- /**
- * Clear the previous IPD calculation.
- */
- protected void clearPrevIPD() {
- hmPrevIPD.clear();
- }
-
- /**
* Returns the current area.
* @return the current area
*/
diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
index 3a0672f4e..7c30ab9bb 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
@@ -20,6 +20,7 @@
package org.apache.fop.layoutmgr.inline;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -36,6 +37,7 @@ import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.Block;
import org.apache.fop.fo.properties.CommonHyphenation;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
@@ -46,7 +48,7 @@ import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.BreakingAlgorithm;
import org.apache.fop.layoutmgr.ElementListObserver;
import org.apache.fop.layoutmgr.InlineKnuthSequence;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBlockBox;
import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthElement;
@@ -115,8 +117,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager
* inline break positions.
*/
private static class LineBreakPosition extends LeafPosition {
- private int iParIndex; // index of the Paragraph this Position refers to
- private int iStartIndex; //index of the first element this Position refers to
+ private int parIndex; // index of the Paragraph this Position refers to
+ private int startIndex; //index of the first element this Position refers to
private int availableShrink;
private int availableStretch;
private int difference;
@@ -129,16 +131,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager
private int spaceAfter;
private int baseline;
- LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
+ LineBreakPosition(LayoutManager lm, int index, int startIndex, int breakIndex,
int shrink, int stretch, int diff,
double ipdA, double adjust, int ind,
int lh, int lw, int sb, int sa, int bl) {
- super(lm, iBreakIndex);
+ super(lm, breakIndex);
availableShrink = shrink;
availableStretch = stretch;
difference = diff;
- iParIndex = index;
- this.iStartIndex = iStartIndex;
+ parIndex = index;
+ this.startIndex = startIndex;
ipdAdjust = ipdA;
dAdjust = adjust;
startIndent = ind;
@@ -166,18 +168,18 @@ public class LineLayoutManager extends InlineStackingLayoutManager
private Length lineHeight;
private int lead;
private int follow;
- private AlignmentContext alignmentContext = null;
+ private AlignmentContext alignmentContext;
- private List knuthParagraphs = null;
- private int iReturnedLBP = 0;
-
- // parameters of Knuth's algorithm:
- // penalty value for flagged penalties
- private int flaggedPenalty = 50;
+ private List knuthParagraphs;
private LineLayoutPossibilities lineLayouts;
private List lineLayoutsList;
- private int iLineWidth = 0;
+ private int ipd = 0;
+ /**
+ * When layout must be re-started due to a change of IPD, there is no need
+ * to perform hyphenation on the remaining Knuth sequence once again.
+ */
+ private boolean hyphenationPerformed;
/**
* this constant is used to create elements when text-align is center:
@@ -237,7 +239,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
} else {
lineFiller = new MinOptMax(lastLineEndIndent,
lastLineEndIndent,
- layoutManager.iLineWidth);
+ layoutManager.ipd);
}
// add auxiliary elements at the beginning of the paragraph
@@ -318,11 +320,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager
private int activePossibility;
private int addedPositions;
private int textIndent;
- private int fillerMinWidth;
private int lineHeight;
private int lead;
private int follow;
- private int maxDiff;
private static final double MAX_DEMERITS = 10e6;
public LineBreakingAlgorithm (int pageAlign,
@@ -333,22 +333,17 @@ public class LineLayoutManager extends InlineStackingLayoutManager
super(textAlign, textAlignLast, first, false, maxFlagCount);
pageAlignment = pageAlign;
textIndent = indent;
- fillerMinWidth = fillerWidth;
lineHeight = lh;
lead = ld;
follow = fl;
thisLLM = llm;
activePossibility = -1;
- maxDiff = fobj.getWidows() >= fobj.getOrphans()
- ? fobj.getWidows()
- : fobj.getOrphans();
}
public void updateData1(int lineCount, double demerits) {
lineLayouts.addPossibility(lineCount, demerits);
- if (super.log.isTraceEnabled()) {
- super.log.trace(
- "Layout possibility in " + lineCount + " lines; break at position:");
+ if (log.isTraceEnabled()) {
+ log.trace("Layout possibility in " + lineCount + " lines; break at position:");
}
}
@@ -362,7 +357,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
indent += (textAlign == Constants.EN_CENTER)
? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
- indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0;
+ indent += (bestActiveNode.line == 1 && indentFirstPart && isFirstInBlock) ? textIndent : 0;
double ratio = (textAlign == Constants.EN_JUSTIFY
|| difference < 0 && -difference <= bestActiveNode.availableShrink)
? bestActiveNode.adjustRatio : 0;
@@ -429,7 +424,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
// true if this line contains only zero-height, auxiliary boxes
// and the actual line width is 0; in this case, the line "collapses"
// i.e. the line area will have bpd = 0
- boolean bZeroHeightLine = (difference == iLineWidth);
+ boolean bZeroHeightLine = (difference == ipd);
// if line-stacking-strategy is "font-height", the line height
// is not affected by its content
@@ -485,7 +480,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
firstElementIndex, lastElementIndex,
availableShrink, availableStretch,
difference, ratio, 0, indent,
- 0, iLineWidth, 0, 0, 0);
+ 0, ipd, 0, 0, 0);
} else {
return new LineBreakPosition(thisLLM,
knuthParagraphs.indexOf(par),
@@ -493,18 +488,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager
availableShrink, availableStretch,
difference, ratio, 0, indent,
lineLead + lineFollow,
- iLineWidth, spaceBefore, spaceAfter,
+ ipd, spaceBefore, spaceAfter,
lineLead);
}
}
- public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
- double threshold, boolean force,
- int allowedBreaks) {
- return super.findBreakingPoints(par, /*lineWidth,*/
- threshold, force, allowedBreaks);
- }
-
protected int filterActiveNodes() {
KnuthNode bestActiveNode = null;
@@ -577,13 +565,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager
FontInfo fi = fobj.getFOEventHandler().getFontInfo();
FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi);
Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this));
- alignmentContext
- = new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode());
+ alignmentContext = new AlignmentContext(fs, lineHeight.getValue(this),
+ context.getWritingMode());
context.setAlignmentContext(alignmentContext);
- // Get a break from currently active child LM
- // Set up constraints for inline level managers
-
- clearPrevIPD();
+ ipd = context.getRefIPD();
//PHASE 1: Create Knuth elements
if (knuthParagraphs == null) {
@@ -605,34 +590,33 @@ public class LineLayoutManager extends InlineStackingLayoutManager
//PHASE 2: Create line breaks
return createLineBreaks(context.getBPAlignment(), context);
- /*
- LineBreakPosition lbp = null;
- if (breakpoints == null) {
- // find the optimal line breaking points for each paragraph
- breakpoints = new ArrayList();
- ListIterator paragraphsIterator
- = knuthParagraphs.listIterator(knuthParagraphs.size());
- Paragraph currPar = null;
- while (paragraphsIterator.hasPrevious()) {
- currPar = (Paragraph) paragraphsIterator.previous();
- findBreakingPoints(currPar, context.getStackLimit().opt);
- }
- }*/
+ }
- //PHASE 3: Return lines
+ public List getNextKnuthElements(LayoutContext context, int alignment,
+ LeafPosition restartPosition) {
+ log.trace("Restarting line breaking from index " + restartPosition.getIndex());
+ int parIndex = restartPosition.getLeafPos();
+ Paragraph paragraph = (Paragraph) knuthParagraphs.get(parIndex);
+ for (int i = 0; i <= restartPosition.getIndex(); i++) {
+ paragraph.remove(0);
+ }
+ Iterator iter = paragraph.iterator();
+ while (iter.hasNext() && !((KnuthElement) iter.next()).isBox()) {
+ iter.remove();
+ }
+ if (!iter.hasNext()) {
+ knuthParagraphs.remove(parIndex);
+ }
- /*
- // get a break point from the list
- lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
- if (iReturnedLBP == breakpoints.size()) {
+ // return finished when there's no content
+ if (knuthParagraphs.size() == 0) {
setFinished(true);
+ return null;
}
- BreakPoss curLineBP = new BreakPoss(lbp);
- curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
- curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
- return curLineBP;
- */
+ ipd = context.getRefIPD();
+ //PHASE 2: Create line breaks
+ return createLineBreaks(context.getBPAlignment(), context);
}
/**
@@ -642,22 +626,18 @@ public class LineLayoutManager extends InlineStackingLayoutManager
private void collectInlineKnuthElements(LayoutContext context) {
LayoutContext inlineLC = new LayoutContext(context);
- InlineLevelLayoutManager curLM;
- List returnedList = null;
- iLineWidth = context.getStackLimitIP().opt;
-
// convert all the text in a sequence of paragraphs made
// of KnuthBox, KnuthGlue and KnuthPenalty objects
- boolean bPrevWasKnuthBox = false;
+ boolean previousIsBox = false;
StringBuffer trace = new StringBuffer("LineLM:");
Paragraph lastPar = null;
+ InlineLevelLayoutManager curLM;
while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
- returnedList = curLM.getNextKnuthElements(inlineLC, effectiveAlignment);
- if (returnedList == null
- || returnedList.size() == 0) {
+ List inlineElements = curLM.getNextKnuthElements(inlineLC, effectiveAlignment);
+ if (inlineElements == null || inlineElements.size() == 0) {
/* curLM.getNextKnuthElements() returned null or an empty list;
* this can happen if there is nothing more to layout,
* so just iterate once more to see if there are other children */
@@ -665,7 +645,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
}
if (lastPar != null) {
- KnuthSequence firstSeq = (KnuthSequence) returnedList.get(0);
+ KnuthSequence firstSeq = (KnuthSequence) inlineElements.get(0);
// finish last paragraph before a new block sequence
if (!firstSeq.isInlineSequence()) {
@@ -675,7 +655,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
if (log.isTraceEnabled()) {
trace.append(" ]");
}
- bPrevWasKnuthBox = false;
+ previousIsBox = false;
}
// does the first element of the first paragraph add to an existing word?
@@ -683,27 +663,24 @@ public class LineLayoutManager extends InlineStackingLayoutManager
KnuthElement thisElement;
thisElement = (KnuthElement) firstSeq.get(0);
if (thisElement.isBox() && !thisElement.isAuxiliary()
- && bPrevWasKnuthBox) {
+ && previousIsBox) {
lastPar.addALetterSpace();
}
}
}
// loop over the KnuthSequences (and single KnuthElements) in returnedList
- ListIterator iter = returnedList.listIterator();
+ ListIterator iter = inlineElements.listIterator();
while (iter.hasNext()) {
KnuthSequence sequence = (KnuthSequence) iter.next();
// the sequence contains inline Knuth elements
if (sequence.isInlineSequence()) {
// look at the last element
ListElement lastElement = sequence.getLast();
- if (lastElement == null) {
- throw new NullPointerException(
- "Sequence was empty! lastElement is null");
- }
- bPrevWasKnuthBox = lastElement.isBox()
- && !((KnuthElement) lastElement).isAuxiliary()
- && ((KnuthElement) lastElement).getW() != 0;
+ assert lastElement != null;
+ previousIsBox = lastElement.isBox()
+ && !((KnuthElement) lastElement).isAuxiliary()
+ && ((KnuthElement) lastElement).getW() != 0;
// if last paragraph is open, add the new elements to the paragraph
// else this is the last paragraph
@@ -728,8 +705,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
// finish last paragraph if it was closed with a linefeed
if (lastElement.isPenalty()
- && ((KnuthPenalty) lastElement).getP()
- == -KnuthPenalty.INFINITE) {
+ && ((KnuthPenalty) lastElement).getP() == -KnuthPenalty.INFINITE) {
// a penalty item whose value is -inf
// represents a preserved linefeed,
// which forces a line break
@@ -737,7 +713,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
if (!lastPar.containsBox()) {
//only a forced linefeed on this line
//-> compensate with an auxiliary glue
- lastPar.add(new KnuthGlue(iLineWidth, 0, iLineWidth, null, true));
+ lastPar.add(new KnuthGlue(ipd, 0, ipd, null, true));
}
lastPar.endParagraph();
ElementListObserver.observe(lastPar, "line", null);
@@ -745,7 +721,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
if (log.isTraceEnabled()) {
trace.append(" ]");
}
- bPrevWasKnuthBox = false;
+ previousIsBox = false;
}
} else { // the sequence is a block sequence
// the positions will be wrapped with this LM in postProcessLineBreaks
@@ -767,144 +743,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager
}
/**
- * Find a set of breaking points.
- * This method is called only once by getNextBreakPoss, and it
- * subsequently calls the other findBreakingPoints() method with
- * different parameters, until a set of breaking points is found.
- *
- * @param par the list of elements that must be parted
- * into lines
- * @param lineWidth the desired length ot the lines
- */
- /*
- private void findBreakingPoints(Paragraph par, int lineWidth) {
- // maximum adjustment ratio permitted
- float maxAdjustment = 1;
-
- // first try
- if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
- // the first try failed, now try something different
- log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
- if (hyphenationProperties.hyphenate == Constants.EN_TRUE) {
- // consider every hyphenation point as a legal break
- findHyphenationPoints(par);
- } else {
- // try with a higher threshold
- maxAdjustment = 5;
- }
-
- if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
- // the second try failed too, try with a huge threshold;
- // if this fails too, use a different algorithm
- log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
- + (hyphenationProperties.hyphenate == Constants.EN_TRUE ? " and hyphenation" : ""));
- maxAdjustment = 20;
- if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) {
- log.debug("No set of breaking points found, using first-fit algorithm");
- }
- }
- }
- }
-
- private boolean findBreakingPoints(Paragraph par, int lineWidth,
- double threshold, boolean force) {
- KnuthParagraph knuthPara = new KnuthParagraph(par);
- int lines = knuthPara.findBreakPoints(lineWidth, threshold, force);
- if (lines == 0) {
- return false;
- }
-
- for (int i = lines-1; i >= 0; i--) {
- int line = i+1;
- if (log.isTraceEnabled()) {
- log.trace("Making line from " + knuthPara.getStart(i) + " to " +
- knuthPara.getEnd(i));
- }
- // compute indent and adjustment ratio, according to
- // the value of text-align and text-align-last
-
- int difference = knuthPara.getDifference(i);
- if (line == lines) {
- difference += par.lineFillerWidth;
- }
- int textAlign = (line < lines)
- ? textAlignment : textAlignmentLast;
- int indent = (textAlign == EN_CENTER)
- ? difference / 2
- : (textAlign == EN_END) ? difference : 0;
- indent += (line == 1 && knuthParagraphs.indexOf(par) == 0)
- ? textIndent.getValue(this) : 0;
- double ratio = (textAlign == EN_JUSTIFY)
- ? knuthPara.getAdjustRatio(i) : 0;
-
- int start = knuthPara.getStart(i);
- int end = knuthPara.getEnd(i);
- makeLineBreakPosition(par, start, end, 0, ratio, indent);
- }
- return true;
- }
-
- private void makeLineBreakPosition(Paragraph par,
- int firstElementIndex, int lastElementIndex,
- int insertIndex, double ratio, int indent) {
- // line height calculation
-
- int halfLeading = (lineHeight - lead - follow) / 2;
- // height above the main baseline
- int lineLead = lead + halfLeading;
- // maximum size of top and bottom alignment
- int lineFollow = follow + halfLeading;
-
- ListIterator inlineIterator
- = par.listIterator(firstElementIndex);
- for (int j = firstElementIndex;
- j <= lastElementIndex;
- j++) {
- KnuthElement element = (KnuthElement) inlineIterator.next();
- if (element.isBox()) {
- KnuthInlineBox box = (KnuthInlineBox)element;
- if (box.getLead() > lineLead) {
- lineLead = box.getLead();
- }
- if (box.getTotal() > lineFollow) {
- lineFollow = box.getTotal();
- }
- if (box.getMiddle() > lineLead + middleShift) {
- lineLead += box.getMiddle()
- - lineLead - middleShift;
- }
- if (box.getMiddle() > middlefollow - middleShift) {
- middlefollow += box.getMiddle()
- - middlefollow + middleShift;
- }
- }
- }
-
- if (lineFollow - lineLead > middlefollow) {
- middlefollow = lineFollow - lineLead;
- }
-
- breakpoints.add(insertIndex,
- new LineBreakPosition(this,
- knuthParagraphs.indexOf(par),
- lastElementIndex ,
- ratio, 0, indent,
- lineLead + middlefollow,
- lineLead));
- }*/
-
-
- /**
* Phase 2 of Knuth algorithm: find optimal break points.
* @param alignment alignment in BP direction of the paragraph
* @param context the layout context
* @return a list of Knuth elements representing broken lines
*/
private List createLineBreaks(int alignment, LayoutContext context) {
-
// find the optimal line breaking points for each paragraph
- ListIterator paragraphsIterator
- = knuthParagraphs.listIterator(knuthParagraphs.size());
+ ListIterator paragraphsIterator = knuthParagraphs.listIterator(knuthParagraphs.size());
lineLayoutsList = new ArrayList(knuthParagraphs.size());
LineLayoutPossibilities llPoss;
while (paragraphsIterator.hasPrevious()) {
@@ -946,7 +792,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager
this);
if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE
- && fobj.getWrapOption() != EN_NO_WRAP) {
+ && fobj.getWrapOption() != EN_NO_WRAP && !hyphenationPerformed) {
+ hyphenationPerformed = true;
findHyphenationPoints(currPar);
}
@@ -957,7 +804,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
} else {
allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES;
}
- alg.setConstantLineWidth(iLineWidth);
+ alg.setConstantLineWidth(ipd);
iBPcount = alg.findBreakingPoints(currPar,
maxAdjustment, false, allowedBreaks);
if (iBPcount == 0 || alignment == EN_JUSTIFY) {
@@ -1013,26 +860,26 @@ public class LineLayoutManager extends InlineStackingLayoutManager
alg.resetAlgorithm();
lineLayouts.savePossibilities(true);
// try with shorter lines
- int savedLineWidth = iLineWidth;
- iLineWidth = (int) (iLineWidth * 0.95);
+ int savedLineWidth = ipd;
+ ipd = (int) (ipd * 0.95);
iBPcount = alg.findBreakingPoints(currPar,
- maxAdjustment, true, allowedBreaks);
+ maxAdjustment, true, allowedBreaks);
// use normal lines, when possible
lineLayouts.restorePossibilities();
- iLineWidth = savedLineWidth;
+ ipd = savedLineWidth;
}
if (!lineLayouts.canUseLessLines()) {
alg.resetAlgorithm();
lineLayouts.savePossibilities(true);
// try with longer lines
- int savedLineWidth = iLineWidth;
- iLineWidth = (int) (iLineWidth * 1.05);
- alg.setConstantLineWidth(iLineWidth);
+ int savedLineWidth = ipd;
+ ipd = (int) (ipd * 1.05);
+ alg.setConstantLineWidth(ipd);
iBPcount = alg.findBreakingPoints(currPar,
maxAdjustment, true, allowedBreaks);
// use normal lines, when possible
lineLayouts.restorePossibilities();
- iLineWidth = savedLineWidth;
+ ipd = savedLineWidth;
}
//log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
//log.debug(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
@@ -1051,15 +898,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager
List returnList = new LinkedList();
+ int endIndex = -1;
for (int p = 0; p < knuthParagraphs.size(); p++) {
// penalty between paragraphs
if (p > 0) {
- int strength = getKeepTogetherStrength();
- int penalty = KeepUtil.getPenaltyForKeep(strength);
- if (penalty < KnuthElement.INFINITE) {
- returnList.add(new BreakElement(
- new Position(this), penalty, context));
- }
+ Keep keep = getKeepTogether();
+ returnList.add(new BreakElement(
+ new Position(this),
+ keep.getPenalty(),
+ keep.getContext(),
+ context));
}
LineLayoutPossibilities llPoss;
@@ -1088,7 +936,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager
} else {
/* "normal" vertical alignment: create a sequence whose boxes
represent effective lines, and contain LineBreakPositions */
- Position returnPosition = new LeafPosition(this, p);
int startIndex = 0;
for (int i = 0;
i < llPoss.getChosenLineCount();
@@ -1098,15 +945,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager
&& i >= fobj.getOrphans()
&& i <= llPoss.getChosenLineCount() - fobj.getWidows()) {
// penalty allowing a page break between lines
- int strength = getKeepTogetherStrength();
- int penalty = KeepUtil.getPenaltyForKeep(strength);
- if (penalty < KnuthElement.INFINITE) {
- returnList.add(new BreakElement(
- returnPosition, penalty, context));
- }
+ Keep keep = getKeepTogether();
+ returnList.add(new BreakElement(
+ new LeafPosition(this, p, endIndex),
+ keep.getPenalty(),
+ keep.getContext(),
+ context));
}
- int endIndex
- = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos();
+ endIndex = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos();
// create a list of the FootnoteBodyLM handling footnotes
// whose citations are in this line
List footnoteList = new LinkedList();
@@ -1114,15 +960,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager
while (elementIterator.nextIndex() <= endIndex) {
KnuthElement element = (KnuthElement) elementIterator.next();
if (element instanceof KnuthInlineBox
- && ((KnuthInlineBox) element).isAnchor()) {
+ && ((KnuthInlineBox) element).isAnchor()) {
footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
} else if (element instanceof KnuthBlockBox) {
footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs());
}
}
startIndex = endIndex + 1;
- LineBreakPosition lbp
- = (LineBreakPosition) llPoss.getChosenPosition(i);
+ LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i);
returnList.add(new KnuthBlockBox
(lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter,
footnoteList, lbp, false));
@@ -1283,28 +1128,43 @@ public class LineLayoutManager extends InlineStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- return ((BlockLevelLayoutManager) getParent()).getKeepTogetherStrength();
+ public KeepProperty getKeepTogetherProperty() {
+ return ((BlockLevelLayoutManager) getParent()).getKeepTogetherProperty();
+ }
+
+ /** {@inheritDoc} */
+ public KeepProperty getKeepWithPreviousProperty() {
+ return ((BlockLevelLayoutManager) getParent()).getKeepWithPreviousProperty();
+ }
+
+ /** {@inheritDoc} */
+ public KeepProperty getKeepWithNextProperty() {
+ return ((BlockLevelLayoutManager) getParent()).getKeepWithNextProperty();
+ }
+
+ /** {@inheritDoc} */
+ public Keep getKeepTogether() {
+ return ((BlockLevelLayoutManager) getParent()).getKeepTogether();
}
/** {@inheritDoc} */
public boolean mustKeepWithPrevious() {
- return getKeepWithPreviousStrength() > KEEP_AUTO;
+ return !getKeepWithPrevious().isAuto();
}
/** {@inheritDoc} */
public boolean mustKeepWithNext() {
- return getKeepWithNextStrength() > KEEP_AUTO;
+ return !getKeepWithNext().isAuto();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
@@ -1409,6 +1269,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
break;
}
//TODO Something's not right here. See block_hyphenation_linefeed_preserve.xml
+ //for more info: see also https://issues.apache.org/bugzilla/show_bug.cgi?id=38264
// collect word fragments, ignoring auxiliary elements;
// each word fragment was created by a different TextLM
@@ -1580,7 +1441,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
Position pos = (Position) parentIter.next();
boolean isLastPosition = !parentIter.hasNext();
if (pos instanceof LineBreakPosition) {
- addInlineArea(context, pos, isLastPosition);
+ addInlineArea(context, (LineBreakPosition) pos, isLastPosition);
} else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) {
addBlockArea(context, pos, isLastPosition);
} else {
@@ -1600,147 +1461,129 @@ public class LineLayoutManager extends InlineStackingLayoutManager
* @param pos the position for which the line is generated
* @param isLastPosition true if this is the last position of this LM
*/
- private void addInlineArea(LayoutContext context, Position pos, boolean isLastPosition) {
- ListIterator seqIterator = null;
- KnuthElement tempElement = null;
- // the TLM which created the last KnuthElement in this line
- LayoutManager lastLM = null;
-
- LineBreakPosition lbp = (LineBreakPosition) pos;
- int iCurrParIndex;
- iCurrParIndex = lbp.iParIndex;
- KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex);
- int iStartElement = lbp.iStartIndex;
- int iEndElement = lbp.getLeafPos();
-
- LineArea lineArea
- = new LineArea((lbp.getLeafPos() < seq.size() - 1
- ? textAlignment : textAlignmentLast),
- lbp.difference, lbp.availableStretch, lbp.availableShrink);
- if (lbp.startIndent != 0) {
- lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent));
- }
- lineArea.setBPD(lbp.lineHeight);
- lineArea.setIPD(lbp.lineWidth);
- lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore));
- lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter));
- alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
-
- if (seq instanceof Paragraph) {
- Paragraph currPar = (Paragraph) seq;
- // ignore the first elements added by the LineLayoutManager
- iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
-
- // if this is the last line area that for this paragraph,
- // ignore the last elements added by the LineLayoutManager and
- // subtract the last-line-end-indent from the area ipd
- if (iEndElement == (currPar.size() - 1)) {
- iEndElement -= currPar.ignoreAtEnd;
- lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
- }
+ private void addInlineArea(LayoutContext context, LineBreakPosition lbp,
+ boolean isLastPosition) {
+ // the TLM which created the last KnuthElement in this line
+ LayoutManager lastLM = null;
+
+ KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(lbp.parIndex);
+ int startElementIndex = lbp.startIndex;
+ int endElementIndex = lbp.getLeafPos();
+
+ LineArea lineArea = new LineArea(
+ (lbp.getLeafPos() < seq.size() - 1 ? textAlignment : textAlignmentLast),
+ lbp.difference, lbp.availableStretch, lbp.availableShrink);
+ if (lbp.startIndent != 0) {
+ lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent));
+ }
+ lineArea.setBPD(lbp.lineHeight);
+ lineArea.setIPD(lbp.lineWidth);
+ lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore));
+ lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter));
+ alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
+
+ if (seq instanceof Paragraph) {
+ Paragraph currPar = (Paragraph) seq;
+ // ignore the first elements added by the LineLayoutManager
+ startElementIndex += (startElementIndex == 0) ? currPar.ignoreAtStart : 0;
+
+ // if this is the last line area that for this paragraph,
+ // ignore the last elements added by the LineLayoutManager and
+ // subtract the last-line-end-indent from the area ipd
+ if (endElementIndex == (currPar.size() - 1)) {
+ endElementIndex -= currPar.ignoreAtEnd;
+ lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
}
+ }
- // Remove trailing spaces if allowed so
- if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
+ // Remove trailing spaces if allowed so
+ if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
|| whiteSpaceTreament == EN_IGNORE
|| whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) {
- // ignore the last element in the line if it is a KnuthGlue object
- seqIterator = seq.listIterator(iEndElement);
- tempElement = (KnuthElement) seqIterator.next();
- if (tempElement.isGlue()) {
- iEndElement--;
- // this returns the same KnuthElement
- seqIterator.previous();
- if (seqIterator.hasPrevious()) {
- tempElement = (KnuthElement) seqIterator.previous();
- } else {
- tempElement = null;
- }
- }
- if (tempElement != null) {
- lastLM = tempElement.getLayoutManager();
+ // ignore the last element in the line if it is a KnuthGlue object
+ ListIterator seqIterator = seq.listIterator(endElementIndex);
+ KnuthElement lastElement = (KnuthElement) seqIterator.next();
+ lastLM = lastElement.getLayoutManager();
+ if (lastElement.isGlue()) {
+ endElementIndex--;
+ // this returns the same KnuthElement
+ seqIterator.previous();
+ if (seqIterator.hasPrevious()) {
+ lastLM = ((KnuthElement) seqIterator.previous()).getLayoutManager();
}
}
+ }
- // Remove leading spaces if allowed so
- if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
+ // Remove leading spaces if allowed so
+ if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
|| whiteSpaceTreament == EN_IGNORE
|| whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) {
- // ignore KnuthGlue and KnuthPenalty objects
- // at the beginning of the line
- seqIterator = seq.listIterator(iStartElement);
- tempElement = (KnuthElement) seqIterator.next();
- while (!tempElement.isBox() && seqIterator.hasNext()) {
- tempElement = (KnuthElement) seqIterator.next();
- iStartElement++;
- }
- }
- // Add the inline areas to lineArea
- PositionIterator inlinePosIter
- = new KnuthPossPosIter(seq, iStartElement, iEndElement + 1);
-
- iStartElement = lbp.getLeafPos() + 1;
- if (iStartElement == seq.size()) {
- // advance to next paragraph
- iStartElement = 0;
+ // ignore KnuthGlue and KnuthPenalty objects
+ // at the beginning of the line
+ ListIterator seqIterator = seq.listIterator(startElementIndex);
+ while (seqIterator.hasNext() && !((KnuthElement) seqIterator.next()).isBox()) {
+ startElementIndex++;
}
+ }
+ // Add the inline areas to lineArea
+ PositionIterator inlinePosIter = new KnuthPossPosIter(seq, startElementIndex,
+ endElementIndex + 1);
- LayoutContext lc = new LayoutContext(0);
- lc.setAlignmentContext(alignmentContext);
- lc.setSpaceAdjust(lbp.dAdjust);
- lc.setIPDAdjust(lbp.ipdAdjust);
- lc.setLeadingSpace(new SpaceSpecifier(true));
- lc.setTrailingSpace(new SpaceSpecifier(false));
- lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
-
- /*
- * extension (not in the XSL FO recommendation): if the left and right margins
- * have been optimized, recompute indents and / or adjust ratio, according
- * to the paragraph horizontal alignment
- */
- if (false && textAlignment == EN_JUSTIFY) {
- // re-compute space adjust ratio
- int updatedDifference = context.getStackLimitIP().opt
- - lbp.lineWidth + lbp.difference;
- double updatedRatio = 0.0;
- if (updatedDifference > 0) {
- updatedRatio = (float) updatedDifference / lbp.availableStretch;
- } else if (updatedDifference < 0) {
- updatedRatio = (float) updatedDifference / lbp.availableShrink;
- }
- lc.setIPDAdjust(updatedRatio);
- //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
- //log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
- } else if (false && textAlignment == EN_CENTER) {
- // re-compute indent
- int updatedIndent = lbp.startIndent
- + (context.getStackLimitIP().opt - lbp.lineWidth) / 2;
- lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
- } else if (false && textAlignment == EN_END) {
- // re-compute indent
- int updatedIndent = lbp.startIndent
- + (context.getStackLimitIP().opt - lbp.lineWidth);
- lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
- }
+ LayoutContext lc = new LayoutContext(0);
+ lc.setAlignmentContext(alignmentContext);
+ lc.setSpaceAdjust(lbp.dAdjust);
+ lc.setIPDAdjust(lbp.ipdAdjust);
+ lc.setLeadingSpace(new SpaceSpecifier(true));
+ lc.setTrailingSpace(new SpaceSpecifier(false));
+ lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
- setCurrentArea(lineArea);
- setChildContext(lc);
- LayoutManager childLM;
- while ((childLM = inlinePosIter.getNextChildLM()) != null) {
- lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
- childLM.addAreas(inlinePosIter, lc);
- lc.setLeadingSpace(lc.getTrailingSpace());
- lc.setTrailingSpace(new SpaceSpecifier(false));
+ /*
+ * extension (not in the XSL FO recommendation): if the left and right margins
+ * have been optimized, recompute indents and / or adjust ratio, according
+ * to the paragraph horizontal alignment
+ */
+ if (false && textAlignment == EN_JUSTIFY) {
+ // re-compute space adjust ratio
+ int updatedDifference = context.getRefIPD()
+ - lbp.lineWidth + lbp.difference;
+ double updatedRatio = 0.0;
+ if (updatedDifference > 0) {
+ updatedRatio = (float) updatedDifference / lbp.availableStretch;
+ } else if (updatedDifference < 0) {
+ updatedRatio = (float) updatedDifference / lbp.availableShrink;
}
+ lc.setIPDAdjust(updatedRatio);
+ //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
+ //log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
+ } else if (false && textAlignment == EN_CENTER) {
+ // re-compute indent
+ int updatedIndent = lbp.startIndent
+ + (context.getRefIPD() - lbp.lineWidth) / 2;
+ lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
+ } else if (false && textAlignment == EN_END) {
+ // re-compute indent
+ int updatedIndent = lbp.startIndent
+ + (context.getRefIPD() - lbp.lineWidth);
+ lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
+ }
- // when can this be null?
- // if display-align is distribute, add space after
- if (context.getSpaceAfter() > 0
- && (!context.isLastArea() || !isLastPosition)) {
- lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
- }
- lineArea.finalise();
- parentLM.addChildArea(lineArea);
+ setCurrentArea(lineArea);
+ setChildContext(lc);
+ LayoutManager childLM;
+ while ((childLM = inlinePosIter.getNextChildLM()) != null) {
+ lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
+ childLM.addAreas(inlinePosIter, lc);
+ lc.setLeadingSpace(lc.getTrailingSpace());
+ lc.setTrailingSpace(new SpaceSpecifier(false));
+ }
+
+ // if display-align is distribute, add space after
+ if (context.getSpaceAfter() > 0
+ && (!context.isLastArea() || !isLastPosition)) {
+ lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
+ }
+ lineArea.finalise();
+ parentLM.addChildArea(lineArea);
}
/**
@@ -1783,7 +1626,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
// set last area flag
blocklc.setFlags(LayoutContext.LAST_AREA,
(context.isLastArea() && childLM == lastLM));
- blocklc.setStackLimitsFrom(context);
+ blocklc.setStackLimitBP(context.getStackLimitBP());
// Add the line areas to Area
childLM.addAreas(childPosIter, blocklc);
blocklc.setLeadingSpace(blocklc.getTrailingSpace());
@@ -1824,5 +1667,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager
public boolean getGeneratesLineArea() {
return true;
}
+
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return true;
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
index 1cd3ab9d2..a9f2eeb27 100644
--- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
@@ -29,10 +29,10 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.fo.flow.ListBlock;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.ConditionalElementListener;
import org.apache.fop.layoutmgr.ElementListUtils;
-import org.apache.fop.layoutmgr.KeepUtil;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.NonLeafPosition;
@@ -279,21 +279,18 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(
- getListBlockFO().getKeepTogether());
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ public KeepProperty getKeepTogetherProperty() {
+ return getListBlockFO().getKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getListBlockFO().getKeepWithNext());
+ public KeepProperty getKeepWithPreviousProperty() {
+ return getListBlockFO().getKeepWithPrevious();
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getListBlockFO().getKeepWithPrevious());
+ public KeepProperty getKeepWithNextProperty() {
+ return getListBlockFO().getKeepWithNext();
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
index 0a2dec945..7fd2219ea 100644
--- a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
@@ -28,8 +28,9 @@ import org.apache.fop.area.Block;
import org.apache.fop.fo.flow.AbstractListItemPart;
import org.apache.fop.fo.flow.ListItemBody;
import org.apache.fop.fo.flow.ListItemLabel;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.NonLeafPosition;
@@ -221,20 +222,18 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(getPartFO().getKeepTogether());
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ public KeepProperty getKeepTogetherProperty() {
+ return getPartFO().getKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
}
}
diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
index a4e8982f7..fb88bb79d 100644
--- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
@@ -32,13 +32,13 @@ import org.apache.fop.area.Block;
import org.apache.fop.fo.flow.ListItem;
import org.apache.fop.fo.flow.ListItemBody;
import org.apache.fop.fo.flow.ListItemLabel;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.ConditionalElementListener;
import org.apache.fop.layoutmgr.ElementListObserver;
import org.apache.fop.layoutmgr.ElementListUtils;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBlockBox;
import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthElement;
@@ -83,8 +83,8 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
private MinOptMax effSpaceBefore;
private MinOptMax effSpaceAfter;
- private int keepWithNextPendingOnLabel;
- private int keepWithNextPendingOnBody;
+ private Keep keepWithNextPendingOnLabel;
+ private Keep keepWithNextPendingOnBody;
private int listItemHeight;
@@ -254,8 +254,8 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
context.updateKeepWithNextPending(this.keepWithNextPendingOnLabel);
context.updateKeepWithNextPending(this.keepWithNextPendingOnBody);
- context.updateKeepWithNextPending(getKeepWithNextStrength());
- context.updateKeepWithPreviousPending(getKeepWithPreviousStrength());
+ context.updateKeepWithNextPending(getKeepWithNext());
+ context.updateKeepWithPreviousPending(getKeepWithPrevious());
setFinished(true);
resetSpaces();
@@ -276,16 +276,16 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
int totalHeight = Math.max(fullHeights[0], fullHeights[1]);
int step;
int addedBoxHeight = 0;
- int keepWithNextActive = BlockLevelLayoutManager.KEEP_AUTO;
+ Keep keepWithNextActive = Keep.KEEP_AUTO;
LinkedList returnList = new LinkedList();
while ((step = getNextStep(elementLists, start, end, partialHeights)) > 0) {
if (end[0] + 1 == elementLists[0].size()) {
- keepWithNextActive = Math.max(keepWithNextActive, keepWithNextPendingOnLabel);
+ keepWithNextActive = keepWithNextActive.compare(keepWithNextPendingOnLabel);
}
if (end[1] + 1 == elementLists[1].size()) {
- keepWithNextActive = Math.max(keepWithNextActive, keepWithNextPendingOnBody);
+ keepWithNextActive = keepWithNextActive.compare(keepWithNextPendingOnBody);
}
// compute penalty height and box height
@@ -339,14 +339,13 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
}
if (addedBoxHeight < totalHeight) {
- int strength = BlockLevelLayoutManager.KEEP_AUTO;
- strength = Math.max(strength, keepWithNextActive);
- strength = Math.max(strength, getKeepTogetherStrength());
+ Keep keep = keepWithNextActive.compare(getKeepTogether());
int p = stepPenalty;
if (p > -KnuthElement.INFINITE) {
- p = Math.max(p, KeepUtil.getPenaltyForKeep(strength));
+ p = Math.max(p, keep.getPenalty());
}
- returnList.add(new BreakElement(stepPosition, penaltyHeight, p, -1, context));
+ returnList.add(new BreakElement(stepPosition, penaltyHeight, p, keep.getContext(),
+ context));
}
}
@@ -644,21 +643,18 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(
- getListItemFO().getKeepTogether());
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ public KeepProperty getKeepTogetherProperty() {
+ return getListItemFO().getKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getListItemFO().getKeepWithNext());
+ public KeepProperty getKeepWithPreviousProperty() {
+ return getListItemFO().getKeepWithPrevious();
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getListItemFO().getKeepWithPrevious());
+ public KeepProperty getKeepWithNextProperty() {
+ return getListItemFO().getKeepWithNext();
}
/** {@inheritDoc} */
@@ -706,6 +702,13 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
}
}
+ /** {@inheritDoc} */
+ public void reset() {
+ super.reset();
+ label.reset();
+ body.reset();
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java b/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java
index a9da7a3ca..53e798e3c 100644
--- a/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java
+++ b/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java
@@ -19,19 +19,21 @@
package org.apache.fop.layoutmgr.table;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.table.ConditionalBorder;
import org.apache.fop.fo.flow.table.EffRow;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBlockBox;
import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthElement;
@@ -73,7 +75,7 @@ class ActiveCell {
/** True if the next CellPart that will be created will be the last one for this cell. */
private boolean lastCellPart;
- private int keepWithNextStrength;
+ private Keep keepWithNext;
private int spanIndex = 0;
@@ -133,7 +135,12 @@ class ActiveCell {
this.totalLength = other.totalLength;
this.penaltyLength = other.penaltyLength;
this.penaltyValue = other.penaltyValue;
- this.footnoteList = other.footnoteList;
+ if (other.footnoteList != null) {
+ if (this.footnoteList == null) {
+ this.footnoteList = new ArrayList();
+ }
+ this.footnoteList.addAll(other.footnoteList);
+ }
this.condBeforeContentLength = other.condBeforeContentLength;
this.breakClass = other.breakClass;
}
@@ -211,7 +218,7 @@ class ActiveCell {
includedLength = -1; // Avoid troubles with cells having content of zero length
totalLength = previousRowsLength + ElementListUtils.calcContentLength(elementList);
endRowIndex = rowIndex + pgu.getCell().getNumberRowsSpanned() - 1;
- keepWithNextStrength = BlockLevelLayoutManager.KEEP_AUTO;
+ keepWithNext = Keep.KEEP_AUTO;
remainingLength = totalLength - previousRowsLength;
afterNextStep = new Step(previousRowsLength);
@@ -297,7 +304,9 @@ class ActiveCell {
afterNextStep.penaltyValue = 0;
afterNextStep.condBeforeContentLength = 0;
afterNextStep.breakClass = Constants.EN_AUTO;
- afterNextStep.footnoteList = null;
+ if (afterNextStep.footnoteList != null) {
+ afterNextStep.footnoteList.clear();
+ }
boolean breakFound = false;
boolean prevIsBox = false;
boolean boxFound = false;
@@ -305,7 +314,11 @@ class ActiveCell {
KnuthElement el = (KnuthElement) knuthIter.next();
if (el.isPenalty()) {
prevIsBox = false;
- if (el.getP() < KnuthElement.INFINITE) {
+ if (el.getP() < KnuthElement.INFINITE
+ || ((KnuthPenalty) el).getBreakClass() == Constants.EN_PAGE) {
+ // TODO too much is being done in that test, only to handle
+ // keep.within-column properly.
+
// First legal break point
breakFound = true;
KnuthPenalty p = (KnuthPenalty) el;
@@ -524,7 +537,7 @@ class ActiveCell {
*/
CellPart createCellPart() {
if (nextStep.end + 1 == elementList.size()) {
- keepWithNextStrength = pgu.getKeepWithNextStrength();
+ keepWithNext = pgu.getKeepWithNext();
// TODO if keep-with-next is set on the row, must every cell of the row
// contribute some content from children blocks?
// see http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200802.mbox/
@@ -563,11 +576,12 @@ class ActiveCell {
void addFootnotes(List footnoteList) {
if (includedInLastStep() && nextStep.footnoteList != null) {
footnoteList.addAll(nextStep.footnoteList);
+ nextStep.footnoteList.clear();
}
}
- int getKeepWithNextStrength() {
- return keepWithNextStrength;
+ Keep getKeepWithNext() {
+ return keepWithNext;
}
int getPenaltyValue() {
diff --git a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
index 54cb1ebfe..83e71bb21 100644
--- a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
@@ -60,8 +60,8 @@ class RowGroupLayoutManager {
LinkedList returnList = new LinkedList();
createElementsForRowGroup(context, alignment, bodyType, returnList);
- context.updateKeepWithPreviousPending(rowGroup[0].getKeepWithPreviousStrength());
- context.updateKeepWithNextPending(rowGroup[rowGroup.length - 1].getKeepWithNextStrength());
+ context.updateKeepWithPreviousPending(rowGroup[0].getKeepWithPrevious());
+ context.updateKeepWithNextPending(rowGroup[rowGroup.length - 1].getKeepWithNext());
int breakBefore = Constants.EN_AUTO;
TableRow firstRow = rowGroup[0].getTableRow();
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
index 4cf68b97b..edf73acab 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
@@ -23,6 +23,7 @@ import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.fo.flow.table.TableAndCaption;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
@@ -201,19 +202,8 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KEEP_AUTO;
- /* TODO Complete me!
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(
- getTableAndCaptionFO().getKeepTogether());
- */
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
- }
-
- /** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
/* TODO Complete me!
return KeepUtil.getCombinedBlockLevelKeepStrength(
getTableAndCaptionFO().getKeepWithNext());
@@ -221,12 +211,12 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
/* TODO Complete me!
return KeepUtil.getCombinedBlockLevelKeepStrength(
getTableAndCaptionFO().getKeepWithPrevious());
*/
}
-} \ No newline at end of file
+}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
index 071082624..9d9255e0c 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
@@ -23,6 +23,7 @@ import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.fo.flow.table.TableCaption;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
@@ -197,21 +198,8 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KEEP_AUTO;
- /* TODO Complete me!
- strength = Math.max(strength, KeepUtil.getKeepStrength(
- getTableCaptionFO().getKeepTogether().getWithinPage()));
- strength = Math.max(strength, KeepUtil.getKeepStrength(
- getTableCaptionFO().getKeepTogether().getWithinColumn()));
- */
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
- }
-
- /** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO;
/* TODO Complete me!
return KeepUtil.getCombinedBlockLevelKeepStrength(
getTableCaptionFO().getKeepWithNext());
@@ -219,8 +207,8 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager {
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO;
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO;
/* TODO Complete me!
return KeepUtil.getCombinedBlockLevelKeepStrength(
getTableCaptionFO().getKeepWithPrevious());
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
index 239a1a88e..4e3c0b102 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
@@ -24,6 +24,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.Trait;
@@ -31,21 +32,23 @@ import org.apache.fop.fo.flow.table.ConditionalBorder;
import org.apache.fop.fo.flow.table.GridUnit;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.flow.table.Table;
-import org.apache.fop.fo.flow.table.TablePart;
import org.apache.fop.fo.flow.table.TableCell;
import org.apache.fop.fo.flow.table.TableColumn;
+import org.apache.fop.fo.flow.table.TablePart;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
import org.apache.fop.layoutmgr.AreaAdditionUtil;
import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.KnuthPenalty;
import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.SpaceResolver;
@@ -138,9 +141,9 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
List contentList = new LinkedList();
List returnList = new LinkedList();
- BlockLevelLayoutManager curLM; // currently active LM
- BlockLevelLayoutManager prevLM = null; // previously active LM
- while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+ LayoutManager curLM; // currently active LM
+ LayoutManager prevLM = null; // previously active LM
+ while ((curLM = getChildLM()) != null) {
LayoutContext childLC = new LayoutContext(0);
// curLM is a ?
childLC.setStackLimitBP(MinOptMax.subtract(context
@@ -153,11 +156,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
log.debug("child LM signals pending keep with next");
}
if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
- primaryGridUnit.setKeepWithPreviousStrength(childLC.getKeepWithPreviousPending());
+ primaryGridUnit.setKeepWithPrevious(childLC.getKeepWithPreviousPending());
childLC.clearKeepWithPreviousPending();
}
- if (prevLM != null) {
+ if (prevLM != null
+ && !ElementListUtils.endsWithForcedBreak(contentList)) {
// there is a block handled by prevLM
// before the one handled by curLM
addInBetweenBreak(contentList, context, childLC);
@@ -174,7 +178,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
}
prevLM = curLM;
}
- primaryGridUnit.setKeepWithNextStrength(context.getKeepWithNextPending());
+ primaryGridUnit.setKeepWithNext(context.getKeepWithNextPending());
returnedList = new LinkedList();
if (!contentList.isEmpty()) {
@@ -195,7 +199,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
}
final KnuthElement lastItem = (KnuthElement) ListUtil
.getLast(returnList);
- if (((KnuthElement) lastItem).isForcedBreak()) {
+ if (lastItem.isForcedBreak()) {
KnuthPenalty p = (KnuthPenalty) lastItem;
primaryGridUnit.setBreakAfter(p.getBreakClass());
p.setP(0);
@@ -556,26 +560,23 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KEEP_AUTO;
+ public Keep getKeepTogether() {
+ Keep keep = Keep.KEEP_AUTO;
if (primaryGridUnit.getRow() != null) {
- strength = Math.max(strength, KeepUtil.getKeepStrength(
- primaryGridUnit.getRow().getKeepTogether().getWithinPage()));
- strength = Math.max(strength, KeepUtil.getKeepStrength(
- primaryGridUnit.getRow().getKeepTogether().getWithinColumn()));
+ keep = Keep.getKeep(primaryGridUnit.getRow().getKeepTogether());
}
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ keep = keep.compare(getParentKeepTogether());
+ return keep;
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-next!)
+ public Keep getKeepWithNext() {
+ return Keep.KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-next!)
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-previous!)
+ public Keep getKeepWithPrevious() {
+ return Keep.KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-previous!)
}
// --------- Property Resolution related functions --------- //
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
index 2ff97a6a7..40ebf9e0d 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
@@ -35,10 +35,9 @@ import org.apache.fop.fo.flow.table.EffRow;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TablePart;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.ElementListUtils;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
@@ -213,13 +212,13 @@ public class TableContentLayoutManager implements PercentBaseContext {
context.clearKeepsPending();
context.setBreakBefore(Constants.EN_AUTO);
context.setBreakAfter(Constants.EN_AUTO);
- int keepWithPrevious = BlockLevelLayoutManager.KEEP_AUTO;
+ Keep keepWithPrevious = Keep.KEEP_AUTO;
int breakBefore = Constants.EN_AUTO;
if (rowGroup != null) {
RowGroupLayoutManager rowGroupLM = new RowGroupLayoutManager(getTableLM(), rowGroup,
stepper);
List nextRowGroupElems = rowGroupLM.getNextKnuthElements(context, alignment, bodyType);
- keepWithPrevious = Math.max(keepWithPrevious, context.getKeepWithPreviousPending());
+ keepWithPrevious = keepWithPrevious.compare(context.getKeepWithPreviousPending());
breakBefore = context.getBreakBefore();
int breakBetween = context.getBreakAfter();
returnList.addAll(nextRowGroupElems);
@@ -228,7 +227,7 @@ public class TableContentLayoutManager implements PercentBaseContext {
//Note previous pending keep-with-next and clear the strength
//(as the layout context is reused)
- int keepWithNextPending = context.getKeepWithNextPending();
+ Keep keepWithNextPending = context.getKeepWithNextPending();
context.clearKeepWithNextPending();
//Get elements for next row group
@@ -246,17 +245,17 @@ public class TableContentLayoutManager implements PercentBaseContext {
*/
//Determine keep constraints
- int penaltyStrength = BlockLevelLayoutManager.KEEP_AUTO;
- penaltyStrength = Math.max(penaltyStrength, keepWithNextPending);
- penaltyStrength = Math.max(penaltyStrength, context.getKeepWithPreviousPending());
+ Keep keep = keepWithNextPending.compare(context.getKeepWithPreviousPending());
context.clearKeepWithPreviousPending();
- penaltyStrength = Math.max(penaltyStrength, getTableLM().getKeepTogetherStrength());
- int penaltyValue = KeepUtil.getPenaltyForKeep(penaltyStrength);
+ keep = keep.compare(getTableLM().getKeepTogether());
+ int penaltyValue = keep.getPenalty();
+ int breakClass = keep.getContext();
breakBetween = BreakUtil.compareBreakClasses(breakBetween,
context.getBreakBefore());
if (breakBetween != Constants.EN_AUTO) {
penaltyValue = -KnuthElement.INFINITE;
+ breakClass = breakBetween;
}
BreakElement breakElement;
ListIterator elemIter = returnList.listIterator(returnList.size());
@@ -267,7 +266,7 @@ public class TableContentLayoutManager implements PercentBaseContext {
breakElement = (BreakElement) elem;
}
breakElement.setPenaltyValue(penaltyValue);
- breakElement.setBreakClass(breakBetween);
+ breakElement.setBreakClass(breakClass);
returnList.addAll(nextRowGroupElems);
breakBetween = context.getBreakAfter();
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
index dc2b3cc46..9ccca8b9e 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
@@ -35,11 +35,11 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableColumn;
+import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.layoutmgr.BlockLevelEventProducer;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.ConditionalElementListener;
-import org.apache.fop.layoutmgr.KeepUtil;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.LayoutContext;
@@ -256,10 +256,10 @@ public class TableLayoutManager extends BlockStackingLayoutManager
log.debug(contentKnuthElements);
wrapPositionElements(contentKnuthElements, returnList);
- context.updateKeepWithPreviousPending(getKeepWithPreviousStrength());
+ context.updateKeepWithPreviousPending(getKeepWithPrevious());
context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- context.updateKeepWithNextPending(getKeepWithNextStrength());
+ context.updateKeepWithNextPending(getKeepWithNext());
context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
if (getTable().isSeparateBorderModel()) {
@@ -448,20 +448,18 @@ public class TableLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public int getKeepTogetherStrength() {
- int strength = KeepUtil.getCombinedBlockLevelKeepStrength(getTable().getKeepTogether());
- strength = Math.max(strength, getParentKeepTogetherStrength());
- return strength;
+ public KeepProperty getKeepTogetherProperty() {
+ return getTable().getKeepTogether();
}
/** {@inheritDoc} */
- public int getKeepWithNextStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getTable().getKeepWithNext());
+ public KeepProperty getKeepWithPreviousProperty() {
+ return getTable().getKeepWithPrevious();
}
/** {@inheritDoc} */
- public int getKeepWithPreviousStrength() {
- return KeepUtil.getCombinedBlockLevelKeepStrength(getTable().getKeepWithPrevious());
+ public KeepProperty getKeepWithNextProperty() {
+ return getTable().getKeepWithNext();
}
// --------- Property Resolution related functions --------- //
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java
index a3fba279e..92a641ed0 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java
@@ -30,12 +30,10 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.table.EffRow;
import org.apache.fop.fo.flow.table.GridUnit;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
-import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
-import org.apache.fop.layoutmgr.KeepUtil;
+import org.apache.fop.layoutmgr.Keep;
import org.apache.fop.layoutmgr.KnuthBlockBox;
import org.apache.fop.layoutmgr.KnuthBox;
-import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.KnuthPenalty;
import org.apache.fop.layoutmgr.LayoutContext;
@@ -241,40 +239,38 @@ public class TableStepper {
}
}
- int strength = BlockLevelLayoutManager.KEEP_AUTO;
+ Keep keep = Keep.KEEP_AUTO;
int stepPenalty = 0;
for (Iterator iter = activeCells.iterator(); iter.hasNext();) {
ActiveCell activeCell = (ActiveCell) iter.next();
- strength = Math.max(strength, activeCell.getKeepWithNextStrength());
+ keep = keep.compare(activeCell.getKeepWithNext());
stepPenalty = Math.max(stepPenalty, activeCell.getPenaltyValue());
}
if (!rowFinished) {
- strength = Math.max(strength, rowGroup[activeRowIndex].getKeepTogetherStrength());
+ keep = keep.compare(rowGroup[activeRowIndex].getKeepTogether());
//The above call doesn't take the penalty from the table into account, so...
- strength = Math.max(strength, getTableLM().getKeepTogetherStrength());
+ keep = keep.compare(getTableLM().getKeepTogether());
} else if (activeRowIndex < rowGroup.length - 1) {
- strength = Math.max(strength,
- rowGroup[activeRowIndex].getKeepWithNextStrength());
- strength = Math.max(strength,
- rowGroup[activeRowIndex + 1].getKeepWithPreviousStrength());
+ keep = keep.compare(rowGroup[activeRowIndex].getKeepWithNext());
+ keep = keep.compare(rowGroup[activeRowIndex + 1].getKeepWithPrevious());
nextBreakClass = BreakUtil.compareBreakClasses(nextBreakClass,
rowGroup[activeRowIndex].getBreakAfter());
nextBreakClass = BreakUtil.compareBreakClasses(nextBreakClass,
rowGroup[activeRowIndex + 1].getBreakBefore());
}
- int p = KeepUtil.getPenaltyForKeep(strength);
+ int p = keep.getPenalty();
if (rowHeightSmallerThanFirstStep) {
rowHeightSmallerThanFirstStep = false;
p = KnuthPenalty.INFINITE;
}
- if (p > -KnuthElement.INFINITE) {
- p = Math.max(p, stepPenalty);
- }
+ p = Math.max(p, stepPenalty);
+ int breakClass = keep.getContext();
if (nextBreakClass != Constants.EN_AUTO) {
log.trace("Forced break encountered");
p = -KnuthPenalty.INFINITE; //Overrides any keeps (see 4.8 in XSL 1.0)
+ breakClass = nextBreakClass;
}
- returnList.add(new BreakElement(penaltyPos, effPenaltyLen, p, nextBreakClass, context));
+ returnList.add(new BreakElement(penaltyPos, effPenaltyLen, p, breakClass, context));
if (penaltyOrGlueLen < 0) {
returnList.add(new KnuthGlue(-penaltyOrGlueLen, 0, 0, new Position(null), true));
}
diff --git a/src/java/org/apache/fop/pdf/PDFColor.java b/src/java/org/apache/fop/pdf/PDFColor.java
index 5bac9532c..f2ae2da33 100644
--- a/src/java/org/apache/fop/pdf/PDFColor.java
+++ b/src/java/org/apache/fop/pdf/PDFColor.java
@@ -541,13 +541,8 @@ public class PDFColor extends PDFPathPaint {
return (new byte[0]);
}
- /**
- * Check for equality of color with another object.
- *
- * @param obj the object to compare
- * @return true if colors are equal
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (!(obj instanceof PDFColor)) {
return false;
}
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java
index 9bc4c0d2e..46effdfd6 100644
--- a/src/java/org/apache/fop/pdf/PDFDocument.java
+++ b/src/java/org/apache/fop/pdf/PDFDocument.java
@@ -526,8 +526,8 @@ public class PDFDocument {
private Object findPDFObject(List list, PDFObject compare) {
for (Iterator iter = list.iterator(); iter.hasNext();) {
- Object obj = iter.next();
- if (compare.equals(obj)) {
+ PDFObject obj = (PDFObject) iter.next();
+ if (compare.contentEquals(obj)) {
return obj;
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index 087ae4277..db2e99875 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -174,22 +174,24 @@ public class PDFFactory {
* PDFDocument later using addObject().
*
* @param resources resources object to use
- * @param pageWidth width of the page in points
- * @param pageHeight height of the page in points
* @param pageIndex index of the page (zero-based)
+ * @param mediaBox the MediaBox area
+ * @param cropBox the CropBox area
+ * @param bleedBox the BleedBox area
+ * @param trimBox the TrimBox area
* @param currentPageParentKey the integer key in the structural parent tree
*
* @return the created /Page object
*/
- public PDFPage makePage(PDFResources resources,
- int pageWidth, int pageHeight, int pageIndex,
+ public PDFPage makePage(PDFResources resources, int pageIndex,
+ Rectangle2D mediaBox, Rectangle2D cropBox,
+ Rectangle2D bleedBox, Rectangle2D trimBox,
int currentPageParentKey) {
/*
* create a PDFPage with the next object number, the given
* resources, contents and dimensions
*/
- PDFPage page = new PDFPage(resources, // old numPages
- pageWidth, pageHeight, pageIndex);
+ PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
if (currentPageParentKey > -1) {
//Accessibility is enabled
page.setStructParents(currentPageParentKey);
@@ -217,7 +219,8 @@ public class PDFFactory {
*/
public PDFPage makePage(PDFResources resources,
int pageWidth, int pageHeight, int pageIndex) {
- return makePage(resources, pageWidth, pageHeight, pageIndex, -1);
+ Rectangle2D mediaBox = new Rectangle2D.Double(0, 0, pageWidth, pageHeight);
+ return makePage(resources, pageIndex, mediaBox, mediaBox, mediaBox, mediaBox, -1);
}
/**
diff --git a/src/java/org/apache/fop/pdf/PDFFileSpec.java b/src/java/org/apache/fop/pdf/PDFFileSpec.java
index a2bc6aefe..8de4164af 100644
--- a/src/java/org/apache/fop/pdf/PDFFileSpec.java
+++ b/src/java/org/apache/fop/pdf/PDFFileSpec.java
@@ -63,13 +63,8 @@ public class PDFFileSpec extends PDFObject {
* endobj
*/
- /**
- * Check if this equals another object.
- *
- * @param obj the object to compare
- * @return true if this equals other object
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (this == obj) {
return true;
}
diff --git a/src/java/org/apache/fop/pdf/PDFFunction.java b/src/java/org/apache/fop/pdf/PDFFunction.java
index 14cc318ba..d57cabb50 100644
--- a/src/java/org/apache/fop/pdf/PDFFunction.java
+++ b/src/java/org/apache/fop/pdf/PDFFunction.java
@@ -696,15 +696,8 @@ public class PDFFunction extends PDFObject {
}
- /**
- * Check if this function is equal to another object.
- * This is used to find if a particular function already exists
- * in a document.
- *
- * @param obj the obj to compare
- * @return true if the functions are equal
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (obj == null) {
return false;
}
diff --git a/src/java/org/apache/fop/pdf/PDFGState.java b/src/java/org/apache/fop/pdf/PDFGState.java
index 93151149b..4b997a145 100644
--- a/src/java/org/apache/fop/pdf/PDFGState.java
+++ b/src/java/org/apache/fop/pdf/PDFGState.java
@@ -175,10 +175,8 @@ public class PDFGState extends PDFObject {
* endobj
*/
- /**
- * {@inheritDoc}
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (obj == this) {
return true;
}
diff --git a/src/java/org/apache/fop/pdf/PDFGoTo.java b/src/java/org/apache/fop/pdf/PDFGoTo.java
index b3ff6bcdc..ad04650c6 100644
--- a/src/java/org/apache/fop/pdf/PDFGoTo.java
+++ b/src/java/org/apache/fop/pdf/PDFGoTo.java
@@ -143,13 +143,8 @@ public class PDFGoTo extends PDFAction {
* endobj
*/
- /**
- * Check if this equals another object.
- *
- * @param obj the object to compare
- * @return true if this equals other object
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (this == obj) {
return true;
}
diff --git a/src/java/org/apache/fop/pdf/PDFGoToRemote.java b/src/java/org/apache/fop/pdf/PDFGoToRemote.java
index e04a1668f..ee7660875 100644
--- a/src/java/org/apache/fop/pdf/PDFGoToRemote.java
+++ b/src/java/org/apache/fop/pdf/PDFGoToRemote.java
@@ -127,13 +127,8 @@ public class PDFGoToRemote extends PDFAction {
* endobj
*/
- /**
- * Check if this equals another object.
- *
- * @param obj the object to compare
- * @return true if this equals other object
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (this == obj) {
return true;
}
diff --git a/src/java/org/apache/fop/pdf/PDFLaunch.java b/src/java/org/apache/fop/pdf/PDFLaunch.java
index d87fa2523..386a7a9a3 100644
--- a/src/java/org/apache/fop/pdf/PDFLaunch.java
+++ b/src/java/org/apache/fop/pdf/PDFLaunch.java
@@ -42,13 +42,8 @@ public class PDFLaunch extends PDFAction {
return sb.toString();
}
- /**
- * Check if this equals another object.
- *
- * @param obj the object to compare
- * @return true if this equals other object
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (this == obj) {
return true;
}
diff --git a/src/java/org/apache/fop/pdf/PDFLink.java b/src/java/org/apache/fop/pdf/PDFLink.java
index 620e5d51d..66791e3ba 100644
--- a/src/java/org/apache/fop/pdf/PDFLink.java
+++ b/src/java/org/apache/fop/pdf/PDFLink.java
@@ -118,13 +118,8 @@ public class PDFLink extends PDFObject {
* endobj
*/
- /**
- * Check if this equals another object.
- *
- * @param obj the object to compare
- * @return true if this equals other object
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (this == obj) {
return true;
}
diff --git a/src/java/org/apache/fop/pdf/PDFObject.java b/src/java/org/apache/fop/pdf/PDFObject.java
index 97e9f4976..21ff82394 100644
--- a/src/java/org/apache/fop/pdf/PDFObject.java
+++ b/src/java/org/apache/fop/pdf/PDFObject.java
@@ -393,4 +393,20 @@ public abstract class PDFObject implements PDFWritable {
return formatDateTime(time, TimeZone.getDefault());
}
+ /**
+ * Check if the other PDFObject has the same content as the current object.
+ * <p>
+ * Note: This function has a contract which is less binding than
+ * {@link #equals(Object)}. Whereas equals would require all values to be
+ * identical, this method is not required to check everything. In the case
+ * of PDFObjects, this means that the overriding function does not have to
+ * check for {@link #getObjectID()}.
+ *
+ * @param o
+ * object to compare to.
+ * @return true if the other object has the same content.
+ */
+ protected boolean contentEquals(PDFObject o) {
+ return this.equals(o);
+ }
}
diff --git a/src/java/org/apache/fop/pdf/PDFPage.java b/src/java/org/apache/fop/pdf/PDFPage.java
index 6cc8c3e57..1bcaa65c6 100644
--- a/src/java/org/apache/fop/pdf/PDFPage.java
+++ b/src/java/org/apache/fop/pdf/PDFPage.java
@@ -38,42 +38,42 @@ public class PDFPage extends PDFResourceContext {
* Create a /Page object
*
* @param resources the /Resources object
- * @param contents the content stream
- * @param pageWidth the page's width in points
- * @param pageHeight the page's height in points
* @param pageIndex the page's zero-based index (or -1 if the page number is auto-determined)
+ * @param mediaBox the MediaBox
+ * @param cropBox the CropBox. If null, mediaBox is used.
+ * @param bleedBox the BleedBox. If null, cropBox is used.
+ * @param trimBox the TrimBox. If null, bleedBox is used.
*/
- public PDFPage(PDFResources resources, PDFStream contents,
- int pageWidth, int pageHeight, int pageIndex) {
+ public PDFPage(PDFResources resources, int pageIndex,
+ Rectangle2D mediaBox, Rectangle2D cropBox,
+ Rectangle2D bleedBox, Rectangle2D trimBox) {
+ /* generic creation of object */
+ super(resources);
+
+ put("Type", new PDFName("Page"));
+ /* set fields using parameters */
+ setSimplePageSize(mediaBox, cropBox, bleedBox, trimBox);
+ this.pageIndex = pageIndex;
+ }
- /* generic creation of object */
- super(resources);
+ private void setSimplePageSize(Rectangle2D mediaBox, Rectangle2D cropBox,
+ Rectangle2D bleedBox, Rectangle2D trimBox) {
+ setMediaBox(mediaBox);
- put("Type", new PDFName("Page"));
- /* set fields using parameters */
- setContents(contents);
- setSimplePageSize(pageWidth, pageHeight);
- this.pageIndex = pageIndex;
- }
+ if (cropBox == null) {
+ cropBox = mediaBox;
+ }
+ setCropBox(cropBox);
- /**
- * Create a /Page object
- *
- * @param resources the /Resources object
- * @param pageWidth the page's width in points
- * @param pageHeight the page's height in points
- * @param pageIndex the page's zero-based index (or -1 if the page number is auto-determined)
- */
- public PDFPage(PDFResources resources,
- int pageWidth, int pageHeight, int pageIndex) {
- this(resources, null, pageWidth, pageHeight, pageIndex);
- }
+ if (bleedBox == null) {
+ bleedBox = cropBox;
+ }
+ setBleedBox(bleedBox); //Recommended by PDF/X
- private void setSimplePageSize(int width, int height) {
- Rectangle2D box = new Rectangle2D.Double(0, 0, width, height);
- setMediaBox(box);
- setBleedBox(box); //Recommended by PDF/X
- setTrimBox(box); //Needed for PDF/X
+ if (trimBox == null) {
+ trimBox = bleedBox;
+ }
+ setTrimBox(trimBox); //Needed for PDF/X
}
private PDFArray toPDFArray(Rectangle2D box) {
@@ -90,11 +90,11 @@ public class PDFPage extends PDFResourceContext {
}
/**
- * Sets the "TrimBox" entry
- * @param box the trim rectangle
+ * Sets the "CropBox" entry
+ * @param box the bleed rectangle
*/
- public void setTrimBox(Rectangle2D box) {
- put("TrimBox", toPDFArray(box));
+ public void setCropBox(Rectangle2D box) {
+ put("CropBox", toPDFArray(box));
}
/**
@@ -106,6 +106,14 @@ public class PDFPage extends PDFResourceContext {
}
/**
+ * Sets the "TrimBox" entry
+ * @param box the trim rectangle
+ */
+ public void setTrimBox(Rectangle2D box) {
+ put("TrimBox", toPDFArray(box));
+ }
+
+ /**
* set this page contents
*
* @param contents the contents of the page
diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java
index 4e862c3f2..89ae1efcd 100644
--- a/src/java/org/apache/fop/pdf/PDFPattern.java
+++ b/src/java/org/apache/fop/pdf/PDFPattern.java
@@ -336,13 +336,8 @@ public class PDFPattern extends PDFPathPaint {
*/
public byte[] toPDF() { return null; }
- /**
- * Check if this pattern is equal to another.
- *
- * @param obj the object to compare against
- * @return true if the patterns are equal
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (obj == null) {
return false;
}
diff --git a/src/java/org/apache/fop/pdf/PDFShading.java b/src/java/org/apache/fop/pdf/PDFShading.java
index 5ac7245c9..fa4d454ed 100644
--- a/src/java/org/apache/fop/pdf/PDFShading.java
+++ b/src/java/org/apache/fop/pdf/PDFShading.java
@@ -529,14 +529,8 @@ public class PDFShading extends PDFObject {
return (p.toString());
}
- /**
- * Check if this shading is equal to another shading.
- * This is used to check if a shading already exists.
- *
- * @param obj the object to compare against
- * @return true if the shadings are equal
- */
- public boolean equals(Object obj) {
+ /** {@inheritDoc} */
+ protected boolean contentEquals(PDFObject obj) {
if (obj == null) {
return false;
}
diff --git a/src/java/org/apache/fop/render/AbstractConfigurator.java b/src/java/org/apache/fop/render/AbstractConfigurator.java
index b1ac1c61a..096007b98 100644
--- a/src/java/org/apache/fop/render/AbstractConfigurator.java
+++ b/src/java/org/apache/fop/render/AbstractConfigurator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.render;
diff --git a/src/java/org/apache/fop/render/DummyPercentBaseContext.java b/src/java/org/apache/fop/render/DummyPercentBaseContext.java
new file mode 100644
index 000000000..f9ecf63fe
--- /dev/null
+++ b/src/java/org/apache/fop/render/DummyPercentBaseContext.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render;
+
+import org.apache.fop.datatypes.PercentBaseContext;
+import org.apache.fop.fo.FObj;
+
+/**
+ * A dummy implementation of PercentBaseContext
+ */
+public final class DummyPercentBaseContext implements PercentBaseContext {
+
+ private static DummyPercentBaseContext singleton = new DummyPercentBaseContext();
+
+ private DummyPercentBaseContext() {
+ }
+
+ /**
+ * Returns an instance of this dummy implementation
+ * @return an instance of this dummy implementation
+ */
+ public static DummyPercentBaseContext getInstance() {
+ return singleton;
+ }
+
+ /** {@inheritDoc} */
+ public int getBaseLength(int lengthBase, FObj fo) {
+ return 0;
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/render/afp/AFPCustomizable.java b/src/java/org/apache/fop/render/afp/AFPCustomizable.java
index ff4fb0100..ed1ea443b 100644
--- a/src/java/org/apache/fop/render/afp/AFPCustomizable.java
+++ b/src/java/org/apache/fop/render/afp/AFPCustomizable.java
@@ -51,6 +51,12 @@ public interface AFPCustomizable {
void setNativeImagesSupported(boolean nativeImages);
/**
+ * Sets the shading mode for painting filled rectangles.
+ * @param shadingMode the shading mode
+ */
+ void setShadingMode(AFPShadingMode shadingMode);
+
+ /**
* Sets the output/device resolution
*
* @param resolution
diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
index 8d29145cf..073d43e20 100644
--- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
+++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
@@ -25,19 +25,26 @@ import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.util.Map;
+import org.apache.fop.afp.AFPDitheredRectanglePainter;
import org.apache.fop.afp.AFPPaintingState;
+import org.apache.fop.afp.AFPRectanglePainter;
import org.apache.fop.afp.AFPResourceLevelDefaults;
import org.apache.fop.afp.AFPResourceManager;
import org.apache.fop.afp.AFPUnitConverter;
+import org.apache.fop.afp.AbstractAFPPainter;
import org.apache.fop.afp.DataStream;
import org.apache.fop.afp.fonts.AFPFontCollection;
import org.apache.fop.afp.fonts.AFPPageFonts;
+import org.apache.fop.afp.modca.ResourceObject;
+import org.apache.fop.afp.util.DefaultFOPResourceAccessor;
+import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fonts.FontCollection;
import org.apache.fop.fonts.FontEventAdapter;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.render.afp.extensions.AFPElementMapping;
+import org.apache.fop.render.afp.extensions.AFPIncludeFormMap;
import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap;
import org.apache.fop.render.afp.extensions.AFPPageSetup;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
@@ -70,12 +77,18 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
private Map/*<String,String>*/pageSegmentMap
= new java.util.HashMap/*<String,String>*/();
+ /** Medium Map referenced on previous page **/
+ private String lastMediumMap;
+
private static final int LOC_ELSEWHERE = 0;
private static final int LOC_FOLLOWING_PAGE_SEQUENCE = 1;
private static final int LOC_IN_PAGE_HEADER = 2;
private int location = LOC_ELSEWHERE;
+ /** the shading mode for filled rectangles */
+ private AFPShadingMode shadingMode = AFPShadingMode.COLOR;
+
/**
* Default constructor.
*/
@@ -125,6 +138,16 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
return this.resourceManager;
}
+ AbstractAFPPainter createRectanglePainter() {
+ if (AFPShadingMode.DITHERED.equals(this.shadingMode)) {
+ return new AFPDitheredRectanglePainter(
+ getPaintingState(), getDataStream(), getResourceManager());
+ } else {
+ return new AFPRectanglePainter(
+ getPaintingState(), getDataStream());
+ }
+ }
+
/** {@inheritDoc} */
public void startDocument() throws IFException {
super.startDocument();
@@ -259,7 +282,8 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
} else {
if (this.location != LOC_IN_PAGE_HEADER) {
throw new IFException(
- "AFP page setup extension encountered outside the page header: " + aps, null);
+ "AFP page setup extension encountered outside the page header: " + aps,
+ null);
}
if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) {
String overlay = aps.getName();
@@ -278,15 +302,31 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
}
}
} else if (extension instanceof AFPInvokeMediumMap) {
- if (this.location != LOC_FOLLOWING_PAGE_SEQUENCE) {
+ if (this.location != LOC_FOLLOWING_PAGE_SEQUENCE
+ && this.location != LOC_IN_PAGE_HEADER) {
+
throw new IFException(
- "AFP IMM extension must be between page-sequence and the first page: "
- + extension, null);
+ "AFP IMM extension must be between page-sequence"
+ + " and the first page or child of page-header: "
+ + extension, null);
}
AFPInvokeMediumMap imm = (AFPInvokeMediumMap)extension;
String mediumMap = imm.getName();
- if (mediumMap != null) {
+ if (mediumMap != null && !mediumMap.equals(lastMediumMap)) {
dataStream.createInvokeMediumMap(mediumMap);
+ lastMediumMap = mediumMap;
+ }
+ } else if (extension instanceof AFPIncludeFormMap) {
+ AFPIncludeFormMap formMap = (AFPIncludeFormMap)extension;
+ ResourceAccessor accessor = new DefaultFOPResourceAccessor(
+ getUserAgent(), null, null);
+ try {
+ getResourceManager().createIncludedResource(formMap.getName(),
+ formMap.getSrc(), accessor,
+ ResourceObject.TYPE_FORMDEF);
+ } catch (IOException ioe) {
+ throw new IFException(
+ "I/O error while embedding form map resource: " + formMap.getName(), ioe);
}
}
}
@@ -309,6 +349,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
}
/** {@inheritDoc} */
+ public void setShadingMode(AFPShadingMode shadingMode) {
+ this.shadingMode = shadingMode;
+ }
+
+ /** {@inheritDoc} */
public void setResolution(int resolution) {
paintingState.setResolution(resolution);
}
diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java
index 440db3eda..cbe6d0ca3 100644
--- a/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java
+++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandlerMaker.java
@@ -30,8 +30,10 @@ import org.apache.fop.render.intermediate.IFDocumentHandler;
*/
public class AFPDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker {
- private static final String[] MIMES = new String[]
- {MimeConstants.MIME_AFP};
+ private static final String[] MIMES = new String[] {
+ MimeConstants.MIME_AFP,
+ MimeConstants.MIME_AFP_ALT
+ };
/** {@inheritDoc} */
public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java
index a92be9d6e..7fcd0b3d1 100644
--- a/src/java/org/apache/fop/render/afp/AFPPainter.java
+++ b/src/java/org/apache/fop/render/afp/AFPPainter.java
@@ -35,8 +35,8 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.fop.afp.AFPBorderPainter;
import org.apache.fop.afp.AFPPaintingState;
-import org.apache.fop.afp.AFPRectanglePainter;
import org.apache.fop.afp.AFPUnitConverter;
+import org.apache.fop.afp.AbstractAFPPainter;
import org.apache.fop.afp.BorderPaintingInfo;
import org.apache.fop.afp.DataStream;
import org.apache.fop.afp.RectanglePaintingInfo;
@@ -46,10 +46,8 @@ import org.apache.fop.afp.fonts.AFPPageFonts;
import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.modca.AbstractPageObject;
import org.apache.fop.afp.modca.PresentationTextObject;
-import org.apache.fop.afp.modca.ResourceObject;
import org.apache.fop.afp.ptoca.PtocaBuilder;
import org.apache.fop.afp.ptoca.PtocaProducer;
-import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
@@ -79,7 +77,7 @@ public class AFPPainter extends AbstractIFPainter {
/** the border painter */
private AFPBorderPainterAdapter borderPainter;
/** the rectangle painter */
- private AFPRectanglePainter rectanglePainter;
+ private AbstractAFPPainter rectanglePainter;
/** unit converter */
private final AFPUnitConverter unitConv;
@@ -94,7 +92,7 @@ public class AFPPainter extends AbstractIFPainter {
this.state = IFState.create();
this.borderPainter = new AFPBorderPainterAdapter(
new AFPBorderPainter(getPaintingState(), getDataStream()));
- this.rectanglePainter = new AFPRectanglePainter(getPaintingState(), getDataStream());
+ this.rectanglePainter = documentHandler.createRectanglePainter();
this.unitConv = getPaintingState().getUnitConverter();
}
@@ -222,7 +220,11 @@ public class AFPPainter extends AbstractIFPainter {
}
RectanglePaintingInfo rectanglePaintInfo = new RectanglePaintingInfo(
toPoint(rect.x), toPoint(rect.y), toPoint(rect.width), toPoint(rect.height));
- rectanglePainter.paint(rectanglePaintInfo);
+ try {
+ rectanglePainter.paint(rectanglePaintInfo);
+ } catch (IOException ioe) {
+ throw new IFException("IO error while painting rectangle", ioe);
+ }
}
}
@@ -341,17 +343,7 @@ public class AFPPainter extends AbstractIFPainter {
if (afpFont.isEmbeddable()) {
try {
- //Embed fonts (char sets and code pages)
- //TODO This should be moved to a place where it has less performance impact
- if (charSet.getResourceAccessor() != null) {
- ResourceAccessor accessor = charSet.getResourceAccessor();
- documentHandler.getResourceManager().createIncludedResource(
- charSet.getName(), accessor,
- ResourceObject.TYPE_FONT_CHARACTER_SET);
- documentHandler.getResourceManager().createIncludedResource(
- charSet.getCodePage(), accessor,
- ResourceObject.TYPE_CODE_PAGE);
- }
+ documentHandler.getResourceManager().embedFont(afpFont, charSet);
} catch (IOException ioe) {
throw new IFException("Error while embedding font resources", ioe);
}
diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java
index 94fd05dc8..e106ac712 100644
--- a/src/java/org/apache/fop/render/afp/AFPRenderer.java
+++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java
@@ -43,6 +43,7 @@ import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.fop.afp.AFPBorderPainter;
import org.apache.fop.afp.AFPDataObjectInfo;
+import org.apache.fop.afp.AFPDitheredRectanglePainter;
import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.afp.AFPPaintingState;
import org.apache.fop.afp.AFPRectanglePainter;
@@ -50,6 +51,7 @@ import org.apache.fop.afp.AFPResourceLevelDefaults;
import org.apache.fop.afp.AFPResourceManager;
import org.apache.fop.afp.AFPTextDataInfo;
import org.apache.fop.afp.AFPUnitConverter;
+import org.apache.fop.afp.AbstractAFPPainter;
import org.apache.fop.afp.BorderPaintingInfo;
import org.apache.fop.afp.DataStream;
import org.apache.fop.afp.RectanglePaintingInfo;
@@ -59,10 +61,14 @@ import org.apache.fop.afp.fonts.AFPFontCollection;
import org.apache.fop.afp.fonts.AFPPageFonts;
import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.modca.PageObject;
+import org.apache.fop.afp.modca.ResourceObject;
+import org.apache.fop.afp.util.DefaultFOPResourceAccessor;
+import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.CTM;
+import org.apache.fop.area.OffDocumentExtensionAttachment;
import org.apache.fop.area.OffDocumentItem;
import org.apache.fop.area.PageSequence;
import org.apache.fop.area.PageViewport;
@@ -73,6 +79,7 @@ import org.apache.fop.area.inline.TextArea;
import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontCollection;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontManager;
@@ -80,6 +87,8 @@ import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.afp.extensions.AFPElementMapping;
+import org.apache.fop.render.afp.extensions.AFPExtensionAttachment;
+import org.apache.fop.render.afp.extensions.AFPIncludeFormMap;
import org.apache.fop.render.afp.extensions.AFPInvokeMediumMap;
import org.apache.fop.render.afp.extensions.AFPPageSetup;
@@ -167,7 +176,13 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
/** the image handler registry */
private final AFPImageHandlerRegistry imageHandlerRegistry;
- private AFPRectanglePainter rectanglePainter;
+ private AbstractAFPPainter rectanglePainter;
+
+ /** the shading mode for filled rectangles */
+ private AFPShadingMode shadingMode = AFPShadingMode.COLOR;
+
+ /** medium map referenced used on previous page **/
+ private String lastMediumMap;
/**
* Constructor for AFPRenderer.
@@ -201,11 +216,21 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
this.dataStream = resourceManager.createDataStream(paintingState, outputStream);
this.borderPainter = new AFPBorderPainter(paintingState, dataStream);
- this.rectanglePainter = new AFPRectanglePainter(paintingState, dataStream);
+ this.rectanglePainter = createRectanglePainter();
dataStream.startDocument();
}
+ AbstractAFPPainter createRectanglePainter() {
+ if (AFPShadingMode.DITHERED.equals(this.shadingMode)) {
+ return new AFPDitheredRectanglePainter(
+ this.paintingState, this.dataStream, this.resourceManager);
+ } else {
+ return new AFPRectanglePainter(
+ this.paintingState, this.dataStream);
+ }
+ }
+
/** {@inheritDoc} */
public void stopRenderer() throws IOException {
dataStream.endDocument();
@@ -263,8 +288,30 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
/** {@inheritDoc} */
public void processOffDocumentItem(OffDocumentItem odi) {
- // TODO
- log.debug("NYI processOffDocumentItem(" + odi + ")");
+ if (odi instanceof OffDocumentExtensionAttachment) {
+ ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
+ if (attachment != null) {
+ if (AFPExtensionAttachment.CATEGORY.equals(attachment.getCategory())) {
+ if (attachment instanceof AFPIncludeFormMap) {
+ handleIncludeFormMap((AFPIncludeFormMap)attachment);
+ }
+ }
+ }
+ }
+ }
+
+ private void handleIncludeFormMap(AFPIncludeFormMap formMap) {
+ ResourceAccessor accessor = new DefaultFOPResourceAccessor(
+ getUserAgent(), null, null);
+ try {
+ this.resourceManager.createIncludedResource(formMap.getName(),
+ formMap.getSrc(), accessor,
+ ResourceObject.TYPE_FORMDEF);
+ } catch (IOException ioe) {
+ AFPEventProducer eventProducer
+ = AFPEventProducer.Provider.get(userAgent.getEventBroadcaster());
+ eventProducer.resourceEmbeddingError(this, formMap.getName(), ioe);
+ }
}
/** {@inheritDoc} */
@@ -336,6 +383,9 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
int resolution = paintingState.getResolution();
+ // IMM should occur before BPG
+ renderInvokeMediumMap(pageViewport);
+
dataStream.startPage(pageWidth, pageHeight, pageRotation,
resolution, resolution);
@@ -362,7 +412,12 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
/** {@inheritDoc} */
public void fillRect(float x, float y, float width, float height) {
RectanglePaintingInfo rectanglePaintInfo = new RectanglePaintingInfo(x, y, width, height);
- rectanglePainter.paint(rectanglePaintInfo);
+ try {
+ rectanglePainter.paint(rectanglePaintInfo);
+ } catch (IOException ioe) {
+ //TODO not ideal, but the AFPRenderer is legacy
+ throw new RuntimeException("I/O error while painting a filled rectangle", ioe);
+ }
}
/** {@inheritDoc} */
@@ -543,6 +598,18 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
AFPFont font = (AFPFont)fontMetricMap.get(internalFontName);
AFPPageFonts pageFonts = paintingState.getPageFonts();
AFPFontAttributes fontAttributes = pageFonts.registerFont(internalFontName, font, fontSize);
+ Font fnt = getFontFromArea(text);
+
+ if (font.isEmbeddable()) {
+ CharacterSet charSet = font.getCharacterSet(fontSize);
+ try {
+ this.resourceManager.embedFont(font, charSet);
+ } catch (IOException ioe) {
+ AFPEventProducer eventProducer
+ = AFPEventProducer.Provider.get(userAgent.getEventBroadcaster());
+ eventProducer.resourceEmbeddingError(this, charSet.getName(), ioe);
+ }
+ }
// create text data info
AFPTextDataInfo textDataInfo = new AFPTextDataInfo();
@@ -583,7 +650,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
textDataInfo.setString(textString);
try {
- dataStream.createText(textDataInfo);
+ dataStream.createText(textDataInfo, textLetterSpaceAdjust, textWordSpaceAdjust, fnt, charSet);
} catch (UnsupportedEncodingException e) {
AFPEventProducer eventProducer
= AFPEventProducer.Provider.get(userAgent.getEventBroadcaster());
@@ -643,6 +710,35 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
}
/**
+ * checks for IMM Extension and renders if found and different
+ * from previous page
+ *
+ * @param pageViewport the page object
+ */
+ private void renderInvokeMediumMap(PageViewport pageViewport) {
+ if (pageViewport.getExtensionAttachments() != null
+ && pageViewport.getExtensionAttachments().size() > 0) {
+ Iterator it = pageViewport.getExtensionAttachments().iterator();
+ while (it.hasNext()) {
+ ExtensionAttachment attachment = (ExtensionAttachment) it.next();
+ if (AFPExtensionAttachment.CATEGORY.equals(attachment.getCategory())) {
+ AFPExtensionAttachment aea = (AFPExtensionAttachment)attachment;
+ if (AFPElementMapping.INVOKE_MEDIUM_MAP.equals(aea.getElementName())) {
+ AFPInvokeMediumMap imm = (AFPInvokeMediumMap)attachment;
+ String mediumMap = imm.getName();
+ if (mediumMap != null) {
+ if (!mediumMap.equals(lastMediumMap)) {
+ dataStream.createInvokeMediumMap(mediumMap);
+ lastMediumMap = mediumMap;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Method to render the page extension.
* <p>
*
@@ -659,27 +755,29 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
while (it.hasNext()) {
ExtensionAttachment attachment = (ExtensionAttachment) it.next();
if (AFPPageSetup.CATEGORY.equals(attachment.getCategory())) {
- AFPPageSetup aps = (AFPPageSetup) attachment;
- String element = aps.getElementName();
- if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) {
- String overlay = aps.getName();
- if (overlay != null) {
- dataStream.createIncludePageOverlay(overlay);
- }
- } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT
- .equals(element)) {
- String name = aps.getName();
- String source = aps.getValue();
- pageSegmentMap.put(source, name);
- } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT
- .equals(element)) {
- String name = aps.getName();
- String value = aps.getValue();
- dataStream.createTagLogicalElement(name, value);
- } else if (AFPElementMapping.NO_OPERATION.equals(element)) {
- String content = aps.getContent();
- if (content != null) {
- dataStream.createNoOperation(content);
+ if (attachment instanceof AFPPageSetup) {
+ AFPPageSetup aps = (AFPPageSetup) attachment;
+ String element = aps.getElementName();
+ if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) {
+ String overlay = aps.getName();
+ if (overlay != null) {
+ dataStream.createIncludePageOverlay(overlay);
+ }
+ } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT
+ .equals(element)) {
+ String name = aps.getName();
+ String source = aps.getValue();
+ pageSegmentMap.put(source, name);
+ } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT
+ .equals(element)) {
+ String name = aps.getName();
+ String value = aps.getValue();
+ dataStream.createTagLogicalElement(name, value);
+ } else if (AFPElementMapping.NO_OPERATION.equals(element)) {
+ String content = aps.getContent();
+ if (content != null) {
+ dataStream.createNoOperation(content);
+ }
}
}
}
@@ -728,6 +826,11 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
}
/** {@inheritDoc} */
+ public void setShadingMode(AFPShadingMode shadingMode) {
+ this.shadingMode = shadingMode;
+ }
+
+ /** {@inheritDoc} */
public void setResolution(int resolution) {
paintingState.setResolution(resolution);
}
diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
index fcc1140c7..007dd3861 100644
--- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
@@ -20,6 +20,7 @@
package org.apache.fop.render.afp;
import java.io.File;
+import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
@@ -67,8 +68,12 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
}
private AFPFontInfo buildFont(Configuration fontCfg, String fontPath)
- throws ConfigurationException {
+ throws ConfigurationException {
+
+ FontManager fontManager = this.userAgent.getFactory().getFontManager();
+ FontTriplet.Matcher referencedFontsMatcher = fontManager.getReferencedFontsMatcher();
+ boolean embeddable = true;
Configuration[] triple = fontCfg.getChildren("font-triplet");
List/*<FontTriplet>*/ tripletList = new java.util.ArrayList/*<FontTriplet>*/();
if (triple.length == 0) {
@@ -80,6 +85,9 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
FontTriplet triplet = new FontTriplet(triple[j].getAttribute("name"),
triple[j].getAttribute("style"),
weight);
+ if (referencedFontsMatcher != null && referencedFontsMatcher.matches(triplet)) {
+ embeddable = false;
+ }
tripletList.add(triplet);
}
@@ -109,7 +117,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
}
ResourceAccessor accessor = new DefaultFOPResourceAccessor(
this.userAgent,
- this.userAgent.getFactory().getFontManager().getFontBaseURL(),
+ fontManager.getFontBaseURL(),
baseURI);
String type = afpFontCfg.getAttribute("type");
@@ -134,11 +142,12 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
// Create a new font object
RasterFont font = new RasterFont(name);
+ font.setEmbeddable(embeddable);
Configuration[] rasters = afpFontCfg.getChildren("afp-raster-font");
if (rasters.length == 0) {
- log.error(
- "Mandatory font configuration elements '<afp-raster-font...' are missing");
+ log.error("Mandatory font configuration elements '<afp-raster-font...'"
+ + " are missing at " + afpFontCfg.getLocation());
return null;
}
for (int j = 0; j < rasters.length; j++) {
@@ -150,7 +159,8 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
"Mandatory afp-raster-font configuration attribute 'characterset=' is missing");
return null;
}
- int size = rasterCfg.getAttributeAsInteger("size");
+ float size = rasterCfg.getAttributeAsFloat("size");
+ int sizeMpt = (int)(size * 1000);
String base14 = rasterCfg.getAttribute("base14-font", null);
if (base14 != null) {
@@ -159,7 +169,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
+ base14);
try {
Typeface tf = (Typeface)clazz.newInstance();
- font.addCharacterSet(size, new FopCharacterSet(
+ font.addCharacterSet(sizeMpt, new FopCharacterSet(
codepage, encoding, characterset, tf));
} catch (Exception ie) {
String msg = "The base 14 font class " + clazz.getName()
@@ -172,7 +182,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
log.error(msg);
}
} else {
- font.addCharacterSet(size, new CharacterSet(
+ font.addCharacterSet(sizeMpt, new CharacterSet(
codepage, encoding, characterset, accessor));
}
}
@@ -210,6 +220,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
}
// Create a new font object
OutlineFont font = new OutlineFont(name, characterSet);
+ font.setEmbeddable(embeddable);
return new AFPFontInfo(font, tripletList);
} else {
log.error("No or incorrect type attribute");
@@ -299,6 +310,12 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
boolean nativeImageSupport = imagesCfg.getAttributeAsBoolean("native", false);
customizable.setNativeImagesSupported(nativeImageSupport);
+ // shading (filled rectangles)
+ Configuration shadingCfg = cfg.getChild("shading");
+ AFPShadingMode shadingMode = AFPShadingMode.valueOf(
+ shadingCfg.getValue(AFPShadingMode.COLOR.getName()));
+ customizable.setShadingMode(shadingMode);
+
// renderer resolution
Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false);
if (rendererResolutionCfg != null) {
@@ -312,16 +329,21 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
String resourceGroupDest = null;
try {
resourceGroupDest = resourceGroupFileCfg.getValue();
+ if (resourceGroupDest != null) {
+ File resourceGroupFile = new File(resourceGroupDest);
+ resourceGroupFile.createNewFile();
+ if (resourceGroupFile.canWrite()) {
+ customizable.setDefaultResourceGroupFilePath(resourceGroupDest);
+ } else {
+ log.warn("Unable to write to default external resource group file '"
+ + resourceGroupDest + "'");
+ }
+ }
} catch (ConfigurationException e) {
LogUtil.handleException(log, e,
userAgent.getFactory().validateUserConfigStrictly());
- }
- File resourceGroupFile = new File(resourceGroupDest);
- if (resourceGroupFile.canWrite()) {
- customizable.setDefaultResourceGroupFilePath(resourceGroupDest);
- } else {
- log.warn("Unable to write to default external resource group file '"
- + resourceGroupDest + "'");
+ } catch (IOException ioe) {
+ throw new FOPException("Could not create default external resource group file", ioe);
}
}
diff --git a/src/java/org/apache/fop/render/afp/AFPShadingMode.java b/src/java/org/apache/fop/render/afp/AFPShadingMode.java
new file mode 100644
index 000000000..b45c33a8e
--- /dev/null
+++ b/src/java/org/apache/fop/render/afp/AFPShadingMode.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.afp;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+/** Enumeration class for the AFP shading mode. */
+public final class AFPShadingMode implements Serializable {
+
+ private static final long serialVersionUID = 8579867898716480779L;
+
+ /** the color mode (the default) */
+ public static final AFPShadingMode COLOR = new AFPShadingMode("COLOR");
+ /** the dithered mode */
+ public static final AFPShadingMode DITHERED = new AFPShadingMode("DITHERED");
+
+ private String name;
+
+ /**
+ * Constructor to add a new named item.
+ * @param name Name of the item.
+ */
+ private AFPShadingMode(String name) {
+ this.name = name;
+ }
+
+ /** @return the name of the enumeration */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns the enumeration/singleton object based on its name.
+ * @param name the name of the enumeration value
+ * @return the enumeration object
+ */
+ public static AFPShadingMode valueOf(String name) {
+ if (COLOR.getName().equalsIgnoreCase(name)) {
+ return COLOR;
+ } else if (DITHERED.getName().equalsIgnoreCase(name)) {
+ return DITHERED;
+ } else {
+ throw new IllegalArgumentException("Illegal value for enumeration: " + name);
+ }
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return valueOf(getName());
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return getClass().getName() + ":" + name;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java
index 83615b75d..cb5478340 100755
--- a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java
@@ -42,6 +42,9 @@ public class AFPElementMapping extends ElementMapping {
/** include page segment element */
public static final String INCLUDE_PAGE_SEGMENT = "include-page-segment";
+ /** include form map element */
+ public static final String INCLUDE_FORM_MAP = "include-form-map";
+
/** NOP */
public static final String NO_OPERATION = "no-operation";
@@ -81,6 +84,9 @@ public class AFPElementMapping extends ElementMapping {
INCLUDE_PAGE_OVERLAY,
new AFPIncludePageOverlayMaker());
foObjs.put(
+ INCLUDE_FORM_MAP,
+ new AFPIncludeFormMapMaker());
+ foObjs.put(
NO_OPERATION,
new AFPNoOperationMaker());
foObjs.put(
@@ -101,6 +107,12 @@ public class AFPElementMapping extends ElementMapping {
}
}
+ static class AFPIncludeFormMapMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new AFPIncludeFormMapElement(parent, INCLUDE_FORM_MAP);
+ }
+ }
+
static class AFPTagLogicalElementMaker extends ElementMapping.Maker {
public FONode make(FONode parent) {
return new AFPPageSetupElement(parent, TAG_LOGICAL_ELEMENT);
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java
index e027e7f32..cc8de5f84 100644
--- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionAttachment.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.render.afp.extensions;
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java
index 1a8cfdf47..c487b1825 100644
--- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java
@@ -19,6 +19,9 @@
package org.apache.fop.render.afp.extensions;
+import java.net.URI;
+import java.net.URISyntaxException;
+
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
@@ -48,13 +51,14 @@ public class AFPExtensionHandler extends DefaultHandler
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
boolean handled = false;
- if (AFPPageSetup.CATEGORY.equals(uri)) {
+ if (AFPExtensionAttachment.CATEGORY.equals(uri)) {
lastAttributes = attributes;
handled = true;
if (localName.equals(AFPElementMapping.NO_OPERATION)
|| localName.equals(AFPElementMapping.TAG_LOGICAL_ELEMENT)
|| localName.equals(AFPElementMapping.INCLUDE_PAGE_OVERLAY)
|| localName.equals(AFPElementMapping.INCLUDE_PAGE_SEGMENT)
+ || localName.equals(AFPElementMapping.INCLUDE_FORM_MAP)
|| localName.equals(AFPElementMapping.INVOKE_MEDIUM_MAP)) {
//handled in endElement
} else {
@@ -62,7 +66,7 @@ public class AFPExtensionHandler extends DefaultHandler
}
}
if (!handled) {
- if (AFPPageSetup.CATEGORY.equals(uri)) {
+ if (AFPExtensionAttachment.CATEGORY.equals(uri)) {
throw new SAXException("Unhandled element " + localName
+ " in namespace: " + uri);
} else {
@@ -74,26 +78,38 @@ public class AFPExtensionHandler extends DefaultHandler
/** {@inheritDoc} */
public void endElement(String uri, String localName, String qName) throws SAXException {
- if (AFPPageSetup.CATEGORY.equals(uri)) {
- AFPPageSetup pageSetupExtn = null;
- if (localName.equals(AFPElementMapping.INVOKE_MEDIUM_MAP)) {
- this.returnedObject = new AFPInvokeMediumMap();
- }
- else {
- pageSetupExtn = new AFPPageSetup(localName);
- this.returnedObject = pageSetupExtn;
- }
- String name = lastAttributes.getValue("name");
- if (name != null) {
- returnedObject.setName(name);
- }
- String value = lastAttributes.getValue("value");
- if (value != null && pageSetupExtn != null) {
- pageSetupExtn.setValue(value);
- }
- if (content.length() > 0 && pageSetupExtn != null) {
- pageSetupExtn.setContent(content.toString());
- content.setLength(0); //Reset text buffer (see characters())
+ if (AFPExtensionAttachment.CATEGORY.equals(uri)) {
+ if (AFPElementMapping.INCLUDE_FORM_MAP.equals(localName)) {
+ AFPIncludeFormMap formMap = new AFPIncludeFormMap();
+ String name = lastAttributes.getValue("name");
+ formMap.setName(name);
+ String src = lastAttributes.getValue("src");
+ try {
+ formMap.setSrc(new URI(src));
+ } catch (URISyntaxException e) {
+ throw new SAXException("Invalid URI: " + src, e);
+ }
+ this.returnedObject = formMap;
+ } else {
+ AFPPageSetup pageSetupExtn = null;
+ if (AFPElementMapping.INVOKE_MEDIUM_MAP.equals(localName)) {
+ this.returnedObject = new AFPInvokeMediumMap();
+ } else {
+ pageSetupExtn = new AFPPageSetup(localName);
+ this.returnedObject = pageSetupExtn;
+ }
+ String name = lastAttributes.getValue("name");
+ if (name != null) {
+ returnedObject.setName(name);
+ }
+ String value = lastAttributes.getValue("value");
+ if (value != null && pageSetupExtn != null) {
+ pageSetupExtn.setValue(value);
+ }
+ if (content.length() > 0 && pageSetupExtn != null) {
+ pageSetupExtn.setContent(content.toString());
+ content.setLength(0); //Reset text buffer (see characters())
+ }
}
}
}
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java
new file mode 100644
index 000000000..06c7cbc9d
--- /dev/null
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.afp.extensions;
+
+import java.net.URI;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * This extension allows to include an AFP form map resource. It is implemented as an extension
+ * attachment ({@link ExtensionAttachment}).
+ */
+public class AFPIncludeFormMap extends AFPExtensionAttachment {
+
+ private static final long serialVersionUID = 8548056652642588914L;
+
+ /** src attribute containing the URI to the form map resource */
+ protected static final String ATT_SRC = "src";
+
+ /**
+ * the URI identifying the form map resource.
+ */
+ protected URI src;
+
+ /**
+ * Default constructor.
+ */
+ public AFPIncludeFormMap() {
+ super(AFPElementMapping.INCLUDE_FORM_MAP);
+ }
+
+ /**
+ * Returns the URI of the form map.
+ * @return the form map URI
+ */
+ public URI getSrc() {
+ return this.src;
+ }
+
+ /**
+ * Sets the URI of the form map.
+ * @param value the form map URI
+ */
+ public void setSrc(URI value) {
+ this.src = value;
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ if (name != null && name.length() > 0) {
+ atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", name);
+ }
+ if (this.src != null) {
+ atts.addAttribute(null, ATT_SRC, ATT_SRC, "CDATA", this.src.toASCIIString());
+ }
+ handler.startElement(CATEGORY, elementName, elementName, atts);
+ handler.endElement(CATEGORY, elementName, elementName);
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return getClass().getName() + "(element-name=" + getElementName()
+ + " name=" + getName() + " src=" + getSrc() + ")";
+ }
+}
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java
new file mode 100644
index 000000000..719d8c765
--- /dev/null
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.afp.extensions;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fo.extensions.ExtensionObj;
+
+/**
+ * This class extends the {@link ExtensionObj} class. It represents the "include-form-map"
+ * extension in the FO tree.
+ */
+public class AFPIncludeFormMapElement extends AbstractAFPExtensionObject {
+
+ private static final String ATT_SRC = "src";
+
+ /**
+ * Constructs an AFP object (called by Maker).
+ *
+ * @param parent the parent formatting object
+ * @param name the name of the AFP element
+ */
+ public AFPIncludeFormMapElement(FONode parent, String name) {
+ super(parent, name);
+ }
+
+ private AFPIncludeFormMap getFormMapAttachment() {
+ return (AFPIncludeFormMap)getExtensionAttachment();
+ }
+
+ /** {@inheritDoc} */
+ protected void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
+ "rule.childOfDeclarations");
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void processNode(String elementName, Locator locator,
+ Attributes attlist, PropertyList propertyList)
+ throws FOPException {
+ super.processNode(elementName, locator, attlist, propertyList);
+ AFPIncludeFormMap formMap = getFormMapAttachment();
+ String attr = attlist.getValue(ATT_SRC);
+ if (attr != null && attr.length() > 0) {
+ try {
+ formMap.setSrc(new URI(attr));
+ } catch (URISyntaxException e) {
+ getFOValidationEventProducer().invalidPropertyValue(this,
+ elementName, ATT_SRC, attr, null, getLocator());
+ }
+ } else {
+ missingPropertyError(ATT_SRC);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected ExtensionAttachment instantiateExtensionAttachment() {
+ return new AFPIncludeFormMap();
+ }
+}
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java
index 99805edd4..f36bd3e12 100644
--- a/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPInvokeMediumMapElement.java
@@ -26,8 +26,8 @@ import org.apache.fop.fo.extensions.ExtensionAttachment;
/**
* This class represents an AFP-specific extension element to embed Invoke Medium Map (IMM)
- * fields at the beginning of a page group. The element is optional and expected as a direct child
- * of an fo:page-sequence.
+ * fields at the beginning of a page group or just prior to a Page. The element is optional
+ * and expected as a direct child of an fo:page-sequence or fo:simple-page-master
*/
public class AFPInvokeMediumMapElement extends AbstractAFPExtensionObject {
@@ -42,7 +42,9 @@ public class AFPInvokeMediumMapElement extends AbstractAFPExtensionObject {
/** {@inheritDoc} */
protected void startOfNode() throws FOPException {
super.startOfNode();
- if (parent.getNameId() != Constants.FO_PAGE_SEQUENCE) {
+ if (parent.getNameId() != Constants.FO_PAGE_SEQUENCE
+ && parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
+
invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
"rule.childOfPageSequence");
}
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java
index 0c6dfadc4..19f98f32a 100755
--- a/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java
@@ -36,6 +36,9 @@ import org.apache.fop.fo.extensions.ExtensionAttachment;
*/
public class AFPPageSetupElement extends AbstractAFPExtensionObject {
+ private static final String ATT_VALUE = "value";
+ private static final String ATT_SRC = "src";
+
/**
* Constructs an AFP object (called by Maker).
*
@@ -86,18 +89,18 @@ public class AFPPageSetupElement extends AbstractAFPExtensionObject {
super.processNode(elementName, locator, attlist, propertyList);
AFPPageSetup pageSetup = getPageSetupAttachment();
if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(elementName)) {
- String attr = attlist.getValue("src");
+ String attr = attlist.getValue(ATT_SRC);
if (attr != null && attr.length() > 0) {
pageSetup.setValue(attr);
} else {
- throw new FOPException(elementName + " must have a src attribute.");
+ missingPropertyError(ATT_SRC);
}
} else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(elementName)) {
- String attr = attlist.getValue("value");
+ String attr = attlist.getValue(ATT_VALUE);
if (attr != null && attr.length() > 0) {
pageSetup.setValue(attr);
} else {
- throw new FOPException(elementName + " must have a value attribute.");
+ missingPropertyError(ATT_VALUE);
}
}
}
diff --git a/src/java/org/apache/fop/render/awt/AWTRenderer.java b/src/java/org/apache/fop/render/awt/AWTRenderer.java
index 5b4c6b13a..b50708112 100644
--- a/src/java/org/apache/fop/render/awt/AWTRenderer.java
+++ b/src/java/org/apache/fop/render/awt/AWTRenderer.java
@@ -30,6 +30,7 @@ package org.apache.fop.render.awt;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.Rectangle2D;
+import java.awt.geom.Point2D;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Paper;
@@ -46,6 +47,7 @@ import org.apache.fop.render.awt.viewer.PreviewDialog;
import org.apache.fop.render.awt.viewer.Renderable;
import org.apache.fop.render.awt.viewer.StatusListener;
import org.apache.fop.render.java2d.Java2DRenderer;
+import org.apache.fop.render.extensions.prepress.PageScale;
/**
* The AWTRender outputs the pages generated by the layout engine to a Swing
@@ -149,11 +151,23 @@ public class AWTRenderer extends Java2DRenderer implements Pageable {
Rectangle2D bounds = getPageViewport(pageNum).getViewArea();
pageWidth = (int) Math.round(bounds.getWidth() / 1000f);
pageHeight = (int) Math.round(bounds.getHeight() / 1000f);
- double scale = scaleFactor
+ double scaleX = scaleFactor
* (25.4 / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
/ userAgent.getTargetPixelUnitToMillimeter();
- int bitmapWidth = (int) ((pageWidth * scale) + 0.5);
- int bitmapHeight = (int) ((pageHeight * scale) + 0.5);
+ double scaleY = scaleFactor
+ * (25.4 / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
+ / userAgent.getTargetPixelUnitToMillimeter();
+ if (getPageViewport(pageNum).getForeignAttributes() != null) {
+ String scale = (String) getPageViewport(pageNum).getForeignAttributes().get(
+ PageScale.EXT_PAGE_SCALE);
+ Point2D scales = PageScale.getScale(scale);
+ if (scales != null) {
+ scaleX *= scales.getX();
+ scaleY *= scales.getY();
+ }
+ }
+ int bitmapWidth = (int) ((pageWidth * scaleX) + 0.5);
+ int bitmapHeight = (int) ((pageHeight * scaleY) + 0.5);
return new Dimension(bitmapWidth, bitmapHeight);
}
diff --git a/src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java b/src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java
new file mode 100644
index 000000000..8001e1fc2
--- /dev/null
+++ b/src/java/org/apache/fop/render/extensions/prepress/PageBoundaries.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.xmlgraphics.util.QName;
+
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fo.properties.FixedLength;
+
+/**
+ * This class is used to calculate the effective boundaries of a page including special-purpose
+ * boxes used in prepress. These are specified using extension attributes:
+ * bleedBox, trimBox and cropBox. The semantics are further described on the website.
+ */
+public class PageBoundaries {
+
+ /**
+ * The extension attribute for calculating the PDF BleedBox area - specifies the bleed width.
+ */
+ public static final QName EXT_BLEED
+ = new QName(ExtensionElementMapping.URI, null, "bleed");
+
+ /**
+ * The extension attribute for the PDF CropBox area.
+ */
+ public static final QName EXT_CROP_OFFSET
+ = new QName(ExtensionElementMapping.URI, null, "crop-offset");
+
+ /**
+ * The extension attribute for the PDF CropBox area.
+ */
+ public static final QName EXT_CROP_BOX
+ = new QName(ExtensionElementMapping.URI, null, "crop-box");
+
+
+ private static final Pattern SIZE_UNIT_PATTERN
+ = Pattern.compile("^(-?\\d*\\.?\\d*)(px|in|cm|mm|pt|pc|mpt)$");
+
+ private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
+
+ private Rectangle trimBox;
+ private Rectangle bleedBox;
+ private Rectangle mediaBox;
+ private Rectangle cropBox;
+
+ /**
+ * Creates a new instance.
+ * @param pageSize the page size (in mpt) defined by the simple-page-master.
+ * @param bleed the bleed value (raw value as given in the property value)
+ * @param cropOffset the crop-offset value (raw value as given in the property value)
+ * @param cropBoxSelector the crop-box, valid values: (trim-box|bleed-box|media-box)
+ */
+ public PageBoundaries(Dimension pageSize, String bleed, String cropOffset,
+ String cropBoxSelector) {
+ calculate(pageSize, bleed, cropOffset, cropBoxSelector);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param pageSize the page size (in mpt) defined by the simple-page-master.
+ * @param foreignAttributes the foreign attributes for the page
+ * (used to extract the extension attribute values)
+ */
+ public PageBoundaries(Dimension pageSize, Map foreignAttributes) {
+ String bleed = (String)foreignAttributes.get(EXT_BLEED);
+ String cropOffset = (String)foreignAttributes.get(EXT_CROP_OFFSET);
+ String cropBoxSelector = (String)foreignAttributes.get(EXT_CROP_BOX);
+ calculate(pageSize, bleed, cropOffset, cropBoxSelector);
+ }
+
+ private void calculate(Dimension pageSize, String bleed, String cropOffset,
+ String cropBoxSelector) {
+ this.trimBox = new Rectangle(pageSize);
+ this.bleedBox = getBleedBoxRectangle(this.trimBox, bleed);
+ Rectangle cropMarksBox = getCropMarksAreaRectangle(trimBox, cropOffset);
+
+ //MediaBox includes all of the following three rectangles
+ this.mediaBox = new Rectangle();
+ this.mediaBox.add(this.trimBox);
+ this.mediaBox.add(this.bleedBox);
+ this.mediaBox.add(cropMarksBox);
+
+ if ("trim-box".equals(cropBoxSelector)) {
+ this.cropBox = this.trimBox;
+ } else if ("bleed-box".equals(cropBoxSelector)) {
+ this.cropBox = this.bleedBox;
+ } else if ("media-box".equals(cropBoxSelector)
+ || cropBoxSelector == null
+ || "".equals(cropBoxSelector)) {
+ this.cropBox = this.mediaBox;
+ } else {
+ final String err = "The crop-box has invalid value: {0}, "
+ + "possible values of crop-box: (trim-box|bleed-box|media-box)";
+ throw new IllegalArgumentException(MessageFormat.format(err,
+ new Object[]{cropBoxSelector}));
+ }
+ }
+
+ /**
+ * Returns the trim box for the page. This is equal to the page size given in XSL-FO.
+ * After production the printed media is trimmed to this rectangle.
+ * @return the trim box
+ */
+ public Rectangle getTrimBox() {
+ return this.trimBox;
+ }
+
+ /**
+ * Returns the bleed box for the page.
+ * @return the bleed box
+ */
+ public Rectangle getBleedBox() {
+ return this.bleedBox;
+ }
+
+ /**
+ * Returns the media box for the page.
+ * @return the media box
+ */
+ public Rectangle getMediaBox() {
+ return this.mediaBox;
+ }
+
+ /**
+ * Returns the crop box for the page. The crop box is used by Adobe Acrobat to select which
+ * parts of the document shall be displayed and it also defines the rectangle to which a
+ * RIP will clip the document. For bitmap output, this defines the size of the bitmap.
+ * @return the crop box
+ */
+ public Rectangle getCropBox() {
+ return this.cropBox;
+ }
+
+ /**
+ * The BleedBox is calculated by expanding the TrimBox by the bleed widths.
+ *
+ * @param trimBox the TrimBox rectangle
+ * @param bleed the given bleed widths
+ * @return the calculated BleedBox rectangle
+ */
+ private static Rectangle getBleedBoxRectangle(Rectangle trimBox, String bleed) {
+ return getRectangleUsingOffset(trimBox, bleed);
+ }
+
+ /**
+ * The MediaBox is calculated by expanding the TrimBox by the crop offsets.
+ *
+ * @param trimBox the TrimBox rectangle
+ * @param cropOffsets the given crop offsets
+ * @return the calculated MediaBox rectangle
+ */
+ private static Rectangle getCropMarksAreaRectangle(Rectangle trimBox, String cropOffsets) {
+ return getRectangleUsingOffset(trimBox, cropOffsets);
+ }
+
+ private static Rectangle getRectangleUsingOffset(Rectangle originalRect, String offset) {
+ if (offset == null || "".equals(offset) || originalRect == null) {
+ return originalRect;
+ }
+
+ String[] offsets = WHITESPACE_PATTERN.split(offset);
+ int[] coords = new int[4]; // top, right, bottom, left
+ switch (offsets.length) {
+ case 1:
+ coords[0] = getLengthIntValue(offsets[0]);
+ coords[1] = coords[0];
+ coords[2] = coords[0];
+ coords[3] = coords[0];
+ break;
+ case 2:
+ coords[0] = getLengthIntValue(offsets[0]);
+ coords[1] = getLengthIntValue(offsets[1]);
+ coords[2] = coords[0];
+ coords[3] = coords[1];
+ break;
+ case 3:
+ coords[0] = getLengthIntValue(offsets[0]);
+ coords[1] = getLengthIntValue(offsets[1]);
+ coords[2] = getLengthIntValue(offsets[2]);
+ coords[3] = coords[1];
+ break;
+ case 4:
+ coords[0] = getLengthIntValue(offsets[0]);
+ coords[1] = getLengthIntValue(offsets[1]);
+ coords[2] = getLengthIntValue(offsets[2]);
+ coords[3] = getLengthIntValue(offsets[3]);
+ break;
+ default:
+ // TODO throw appropriate exception that can be caught by the event
+ // notification mechanism
+ throw new IllegalArgumentException("Too many arguments");
+ }
+ return new Rectangle(originalRect.x - coords[3],
+ originalRect.y - coords[0],
+ originalRect.width + coords[3] + coords[1],
+ originalRect.height + coords[0] + coords[2]);
+ }
+
+ private static int getLengthIntValue(final String length) {
+ final String err = "Incorrect length value: {0}";
+ Matcher m = SIZE_UNIT_PATTERN.matcher(length);
+
+ if (m.find()) {
+ return FixedLength.getInstance(Double.parseDouble(m.group(1)),
+ m.group(2)).getLength().getValue();
+ } else {
+ throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{length}));
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/extensions/prepress/PageScale.java b/src/java/org/apache/fop/render/extensions/prepress/PageScale.java
new file mode 100644
index 000000000..361423753
--- /dev/null
+++ b/src/java/org/apache/fop/render/extensions/prepress/PageScale.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.geom.Point2D;
+import java.text.MessageFormat;
+import java.util.regex.Pattern;
+
+import org.apache.xmlgraphics.util.QName;
+
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+
+/**
+ * This class provides utility methods to parse the 'fox:scale' extension attribute.
+ */
+public final class PageScale {
+
+ /**
+ * The extension 'scale' attribute for the simple-page-master element.
+ */
+ public static final QName EXT_PAGE_SCALE
+ = new QName(ExtensionElementMapping.URI, null, "scale");
+
+ private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
+
+ /**
+ * Utility classes should not have a public or default constructor
+ */
+ private PageScale() {
+ }
+
+ /**
+ * Compute scale parameters from given fox:scale attribute which has the format: scaleX [scaleY]
+ * If scaleY is not defined, it equals scaleX.
+ * @param scale scale attribute, input format: scaleX [scaleY]
+ * @return the pair of (sx, sy) values
+ */
+ public static Point2D getScale(String scale) {
+ // TODO throw appropriate exceptions that can be caught by the event
+ // notification mechanism
+ final String err = "Extension 'scale' attribute has incorrect value(s): {0}";
+
+ if (scale == null || scale.equals("")) {
+ return null;
+ }
+
+ String[] scales = WHITESPACE_PATTERN.split(scale);
+ double scaleX;
+ try {
+ scaleX = Double.parseDouble(scales[0]);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{scale}));
+ }
+ double scaleY;
+ switch (scales.length) {
+ case 1:
+ scaleY = scaleX;
+ break;
+ case 2:
+ try {
+ scaleY = Double.parseDouble(scales[1]);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{scale}));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Too many arguments");
+ }
+ if (scaleX <= 0 || scaleY <= 0) {
+ throw new IllegalArgumentException(MessageFormat.format(err, new Object[]{scale}));
+ }
+
+ return new Point2D.Double(scaleX, scaleY);
+ }
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index 7c0bf8abc..2a23d06f8 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -563,10 +563,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
try {
pageIndices.put(page.getKey(), new Integer(page.getPageIndex()));
- Rectangle2D viewArea = page.getViewArea();
- Dimension dim = new Dimension(
- (int)Math.ceil(viewArea.getWidth()),
- (int)Math.ceil(viewArea.getHeight()));
+ Rectangle viewArea = page.getViewArea();
+ Dimension dim = new Dimension(viewArea.width, viewArea.height);
establishForeignAttributes(page.getForeignAttributes());
documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(),
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
index 8516277c5..376130838 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
@@ -106,9 +106,17 @@ public class DocumentNavigationHandler extends DefaultHandler
} else {
String id = attributes.getValue("id");
int pageIndex = XMLUtil.getAttributeAsInt(attributes, "page-index");
- int x = XMLUtil.getAttributeAsInt(attributes, "x");
- int y = XMLUtil.getAttributeAsInt(attributes, "y");
- action = new GoToXYAction(id, pageIndex, new Point(x, y));
+ final Point location;
+ if (pageIndex < 0) {
+ location = null;
+ } else {
+ final int x = XMLUtil
+ .getAttributeAsInt(attributes, "x");
+ final int y = XMLUtil
+ .getAttributeAsInt(attributes, "y");
+ location = new Point(x, y);
+ }
+ action = new GoToXYAction(id, pageIndex, location);
}
objectStack.push(action);
} else if (GOTO_URI.getLocalName().equals(localName)) {
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java b/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java
index 7efb82a12..a2b4f31b6 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java
@@ -71,18 +71,34 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx
/**
* Returns the page index of the target page.
+ * <p>
+ * This function will always return a valid value for safety. Use
+ * {@link #isComplete()} to check if the link is actually complete.
+ *
* @return the page index (0-based)
*/
public int getPageIndex() {
- return this.pageIndex;
+ if (this.pageIndex >= 0) {
+ return this.pageIndex;
+ } else {
+ return 0;
+ }
}
/**
* Returns the absolute coordinates of the target location on the page.
+ * <p>
+ * This function will always return a valid value for safety. Use
+ * {@link #isComplete()} to check if the link is actually complete.
+ *
* @return the target location (coordinates in millipoints)
*/
public Point getTargetLocation() {
- return this.targetLocation;
+ if (this.targetLocation == null) {
+ return new Point(0, 0);
+ } else {
+ return this.targetLocation;
+ }
}
/**
@@ -93,9 +109,13 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx
this.targetLocation = location;
}
+ private boolean isCompleteExceptTargetLocation() {
+ return (getPageIndex() >= 0);
+ }
+
/** {@inheritDoc} */
public boolean isComplete() {
- return (getPageIndex() >= 0) && (getTargetLocation() != null);
+ return this.isCompleteExceptTargetLocation() && (this.targetLocation != null);
}
/** {@inheritDoc} */
@@ -107,10 +127,10 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx
return false;
}
GoToXYAction otherAction = (GoToXYAction)other;
- if (getPageIndex() != otherAction.getPageIndex()) {
+ if (this.pageIndex != otherAction.pageIndex) {
return false;
}
- if (getTargetLocation() == null || otherAction.getTargetLocation() == null) {
+ if (this.targetLocation == null || otherAction.targetLocation == null) {
return false;
}
if (!getTargetLocation().equals(otherAction.getTargetLocation())) {
@@ -121,16 +141,16 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx
/** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
- if (getTargetLocation() == null) {
- setTargetLocation(new Point(0, 0));
- }
AttributesImpl atts = new AttributesImpl();
- if (isComplete()) {
+ if (this.isCompleteExceptTargetLocation()) {
+ final Point reportedTargetLocation = this.getTargetLocation();
atts.addAttribute(null, "id", "id", XMLUtil.CDATA, getID());
atts.addAttribute(null, "page-index", "page-index",
XMLUtil.CDATA, Integer.toString(pageIndex));
- atts.addAttribute(null, "x", "x", XMLUtil.CDATA, Integer.toString(targetLocation.x));
- atts.addAttribute(null, "y", "y", XMLUtil.CDATA, Integer.toString(targetLocation.y));
+ atts.addAttribute(null, "x", "x", XMLUtil.CDATA,
+ Integer.toString(reportedTargetLocation.x));
+ atts.addAttribute(null, "y", "y", XMLUtil.CDATA,
+ Integer.toString(reportedTargetLocation.y));
} else {
atts.addAttribute(null, "idref", "idref", XMLUtil.CDATA, getID());
}
diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
index 933398125..f09794ff2 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
@@ -24,6 +24,7 @@ import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
+import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
@@ -75,6 +76,8 @@ import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.extensions.prepress.PageBoundaries;
+import org.apache.fop.render.extensions.prepress.PageScale;
import org.apache.fop.render.pdf.CTMHelper;
import org.apache.fop.util.CharUtilities;
import org.apache.fop.util.ColorUtil;
@@ -290,7 +293,10 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
this.currentPageViewport = pageViewport;
try {
- Rectangle2D bounds = pageViewport.getViewArea();
+ PageBoundaries boundaries = new PageBoundaries(
+ pageViewport.getViewArea().getSize(), pageViewport.getForeignAttributes());
+ Rectangle bounds = boundaries.getCropBox();
+ Rectangle bleedBox = boundaries.getBleedBox();
this.pageWidth = (int) Math.round(bounds.getWidth() / 1000f);
this.pageHeight = (int) Math.round(bounds.getHeight() / 1000f);
@@ -299,11 +305,25 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
+ " (pageWidth " + pageWidth + ", pageHeight "
+ pageHeight + ")");
- double scale = scaleFactor
+ // set scale factor
+ double scaleX = scaleFactor;
+ double scaleY = scaleFactor;
+ String scale = (String) currentPageViewport.getForeignAttributes().get(
+ PageScale.EXT_PAGE_SCALE);
+ Point2D scales = PageScale.getScale(scale);
+ if (scales != null) {
+ scaleX *= scales.getX();
+ scaleY *= scales.getY();
+ }
+
+ scaleX = scaleX
+ * (25.4f / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
+ / userAgent.getTargetPixelUnitToMillimeter();
+ scaleY = scaleY
* (25.4f / FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION)
/ userAgent.getTargetPixelUnitToMillimeter();
- int bitmapWidth = (int) ((pageWidth * scale) + 0.5);
- int bitmapHeight = (int) ((pageHeight * scale) + 0.5);
+ int bitmapWidth = (int) ((pageWidth * scaleX) + 0.5);
+ int bitmapHeight = (int) ((pageHeight * scaleY) + 0.5);
BufferedImage currentPageImage = getBufferedImage(bitmapWidth, bitmapHeight);
@@ -326,20 +346,27 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
// transform page based on scale factor supplied
AffineTransform at = graphics.getTransform();
- at.scale(scale, scale);
+ at.scale(scaleX, scaleY);
+ at.translate(bounds.getMinX() / -1000f, bounds.getMinY() / -1000f);
graphics.setTransform(at);
// draw page frame
if (!transparentPageBackground) {
graphics.setColor(Color.white);
- graphics.fillRect(0, 0, pageWidth, pageHeight);
+ graphics.fillRect(
+ (int)Math.round(bleedBox.getMinX() / 1000f),
+ (int)Math.round(bleedBox.getMinY() / 1000f),
+ (int)Math.round(bleedBox.getWidth() / 1000f),
+ (int)Math.round(bleedBox.getHeight() / 1000f));
}
+ /* why did we have this???
graphics.setColor(Color.black);
graphics.drawRect(-1, -1, pageWidth + 2, pageHeight + 2);
graphics.drawLine(pageWidth + 2, 0, pageWidth + 2, pageHeight + 2);
graphics.drawLine(pageWidth + 3, 1, pageWidth + 3, pageHeight + 3);
graphics.drawLine(0, pageHeight + 2, pageWidth + 2, pageHeight + 2);
graphics.drawLine(1, pageHeight + 3, pageWidth + 3, pageHeight + 3);
+ */
state = new Java2DGraphicsState(graphics, this.fontInfo, at);
try {
diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
index e4923b2c3..2ba3582e6 100644
--- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
@@ -186,6 +186,13 @@ public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
gen.selectPaperSource(Integer.parseInt(paperSource.toString()));
}
+ //Output bin
+ Object outputBin = getContext().getForeignAttribute(
+ PCLElementMapping.PCL_OUTPUT_BIN);
+ if (outputBin != null) {
+ gen.selectOutputBin(Integer.parseInt(outputBin.toString()));
+ }
+
// Is Page duplex?
Object pageDuplex = getContext().getForeignAttribute(
PCLElementMapping.PCL_DUPLEX_MODE);
diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java
index 797daa3a1..85653e423 100644
--- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java
+++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandlerMaker.java
@@ -30,7 +30,10 @@ import org.apache.fop.render.intermediate.IFDocumentHandler;
*/
public class PCLDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker {
- private static final String[] MIMES = new String[] {MimeConstants.MIME_PCL};
+ private static final String[] MIMES = new String[] {
+ MimeConstants.MIME_PCL,
+ MimeConstants.MIME_PCL_ALT
+ };
/** {@inheritDoc} */
public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java
index c36d2a66e..f89c03add 100644
--- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java
+++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java
@@ -48,6 +48,7 @@ import org.apache.xmlgraphics.image.GraphicsUtil;
import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.util.bitmap.BitmapImageUtil;
+import org.apache.fop.util.bitmap.DitherUtil;
import org.apache.fop.util.bitmap.MonochromeBitmapConverter;
/**
@@ -65,11 +66,6 @@ public class PCLGenerator {
/** A list of all supported resolutions in PCL (values in dpi) */
public static final int[] PCL_RESOLUTIONS = new int[] {75, 100, 150, 200, 300, 600};
- /** Selects a 4x4 Bayer dither matrix (17 grayscales) */
- public static final int DITHER_MATRIX_4X4 = 4;
- /** Selects a 8x8 Bayer dither matrix (65 grayscales) */
- public static final int DITHER_MATRIX_8X8 = 8;
-
private final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
private final DecimalFormat df2 = new DecimalFormat("0.##", symbols);
private final DecimalFormat df4 = new DecimalFormat("0.####", symbols);
@@ -244,6 +240,18 @@ public class PCLGenerator {
}
/**
+ * Selects the output bin. The parameter is usually printer-specific. Usually, "1" is the
+ * default output bin (upper bin) and "2" is the lower (rear) output bin. Some printers
+ * may support additional output bins. Consult the technical reference for your printer
+ * for all available values.
+ * @param selector the integer representing the output bin
+ * @throws IOException In case of an I/O error
+ */
+ public void selectOutputBin(int selector) throws IOException {
+ writeCommand("&l" + selector + "G");
+ }
+
+ /**
* Selects the duplexing mode for the page.
* The parameter is usually printer-specific.
* "0" means Simplex,
@@ -390,7 +398,7 @@ public class PCLGenerator {
writeCommand("*c" + lineshade + "G");
writeCommand("*c2P"); //Shaded fill
} else {
- defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4);
+ defineGrayscalePattern(col, 32, DitherUtil.DITHER_MATRIX_4X4);
writeCommand("*c" + formatDouble4(w / 100.0) + "h"
+ formatDouble4(h / 100.0) + "V");
@@ -401,34 +409,6 @@ public class PCLGenerator {
setPatternTransparencyMode(true);
}
- //Bayer dither matrices (4x4 and 8x8 are derived from the 2x2 matrix)
- private static final int[] BAYER_D2 = new int[] {0, 2, 3, 1};
- private static final int[] BAYER_D4;
- private static final int[] BAYER_D8;
-
- static {
- BAYER_D4 = deriveBayerMatrix(BAYER_D2);
- BAYER_D8 = deriveBayerMatrix(BAYER_D4);
- }
-
- private static void setValueInMatrix(int[] dn, int half, int part, int idx, int value) {
- int xoff = (part & 1) * half;
- int yoff = (part & 2) * half * half;
- int matrixIndex = yoff + ((idx / half) * half * 2) + (idx % half) + xoff;
- dn[matrixIndex] = value;
- }
-
- private static int[] deriveBayerMatrix(int[] d) {
- int[] dn = new int[d.length * 4];
- int half = (int)Math.sqrt(d.length);
- for (int part = 0; part < 4; part++) {
- for (int i = 0, c = d.length; i < c; i++) {
- setValueInMatrix(dn, half, part, i, d[i] * 4 + BAYER_D2[part]);
- }
- }
- return dn;
- }
-
/**
* Generates a user-defined pattern for a dithering pattern matching the grayscale value
* of the color given.
@@ -453,35 +433,12 @@ public class PCLGenerator {
byte[] pattern;
if (ditherMatrixSize == 8) {
- int gray65 = gray255 * 65 / 255;
-
- pattern = new byte[BAYER_D8.length / 8];
-
- for (int i = 0, c = BAYER_D8.length; i < c; i++) {
- boolean dot = !(BAYER_D8[i] < gray65 - 1);
- if (dot) {
- int byteIdx = i / 8;
- pattern[byteIdx] |= 1 << (i % 8);
- }
- }
+ pattern = DitherUtil.getBayerDither(DitherUtil.DITHER_MATRIX_8X8, gray255, false);
} else {
- int gray17 = gray255 * 17 / 255;
-
//Since a 4x4 pattern did not work, the 4x4 pattern is applied 4 times to an
//8x8 pattern. Maybe this could be changed to use an 8x8 bayer dither pattern
//instead of the 4x4 one.
- pattern = new byte[BAYER_D4.length / 8 * 4];
-
- for (int i = 0, c = BAYER_D4.length; i < c; i++) {
- boolean dot = !(BAYER_D4[i] < gray17 - 1);
- if (dot) {
- int byteIdx = i / 4;
- pattern[byteIdx] |= 1 << (i % 4);
- pattern[byteIdx] |= 1 << ((i % 4) + 4);
- pattern[byteIdx + 4] |= 1 << (i % 4);
- pattern[byteIdx + 4] |= 1 << ((i % 4) + 4);
- }
- }
+ pattern = DitherUtil.getBayerDither(DitherUtil.DITHER_MATRIX_4X4, gray255, true);
}
data.write(pattern);
if ((baout.size() % 2) > 0) {
@@ -564,7 +521,7 @@ public class PCLGenerator {
if (usePCLShades) {
selectCurrentPattern(convertToPCLShade(col), 2);
} else {
- defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4);
+ defineGrayscalePattern(col, 32, DitherUtil.DITHER_MATRIX_4X4);
selectCurrentPattern(32, 4);
}
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java b/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java
index 54ad1e73a..3e577cfa0 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPageDefinition.java
@@ -177,6 +177,9 @@ public class PCLPageDefinition {
pageDefinitions.add(new PCLPageDefinition("Ledger", 6,
createPhysicalPageSizeInch(11, 17),
createLogicalPageRect(75, 0, 3150, 5100), false));
+ pageDefinitions.add(new PCLPageDefinition("A5", 25,
+ createPhysicalPageSizeMm(148, 210),
+ createLogicalPageRect(71, 0, 1745, 2480), false));
pageDefinitions.add(new PCLPageDefinition("A4", 26,
createPhysicalPageSizeMm(210, 297),
createLogicalPageRect(71, 0, 2338, 3507), false));
@@ -198,6 +201,9 @@ public class PCLPageDefinition {
pageDefinitions.add(new PCLPageDefinition("LedgerL", 6,
createPhysicalPageSizeInch(17, 11),
createLogicalPageRect(60, 0, 4980, 3300), true));
+ pageDefinitions.add(new PCLPageDefinition("A5L", 25,
+ createPhysicalPageSizeMm(210, 148),
+ createLogicalPageRect(59, 0, 2362, 1747), true));
pageDefinitions.add(new PCLPageDefinition("A4L", 26,
createPhysicalPageSizeMm(297, 210),
createLogicalPageRect(59, 0, 3389, 2480), true));
diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderer.java b/src/java/org/apache/fop/render/pcl/PCLRenderer.java
index 0c1373fb8..7b48dbccf 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRenderer.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRenderer.java
@@ -289,6 +289,12 @@ public class PCLRenderer extends PrintRenderer implements PCLConstants {
gen.selectPaperSource(Integer.parseInt(paperSource));
}
+ //Output bin
+ String outputBin = page.getForeignAttributeValue(PCLElementMapping.PCL_OUTPUT_BIN);
+ if (outputBin != null) {
+ gen.selectOutputBin(Integer.parseInt(outputBin));
+ }
+
// Is Page duplex?
String pageDuplex = page.getForeignAttributeValue(PCLElementMapping.PCL_DUPLEX_MODE);
if (pageDuplex != null) {
diff --git a/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java b/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java
index 53931f671..700a95c65 100644
--- a/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java
+++ b/src/java/org/apache/fop/render/pcl/extensions/PCLElementMapping.java
@@ -40,6 +40,10 @@ public class PCLElementMapping extends ElementMapping {
public static final QName PCL_PAPER_SOURCE
= new QName(PCLElementMapping.NAMESPACE, null, "paper-source");
+ /** The extension attribute for the PCL output bin */
+ public static final QName PCL_OUTPUT_BIN
+ = new QName(PCLElementMapping.NAMESPACE, null, "output-bin");
+
/** The extension attribute for the PCL duplex mode */
public static final QName PCL_DUPLEX_MODE
= new QName(PCLElementMapping.NAMESPACE, null, "duplex-mode");
diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
index cbaac4e55..165236359 100644
--- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
@@ -129,12 +129,12 @@ public abstract class AbstractImageAdapter implements PDFImage {
if (cs == null && desc.startsWith("sRGB")) {
//It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer
cs = doc.getResources().getColorSpace("DefaultRGB");
- if (cs == null) {
- //sRGB hasn't been set up for the PDF document
- //so install but don't set to DefaultRGB
- cs = PDFICCBasedColorSpace.setupsRGBColorSpace(doc);
- }
}
+ if (cs == null) {
+ // sRGB hasn't been set up for the PDF document
+ // so install but don't set to DefaultRGB
+ cs = PDFICCBasedColorSpace.setupsRGBColorSpace(doc);
+ }
pdfICCStream = cs.getICCStream();
}
return pdfICCStream;
diff --git a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
index 5ddcd06c6..27d25e15b 100644
--- a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
@@ -19,6 +19,8 @@
package org.apache.fop.render.pdf;
import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
@@ -97,9 +99,19 @@ public class ImageRenderedAdapter extends AbstractImageAdapter {
}
/** {@inheritDoc} */
+ protected ICC_Profile getEffectiveICCProfile() {
+ ColorSpace cs = getImageColorSpace();
+ if (cs instanceof ICC_ColorSpace) {
+ ICC_ColorSpace iccSpace = (ICC_ColorSpace)cs;
+ return iccSpace.getProfile();
+ } else {
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
public void setup(PDFDocument doc) {
RenderedImage ri = getImage().getRenderedImage();
- ColorModel cm = getEffectiveColorModel();
super.setup(doc);
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
index 285542635..9e2b2cdb3 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -20,7 +20,11 @@
package org.apache.fop.render.pdf;
import java.awt.Dimension;
+import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.Rectangle2D.Double;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
@@ -37,15 +41,13 @@ import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
-
import org.xml.sax.SAXException;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.fop.apps.MimeConstants;
@@ -64,6 +66,8 @@ import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.pdf.PDFStructTreeRoot;
+import org.apache.fop.render.extensions.prepress.PageBoundaries;
+import org.apache.fop.render.extensions.prepress.PageScale;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
@@ -382,11 +386,33 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
//
this.pdfResources = this.pdfDoc.getResources();
+ PageBoundaries boundaries = new PageBoundaries(size, getContext().getForeignAttributes());
+
+ Rectangle trimBox = boundaries.getTrimBox();
+ Rectangle bleedBox = boundaries.getBleedBox();
+ Rectangle mediaBox = boundaries.getMediaBox();
+ Rectangle cropBox = boundaries.getCropBox();
+
+ // set scale attributes
+ double scaleX = 1;
+ double scaleY = 1;
+ String scale = (String) getContext().getForeignAttribute(
+ PageScale.EXT_PAGE_SCALE);
+ Point2D scales = PageScale.getScale(scale);
+ if (scales != null) {
+ scaleX = scales.getX();
+ scaleY = scales.getY();
+ }
+
this.currentPage = this.pdfDoc.getFactory().makePage(
- this.pdfResources,
- (int)Math.round(size.getWidth() / 1000),
- (int)Math.round(size.getHeight() / 1000), index,
- parentTreeKey); // used for accessibility
+ this.pdfResources,
+ index,
+ toPointAndScale(mediaBox, scaleX, scaleY),
+ toPointAndScale(cropBox, scaleX, scaleY),
+ toPointAndScale(bleedBox, scaleX, scaleY),
+ toPointAndScale(trimBox, scaleX, scaleY),
+ parentTreeKey);
+
pdfUtil.generatePageLabel(index, name);
currentPageRef = new PageReference(currentPage, size);
@@ -396,10 +422,18 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
this.currentPage);
// Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's
AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
- size.height / 1000f);
+ (scaleY * size.height) / 1000f);
+ basicPageTransform.scale(scaleX, scaleY);
generator.concatenate(basicPageTransform);
}
+ private Double toPointAndScale(Rectangle box, double scaleX, double scaleY) {
+ return new Rectangle2D.Double(box.getX() * scaleX / 1000,
+ box.getY() * scaleY / 1000,
+ box.getWidth() * scaleX / 1000,
+ box.getHeight() * scaleY / 1000);
+ }
+
/** {@inheritDoc} */
public IFPainter startPageContent() throws IFException {
return new PDFPainter(this);
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
index 2a9a07534..138129334 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
@@ -155,15 +155,20 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler
if (pdfAction != null) {
return pdfAction;
} else if (action instanceof GoToXYAction) {
- GoToXYAction a = (GoToXYAction)action;
- PDFGoTo pdfGoTo = new PDFGoTo(null);
- getPDFDoc().assignObjectNumber(pdfGoTo);
- if (action.isComplete()) {
- updateTargetLocation(pdfGoTo, a);
+ pdfAction = (PDFAction) incompleteActions.get(action.getID());
+ if (pdfAction != null) {
+ return pdfAction;
} else {
- this.incompleteActions.put(action.getID(), pdfGoTo);
+ GoToXYAction a = (GoToXYAction)action;
+ PDFGoTo pdfGoTo = new PDFGoTo(null);
+ getPDFDoc().assignObjectNumber(pdfGoTo);
+ if (action.isComplete()) {
+ updateTargetLocation(pdfGoTo, a);
+ } else {
+ this.incompleteActions.put(action.getID(), pdfGoTo);
+ }
+ return pdfGoTo;
}
- return pdfGoTo;
} else if (action instanceof URIAction) {
URIAction u = (URIAction)action;
assert u.isComplete();
diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java
index 2c3be9736..d70409870 100644
--- a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java
@@ -30,7 +30,12 @@ import org.apache.fop.events.model.EventModel;
public interface PDFEventProducer extends EventProducer {
/** Provider class for the event producer. */
- class Provider {
+ final class Provider {
+
+ /**
+ * Utility classes should not have a public or default constructor.
+ */
+ private Provider() { }
/**
* Returns an event producer.
diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml b/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
index 420f16a09..fd57d5099 100644
--- a/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
+++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
@@ -1,3 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
<message key="org.apache.fop.render.pdf.PDFEventProducer.nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message>
</catalogue>
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index c40c94fc4..3b737150b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -457,9 +457,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf
}
currentPageRef = currentPage.referencePDF();
- Rectangle2D bounds = page.getViewArea();
- double h = bounds.getHeight();
- pageHeight = (int) h;
+ Rectangle bounds = page.getViewArea();
+ pageHeight = bounds.height;
this.generator = new PDFContentGenerator(this.pdfDoc, this.ostream, this.currentPage);
this.borderPainter = new PDFBorderPainter(this.generator);
diff --git a/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java b/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java
index 705515311..c1f3daf2f 100644
--- a/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java
+++ b/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java
@@ -28,20 +28,18 @@ import java.io.OutputStream;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGLength;
-import org.apache.avalon.framework.configuration.Configuration;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.UnitProcessor;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
-import org.apache.xmlgraphics.java2d.TextHandler;
import org.apache.xmlgraphics.java2d.ps.AbstractPSDocumentGraphics2D;
-import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.fop.apps.FOPException;
import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontSetup;
import org.apache.fop.svg.AbstractFOPTranscoder;
+import org.apache.fop.svg.PDFDocumentGraphics2DConfigurator;
/**
* This class enables to transcode an input to a PostScript document.
@@ -72,9 +70,11 @@ import org.apache.fop.svg.AbstractFOPTranscoder;
*/
public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
- private final Configuration cfg = null;
+ /** the root Graphics2D instance for generating PostScript */
protected AbstractPSDocumentGraphics2D graphics = null;
+ private FontInfo fontInfo;
+
/**
* Constructs a new <tt>AbstractPSTranscoder</tt>.
*/
@@ -82,8 +82,21 @@ public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
super();
}
+ /**
+ * Creates the root Graphics2D instance for generating PostScript.
+ * @return the root Graphics2D
+ */
protected abstract AbstractPSDocumentGraphics2D createDocumentGraphics2D();
+ /** {@inheritDoc} */
+ protected boolean getAutoFontsDefault() {
+ //Currently set to false because auto-fonts requires a lot of memory in the PostScript
+ //case: All fonts (even the unsupported TTF fonts) need to be loaded and TrueType loading
+ //is currently very memory-intensive. At default JVM memory settings, this would result
+ //in OutOfMemoryErrors otherwise.
+ return false;
+ }
+
/**
* Transcodes the specified Document as an image in the specified output.
*
@@ -98,11 +111,13 @@ public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
graphics = createDocumentGraphics2D();
if (!isTextStroked()) {
- FontInfo fontInfo = new FontInfo();
- //TODO Do custom font configuration here somewhere/somehow
- FontSetup.setup(fontInfo);
- PSGenerator generator = graphics.getPSGenerator();
- graphics.setCustomTextHandler(new NativeTextHandler(graphics, fontInfo));
+ try {
+ this.fontInfo = PDFDocumentGraphics2DConfigurator.createFontInfo(
+ getEffectiveConfiguration());
+ graphics.setCustomTextHandler(new NativeTextHandler(graphics, fontInfo));
+ } catch (FOPException fe) {
+ throw new TranscoderException(fe);
+ }
}
super.transcode(document, uri, output);
@@ -146,21 +161,15 @@ public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
/** {@inheritDoc} */
protected BridgeContext createBridgeContext() {
+ //For compatibility with Batik 1.6
+ return createBridgeContext("1.x");
+ }
- BridgeContext ctx = new BridgeContext(userAgent);
- if (!isTextStroked()) {
- TextHandler handler = graphics.getCustomTextHandler();
- if (handler instanceof NativeTextHandler) {
- NativeTextHandler nativeTextHandler = (NativeTextHandler)handler;
- PSTextPainter textPainter = new PSTextPainter(nativeTextHandler);
- ctx.setTextPainter(textPainter);
- ctx.putBridge(new PSTextElementBridge(textPainter));
- }
- }
-
- //ctx.putBridge(new PSImageElementBridge());
+ /** {@inheritDoc} */
+ public BridgeContext createBridgeContext(String version) {
+ BridgeContext ctx = new PSBridgeContext(userAgent, (isTextStroked() ? null : fontInfo),
+ getImageManager(), getImageSessionContext());
return ctx;
}
-
}
diff --git a/src/java/org/apache/fop/render/ps/FontResourceCache.java b/src/java/org/apache/fop/render/ps/FontResourceCache.java
new file mode 100644
index 000000000..7d6f076a7
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/FontResourceCache.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.util.Map;
+
+import org.apache.xmlgraphics.ps.PSResource;
+
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.Typeface;
+
+/**
+ * A cache for font resource objects.
+ */
+class FontResourceCache {
+
+ private FontInfo fontInfo;
+
+ /** This is a map of PSResource instances of all fonts defined (key: font key) */
+ private Map fontResources = new java.util.HashMap();
+
+ public FontResourceCache(FontInfo fontInfo) {
+ this.fontInfo = fontInfo;
+ }
+
+ /**
+ * Returns the PSResource for the given font key.
+ * @param key the font key ("F*")
+ * @return the matching PSResource
+ */
+ public PSResource getPSResourceForFontKey(String key) {
+ PSResource res = null;
+ if (this.fontResources != null) {
+ res = (PSResource)this.fontResources.get(key);
+ } else {
+ this.fontResources = new java.util.HashMap();
+ }
+ if (res == null) {
+ res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
+ this.fontResources.put(key, res);
+ }
+ return res;
+ }
+
+ private String getPostScriptNameForFontKey(String key) {
+ int pos = key.indexOf('_');
+ String postFix = null;
+ if (pos > 0) {
+ postFix = key.substring(pos);
+ key = key.substring(0, pos);
+ }
+ Map fonts = fontInfo.getFonts();
+ Typeface tf = (Typeface)fonts.get(key);
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ }
+ if (tf == null) {
+ throw new IllegalStateException("Font not available: " + key);
+ }
+ if (postFix == null) {
+ return tf.getFontName();
+ } else {
+ return tf.getFontName() + postFix;
+ }
+ }
+
+ /**
+ * Adds a number of fonts to the cache.
+ * @param fontMap the font map
+ */
+ public void addAll(Map fontMap) {
+ this.fontResources.putAll(fontMap);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSBatikFlowTextElementBridge.java b/src/java/org/apache/fop/render/ps/PSBatikFlowTextElementBridge.java
new file mode 100644
index 000000000..31571f987
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSBatikFlowTextElementBridge.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.text.AttributedCharacterIterator;
+import java.util.List;
+
+import org.apache.batik.extension.svg.BatikFlowTextElementBridge;
+import org.apache.batik.extension.svg.FlowExtTextPainter;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Element Bridge for Batik's flow text extension, so those texts can be painted using
+ * PostScript primitives.
+ */
+public class PSBatikFlowTextElementBridge extends BatikFlowTextElementBridge {
+
+ private PSTextPainter textPainter;
+
+ /**
+ * Main Constructor.
+ * @param fontInfo the font directory
+ */
+ public PSBatikFlowTextElementBridge(FontInfo fontInfo) {
+ this.textPainter = new PSFlowExtTextPainter(fontInfo);
+ }
+
+ /** {@inheritDoc} */
+ protected GraphicsNode instantiateGraphicsNode() {
+ GraphicsNode node = super.instantiateGraphicsNode();
+ if (node != null) {
+ //Set our own text painter
+ ((TextNode)node).setTextPainter(getTextPainter());
+ }
+ return node;
+ }
+
+ /**
+ * Returns the text painter used by this bridge.
+ * @return the text painter
+ */
+ public TextPainter getTextPainter() {
+ return this.textPainter;
+ }
+
+ private class PSFlowExtTextPainter extends PSTextPainter {
+
+ /**
+ * Main constructor
+ * @param fontInfo the font directory
+ */
+ public PSFlowExtTextPainter(FontInfo fontInfo) {
+ super(fontInfo);
+ }
+
+ /** {@inheritDoc} */
+ public List getTextRuns(TextNode node, AttributedCharacterIterator aci) {
+ //Text runs are delegated to the normal FlowExtTextPainter, we just paint the text.
+ FlowExtTextPainter delegate = (FlowExtTextPainter)FlowExtTextPainter.getInstance();
+ return delegate.getTextRuns(node, aci);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSBridgeContext.java b/src/java/org/apache/fop/render/ps/PSBridgeContext.java
new file mode 100644
index 000000000..1ec6acadf
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSBridgeContext.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.geom.AffineTransform;
+
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.DocumentLoader;
+import org.apache.batik.bridge.SVGTextElementBridge;
+import org.apache.batik.bridge.UserAgent;
+import org.apache.batik.gvt.TextPainter;
+
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.svg.AbstractFOPBridgeContext;
+
+/**
+ * BridgeContext which registers the custom bridges for PostScript output.
+ */
+public class PSBridgeContext extends AbstractFOPBridgeContext {
+
+ /**
+ * Constructs a new bridge context.
+ * @param userAgent the user agent
+ * @param documentLoader the Document Loader to use for referenced documents.
+ * @param fontInfo the font list for the text painter, may be null
+ * in which case text is painted as shapes
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ * @param linkTransform AffineTransform to properly place links,
+ * may be null
+ */
+ public PSBridgeContext(UserAgent userAgent, DocumentLoader documentLoader,
+ FontInfo fontInfo, ImageManager imageManager,
+ ImageSessionContext imageSessionContext,
+ AffineTransform linkTransform) {
+ super(userAgent, documentLoader, fontInfo,
+ imageManager, imageSessionContext, linkTransform);
+ }
+
+ /**
+ * Constructs a new bridge context.
+ * @param userAgent the user agent
+ * @param fontInfo the font list for the text painter, may be null
+ * in which case text is painted as shapes
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ */
+ public PSBridgeContext(UserAgent userAgent, FontInfo fontInfo,
+ ImageManager imageManager, ImageSessionContext imageSessionContext) {
+ super(userAgent, fontInfo, imageManager, imageSessionContext);
+ }
+
+ /** {@inheritDoc} */
+ public void registerSVGBridges() {
+ super.registerSVGBridges();
+
+ if (fontInfo != null) {
+ TextPainter textPainter = new PSTextPainter(fontInfo);
+ SVGTextElementBridge textElementBridge = new PSTextElementBridge(textPainter);
+ putBridge(textElementBridge);
+
+ //Batik flow text extension (may not always be available)
+ //putBridge(new PDFBatikFlowTextElementBridge(fontInfo);
+ putElementBridgeConditional(
+ "org.apache.fop.render.ps.PSBatikFlowTextElementBridge",
+ "org.apache.batik.extension.svg.BatikFlowTextElementBridge");
+
+ //SVG 1.2 flow text support
+ //putBridge(new PDFSVG12TextElementBridge(fontInfo)); //-->Batik 1.7
+ putElementBridgeConditional(
+ "org.apache.fop.render.ps.PSSVG12TextElementBridge",
+ "org.apache.batik.bridge.svg12.SVG12TextElementBridge");
+
+ //putBridge(new PDFSVGFlowRootElementBridge(fontInfo));
+ putElementBridgeConditional(
+ "org.apache.fop.render.ps.PSSVGFlowRootElementBridge",
+ "org.apache.batik.bridge.svg12.SVGFlowRootElementBridge");
+ }
+
+ //putBridge(new PSImageElementBridge()); //TODO uncomment when implemented
+ }
+
+ // Make sure any 'sub bridge contexts' also have our bridges.
+ //TODO There's no matching method in the super-class here
+ public BridgeContext createBridgeContext() {
+ return new PSBridgeContext(getUserAgent(), getDocumentLoader(),
+ fontInfo,
+ getImageManager(),
+ getImageSessionContext(),
+ linkTransform);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
index 1379651c8..5a6db172a 100644
--- a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
+++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
@@ -50,8 +50,6 @@ import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
import org.apache.fop.apps.MimeConstants;
-import org.apache.fop.fonts.LazyFont;
-import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
@@ -91,8 +89,8 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** Used to temporarily store PSSetupCode instance until they can be written. */
private List setupCodeList;
- /** This is a map of PSResource instances of all fonts defined (key: font key) */
- private Map fontResources;
+ /** This is a cache of PSResource instances of all fonts defined */
+ private FontResourceCache fontResources;
/** This is a map of PSResource instances of all forms (key: uri) */
private Map formResources;
@@ -139,6 +137,7 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void startDocument() throws IFException {
super.startDocument();
+ this.fontResources = new FontResourceCache(getFontInfo());
try {
OutputStream out;
if (psUtil.isOptimizeResources()) {
@@ -200,7 +199,7 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
if (!psUtil.isOptimizeResources()) {
- this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
+ this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo));
} else {
gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
}
@@ -436,8 +435,7 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void endPageContent() throws IFException {
try {
- //Show page
- gen.writeln("showpage");
+ gen.showPage();
} catch (IOException ioe) {
throw new IFException("I/O error in endPageContent()", ioe);
}
@@ -534,45 +532,13 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
}
}
- private String getPostScriptNameForFontKey(String key) {
- int pos = key.indexOf('_');
- String postFix = null;
- if (pos > 0) {
- postFix = key.substring(pos);
- key = key.substring(0, pos);
- }
- Map fonts = fontInfo.getFonts();
- Typeface tf = (Typeface)fonts.get(key);
- if (tf instanceof LazyFont) {
- tf = ((LazyFont)tf).getRealFont();
- }
- if (tf == null) {
- throw new IllegalStateException("Font not available: " + key);
- }
- if (postFix == null) {
- return tf.getFontName();
- } else {
- return tf.getFontName() + postFix;
- }
- }
-
/**
* Returns the PSResource for the given font key.
* @param key the font key ("F*")
* @return the matching PSResource
*/
protected PSResource getPSResourceForFontKey(String key) {
- PSResource res = null;
- if (this.fontResources != null) {
- res = (PSResource)this.fontResources.get(key);
- } else {
- this.fontResources = new java.util.HashMap();
- }
- if (res == null) {
- res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
- this.fontResources.put(key, res);
- }
- return res;
+ return this.fontResources.getPSResourceForFontKey(key);
}
/**
diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
index f6679e8da..41cba7563 100644
--- a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
+++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
@@ -65,16 +65,10 @@ public class PSImageHandlerSVG implements ImageHandler {
PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
- NativeTextHandler nativeTextHandler = null;
- BridgeContext ctx = new BridgeContext(ua);
- if (!strokeText) {
- nativeTextHandler = new NativeTextHandler(graphics, psContext.getFontInfo());
- graphics.setCustomTextHandler(nativeTextHandler);
- PSTextPainter textPainter = new PSTextPainter(nativeTextHandler);
- ctx.setTextPainter(textPainter);
- PSTextElementBridge tBridge = new PSTextElementBridge(textPainter);
- ctx.putBridge(tBridge);
- }
+ BridgeContext ctx = new PSBridgeContext(ua,
+ (strokeText ? null : psContext.getFontInfo()),
+ context.getUserAgent().getFactory().getImageManager(),
+ context.getUserAgent().getImageSessionContext());
GraphicsNode root;
try {
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
index cb88f4670..051013a63 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -350,7 +350,6 @@ public class PSPainter extends AbstractIFPainter {
//TODO Opportunity for font caching if font state is more heavily used
String fontKey = getFontInfo().getInternalFontKey(triplet);
int sizeMillipoints = state.getFontSize();
- float fontSize = sizeMillipoints / 1000f;
// This assumes that *all* CIDFonts use a /ToUnicode mapping
Typeface tf = getTypeface(fontKey);
@@ -360,9 +359,7 @@ public class PSPainter extends AbstractIFPainter {
}
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
- PSResource res = this.documentHandler.getPSResourceForFontKey(fontKey);
- generator.useFont("/" + res.getName(), fontSize);
- generator.getResourceTracker().notifyResourceUsageOnPage(res);
+ useFont(fontKey, sizeMillipoints);
generator.writeln("1 0 0 -1 " + formatMptAsPt(generator, x)
+ " " + formatMptAsPt(generator, y) + " Tm");
diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java
index 19fcd8af8..14924996e 100644
--- a/src/java/org/apache/fop/render/ps/PSRenderer.java
+++ b/src/java/org/apache/fop/render/ps/PSRenderer.java
@@ -815,8 +815,8 @@ public class PSRenderer extends AbstractPathOrientedRenderer
{page.getPageNumberString(),
new Integer(this.currentPageNumber)});
- double pageWidth = Math.round(page.getViewArea().getWidth()) / 1000f;
- double pageHeight = Math.round(page.getViewArea().getHeight()) / 1000f;
+ double pageWidth = page.getViewArea().width / 1000f;
+ double pageHeight = page.getViewArea().height / 1000f;
boolean rotate = false;
List pageSizes = new java.util.ArrayList();
if (getPSUtil().isAutoRotateLandscape() && (pageHeight < pageWidth)) {
@@ -933,7 +933,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
super.renderPage(page);
//Show page
- writeln("showpage");
+ gen.showPage();
gen.writeDSCComment(DSCConstants.PAGE_TRAILER);
if (page.hasExtensionAttachments()) {
List extensionAttachments = page.getExtensionAttachments();
diff --git a/src/java/org/apache/fop/render/ps/PSSVGFlowRootElementBridge.java b/src/java/org/apache/fop/render/ps/PSSVGFlowRootElementBridge.java
new file mode 100644
index 000000000..56b1f91bd
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSSVGFlowRootElementBridge.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.text.AttributedCharacterIterator;
+import java.util.List;
+
+import org.apache.batik.bridge.svg12.SVGFlowRootElementBridge;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.apache.batik.gvt.flow.FlowTextPainter;
+
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Element Bridge for SVG 1.2 flow text, so those texts can be painted using
+ * PDF primitives.
+ */
+public class PSSVGFlowRootElementBridge extends SVGFlowRootElementBridge {
+
+ private PSTextPainter textPainter;
+
+ /**
+ * Main Constructor.
+ * @param fontInfo the font directory
+ */
+ public PSSVGFlowRootElementBridge(FontInfo fontInfo) {
+ this.textPainter = new PSFlowTextPainter(fontInfo);
+ }
+
+ /** {@inheritDoc} */
+ protected GraphicsNode instantiateGraphicsNode() {
+ GraphicsNode node = super.instantiateGraphicsNode();
+ if (node != null) {
+ //Set our own text painter
+ ((TextNode)node).setTextPainter(getTextPainter());
+ }
+ return node;
+ }
+
+ /**
+ * Returns the text painter used by this bridge.
+ * @return the text painter
+ */
+ public TextPainter getTextPainter() {
+ return this.textPainter;
+ }
+
+ private class PSFlowTextPainter extends PSTextPainter {
+
+ /**
+ * Main constructor
+ * @param fontInfo the font directory
+ */
+ public PSFlowTextPainter(FontInfo fontInfo) {
+ super(fontInfo);
+ }
+
+ /** {@inheritDoc} */
+ public List getTextRuns(TextNode node, AttributedCharacterIterator aci) {
+ //Text runs are delegated to the normal FlowTextPainter, we just paint the text.
+ FlowTextPainter delegate = (FlowTextPainter)FlowTextPainter.getInstance();
+ return delegate.getTextRuns(node, aci);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSSVGHandler.java b/src/java/org/apache/fop/render/ps/PSSVGHandler.java
index 75182682d..5cc1a1b01 100644
--- a/src/java/org/apache/fop/render/ps/PSSVGHandler.java
+++ b/src/java/org/apache/fop/render/ps/PSSVGHandler.java
@@ -259,17 +259,10 @@ public class PSSVGHandler extends AbstractGenericSVGHandler
PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
- NativeTextHandler nativeTextHandler = null;
- BridgeContext ctx = new BridgeContext(ua);
- if (!strokeText) {
- FontInfo fontInfo = psInfo.getFontInfo();
- nativeTextHandler = new NativeTextHandler(graphics, fontInfo);
- graphics.setCustomTextHandler(nativeTextHandler);
- PSTextPainter textPainter = new PSTextPainter(nativeTextHandler);
- ctx.setTextPainter(textPainter);
- PSTextElementBridge tBridge = new PSTextElementBridge(textPainter);
- ctx.putBridge(tBridge);
- }
+ BridgeContext ctx = new PSBridgeContext(ua,
+ (strokeText ? null : psInfo.fontInfo),
+ context.getUserAgent().getFactory().getImageManager(),
+ context.getUserAgent().getImageSessionContext());
//Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
//to it.
diff --git a/src/java/org/apache/fop/render/ps/PSTextElementBridge.java b/src/java/org/apache/fop/render/ps/PSTextElementBridge.java
index ab0c2d723..524fbdad8 100644
--- a/src/java/org/apache/fop/render/ps/PSTextElementBridge.java
+++ b/src/java/org/apache/fop/render/ps/PSTextElementBridge.java
@@ -19,13 +19,13 @@
package org.apache.fop.render.ps;
-import org.apache.batik.bridge.SVGTextElementBridge;
+import org.w3c.dom.Element;
+
import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.TextNode;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
+import org.apache.batik.gvt.TextPainter;
/**
* Bridge class for the &lt;text> element.
@@ -37,13 +37,13 @@ import org.w3c.dom.Node;
*/
public class PSTextElementBridge extends SVGTextElementBridge {
- private PSTextPainter textPainter;
+ private TextPainter textPainter;
/**
* Constructs a new bridge for the &lt;text> element.
* @param textPainter the text painter to use
*/
- public PSTextElementBridge(PSTextPainter textPainter) {
+ public PSTextElementBridge(TextPainter textPainter) {
this.textPainter = textPainter;
}
@@ -56,60 +56,13 @@ public class PSTextElementBridge extends SVGTextElementBridge {
*/
public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
GraphicsNode node = super.createGraphicsNode(ctx, e);
- /* this code is worthless I think. PSTextPainter does a much better job
- * at determining whether to stroke or not. */
- if (true/*node != null && isSimple(ctx, e, node)*/) {
- ((TextNode)node).setTextPainter(getTextPainter());
- }
+ ((TextNode)node).setTextPainter(getTextPainter());
return node;
}
- private PSTextPainter getTextPainter() {
+ private TextPainter getTextPainter() {
return this.textPainter;
}
- /**
- * Check if text element contains simple text.
- * This checks the children of the text element to determine
- * if the text is simple. The text is simple if it can be rendered
- * with basic text drawing algorithms. This means there are no
- * alternate characters, the font is known and there are no effects
- * applied to the text.
- *
- * @param ctx the bridge context
- * @param element the svg text element
- * @param node the graphics node
- * @return true if this text is simple of false if it cannot be
- * easily rendered using normal drawString on the PDFGraphics2D
- */
- private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
- for (Node n = element.getFirstChild();
- n != null;
- n = n.getNextSibling()) {
-
- switch (n.getNodeType()) {
- case Node.ELEMENT_NODE:
-
- if (n.getLocalName().equals(SVG_TSPAN_TAG)
- || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
- return false;
- } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
- return false;
- } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
- return false;
- }
- break;
- case Node.TEXT_NODE:
- case Node.CDATA_SECTION_NODE:
- default:
- }
- }
-
- /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
- return false;
- }*/
-
- return true;
- }
}
diff --git a/src/java/org/apache/fop/render/ps/PSTextPainter.java b/src/java/org/apache/fop/render/ps/PSTextPainter.java
index a318c6465..018b6f9b7 100644
--- a/src/java/org/apache/fop/render/ps/PSTextPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSTextPainter.java
@@ -19,555 +19,516 @@
package org.apache.fop.render.ps;
+import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
-import java.awt.font.TextAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
-import java.text.CharacterIterator;
import java.util.Iterator;
import java.util.List;
-import org.apache.batik.dom.svg.SVGOMTextElement;
-import org.apache.batik.gvt.TextNode;
-import org.apache.batik.gvt.TextPainter;
-import org.apache.batik.gvt.font.GVTFontFamily;
-import org.apache.batik.gvt.renderer.StrokingTextPainter;
-import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
-import org.apache.batik.gvt.text.Mark;
+import org.apache.batik.gvt.font.GVTGlyphVector;
import org.apache.batik.gvt.text.TextPaintInfo;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.fop.fonts.Font;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontTriplet;
+import org.apache.batik.gvt.text.TextSpanLayout;
+
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.svg.NativeTextPainter;
+import org.apache.fop.util.CharUtilities;
/**
* Renders the attributed character iterator of a <tt>TextNode</tt>.
- * This class draws the text directly into the PSGraphics2D so that
+ * This class draws the text directly using PostScript text operators so
* the text is not drawn using shapes which makes the PS files larger.
- * If the text is simple enough to draw then it sets the font and calls
- * drawString. If the text is complex or the cannot be translated
- * into a simple drawString the StrokingTextPainter is used instead.
- *
- * (todo) handle underline, overline and strikethrough
- * (todo) use drawString(AttributedCharacterIterator iterator...) for some
- *
- * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
- * @version $Id$
+ * <p>
+ * The text runs are split into smaller text runs that can be bundles in single
+ * calls of the xshow, yshow or xyshow operators. For outline text, the charpath
+ * operator is used.
*/
-public class PSTextPainter implements TextPainter {
+public class PSTextPainter extends NativeTextPainter {
- /** the logger for this class */
- protected Log log = LogFactory.getLog(PSTextPainter.class);
+ private static final boolean DEBUG = false;
- private final NativeTextHandler nativeTextHandler;
- private final FontInfo fontInfo;
+ private FontResourceCache fontResources;
- /**
- * Use the stroking text painter to get the bounds and shape.
- * Also used as a fallback to draw the string with strokes.
- */
- protected static final TextPainter
- PROXY_PAINTER = StrokingTextPainter.getInstance();
+ private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
/**
* Create a new PS text painter with the given font information.
- * @param nativeTextHandler the NativeTextHandler instance used for text painting
+ * @param fontInfo the font collection
*/
- public PSTextPainter(NativeTextHandler nativeTextHandler) {
- this.nativeTextHandler = nativeTextHandler;
- this.fontInfo = nativeTextHandler.getFontInfo();
+ public PSTextPainter(FontInfo fontInfo) {
+ super(fontInfo);
+ this.fontResources = new FontResourceCache(fontInfo);
}
- /**
- * Paints the specified attributed character iterator using the
- * specified Graphics2D and context and font context.
- * @param node the TextNode to paint
- * @param g2d the Graphics2D to use
- */
- public void paint(TextNode node, Graphics2D g2d) {
- String txt = node.getText();
- Point2D loc = node.getLocation();
-
- if (hasUnsupportedAttributes(node)) {
- PROXY_PAINTER.paint(node, g2d);
- } else {
- paintTextRuns(node.getTextRuns(), g2d, loc);
- }
+ /** {@inheritDoc} */
+ protected boolean isSupported(Graphics2D g2d) {
+ return g2d instanceof PSGraphics2D;
}
+ /** {@inheritDoc} */
+ protected void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException {
+ AttributedCharacterIterator runaci = textRun.getACI();
+ runaci.first();
- private boolean hasUnsupportedAttributes(TextNode node) {
- Iterator i = node.getTextRuns().iterator();
- while (i.hasNext()) {
- StrokingTextPainter.TextRun
- run = (StrokingTextPainter.TextRun)i.next();
- AttributedCharacterIterator aci = run.getACI();
- boolean hasUnsupported = hasUnsupportedAttributes(aci);
- if (hasUnsupported) {
- return true;
- }
+ TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
+ if (tpi == null || !tpi.visible) {
+ return;
}
- return false;
- }
-
- private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
- boolean hasunsupported = false;
-
- String text = getText(aci);
- Font font = makeFont(aci);
- if (hasUnsupportedGlyphs(text, font)) {
- log.trace("-> Unsupported glyphs found");
- hasunsupported = true;
- }
-
- TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
- if ((tpi != null)
- && ((tpi.strokeStroke != null && tpi.strokePaint != null)
- || (tpi.strikethroughStroke != null)
- || (tpi.underlineStroke != null)
- || (tpi.overlineStroke != null))) {
- log.trace("-> under/overlines etc. found");
- hasunsupported = true;
- }
-
- //Alpha is not supported
- Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
- if (foreground instanceof Color) {
- Color col = (Color)foreground;
- if (col.getAlpha() != 255) {
- log.trace("-> transparency found");
- hasunsupported = true;
- }
+ if ((tpi != null) && (tpi.composite != null)) {
+ g2d.setComposite(tpi.composite);
}
- Object letSpace = aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
- if (letSpace != null) {
- log.trace("-> letter spacing found");
- hasunsupported = true;
- }
+ //------------------------------------
+ TextSpanLayout layout = textRun.getLayout();
+ logTextRun(runaci, layout);
+ CharSequence chars = collectCharacters(runaci);
+ runaci.first(); //Reset ACI
- Object wordSpace = aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
- if (wordSpace != null) {
- log.trace("-> word spacing found");
- hasunsupported = true;
- }
+ final PSGraphics2D ps = (PSGraphics2D)g2d;
+ final PSGenerator gen = ps.getPSGenerator();
+ ps.preparePainting();
- Object lengthAdjust = aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST);
- if (lengthAdjust != null) {
- log.trace("-> length adjustments found");
- hasunsupported = true;
+ if (DEBUG) {
+ log.debug("Text: " + chars);
+ gen.commentln("%Text: " + chars);
}
- Object writeMod = aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE);
- if (writeMod != null
- && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
- writeMod)) {
- log.trace("-> Unsupported writing modes found");
- hasunsupported = true;
+ GeneralPath debugShapes = null;
+ if (DEBUG) {
+ debugShapes = new GeneralPath();
}
- Object vertOr = aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
- if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
- vertOr)) {
- log.trace("-> vertical orientation found");
- hasunsupported = true;
+ TextUtil textUtil = new TextUtil(gen);
+ textUtil.setupFonts(runaci);
+ if (!textUtil.hasFonts()) {
+ //Draw using Java2D when no native fonts are available
+ textRun.getLayout().draw(g2d);
+ return;
}
- Object rcDel = aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
- //Batik 1.6 returns null here which makes it impossible to determine whether this can
- //be painted or not, i.e. fall back to stroking. :-(
- if (/*rcDel != null &&*/ !(rcDel instanceof SVGOMTextElement)) {
- log.trace("-> spans found");
- hasunsupported = true; //Filter spans
- }
+ gen.saveGraphicsState();
+ gen.concatMatrix(g2d.getTransform());
+ Shape imclip = g2d.getClip();
+ clip(ps, imclip);
+
+ gen.writeln("BT"); //beginTextObject()
+
+ AffineTransform localTransform = new AffineTransform();
+ Point2D prevPos = null;
+ GVTGlyphVector gv = layout.getGlyphVector();
+ PSTextRun psRun = new PSTextRun(); //Used to split a text run into smaller runs
+ for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
+ char ch = chars.charAt(index);
+ boolean visibleChar = gv.isGlyphVisible(index)
+ || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
+ logCharacter(ch, layout, index, visibleChar);
+ if (!visibleChar) {
+ continue;
+ }
+ Point2D glyphPos = gv.getGlyphPosition(index);
+
+ AffineTransform glyphTransform = gv.getGlyphTransform(index);
+ if (log.isTraceEnabled()) {
+ log.trace("pos " + glyphPos + ", transform " + glyphTransform);
+ }
+ if (DEBUG) {
+ Shape sh = gv.getGlyphLogicalBounds(index);
+ if (sh == null) {
+ sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2);
+ }
+ debugShapes.append(sh, false);
+ }
+
+ //Exact position of the glyph
+ localTransform.setToIdentity();
+ localTransform.translate(glyphPos.getX(), glyphPos.getY());
+ if (glyphTransform != null) {
+ localTransform.concatenate(glyphTransform);
+ }
+ localTransform.scale(1, -1);
+
+ boolean flushCurrentRun = false;
+ //Try to optimize by combining characters using the same font and on the same line.
+ if (glyphTransform != null) {
+ //Happens for text-on-a-path
+ flushCurrentRun = true;
+ }
+ if (psRun.getRunLength() >= 128) {
+ //Don't let a run get too long
+ flushCurrentRun = true;
+ }
+
+ //Note the position of the glyph relative to the previous one
+ Point2D relPos;
+ if (prevPos == null) {
+ relPos = new Point2D.Double(0, 0);
+ } else {
+ relPos = new Point2D.Double(
+ glyphPos.getX() - prevPos.getX(),
+ glyphPos.getY() - prevPos.getY());
+ }
+ if (psRun.vertChanges == 0
+ && psRun.getHorizRunLength() > 2
+ && relPos.getY() != 0) {
+ //new line
+ flushCurrentRun = true;
+ }
- if (hasunsupported) {
- log.trace("Unsupported attributes found in ACI, using StrokingTextPainter");
+ //Select the actual character to paint
+ char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
+
+ //Select (sub)font for character
+ Font f = textUtil.selectFontForChar(paintChar);
+ char mapped = f.mapChar(ch);
+ boolean fontChanging = textUtil.isFontChanging(f, mapped);
+ if (fontChanging) {
+ flushCurrentRun = true;
+ }
+
+ if (flushCurrentRun) {
+ //Paint the current run and reset for the next run
+ psRun.paint(ps, textUtil, tpi);
+ psRun.reset();
+ }
+
+ //Track current run
+ psRun.addCharacter(paintChar, relPos);
+ psRun.noteStartingTransformation(localTransform);
+
+ //Change font if necessary
+ if (fontChanging) {
+ textUtil.setCurrentFont(f, mapped);
+ }
+
+ //Update last position
+ prevPos = glyphPos;
+ }
+ psRun.paint(ps, textUtil, tpi);
+ gen.writeln("ET"); //endTextObject()
+ gen.restoreGraphicsState();
+
+ if (DEBUG) {
+ //Paint debug shapes
+ g2d.setStroke(new BasicStroke(0));
+ g2d.setColor(Color.LIGHT_GRAY);
+ g2d.draw(debugShapes);
}
- return hasunsupported;
}
- /**
- * Paint a list of text runs on the Graphics2D at a given location.
- * @param textRuns the list of text runs
- * @param g2d the Graphics2D to paint to
- * @param loc the current location of the "cursor"
- */
- protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) {
- Point2D currentloc = loc;
- Iterator i = textRuns.iterator();
- while (i.hasNext()) {
- StrokingTextPainter.TextRun
- run = (StrokingTextPainter.TextRun)i.next();
- currentloc = paintTextRun(run, g2d, currentloc);
+ private void applyColor(Paint paint, final PSGenerator gen) throws IOException {
+ if (paint == null) {
+ return;
+ } else if (paint instanceof Color) {
+ Color col = (Color)paint;
+ gen.useColor(col);
+ } else {
+ log.warn("Paint not supported: " + paint.toString());
}
}
- /**
- * Paint a single text run on the Graphics2D at a given location.
- * @param run the text run to paint
- * @param g2d the Graphics2D to paint to
- * @param loc the current location of the "cursor"
- * @return the new location of the "cursor" after painting the text run
- */
- protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) {
- AttributedCharacterIterator aci = run.getACI();
- return paintACI(aci, g2d, loc);
+ private PSResource getResourceForFont(Font f, String postfix) {
+ String key = (postfix != null ? f.getFontName() + '_' + postfix : f.getFontName());
+ return this.fontResources.getPSResourceForFontKey(key);
}
- /**
- * Extract the raw text from an ACI.
- * @param aci ACI to inspect
- * @return the extracted text
- */
- protected String getText(AttributedCharacterIterator aci) {
- StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex());
- for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) {
- sb.append(c);
+ private void clip(PSGraphics2D ps, Shape shape) throws IOException {
+ if (shape == null) {
+ return;
}
- return sb.toString();
+ ps.getPSGenerator().writeln("newpath");
+ PathIterator iter = shape.getPathIterator(IDENTITY_TRANSFORM);
+ ps.processPathIterator(iter);
+ ps.getPSGenerator().writeln("clip");
}
- /**
- * Paint an ACI on a Graphics2D at a given location. The method has to
- * update the location after painting.
- * @param aci ACI to paint
- * @param g2d Graphics2D to paint on
- * @param loc start location
- * @return new current location
- */
- protected Point2D paintACI(AttributedCharacterIterator aci, Graphics2D g2d, Point2D loc) {
- //ACIUtils.dumpAttrs(aci);
+ private class TextUtil {
- aci.first();
+ private PSGenerator gen;
+ private Font[] fonts;
+ private Font currentFont;
+ private int currentEncoding = -1;
- updateLocationFromACI(aci, loc);
-
- TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
-
- if (tpi == null) {
- return loc;
+ public TextUtil(PSGenerator gen) {
+ this.gen = gen;
}
- TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
+ public Font selectFontForChar(char ch) {
+ for (int i = 0, c = fonts.length; i < c; i++) {
+ if (fonts[i].hasChar(ch)) {
+ return fonts[i];
+ }
+ }
+ return fonts[0]; //TODO Maybe fall back to painting with shapes
+ }
- //Set up font
- List gvtFonts = (List)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
- Paint foreground = tpi.fillPaint;
- Paint strokePaint = tpi.strokePaint;
- Stroke stroke = tpi.strokeStroke;
+ public void writeTextMatrix(AffineTransform transform) throws IOException {
+ double[] matrix = new double[6];
+ transform.getMatrix(matrix);
+ gen.writeln(gen.formatDouble5(matrix[0]) + " "
+ + gen.formatDouble5(matrix[1]) + " "
+ + gen.formatDouble5(matrix[2]) + " "
+ + gen.formatDouble5(matrix[3]) + " "
+ + gen.formatDouble5(matrix[4]) + " "
+ + gen.formatDouble5(matrix[5]) + " Tm");
+ }
- Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
- if (fontSize == null) {
- return loc;
+ public boolean isFontChanging(Font f, char mapped) {
+ if (f != getCurrentFont()) {
+ int encoding = mapped / 256;
+ if (encoding != getCurrentFontEncoding()) {
+ return true; //Font is changing
+ }
+ }
+ return false; //Font is the same
}
- Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
- Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
- if (foreground instanceof Color) {
- Color col = (Color)foreground;
- g2d.setColor(col);
+ public void selectFont(Font f, char mapped) throws IOException {
+ int encoding = mapped / 256;
+ String postfix = (encoding == 0 ? null : Integer.toString(encoding));
+ PSResource res = getResourceForFont(f, postfix);
+ gen.useFont("/" + res.getName(), f.getFontSize() / 1000f);
+ gen.getResourceTracker().notifyResourceUsageOnPage(res);
}
- g2d.setPaint(foreground);
- g2d.setStroke(stroke);
- Font font = makeFont(aci);
- java.awt.Font awtFont = makeAWTFont(aci, font);
+ public Font getCurrentFont() {
+ return this.currentFont;
+ }
- g2d.setFont(awtFont);
+ public int getCurrentFontEncoding() {
+ return this.currentEncoding;
+ }
- String txt = getText(aci);
- float advance = getStringWidth(txt, font);
- float tx = 0;
- if (anchor != null) {
- switch (anchor.getType()) {
- case TextNode.Anchor.ANCHOR_MIDDLE:
- tx = -advance / 2;
- break;
- case TextNode.Anchor.ANCHOR_END:
- tx = -advance;
- break;
- default: //nop
- }
+ public void setCurrentFont(Font font, int encoding) {
+ this.currentFont = font;
+ this.currentEncoding = encoding;
}
- drawPrimitiveString(g2d, loc, font, txt, tx);
- loc.setLocation(loc.getX() + advance, loc.getY());
- return loc;
- }
+ public void setCurrentFont(Font font, char mapped) {
+ int encoding = mapped / 256;
+ setCurrentFont(font, encoding);
+ }
- protected void drawPrimitiveString(Graphics2D g2d, Point2D loc, Font font, String txt, float tx) {
- //Finally draw text
- nativeTextHandler.setOverrideFont(font);
- try {
- try {
- nativeTextHandler.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
- } catch (IOException ioe) {
- if (g2d instanceof PSGraphics2D) {
- ((PSGraphics2D)g2d).handleIOException(ioe);
- }
- }
- } finally {
- nativeTextHandler.setOverrideFont(null);
+ public void setupFonts(AttributedCharacterIterator runaci) {
+ this.fonts = findFonts(runaci);
}
- }
- private void updateLocationFromACI(
- AttributedCharacterIterator aci,
- Point2D loc) {
- //Adjust position of span
- Float xpos = (Float)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.X);
- Float ypos = (Float)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.Y);
- Float dxpos = (Float)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.DX);
- Float dypos = (Float)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.DY);
- if (xpos != null) {
- loc.setLocation(xpos.doubleValue(), loc.getY());
- }
- if (ypos != null) {
- loc.setLocation(loc.getX(), ypos.doubleValue());
- }
- if (dxpos != null) {
- loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY());
- }
- if (dypos != null) {
- loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue());
+ public boolean hasFonts() {
+ return (fonts != null) && (fonts.length > 0);
}
- }
- private String getStyle(AttributedCharacterIterator aci) {
- Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
- return ((posture != null) && (posture.floatValue() > 0.0))
- ? "italic"
- : "normal";
}
- private int getWeight(AttributedCharacterIterator aci) {
- Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
- return ((taWeight != null) && (taWeight.floatValue() > 1.0))
- ? Font.WEIGHT_BOLD
- : Font.WEIGHT_NORMAL;
- }
+ private class PSTextRun {
- private Font makeFont(AttributedCharacterIterator aci) {
- Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
- if (fontSize == null) {
- fontSize = new Float(10.0f);
- }
- String style = getStyle(aci);
- int weight = getWeight(aci);
-
- String fontFamily = null;
- List gvtFonts = (List) aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
- if (gvtFonts != null) {
- Iterator i = gvtFonts.iterator();
- while (i.hasNext()) {
- GVTFontFamily fam = (GVTFontFamily) i.next();
- /* (todo) Enable SVG Font painting
- if (fam instanceof SVGFontFamily) {
- PROXY_PAINTER.paint(node, g2d);
- return;
- }*/
- fontFamily = fam.getFamilyName();
- if (fontInfo.hasFont(fontFamily, style, weight)) {
- FontTriplet triplet = fontInfo.fontLookup(
- fontFamily, style, weight);
- int fsize = (int)(fontSize.floatValue() * 1000);
- return fontInfo.getFontInstance(triplet, fsize);
- }
- }
+ private AffineTransform textTransform;
+ private List relativePositions = new java.util.LinkedList();
+ private StringBuffer currentChars = new StringBuffer();
+ private int horizChanges = 0;
+ private int vertChanges = 0;
+
+ public void reset() {
+ textTransform = null;
+ currentChars.setLength(0);
+ horizChanges = 0;
+ vertChanges = 0;
+ relativePositions.clear();
}
- FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
- int fsize = (int)(fontSize.floatValue() * 1000);
- return fontInfo.getFontInstance(triplet, fsize);
- }
- private java.awt.Font makeAWTFont(AttributedCharacterIterator aci, Font font) {
- final String style = getStyle(aci);
- final int weight = getWeight(aci);
- int fStyle = java.awt.Font.PLAIN;
- if (weight == Font.WEIGHT_BOLD) {
- fStyle |= java.awt.Font.BOLD;
+ public int getHorizRunLength() {
+ if (this.vertChanges == 0
+ && getRunLength() > 0) {
+ return getRunLength();
+ }
+ return 0;
}
- if ("italic".equals(style)) {
- fStyle |= java.awt.Font.ITALIC;
+
+ public void addCharacter(char paintChar, Point2D relPos) {
+ addRelativePosition(relPos);
+ currentChars.append(paintChar);
}
- return new java.awt.Font(font.getFontName(), fStyle,
- (font.getFontSize() / 1000));
- }
- private float getStringWidth(String str, Font font) {
- float wordWidth = 0;
- float whitespaceWidth = font.getWidth(font.mapChar(' '));
-
- for (int i = 0; i < str.length(); i++) {
- float charWidth;
- char c = str.charAt(i);
- if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
- charWidth = font.getWidth(font.mapChar(c));
- if (charWidth <= 0) {
- charWidth = whitespaceWidth;
+ private void addRelativePosition(Point2D relPos) {
+ if (getRunLength() > 0) {
+ if (relPos.getX() != 0) {
+ horizChanges++;
+ }
+ if (relPos.getY() != 0) {
+ vertChanges++;
}
- } else {
- charWidth = whitespaceWidth;
}
- wordWidth += charWidth;
+ relativePositions.add(relPos);
}
- return wordWidth / 1000f;
- }
- private boolean hasUnsupportedGlyphs(String str, Font font) {
- for (int i = 0; i < str.length(); i++) {
- float charWidth;
- char c = str.charAt(i);
- if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
- if (!font.hasChar(c)) {
- return true;
- }
+ public void noteStartingTransformation(AffineTransform transform) {
+ if (textTransform == null) {
+ this.textTransform = new AffineTransform(transform);
}
}
- return false;
- }
- /**
- * Get the outline shape of the text characters.
- * This uses the StrokingTextPainter to get the outline
- * shape since in theory it should be the same.
- *
- * @param node the text node
- * @return the outline shape of the text characters
- */
- public Shape getOutline(TextNode node) {
- return PROXY_PAINTER.getOutline(node);
- }
-
- /**
- * Get the bounds.
- * This uses the StrokingTextPainter to get the bounds
- * since in theory it should be the same.
- *
- * @param node the text node
- * @return the bounds of the text
- */
- public Rectangle2D getBounds2D(TextNode node) {
- /* (todo) getBounds2D() is too slow
- * because it uses the StrokingTextPainter. We should implement this
- * method ourselves. */
- return PROXY_PAINTER.getBounds2D(node);
- }
-
- /**
- * Get the geometry bounds.
- * This uses the StrokingTextPainter to get the bounds
- * since in theory it should be the same.
- * @param node the text node
- * @return the bounds of the text
- */
- public Rectangle2D getGeometryBounds(TextNode node) {
- return PROXY_PAINTER.getGeometryBounds(node);
- }
-
- // Methods that have no purpose for PS
+ public int getRunLength() {
+ return currentChars.length();
+ }
- /**
- * Get the mark.
- * This does nothing since the output is pdf and not interactive.
- * @param node the text node
- * @param pos the position
- * @param all select all
- * @return null
- */
- public Mark getMark(TextNode node, int pos, boolean all) {
- return null;
- }
+ private boolean isXShow() {
+ return vertChanges == 0;
+ }
- /**
- * Select at.
- * This does nothing since the output is pdf and not interactive.
- * @param x the x position
- * @param y the y position
- * @param node the text node
- * @return null
- */
- public Mark selectAt(double x, double y, TextNode node) {
- return null;
- }
+ private boolean isYShow() {
+ return horizChanges == 0;
+ }
- /**
- * Select to.
- * This does nothing since the output is pdf and not interactive.
- * @param x the x position
- * @param y the y position
- * @param beginMark the start mark
- * @return null
- */
- public Mark selectTo(double x, double y, Mark beginMark) {
- return null;
- }
+ public void paint(PSGraphics2D g2d, TextUtil textUtil, TextPaintInfo tpi)
+ throws IOException {
+ if (getRunLength() > 0) {
+ if (log.isDebugEnabled()) {
+ log.debug("Text run: " + currentChars);
+ }
+ textUtil.writeTextMatrix(this.textTransform);
+ if (isXShow()) {
+ log.debug("Horizontal text: xshow");
+ paintXYShow(g2d, textUtil, tpi.fillPaint, true, false);
+ } else if (isYShow()) {
+ log.debug("Vertical text: yshow");
+ paintXYShow(g2d, textUtil, tpi.fillPaint, false, true);
+ } else {
+ log.debug("Arbitrary text: xyshow");
+ paintXYShow(g2d, textUtil, tpi.fillPaint, true, true);
+ }
+ boolean stroke = (tpi.strokePaint != null) && (tpi.strokeStroke != null);
+ if (stroke) {
+ log.debug("Stroked glyph outlines");
+ paintStrokedGlyphs(g2d, textUtil, tpi.strokePaint, tpi.strokeStroke);
+ }
+ }
+ }
- /**
- * Selec first.
- * This does nothing since the output is pdf and not interactive.
- * @param node the text node
- * @return null
- */
- public Mark selectFirst(TextNode node) {
- return null;
- }
+ private void paintXYShow(PSGraphics2D g2d, TextUtil textUtil, Paint paint,
+ boolean x, boolean y) throws IOException {
+ PSGenerator gen = textUtil.gen;
+ char firstChar = this.currentChars.charAt(0);
+ //Font only has to be setup up before the first character
+ Font f = textUtil.selectFontForChar(firstChar);
+ char mapped = f.mapChar(firstChar);
+ textUtil.selectFont(f, mapped);
+ textUtil.setCurrentFont(f, mapped);
+ applyColor(paint, gen);
+
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ for (int i = 0, c = this.currentChars.length(); i < c; i++) {
+ char ch = this.currentChars.charAt(i);
+ mapped = f.mapChar(ch);
+ PSGenerator.escapeChar(mapped, sb);
+ }
+ sb.append(')');
+ if (x || y) {
+ sb.append("\n[");
+ int idx = 0;
+ Iterator iter = this.relativePositions.iterator();
+ while (iter.hasNext()) {
+ Point2D pt = (Point2D)iter.next();
+ if (idx > 0) {
+ if (x) {
+ sb.append(format(gen, pt.getX()));
+ }
+ if (y) {
+ if (x) {
+ sb.append(' ');
+ }
+ sb.append(format(gen, -pt.getY()));
+ }
+ if (idx % 8 == 0) {
+ sb.append('\n');
+ } else {
+ sb.append(' ');
+ }
+ }
+ idx++;
+ }
+ if (x) {
+ sb.append('0');
+ }
+ if (y) {
+ if (x) {
+ sb.append(' ');
+ }
+ sb.append('0');
+ }
+ sb.append(']');
+ }
+ sb.append(' ');
+ if (x) {
+ sb.append('x');
+ }
+ if (y) {
+ sb.append('y');
+ }
+ sb.append("show"); // --> xshow, yshow or xyshow
+ gen.writeln(sb.toString());
+ }
- /**
- * Select last.
- * This does nothing since the output is pdf and not interactive.
- * @param node the text node
- * @return null
- */
- public Mark selectLast(TextNode node) {
- return null;
- }
+ private String format(PSGenerator gen, double coord) {
+ if (Math.abs(coord) < 0.00001) {
+ return "0";
+ } else {
+ return gen.formatDouble5(coord);
+ }
+ }
- /**
- * Get selected.
- * This does nothing since the output is pdf and not interactive.
- * @param start the start mark
- * @param finish the finish mark
- * @return null
- */
- public int[] getSelected(Mark start, Mark finish) {
- return null;
- }
+ private void paintStrokedGlyphs(PSGraphics2D g2d, TextUtil textUtil,
+ Paint strokePaint, Stroke stroke) throws IOException {
+ PSGenerator gen = textUtil.gen;
+
+ applyColor(strokePaint, gen);
+ PSGraphics2D.applyStroke(stroke, gen);
+
+ Font f = null;
+ Iterator iter = this.relativePositions.iterator();
+ iter.next();
+ Point2D pos = new Point2D.Double(0, 0);
+ gen.writeln("0 0 M");
+ for (int i = 0, c = this.currentChars.length(); i < c; i++) {
+ char ch = this.currentChars.charAt(0);
+ if (i == 0) {
+ //Font only has to be setup up before the first character
+ f = textUtil.selectFontForChar(ch);
+ }
+ char mapped = f.mapChar(ch);
+ if (i == 0) {
+ textUtil.selectFont(f, mapped);
+ textUtil.setCurrentFont(f, mapped);
+ }
+ mapped = f.mapChar(this.currentChars.charAt(i));
+ //add glyph outlines to current path
+ char codepoint = (char)(mapped % 256);
+ gen.write("(" + codepoint + ")");
+ gen.writeln(" false charpath");
+
+ if (iter.hasNext()) {
+ //Position for the next character
+ Point2D pt = (Point2D)iter.next();
+ pos.setLocation(pos.getX() + pt.getX(), pos.getY() - pt.getY());
+ gen.writeln(gen.formatDouble5(pos.getX()) + " "
+ + gen.formatDouble5(pos.getY()) + " M");
+ }
+ }
+ gen.writeln("stroke"); //paints all accumulated glyph outlines
+ }
- /**
- * Get the highlighted shape.
- * This does nothing since the output is pdf and not interactive.
- * @param beginMark the start mark
- * @param endMark the end mark
- * @return null
- */
- public Shape getHighlightShape(Mark beginMark, Mark endMark) {
- return null;
}
}
diff --git a/src/java/org/apache/fop/render/rtf/FOPRtfAttributes.java b/src/java/org/apache/fop/render/rtf/FOPRtfAttributes.java
index 0cfa3e0fe..5cc752d30 100755
--- a/src/java/org/apache/fop/render/rtf/FOPRtfAttributes.java
+++ b/src/java/org/apache/fop/render/rtf/FOPRtfAttributes.java
@@ -21,8 +21,7 @@ package org.apache.fop.render.rtf;
import java.awt.Color;
import org.apache.fop.datatypes.Length;
-import org.apache.fop.datatypes.PercentBaseContext;
-import org.apache.fop.fo.FObj;
+import org.apache.fop.render.DummyPercentBaseContext;
import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfAttributes;
import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfColorTable;
@@ -62,7 +61,7 @@ public class FOPRtfAttributes extends RtfAttributes {
*/
public RtfAttributes setHalfPoints(String name, Length value) {
//Convert millipoints to half-points
- set(name, value.getValue(DummyPercentBaseContext.singleton) / (1000 / 2));
+ set(name, value.getValue(DummyPercentBaseContext.getInstance()) / (1000 / 2));
return this;
}
@@ -82,17 +81,4 @@ public class FOPRtfAttributes extends RtfAttributes {
return this;
}
- private static class DummyPercentBaseContext implements PercentBaseContext {
-
- static DummyPercentBaseContext singleton = new DummyPercentBaseContext();
-
- private DummyPercentBaseContext() {
- // noop
- }
-
- public int getBaseLength(int lengthBase, FObj fo) {
- return 0;
- }
- }
-
}
diff --git a/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java b/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java
index ae4d67516..acb59ed7d 100644
--- a/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java
+++ b/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java
@@ -26,10 +26,12 @@ import org.apache.batik.bridge.Bridge;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.UserAgent;
-import org.apache.fop.fonts.FontInfo;
+
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.fop.fonts.FontInfo;
+
/**
* A FOP base implementation of a Batik BridgeContext.
*/
@@ -49,8 +51,6 @@ public abstract class AbstractFOPBridgeContext extends BridgeContext {
* @param loader the Document Loader to use for referenced documents.
* @param fontInfo the font list for the text painter, may be null
* in which case text is painted as shapes
- * @param linkTransform AffineTransform to properly place links,
- * may be null
* @param imageManager an image manager
* @param imageSessionContext an image session context
* @param linkTransform AffineTransform to properly place links,
diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java b/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java
index 53b8e2ad5..aec4126b4 100644
--- a/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java
+++ b/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java
@@ -19,13 +19,13 @@
package org.apache.fop.svg;
+import org.w3c.dom.Element;
+
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.TextNode;
import org.apache.batik.gvt.TextPainter;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
/**
* Bridge class for the &lt;text> element.
@@ -65,49 +65,5 @@ public abstract class AbstractFOPTextElementBridge extends SVGTextElementBridge
return node;
}
- /**
- * Check if text element contains simple text.
- * This checks the children of the text element to determine
- * if the text is simple. The text is simple if it can be rendered
- * with basic text drawing algorithms. This means there are no
- * alternate characters, the font is known and there are no effects
- * applied to the text.
- *
- * @param ctx the bridge context
- * @param element the svg text element
- * @param node the graphics node
- * @return true if this text is simple of false if it cannot be
- * easily rendered using normal drawString on the Graphics2D
- */
- protected boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
- for (Node n = element.getFirstChild();
- n != null;
- n = n.getNextSibling()) {
-
- switch (n.getNodeType()) {
- case Node.ELEMENT_NODE:
-
- if (n.getLocalName().equals(SVG_TSPAN_TAG)
- || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
- return false;
- } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
- return false;
- } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
- return false;
- }
- break;
- case Node.TEXT_NODE:
- case Node.CDATA_SECTION_NODE:
- default:
- }
- }
-
- /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
- return false;
- }*/
-
- return true;
- }
-
}
diff --git a/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java b/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java
index 83cfa8021..551da21df 100644
--- a/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java
+++ b/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java
@@ -19,6 +19,19 @@
package org.apache.fop.svg;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.DOMImplementation;
+
+import org.xml.sax.EntityResolver;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.util.DocumentFactory;
@@ -28,11 +41,16 @@ import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.keys.BooleanKey;
+import org.apache.batik.transcoder.keys.FloatKey;
+import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.SVGConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SimpleLog;
-import org.w3c.dom.DOMImplementation;
-import org.xml.sax.EntityResolver;
+
+import org.apache.xmlgraphics.image.loader.ImageContext;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
/**
* This is the common base class of all of FOP's transcoders.
@@ -40,11 +58,24 @@ import org.xml.sax.EntityResolver;
public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
/**
+ * The key is used to specify the resolution for on-the-fly images generated
+ * due to complex effects like gradients and filters.
+ */
+ public static final TranscodingHints.Key KEY_DEVICE_RESOLUTION = new FloatKey();
+
+ /**
* The key to specify whether to stroke text instead of using text
* operations.
*/
public static final TranscodingHints.Key KEY_STROKE_TEXT = new BooleanKey();
+ /**
+ * The key is used to specify whether the available fonts should be automatically
+ * detected. The alternative is to configure the transcoder manually using a configuration
+ * file.
+ */
+ public static final TranscodingHints.Key KEY_AUTO_FONTS = new BooleanKey();
+
/** The value to turn on text stroking. */
public static final Boolean VALUE_FORMAT_ON = Boolean.TRUE;
@@ -58,6 +89,9 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
private Log logger;
private EntityResolver resolver;
+ private Configuration cfg = null;
+ private ImageManager imageManager;
+ private ImageSessionContext imageSessionContext;
/**
* Constructs a new FOP-style transcoder.
@@ -80,7 +114,8 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
}
/**
- * @param logger
+ * Sets the logger.
+ * @param logger the logger
*/
public void setLogger(Log logger) {
this.logger = logger;
@@ -94,6 +129,43 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
this.resolver = resolver;
}
+ /** {@inheritDoc} */
+ public void configure(Configuration cfg) throws ConfigurationException {
+ this.cfg = cfg;
+ }
+
+ /**
+ * Returns the default value for the KEY_AUTO_FONTS value.
+ * @return the default value
+ */
+ protected boolean getAutoFontsDefault() {
+ return true;
+ }
+
+ /**
+ * Returns the effective configuration for the transcoder.
+ * @return the effective configuration
+ */
+ protected Configuration getEffectiveConfiguration() {
+ Configuration effCfg = this.cfg;
+ if (effCfg == null) {
+ //By default, enable font auto-detection if no cfg is given
+ boolean autoFonts = getAutoFontsDefault();
+ if (hints.containsKey(KEY_AUTO_FONTS)) {
+ autoFonts = ((Boolean)hints.get(KEY_AUTO_FONTS)).booleanValue();
+ }
+ if (autoFonts) {
+ DefaultConfiguration c = new DefaultConfiguration("cfg");
+ DefaultConfiguration fonts = new DefaultConfiguration("fonts");
+ c.addChild(fonts);
+ DefaultConfiguration autodetect = new DefaultConfiguration("auto-detect");
+ fonts.addChild(autodetect);
+ effCfg = c;
+ }
+ }
+ return effCfg;
+ }
+
/**
* Returns the logger associated with this transcoder. It returns a
* SimpleLog if no logger has been explicitly set.
@@ -142,6 +214,71 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
return stroke;
}
+ /**
+ * Returns the device resolution that has been set up.
+ * @return the device resolution (in dpi)
+ */
+ protected float getDeviceResolution() {
+ if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
+ return ((Float)hints.get(KEY_DEVICE_RESOLUTION)).floatValue();
+ } else {
+ return 72;
+ }
+ }
+
+ /**
+ * Returns the ImageManager to be used by the transcoder.
+ * @return the image manager
+ */
+ protected ImageManager getImageManager() {
+ return this.imageManager;
+ }
+
+ /**
+ * Returns the ImageSessionContext to be used by the transcoder.
+ * @return the image session context
+ */
+ protected ImageSessionContext getImageSessionContext() {
+ return this.imageSessionContext;
+ }
+
+ /**
+ * Sets up the image infrastructure (the image loading framework).
+ * @param baseURI the base URI of the current document
+ */
+ protected void setupImageInfrastructure(final String baseURI) {
+ final ImageContext imageContext = new ImageContext() {
+ public float getSourceResolution() {
+ return 25.4f / userAgent.getPixelUnitToMillimeter();
+ }
+ };
+ this.imageManager = new ImageManager(imageContext);
+ this.imageSessionContext = new AbstractImageSessionContext() {
+
+ public ImageContext getParentContext() {
+ return imageContext;
+ }
+
+ public float getTargetResolution() {
+ return getDeviceResolution();
+ }
+
+ public Source resolveURI(String uri) {
+ System.out.println("resolve " + uri);
+ try {
+ ParsedURL url = new ParsedURL(baseURI, uri);
+ InputStream in = url.openStream();
+ StreamSource source = new StreamSource(in, url.toString());
+ return source;
+ } catch (IOException ioe) {
+ userAgent.displayError(ioe);
+ return null;
+ }
+ }
+
+ };
+ }
+
// --------------------------------------------------------------------
// FOP's default error handler (for transcoders)
// --------------------------------------------------------------------
diff --git a/src/java/org/apache/fop/svg/NativeTextPainter.java b/src/java/org/apache/fop/svg/NativeTextPainter.java
new file mode 100644
index 000000000..7da7269c2
--- /dev/null
+++ b/src/java/org/apache/fop/svg/NativeTextPainter.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.svg;
+
+import java.awt.Graphics2D;
+import java.awt.font.TextAttribute;
+import java.io.IOException;
+import java.text.AttributedCharacterIterator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.batik.bridge.SVGFontFamily;
+import org.apache.batik.gvt.font.GVTFont;
+import org.apache.batik.gvt.font.GVTFontFamily;
+import org.apache.batik.gvt.renderer.StrokingTextPainter;
+import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.TextSpanLayout;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.util.CharUtilities;
+
+/**
+ * Abstract base class for text painters that use specialized text commands native to an output
+ * format to render text.
+ */
+public abstract class NativeTextPainter extends StrokingTextPainter {
+
+ /** the logger for this class */
+ protected Log log = LogFactory.getLog(NativeTextPainter.class);
+
+ /** the font collection */
+ protected final FontInfo fontInfo;
+
+ /**
+ * Creates a new instance.
+ * @param fontInfo the font collection
+ */
+ public NativeTextPainter(FontInfo fontInfo) {
+ this.fontInfo = fontInfo;
+ }
+
+ /**
+ * Indicates whether the given {@link Graphics2D} instance if compatible with this text painter
+ * implementation.
+ * @param g2d the instance to check
+ * @return true if the instance is compatible.
+ */
+ protected abstract boolean isSupported(Graphics2D g2d);
+
+ /**
+ * Paints a single text run.
+ * @param textRun the text run
+ * @param g2d the target Graphics2D instance
+ * @throws IOException if an I/O error occurs while rendering the text
+ */
+ protected abstract void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException;
+
+ /** {@inheritDoc} */
+ protected void paintTextRuns(List textRuns, Graphics2D g2d) {
+ if (log.isTraceEnabled()) {
+ log.trace("paintTextRuns: count = " + textRuns.size());
+ }
+ if (!isSupported(g2d)) {
+ super.paintTextRuns(textRuns, g2d);
+ return;
+ }
+ for (int i = 0; i < textRuns.size(); i++) {
+ TextRun textRun = (TextRun)textRuns.get(i);
+ try {
+ paintTextRun(textRun, g2d);
+ } catch (IOException ioe) {
+ //No other possibility than to use a RuntimeException
+ throw new RuntimeException(ioe);
+ }
+ }
+ }
+
+ /**
+ * Finds an array of suitable fonts for a given AttributedCharacterIterator.
+ * @param aci the character iterator
+ * @return the array of fonts
+ */
+ protected Font[] findFonts(AttributedCharacterIterator aci) {
+ List fonts = new java.util.ArrayList();
+ List gvtFonts = (List) aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
+ Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
+ Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
+ Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);
+
+ String style = ((posture != null) && (posture.floatValue() > 0.0))
+ ? Font.STYLE_ITALIC : Font.STYLE_NORMAL;
+ int weight = ((taWeight != null)
+ && (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
+ : Font.WEIGHT_NORMAL;
+
+ String firstFontFamily = null;
+
+ //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES
+ //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set
+ /* The following code section is not available until Batik 1.7 is released. */
+ GVTFont gvtFont = (GVTFont)aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
+ if (gvtFont != null) {
+ try {
+ String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6!
+ if (log.isDebugEnabled()) {
+ log.debug("Matching font family: " + gvtFontFamily);
+ }
+ if (fontInfo.hasFont(gvtFontFamily, style, weight)) {
+ FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style,
+ weight);
+ int fsize = (int)(fontSize.floatValue() * 1000);
+ fonts.add(fontInfo.getFontInstance(triplet, fsize));
+ }
+ firstFontFamily = gvtFontFamily;
+ } catch (Exception e) {
+ //Most likely NoSuchMethodError here when using Batik 1.6
+ //Just skip this section in this case
+ }
+ }
+
+ if (gvtFonts != null) {
+ Iterator i = gvtFonts.iterator();
+ while (i.hasNext()) {
+ GVTFontFamily fam = (GVTFontFamily) i.next();
+ if (fam instanceof SVGFontFamily) {
+ return null; //Let Batik paint this text!
+ }
+ String fontFamily = fam.getFamilyName();
+ if (log.isDebugEnabled()) {
+ log.debug("Matching font family: " + fontFamily);
+ }
+ if (fontInfo.hasFont(fontFamily, style, weight)) {
+ FontTriplet triplet = fontInfo.fontLookup(fontFamily, style,
+ weight);
+ int fsize = (int)(fontSize.floatValue() * 1000);
+ fonts.add(fontInfo.getFontInstance(triplet, fsize));
+ }
+ if (firstFontFamily == null) {
+ firstFontFamily = fontFamily;
+ }
+ }
+ }
+ if (fonts.size() == 0) {
+ if (firstFontFamily == null) {
+ //This will probably never happen. Just to be on the safe side.
+ firstFontFamily = "any";
+ }
+ //lookup with fallback possibility (incl. substitution notification)
+ FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight);
+ int fsize = (int)(fontSize.floatValue() * 1000);
+ fonts.add(fontInfo.getFontInstance(triplet, fsize));
+ }
+ return (Font[])fonts.toArray(new Font[fonts.size()]);
+ }
+
+ /**
+ * Collects all characters from an {@link AttributedCharacterIterator}.
+ * @param runaci the character iterator
+ * @return the characters
+ */
+ protected CharSequence collectCharacters(AttributedCharacterIterator runaci) {
+ StringBuffer chars = new StringBuffer();
+ for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) {
+ chars.append(runaci.current());
+ runaci.next();
+ }
+ return chars;
+ }
+
+ protected final void logTextRun(AttributedCharacterIterator runaci, TextSpanLayout layout) {
+ if (log.isTraceEnabled()) {
+ int charCount = runaci.getEndIndex() - runaci.getBeginIndex();
+ log.trace("================================================");
+ log.trace("New text run:");
+ log.trace("char count: " + charCount);
+ log.trace("range: "
+ + runaci.getBeginIndex() + " - " + runaci.getEndIndex());
+ log.trace("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs()
+ }
+ }
+
+ protected final void logCharacter(char ch, TextSpanLayout layout, int index,
+ boolean visibleChar) {
+ if (log.isTraceEnabled()) {
+ log.trace("glyph " + index
+ + " -> " + layout.getGlyphIndex(index) + " => " + ch);
+ if (CharUtilities.isAnySpace(ch) && ch != 32) {
+ log.trace("Space found: " + Integer.toHexString(ch));
+ } else if (ch == CharUtilities.ZERO_WIDTH_JOINER) {
+ log.trace("ZWJ found: " + Integer.toHexString(ch));
+ } else if (ch == CharUtilities.SOFT_HYPHEN) {
+ log.trace("Soft hyphen found: " + Integer.toHexString(ch));
+ }
+ if (!visibleChar) {
+ log.trace("Invisible glyph found: " + Integer.toHexString(ch));
+ }
+ }
+ }
+
+
+}
diff --git a/src/java/org/apache/fop/svg/PDFBridgeContext.java b/src/java/org/apache/fop/svg/PDFBridgeContext.java
index 364c7a6f3..e8569f881 100644
--- a/src/java/org/apache/fop/svg/PDFBridgeContext.java
+++ b/src/java/org/apache/fop/svg/PDFBridgeContext.java
@@ -26,10 +26,12 @@ import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.gvt.TextPainter;
-import org.apache.fop.fonts.FontInfo;
+
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.fop.fonts.FontInfo;
+
/**
* BridgeContext which registers the custom bridges for PDF output.
*/
@@ -38,11 +40,9 @@ public class PDFBridgeContext extends AbstractFOPBridgeContext {
/**
* Constructs a new bridge context.
* @param userAgent the user agent
- * @param loader the Document Loader to use for referenced documents.
+ * @param documentLoader the Document Loader to use for referenced documents.
* @param fontInfo the font list for the text painter, may be null
* in which case text is painted as shapes
- * @param linkTransform AffineTransform to properly place links,
- * may be null
* @param imageManager an image manager
* @param imageSessionContext an image session context
* @param linkTransform AffineTransform to properly place links,
@@ -52,7 +52,8 @@ public class PDFBridgeContext extends AbstractFOPBridgeContext {
FontInfo fontInfo, ImageManager imageManager,
ImageSessionContext imageSessionContext,
AffineTransform linkTransform) {
- super(userAgent, documentLoader, fontInfo, imageManager, imageSessionContext, linkTransform);
+ super(userAgent, documentLoader, fontInfo,
+ imageManager, imageSessionContext, linkTransform);
}
/**
diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
index e101a9573..b77518ab0 100644
--- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
+++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
@@ -55,6 +55,22 @@ public class PDFDocumentGraphics2DConfigurator {
//Fonts
try {
+ FontInfo fontInfo = createFontInfo(cfg);
+ graphics.setFontInfo(fontInfo);
+ } catch (FOPException e) {
+ throw new ConfigurationException("Error while setting up fonts", e);
+ }
+ }
+
+ /**
+ * Creates the {@link FontInfo} instance for the given configuration.
+ * @param cfg the configuration
+ * @return the font collection
+ * @throws FOPException if an error occurs while setting up the fonts
+ */
+ public static FontInfo createFontInfo(Configuration cfg) throws FOPException {
+ FontInfo fontInfo = new FontInfo();
+ if (cfg != null) {
FontResolver fontResolver = FontManager.createMinimalFontResolver();
//TODO The following could be optimized by retaining the FontManager somewhere
FontManager fontManager = new FontManager();
@@ -73,12 +89,11 @@ public class PDFDocumentGraphics2DConfigurator {
if (fontManager.useCache()) {
fontManager.getFontCache().save();
}
- FontInfo fontInfo = new FontInfo();
FontSetup.setup(fontInfo, fontInfoList, fontResolver);
- graphics.setFontInfo(fontInfo);
- } catch (FOPException e) {
- throw new ConfigurationException("Error while setting up fonts", e);
+ } else {
+ FontSetup.setup(fontInfo);
}
+ return fontInfo;
}
}
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index ede368f58..c5da8af6f 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -707,8 +707,13 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
if (s == null) {
return;
}
- preparePainting();
PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
+ if (iter.isDone()) {
+ // no segments available. Not worth doing anything
+ return;
+ }
+ preparePainting();
+
processPathIterator(iter);
// clip area
currentStream.write("W\n");
diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java
index 85447a4f9..dddf61a6e 100644
--- a/src/java/org/apache/fop/svg/PDFTextPainter.java
+++ b/src/java/org/apache/fop/svg/PDFTextPainter.java
@@ -25,28 +25,18 @@ import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
-import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
-import java.lang.reflect.Method;
import java.text.AttributedCharacterIterator;
-import java.util.Iterator;
-import java.util.List;
-import org.apache.batik.bridge.SVGFontFamily;
-import org.apache.batik.gvt.font.GVTFont;
-import org.apache.batik.gvt.font.GVTFontFamily;
import org.apache.batik.gvt.font.GVTGlyphVector;
-import org.apache.batik.gvt.renderer.StrokingTextPainter;
-import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.text.TextPaintInfo;
import org.apache.batik.gvt.text.TextSpanLayout;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.util.CharUtilities;
/**
@@ -59,193 +49,159 @@ import org.apache.fop.util.CharUtilities;
*
* @version $Id$
*/
-public class PDFTextPainter extends StrokingTextPainter {
+class PDFTextPainter extends NativeTextPainter {
private static final boolean DEBUG = false;
- private final boolean strokeText = false;
- private final FontInfo fontInfo;
-
/**
* Create a new PDF text painter with the given font information.
* @param fi the font info
*/
public PDFTextPainter(FontInfo fi) {
- fontInfo = fi;
+ super(fi);
}
/** {@inheritDoc} */
- protected void paintTextRuns(List textRuns, Graphics2D g2d) {
- if (DEBUG) {
- System.out.println("paintTextRuns: count = " + textRuns.size());
- //fontInfo.dumpAllTripletsToSystemOut();
- }
- if (!(g2d instanceof PDFGraphics2D) || strokeText) {
- super.paintTextRuns(textRuns, g2d);
+ protected boolean isSupported(Graphics2D g2d) {
+ return g2d instanceof PDFGraphics2D;
+ }
+
+ /** {@inheritDoc} */
+ protected void paintTextRun(TextRun textRun, Graphics2D g2d) {
+ AttributedCharacterIterator runaci = textRun.getACI();
+ runaci.first();
+
+ TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
+ if (tpi == null || !tpi.visible) {
return;
}
+ if ((tpi != null) && (tpi.composite != null)) {
+ g2d.setComposite(tpi.composite);
+ }
+
+ //------------------------------------
+ TextSpanLayout layout = textRun.getLayout();
+ logTextRun(runaci, layout);
+ CharSequence chars = collectCharacters(runaci);
+ runaci.first(); //Reset ACI
+
final PDFGraphics2D pdf = (PDFGraphics2D)g2d;
PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) {
protected void write(String code) {
pdf.currentStream.write(code);
}
};
- for (int i = 0; i < textRuns.size(); i++) {
- TextRun textRun = (TextRun)textRuns.get(i);
- AttributedCharacterIterator runaci = textRun.getACI();
- runaci.first();
- TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
- if (tpi == null || !tpi.visible) {
- continue;
- }
- if ((tpi != null) && (tpi.composite != null)) {
- g2d.setComposite(tpi.composite);
- }
+ if (DEBUG) {
+ log.debug("Text: " + chars);
+ pdf.currentStream.write("%Text: " + chars + "\n");
+ }
- //------------------------------------
- TextSpanLayout layout = textRun.getLayout();
- if (DEBUG) {
- int charCount = runaci.getEndIndex() - runaci.getBeginIndex();
- System.out.println("================================================");
- System.out.println("New text run:");
- System.out.println("char count: " + charCount);
- System.out.println("range: "
- + runaci.getBeginIndex() + " - " + runaci.getEndIndex());
- System.out.println("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs()
- }
- //Gather all characters of the run
- StringBuffer chars = new StringBuffer();
- for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) {
- chars.append(runaci.current());
- runaci.next();
- }
- runaci.first();
- if (DEBUG) {
- System.out.println("Text: " + chars);
- pdf.currentStream.write("%Text: " + chars + "\n");
- }
+ GeneralPath debugShapes = null;
+ if (DEBUG) {
+ debugShapes = new GeneralPath();
+ }
- GeneralPath debugShapes = null;
- if (DEBUG) {
- debugShapes = new GeneralPath();
- }
+ Font[] fonts = findFonts(runaci);
+ if (fonts == null || fonts.length == 0) {
+ //Draw using Java2D when no native fonts are available
+ textRun.getLayout().draw(g2d);
+ return;
+ }
- Font[] fonts = findFonts(runaci);
- if (fonts == null || fonts.length == 0) {
- //Draw using Java2D
- textRun.getLayout().draw(g2d);
+ textUtil.saveGraphicsState();
+ textUtil.concatMatrix(g2d.getTransform());
+ Shape imclip = g2d.getClip();
+ pdf.writeClip(imclip);
+
+ applyColorAndPaint(tpi, pdf);
+
+ textUtil.beginTextObject();
+ textUtil.setFonts(fonts);
+ boolean stroke = (tpi.strokePaint != null)
+ && (tpi.strokeStroke != null);
+ textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false);
+
+ AffineTransform localTransform = new AffineTransform();
+ Point2D prevPos = null;
+ double prevVisibleCharWidth = 0.0;
+ GVTGlyphVector gv = layout.getGlyphVector();
+ for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
+ char ch = chars.charAt(index);
+ boolean visibleChar = gv.isGlyphVisible(index)
+ || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
+ logCharacter(ch, layout, index, visibleChar);
+ if (!visibleChar) {
continue;
}
+ Point2D glyphPos = gv.getGlyphPosition(index);
- textUtil.saveGraphicsState();
- textUtil.concatMatrix(g2d.getTransform());
- Shape imclip = g2d.getClip();
- pdf.writeClip(imclip);
-
- applyColorAndPaint(tpi, pdf);
-
- textUtil.beginTextObject();
- textUtil.setFonts(fonts);
- boolean stroke = (tpi.strokePaint != null)
- && (tpi.strokeStroke != null);
- textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false);
-
- AffineTransform localTransform = new AffineTransform();
- Point2D prevPos = null;
- double prevVisibleCharWidth = 0.0;
- GVTGlyphVector gv = layout.getGlyphVector();
- for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
- char ch = chars.charAt(index);
- boolean visibleChar = gv.isGlyphVisible(index)
- || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
- if (DEBUG) {
- System.out.println("glyph " + index
- + " -> " + layout.getGlyphIndex(index) + " => " + ch);
- if (CharUtilities.isAnySpace(ch) && ch != 32) {
- System.out.println("Space found: " + Integer.toHexString(ch));
- }
- if (ch == CharUtilities.ZERO_WIDTH_JOINER) {
- System.out.println("ZWJ found: " + Integer.toHexString(ch));
- }
- if (ch == CharUtilities.SOFT_HYPHEN) {
- System.out.println("Soft hyphen found: " + Integer.toHexString(ch));
- }
- if (!visibleChar) {
- System.out.println("Invisible glyph found: " + Integer.toHexString(ch));
- }
- }
- if (!visibleChar) {
- continue;
- }
- Point2D p = gv.getGlyphPosition(index);
-
- AffineTransform glyphTransform = gv.getGlyphTransform(index);
- //TODO Glyph transforms could be refined so not every char has to be painted
- //with its own TJ command (stretch/squeeze case could be optimized)
- if (DEBUG) {
- System.out.println("pos " + p + ", transform " + glyphTransform);
- Shape sh;
- sh = gv.getGlyphLogicalBounds(index);
- if (sh == null) {
- sh = new Ellipse2D.Double(p.getX(), p.getY(), 2, 2);
- }
- debugShapes.append(sh, false);
+ AffineTransform glyphTransform = gv.getGlyphTransform(index);
+ //TODO Glyph transforms could be refined so not every char has to be painted
+ //with its own TJ command (stretch/squeeze case could be optimized)
+ if (log.isTraceEnabled()) {
+ log.trace("pos " + glyphPos + ", transform " + glyphTransform);
+ }
+ if (DEBUG) {
+ Shape sh = gv.getGlyphLogicalBounds(index);
+ if (sh == null) {
+ sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2);
}
+ debugShapes.append(sh, false);
+ }
- //Exact position of the glyph
- localTransform.setToIdentity();
- localTransform.translate(p.getX(), p.getY());
- if (glyphTransform != null) {
- localTransform.concatenate(glyphTransform);
- }
- localTransform.scale(1, -1);
+ //Exact position of the glyph
+ localTransform.setToIdentity();
+ localTransform.translate(glyphPos.getX(), glyphPos.getY());
+ if (glyphTransform != null) {
+ localTransform.concatenate(glyphTransform);
+ }
+ localTransform.scale(1, -1);
- boolean yPosChanged = (prevPos == null
- || prevPos.getY() != p.getY()
- || glyphTransform != null);
- if (yPosChanged) {
- if (index > 0) {
- textUtil.writeTJ();
- textUtil.writeTextMatrix(localTransform);
- }
- } else {
- double xdiff = p.getX() - prevPos.getX();
- //Width of previous character
- Font font = textUtil.getCurrentFont();
- double cw = prevVisibleCharWidth;
- double effxdiff = (1000 * xdiff) - cw;
- if (effxdiff != 0) {
- double adjust = (-effxdiff / font.getFontSize());
- textUtil.adjustGlyphTJ(adjust * 1000);
- }
- if (DEBUG) {
- System.out.println("==> x diff: " + xdiff + ", " + effxdiff
- + ", charWidth: " + cw);
- }
- }
- Font f = textUtil.selectFontForChar(ch);
- if (f != textUtil.getCurrentFont()) {
+ boolean yPosChanged = (prevPos == null
+ || prevPos.getY() != glyphPos.getY()
+ || glyphTransform != null);
+ if (yPosChanged) {
+ if (index > 0) {
textUtil.writeTJ();
- textUtil.setCurrentFont(f);
- textUtil.writeTf(f);
textUtil.writeTextMatrix(localTransform);
}
- char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
- textUtil.writeTJChar(paintChar);
-
- //Update last position
- prevPos = p;
- prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
+ } else {
+ double xdiff = glyphPos.getX() - prevPos.getX();
+ //Width of previous character
+ Font font = textUtil.getCurrentFont();
+ double cw = prevVisibleCharWidth;
+ double effxdiff = (1000 * xdiff) - cw;
+ if (effxdiff != 0) {
+ double adjust = (-effxdiff / font.getFontSize());
+ textUtil.adjustGlyphTJ(adjust * 1000);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("==> x diff: " + xdiff + ", " + effxdiff
+ + ", charWidth: " + cw);
+ }
}
- textUtil.writeTJ();
- textUtil.endTextObject();
- textUtil.restoreGraphicsState();
- if (DEBUG) {
- g2d.setStroke(new BasicStroke(0));
- g2d.setColor(Color.LIGHT_GRAY);
- g2d.draw(debugShapes);
+ Font f = textUtil.selectFontForChar(ch);
+ if (f != textUtil.getCurrentFont()) {
+ textUtil.writeTJ();
+ textUtil.setCurrentFont(f);
+ textUtil.writeTf(f);
+ textUtil.writeTextMatrix(localTransform);
}
+ char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
+ textUtil.writeTJChar(paintChar);
+
+ //Update last position
+ prevPos = glyphPos;
+ prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
+ }
+ textUtil.writeTJ();
+ textUtil.endTextObject();
+ textUtil.restoreGraphicsState();
+ if (DEBUG) {
+ g2d.setStroke(new BasicStroke(0));
+ g2d.setColor(Color.LIGHT_GRAY);
+ g2d.draw(debugShapes);
}
}
@@ -271,85 +227,4 @@ public class PDFTextPainter extends StrokingTextPainter {
pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE);
}
- private Font[] findFonts(AttributedCharacterIterator aci) {
- List fonts = new java.util.ArrayList();
- List gvtFonts = (List) aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
- Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
- Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
- Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);
-
- String style = ((posture != null) && (posture.floatValue() > 0.0))
- ? Font.STYLE_ITALIC : Font.STYLE_NORMAL;
- int weight = ((taWeight != null)
- && (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
- : Font.WEIGHT_NORMAL;
-
- String firstFontFamily = null;
-
- //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES
- //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set
- /* The following code section is not available until Batik 1.7 is released. */
- GVTFont gvtFont = (GVTFont)aci.getAttribute(
- GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
- if (gvtFont != null) {
- try {
- Method method = gvtFont.getClass().getMethod("getFamilyName", null);
- String gvtFontFamily = (String)method.invoke(gvtFont, null);
- //TODO Uncomment the following line when Batik 1.7 is shipped with FOP
- //String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6
- if (DEBUG) {
- System.out.print(gvtFontFamily + ", ");
- }
- if (fontInfo.hasFont(gvtFontFamily, style, weight)) {
- FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style,
- weight);
- int fsize = (int)(fontSize.floatValue() * 1000);
- fonts.add(fontInfo.getFontInstance(triplet, fsize));
- }
- firstFontFamily = gvtFontFamily;
- } catch (Exception e) {
- //Most likely NoSuchMethodError here when using Batik 1.6
- //Just skip this section in this case
- }
- }
-
- if (gvtFonts != null) {
- Iterator i = gvtFonts.iterator();
- while (i.hasNext()) {
- GVTFontFamily fam = (GVTFontFamily) i.next();
- if (fam instanceof SVGFontFamily) {
- return null; //Let Batik paint this text!
- }
- String fontFamily = fam.getFamilyName();
- if (DEBUG) {
- System.out.print(fontFamily + ", ");
- }
- if (fontInfo.hasFont(fontFamily, style, weight)) {
- FontTriplet triplet = fontInfo.fontLookup(fontFamily, style,
- weight);
- int fsize = (int)(fontSize.floatValue() * 1000);
- fonts.add(fontInfo.getFontInstance(triplet, fsize));
- }
- if (firstFontFamily == null) {
- firstFontFamily = fontFamily;
- }
- }
- }
- if (fonts.size() == 0) {
- if (firstFontFamily == null) {
- //This will probably never happen. Just to be on the safe side.
- firstFontFamily = "any";
- }
- //lookup with fallback possibility (incl. substitution notification)
- FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight);
- int fsize = (int)(fontSize.floatValue() * 1000);
- fonts.add(fontInfo.getFontInstance(triplet, fsize));
- }
- if (DEBUG) {
- System.out.println();
- }
- return (Font[])fonts.toArray(new Font[fonts.size()]);
- }
-
} \ No newline at end of file
diff --git a/src/java/org/apache/fop/svg/PDFTranscoder.java b/src/java/org/apache/fop/svg/PDFTranscoder.java
index 333cd5e4c..062270f6b 100644
--- a/src/java/org/apache/fop/svg/PDFTranscoder.java
+++ b/src/java/org/apache/fop/svg/PDFTranscoder.java
@@ -23,34 +23,19 @@ import java.awt.Color;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.InputStream;
-
-import javax.xml.transform.Source;
-import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGLength;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
-import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.UnitProcessor;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.ext.awt.RenderingHintsKeyExt;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderOutput;
-import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
-import org.apache.batik.transcoder.keys.BooleanKey;
-import org.apache.batik.transcoder.keys.FloatKey;
-import org.apache.batik.util.ParsedURL;
-
-import org.apache.xmlgraphics.image.loader.ImageContext;
-import org.apache.xmlgraphics.image.loader.ImageManager;
-import org.apache.xmlgraphics.image.loader.ImageSessionContext;
-import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
import org.apache.fop.Version;
import org.apache.fop.fonts.FontInfo;
@@ -91,27 +76,9 @@ import org.apache.fop.fonts.FontInfo;
public class PDFTranscoder extends AbstractFOPTranscoder
implements Configurable {
- /**
- * The key is used to specify the resolution for on-the-fly images generated
- * due to complex effects like gradients and filters.
- */
- public static final TranscodingHints.Key KEY_DEVICE_RESOLUTION = new FloatKey();
-
- /**
- * The key is used to specify whether the available fonts should be automatically
- * detected. The alternative is to configure the transcoder manually using a configuration
- * file.
- */
- public static final TranscodingHints.Key KEY_AUTO_FONTS = new BooleanKey();
-
- private Configuration cfg = null;
-
/** Graphics2D instance that is used to paint to */
protected PDFDocumentGraphics2D graphics = null;
- private ImageManager imageManager;
- private ImageSessionContext imageSessionContext;
-
/**
* Constructs a new <tt>PDFTranscoder</tt>.
*/
@@ -133,11 +100,6 @@ public class PDFTranscoder extends AbstractFOPTranscoder
};
}
- /** {@inheritDoc} */
- public void configure(Configuration cfg) throws ConfigurationException {
- this.cfg = cfg;
- }
-
/**
* Transcodes the specified Document as an image in the specified output.
*
@@ -155,28 +117,13 @@ public class PDFTranscoder extends AbstractFOPTranscoder
+ Version.getVersion()
+ ": PDF Transcoder for Batik");
if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
- graphics.setDeviceDPI(((Float)hints.get(KEY_DEVICE_RESOLUTION)).floatValue());
+ graphics.setDeviceDPI(getDeviceResolution());
}
setupImageInfrastructure(uri);
try {
- Configuration effCfg = this.cfg;
- if (effCfg == null) {
- //By default, enable font auto-detection if no cfg is given
- boolean autoFonts = true;
- if (hints.containsKey(KEY_AUTO_FONTS)) {
- autoFonts = ((Boolean)hints.get(KEY_AUTO_FONTS)).booleanValue();
- }
- if (autoFonts) {
- DefaultConfiguration c = new DefaultConfiguration("pdf-transcoder");
- DefaultConfiguration fonts = new DefaultConfiguration("fonts");
- c.addChild(fonts);
- DefaultConfiguration autodetect = new DefaultConfiguration("auto-detect");
- fonts.addChild(autodetect);
- effCfg = c;
- }
- }
+ Configuration effCfg = getEffectiveConfiguration();
if (effCfg != null) {
PDFDocumentGraphics2DConfigurator configurator
@@ -242,39 +189,6 @@ public class PDFTranscoder extends AbstractFOPTranscoder
}
}
- private void setupImageInfrastructure(final String baseURI) {
- final ImageContext imageContext = new ImageContext() {
- public float getSourceResolution() {
- return 25.4f / userAgent.getPixelUnitToMillimeter();
- }
- };
- this.imageManager = new ImageManager(imageContext);
- this.imageSessionContext = new AbstractImageSessionContext() {
-
- public ImageContext getParentContext() {
- return imageContext;
- }
-
- public float getTargetResolution() {
- return graphics.getDeviceDPI();
- }
-
- public Source resolveURI(String uri) {
- System.out.println("resolve " + uri);
- try {
- ParsedURL url = new ParsedURL(baseURI, uri);
- InputStream in = url.openStream();
- StreamSource source = new StreamSource(in, url.toString());
- return source;
- } catch (IOException ioe) {
- userAgent.displayError(ioe);
- return null;
- }
- }
-
- };
- }
-
/** {@inheritDoc} */
protected BridgeContext createBridgeContext() {
//For compatibility with Batik 1.6
@@ -288,7 +202,7 @@ public class PDFTranscoder extends AbstractFOPTranscoder
fontInfo = null;
}
BridgeContext ctx = new PDFBridgeContext(userAgent, fontInfo,
- this.imageManager, this.imageSessionContext);
+ getImageManager(), getImageSessionContext());
return ctx;
}
diff --git a/src/java/org/apache/fop/tools/anttasks/SerializeHyphPattern.java b/src/java/org/apache/fop/tools/anttasks/SerializeHyphPattern.java
deleted file mode 100644
index 778c39f9a..000000000
--- a/src/java/org/apache/fop/tools/anttasks/SerializeHyphPattern.java
+++ /dev/null
@@ -1,178 +0,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.
- */
-
-/* $Id$ */
-
-package org.apache.fop.tools.anttasks;
-
-// Java
-import java.io.File;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.util.List;
-
-// Ant
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.DirectoryScanner;
-import org.apache.tools.ant.types.FileSet;
-
-// FOP
-import org.apache.fop.hyphenation.HyphenationTree;
-import org.apache.fop.hyphenation.HyphenationException;
-
-/**
- * SerializeHyphPattern
- */
-
-
-public class SerializeHyphPattern extends Task {
- private List filesets = new java.util.ArrayList();
- private File targetDir;
- private boolean errorDump = false;
-
- /**
- * {@inheritDoc}
- */
- public void execute() throws org.apache.tools.ant.BuildException {
- // deal with the filesets
- for (int i = 0; i < getFilesets().size(); i++) {
- FileSet fs = (FileSet) getFilesets().get(i);
- DirectoryScanner ds = fs.getDirectoryScanner(getProject());
- File basedir = ds.getBasedir();
- String[] files = ds.getIncludedFiles();
- for (int j = 0; j < files.length; j++) {
- processFile(basedir, files[j].substring(0, files[j].length() - 4));
- }
- }
- } // end execute
-
-
- /**
- * Adds a set of pattern files (nested fileset attribute).
- * @param set a fileset
- */
- public void addFileset(FileSet set) {
- filesets.add(set);
- }
-
- /**
- * Returns the current list of filesets.
- * @return the filesets
- */
- public List getFilesets() {
- return this.filesets;
- }
-
- /**
- * Sets the target directory
- * @param targetDir target directory
- */
- public void setTargetDir(String targetDir) {
- File dir = new File(targetDir);
- this.targetDir = dir;
- }
-
- /**
- * Controls the amount of error information dumped.
- * @param errorDump True if more error info should be provided
- */
- public void setErrorDump(boolean errorDump) {
- this.errorDump = errorDump;
- }
-
-
- /*
- * checks whether input or output files exists or the latter is older than input file
- * and start build if necessary
- */
- private void processFile(File basedir, String filename) {
- File infile = new File(basedir, filename + ".xml");
- File outfile = new File(targetDir, filename + ".hyp");
- //long outfileLastModified = outfile.lastModified();
- boolean startProcess = true;
-
- startProcess = rebuild(infile, outfile);
- if (startProcess) {
- buildPatternFile(infile, outfile);
- }
- }
-
- /*
- * serializes pattern files
- */
- private void buildPatternFile(File infile, File outfile) {
- System.out.println("Processing " + infile);
- HyphenationTree hTree = new HyphenationTree();
- try {
- hTree.loadPatterns(infile.toString());
- if (errorDump) {
- System.out.println("Stats: ");
- hTree.printStats();
- }
- } catch (HyphenationException ex) {
- System.err.println("Can't load patterns from xml file " + infile
- + " - Maybe hyphenation.dtd is missing?");
- if (errorDump) {
- System.err.println(ex.toString());
- }
- }
- // serialize class
- try {
- ObjectOutputStream out = new ObjectOutputStream(
- new java.io.BufferedOutputStream(
- new java.io.FileOutputStream(outfile)));
- out.writeObject(hTree);
- out.close();
- } catch (IOException ioe) {
- System.err.println("Can't write compiled pattern file: "
- + outfile);
- System.err.println(ioe);
- }
- }
-
- /**
- * Checks for existence of output file and compares
- * dates with input and stylesheet file
- */
- private boolean rebuild(File infile, File outfile) {
- if (outfile.exists()) {
- // checks whether output file is older than input file
- if (outfile.lastModified() < infile.lastModified()) {
- return true;
- }
- } else {
- // if output file does not exist, start process
- return true;
- }
- return false;
- } // end rebuild
-
- /*
- * //quick access for debugging
- * public static void main (String args[]) {
- * SerializeHyphPattern ser = new SerializeHyphPattern();
- * FileSet set = new FileSet();
- * set.setDir(new File("src/hyph"));
- * set.setIncludes("*.xml");
- * ser.addFileset(set);
- * ser.setTargetDir("build/hyph");
- * ser.execute();
- * }
- */
-
-
-}
diff --git a/src/java/org/apache/fop/util/ColorExt.java b/src/java/org/apache/fop/util/ColorExt.java
index d2e73d227..22c2dcc36 100644
--- a/src/java/org/apache/fop/util/ColorExt.java
+++ b/src/java/org/apache/fop/util/ColorExt.java
@@ -176,7 +176,9 @@ public final class ColorExt extends Color {
sb.append(this.rgbReplacementGreen + ",");
sb.append(this.rgbReplacementBlue + ",");
sb.append(this.iccProfileName + ",");
- sb.append("\"" + this.iccProfileSrc + "\"");
+ if (this.iccProfileSrc != null) {
+ sb.append("\"" + this.iccProfileSrc + "\"");
+ }
float[] colorComponents = this.getColorComponents(null);
for (int ix = 0; ix < colorComponents.length; ix++) {
sb.append(",");
diff --git a/src/java/org/apache/fop/util/ColorUtil.java b/src/java/org/apache/fop/util/ColorUtil.java
index 9534bfba3..8100aef96 100644
--- a/src/java/org/apache/fop/util/ColorUtil.java
+++ b/src/java/org/apache/fop/util/ColorUtil.java
@@ -38,6 +38,9 @@ import org.apache.fop.fo.expr.PropertyException;
*/
public final class ColorUtil {
+ /** The name for the uncalibrated CMYK pseudo-profile */
+ public static final String CMYK_PSEUDO_PROFILE = "#CMYK";
+
/**
*
* keeps all the predefined and parsed colors.
@@ -319,26 +322,32 @@ public final class ColorUtil {
if (iccProfileName == null || "".equals(iccProfileName)) {
throw new PropertyException("ICC profile name missing");
}
- /* Get and verify ICC profile source */
- String iccProfileSrc = args[4].trim();
- if (iccProfileSrc == null || "".equals(iccProfileSrc)) {
- throw new PropertyException("ICC profile source missing");
- }
- if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) {
- iccProfileSrc = iccProfileSrc.substring(1);
- }
- if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) {
- iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1);
+ ColorSpace colorSpace = null;
+ String iccProfileSrc = null;
+ if (isPseudoProfile(iccProfileName)) {
+ if (CMYK_PSEUDO_PROFILE.equalsIgnoreCase(iccProfileName)) {
+ colorSpace = CMYKColorSpace.getInstance();
+ } else {
+ assert false : "Incomplete implementation";
+ }
+ } else {
+ /* Get and verify ICC profile source */
+ iccProfileSrc = args[4].trim();
+ if (iccProfileSrc == null || "".equals(iccProfileSrc)) {
+ throw new PropertyException("ICC profile source missing");
+ }
+ if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) {
+ iccProfileSrc = iccProfileSrc.substring(1);
+ }
+ if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) {
+ iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1);
+ }
}
/* ICC profile arguments */
float[] iccComponents = new float[args.length - 5];
for (int ix = 4; ++ix < args.length;) {
iccComponents[ix - 5] = Float.parseFloat(args[ix].trim());
}
- /* Ask FOP factory to get ColorSpace for the specified ICC profile source */
- ColorSpace colorSpace = (foUserAgent != null
- ? foUserAgent.getFactory().getColorSpace(
- foUserAgent.getBaseURL(), iccProfileSrc) : null);
float red = 0, green = 0, blue = 0;
red = Float.parseFloat(args[0].trim());
@@ -352,6 +361,11 @@ public final class ColorUtil {
+ "Fallback RGB arguments to fop-rgb-icc() must be [0..1]");
}
+ /* Ask FOP factory to get ColorSpace for the specified ICC profile source */
+ if (foUserAgent != null && iccProfileSrc != null) {
+ colorSpace = foUserAgent.getFactory().getColorSpace(
+ foUserAgent.getBaseURL(), iccProfileSrc);
+ }
if (colorSpace != null) {
// ColorSpace available - create ColorExt (keeps track of replacement rgb
// values for possible later colorTOsRGBString call
@@ -440,7 +454,7 @@ public final class ColorUtil {
CMYKColorSpace cmykCs = CMYKColorSpace.getInstance();
float[] rgb = cmykCs.toRGB(cmyk);
parsedColor = ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2],
- null, "#CMYK", cmykCs, cmyk);
+ CMYK_PSEUDO_PROFILE, null, cmykCs, cmyk);
} catch (PropertyException pe) {
throw pe;
} catch (Exception e) {
@@ -465,13 +479,13 @@ public final class ColorUtil {
*/
public static String colorToString(Color color) {
ColorSpace cs = color.getColorSpace();
- if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) {
+ if (color instanceof ColorExt) {
+ return ((ColorExt)color).toFunctionCall();
+ } else if (cs != null && cs.getType() == ColorSpace.TYPE_CMYK) {
StringBuffer sbuf = new StringBuffer(24);
float[] cmyk = color.getColorComponents(null);
sbuf.append("cmyk(" + cmyk[0] + "," + cmyk[1] + "," + cmyk[2] + "," + cmyk[3] + ")");
return sbuf.toString();
- } else if (color instanceof ColorExt) {
- return ((ColorExt)color).toFunctionCall();
} else {
StringBuffer sbuf = new StringBuffer();
sbuf.append('#');
@@ -681,4 +695,35 @@ public final class ColorUtil {
return new Color(cols[0], cols[1], cols[2], cols[3]);
}
+ /**
+ * Indicates whether the given color profile name is one of the pseudo-profiles supported
+ * by FOP (ex. #CMYK).
+ * @param colorProfileName the color profile name to check
+ * @return true if the color profile name is of a built-in pseudo-profile
+ */
+ public static boolean isPseudoProfile(String colorProfileName) {
+ return CMYK_PSEUDO_PROFILE.equalsIgnoreCase(colorProfileName);
+ }
+
+ /**
+ * Indicates whether the color is a gray value.
+ * @param col the color
+ * @return true if it is a gray value
+ */
+ public static boolean isGray(Color col) {
+ return (col.getRed() == col.getBlue() && col.getRed() == col.getGreen());
+ }
+
+ /**
+ * Creates an uncalibrary CMYK color with the given gray value.
+ * @param black the gray component (0 - 1)
+ * @return the CMYK color
+ */
+ public static Color toCMYKGrayColor(float black) {
+ float[] cmyk = new float[] {0f, 0f, 0f, 1.0f - black};
+ CMYKColorSpace cmykCs = CMYKColorSpace.getInstance();
+ float[] rgb = cmykCs.toRGB(cmyk);
+ return ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2],
+ CMYK_PSEUDO_PROFILE, null, cmykCs, cmyk);
+ }
}
diff --git a/src/java/org/apache/fop/util/DOM2SAX.java b/src/java/org/apache/fop/util/DOM2SAX.java
index b9021ed3a..39d2af4a1 100644
--- a/src/java/org/apache/fop/util/DOM2SAX.java
+++ b/src/java/org/apache/fop/util/DOM2SAX.java
@@ -26,7 +26,6 @@ import java.util.Stack;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
-
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
@@ -77,7 +76,7 @@ public class DOM2SAX {
contentHandler.endDocument();
}
}
-
+
/**
* Writes the given fragment using the given ContentHandler.
* @param node DOM node
diff --git a/src/java/org/apache/fop/util/bitmap/DitherUtil.java b/src/java/org/apache/fop/util/bitmap/DitherUtil.java
new file mode 100644
index 000000000..c61befc9c
--- /dev/null
+++ b/src/java/org/apache/fop/util/bitmap/DitherUtil.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util.bitmap;
+
+import java.awt.Color;
+
+/**
+ * Utility methods for dithering.
+ */
+public class DitherUtil {
+
+ /** Selects a 2x2 Bayer dither matrix (5 grayscales) */
+ public static final int DITHER_MATRIX_2X2 = 2;
+ /** Selects a 4x4 Bayer dither matrix (17 grayscales) */
+ public static final int DITHER_MATRIX_4X4 = 4;
+ /** Selects a 8x8 Bayer dither matrix (65 grayscales) */
+ public static final int DITHER_MATRIX_8X8 = 8;
+
+ //Bayer dither matrices (4x4 and 8x8 are derived from the 2x2 matrix)
+ private static final int[] BAYER_D2 = new int[] {0, 2, 3, 1};
+ private static final int[] BAYER_D4;
+ private static final int[] BAYER_D8;
+
+ static {
+ BAYER_D4 = deriveBayerMatrix(BAYER_D2);
+ BAYER_D8 = deriveBayerMatrix(BAYER_D4);
+ }
+
+ private static int[] deriveBayerMatrix(int[] d) {
+ int[] dn = new int[d.length * 4];
+ int half = (int)Math.sqrt(d.length);
+ for (int part = 0; part < 4; part++) {
+ for (int i = 0, c = d.length; i < c; i++) {
+ setValueInMatrix(dn, half, part, i, d[i] * 4 + BAYER_D2[part]);
+ }
+ }
+ return dn;
+ }
+
+ private static void setValueInMatrix(int[] dn, int half, int part, int idx, int value) {
+ int xoff = (part & 1) * half;
+ int yoff = (part & 2) * half * half;
+ int matrixIndex = yoff + ((idx / half) * half * 2) + (idx % half) + xoff;
+ dn[matrixIndex] = value;
+ }
+
+ /**
+ * Returns the Bayer dither base pattern for a particular matrix size.
+ * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4}
+ * or {@link #DITHER_MATRIX_8X8})
+ * @return the base pattern for the given size
+ */
+ public static int[] getBayerBasePattern(int matrix) {
+ int[] result = new int[matrix * matrix];
+ switch (matrix) {
+ case DITHER_MATRIX_2X2:
+ System.arraycopy(BAYER_D2, 0, result, 0, BAYER_D2.length);
+ break;
+ case DITHER_MATRIX_4X4:
+ System.arraycopy(BAYER_D4, 0, result, 0, BAYER_D4.length);
+ break;
+ case DITHER_MATRIX_8X8:
+ System.arraycopy(BAYER_D8, 0, result, 0, BAYER_D8.length);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported dither matrix: " + matrix);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a byte array containing the dither pattern for the given 8-bit gray value.
+ * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4}
+ * or {@link #DITHER_MATRIX_8X8})
+ * @param gray255 the gray value (0-255)
+ * @param doubleMatrix true if the 4x4 matrix shall be doubled to a 8x8
+ * @return the dither pattern
+ */
+ public static byte[] getBayerDither(int matrix, int gray255, boolean doubleMatrix) {
+ int ditherIndex;
+ byte[] dither;
+ int[] bayer;
+ switch (matrix) {
+ case DITHER_MATRIX_4X4:
+ ditherIndex = gray255 * 17 / 255;
+ bayer = BAYER_D4;
+ break;
+ case DITHER_MATRIX_8X8:
+ ditherIndex = gray255 * 65 / 255;
+ bayer = BAYER_D8;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported dither matrix: " + matrix);
+ }
+ if (doubleMatrix) {
+ if (doubleMatrix && (matrix != DITHER_MATRIX_4X4)) {
+ throw new IllegalArgumentException("doubleMatrix=true is only allowed for 4x4");
+ }
+ dither = new byte[bayer.length / 8 * 4];
+ for (int i = 0, c = bayer.length; i < c; i++) {
+ boolean dot = !(bayer[i] < ditherIndex - 1);
+ if (dot) {
+ int byteIdx = i / 4;
+ dither[byteIdx] |= 1 << (i % 4);
+ dither[byteIdx] |= 1 << ((i % 4) + 4);
+ dither[byteIdx + 4] |= 1 << (i % 4);
+ dither[byteIdx + 4] |= 1 << ((i % 4) + 4);
+ }
+ }
+ } else {
+ dither = new byte[bayer.length / 8];
+ for (int i = 0, c = bayer.length; i < c; i++) {
+ boolean dot = !(bayer[i] < ditherIndex - 1);
+ if (dot) {
+ int byteIdx = i / 8;
+ dither[byteIdx] |= 1 << (i % 8);
+ }
+ }
+ }
+ return dither;
+ }
+
+ /**
+ * Returns a byte array containing the dither pattern for the given 8-bit gray value.
+ * @param matrix the matrix size ({@link #DITHER_MATRIX_2X2}, {@link #DITHER_MATRIX_4X4}
+ * or {@link #DITHER_MATRIX_8X8})
+ * @param col the color
+ * @param doubleMatrix true if the 4x4 matrix shall be doubled to a 8x8
+ * @return the dither pattern
+ */
+ public static byte[] getBayerDither(int matrix, Color col, boolean doubleMatrix) {
+ float black = BitmapImageUtil.convertToGray(col.getRGB()) / 256f;
+ return getBayerDither(matrix, Math.round(black * 256), doubleMatrix);
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/svg/EmbeddedSVGImageHandler.java b/src/sandbox/org/apache/fop/render/svg/EmbeddedSVGImageHandler.java
index b20982d54..fdcac6c35 100644
--- a/src/sandbox/org/apache/fop/render/svg/EmbeddedSVGImageHandler.java
+++ b/src/sandbox/org/apache/fop/render/svg/EmbeddedSVGImageHandler.java
@@ -90,12 +90,11 @@ public class EmbeddedSVGImageHandler implements ImageHandler, SVGConstants {
ImageXMLDOM svg = (ImageXMLDOM)image;
ContentHandler handler = svgContext.getContentHandler();
AttributesImpl atts = new AttributesImpl();
- atts.addAttribute("", "x", "x", CDATA, Integer.toString(pos.x));
- atts.addAttribute("", "y", "y", CDATA, Integer.toString(pos.y));
- atts.addAttribute("", "width", "width", CDATA, Integer.toString(pos.width));
- atts.addAttribute("", "height", "height", CDATA, Integer.toString(pos.height));
+ atts.addAttribute("", "x", "x", CDATA, SVGUtil.formatMptToPt(pos.x));
+ atts.addAttribute("", "y", "y", CDATA, SVGUtil.formatMptToPt(pos.y));
+ atts.addAttribute("", "width", "width", CDATA, SVGUtil.formatMptToPt(pos.width));
+ atts.addAttribute("", "height", "height", CDATA, SVGUtil.formatMptToPt(pos.height));
try {
- //handler.startElement(NAMESPACE, "svg", "svg", atts);
Document doc = (Document)svg.getDocument();
Element svgEl = (Element)doc.getDocumentElement();
@@ -127,10 +126,10 @@ public class EmbeddedSVGImageHandler implements ImageHandler, SVGConstants {
&& SVG_ELEMENT.getLocalName().equals(localName)) {
topLevelSVGFound = true;
AttributesImpl modAtts = new AttributesImpl(atts);
- setAttribute(modAtts, "x", Integer.toString(pos.x));
- setAttribute(modAtts, "y", Integer.toString(pos.y));
- setAttribute(modAtts, "width", Integer.toString(pos.width));
- setAttribute(modAtts, "height", Integer.toString(pos.height));
+ setAttribute(modAtts, "x", SVGUtil.formatMptToPt(pos.x));
+ setAttribute(modAtts, "y", SVGUtil.formatMptToPt(pos.y));
+ setAttribute(modAtts, "width", SVGUtil.formatMptToPt(pos.width));
+ setAttribute(modAtts, "height", SVGUtil.formatMptToPt(pos.height));
super.startElement(uri, localName, name, modAtts);
} else {
super.startElement(uri, localName, name, atts);
@@ -139,9 +138,6 @@ public class EmbeddedSVGImageHandler implements ImageHandler, SVGConstants {
});
transformer.transform(src, res);
- //handler.endElement(NAMESPACE, "svg", "svg");
- //} catch (SAXException e) {
- //throw new IOException(e.getMessage());
} catch (TransformerException te) {
throw new IOException(te.getMessage());
}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
index a8ce2dd2b..42ca17e08 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
@@ -22,9 +22,6 @@ package org.apache.fop.render.svg;
import java.awt.Dimension;
import java.io.IOException;
import java.io.OutputStream;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -66,6 +63,9 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
private StreamResult firstStream;
private StreamResult currentStream;
+ /** Used for single-page documents rendered to a DOM or SAX. */
+ private Result simpleResult;
+
private Document reusedParts;
/**
@@ -92,7 +92,7 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
getUserAgent().getOutputFile());
this.firstStream = (StreamResult)result;
} else {
- throw new UnsupportedOperationException("Result is not supported: " + result);
+ this.simpleResult = result;
}
}
@@ -152,46 +152,19 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
/** {@inheritDoc} */
public void startPage(int index, String name, String pageMasterName, Dimension size)
throws IFException {
- OutputStream out;
- try {
- if (index == 0) {
- out = null;
- } else {
- out = this.multiFileUtil.createOutputStream(index);
- if (out == null) {
- //TODO Convert to event
- throw new IFException(
- "No filename information available. Stopping after first page.", null);
- }
- }
- } catch (IOException ioe) {
- throw new IFException("I/O exception while setting up output file", ioe);
- }
- if (out == null) {
- this.handler = decorate(createContentHandler(this.firstStream));
+ if (this.multiFileUtil != null) {
+ prepareHandlerWithOutputStream(index);
} else {
- this.currentStream = new StreamResult(out);
- this.handler = decorate(createContentHandler(this.currentStream));
- }
- if (false) {
- final ContentHandler originalHandler = this.handler;
- this.handler = decorate((ContentHandler)Proxy.newProxyInstance(
- ContentHandler.class.getClassLoader(),
- new Class[] {ContentHandler.class},
- new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- String methodName = method.getName();
- System.out.println(methodName + ":");
- if (args != null) {
- for (int i = 0; i < args.length; i++) {
- System.out.println(" " + args[i]);
- }
- }
- return method.invoke(originalHandler, args);
- }
- }));
+ if (this.simpleResult == null) {
+ //Only one page is supported with this approach at the moment
+ throw new IFException(
+ "Only one page is supported for output with the given Result instance!",
+ null);
+ }
+ super.setResult(this.simpleResult);
+ this.simpleResult = null;
}
+
try {
handler.startDocument();
handler.startPrefixMapping("", NAMESPACE);
@@ -202,10 +175,11 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
XMLUtil.addAttribute(atts, "index", Integer.toString(index));
XMLUtil.addAttribute(atts, "name", name);
*/
- XMLUtil.addAttribute(atts, "width", Float.toString(size.width / 1000f) + "pt");
- XMLUtil.addAttribute(atts, "height", Float.toString(size.height / 1000f) + "pt");
+ XMLUtil.addAttribute(atts, "width", SVGUtil.formatMptToPt(size.width) + "pt");
+ XMLUtil.addAttribute(atts, "height", SVGUtil.formatMptToPt(size.height) + "pt");
XMLUtil.addAttribute(atts, "viewBox",
- "0 0 " + Integer.toString(size.width) + " " + Integer.toString(size.height));
+ "0 0 " + SVGUtil.formatMptToPt(size.width)
+ + " " + SVGUtil.formatMptToPt(size.height));
handler.startElement("svg", atts);
try {
@@ -227,6 +201,30 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
}
}
+ private void prepareHandlerWithOutputStream(int index) throws IFException {
+ OutputStream out;
+ try {
+ if (index == 0) {
+ out = null;
+ } else {
+ out = this.multiFileUtil.createOutputStream(index);
+ if (out == null) {
+ //TODO Convert to event
+ throw new IFException(
+ "No filename information available. Stopping after first page.", null);
+ }
+ }
+ } catch (IOException ioe) {
+ throw new IFException("I/O exception while setting up output file", ioe);
+ }
+ if (out == null) {
+ this.handler = decorate(createContentHandler(this.firstStream));
+ } else {
+ this.currentStream = new StreamResult(out);
+ this.handler = decorate(createContentHandler(this.currentStream));
+ }
+ }
+
private GenerationHelperContentHandler decorate(ContentHandler contentHandler) {
return new GenerationHelperContentHandler(contentHandler, getMainNamespace());
}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
index c31190eba..aa2e325d9 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
@@ -52,7 +52,6 @@ import org.apache.fop.render.intermediate.IFConstants;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState;
-import org.apache.fop.render.intermediate.IFUtil;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.ColorUtil;
@@ -99,13 +98,13 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
/** {@inheritDoc} */
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
throws IFException {
- startViewport(IFUtil.toString(transform), size, clipRect);
+ startViewport(SVGUtil.formatAffineTransformMptToPt(transform), size, clipRect);
}
/** {@inheritDoc} */
public void startViewport(AffineTransform[] transforms, Dimension size, Rectangle clipRect)
throws IFException {
- startViewport(IFUtil.toString(transforms), size, clipRect);
+ startViewport(SVGUtil.formatAffineTransformsMptToPt(transforms), size, clipRect);
}
private void startViewport(String transform, Dimension size, Rectangle clipRect)
@@ -119,8 +118,8 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
handler.startElement("g", atts);
atts.clear();
- XMLUtil.addAttribute(atts, "width", Integer.toString(size.width));
- XMLUtil.addAttribute(atts, "height", Integer.toString(size.height));
+ XMLUtil.addAttribute(atts, "width", SVGUtil.formatMptToPt(size.width));
+ XMLUtil.addAttribute(atts, "height", SVGUtil.formatMptToPt(size.height));
if (clipRect != null) {
int[] v = new int[] {
clipRect.y,
@@ -133,10 +132,10 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
}
if (sum != 0) {
StringBuffer sb = new StringBuffer("rect(");
- sb.append(v[0]).append(',');
- sb.append(v[1]).append(',');
- sb.append(v[2]).append(',');
- sb.append(v[3]).append(')');
+ sb.append(SVGUtil.formatMptToPt(v[0])).append(',');
+ sb.append(SVGUtil.formatMptToPt(v[1])).append(',');
+ sb.append(SVGUtil.formatMptToPt(v[2])).append(',');
+ sb.append(SVGUtil.formatMptToPt(v[3])).append(')');
XMLUtil.addAttribute(atts, "clip", sb.toString());
}
XMLUtil.addAttribute(atts, "overflow", "hidden");
@@ -162,12 +161,12 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
/** {@inheritDoc} */
public void startGroup(AffineTransform[] transforms) throws IFException {
- startGroup(IFUtil.toString(transforms));
+ startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms));
}
/** {@inheritDoc} */
public void startGroup(AffineTransform transform) throws IFException {
- startGroup(IFUtil.toString(transform));
+ startGroup(SVGUtil.formatAffineTransformMptToPt(transform));
}
private void startGroup(String transform) throws IFException {
@@ -216,10 +215,10 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
//TODO Some additional URI rewriting might be necessary
AttributesImpl atts = new AttributesImpl();
XMLUtil.addAttribute(atts, IFConstants.XLINK_HREF, uri);
- XMLUtil.addAttribute(atts, "x", Integer.toString(rect.x));
- XMLUtil.addAttribute(atts, "y", Integer.toString(rect.y));
- XMLUtil.addAttribute(atts, "width", Integer.toString(rect.width));
- XMLUtil.addAttribute(atts, "height", Integer.toString(rect.height));
+ XMLUtil.addAttribute(atts, "x", SVGUtil.formatMptToPt(rect.x));
+ XMLUtil.addAttribute(atts, "y", SVGUtil.formatMptToPt(rect.y));
+ XMLUtil.addAttribute(atts, "width", SVGUtil.formatMptToPt(rect.width));
+ XMLUtil.addAttribute(atts, "height", SVGUtil.formatMptToPt(rect.height));
handler.element("image", atts);
} else {
drawImageUsingImageHandler(info, rect);
@@ -282,10 +281,10 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
try {
establish(MODE_NORMAL);
AttributesImpl atts = new AttributesImpl();
- XMLUtil.addAttribute(atts, "x", Integer.toString(rect.x));
- XMLUtil.addAttribute(atts, "y", Integer.toString(rect.y));
- XMLUtil.addAttribute(atts, "width", Integer.toString(rect.width));
- XMLUtil.addAttribute(atts, "height", Integer.toString(rect.height));
+ XMLUtil.addAttribute(atts, "x", SVGUtil.formatMptToPt(rect.x));
+ XMLUtil.addAttribute(atts, "y", SVGUtil.formatMptToPt(rect.y));
+ XMLUtil.addAttribute(atts, "width", SVGUtil.formatMptToPt(rect.width));
+ XMLUtil.addAttribute(atts, "height", SVGUtil.formatMptToPt(rect.height));
if (fill != null) {
XMLUtil.addAttribute(atts, "fill", toString(fill));
}
@@ -311,10 +310,10 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
try {
establish(MODE_NORMAL);
AttributesImpl atts = new AttributesImpl();
- XMLUtil.addAttribute(atts, "x1", Integer.toString(start.x));
- XMLUtil.addAttribute(atts, "y1", Integer.toString(start.y));
- XMLUtil.addAttribute(atts, "x2", Integer.toString(end.x));
- XMLUtil.addAttribute(atts, "y2", Integer.toString(end.y));
+ XMLUtil.addAttribute(atts, "x1", SVGUtil.formatMptToPt(start.x));
+ XMLUtil.addAttribute(atts, "y1", SVGUtil.formatMptToPt(start.y));
+ XMLUtil.addAttribute(atts, "x2", SVGUtil.formatMptToPt(end.x));
+ XMLUtil.addAttribute(atts, "y2", SVGUtil.formatMptToPt(end.y));
XMLUtil.addAttribute(atts, "stroke-width", toString(color));
XMLUtil.addAttribute(atts, "fill", toString(color));
//TODO Handle style parameter
@@ -332,16 +331,16 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
establish(MODE_TEXT);
AttributesImpl atts = new AttributesImpl();
XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
- XMLUtil.addAttribute(atts, "x", Integer.toString(x));
- XMLUtil.addAttribute(atts, "y", Integer.toString(y));
+ XMLUtil.addAttribute(atts, "x", SVGUtil.formatMptToPt(x));
+ XMLUtil.addAttribute(atts, "y", SVGUtil.formatMptToPt(y));
if (letterSpacing != 0) {
- XMLUtil.addAttribute(atts, "letter-spacing", Integer.toString(letterSpacing));
+ XMLUtil.addAttribute(atts, "letter-spacing", SVGUtil.formatMptToPt(letterSpacing));
}
if (wordSpacing != 0) {
- XMLUtil.addAttribute(atts, "word-spacing", Integer.toString(wordSpacing));
+ XMLUtil.addAttribute(atts, "word-spacing", SVGUtil.formatMptToPt(wordSpacing));
}
if (dx != null) {
- XMLUtil.addAttribute(atts, "dx", IFUtil.toString(dx));
+ XMLUtil.addAttribute(atts, "dx", SVGUtil.formatMptArrayToPt(dx));
}
handler.startElement("text", atts);
char[] chars = text.toCharArray();
@@ -386,7 +385,7 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
XMLUtil.addAttribute(atts, "font-style", state.getFontStyle());
XMLUtil.addAttribute(atts, "font-weight", Integer.toString(state.getFontWeight()));
XMLUtil.addAttribute(atts, "font-variant", state.getFontVariant());
- XMLUtil.addAttribute(atts, "font-size", Integer.toString(state.getFontSize()));
+ XMLUtil.addAttribute(atts, "font-size", SVGUtil.formatMptToPt(state.getFontSize()));
XMLUtil.addAttribute(atts, "fill", toString(state.getTextColor()));
handler.startElement("g", atts);
state.resetFontChanged();
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGUtil.java b/src/sandbox/org/apache/fop/render/svg/SVGUtil.java
new file mode 100644
index 000000000..2953b0d5a
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/SVGUtil.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.svg;
+
+import java.awt.geom.AffineTransform;
+
+import org.apache.fop.render.intermediate.IFUtil;
+
+/**
+ * This class provides utility methods for generating SVG.
+ */
+public class SVGUtil {
+
+ /**
+ * Formats a length in millipoints as a point value.
+ * @param mpt the length in millipoints
+ * @return the formatted value in points
+ */
+ public static String formatMptToPt(int mpt) {
+ return Float.toString(mpt / 1000f);
+ }
+
+ /**
+ * Formats an array of lengths in millipoints as point values.
+ * @param lengths the lengths in millipoints
+ * @return the formatted array in points
+ */
+ public static String formatMptArrayToPt(int[] lengths) {
+ return IFUtil.toString(lengths);
+ }
+
+ /**
+ * Formats a transformation matrix in millipoints with values as points.
+ * @param transform the transformation matrix in millipoints
+ * @return the formatted matrix in points
+ */
+ public static String formatAffineTransformMptToPt(AffineTransform transform) {
+ AffineTransform scaled = new AffineTransform(transform);
+ scaled.setToTranslation(
+ transform.getTranslateX() / 1000,
+ transform.getTranslateY() / 1000);
+ return IFUtil.toString(scaled);
+ }
+
+ /**
+ * Formats an array of transformation matrices in millipoints with values as points.
+ * @param transforms the transformation matrices in millipoints
+ * @return the formatted matrices in points
+ */
+ public static String formatAffineTransformsMptToPt(AffineTransform[] transforms) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, c = transforms.length; i < c; i++) {
+ if (i > 0) {
+ sb.append(' ');
+ }
+ sb.append(formatAffineTransformMptToPt(transforms[i]));
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/status.xml b/status.xml
index b6a2d23c0..d26ec015e 100644
--- a/status.xml
+++ b/status.xml
@@ -61,9 +61,102 @@
<action context="Renderers" dev="JM" type="add" fixes-bug="46705" due-to="Jost Klopfstein">
Added basic accessibility and Tagged PDF support.
</action>
+ <action context="Extensions" dev="JM" type="add">
+ Added support for the #CMYK pseudo-profile supported by some commercial XSL implementations
+ on the rgb-icc() function.
+ </action>
+ <action context="Layout" dev="VH" type="add" importance="high">
+ Added limited support for pages of different inline-progression-dimensions within a
+ page-sequence.
+ </action>
+ <action context="Renderers" dev="AD" type="add" fixes-bug="46905">
+ Added basic implementation for column-keeps.
+ </action>
+ <action context="Renderers" dev="AD" type="fix" fixes-bug="46883">
+ Hotspot in AbstractGraphicsDrawingOrderContainer. Reduced time spent in the method
+ by introducing a member variable to hold the data-length.
+ </action>
+ <action context="Code" dev="AD" type="fix" fixes-bug="47710">
+ White-space handling in markers with inline-content throws a NullPointerException
+ in some cases.
+ </action>
+ <action context="Renderers" dev="CB" type="fix" fixes-bug="47694">
+ Dithered Background Shading can produce illegal AFP if objects are very small
+ </action>
+ <action context="Renderers" dev="CB" type="add">
+ AFP Output: Added support for IMM Extension on fo:simple-page-master.
+ </action>
+ <action context="Renderers" dev="JM" type="add" fixes-bug="47311" due-to="Peter Coppens">
+ Added an initial set of extensions for prepress support (fox:bleed, fox:crop-offset,
+ fox:crop-box and fox:scale). This is currently supported only by PDF and Java2D renderers.
+ </action>
+ <action context="Renderers" dev="JM" type="add">
+ PCL Output: Added support for specifying the output bin.
+ </action>
+ <action context="Renderers" dev="JM" type="add">
+ AFP Output: Added support for embedding external AFP form maps (form defs) using the
+ afp:include-form-map extension.
+ </action>
+ <action context="Renderers" dev="JM" type="add">
+ AFP Output: Added support for AFP font embedding. Note: this changes the default behaviour.
+ Like with PDF and PS, all fonts are embedded by default unless matched in the
+ "referenced-fonts" section in the configuration.
+ </action>
+ <action context="Renderers" dev="AD" type="fix" fixes-bug="47508" due-to="Bharat Attaluri">
+ Bugfix: Error while writing TLE's attribute qualifier in the output.
+ </action>
+ <action context="Renderers" dev="CB" type="fix">
+ Bugfix: support justified text in AFP Renderer (already working in AFP Painter)
+ </action>
+ <action context="Renderers" dev="AD" type="add">
+ AFP Renderer Raster Fonts:
+ <ul>
+ <li>added support for fractional font-sizes in the configuration.</li>
+ <li>selection of the smaller font-size, in case two possible fallbacks have the
+ same difference to a requested size.</li>
+ </ul>
+ </action>
+ <action context="Fonts" dev="CB" type="fix">
+ Bugfix: support PFM Files with no extent table.
+ </action>
+ <action context="Code" dev="AD" type="fix" fixes-bug="46960">
+ Bugfix: previously retrieved markers were not cleared if the new marker was empty.
+ </action>
+ <action context="Fonts" dev="VH" type="fix" fixes-bug="47232" due-to="Maxim Wirt">
+ Bugfix: for the last character of a Type1 font, always a width of 0 was returned.
+ </action>
+ <action context="Code" dev="VH" type="fix">
+ Changed meaning of ‘-v’ option to ‘verbose’, which will print FOP’s version and proceed.
+ Added a ‘-version’ option to simply print the version then exit, following Java practices.
+ </action>
+ <action context="Layout" dev="VH" type="fix" fixes-bug="47101">
+ Bugfix: The cells of a table inside a marker were duplicated at every marker retrieval.
+ </action>
+ <action context="Images" dev="JM" type="fix">
+ Bugfix: use the effective color profile supplied by the ImageEncodingHelper, instead of the
+ original one.
+ </action>
+ <action context="Renderers" dev="JM" type="fix">
+ Bugfix: reset graphic state when a page is finished in PostScript.
+ </action>
+ <action context="Renderers" dev="JM" type="add">
+ Added setting to enable dithered painting of filled rectangles in AFP and PCL.
+ </action>
+ <action context="Layout" dev="VH" type="fix">
+ Bugfix: footnotes occurring within the forced height of a table row did not appear on the
+ output
+ </action>
+ <action context="Renderers" dev="JM" type="update" fixes-bug="47031" due-to="Francois Fernandes">
+ PDFGraphics2D.writeClip doesn't generate a clip command anymore when the clip path
+ is empty.
+ </action>
+ <action context="Renderers" dev="JM" type="add" fixes-bug="47000">
+ Added a custom text painter for rendering SVG text using text operators when rendering
+ to PostScript or EPS. Text is no longer painted as shapes, thus creating much smaller files.
+ </action>
<action context="Renderers" dev="JM" type="fix">
Fixed a bug that left the PrintRenderer unconfigured even if a configuration was
- specified for "application/X-fop-print".
+ specified for "application/X-fop-print".
</action>
<action context="Renderers" dev="JM" type="fix" fixes-bug="46882" due-to="Yegor Kozlov">
Fixed the handling of CMYK colors in PDFGraphics2D.
@@ -360,7 +453,7 @@
<p>
Besides the important changes listed below, the most important areas with
improvements in this release are:
- </p>
+ </p>
<ul>
<li>
Many bugfixes in tables, plus some new features (full support for keeps and
diff --git a/test/fotree/testcases/pixel_length.fo b/test/fotree/testcases/pixel_length.fo
new file mode 100644
index 000000000..2ad1d4a19
--- /dev/null
+++ b/test/fotree/testcases/pixel_length.fo
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:test="http://xmlgraphics.apache.org/fop/test">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container inline-progression-dimension="144px">
+ <test:assert property="inline-progression-dimension.optimum" expected="144000mpt" />
+ <fo:block>Block 1</fo:block>
+ </fo:block-container>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/test/fotree/testcases/pixel_length_96dpi.fo b/test/fotree/testcases/pixel_length_96dpi.fo
new file mode 100644
index 000000000..00c35eddb
--- /dev/null
+++ b/test/fotree/testcases/pixel_length_96dpi.fo
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<?fop-source-resolution 144?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:test="http://xmlgraphics.apache.org/fop/test">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container inline-progression-dimension="144px">
+ <test:assert property="inline-progression-dimension.optimum" expected="72000mpt" />
+ <fo:block>Block 1</fo:block>
+ </fo:block-container>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java
index e6a718643..b8bb541cd 100644
--- a/test/java/org/apache/fop/StandardTestSuite.java
+++ b/test/java/org/apache/fop/StandardTestSuite.java
@@ -26,6 +26,8 @@ import org.apache.fop.fonts.TrueTypeAnsiTestCase;
import org.apache.fop.image.loader.batik.ImageLoaderTestCase;
import org.apache.fop.image.loader.batik.ImagePreloaderTestCase;
import org.apache.fop.intermediate.IFMimickingTestCase;
+import org.apache.fop.render.extensions.prepress.PageBoundariesTest;
+import org.apache.fop.render.extensions.prepress.PageScaleTest;
import org.apache.fop.render.pdf.PDFAConformanceTestCase;
import org.apache.fop.render.pdf.PDFCMapTestCase;
import org.apache.fop.render.pdf.PDFEncodingTestCase;
@@ -56,6 +58,8 @@ public class StandardTestSuite {
suite.addTest(new TestSuite(ImageLoaderTestCase.class));
suite.addTest(new TestSuite(ImagePreloaderTestCase.class));
suite.addTest(new TestSuite(IFMimickingTestCase.class));
+ suite.addTest(new TestSuite(PageBoundariesTest.class));
+ suite.addTest(new TestSuite(PageScaleTest.class));
//$JUnit-END$
return suite;
}
diff --git a/test/java/org/apache/fop/config/FOURIResolverTestCase.java b/test/java/org/apache/fop/config/FOURIResolverTestCase.java
index e6f8db712..e0f6d7f81 100644
--- a/test/java/org/apache/fop/config/FOURIResolverTestCase.java
+++ b/test/java/org/apache/fop/config/FOURIResolverTestCase.java
@@ -39,6 +39,7 @@ public class FOURIResolverTestCase extends TestCase {
System.out.println(resolver.checkBaseURL("./test/config"));
System.out.println(resolver.checkBaseURL("file:test/config"));
System.out.println(resolver.checkBaseURL("fantasy:myconfig"));
+ System.out.println(resolver.checkBaseURL("file:test\\config\\"));
try {
resolver.checkBaseURL("./doesnotexist");
fail("Expected an exception for a inexistent base directory");
diff --git a/test/java/org/apache/fop/config/UserConfigTestSuite.java b/test/java/org/apache/fop/config/UserConfigTestSuite.java
index f933a0342..168d87d51 100644
--- a/test/java/org/apache/fop/config/UserConfigTestSuite.java
+++ b/test/java/org/apache/fop/config/UserConfigTestSuite.java
@@ -45,6 +45,7 @@ public class UserConfigTestSuite {
suite.addTest(new TestSuite(FontsDirectoryRecursiveTestCase.class));
suite.addTest(new TestSuite(FontsAutoDetectTestCase.class));
suite.addTest(new TestSuite(FontsSubstitutionTestCase.class));
+ suite.addTest(new TestSuite(FOURIResolverTestCase.class));
//$JUnit-END$
return suite;
}
diff --git a/test/java/org/apache/fop/fotreetest/FOTreeTester.java b/test/java/org/apache/fop/fotreetest/FOTreeTester.java
index fe44e3e23..ba9fde4f9 100644
--- a/test/java/org/apache/fop/fotreetest/FOTreeTester.java
+++ b/test/java/org/apache/fop/fotreetest/FOTreeTester.java
@@ -32,6 +32,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.FopFactoryConfigurator;
import org.apache.fop.fotreetest.ext.TestElementMapping;
import org.apache.fop.util.ConsoleEventListenerForTests;
@@ -64,8 +65,13 @@ public class FOTreeTester {
SAXParser parser = spf.newSAXParser();
XMLReader reader = parser.getXMLReader();
+ //Resetting values modified by processing instructions
+ fopFactory.setBreakIndentInheritanceOnReferenceAreaBoundary(
+ FopFactoryConfigurator.DEFAULT_BREAK_INDENT_INHERITANCE);
+ fopFactory.setSourceResolution(FopFactoryConfigurator.DEFAULT_SOURCE_RESOLUTION);
+
FOUserAgent ua = fopFactory.newFOUserAgent();
- ua.setBaseURL(testFile.getParentFile().toURL().toString());
+ ua.setBaseURL(testFile.getParentFile().toURI().toURL().toString());
ua.setFOEventHandlerOverride(new DummyFOEventHandler(ua));
ua.getEventBroadcaster().addEventListener(
new ConsoleEventListenerForTests(testFile.getName()));
@@ -79,14 +85,19 @@ public class FOTreeTester {
reader.setDTDHandler(fop.getDefaultHandler());
reader.setErrorHandler(fop.getDefaultHandler());
reader.setEntityResolver(fop.getDefaultHandler());
- reader.parse(testFile.toURL().toExternalForm());
+ try {
+ reader.parse(testFile.toURI().toURL().toExternalForm());
+ } catch (Exception e) {
+ collector.notifyError(e.getLocalizedMessage());
+ throw e;
+ }
List results = collector.getResults();
if (results.size() > 0) {
for (int i = 0; i < results.size(); i++) {
- System.out.println(((Exception)results.get(i)).getMessage());
+ System.out.println((String)results.get(i));
}
- throw (Exception)results.get(0);
+ throw new IllegalStateException((String)results.get(0));
}
}
@@ -104,6 +115,8 @@ public class FOTreeTester {
if ("fop-useragent-break-indent-inheritance".equals(target)) {
userAgent.getFactory().setBreakIndentInheritanceOnReferenceAreaBoundary(
Boolean.valueOf(data).booleanValue());
+ } else if ("fop-source-resolution".equals(target)) {
+ userAgent.getFactory().setSourceResolution(Float.parseFloat(data));
}
super.processingInstruction(target, data);
}
diff --git a/test/java/org/apache/fop/fotreetest/ResultCollector.java b/test/java/org/apache/fop/fotreetest/ResultCollector.java
index 36d111c7f..3f6502803 100644
--- a/test/java/org/apache/fop/fotreetest/ResultCollector.java
+++ b/test/java/org/apache/fop/fotreetest/ResultCollector.java
@@ -23,7 +23,7 @@ import java.util.Collections;
import java.util.List;
/**
- * This class collects the results from assertions injected into the FO stream.
+ * This class collects failures for assertions injected into the FO stream.
*/
public class ResultCollector {
@@ -31,7 +31,7 @@ public class ResultCollector {
private List results = new java.util.ArrayList();
- /** @return the ResultColletor singleton */
+ /** @return the ResultCollector singleton */
public static ResultCollector getInstance() {
if (instance == null) {
instance = new ResultCollector();
@@ -45,12 +45,23 @@ public class ResultCollector {
}
/**
- * This notifies the ResultCollector about an Exception.
- * @param e the exception
+ * This notifies the ResultCollector about an assertion failure.
+ *
+ * @param message the message containing the details
*/
- public void notifyException(Exception e) {
- System.out.println(e.getMessage());
- results.add(e);
+ public void notifyAssertionFailure(String message) {
+ System.out.println(message);
+ results.add(message);
+ }
+
+ /**
+ * This notifies the ResultCollector about a testcase that ended
+ * with a fatal error
+ *
+ * @param message the message containing the details
+ */
+ public void notifyError(String message) {
+ results.add(message);
}
/** Resets the result list. */
diff --git a/test/java/org/apache/fop/fotreetest/ext/AssertElement.java b/test/java/org/apache/fop/fotreetest/ext/AssertElement.java
index 97b73947c..f4a76d7ed 100644
--- a/test/java/org/apache/fop/fotreetest/ext/AssertElement.java
+++ b/test/java/org/apache/fop/fotreetest/ext/AssertElement.java
@@ -41,7 +41,10 @@ import org.xml.sax.Locator;
public class AssertElement extends TestObj {
/**
- * @see org.apache.fop.fo.FONode#FONode(FONode)
+ * Creates a new AssertElement instance that is a child
+ * of the given {@link FONode}
+ *
+ * @param parent the parent {@link FONode}
*/
public AssertElement(FONode parent) {
super(parent);
@@ -58,6 +61,7 @@ public class AssertElement extends TestObj {
ResultCollector collector = ResultCollector.getInstance();
String propName = attlist.getValue("property");
+ String expected = attlist.getValue("expected");
String component = null;
int dotIndex = propName.indexOf('.');
if (dotIndex >= 0) {
@@ -66,8 +70,7 @@ public class AssertElement extends TestObj {
}
int propID = FOPropertyMapping.getPropertyId(propName);
if (propID < 0) {
- collector.notifyException(new IllegalArgumentException(
- "Property not found: " + propName));
+ collector.notifyAssertionFailure("Property not found: " + propName);
} else {
Property prop;
prop = propertyList.getParentPropertyList().get(propID);
@@ -94,21 +97,20 @@ public class AssertElement extends TestObj {
}
String s;
if (prop instanceof PercentLength) {
- s = ((PercentLength)prop).getString();
+ s = prop.getString();
} else {
s = String.valueOf(prop);
}
- String expected = attlist.getValue("expected");
if (!expected.equals(s)) {
- collector.notifyException(
- new IllegalStateException(locator.getSystemId()
+ collector.notifyAssertionFailure(
+ locator.getSystemId()
+ "\nProperty '" + propName
+ "' expected to evaluate to '" + expected
+ "' but got '" + s
+ "'\n(test:assert in "
+ propertyList.getParentFObj().getName()
+ " at line #" + locator.getLineNumber()
- + ", column #" + locator.getColumnNumber() + ")\n"));
+ + ", column #" + locator.getColumnNumber() + ")\n");
}
}
@@ -120,4 +122,3 @@ public class AssertElement extends TestObj {
}
}
-
diff --git a/test/java/org/apache/fop/render/extensions/prepress/PageBoundariesTest.java b/test/java/org/apache/fop/render/extensions/prepress/PageBoundariesTest.java
new file mode 100644
index 000000000..5cd23c17e
--- /dev/null
+++ b/test/java/org/apache/fop/render/extensions/prepress/PageBoundariesTest.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the fox:bleed, fox:crop-offset, fox:crop-box extension properties.
+ */
+public class PageBoundariesTest extends TestCase {
+
+ private static final Dimension TEST_AREA_SIZE = new Dimension(20000, 15000);
+
+ private static final Rectangle TEST_AREA = new Rectangle(TEST_AREA_SIZE);
+
+ private static final String BLEED = "5pt";
+
+ private static final String CROP_OFFSET = "8pt";
+
+ /**
+ * Default constructor.
+ */
+ public PageBoundariesTest() {
+ }
+
+ /**
+ * Creates a test case with the given name.
+ *
+ * @param name name for the test case
+ */
+ public PageBoundariesTest(String name) {
+ super(name);
+ }
+
+ /** Test for page boundaries. */
+ public void testBoundaries1() {
+ PageBoundaries boundaries = new PageBoundaries(TEST_AREA_SIZE, BLEED, CROP_OFFSET, null);
+ assertEquals(TEST_AREA, boundaries.getTrimBox());
+
+ Rectangle bleedBox = boundaries.getBleedBox();
+ assertNotNull("Expected not null object", bleedBox);
+ assertEquals(-5000, bleedBox.x);
+ assertEquals(-5000, bleedBox.y);
+ assertEquals(30000, bleedBox.width);
+ assertEquals(25000, bleedBox.height);
+
+ Rectangle mediaBox = boundaries.getMediaBox();
+ assertNotNull("Expected not null object", mediaBox);
+ assertEquals(-8000, mediaBox.x);
+ assertEquals(-8000, mediaBox.y);
+ assertEquals(36000, mediaBox.width);
+ assertEquals(31000, mediaBox.height);
+ }
+
+ /** Test for page boundaries. */
+ public void testBoundaries2() {
+ PageBoundaries boundaries = new PageBoundaries(
+ TEST_AREA_SIZE, BLEED, null, null);
+ Rectangle bleedBox = boundaries.getBleedBox();
+ assertNotNull("Expected not null object", bleedBox);
+ assertEquals(-5000, bleedBox.x);
+ assertEquals(-5000, bleedBox.y);
+ assertEquals(30000, bleedBox.width);
+ assertEquals(25000, bleedBox.height);
+ assertEquals(bleedBox, boundaries.getMediaBox());
+ }
+
+ /** Two values for the properties. */
+ public void testBoundaries2Values() {
+ PageBoundaries boundaries = new PageBoundaries(
+ TEST_AREA_SIZE, "5pt 10pt", "6pt \t 12pt", null);
+ Rectangle bleedBox = boundaries.getBleedBox();
+ assertEquals(-10000, bleedBox.x);
+ assertEquals(-5000, bleedBox.y);
+ assertEquals(40000, bleedBox.width);
+ assertEquals(25000, bleedBox.height);
+
+ Rectangle mediaBox = boundaries.getMediaBox();
+ assertEquals(-12000, mediaBox.x);
+ assertEquals(-6000, mediaBox.y);
+ assertEquals(44000, mediaBox.width);
+ assertEquals(27000, mediaBox.height);
+ }
+
+ /** Three values for the properties. */
+ public void testBoundaries3Values() {
+ PageBoundaries boundaries = new PageBoundaries(
+ TEST_AREA_SIZE, "5pt 10pt 7pt", "6pt \t 12pt 14pt", null);
+ Rectangle bleedBox = boundaries.getBleedBox();
+ assertEquals(-10000, bleedBox.x);
+ assertEquals(-5000, bleedBox.y);
+ assertEquals(40000, bleedBox.width);
+ assertEquals(27000, bleedBox.height);
+
+ Rectangle mediaBox = boundaries.getMediaBox();
+ assertEquals(-12000, mediaBox.x);
+ assertEquals(-6000, mediaBox.y);
+ assertEquals(44000, mediaBox.width);
+ assertEquals(35000, mediaBox.height);
+ }
+
+ /** Four values for the properties. */
+ public void testBoundaries4Values() {
+ PageBoundaries boundaries = new PageBoundaries(
+ TEST_AREA_SIZE, "5pt 6pt 7pt 8pt", "9pt 10pt 11pt 12pt", null);
+ Rectangle bleedBox = boundaries.getBleedBox();
+ assertEquals(-8000, bleedBox.x);
+ assertEquals(-5000, bleedBox.y);
+ assertEquals(34000, bleedBox.width);
+ assertEquals(27000, bleedBox.height);
+
+ Rectangle mediaBox = boundaries.getMediaBox();
+ assertEquals(-12000, mediaBox.x);
+ assertEquals(-9000, mediaBox.y);
+ assertEquals(42000, mediaBox.width);
+ assertEquals(35000, mediaBox.height);
+ }
+
+ /** Test for the different values of crop-box. */
+ public void testCropBox() {
+ PageBoundaries boundaries = new PageBoundaries(TEST_AREA_SIZE, BLEED, CROP_OFFSET, null);
+ assertEquals(boundaries.getMediaBox(), boundaries.getCropBox());
+
+ boundaries = new PageBoundaries(TEST_AREA_SIZE, BLEED, CROP_OFFSET, "");
+ assertEquals(boundaries.getMediaBox(), boundaries.getCropBox());
+
+ boundaries = new PageBoundaries(TEST_AREA_SIZE, BLEED, CROP_OFFSET, "trim-box");
+ assertEquals(TEST_AREA, boundaries.getCropBox());
+
+ boundaries = new PageBoundaries(TEST_AREA_SIZE, BLEED, CROP_OFFSET, "bleed-box");
+ assertEquals(boundaries.getBleedBox(), boundaries.getCropBox());
+
+ boundaries = new PageBoundaries(TEST_AREA_SIZE, BLEED, CROP_OFFSET, "media-box");
+ assertEquals(boundaries.getMediaBox(), boundaries.getCropBox());
+ }
+
+ /** Test for default values returned when properties are null. */
+ public void testBoundariesNull() {
+ PageBoundaries b = new PageBoundaries(TEST_AREA_SIZE, null, null, null);
+
+ assertEquals("Result should be the same as TEST_AREA object", b.getTrimBox(), TEST_AREA);
+ assertEquals("Result should be the same as TEST_AREA object", b.getBleedBox(), TEST_AREA);
+ assertEquals("Result should be the same as TEST_AREA object", b.getMediaBox(), TEST_AREA);
+ assertEquals("Result should be the same as TEST_AREA object", b.getCropBox(), TEST_AREA);
+ }
+
+ /** Units must be specified. */
+ public void testBoundariesFail() {
+ try {
+ new PageBoundaries(TEST_AREA_SIZE, "0", null, null);
+ fail("Expected IllegalArgumentException. Box should have units");
+ } catch (IllegalArgumentException iae) {
+ // Good!
+ }
+ }
+}
diff --git a/test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java b/test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java
new file mode 100644
index 000000000..cbf6f5226
--- /dev/null
+++ b/test/java/org/apache/fop/render/extensions/prepress/PageScaleTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.extensions.prepress;
+
+import java.awt.geom.Point2D;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the fox:scale extension property.
+ */
+public class PageScaleTest extends TestCase {
+
+ /**
+ * Default constructor.
+ */
+ public PageScaleTest() {
+ super();
+ }
+
+ /**
+ * Creates a test case with the given name.
+ *
+ * @param name name for the test case
+ */
+ public PageScaleTest(String name) {
+ super(name);
+ }
+
+ /** 1 value is used for both x and y. */
+ public void testScale1() {
+ Point2D res = PageScale.getScale(".5");
+ assertEquals(0.5, res.getX(), 0.0);
+ assertEquals(0.5, res.getY(), 0.0);
+ }
+
+ /** Two values, used resp. for x and y. */
+ public void testScale2() {
+ Point2D res = PageScale.getScale("1. \t \n 1.2");
+ assertEquals(1.0, res.getX(), 0.0);
+ assertEquals(1.2, res.getY(), 0.0);
+ }
+
+ /** Scale must not contain units. */
+ public void testScaleFail() {
+ try {
+ PageScale.getScale("0.5mm 0.5cm");
+ fail("Expected IllegalArgumentException. Scale shouldn't contain units");
+ } catch (IllegalArgumentException iae) {
+ // Good!
+ }
+ }
+
+ /** @{code null} is returned when scale is unspecified. */
+ public void testScaleNull() {
+ Point2D res = PageScale.getScale(null);
+ assertNull("Result should be null", res);
+ res = PageScale.getScale("");
+ assertNull("Result should be null", res);
+ }
+
+}
diff --git a/test/java/org/apache/fop/util/ColorUtilTestCase.java b/test/java/org/apache/fop/util/ColorUtilTestCase.java
index bdc57f86a..9b905dd42 100644
--- a/test/java/org/apache/fop/util/ColorUtilTestCase.java
+++ b/test/java/org/apache/fop/util/ColorUtilTestCase.java
@@ -154,7 +154,8 @@ public class ColorUtilTestCase extends TestCase {
assertEquals(0f, comps[1], 0);
assertEquals(1f, comps[2], 0);
assertEquals(0f, comps[3], 0);
- assertEquals("cmyk(0.0,0.0,1.0,0.0)", ColorUtil.colorToString(colActual));
+ assertEquals("fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)",
+ ColorUtil.colorToString(colActual));
colSpec = "cmyk(0.0274, 0.2196, 0.3216, 0.0)";
colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec);
@@ -167,7 +168,23 @@ public class ColorUtilTestCase extends TestCase {
assertEquals(0.2196f, comps[1], 0.001);
assertEquals(0.3216f, comps[2], 0.001);
assertEquals(0f, comps[3], 0);
- assertEquals("cmyk(0.0274,0.2196,0.3216,0.0)", ColorUtil.colorToString(colActual));
+ assertEquals("fop-rgb-icc(0.9726,0.7804,0.67840004,#CMYK,,0.0274,0.2196,0.3216,0.0)",
+ ColorUtil.colorToString(colActual));
+
+ colSpec = "fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)";
+ colActual = (ColorExt)ColorUtil.parseColorString(null, colSpec);
+ assertEquals(255, colActual.getRed());
+ assertEquals(255, colActual.getGreen());
+ assertEquals(0, colActual.getBlue());
+ assertEquals(CMYKColorSpace.getInstance(), colActual.getColorSpace());
+ comps = colActual.getOriginalColorComponents();
+ assertEquals(4, comps.length);
+ assertEquals(0f, comps[0], 0);
+ assertEquals(0f, comps[1], 0);
+ assertEquals(1f, comps[2], 0);
+ assertEquals(0f, comps[3], 0);
+ assertEquals("fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)",
+ ColorUtil.colorToString(colActual));
}
}
diff --git a/test/layoutengine/disabled-testcases.xml b/test/layoutengine/disabled-testcases.xml
index ba985bf8d..1fb4f2926 100644
--- a/test/layoutengine/disabled-testcases.xml
+++ b/test/layoutengine/disabled-testcases.xml
@@ -216,4 +216,10 @@
<description>A soft hyphen should be a preferred as break compared to a
normal hyphenation point but is not.</description>
</testcase>
+ <testcase>
+ <name>Page-keep not respected in multi-column layout</name>
+ <file>keep_within-page_multi-column_overflow.xml</file>
+ <description>The block should cause overflow in the
+ last column on the page, rather than be broken.</description>
+ </testcase>
</disabled-testcases>
diff --git a/test/layoutengine/standard-testcases/afp-extension_1.xml b/test/layoutengine/standard-testcases/afp-extension_1.xml
index 70bbef450..41659ab6d 100644
--- a/test/layoutengine/standard-testcases/afp-extension_1.xml
+++ b/test/layoutengine/standard-testcases/afp-extension_1.xml
@@ -36,8 +36,13 @@
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
+
+ <fo:declarations>
+ <afp:include-form-map name="FORMMAP1" src="file:../../resources/afp/F1SAMPLE.afp"/>
+ </fo:declarations>
+
<fo:page-sequence master-reference="normal" fox:test-ignore="this">
- <afp:invoke-medium-map name="MYMAP"/>
+ <afp:invoke-medium-map name="NOPAR"/>
<afp:tag-logical-element name="foo" value="bar"/>
<fo:flow flow-name="xsl-region-body">
<fo:block>Text on page <fo:page-number/>.</fo:block>
@@ -47,6 +52,9 @@
</fo:root>
</fo>
<checks xmlns:afp="apache:fop:extensions:afp">
+ <eval expected="FORMMAP1" xpath="/areaTree/extension-attachments/afp:include-form-map/@name"/>
+ <eval expected="file:../../resources/afp/F1SAMPLE.afp" xpath="/areaTree/extension-attachments/afp:include-form-map/@src"/>
+
<eval expected="4" xpath="count(/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*)"/>
<eval expected="O1SAMP1 " xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*[1]/@name"/>
<eval expected="S1ISLOGO" xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/child::*[2]/@name"/>
@@ -57,7 +65,7 @@
<eval expected="4" xpath="count(/areaTree/pageSequence/pageViewport[@nr=2]/page/extension-attachments/child::*)"/>
<eval expected="2" xpath="count(/areaTree/pageSequence/extension-attachments/child::*)"/>
- <eval expected="MYMAP" xpath="/areaTree/pageSequence/extension-attachments/child::*[1]/@name"/>
+ <eval expected="NOPAR" xpath="/areaTree/pageSequence/extension-attachments/child::*[1]/@name"/>
<eval expected="bar" xpath="/areaTree/pageSequence/extension-attachments/afp:tag-logical-element[@name = 'foo']/@value"/>
<!-- This just tests if extension attributes make it through to the PageSequence object. -->
@@ -65,6 +73,9 @@
</checks>
<if-checks xmlns:if="http://xmlgraphics.apache.org/fop/intermediate"
xmlns:afp="apache:fop:extensions:afp">
+ <eval expected="FORMMAP1" xpath="/if:document/if:header/afp:include-form-map/@name"/>
+ <eval expected="file:../../resources/afp/F1SAMPLE.afp" xpath="/if:document/if:header/afp:include-form-map/@src"/>
+
<eval expected="4" xpath="count(//if:page[@name = '1']/if:page-header/child::*)"/>
<eval expected="O1SAMP1 " xpath="//if:page[@name = '1']/if:page-header/afp:include-page-overlay[1]/@name"/>
<eval expected="S1ISLOGO" xpath="//if:page[@name = '1']/if:page-header/afp:include-page-segment[1]/@name"/>
@@ -74,7 +85,7 @@
<eval expected="4" xpath="count(//if:page[@name = '2']/if:page-header/child::*)"/>
- <eval expected="MYMAP" xpath="//if:page-sequence/afp:invoke-medium-map/@name"/>
+ <eval expected="NOPAR" xpath="//if:page-sequence/afp:invoke-medium-map/@name"/>
<eval expected="bar" xpath="//if:page-sequence/afp:tag-logical-element[@name = 'foo']/@value"/>
<!-- This just tests if extension attributes make it through to the PageSequence object. -->
diff --git a/test/layoutengine/standard-testcases/color_1.xml b/test/layoutengine/standard-testcases/color_1.xml
index 0eb08f8b1..27086a41d 100644
--- a/test/layoutengine/standard-testcases/color_1.xml
+++ b/test/layoutengine/standard-testcases/color_1.xml
@@ -41,9 +41,7 @@
<fo:block color="rgb-icc(100%,50%,0%, sRGB, 1, 0.5, 0)">color "rgb-icc(100%,50%,0%, sRGB, 1, 0.5, 0)"</fo:block>
<fo:block color="rgb-icc(0%,100%,0%, unknown, 1, 0.5, 0)">color "rgb-icc(0%,100%,0%, unknown, 1, 0.5, 0)"</fo:block>
<fo:block color="cmyk(0%,0%,20%,40%)">color "cmyk(0%,0%,20%,40%)" (Khaki)</fo:block>
- <!-- NYI
<fo:block color="rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)">color "rgb-icc(153, 153, 102, #CMYK, 0, 0, 0.2, 0.4)" (Khaki)</fo:block>
- -->
</fo:flow>
</fo:page-sequence>
</fo:root>
@@ -58,6 +56,7 @@
<eval expected="#ff8000" xpath="//block[3]//text/@color"/>
<eval expected="fop-rgb-icc(1.0,0.5,0.0,sRGB,&quot;../../../src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm&quot;,1.0,0.5,0.0)" xpath="//block[4]//text/@color"/>
<eval expected="#00ff00" xpath="//block[5]//text/@color"/>
- <eval expected="cmyk(0.0,0.0,0.2,0.4)" xpath="//block[6]//text/@color"/>
+ <eval expected="fop-rgb-icc(0.6,0.6,0.48000002,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[6]//text/@color"/>
+ <eval expected="fop-rgb-icc(0.6,0.6,0.48000002,#CMYK,,0.0,0.0,0.2,0.4)" xpath="//block[7]//text/@color"/>
</checks>
</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml
new file mode 100644
index 000000000..d4db6e696
--- /dev/null
+++ b/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that blocks of texts are re-laid out after a change of the flow ipd.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="narrow"
+ page-height="300pt" page-width="400pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="wide"
+ page-height="300pt" page-width="600pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="pages">
+ <fo:single-page-master-reference master-reference="narrow"/>
+ <fo:repeatable-page-master-reference master-reference="wide"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body" language="en" hyphenate="true">
+ <fo:block text-align="justify" id="surrounding"
+ space-before.minimum="10pt"
+ space-before.optimum="12pt"
+ space-before.maximum="50pt">
+ <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b3" border-top="1pt solid black"
+ border-before-width.conditionality="retain">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face. In olden times when wishing still helped one, there lived a king
+ whose daughters were all beautiful, but the youngest was so beautiful that the sun
+ itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ <fo:block space-before="inherit" id="b4" border-top="1pt solid black">In olden times
+ when wishing still helped one, there lived a king whose daughters were all beautiful,
+ but the youngest was so beautiful that the sun itself, which has seen so much, was
+ astonished whenever it shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="13100" xpath="//pageViewport[1]//flow/block/block[2]/@space-before"/>
+ <eval expected="13100" xpath="//pageViewport[1]//flow/block/block[3]/@space-before"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[1]//flow/block/block[3]/@border-before"/>
+ <eval expected="In" xpath="//pageViewport[1]//flow/block/block[3]/lineArea[4]/text/word[position()=last()]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/@ipd"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[2]//flow/block/block[1]/@border-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/@ipd"/>
+ <eval expected="olden" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[1]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/>
+ <eval expected="12000" xpath="//pageViewport[2]//flow/block/block[2]/@space-before"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[2]//flow/block/block[2]/@border-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/@ipd"/>
+ <eval expected="12000" xpath="//pageViewport[2]//flow/block/block[3]/@space-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/lineArea[1]/@ipd"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml
new file mode 100644
index 000000000..dbe002e43
--- /dev/null
+++ b/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that blocks of texts are re-laid out after a change of the flow ipd.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="narrow"
+ page-height="300pt" page-width="400pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="wide"
+ page-height="300pt" page-width="600pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="pages">
+ <fo:single-page-master-reference master-reference="narrow"/>
+ <fo:repeatable-page-master-reference master-reference="wide"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block id="b1">In olden times when wishing still helped one, there lived a king whose
+ daughters were all beautiful, but the youngest was so beautiful that the sun itself,
+ which has seen so much, was astonished whenever it shone in her face.</fo:block>
+ <fo:block id="b2">In olden times when wishing still helped one, there lived a king whose
+ daughters were all beautiful, but the youngest was so beautiful that the sun itself,
+ which has seen so much, was astonished whenever it shone in her face.</fo:block>
+ <fo:block id="b3">In olden times when wishing still helped one, there lived a king whose
+ daughters were all beautiful, but the youngest was so beautiful that the sun itself,
+ which has seen so much, was astonished whenever it shone in her face. In olden times
+ when wishing still helped one, there lived a king whose daughters were all beautiful,
+ but the youngest was so beautiful that the sun itself, which has seen so much, was
+ astonished whenever it shone in her face.</fo:block>
+ <fo:block id="b4">In olden times when wishing still helped one, there lived a king whose
+ daughters were all beautiful, but the youngest was so beautiful that the sun itself,
+ which has seen so much, was astonished whenever it shone in her face.</fo:block>
+ <fo:block id="b5">In olden times when wishing still helped one, there lived a king whose
+ daughters were all beautiful, but the youngest was so beautiful that the sun itself,
+ which has seen so much, was astonished whenever it shone in her face.</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="has" xpath="//pageViewport[1]//flow/block[3]/lineArea[3]/text/word[position()=last()]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block[1]/lineArea[1]/@ipd"/>
+ <eval expected="seen" xpath="//pageViewport[2]//flow/block[1]/lineArea[1]/text/word[1]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block[2]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block[2]/lineArea[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block[3]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block[3]/lineArea[1]/@ipd"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml
new file mode 100644
index 000000000..2badfe71e
--- /dev/null
+++ b/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a change of IPD between two blocks is correctly handled.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="narrow"
+ page-height="300pt" page-width="400pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="wide"
+ page-height="300pt" page-width="600pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="pages">
+ <fo:single-page-master-reference master-reference="narrow"/>
+ <fo:repeatable-page-master-reference master-reference="wide"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block text-align="justify" id="surrounding"
+ space-before.minimum="10pt"
+ space-before.optimum="12pt"
+ space-before.maximum="50pt">
+ <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b3">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block border-top="1pt solid black" space-before.minimum="10pt"
+ space-before.optimum="12pt" space-before.maximum="50pt"
+ space-before.conditionality="retain" id="b4">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="face." xpath="//pageViewport[1]//flow/block/block[3]/lineArea[4]/text/word[position()=last()]"/>
+ <eval expected="12000" xpath="//pageViewport[2]//flow/block/block[1]/@space-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/@ipd"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[2]//flow/block/block[1]/@border-before"/>
+ <eval expected="In" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[1]"/>
+ <eval expected="olden" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[2]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml
new file mode 100644
index 000000000..fe166d05c
--- /dev/null
+++ b/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that non-restartable elements still show up at IPD change, even if not
+ re-laid out.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="narrow"
+ page-height="300pt" page-width="400pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="wide"
+ page-height="300pt" page-width="600pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="pages">
+ <fo:single-page-master-reference master-reference="narrow"/>
+ <fo:repeatable-page-master-reference master-reference="wide"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body" text-align="justify">
+ <fo:block space-before="10pt" id="b1_1">In olden times when wishing still helped one, there
+ lived a king whose daughters were all beautiful, but the youngest was so beautiful that
+ the sun itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ <fo:block space-before="10pt" id="b1_2">In olden times when wishing still helped one, there
+ lived a king whose daughters were all beautiful, but the youngest was so beautiful that
+ the sun itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ <fo:table table-layout="fixed" width="100%" border="1pt solid black" space-before="10pt"
+ padding="2pt" border-collapse="separate">
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block space-before="10pt" id="b1_3">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was
+ so beautiful that the sun itself, which has seen so much, was astonished
+ whenever it shone in her face.</fo:block>
+ <fo:block space-before="10pt" id="b1_4">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was
+ so beautiful that the sun itself, which has seen so much, was astonished
+ whenever it shone in her face.</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block space-before="10pt" id="b1_5" border-top="1pt solid red">In olden times when
+ wishing still helped one, there lived a king whose daughters were all beautiful, but the
+ youngest was so beautiful that the sun itself, which has seen so much, was astonished
+ whenever it shone in her face.</fo:block>
+ <fo:block space-before="10pt" id="b1_6">In olden times when wishing still helped one, there
+ lived a king whose daughters were all beautiful, but the youngest was so beautiful that
+ the sun itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body" text-align="justify">
+ <fo:block space-before="10pt" id="b2_1">In olden times when wishing still helped one, there
+ lived a king whose daughters were all beautiful, but the youngest was so beautiful that
+ the sun itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ <fo:block space-before="10pt" id="b2_2">In olden times when wishing still helped one, there
+ lived a king whose daughters were all beautiful, but the youngest was so beautiful that
+ the sun itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ <fo:list-block space-before="10pt" provisional-distance-between-starts="0.5cm">
+ <fo:list-item>
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block start-indent="2pt">•</fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:block space-before="10pt" id="b2_3">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was
+ so beautiful that the sun itself, which has seen so much, was
+ astonished…</fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ <fo:list-item>
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block start-indent="2pt">•</fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:block id="b2_4">In olden times when wishing still helped one, there lived a king
+ whose daughters were all beautiful, but the youngest was so beautiful that the sun
+ itself, which has seen so much, was astonished…</fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ </fo:list-block>
+ <fo:block space-before="10pt" space-before.conditionality="retain" border-top="1pt solid
+ red" id="b2_5">In olden times when wishing still helped one, there lived a king whose
+ daughters were all beautiful, but the youngest was so beautiful that the sun itself,
+ which has seen so much, was astonished whenever it shone in her face.</fo:block>
+ <fo:block space-before="10pt" id="b2_6">In olden times when wishing still helped one, there
+ lived a king whose daughters were all beautiful, but the youngest was so beautiful that
+ the sun itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <!-- First page sequence – table -->
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]/@border-after"/>
+ <eval expected="300000" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/@ipd"/>
+ <eval expected="b1_4" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]/block/block/@prod-id"/>
+ <eval expected="In" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/text/word[1]"/>
+ <eval expected="olden" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/text/word[2]"/>
+ <eval expected="her" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[4]/text/word[position()=last()-1]"/>
+ <eval expected="face." xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[4]/text/word[position()=last()]"/>
+
+ <eval expected="b1_5" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@prod-id"/>
+ <eval expected="500000" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@ipd"/>
+ <eval expected="(solid,#ff0000,1000)"
+ xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@border-before"/>
+ <eval expected="In" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[1]"/>
+ <eval expected="olden" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[2]"/>
+
+ <!-- Second page sequence – list -->
+ <eval expected="300000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/@ipd"/>
+ <eval expected="b2_4" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/@prod-id"/>
+ <eval expected="In" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[1]/text/word[1]"/>
+ <eval expected="olden" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[1]/text/word[2]"/>
+ <eval expected="was" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[4]/text/word[position()=last()-1]"/>
+ <eval expected="astonished…"
+ xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[4]/text/word[position()=last()]"/>
+
+ <eval expected="b2_5" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@prod-id"/>
+ <eval expected="500000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@ipd"/>
+ <eval expected="10000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@space-before"/>
+ <eval expected="(solid,#ff0000,1000)"
+ xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@border-before"/>
+ <eval expected="In" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[1]"/>
+ <eval expected="olden" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[2]"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml
new file mode 100644
index 000000000..82b757d46
--- /dev/null
+++ b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that block-container elements correctly support an IPD change.
+ </p>
+ <!-- NOTE: This test case is a copy of flow_changing-ipd_1.xml, modified to simply surround
+ block 3 with a block-container. -->
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="narrow"
+ page-height="300pt" page-width="400pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="wide"
+ page-height="300pt" page-width="600pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="pages">
+ <fo:single-page-master-reference master-reference="narrow"/>
+ <fo:repeatable-page-master-reference master-reference="wide"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block text-align="justify" id="surrounding"
+ space-before.minimum="10pt"
+ space-before.optimum="12pt"
+ space-before.maximum="50pt">
+ <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block-container space-before="inherit">
+ <fo:block id="b3" border-top="1pt solid black"
+ border-before-width.conditionality="retain">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face. In olden times when wishing still helped one, there lived a king
+ whose daughters were all beautiful, but the youngest was so beautiful that the sun
+ itself, which has seen so much, was astonished whenever it shone in her
+ face.</fo:block>
+ </fo:block-container>
+ <fo:block space-before="inherit" id="b4" border-top="1pt solid black">In olden times
+ when wishing still helped one, there lived a king whose daughters were all beautiful,
+ but the youngest was so beautiful that the sun itself, which has seen so much, was
+ astonished whenever it shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="13100" xpath="//pageViewport[1]//flow/block/block[2]/@space-before"/>
+ <eval expected="13100" xpath="//pageViewport[1]//flow/block/block[3]/@space-before"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[1]//flow/block/block[3]/block/block/@border-before"/>
+ <eval expected="In" xpath="//pageViewport[1]//flow/block/block[3]/block/block/lineArea[4]/text/word[position()=last()]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/@ipd"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[2]//flow/block/block[1]/block/block/@border-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/block/block/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/block/block/lineArea[1]/@ipd"/>
+ <eval expected="olden" xpath="//pageViewport[2]//flow/block/block[1]/block/block/lineArea[1]/text/word[1]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/>
+ <eval expected="12000" xpath="//pageViewport[2]//flow/block/block[2]/@space-before"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[2]//flow/block/block[2]/@border-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/@ipd"/>
+ <eval expected="12000" xpath="//pageViewport[2]//flow/block/block[3]/@space-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/lineArea[1]/@ipd"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml
new file mode 100644
index 000000000..7a2bb9b19
--- /dev/null
+++ b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a change of IPD between two blocks surrounded by a block-container is
+ correctly handled.
+ </p>
+ <!-- NOTE: This test case is a copy of flow_changing-ipd_3.xml, with the surrounding block
+ replaced with a block-container. -->
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="narrow"
+ page-height="300pt" page-width="400pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="wide"
+ page-height="300pt" page-width="600pt" margin="50pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="pages">
+ <fo:single-page-master-reference master-reference="narrow"/>
+ <fo:repeatable-page-master-reference master-reference="wide"/>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="pages">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container text-align="justify" id="surrounding"
+ space-before.minimum="10pt"
+ space-before.optimum="12pt"
+ space-before.maximum="50pt">
+ <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b3">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block border-top="1pt solid black" space-before.minimum="10pt"
+ space-before.optimum="12pt" space-before.maximum="50pt"
+ space-before.conditionality="retain" id="b4">In olden times when wishing still helped
+ one, there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one,
+ there lived a king whose daughters were all beautiful, but the youngest was so
+ beautiful that the sun itself, which has seen so much, was astonished whenever it
+ shone in her face.</fo:block>
+ </fo:block-container>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="face." xpath="//pageViewport[1]//flow/block/block/block[3]/lineArea[4]/text/word[position()=last()]"/>
+ <eval expected="12000" xpath="//pageViewport[2]//flow/block/block/block[1]/@space-before"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[1]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/@ipd"/>
+ <eval expected="(solid,#000000,1000)"
+ xpath="//pageViewport[2]//flow/block/block/block[1]/@border-before"/>
+ <eval expected="In" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/text/word[1]"/>
+ <eval expected="olden" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/text/word[2]"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[2]/@ipd"/>
+ <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[2]/lineArea[1]/@ipd"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/footnote_in_table_2.xml b/test/layoutengine/standard-testcases/footnote_in_table_2.xml
index f0514a889..5adb65a4e 100644
--- a/test/layoutengine/standard-testcases/footnote_in_table_2.xml
+++ b/test/layoutengine/standard-testcases/footnote_in_table_2.xml
@@ -171,14 +171,18 @@
<eval expected="(3) Third footnote from the table." xpath="//pageSequence[1]//footnote/block[3]"/>
<eval expected="1" xpath="count(//pageSequence[2]/pageViewport[1]//footnote/block)"/>
+ <eval expected="Cell 1.2(1)." xpath="//pageSequence[2]/pageViewport[1]//regionBody//block[2]/block[3]/block[2]"/>
<eval expected="(1) First footnote from the table." xpath="//pageSequence[2]/pageViewport[1]//footnote/block[1]"/>
<eval expected="2" xpath="count(//pageSequence[2]/pageViewport[2]//footnote/block)"/>
<eval expected="(2) Second footnote from the table." xpath="//pageSequence[2]/pageViewport[2]//footnote/block[1]"/>
<eval expected="(3) Third footnote from the table." xpath="//pageSequence[2]/pageViewport[2]//footnote/block[2]"/>
+ <eval expected="Cell 1.1(2)" xpath="//pageSequence[3]/pageViewport[1]//regionBody//block[2]/block[2]/block[3]"/>
+ <eval expected="Cell 1.2(1)." xpath="//pageSequence[3]/pageViewport[1]//regionBody//block[2]/block[3]/block[2]"/>
<eval expected="2" xpath="count(//pageSequence[3]/pageViewport[1]//footnote/block)"/>
<eval expected="(1) First footnote from the table." xpath="//pageSequence[3]/pageViewport[1]//footnote/block[1]"/>
<eval expected="(2) Second footnote from the table." xpath="//pageSequence[3]/pageViewport[1]//footnote/block[2]"/>
+ <eval expected="Cell(3) 2.2" xpath="//pageSequence[3]/pageViewport[2]//regionBody//block[1]/block[3]/block[1]"/>
<eval expected="1" xpath="count(//pageSequence[3]/pageViewport[2]//footnote/block)"/>
<eval expected="(3) Third footnote from the table." xpath="//pageSequence[3]/pageViewport[2]//footnote/block[1]"/>
</checks>
diff --git a/test/layoutengine/standard-testcases/footnote_in_table_3.xml b/test/layoutengine/standard-testcases/footnote_in_table_3.xml
new file mode 100644
index 000000000..037e70f51
--- /dev/null
+++ b/test/layoutengine/standard-testcases/footnote_in_table_3.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a footnote within the fixed height of a table row is properly handled.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="simple" page-height="220pt" page-width="3in"
+ margin="10pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="simple" font-size="8pt" line-height="10pt">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Before the table</fo:block>
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)"/>
+ <fo:table-column column-width="proportional-column-width(1)"/>
+ <fo:table-body border="1pt solid black">
+ <fo:table-row border="inherit">
+ <fo:table-cell border="inherit">
+ <fo:block break-after="page">Cell 1.1</fo:block>
+ <fo:block>Cell 1.1<fo:footnote>
+ <fo:inline>(2)</fo:inline>
+ <fo:footnote-body>
+ <fo:block>(2) Footnote number 2.</fo:block>
+ </fo:footnote-body>
+ </fo:footnote></fo:block>
+ <fo:block>Cell 1.1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="inherit">
+ <fo:block>Cell 1.2<fo:footnote>
+ <fo:inline>(1)</fo:inline>
+ <fo:footnote-body>
+ <fo:block>(1) Footnote number 1.</fo:block>
+ </fo:footnote-body>
+ </fo:footnote></fo:block>
+ <fo:block>Cell 1.2</fo:block>
+ <fo:block>Cell 1.2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="Cell 1.2(1)" xpath="//pageViewport[1]//regionBody//block[2]/block[2]/block"/>
+ <eval expected="1" xpath="count(//pageViewport[1]//footnote/block)"/>
+ <eval expected="(1) Footnote number 1." xpath="//pageViewport[1]//footnote/block[1]"/>
+
+ <eval expected="Cell 1.1(2)" xpath="//pageViewport[2]//regionBody//block[1]/block[1]/block[1]"/>
+ <eval expected="1" xpath="count(//pageViewport[2]//footnote/block)"/>
+ <eval expected="(2) Footnote number 2." xpath="//pageViewport[2]//footnote/block[1]"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/footnote_in_table_fixed-height.xml b/test/layoutengine/standard-testcases/footnote_in_table_fixed-height.xml
new file mode 100644
index 000000000..0b1e02d94
--- /dev/null
+++ b/test/layoutengine/standard-testcases/footnote_in_table_fixed-height.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a footnote within the fixed height of a table row is properly handled.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="simple" page-height="220pt" page-width="3in"
+ margin="10pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="simple" font-size="8pt" line-height="10pt">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Before the table</fo:block>
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)"/>
+ <fo:table-column column-width="proportional-column-width(1)"/>
+ <fo:table-body border="1pt solid black">
+ <fo:table-row border="inherit" height="40pt">
+ <fo:table-cell border="inherit">
+ <fo:block>Cell 1.1</fo:block>
+ <fo:block>Cell 1.1</fo:block>
+ <fo:block>Cell 1.1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="inherit">
+ <fo:block>Cell 1.2<fo:footnote>
+ <fo:inline>(1)</fo:inline>
+ <fo:footnote-body>
+ <fo:block>(1) Footnote within the fixed height.</fo:block>
+ </fo:footnote-body>
+ </fo:footnote></fo:block>
+ <fo:block>Cell 1.2</fo:block>
+ <fo:block>Cell 1.2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageSequence[1]//footnote/block)"/>
+ <eval expected="(1) Footnote within the fixed height." xpath="//pageSequence[1]//footnote/block[1]"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/inline_block_nested_6.xml b/test/layoutengine/standard-testcases/inline_block_nested_6.xml
index 9dc588074..86827047c 100644
--- a/test/layoutengine/standard-testcases/inline_block_nested_6.xml
+++ b/test/layoutengine/standard-testcases/inline_block_nested_6.xml
@@ -52,16 +52,15 @@
<skip>5</skip>
<!-- penalty between blocks b11 and b12, set by InlineLM in b1 -->
<penalty w="0" p="0"/>
- <skip>6</skip>
+ <skip>5</skip>
<!-- penalty between blocks b21 and b22, set by InlineLM in b2 -->
<!-- keep-together.within-page="always" -->
<penalty w="0" p="1000"/>
- <skip>6</skip>
+ <skip>3</skip>
<!-- penalty between blocks b31 and b32, set by InlineLM in b3 -->
<!-- keep-with-next.within-page="always" -->
<penalty w="0" p="1000"/>
- <skip>5</skip>
- <skip>3</skip>
+ <skip>14</skip>
</element-list>
</checks>
</testcase>
diff --git a/test/layoutengine/standard-testcases/keep_within-column_basic.xml b/test/layoutengine/standard-testcases/keep_within-column_basic.xml
new file mode 100644
index 000000000..001690521
--- /dev/null
+++ b/test/layoutengine/standard-testcases/keep_within-column_basic.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks whether keeps within-column are respected.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page" page-width="400pt" page-height="70pt">
+ <fo:region-body column-count="5" />
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page" font-size="10pt">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block break-before="page">
+ <!-- simple test: keep the second block together within
+ one column, breaking the preceding block early
+ if necessary -->
+ <fo:block id="block-1">
+ [BOB-1] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-1]
+ </fo:block>
+ <fo:block id="block-2" keep-together.within-column="always">
+ [BOB-2] foo bar foo bar foo bar foo bar foo [EOB-2]
+ </fo:block>
+ </fo:block>
+ <fo:block break-before="page">
+ <!-- same as the first, but now a nested block
+ with a higher integer value, and some content
+ following -->
+ <fo:block id="block-3" keep-together.within-column="5">
+ [BOB-3] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ <fo:block font-weight="bold" id="block-3a" keep-together.within-column="always">
+ [BOB-3a] foo bar foo bar foo bar foo bar foo [EOB-3a]
+ </fo:block>
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-3]
+ </fo:block>
+ </fo:block>
+ <fo:block break-before="page">
+ <!-- nested block must be kept together within the same
+ page, while the outer block may be broken, if necessary -->
+ <fo:block font-style="italic" id="block-4" keep-together.within-column="5">
+ [BOB-4] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ <fo:block id="block-4a" keep-together.within-page="always">
+ [BOB-4a] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-4a]
+ </fo:block>
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-4]
+ </fo:block>
+ </fo:block>
+ <fo:block break-before="page">
+ <!-- test keep-with-next in conjunction with keep-together
+ respecting the default value for widows/orphans -->
+ <fo:block id="block-5">
+ <fo:block id="block-5a">
+ [BOB-5a] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-5a]
+ </fo:block>
+ <fo:block id="block-5b" keep-with-next.within-column="always">
+ [BOB-5b] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-5b]
+ </fo:block>
+ <fo:block id="block-5c" keep-together.within-column="always">
+ [BOB-5c] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-5c]
+ </fo:block>
+ </fo:block>
+ </fo:block>
+ <fo:block break-before="page">
+ <!-- test keep-together in conjunction with keep-with-previous -->
+ <fo:block id="block-6">
+ <fo:block id="block-6a">
+ [BOB-6a] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-6a]
+ </fo:block>
+ <fo:block id="block-6b" keep-together.within-column="always">
+ [BOB-6b] foo bar foo bar foo bar foo bar foo bar [EOB-6b]
+ </fo:block>
+ <fo:block id="block-6c" keep-with-previous.within-column="always">
+ [BOB-6c] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-6c]
+ </fo:block>
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <!-- check total page-count -->
+ <eval expected="10" xpath="count(//page)" />
+ <!-- block-2 should end up in its own column, column 4 -->
+ <eval expected="1" xpath="count(//block[@prod-id='block-2']/ancestor::flow)" />
+ <eval expected="3" xpath="count(//block[@prod-id='block-2']/ancestor::flow/preceding-sibling::flow)" />
+ <!-- block-3a should end up in its own column, column 5 -->
+ <eval expected="1" xpath="count(//block[@prod-id='block-3a']/ancestor::flow)" />
+ <eval expected="4" xpath="count(//block[@prod-id='block-3a']/ancestor::flow/preceding-sibling::flow)" />
+ <!-- block-4a should end up in its own page -->
+ <eval expected="1" xpath="count(//block[@prod-id='block-4a']/ancestor::page)" />
+ <!-- block 5c should end up in its own column, with two preceding lines from block 5b -->
+ <eval expected="1" xpath="count(//block[@prod-id='block-5c']/ancestor::flow)" />
+ <eval expected="2" xpath="count(//block[@prod-id='block-5c']/preceding-sibling::block/lineArea)" />
+ <!-- block 6b should end up in its own column, with two following lines from block 6c -->
+ <eval expected="1" xpath="count(//block[@prod-id='block-6b']/ancestor::flow)" />
+ <eval expected="2" xpath="count(//block[@prod-id='block-6b']/following-sibling::block/lineArea)" />
+ </checks>
+</testcase>
+
diff --git a/test/layoutengine/standard-testcases/keep_within-page_multi-column_overflow.xml b/test/layoutengine/standard-testcases/keep_within-page_multi-column_overflow.xml
new file mode 100644
index 000000000..f7fac57b0
--- /dev/null
+++ b/test/layoutengine/standard-testcases/keep_within-page_multi-column_overflow.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>This test checks for a remaining issue after adding support
+ for keep-*.within column (see Bugzilla 46905).
+ keep-together.within-page does not work as expected in multi-column
+ layout. If the part does not fit into one page, it will ultimately
+ still be broken.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page" page-width="400pt" page-height="70pt">
+ <fo:region-body column-count="5" />
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page" font-size="10pt">
+ <fo:flow flow-name="xsl-region-body">
+ <!-- block must be kept together within the same page -->
+ <fo:block id="block-4a" keep-together.within-page="always">
+ [BOB-4a] foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar foo bar
+ foo bar foo bar foo bar foo bar foo bar [EOB-4a]
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageViewport)" />
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/marker_white-space_npe.xml b/test/layoutengine/standard-testcases/marker_white-space_npe.xml
new file mode 100644
index 000000000..f8bcf869e
--- /dev/null
+++ b/test/layoutengine/standard-testcases/marker_white-space_npe.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks for a potential NPE after white-space handling
+ for retrieved markers with only inline content.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master margin-right="6mm" margin-left="7mm" margin-bottom="4mm" margin-top="5mm" page-width="210mm" page-height="297mm" master-name="letterPageFront">
+ <fo:region-body margin-right="59mm" margin-left="8mm" margin-top="53mm" margin-bottom="57mm" region-name="letterPageBody"/>
+ <fo:region-after precedence="false" extent="52mm" region-name="letterPageFooter"/>
+ <fo:region-end precedence="true" extent="49mm" region-name="letterPageSidebar"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence initial-page-number="1" page-break-before="right" master-reference="letterPageFront">
+ <fo:flow flow-name="letterPageBody">
+ <fo:block>page 1</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="letterPageFront">
+ <fo:static-content flow-name="letterPageFooter">
+ <fo:block text-align="right" margin-right="1cm" margin-left="8mm">
+ <fo:retrieve-marker retrieve-position="last-ending-within-page" retrieve-boundary="page-sequence" retrieve-class-name="statementFooterMarker"/>
+ </fo:block>
+ </fo:static-content>
+ <fo:flow flow-name="letterPageBody">
+ <fo:block>
+ <fo:block font="bold 16pt Arial">
+ <fo:block>
+ <fo:marker marker-class-name="statementFooterMarker">
+ <fo:inline font-family="HelveticaNeue-LightCond" font-size="11pt" line-height="13pt" text-align="right">Test Marker</fo:inline>
+ </fo:marker>
+ </fo:block>
+ </fo:block>
+ <fo:block break-before="page">
+ <fo:block>
+ <fo:marker marker-class-name="statementFooterMarker"/>
+ </fo:block>
+ </fo:block>
+ </fo:block>
+ <fo:block id="TH_LastPage"/>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="letterPageFront">
+ <fo:static-content flow-name="letterPageSidebar">
+ <fo:block-container height="284mm">
+ <fo:block> </fo:block>
+ </fo:block-container>
+ </fo:static-content>
+ <fo:flow break-before="odd-page" flow-name="letterPageBody">
+ <fo:block>last page</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks /><!-- none except the default; simply check for non-occurrence of NPE -->
+</testcase>
diff --git a/test/layoutengine/standard-testcases/markers_empty_bug46960.xml b/test/layoutengine/standard-testcases/markers_empty_bug46960.xml
new file mode 100644
index 000000000..a959517d1
--- /dev/null
+++ b/test/layoutengine/standard-testcases/markers_empty_bug46960.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks for bug 46960: retrieve-markers not cleared if a subsequent
+ retrieved marker is empty.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="a4" page-width="210mm" page-height="297mm" margin="5mm">
+ <fo:region-body margin-top="2cm"/>
+ <fo:region-before extent="2cm" region-name="header"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="a4">
+ <fo:static-content flow-name="header">
+ <fo:block><fo:retrieve-marker id="rm-cont-1" retrieve-class-name="Continued" retrieve-boundary="document" retrieve-position="first-starting-within-page"/></fo:block>
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block><fo:marker marker-class-name="Continued"/>First Page</fo:block>
+ <fo:block break-before="page"><fo:marker marker-class-name="Continued">Continued</fo:marker>Second Page</fo:block>
+ <fo:block break-before="page"><fo:marker marker-class-name="Continued"/>Third Page</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <!-- first retrieved marker is empty -->
+ <eval expected="0" xpath="(//page)[1]//regionBefore/block/@bpd" />
+ <!-- second marker is "Continued" -->
+ <eval expected="Continued" xpath="(//page)[2]//regionBefore/block/lineArea/text/word" />
+ <!-- third marker should be empty again -->
+ <eval expected="0" xpath="(//page)[3]//regionBefore/block/@bpd" />
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/page-number-citation-last_break-after_bug45702.xml b/test/layoutengine/standard-testcases/page-number-citation-last_break-after_bug45702.xml
index 1ca74f4c3..3e3ef786b 100644
--- a/test/layoutengine/standard-testcases/page-number-citation-last_break-after_bug45702.xml
+++ b/test/layoutengine/standard-testcases/page-number-citation-last_break-after_bug45702.xml
@@ -1,76 +1,76 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- $Id$ -->
-<testcase>
- <info>
- <p>
- This test checks for correct resolution of page-number-citation-last, in
- case a break-after is set on the last child block to the block carrying the id.
- (see also Bugzilla #45702)
- </p>
- </info>
- <fo>
- <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
- <fo:layout-master-set>
- <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
- <fo:region-body margin-top="0cm" region-name="xsl-region-body"/>
- </fo:simple-page-master>
- </fo:layout-master-set>
- <fo:page-sequence master-reference="A4" id="toc">
- <fo:flow flow-name="xsl-region-body">
- <fo:block>
- PS : from <fo:page-number-citation id="pnc.page-sequence" ref-id="page-sequence"/> to <fo:page-number-citation-last id="pncl.page-sequence" ref-id="page-sequence"/>
- </fo:block>
- <fo:block>
- toc : from <fo:page-number-citation id="pnc.toc" ref-id="toc"/> to <fo:page-number-citation-last id="pncl.toc" ref-id="toc"/>
- </fo:block>
- <fo:block>
- block-1 : from <fo:page-number-citation id="pnc.block-1" ref-id="block-1"/> to <fo:page-number-citation-last id="pncl.block-1" ref-id="block-1"/>
- </fo:block>
- </fo:flow>
- </fo:page-sequence>
- <fo:page-sequence master-reference="A4" id="page-sequence">
- <fo:flow flow-name="xsl-region-body">
- <fo:block id="block-1">
- <fo:block padding="1pt">two blocks, with break-after specified on the last one</fo:block>
- <fo:block break-before="page">XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX </fo:block>
- <fo:block break-before="page" break-after="page">XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
- XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX </fo:block>
- </fo:block>
- </fo:flow>
- </fo:page-sequence>
- </fo:root>
- </fo>
- <checks>
- <eval expected="1" xpath="/areaTree/pageSequence[1]//text[@prod-id='pnc.toc']/word" />
- <eval expected="1" xpath="/areaTree/pageSequence[1]//text[@prod-id='pncl.toc']/word" />
- <eval expected="2" xpath="/areaTree/pageSequence[1]//text[@prod-id='pnc.page-sequence']/word" />
- <eval expected="4" xpath="/areaTree/pageSequence[1]//text[@prod-id='pncl.page-sequence']/word" />
- <eval expected="2" xpath="/areaTree/pageSequence[1]//text[@prod-id='pnc.block-1']/word" />
- <eval expected="4" xpath="/areaTree/pageSequence[1]//text[@prod-id='pncl.block-1']/word" />
- </checks>
-</testcase>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks for correct resolution of page-number-citation-last, in
+ case a break-after is set on the last child block to the block carrying the id.
+ (see also Bugzilla #45702)
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body margin-top="0cm" region-name="xsl-region-body"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4" id="toc">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ PS : from <fo:page-number-citation id="pnc.page-sequence" ref-id="page-sequence"/> to <fo:page-number-citation-last id="pncl.page-sequence" ref-id="page-sequence"/>
+ </fo:block>
+ <fo:block>
+ toc : from <fo:page-number-citation id="pnc.toc" ref-id="toc"/> to <fo:page-number-citation-last id="pncl.toc" ref-id="toc"/>
+ </fo:block>
+ <fo:block>
+ block-1 : from <fo:page-number-citation id="pnc.block-1" ref-id="block-1"/> to <fo:page-number-citation-last id="pncl.block-1" ref-id="block-1"/>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="A4" id="page-sequence">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block id="block-1">
+ <fo:block padding="1pt">two blocks, with break-after specified on the last one</fo:block>
+ <fo:block break-before="page">XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX </fo:block>
+ <fo:block break-before="page" break-after="page">XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX
+ XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="/areaTree/pageSequence[1]//text[@prod-id='pnc.toc']/word" />
+ <eval expected="1" xpath="/areaTree/pageSequence[1]//text[@prod-id='pncl.toc']/word" />
+ <eval expected="2" xpath="/areaTree/pageSequence[1]//text[@prod-id='pnc.page-sequence']/word" />
+ <eval expected="4" xpath="/areaTree/pageSequence[1]//text[@prod-id='pncl.page-sequence']/word" />
+ <eval expected="2" xpath="/areaTree/pageSequence[1]//text[@prod-id='pnc.block-1']/word" />
+ <eval expected="4" xpath="/areaTree/pageSequence[1]//text[@prod-id='pncl.block-1']/word" />
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/table-row_keep-together.xml b/test/layoutengine/standard-testcases/table-row_keep-together.xml
index 067c4daf6..73da10fb8 100644
--- a/test/layoutengine/standard-testcases/table-row_keep-together.xml
+++ b/test/layoutengine/standard-testcases/table-row_keep-together.xml
@@ -64,10 +64,10 @@
<element-list category="breaker" index="0">
<box w="14400"/>
<penalty w="0" p="0"/>
- <box w="28800"/>
- <penalty w="0" p="0"/>
<box w="14400"/>
- <skip>3</skip>
+ <penalty w="0" p="INF"/>
+ <box w="14400"/>
+ <skip>5</skip>
</element-list>
</checks>
</testcase>
diff --git a/test/layoutengine/standard-testcases/table_in_marker_bug47101.xml b/test/layoutengine/standard-testcases/table_in_marker_bug47101.xml
new file mode 100644
index 000000000..48d9bbc69
--- /dev/null
+++ b/test/layoutengine/standard-testcases/table_in_marker_bug47101.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks Bugzilla #47101 where the cells of a table defined inside a marker were
+ duplicated at every marker retrieval.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page" page-width="400pt" page-height="300pt" margin="20pt">
+ <fo:region-body margin-bottom="40pt"/>
+ <fo:region-after extent="40pt"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page">
+ <fo:static-content flow-name="xsl-region-after">
+ <fo:retrieve-marker retrieve-boundary="page" retrieve-position="first-including-carryover"
+ retrieve-class-name="page-footer"/>
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <fo:marker marker-class-name="page-footer"
+ ><fo:table table-layout="fixed" width="100%">
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block font-size="8pt" font-style="italic">Table in a marker</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:marker>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ <fo:block space-after="12pt">text</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageViewport[1]//regionAfter/block/block)"/>
+ <eval expected="1" xpath="count(//pageViewport[2]//regionAfter/block/block)"/>
+ <eval expected="1" xpath="count(//pageViewport[3]//regionAfter/block/block)"/>
+ </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/table_keep-together.xml b/test/layoutengine/standard-testcases/table_keep-together.xml
index 227f5ede4..a904a144a 100644
--- a/test/layoutengine/standard-testcases/table_keep-together.xml
+++ b/test/layoutengine/standard-testcases/table_keep-together.xml
@@ -101,7 +101,9 @@
<element-list category="breaker" index="0">
<box w="14400"/>
<penalty w="0" p="0"/>
- <box w="28800"/>
+ <box w="14400"/>
+ <penalty w="0" p="INF"/>
+ <box w="14400"/>
<penalty w="0" p="INF"/>
<box w="14400"/>
<penalty w="0" p="0"/>
diff --git a/test/resources/afp/F1SAMPLE.afp b/test/resources/afp/F1SAMPLE.afp
new file mode 100644
index 000000000..9dfd5b910
--- /dev/null
+++ b/test/resources/afp/F1SAMPLE.afp
Binary files differ