diff options
16 files changed, 336 insertions, 22 deletions
diff --git a/conf/fop.xconf b/conf/fop.xconf index 10b31cf86..01f12fd14 100644 --- a/conf/fop.xconf +++ b/conf/fop.xconf @@ -94,6 +94,7 @@ the location of this file. --> <images mode="b+w" bits-per-pixel="8"/> <renderer-resolution>240</renderer-resolution> + <line-width-correction>2.5</line-width-correction> <resource-group-file>resources.afp</resource-group-file> <fonts> diff --git a/src/documentation/content/xdocs/trunk/configuration.xml b/src/documentation/content/xdocs/trunk/configuration.xml index c56b974f8..b30a091a5 100644 --- a/src/documentation/content/xdocs/trunk/configuration.xml +++ b/src/documentation/content/xdocs/trunk/configuration.xml @@ -471,6 +471,7 @@ <source><![CDATA[<renderer mime="application/x-afp"> <images mode="b+w" bits-per-pixel="8" native="true"/> <renderer-resolution>240</renderer-resolution> + <line-width-correction>2.5</line-width-correction> <!-- a default external resource group file --> <resource-group-file>resources.afp</resource-group-file> @@ -482,6 +483,9 @@ <p> The default value for the "renderer-resolution" is 240 dpi. </p> + <p> + The default line width is device dependent and may need to be fine tuned so that the output matches the expected result. The default correction value is 2.5. + </p> <!-- <p> The default value for the MO:DCA "interchange-set" is "MO:DCA-L". Other compliance settings include presentation interchange sets "MO:DCA-P IS/1" and "MO:DCA-P IS/2" (Resource Groups). diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index d61ba8840..cabbbb0fa 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -757,6 +757,14 @@ Note that the value of the encoding attribute in the example is the double-byte <source><![CDATA[ <renderer-resolution>240</renderer-resolution>]]></source> </section> + <section id="afp-line-width-correction-config"> + <title>Line Width Correction</title> + <p>The default line width in AFP is device dependent. This means that a line width specified in, say, + a SVG source file may not render the way it was intended. The output AFP line with can be corrected + by the <line-width-correction/> configuration element. Example:</p> + <source><![CDATA[ + <line-width-correction>2.5</line-width-correction>]]></source> + </section> <section id="afp-image-config"> <title>Images</title> <p>By default the AFP Renderer converts all images to 8 bit grey level. diff --git a/src/java/org/apache/fop/afp/AFPConstants.java b/src/java/org/apache/fop/afp/AFPConstants.java index 6b26c18fd..49b671e66 100644 --- a/src/java/org/apache/fop/afp/AFPConstants.java +++ b/src/java/org/apache/fop/afp/AFPConstants.java @@ -50,4 +50,15 @@ public interface AFPConstants { * 72dpi in millipoints */ int DPI_72_MPTS = DPI_72 * 1000; + + /** + * The line width is set as a multiplier of a default line with; the width of the default + * line width is implementation defined, which probably means different devices use different + * actual widths; this means that the source line width (as specified in, say, a SVG line + * element) needs to be corrected by a fudge factor that depends on the output device so that + * the final output (print to paper, screen viewer) looks as intended. + */ + float LINE_WIDTH_CORRECTION = 2.5f; + } + diff --git a/src/java/org/apache/fop/afp/AFPGraphics2D.java b/src/java/org/apache/fop/afp/AFPGraphics2D.java index 41be79160..e9a269ac4 100644 --- a/src/java/org/apache/fop/afp/AFPGraphics2D.java +++ b/src/java/org/apache/fop/afp/AFPGraphics2D.java @@ -263,14 +263,6 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand return length * factor; } - /** IBM's AFP Workbench paints lines that are wider than expected. We correct manually. */ - private static final double GUESSED_WIDTH_CORRECTION = 1.7; - - private static final double SPEC_NORMAL_LINE_WIDTH = UnitConv.in2pt(0.01); //"approx" 0.01 inch - private static final double NORMAL_LINE_WIDTH - = SPEC_NORMAL_LINE_WIDTH * GUESSED_WIDTH_CORRECTION; - - /** * Apply the stroke to the AFP graphics object. * This takes the java stroke and outputs the appropriate settings @@ -282,17 +274,11 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand if (stroke instanceof BasicStroke) { BasicStroke basicStroke = (BasicStroke) stroke; - // set line width + // set line width and correct it; NOTE: apparently we need to correct the width so that the + // output looks OK since the default with depends on the output device float lineWidth = basicStroke.getLineWidth(); - if (false) { - //Old approach. Retained until verified problems with 1440 resolution - graphicsObj.setLineWidth(Math.round(lineWidth / 2)); - } else { - double absoluteLineWidth = lineWidth * Math.abs(getTransform().getScaleY()); - double multiplier = absoluteLineWidth / NORMAL_LINE_WIDTH; - graphicsObj.setLineWidth((int)Math.round(multiplier)); - //TODO Use GSFLW instead of GSLW for higher accuracy? - } + float correction = paintingState.getLineWidthCorrection(); + graphicsObj.setLineWidth(lineWidth * correction); //No line join, miter limit and end cap support in GOCA. :-( diff --git a/src/java/org/apache/fop/afp/AFPPaintingState.java b/src/java/org/apache/fop/afp/AFPPaintingState.java index 7e6b940a5..ea2ebe475 100644 --- a/src/java/org/apache/fop/afp/AFPPaintingState.java +++ b/src/java/org/apache/fop/afp/AFPPaintingState.java @@ -79,6 +79,12 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState /** the output resolution */ private int resolution = 240; // 240 dpi + /** + * A configurable value to correct the line width so that the output matches the expected. Different + * devices may need different values. + */ + private float lineWidthCorrection = AFPConstants.LINE_WIDTH_CORRECTION; + /** determines whether GOCA is enabled or disabled */ private boolean gocaEnabled = true; /** determines whether to stroke text in GOCA mode or to use text operators where possible */ @@ -323,6 +329,18 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** + * Sets the line width correction + * + * @param correction the line width multiplying factor correction + */ + public void setLineWidthCorrection(float correction) { + if (log.isDebugEnabled()) { + log.debug("line width correction set to: " + correction); + } + this.lineWidthCorrection = correction; + } + + /** * Returns the output/device resolution. * * @return the resolution in dpi @@ -332,6 +350,14 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState } /** + * Returns the line width correction. + * @return the correction + */ + public float getLineWidthCorrection() { + return this.lineWidthCorrection; + } + + /** * Controls whether GOCA is enabled or disabled. * @param enabled true if GOCA is enabled, false if it is disabled */ diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetFractionalLineWidth.java b/src/java/org/apache/fop/afp/goca/GraphicsSetFractionalLineWidth.java new file mode 100644 index 000000000..529001676 --- /dev/null +++ b/src/java/org/apache/fop/afp/goca/GraphicsSetFractionalLineWidth.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.afp.goca; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Sets the line width to use when stroking GOCA shapes (structured fields) + */ +public class GraphicsSetFractionalLineWidth extends AbstractGraphicsDrawingOrder { + + /** line width multiplier */ + private final float multiplier; + + /** + * Main constructor + * + * @param multiplier the line width multiplier + */ + public GraphicsSetFractionalLineWidth(float multiplier) { + this.multiplier = multiplier; + } + + /** {@inheritDoc} */ + public int getDataLength() { + return 4; + } + + /** {@inheritDoc} */ + public void writeToStream(OutputStream os) throws IOException { + int integral = (int) multiplier; + int fractional = (int) ((multiplier - (float) integral) * 256); + byte[] data = new byte[] { + getOrderCode(), // GSLW order code + 0x02, // two bytes next + (byte) integral, // integral line with + (byte) fractional // and fractional + }; + os.write(data); + } + + /** {@inheritDoc} */ + public String toString() { + return "GraphicsSetFractionalLineWidth{multiplier=" + multiplier + "}"; + } + + /** {@inheritDoc} */ + byte getOrderCode() { + return 0x11; + } +} diff --git a/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java b/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java index 96b54f3cd..ca8812f14 100644 --- a/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java +++ b/src/java/org/apache/fop/afp/goca/GraphicsSetLineWidth.java @@ -28,7 +28,7 @@ import java.io.OutputStream; public class GraphicsSetLineWidth extends AbstractGraphicsDrawingOrder { /** line width multiplier */ - private int multiplier = 1; + private final int multiplier; /** * Main constructor @@ -48,7 +48,7 @@ public class GraphicsSetLineWidth extends AbstractGraphicsDrawingOrder { public void writeToStream(OutputStream os) throws IOException { byte[] data = new byte[] { getOrderCode(), // GSLW order code - (byte)multiplier // MH (line-width) + (byte) multiplier // MH (line-width) }; os.write(data); } diff --git a/src/java/org/apache/fop/afp/modca/GraphicsObject.java b/src/java/org/apache/fop/afp/modca/GraphicsObject.java index 9b870767e..05cdf5eb8 100644 --- a/src/java/org/apache/fop/afp/modca/GraphicsObject.java +++ b/src/java/org/apache/fop/afp/modca/GraphicsObject.java @@ -48,6 +48,7 @@ import org.apache.fop.afp.goca.GraphicsLine; import org.apache.fop.afp.goca.GraphicsSetArcParameters; import org.apache.fop.afp.goca.GraphicsSetCharacterSet; import org.apache.fop.afp.goca.GraphicsSetCurrentPosition; +import org.apache.fop.afp.goca.GraphicsSetFractionalLineWidth; import org.apache.fop.afp.goca.GraphicsSetLineType; import org.apache.fop.afp.goca.GraphicsSetLineWidth; import org.apache.fop.afp.goca.GraphicsSetPatternSymbol; @@ -182,8 +183,20 @@ public class GraphicsObject extends AbstractDataObject { * @param lineWidth the line width multiplier */ public void setLineWidth(int lineWidth) { - if (lineWidth != graphicsState.lineWidth) { + if ((float) lineWidth != graphicsState.lineWidth) { addObject(new GraphicsSetLineWidth(lineWidth)); + graphicsState.lineWidth = (float) lineWidth; + } + } + + /** + * Sets the line width + * + * @param lineWidth the line width multiplier + */ + public void setLineWidth(float lineWidth) { + if (lineWidth != graphicsState.lineWidth) { + addObject(new GraphicsSetFractionalLineWidth(lineWidth)); graphicsState.lineWidth = lineWidth; } } @@ -414,7 +427,7 @@ public class GraphicsObject extends AbstractDataObject { private byte lineType; /** the current line width */ - private int lineWidth; + private float lineWidth; /** the current fill pattern */ private byte patternSymbol; diff --git a/src/java/org/apache/fop/render/afp/AFPCustomizable.java b/src/java/org/apache/fop/render/afp/AFPCustomizable.java index 6eca86458..e0924c0d0 100644 --- a/src/java/org/apache/fop/render/afp/AFPCustomizable.java +++ b/src/java/org/apache/fop/render/afp/AFPCustomizable.java @@ -90,6 +90,13 @@ public interface AFPCustomizable { void setResolution(int resolution); /** + * Sets the line width correction + * + * @param correction the line width multiplying factor correction + */ + void setLineWidthCorrection(float correction); + + /** * Sets whether FS11 and FS45 non-inline images should be wrapped in a page segment * @param pSeg true iff images should be wrapped */ diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 0c778303c..7823a2ce6 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -443,6 +443,11 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } /** {@inheritDoc} */ + public void setLineWidthCorrection(float correction) { + paintingState.setLineWidthCorrection(correction); + } + + /** {@inheritDoc} */ public int getResolution() { return paintingState.getResolution(); } diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java index 36cdbd077..e93d8b6aa 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java @@ -29,6 +29,7 @@ import java.util.List; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.fop.afp.AFPConstants; import org.apache.fop.afp.AFPEventProducer; import org.apache.fop.afp.AFPResourceLevel; import org.apache.fop.afp.AFPResourceLevelDefaults; @@ -446,6 +447,13 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator customizable.setResolution(rendererResolutionCfg.getValueAsInteger(240)); } + // renderer resolution + Configuration lineWidthCorrectionCfg = cfg.getChild("line-width-correction", false); + if (lineWidthCorrectionCfg != null) { + customizable.setLineWidthCorrection(lineWidthCorrectionCfg + .getValueAsFloat(AFPConstants.LINE_WIDTH_CORRECTION)); + } + // a default external resource group file setting Configuration resourceGroupFileCfg = cfg.getChild("resource-group-file", false); diff --git a/status.xml b/status.xml index 5a5c2b2dd..9b63c1d73 100644 --- a/status.xml +++ b/status.xml @@ -63,6 +63,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> <release version="FOP Trunk" date="TBD"> + <action context="Renderers" dev="GA" type="fix" fixes-bug="53242" due-to="Luis Bernardo"> + Support fractional line widths in AFP renderer, fixing problem with SVG line drawing. + </action> <action context="Config" dev="GA" type="fix" fixes-bug="53248" due-to="Luis Bernardo"> Fix exception thrown from use of -print option in CLI. </action> diff --git a/test/java/org/apache/fop/afp/AFPGraphics2DTestCase.java b/test/java/org/apache/fop/afp/AFPGraphics2DTestCase.java new file mode 100644 index 000000000..7b261d482 --- /dev/null +++ b/test/java/org/apache/fop/afp/AFPGraphics2DTestCase.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.afp; + +import java.awt.BasicStroke; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.afp.modca.GraphicsObject; +import org.apache.fop.fonts.FontInfo; + +public class AFPGraphics2DTestCase { + + private final float lineWidth = 1.0f; + private final float correction = 2.5f; + private final BasicStroke stroke = mock(BasicStroke.class); + private final GraphicsObject gObject = mock(GraphicsObject.class); + private final AFPPaintingState paintingState = mock(AFPPaintingState.class); + private final AFPResourceManager resourceManager = mock(AFPResourceManager.class); + private final AFPResourceInfo resourceInfo = mock(AFPResourceInfo.class); + private final FontInfo fontInfo = mock(FontInfo.class); + private AFPGraphics2D graphics2D = new AFPGraphics2D(false, paintingState, resourceManager, resourceInfo, + fontInfo); + + @Test + public void testApplyStroke() { + // note: this only tests the setLineWidth in the GraphicsObject + float correctedLineWidth = lineWidth * correction; + when(stroke.getLineWidth()).thenReturn(lineWidth); + when(paintingState.getLineWidthCorrection()).thenReturn(correction); + graphics2D.setGraphicsObject(gObject); + graphics2D.applyStroke(stroke); + verify(gObject).setLineWidth(correctedLineWidth); + } + +} diff --git a/test/java/org/apache/fop/afp/goca/GraphicsSetFractionalLineWidthTestCase.java b/test/java/org/apache/fop/afp/goca/GraphicsSetFractionalLineWidthTestCase.java new file mode 100644 index 000000000..f34275de0 --- /dev/null +++ b/test/java/org/apache/fop/afp/goca/GraphicsSetFractionalLineWidthTestCase.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// $Id$ + +package org.apache.fop.afp.goca; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class GraphicsSetFractionalLineWidthTestCase { + + private final float multiplier = 5.25f; + private final GraphicsSetFractionalLineWidth gsflw = new GraphicsSetFractionalLineWidth(multiplier); + + @Test + public void testGetDataLength() { + assertEquals(4, gsflw.getDataLength()); + } + + @Test + public void testWriteToStream() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + gsflw.writeToStream(baos); + baos.close(); + // note: 0.25 = 64/256 and 64 = 4*16, so 0x40 + // expected: 0x11 (order code), 0x02 (2 bytes next), 0x05 (integral multiplier), 0x40 (fractional + // multiplier) + byte[] expected = new byte[] {0x11, 0x02, 0x05, 0x40}; + assertTrue(Arrays.equals(expected, baos.toByteArray())); + } + + @Test + public void testToString() { + // lets make sure we keep good coverage... + assertEquals("GraphicsSetFractionalLineWidth{multiplier=" + multiplier + "}", gsflw.toString()); + } + +} diff --git a/test/java/org/apache/fop/afp/goca/GraphicsSetLineWidthTestCase.java b/test/java/org/apache/fop/afp/goca/GraphicsSetLineWidthTestCase.java new file mode 100644 index 000000000..c0a18a551 --- /dev/null +++ b/test/java/org/apache/fop/afp/goca/GraphicsSetLineWidthTestCase.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// $Id$ + +package org.apache.fop.afp.goca; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class GraphicsSetLineWidthTestCase { + + private final int multiplier = 5; + private final GraphicsSetLineWidth gslw = new GraphicsSetLineWidth(multiplier); + + @Test + public void testGetDataLength() { + assertEquals(2, gslw.getDataLength()); + } + + @Test + public void testWriteToStream() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + gslw.writeToStream(baos); + baos.close(); + // expected: 0x19 (order code), 0x05 (integral multiplier) + byte[] expected = new byte[] {0x19, 0x05}; + assertTrue(Arrays.equals(expected, baos.toByteArray())); + } + + @Test + public void testToString() { + // lets make sure we keep good coverage... + assertEquals("GraphicsSetLineWidth{multiplier=" + multiplier + "}", gslw.toString()); + } + +} |