aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache/fop')
-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
210 files changed, 14599 insertions, 3741 deletions
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
+ĵĴ
+ķĶ
+ĺĹ
+ļĻ
+ľĽ
+ŀĿ
+łŁ
+ńŃ
+ņŅ
+ňŇ
+ŋŊ
+ōŌ
+ŏŎ
+őŐ
+œŒ
+ŕŔ
+ŗŖ
+řŘ
+śŚ
+ŝŜ
+şŞ
+šŠ
+ţŢ
+ťŤ
+ŧŦ
+ũŨ
+ūŪ
+ŭŬ
+ůŮ
+űŰ
+ųŲ
+ŵŴ
+ŷŶ
+źŹ
+żŻ
+žŽ
+ſ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);
+ }
+
+}