aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/fop/fo/flow/TableColumn.java10
-rw-r--r--src/java/org/apache/fop/render/RendererFactory.java3
-rw-r--r--src/java/org/apache/fop/render/txt/Helper.java117
-rw-r--r--src/java/org/apache/fop/render/txt/TXTHandler.java570
-rw-r--r--src/java/org/apache/fop/render/txt/TXTRenderer.java578
-rw-r--r--src/java/org/apache/fop/render/txt/TXTState.java137
-rw-r--r--src/java/org/apache/fop/render/txt/TXTStream.java4
-rw-r--r--src/java/org/apache/fop/render/txt/border/AbstractBorderElement.java148
-rw-r--r--src/java/org/apache/fop/render/txt/border/BorderManager.java164
-rw-r--r--src/java/org/apache/fop/render/txt/border/DashedBorderElement.java141
-rw-r--r--src/java/org/apache/fop/render/txt/border/DottedBorderElement.java45
-rw-r--r--src/java/org/apache/fop/render/txt/border/SolidAndDoubleBorderElement.java276
12 files changed, 2105 insertions, 88 deletions
diff --git a/src/java/org/apache/fop/fo/flow/TableColumn.java b/src/java/org/apache/fop/fo/flow/TableColumn.java
index 94cacf4e6..df3cd7663 100644
--- a/src/java/org/apache/fop/fo/flow/TableColumn.java
+++ b/src/java/org/apache/fop/fo/flow/TableColumn.java
@@ -25,7 +25,6 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.expr.PropertyException;
@@ -127,6 +126,14 @@ public class TableColumn extends TableFObj {
}
/**
+ * Sets the column width.
+ * @param columnWidth the column width
+ */
+ public void setColumnWidth(Length columnWidth) {
+ this.columnWidth = columnWidth;
+ }
+
+ /**
* @return the "column-number" property.
*/
public int getColumnNumber() {
@@ -176,5 +183,6 @@ public class TableColumn extends TableFObj {
sb.append(" column-width=").append(getColumnWidth());
return sb.toString();
}
+
}
diff --git a/src/java/org/apache/fop/render/RendererFactory.java b/src/java/org/apache/fop/render/RendererFactory.java
index dbd94119d..d4d1460bd 100644
--- a/src/java/org/apache/fop/render/RendererFactory.java
+++ b/src/java/org/apache/fop/render/RendererFactory.java
@@ -33,6 +33,7 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.render.mif.MIFHandler;
import org.apache.fop.render.rtf.RTFHandler;
+import org.apache.fop.render.txt.TXTHandler;
/**
* Factory for FOEventHandlers and Renderers.
@@ -132,6 +133,8 @@ public class RendererFactory {
return new MIFHandler(userAgent, out);
} else if (renderType == Constants.RENDER_RTF) {
return new RTFHandler(userAgent, out);
+ } else if (renderType == Constants.RENDER_TXT) {
+ return new TXTHandler(userAgent, out);
} else {
if (renderType < Constants.RENDER_MIN_CONST
|| renderType > Constants.RENDER_MAX_CONST) {
diff --git a/src/java/org/apache/fop/render/txt/Helper.java b/src/java/org/apache/fop/render/txt/Helper.java
new file mode 100644
index 000000000..fe44e4ec1
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/Helper.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt;
+
+/**
+ * This class has a few convenient static methods for number quantization.
+ */
+public final class Helper {
+
+ /**
+ * Don't let anyone instantiate this class.
+ */
+ private Helper() { }
+
+ /**
+ * Returns nearest integer to <code>x</code>, divisible by
+ * <code>quantum</code>.
+ *
+ * @param x integer for quantization
+ * @param quantum integer, representing quantization
+ * @return computed nearest integer
+ */
+ public static int round(int x, int quantum) {
+ int ceil = ceil(x, quantum);
+ int floor = floor(x, quantum);
+ return (ceil - x < x - floor) ? ceil : floor;
+ }
+
+ /**
+ * Returns minimal possible integer, greater or equal than
+ * <code>x</code>, divisible by <code>quantum</code>.
+ *
+ * @param x integer for quantization
+ * @param quantum integer, representing quantization
+ * @return computed nearest integer
+ */
+ public static int ceil(int x, int quantum) {
+ int dx = (x < 0) || (x % quantum == 0) ? 0 : 1;
+ return (x / quantum + dx) * quantum;
+ }
+
+ /**
+ * Returns maximum possible integer, less or equal than
+ * <code>oldValue</code>, divisible by <code>quantum</code>.
+ *
+ * @param x integer for quantization
+ * @param quantum integer, representing quantization
+ * @return computed nearest integer
+ */
+ public static int floor(int x, int quantum) {
+ int dx = (x > 0) || (x % quantum == 0) ? 0 : -1;
+ return (x / quantum + dx) * quantum;
+ }
+
+ /**
+ * Returns the closest integer to <code>x/y</code> fraction.
+ * It's possible to consider this methos as a analog of Math.round(x/y),
+ * without having deal with non-integer.
+ *
+ * @param x integer, fraction numerator
+ * @param y integer, fraction denominator
+ * @return the value of the fraction rounded to the nearest
+ * @see java.lang.Math#round(double)
+ */
+ public static int roundPosition(int x, int y) {
+ return round(x, y) / y;
+ }
+
+ /**
+ * Returns the smallest integer that is greater than or equal to the
+ * <code>x/y</code> fraction.
+ * It's possible to consider this function as a analog of Math.ceil(x/y),
+ * without having deal with non-integer.
+ *
+ * @param x integer, fraction numerator
+ * @param y integer, fraction denominator
+ * @return the smallest integer that is greater than or equal to
+ * <code>x/y</code> fraction
+ * @see java.lang.Math#ceil(double)
+ */
+ public static int ceilPosition(int x, int y) {
+ return ceil(x, y) / y;
+ }
+
+
+ /**
+ * Returns the largest integer that is less than or equal to the
+ * argument and is equal to <code>x/y</code> fraction.
+ * It's possible to consider this function as a analog of Math.floor(x/y),
+ * without having deal with non-integer.
+ *
+ * @param x integer, fraction numerator
+ * @param y integer, fraction denominator
+ * @return the largest integer that is less than or equal to
+ * the argument and is equal to <code>x/y</code> fraction
+ * @see java.lang.Math#ceil(double)
+ */
+ public static int floorPosition(int x, int y) {
+ return floor(x, y) / y;
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/TXTHandler.java b/src/java/org/apache/fop/render/txt/TXTHandler.java
new file mode 100644
index 000000000..d75e65f50
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/TXTHandler.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt;
+
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.area.AreaTreeHandler;
+import org.apache.fop.datatypes.CompoundDatatype;
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.datatypes.PercentBaseContext;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FOText;
+import org.apache.fop.fo.expr.NumericProperty;
+import org.apache.fop.fo.expr.RelativeNumericProperty;
+import org.apache.fop.fo.flow.Block;
+import org.apache.fop.fo.flow.BlockContainer;
+import org.apache.fop.fo.flow.ExternalGraphic;
+import org.apache.fop.fo.flow.Inline;
+import org.apache.fop.fo.flow.ListBlock;
+import org.apache.fop.fo.flow.ListItem;
+import org.apache.fop.fo.flow.PageNumber;
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.fo.properties.CommonAbsolutePosition;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.CommonFont;
+import org.apache.fop.fo.properties.CommonMarginBlock;
+import org.apache.fop.fo.properties.FixedLength;
+import org.apache.fop.fo.properties.Property;
+import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.layoutmgr.BlockLayoutManager;
+
+/**
+ * Handler for formatting objects in case of rendering to txt.
+ *
+ * This handler gets page-sequence, modifies formatting objects and return them
+ * to superclass. So areas are generated from modified FO. Idea of modifying is
+ * to quantize FO properties, making them divisible by width of char or height
+ * of char.
+ */
+public class TXTHandler extends AreaTreeHandler {
+
+ /** Percent base context. Needed for line-height. */
+ private static final PercentBaseContext CONTEXT
+ = new BlockLayoutManager(new Block(null));
+
+ /** Modified font size in millipoints. */
+ private static final int MODIFIED_FONT_SIZE = 10000;
+
+ /** Quantum for each side (BEFORE, AFTER, START, END). */
+ private final int[] quantum = {TXTRenderer.CHAR_HEIGHT,
+ TXTRenderer.CHAR_HEIGHT, TXTRenderer.CHAR_WIDTH,
+ TXTRenderer.CHAR_WIDTH};
+
+ /** Keeps overpatching for each side. */
+ private int[] overPatching = new int[4];
+
+ /**
+ * Keeps last overpatching for each side. Needed for selective modifying of
+ * start-indent and end-indent.
+ */
+ private int[] lastOverPatching = new int[4];
+
+ /**
+ * Constructs a newly allocated <code>TXTHandler</code> object.
+ *
+ * @param userAgent FOUserAgent
+ * @param stream OutputStream
+ * @throws FOPException if the RenderPagesModel cannot be created
+ */
+ public TXTHandler(FOUserAgent userAgent, OutputStream stream)
+ throws FOPException {
+ super(userAgent, Constants.RENDER_TXT, stream);
+ }
+
+ /**
+ * Sets a component <code>CP_LENGTH</code> of <code>cd</code> to
+ * <code>value</code>.
+
+ * @param cd CompoundDatatype
+ * @param value new integer value
+ */
+ private static void setLength(CompoundDatatype cd, int value) {
+ cd.setComponent(Constants.CP_LENGTH, new FixedLength(value), true);
+ }
+
+ /**
+ * Sets components <code>CP_MINIMUM, CP_OPTIMUM, CP_MAXIMUM</code> of
+ * <code>cd</code> to <code>p</code>.
+ *
+ * @param cd instance of CompoundDatatype for modifying.
+ * @param p property for setting.
+ */
+ private static void setMinOptMax(CompoundDatatype cd, Property p) {
+ cd.setComponent(Constants.CP_MINIMUM, p, true);
+ cd.setComponent(Constants.CP_OPTIMUM, p, true);
+ cd.setComponent(Constants.CP_MAXIMUM, p, true);
+ }
+
+ /**
+ * Modifies border of side. If there is no border of given side, does
+ * nothing, otherwise sets border-width to half of char width or char height
+ * depending on side. <p>
+ * Difference between values of new border-width and old border-width is
+ * saved in <code>lastOverPatching</code>.
+ *
+ * @param side side to modify.
+ * @param bpb instance of CommonBorderPaddingBackground for modifying.
+ */
+ private void modifyBorder(int side, CommonBorderPaddingBackground bpb) {
+ CommonBorderPaddingBackground.BorderInfo bi = bpb.getBorderInfo(side);
+
+ if (bi != null) {
+ int width = bpb.getBorderWidth(side, false);
+ setLength(bi.getWidth(), quantum[side] / 2);
+ lastOverPatching[side] += bpb.getBorderWidth(side, false) - width;
+ }
+ }
+
+ /**
+ * Modifies padding of side. First rounds padding to nearest integer,
+ * divisible by char width or char height depending on side. If border of
+ * given side is available, modifies padding in such a way, so sum of border
+ * width and padding will be divisible by char width or char height,
+ * depending on side. <p>
+ * Difference between values of new padding and old padding is saved
+ * in <code>lastOverPatching</code>.
+ *
+ * @param side side to modify.
+ * @param bpb instance of CommonBorderPaddingBackground for modifying.
+ */
+ private void modifyPadding(int side, CommonBorderPaddingBackground bpb) {
+ int oldPadding = bpb.getPadding(side, false, null);
+ int newPadding = Helper.round(oldPadding, quantum[side]);
+ if (bpb.getBorderInfo(side) != null) {
+ newPadding = Math.max(newPadding, quantum[side])
+ - bpb.getBorderWidth(side, false);
+ }
+
+ setLength(bpb.getPaddingLengthProperty(side), newPadding);
+ lastOverPatching[side] += newPadding - oldPadding;
+ }
+
+ /**
+ * Modifies borders and paddings of <code>bpb</code>.
+ *
+ * @param bpb instance of CommonBorderPaddingBackground for modifying.
+ */
+ private void modifyBPB(CommonBorderPaddingBackground bpb) {
+ modifyBorder(CommonBorderPaddingBackground.BEFORE, bpb);
+ modifyBorder(CommonBorderPaddingBackground.AFTER, bpb);
+ modifyBorder(CommonBorderPaddingBackground.START, bpb);
+ modifyBorder(CommonBorderPaddingBackground.END, bpb);
+
+ modifyPadding(CommonBorderPaddingBackground.BEFORE, bpb);
+ modifyPadding(CommonBorderPaddingBackground.AFTER, bpb);
+ modifyPadding(CommonBorderPaddingBackground.START, bpb);
+ modifyPadding(CommonBorderPaddingBackground.END, bpb);
+ }
+
+ /**
+ * Rounds optimum value of <code>space</code> to nearest integer,
+ * divisible by <code>q</code>.
+ *
+ * @param space instance of SpaceProperty.
+ * @param q integer.
+ */
+ private void modifySpace(SpaceProperty space, int q) {
+ int value = space.getOptimum(null).getLength().getValue();
+ setMinOptMax(space, new FixedLength(Helper.round(value, q)));
+ }
+
+ /**
+ * @param length instance of Length.
+ * @param q integer.
+ * @return instance of Length, having value nearest to value of
+ * <code>length</code>, and divisible by <code>q</code>.
+ */
+ private Length roundLength(Length length, int q) {
+ int x = Helper.round(length.getValue(), q);
+ return new FixedLength(x);
+ }
+
+ /**
+ * @param length instance of Length.
+ * @param q integer.
+ * @return instance of Length, having minimal value, greater value of
+ * <code>length</code>, and divisible by <code>q</code>.
+ */
+ private Length ceilLength(Length length, int q) {
+ int x = Helper.ceil(length.getValue(), q);
+ return new FixedLength(x);
+ }
+
+ /**
+ * Modifies indent for given side. Summarizes value of indent and modifing
+ * error (i.e. overPatching). Rounds result to nearest integer, divisible by
+ * quantum.
+ *
+ * @param indent Length to modify.
+ * @param side an integer, representing side.
+ * @return modified Length.
+ */
+ private Length modifyIndent(Length indent, int side) {
+ if (indent instanceof NumericProperty) {
+ overPatching[side] += lastOverPatching[side];
+ }
+ int newValue = indent.getValue() + overPatching[side];
+ newValue = Helper.round(newValue, quantum[side]);
+ return new FixedLength(newValue);
+ }
+
+ /**
+ * Modifies Common Margin Properties-Block:
+ * <ul>
+ * <li>margin-top, margin-left, margin-bottom, margin-right
+ * <li>start-indent, end-indent
+ * <li>space-before, space-after.
+ * </ul>
+ *
+ * @param cmb instance of CommonMarginBlock to modify.
+ */
+ private void modifyCommonMarginBlock(CommonMarginBlock cmb) {
+ cmb.marginTop = roundLength(cmb.marginTop, TXTRenderer.CHAR_HEIGHT);
+ cmb.marginBottom = roundLength(cmb.marginBottom,
+ TXTRenderer.CHAR_HEIGHT);
+ cmb.marginLeft = roundLength(cmb.marginLeft, TXTRenderer.CHAR_WIDTH);
+ cmb.marginRight = roundLength(cmb.marginRight, TXTRenderer.CHAR_WIDTH);
+
+ modifySpace(cmb.spaceBefore, TXTRenderer.CHAR_HEIGHT);
+ modifySpace(cmb.spaceAfter, TXTRenderer.CHAR_HEIGHT);
+
+ if (!(cmb.startIndent instanceof RelativeNumericProperty)) {
+ cmb.startIndent = modifyIndent(cmb.startIndent,
+ CommonBorderPaddingBackground.START);
+ }
+ if (!(cmb.endIndent instanceof RelativeNumericProperty)) {
+ cmb.endIndent = modifyIndent(cmb.endIndent,
+ CommonBorderPaddingBackground.END);
+ }
+ }
+
+ /**
+ * Modifies fo:table attributes:
+ * <ul>
+ * <li>Common Margin Properties Block
+ * <li>Common Border, Padding, and Background Properties
+ * <li>columns.
+ * </ul>
+ *
+ * @param table Table to modify.
+ */
+ private void modifyTable(Table table) {
+ CommonMarginBlock cmb = table.getCommonMarginBlock();
+ if (table.getBorderCollapse() == Constants.EN_COLLAPSE) {
+ // If border-collapse == "collapse", add space-after in order to
+ // impove interaction with other FO.
+ int value = cmb.spaceAfter.getOptimum(null).getLength().getValue();
+ value += TXTRenderer.CHAR_HEIGHT;
+ setMinOptMax(cmb.spaceAfter, new FixedLength(value));
+ }
+ modifyCommonMarginBlock(cmb);
+
+ modifyBPB(table.getCommonBorderPaddingBackground());
+
+ // modify all table-columns
+ List columnList = table.getColumns();
+ Iterator iter = columnList.iterator();
+ while (iter.hasNext()) {
+ modifyTableColumn((TableColumn) iter.next());
+ }
+ }
+
+ /**
+ * Modifies fo:table-column attributes:
+ * <ul>
+ * <li>width.
+ * </ul>
+ *
+ * @param column TableColumn to modify.
+ */
+ private void modifyTableColumn(TableColumn column) {
+ column.setColumnWidth(ceilLength(column.getColumnWidth(),
+ TXTRenderer.CHAR_WIDTH));
+ }
+
+ /**
+ * Modifies padding of fo:table-cell.
+ *
+ * @param side side.
+ * @param bpb instance of CommonBorderPaddingBackground to modify.
+ */
+ private void modifyCellPadding(int side, CommonBorderPaddingBackground bpb) {
+ if (bpb.getBorderInfo(side) == null) {
+ int oldPadding = bpb.getPadding(side, false, null);
+ int newPadding = oldPadding + quantum[side] / 2;
+ setLength(bpb.getPaddingLengthProperty(side), newPadding);
+ }
+ }
+
+ /**
+ * Modifies table-cell properties:
+ * <ul>
+ * <li>Common Border, Padding, and Background Properties.
+ * </ul>
+ *
+ * @param c TableCell to modify.
+ */
+ private void modifyTableCell(TableCell c) {
+ CommonBorderPaddingBackground bpb = c
+ .getCommonBorderPaddingBackground();
+ modifyBPB(bpb);
+ modifyCellPadding(CommonBorderPaddingBackground.BEFORE, bpb);
+ modifyCellPadding(CommonBorderPaddingBackground.AFTER, bpb);
+ modifyCellPadding(CommonBorderPaddingBackground.START, bpb);
+ modifyCellPadding(CommonBorderPaddingBackground.END, bpb);
+ }
+
+ /**
+ * Modifies Common Absolute Position Properties:
+ * <ul>
+ * <li>left
+ * <li>top.
+ * </ul>
+ *
+ * @param cap CommonAbsolutePosition to modify.
+ */
+ private void modifyCommonAbsolutePosition(CommonAbsolutePosition cap) {
+ if (cap.absolutePosition == Constants.EN_ABSOLUTE) {
+ cap.left = roundLength(cap.left, TXTRenderer.CHAR_WIDTH);
+ cap.top = roundLength(cap.top, TXTRenderer.CHAR_HEIGHT);
+ }
+ }
+
+ /**
+ * Modifies line-height property. Sets a value of line-height to max(char
+ * height; lowest integer, divisible by char height).
+ *
+ * @param lineHeight SpaceProperty to modify.
+ */
+ private void modifyLineHeight(SpaceProperty lineHeight) {
+ Property p = lineHeight.getOptimum(null);
+ int value = p.getLength().getValue(CONTEXT);
+
+ int height = TXTRenderer.CHAR_HEIGHT;
+ int newValue = Math.max(Helper.floor(value, height), height);
+ setMinOptMax(lineHeight, new FixedLength(newValue));
+ }
+
+ /**
+ * Modifies Common Font Properties:
+ * <ul>
+ * <li>font-family = Courier;
+ * <li>font-size = MODIFIED_FONT_SIZE;
+ * <li>font-stretch = EN_NORMAL;
+ * <li>font-weight = EN_NORMAL.
+ * </ul>
+ *
+ * @param cf the font to modify.
+ */
+ private void modifyCommonFont(CommonFont cf) {
+ if (cf != null) {
+ cf.fontFamily = "Courier";
+ cf.fontSize = new FixedLength(MODIFIED_FONT_SIZE);
+ cf.fontStretch = Constants.EN_NORMAL;
+ cf.fontWeight = Constants.EN_NORMAL;
+ }
+ }
+
+ /**
+ * Modifies fo:block:
+ * <ul>
+ * <li>Common Border, Padding, and Background Properties
+ * <li>Common Margin Properties-Block
+ * <li>Common Font Properties
+ * <li>line-height.
+ * </ul>
+ *
+ * @param block Block to modify.
+ */
+ private void modifyBlock(Block block) {
+ modifyBPB(block.getCommonBorderPaddingBackground());
+ modifyCommonMarginBlock(block.getCommonMarginBlock());
+ modifyCommonFont(block.getCommonFont());
+ modifyLineHeight(block.getLineHeight());
+ }
+
+ /**
+ * Modifies fo:block-container:
+ * <ul>
+ * <li>Common Border, Padding, and Background Properties
+ * <li>Common Margin Properties-Block
+ * <li>Common Absolute Position Properties.
+ * </ul>
+ *
+ * @param bc BlockContainer to modify.
+ */
+ private void modifyBlockContainer(BlockContainer bc) {
+ modifyBPB(bc.getCommonBorderPaddingBackground());
+ modifyCommonMarginBlock(bc.getCommonMarginBlock());
+ modifyCommonAbsolutePosition(bc.getCommonAbsolutePosition());
+ }
+
+ /**
+ * Modifies fo:inline:
+ * <ul>
+ * <li>Common Font Properties
+ * </ul>
+ *
+ * @param inline Inline to modify.
+ */
+ private void modifyInline(Inline inline) {
+ modifyCommonFont(inline.getCommonFont());
+ }
+
+ /**
+ * Modifies FOText:
+ * <ul>
+ * <li>Common Font Properties
+ * </ul>
+ *
+ * @param text FOText to modify.
+ */
+ private void modifyFOText(FOText text) {
+ modifyCommonFont(text.getCommonFont());
+ }
+
+ /**
+ * Modifies fo:external-graphic:
+ * <ul>
+ * <li>Common Border, Padding, and Background Properties
+ * <li>line-height.
+ * </ul>
+ *
+ * @param eg ExternalGraphic to modify.
+ */
+ private void modifyExternalGraphic(ExternalGraphic eg) {
+ modifyBPB(eg.getCommonBorderPaddingBackground());
+ modifyLineHeight(eg.getLineHeight());
+ }
+
+ /**
+ * Modifies fo:list-block:
+ * <ul>
+ * <li>Common Border, Padding, and Background Properties
+ * <li>Common Margin Properties-Block.
+ * </ul>
+ *
+ * @param lb ListBlock to modify.
+ */
+ private void modifyListBlock(ListBlock lb) {
+ modifyBPB(lb.getCommonBorderPaddingBackground());
+ modifyCommonMarginBlock(lb.getCommonMarginBlock());
+ }
+
+ /**
+ * Modifies fo:list-item:
+ * <ul>
+ * <li>Common Border, Padding, and Background Properties
+ * <li>Common Margin Properties-Block.
+ * </ul>
+ * <p>
+ * Make refinement for fo:list-item-label and fo:list-item-body.
+ *
+ * @param li ListItem to modify.
+ */
+ private void modifyListItem(ListItem li) {
+ modifyBPB(li.getCommonBorderPaddingBackground());
+ modifyCommonMarginBlock(li.getCommonMarginBlock());
+ refinement(li.getLabel());
+ refinement(li.getBody());
+ }
+
+ /**
+ * Does refinement for particular node. Modifies node's properties and
+ * refines its children recursively.
+ *
+ * @param node the node to refine.
+ */
+ private void refinement(FONode node) {
+ int[] saveOverPatching = (int[]) overPatching.clone();
+ Arrays.fill(lastOverPatching, 0);
+
+ if (node instanceof Block) {
+ modifyBlock((Block) node);
+ } else if (node instanceof BlockContainer) {
+ modifyBlockContainer((BlockContainer) node);
+ } else if (node instanceof Inline) {
+ modifyInline((Inline) node);
+ } else if (node instanceof FOText) {
+ modifyFOText((FOText) node);
+ } else if (node instanceof Table) {
+ modifyTable((Table) node);
+ Arrays.fill(overPatching, 0);
+ } else if (node instanceof TableCell) {
+ modifyTableCell((TableCell) node);
+ } else if (node instanceof ExternalGraphic) {
+ modifyExternalGraphic((ExternalGraphic) node);
+ } else if (node instanceof ListBlock) {
+ modifyListBlock((ListBlock) node);
+ } else if (node instanceof ListItem) {
+ modifyListItem((ListItem) node);
+ } else if (node instanceof PageNumber) {
+ modifyCommonFont(((PageNumber) node).getCommonFont());
+ }
+
+ Iterator it = node.getChildNodes();
+ if (it != null) {
+ while (it.hasNext()) {
+ refinement((FONode) it.next());
+ }
+ }
+ overPatching = saveOverPatching;
+ }
+
+ /**
+ * Run refinement for:
+ * <ul>
+ * <li>mainflow (xsl-region-body)
+ * <li>staticflow (xsl-region-before, xsl-region-after, xsl-region-start,
+ * xsl-region-end).
+ * </ul>
+ *
+ * @param pageSequence PageSequence to refine.
+ */
+ public void endPageSequence(PageSequence pageSequence) {
+ Arrays.fill(overPatching, 0);
+
+ refinement(pageSequence.getMainFlow());
+
+ if (pageSequence.getStaticContent("xsl-region-before") != null) {
+ refinement(pageSequence.getStaticContent("xsl-region-before"));
+ }
+ if (pageSequence.getStaticContent("xsl-region-after") != null) {
+ refinement(pageSequence.getStaticContent("xsl-region-after"));
+ }
+ if (pageSequence.getStaticContent("xsl-region-start") != null) {
+ refinement(pageSequence.getStaticContent("xsl-region-start"));
+ }
+ if (pageSequence.getStaticContent("xsl-region-end") != null) {
+ refinement(pageSequence.getStaticContent("xsl-region-end"));
+ }
+
+ super.endPageSequence(pageSequence);
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/TXTRenderer.java b/src/java/org/apache/fop/render/txt/TXTRenderer.java
index 0f9dda7fa..a06c815ea 100644
--- a/src/java/org/apache/fop/render/txt/TXTRenderer.java
+++ b/src/java/org/apache/fop/render/txt/TXTRenderer.java
@@ -18,122 +18,530 @@
package org.apache.fop.render.txt;
-// FOP
-import org.apache.fop.render.PrintRenderer;
-import org.apache.fop.render.pcl.PCLStream;
+import java.awt.Point;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.CTM;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.datatypes.ColorType;
+import org.apache.fop.render.AbstractPathOrientedRenderer;
+import org.apache.fop.render.txt.border.AbstractBorderElement;
+import org.apache.fop.render.txt.border.BorderManager;
/**
- * Renderer that renders areas to plain text
- *
- * Created by Arthur E Welch III while at M&I EastPoint Technology
- * Modified by Mark Lillywhite mark-fop@inomial.com to use the new
- * Renderer interface.
+ * Renderer that renders areas to plain text.
+ *
+ * @author Art Welch
+ * @author <a href="mailto:mark-fop@inomial.com">Mark Lillywhite</a> (to use
+ * the new Renderer interface)
*/
-public class TXTRenderer extends PrintRenderer {
+public class TXTRenderer extends AbstractPathOrientedRenderer {
+
+ private static final char LIGHT_SHADE = '\u2591';
+
+ private static final char MEDIUM_SHADE = '\u2592';
+
+ private static final char DARK_SHADE = '\u2593';
- /** The MIME type for PostScript */
- public static final String MIME_TYPE = "text/plain";
+ private static final char FULL_BLOCK = '\u2588';
- /**
- * the current stream to add Text commands to
- */
- private PCLStream currentStream;
+ private static final char IMAGE_CHAR = '#';
- private int pageHeight = 7920;
+ /**The stream for output */
+ private OutputStream outputStream;
- // These variables control the virtual paggination functionality.
- private int curdiv = 0;
- private int divisions = -1;
- private int paperheight = -1; // Paper height in decipoints?
- private int orientation = -1; // -1=default/unknown, 0=portrait, 1=landscape.
- private int topmargin = -1; // Top margin in decipoints?
- private int leftmargin = -1; // Left margin in decipoints?
- private int fullmargin = 0;
- private final boolean debug = false;
+ /** The current stream to add Text commands to. */
+ private TXTStream currentStream;
- // Variables for rendering text.
- private StringBuffer charData[];
- private StringBuffer decoData[];
- private float textCPI = 16.67f;
- private float textLPI = 8;
- private int maxX = (int)(8.5f * textCPI + 1);
- private int maxY = (int)(11f * textLPI + 1);
- private float xFactor;
- private float yFactor;
- /**
- * Every line except the last line on a page (which will end with
+ /** Buffer for text. */
+ private StringBuffer[] charData;
+
+ /** Buffer for background and images. */
+ private StringBuffer[] decoData;
+
+ /** Height of one symbol in Courier font size of 10pt. */
+ public static final int CHAR_HEIGHT = 7860;
+
+ /** Width of one symbol in Courier font size of 10pt. */
+ public static final int CHAR_WIDTH = 6000;
+
+ /** Current processing page width. */
+ private int pageWidth;
+
+ /** Current processing page height. */
+ private int pageHeight;
+
+ /**
+ * Every line except the last line on a page (which will end with
* pageEnding) will be terminated with this string.
*/
private String lineEnding = "\r\n";
- /**
- * Every page except the last one will end with this string.
- */
+
+ /** Every page except the last one will end with this string. */
private String pageEnding = "\f";
+
+ /** Equals true, if current page is first. */
+ private boolean firstPage = false;
+
+ /** Manager for storing border's information. */
+ private BorderManager bm;
+
+ /** Char for current filling. */
+ private char fillChar;
+
+ /** Saves current coordinate transformation. */
+ private TXTState currentState = new TXTState();
+
/**
- * If true then graphics/decorations will not be rendered - text only.
+ * Constructs a newly allocated <code>TXTRenderer</code> object.
*/
- private boolean suppressGraphics = false;
- private boolean firstPage = false;
+ public TXTRenderer() {
+ }
- private void addStr(int row, int col, String str, boolean ischar) {
- if (debug) {
- log.debug("TXTRenderer.addStr(" + row + ", " + col
- + ", \"" + str + "\", " + ischar + ")");
- }
- if (suppressGraphics && !ischar) {
- return;
+ /**
+ * Indicates if point (x, y) lay inside currentPage.
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ * @return <b>true</b> if point lay inside page
+ */
+ public boolean isLayInside(int x, int y) {
+ return (x >= 0) && (x < pageWidth) && (y >= 0) && (y < pageHeight);
+ }
+
+ /**
+ * Add char to text buffer.
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ * @param ch char to add
+ * @param ischar boolean, repersenting is character adding to text buffer
+ */
+ protected void addChar(int x, int y, char ch, boolean ischar) {
+ Point point = currentState.transformPoint(x, y);
+ putChar(point.x, point.y, ch, ischar);
+ }
+
+ /**
+ * Add char to text or background buffer.
+ *
+ * @param x x coordinate
+ * @param y x coordinate
+ * @param ch char to add
+ * @param ischar indicates if it char or background
+ */
+ protected void putChar(int x, int y, char ch, boolean ischar) {
+ if (isLayInside(x, y)) {
+ StringBuffer sb = ischar ? charData[y] : decoData[y];
+ while (sb.length() <= x) {
+ sb.append(' ');
+ }
+ sb.setCharAt(x, ch);
}
- StringBuffer sb;
- if (row < 0) {
- row = 0;
+ }
+
+ /**
+ * Adds string to text buffer (<code>charData</code>). <p>
+ * Chars of string map in turn.
+ *
+ * @param row x coordinate
+ * @param col y coordinate
+ * @param s string to add
+ */
+ protected void addString(int row, int col, String s) {
+ for (int l = 0; l < s.length(); l++) {
+ addChar(col + l, row, s.charAt(l), true);
}
- if (ischar) {
- sb = charData[row];
+ }
+
+ /**
+ * Render TextArea to Text.
+ *
+ * @param area inline area to render
+ */
+ protected void renderText(TextArea area) {
+ int col = Helper.ceilPosition(this.currentIPPosition, CHAR_WIDTH);
+ int row = Helper.ceilPosition(this.currentBPPosition, CHAR_HEIGHT);
+
+ String s = area.getText();
+
+ addString(row, col, s);
+
+ super.renderText(area);
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
+ */
+ public void renderPage(PageViewport page) throws IOException, FOPException {
+ if (firstPage) {
+ firstPage = false;
} else {
- sb = decoData[row];
+ currentStream.add(pageEnding);
}
- if (sb == null) {
- sb = new StringBuffer();
+
+ Rectangle2D bounds = page.getViewArea();
+ double width = bounds.getWidth();
+ double height = bounds.getHeight();
+
+ pageWidth = Helper.ceilPosition((int) width, CHAR_WIDTH);
+ pageHeight = Helper.ceilPosition((int) height, CHAR_HEIGHT);
+
+ // init buffers
+ charData = new StringBuffer[pageHeight];
+ decoData = new StringBuffer[pageHeight];
+ for (int i = 0; i < pageHeight; i++) {
+ charData[i] = new StringBuffer();
+ decoData[i] = new StringBuffer();
}
- if ((col + str.length()) > maxX) {
- col = maxX - str.length();
+
+ bm = new BorderManager(pageWidth, pageHeight, currentState);
+
+ super.renderPage(page);
+
+ flushBorderToBuffer();
+ flushBuffer();
+ }
+
+ /**
+ * Projects current page borders (i.e.<code>bm</code>) to buffer for
+ * background and images (i.e.<code>decoData</code>).
+ */
+ private void flushBorderToBuffer() {
+ for (int x = 0; x < pageWidth; x++) {
+ for (int y = 0; y < pageHeight; y++) {
+ Character c = bm.getCharacter(x, y);
+ if (c != null) {
+ putChar(x, y, c.charValue(), false);
+ }
+ }
}
- if (col < 0) {
- col = 0;
- if (str.length() > maxX) {
- str = str.substring(0, maxX);
+ }
+
+ /**
+ * Write out the buffer to output stream.
+ */
+ private void flushBuffer() {
+ for (int row = 0; row < pageHeight; row++) {
+ StringBuffer cr = charData[row];
+ StringBuffer dr = decoData[row];
+ StringBuffer outr = null;
+
+ if (cr != null && dr == null) {
+ outr = cr;
+ } else if (dr != null && cr == null) {
+ outr = dr;
+ } else if (cr != null && dr != null) {
+ int len = dr.length();
+ if (cr.length() > len) {
+ len = cr.length();
+ }
+ outr = new StringBuffer();
+ for (int countr = 0; countr < len; countr++) {
+ if (countr < cr.length() && cr.charAt(countr) != ' ') {
+ outr.append(cr.charAt(countr));
+ } else if (countr < dr.length()) {
+ outr.append(dr.charAt(countr));
+ } else {
+ outr.append(' ');
+ }
+ }
+ }
+
+ if (outr != null) {
+ currentStream.add(outr.toString());
+ }
+ if (row < pageHeight) {
+ currentStream.add(lineEnding);
}
}
- // Pad to col
- for (int countr = sb.length(); countr < col; countr++) {
- sb.append(' ');
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream)
+ */
+ public void startRenderer(OutputStream os) throws IOException {
+ log.info("Rendering areas to TEXT.");
+ this.outputStream = os;
+ currentStream = new TXTStream(os);
+ firstPage = true;
+ }
+
+ /**
+ * @see org.apache.fop.render.Renderer#stopRenderer()
+ */
+ public void stopRenderer() throws IOException {
+ log.info("writing out TEXT");
+ outputStream.flush();
+ super.stopRenderer();
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void restoreStateStackAfterBreakOut(List breakOutList) {
+ }
+
+ /**
+ * Does nothing.
+ * @return null
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected List breakOutOfStateStack() {
+ return null;
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void saveGraphicsState() {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void restoreGraphicsState() {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void beginTextObject() {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void endTextObject() {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void clip() {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void clipRect(float x, float y, float width, float height) {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void moveTo(float x, float y) {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void lineTo(float x, float y) {
+ }
+
+ /**
+ * Does nothing.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void closePath() {
+ }
+
+ /**
+ * Fills rectangle startX, startY, width, height with char
+ * <code>charToFill</code>.
+ *
+ * @param startX x-coordinate of upper left point
+ * @param startY y-coordinate of upper left point
+ * @param width width of rectangle
+ * @param height height of rectangle
+ * @param charToFill filling char
+ */
+ private void fillRect(int startX, int startY, int width, int height,
+ char charToFill) {
+ for (int x = startX; x < startX + width; x++) {
+ for (int y = startY; y < startY + height; y++) {
+ addChar(x, y, charToFill, false);
+ }
+ }
+ }
+
+ /**
+ * Fills a rectangular area with the current filling char.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void fillRect(float x, float y, float width, float height) {
+ fillRect(bm.getStartX(), bm.getStartY(), bm.getWidth(), bm.getHeight(),
+ fillChar);
+ }
+
+ /**
+ * Changes current filling char.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void updateColor(ColorType col, boolean fill) {
+ if (col == null) {
+ return;
}
- if (debug) {
- log.debug("TXTRenderer.addStr() sb.length()="
- + sb.length());
+ // fillShade evaluation was taken from fop-0.20.5
+ double fillShade = 0.30f * col.getRed()
+ + 0.59f * col.getGreen()
+ + 0.11f * col.getBlue();
+ fillShade = 1 - fillShade;
+
+ if (fillShade > 0.8f) {
+ fillChar = FULL_BLOCK;
+ } else if (fillShade > 0.6f) {
+ fillChar = DARK_SHADE;
+ } else if (fillShade > 0.4f) {
+ fillChar = MEDIUM_SHADE;
+ } else if (fillShade > 0.2f) {
+ fillChar = LIGHT_SHADE;
+ } else {
+ fillChar = ' ';
}
- for (int countr = col; countr < (col + str.length()); countr++) {
- if (countr >= sb.length()) {
- sb.append(str.charAt(countr - col));
- } else {
- if (debug) {
- log.debug("TXTRenderer.addStr() sb.length()="
- + sb.length() + " countr=" + countr);
- }
- sb.setCharAt(countr, str.charAt(countr - col));
- }
+ }
+
+ /**
+ * Does nothing.
+ * @param url String
+ * @param pos Rectangle2D
+ */
+ protected void drawImage(String url, Rectangle2D pos) {
+ }
+
+ /**
+ * Fills image rectangle with a <code>IMAGE_CHAR</code>.
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ public void renderImage(Image image, Rectangle2D pos) {
+ int x1 = Helper.ceilPosition(currentIPPosition, CHAR_WIDTH);
+ int y1 = Helper.ceilPosition(currentBPPosition, CHAR_HEIGHT);
+ int width = Helper.ceilPosition((int) pos.getWidth(), CHAR_WIDTH);
+ int height = Helper.ceilPosition((int) pos.getHeight(), CHAR_HEIGHT);
+
+ fillRect(x1, y1, width, height, IMAGE_CHAR);
+ }
+
+
+ /**
+ * Returns the closest integer to the multiplication of a number and 1000.
+ *
+ * @param x the value of the argument, multiplied by
+ * 1000 and rounded
+ * @return the value of the argument multiplied by
+ * 1000 and rounded to the nearest integer
+ */
+ protected int toMilli(float x) {
+ return Math.round(x * 1000f);
+ }
+
+ /**
+ * Adds one element of border.
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ * @param style integer, representing border style
+ * @param type integer, representing border element type
+ */
+ private void addBitOfBorder(int x, int y, int style, int type) {
+ Point point = currentState.transformPoint(x, y);
+ if (isLayInside(point.x, point.y)) {
+ bm.addBorderElement(point.x, point.y, style, type);
}
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void drawBorderLine(float x1, float y1, float x2, float y2,
+ boolean horz, boolean startOrBefore, int style, ColorType col) {
- if (ischar) {
- charData[row] = sb;
+ int borderHeight = bm.getHeight();
+ int borderWidth = bm.getWidth();
+ int borderStartX = bm.getStartX();
+ int borderStartY = bm.getStartY();
+
+ int x, y;
+ if (horz && startOrBefore) { // BEFORE
+ x = borderStartX;
+ y = borderStartY;
+ } else if (horz && !startOrBefore) { // AFTER
+ x = borderStartX;
+ y = borderStartY + borderHeight - 1;
+ } else if (!horz && startOrBefore) { // START
+ x = borderStartX;
+ y = borderStartY;
+ } else { // END
+ x = borderStartX + borderWidth - 1;
+ y = borderStartY;
+ }
+
+ int dx, dy, length, startType, endType;
+ if (horz) {
+ length = borderWidth;
+ dx = 1;
+ dy = 0;
+ startType = 1 << AbstractBorderElement.RIGHT;
+ endType = 1 << AbstractBorderElement.LEFT;
} else {
- decoData[row] = sb;
+ length = borderHeight;
+ dx = 0;
+ dy = 1;
+ startType = 1 << AbstractBorderElement.DOWN;
+ endType = 1 << AbstractBorderElement.UP;
+ }
+
+ addBitOfBorder(x, y, style, startType);
+ for (int i = 0; i < length - 2; i++) {
+ x += dx;
+ y += dy;
+ addBitOfBorder(x, y, style, startType + endType);
}
+ x += dx;
+ y += dy;
+ addBitOfBorder(x, y, style, endType);
}
- /** @see org.apache.fop.render.AbstractRenderer */
- public String getMimeType() {
- return MIME_TYPE;
+ /**
+ * @see org.apache.fop.render.AbstractPathOrientedRenderer
+ */
+ protected void drawBackAndBorders(Area area, float startx, float starty,
+ float width, float height) {
+ bm.setWidth(Helper.ceilPosition(toMilli(width), CHAR_WIDTH));
+ bm.setHeight(Helper.ceilPosition(toMilli(height), CHAR_HEIGHT));
+ bm.setStartX(Helper.ceilPosition(toMilli(startx), CHAR_WIDTH));
+ bm.setStartY(Helper.ceilPosition(toMilli(starty), CHAR_HEIGHT));
+
+ super.drawBackAndBorders(area, startx, starty, width, height);
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM)
+ */
+ protected void startVParea(CTM ctm) {
+ currentState.push(ctm);
}
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#endVParea()
+ */
+ protected void endVParea() {
+ currentState.pop();
+ }
}
diff --git a/src/java/org/apache/fop/render/txt/TXTState.java b/src/java/org/apache/fop/render/txt/TXTState.java
new file mode 100644
index 000000000..b63f04922
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/TXTState.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt;
+
+import java.awt.Point;
+import java.awt.geom.Rectangle2D;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.apache.fop.area.CTM;
+
+/**
+ * This keeps information about the current state when writing to txt, i.e.
+ * manages coordinate transformation matrices for getting absolute coordinates.
+ */
+public class TXTState {
+
+ /** Keeps all coordinate transformation matrices during rendering. */
+ private LinkedList stackCTM = new LinkedList();
+
+ /**
+ * Current result coordinate transformation matrix. It's product of
+ * all matrices in order, saved in <code>stackCTM</code>.
+ */
+ private CTM resultCTM = new CTM();
+
+ /**
+ * Constructs a newly allocated <code>TXTState</code> object.
+ */
+ public TXTState() {
+ }
+
+ /**
+ * Updates result coordinate transformation matrix
+ * (i.e. <code>resultCTM</code>), multipliing it by given matrix.
+ *
+ * @param ctm CTM
+ */
+ private void updateResultCTM(CTM ctm) {
+ resultCTM = resultCTM.multiply(ctm);
+ }
+
+ /**
+ * Recalculate current result coordinate transformation matrix.
+ */
+ private void calcResultCTM() {
+ resultCTM = new CTM();
+ for (Iterator i = stackCTM.iterator(); i.hasNext();) {
+ updateResultCTM((CTM) i.next());
+ }
+ }
+
+ /**
+ * Push the current coordinate transformation matrix onto the stack and
+ * reevaluate <code>resultCTM</code>.
+ *
+ * @param ctm instance of CTM
+ */
+ public void push(CTM ctm) {
+ stackCTM.addLast(ctm);
+ updateResultCTM(ctm);
+ }
+
+ /**
+ * Pop the coordinate transformation matrix from the stack and reevaluate
+ * <code>resultCTM</code>.
+ */
+ public void pop() {
+ stackCTM.removeLast();
+ calcResultCTM();
+ }
+
+ /**
+ * Modifies coordinate transformation matrix in such a way, so
+ * x-shift and y-shift will be transformed in text positions.
+ *
+ * @param ctm CTM to modify
+ * @return instance of CTM
+ */
+ public CTM refineCTM(CTM ctm) {
+ double[] da = ctm.toArray();
+ // refine x-shift
+ da[4] = Helper.roundPosition((int) da[4], TXTRenderer.CHAR_WIDTH);
+ // refine y-shift
+ da[5] = Helper.roundPosition((int) da[5], TXTRenderer.CHAR_HEIGHT);
+
+ return new CTM(da[0], da[1], da[2], da[3], da[4], da[5]);
+ }
+
+ /**
+ * Transforms <code>point</code> using <code>ctm</code>.
+ *
+ * @param p Point
+ * @param ctm CTM
+ * @return transformed Point
+ */
+ public Point transformPoint(Point p, CTM ctm) {
+ Rectangle2D r = new Rectangle2D.Double(p.x, p.y, 0, 0);
+ CTM nctm = refineCTM(ctm);
+ r = nctm.transform(r);
+ return new Point((int) r.getX(), (int) r.getY());
+ }
+
+ /**
+ * Transforms point (x, y) using <code>resultCTM</code>.
+ *
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @return transformed Point
+ */
+ public Point transformPoint(int x, int y) {
+ return transformPoint(new Point(x, y), resultCTM);
+ }
+
+ /**
+ * @return current result coordinate transformation matrix
+ */
+ public CTM getResultCTM() {
+ return resultCTM;
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/TXTStream.java b/src/java/org/apache/fop/render/txt/TXTStream.java
index df38cd10e..f3dc9e194 100644
--- a/src/java/org/apache/fop/render/txt/TXTStream.java
+++ b/src/java/org/apache/fop/render/txt/TXTStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ public class TXTStream {
}
try {
- byte buff[] = str.getBytes("UTF-8");
+ byte[] buff = str.getBytes("UTF-8");
out.write(buff);
} catch (IOException e) {
throw new RuntimeException(e.toString());
diff --git a/src/java/org/apache/fop/render/txt/border/AbstractBorderElement.java b/src/java/org/apache/fop/render/txt/border/AbstractBorderElement.java
new file mode 100644
index 000000000..d02a8da1b
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/border/AbstractBorderElement.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt.border;
+
+import java.awt.Point;
+import java.util.Arrays;
+
+import org.apache.fop.area.CTM;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.txt.TXTState;
+
+/**
+ * This class keeps information about abstract border element, i.e. specifies
+ * border element for one text position.
+ */
+public abstract class AbstractBorderElement implements Constants {
+
+ /**
+ * Constant for a line segment, directing from a center of symbol up
+ * the the symbol boundary.
+ */
+ public static final int UP = 0;
+
+ /**
+ * Constant for a line segment, directing from a center of symbol right
+ * the the symbol boundary.
+ */
+ public static final int RIGHT = 1;
+
+ /**
+ * Constant for a line segment, directing from a center of symbol down
+ * the the symbol boundary.
+ */
+ public static final int DOWN = 2;
+
+ /**
+ * Constant for a line segment, directing from a center of symbol left
+ * the the symbol boundary.
+ */
+ public static final int LEFT = 3;
+
+ /**
+ * I-th element of this array specify, if there line from center of symbol
+ * to corresponding side (UP, RIGHT, DOWN, LEFT).
+ */
+ protected int[] data = {0, 0, 0, 0};
+
+ /**
+ * Initializes a newly created <code>AbstractBorderElement</code> object
+ * so that it represents an empty border element.
+ */
+ public AbstractBorderElement() {
+ }
+
+ /**
+ * Constructs a newly allocated <code>AbstractBorderElement</code> object.
+ * Fills array <code>data</code> using binary representation of
+ * <code>type</code>.
+ *
+ * @param type binary representation of type gives <code>data</code>
+ */
+ public AbstractBorderElement(int type) {
+ for (int i = 0; i < 4; i++) {
+ data[i] = (type >> i) & 1;
+ }
+ }
+
+ /**
+ * Returns value of side's element of <code>data</code>.
+ *
+ * @param side integer, representing side
+ * @return value of side's element
+ */
+ public int getData(int side) {
+ return data[side];
+ }
+
+ /**
+ * Sets a value for <code>data[side]</code>.
+ *
+ * @param side integer, representing side
+ * @param value a new value for <code>data[side]</code>
+ */
+ public void setData(int side, int value) {
+ data[side] = value;
+ }
+
+ /**
+ * Transform border element in according with <code>state</code>.
+ * @param state instance of TXTState
+ */
+ public void transformElement(TXTState state) {
+ // here we'll get CTM^-1 without shift
+ double[] da = state.getResultCTM().toArray();
+ CTM ctm = new CTM(da[0], -da[1], -da[2], da[3], 0, 0);
+
+ Point[] pa = new Point[4];
+ pa[0] = new Point(0, data[UP]);
+ pa[1] = new Point(data[RIGHT], 0);
+ pa[2] = new Point(0, -data[DOWN]);
+ pa[3] = new Point(-data[LEFT], 0);
+
+ Arrays.fill(data, 0);
+ for (int i = 0; i < 4; i++) {
+ Point p = state.transformPoint(pa[i], ctm);
+
+ int length = (int) p.distance(0, 0);
+ if (p.x == 0 && p.y > 0) {
+ data[UP] = length;
+ } else if (p.x == 0 && p.y < 0) {
+ data[DOWN] = length;
+ } else if (p.x > 0 && p.y == 0) {
+ data[RIGHT] = length;
+ } else if (p.x < 0 && p.y == 0) {
+ data[LEFT] = length;
+ }
+ }
+ }
+
+ /**
+ * Merges with border element.
+ * @param e instance of AbstractBorderElement
+ * @return instance of AbstractBorderElement
+ */
+ public abstract AbstractBorderElement merge(AbstractBorderElement e);
+
+ /**
+ * Convert internal representation of border element to char.
+ * @return corresponding char
+ */
+ public abstract char convert2Char();
+}
diff --git a/src/java/org/apache/fop/render/txt/border/BorderManager.java b/src/java/org/apache/fop/render/txt/border/BorderManager.java
new file mode 100644
index 000000000..2e20d0cd3
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/border/BorderManager.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt.border;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.render.txt.TXTState;
+
+/**
+ * This keeps all information about borders for current processed page.
+ */
+public class BorderManager {
+
+ /** Matrix for storing information about one border element. */
+ private AbstractBorderElement[][] borderInfo;
+
+ /** Width of current processed border. */
+ private int width;
+
+ /** Height of current processed border. */
+ private int height;
+
+ /** x-coordinate of upper left point of current processed border. */
+ private int startX;
+
+ /** y-coordinate of upper left point of current processed border. */
+ private int startY;
+
+ /** Stores TXTState for transforming border elements. */
+ private TXTState state;
+
+ /**
+ * Constructs BorderManger, using <code>pageWidth</code> and
+ * <code>pageHeight</code> for creating <code>borderInfo</code>.
+ *
+ * @param pageWidth page width
+ * @param pageHeight page height
+ * @param state TXTState
+ */
+ public BorderManager(int pageWidth, int pageHeight, TXTState state) {
+ this.state = state;
+ borderInfo = new AbstractBorderElement[pageHeight][pageWidth];
+ }
+
+ /**
+ * Adds border element to <code>borderInfo</code>.
+ *
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @param style border-style
+ * @param type border element type, binary representation of wich gives
+ * information about availability or absence of corresponding side.
+ */
+ public void addBorderElement(int x, int y, int style, int type) {
+ AbstractBorderElement be = null;
+
+ if (style == Constants.EN_SOLID || style == Constants.EN_DOUBLE) {
+ be = new SolidAndDoubleBorderElement(style, type);
+ } else if (style == Constants.EN_DOTTED) {
+ be = new DottedBorderElement();
+ } else if (style == Constants.EN_DASHED) {
+ be = new DashedBorderElement(type);
+ } else {
+ return;
+ }
+ be.transformElement(state);
+
+ if (borderInfo[y][x] != null) {
+ borderInfo[y][x] = borderInfo[y][x].merge(be);
+ } else {
+ borderInfo[y][x] = be;
+ }
+ }
+
+ /**
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @return if border element at point (x,y) is available, returns instance
+ * of Character, created on char, given by corresponding border element,
+ * otherwise returns null.
+ */
+ public Character getCharacter(int x, int y) {
+ Character c = null;
+ if (borderInfo[y][x] != null) {
+ c = new Character(borderInfo[y][x].convert2Char());
+ }
+ return c;
+ }
+
+ /**
+ * @return width of current processed border.
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Sets width of current processed border.
+ * @param width width of border
+ */
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ /**
+ * @return height of current processed border.
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Sets height of current processed border.
+ * @param height height of border
+ */
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ /**
+ * @return x-coordinate of upper left point of current processed border.
+ */
+ public int getStartX() {
+ return startX;
+ }
+
+ /**
+ * Sets x-coordinate of upper left point of current processed border.
+ * @param startX x-coordinate of upper left border's point.
+ */
+ public void setStartX(int startX) {
+ this.startX = startX;
+ }
+
+ /**
+ * @return y-coordinate of upper left point of current processed border.
+ */
+ public int getStartY() {
+ return startY;
+ }
+
+ /**
+ * Sets y-coordinate of upper left point of current processed border.
+ * @param startY y-coordinate of upper left border's point.
+ */
+ public void setStartY(int startY) {
+ this.startY = startY;
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/border/DashedBorderElement.java b/src/java/org/apache/fop/render/txt/border/DashedBorderElement.java
new file mode 100644
index 000000000..7e7aa7acd
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/border/DashedBorderElement.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt.border;
+
+import java.util.Arrays;
+
+/**
+ * This class is responsible for managing of dashed border elements.
+ */
+public class DashedBorderElement extends AbstractBorderElement {
+
+ private static final char DASH_HORIZONTAL = '-';
+
+ private static final char DASH_VERTICAL = '|';
+
+ private static final char UNDEFINED = '?';
+
+ private static final int UP2 = 1;
+
+ private static final int RIGHT2 = 2;
+
+ private static final int DOWN2 = 4;
+
+ private static final int LEFT2 = 8;
+
+ private static char[] map = new char[20];
+
+ static {
+ Arrays.fill(map, UNDEFINED);
+ map[0] = ' ';
+ map[UP2] = DASH_VERTICAL;
+ map[DOWN2] = DASH_VERTICAL;
+ map[UP2 + DOWN2] = DASH_VERTICAL;
+
+ map[LEFT2] = DASH_HORIZONTAL;
+ map[RIGHT2] = DASH_HORIZONTAL;
+ map[LEFT2 + RIGHT2] = DASH_HORIZONTAL;
+ }
+
+ /**
+ * Constructs a newly allocated <code>DashedBorderElement</code> object.
+ * Fills <code>data</code> using superclass constructor.
+ *
+ * @param type binary representation of type gives <code>data</code>
+ */
+ public DashedBorderElement(int type) {
+ super(type);
+ }
+
+ /**
+ * Merges dashed border element with instance of solid and double border
+ * element, returns instance of <code>SolidAndDoubleBorderElement</code>.
+ *
+ * @param sdb instance of <code>SolidAndDoubleBorderElement</code> to merge
+ * @return merged border element
+ */
+ private AbstractBorderElement mergeSolid(SolidAndDoubleBorderElement sdb) {
+ AbstractBorderElement e = new SolidAndDoubleBorderElement(EN_SOLID, 0);
+ for (int i = 0; i < 4; i++) {
+ e.setData(i, Math.max(data[i], sdb.getData(i)));
+ }
+ return e;
+ }
+
+ /**
+ * Merges dashed border element with dashed border element and returns
+ * instance of <code>DashedBorderElement</code>.
+ *
+ * @param dbe instance of <code>DashedBorderElement</code> to merge
+ * @return merged border element
+ */
+ private AbstractBorderElement mergeDashed(DashedBorderElement dbe) {
+ for (int i = 0; i < 4; i++) {
+ data[i] = Math.max(data[i], dbe.getData(i));
+ }
+ return this;
+ }
+
+ /**
+ * Converts dashed border element to
+ * <code>SolidAndDoubleBorderElement</code>.
+ *
+ * @return converted instance of <code>SolidAndDoubleBorderElement</code>
+ */
+ private AbstractBorderElement toSolidAndDouble() {
+ AbstractBorderElement e = new SolidAndDoubleBorderElement(EN_SOLID, 0);
+ for (int i = 0; i < 4; i++) {
+ e.setData(i, data[i]);
+ }
+ return e;
+ }
+
+ /**
+ * Merges with border element.
+ * @param e instance of AbstractBorderElement
+ * @return instance of AbstractBorderElement
+ */
+ public AbstractBorderElement merge(AbstractBorderElement e) {
+ AbstractBorderElement abe = this;
+ if (e instanceof SolidAndDoubleBorderElement) {
+ abe = mergeSolid((SolidAndDoubleBorderElement) e);
+ } else if (e instanceof DashedBorderElement) {
+ abe = mergeDashed((DashedBorderElement) e);
+ } else {
+ abe = e;
+ }
+ return abe;
+ }
+
+ /**
+ * @see org.apache.fop.render.txt.border.AbstractBorderElement#convert2Char()
+ */
+ public char convert2Char() {
+ int key = 0;
+ key += data[UP] * UP2;
+ key += data[DOWN] * DOWN2;
+ key += data[LEFT] * LEFT2;
+ key += data[RIGHT] * RIGHT2;
+ char ch = map[key];
+ if (ch == UNDEFINED) {
+ ch = toSolidAndDouble().convert2Char();
+ }
+ return ch;
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/border/DottedBorderElement.java b/src/java/org/apache/fop/render/txt/border/DottedBorderElement.java
new file mode 100644
index 000000000..8e693974b
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/border/DottedBorderElement.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt.border;
+
+/**
+ * This class is responsible for managing of dotted border elements.
+ */
+public class DottedBorderElement extends AbstractBorderElement {
+
+ private static final char MIDDLE_DOT = '\u00B7';
+
+ /**
+ * Merges dotted border element with another border element. Here merging
+ * is quite simple: returning <code>this</code> without any comparing.
+ *
+ * @param e instance of AbstractBorderElement
+ * @return instance of DottedBorderElement
+ */
+ public AbstractBorderElement merge(AbstractBorderElement e) {
+ return this;
+ }
+
+ /**
+ * @see org.apache.fop.render.txt.border.AbstractBorderElement#convert2Char()
+ */
+ public char convert2Char() {
+ return MIDDLE_DOT;
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/border/SolidAndDoubleBorderElement.java b/src/java/org/apache/fop/render/txt/border/SolidAndDoubleBorderElement.java
new file mode 100644
index 000000000..72ca9d2dc
--- /dev/null
+++ b/src/java/org/apache/fop/render/txt/border/SolidAndDoubleBorderElement.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.txt.border;
+
+import java.util.Arrays;
+
+/**
+ * This class is responsible for solid and double border elements managing.
+ */
+public class SolidAndDoubleBorderElement extends AbstractBorderElement {
+
+ private static final char LIGHT_HORIZONTAL = '\u2500';
+
+ private static final char LIGHT_VERTICAL = '\u2502';
+
+ private static final char LIGHT_DOWN_AND_RIGHT = '\u250C';
+
+ private static final char LIGHT_DOWN_AND_LEFT = '\u2510';
+
+ private static final char LIGHT_UP_AND_RIGHT = '\u2514';
+
+ private static final char LIGHT_UP_AND_LEFT = '\u2518';
+
+ private static final char LIGHT_VERTICAL_AND_RIGHT = '\u251C';
+
+ private static final char LIGHT_VERTICAL_AND_LEFT = '\u2524';
+
+ private static final char LIGHT_DOWN_AND_HORIZONTAL = '\u252C';
+
+ private static final char LIGHT_UP_AND_HORIZONTAL = '\u2534';
+
+ private static final char LIGHT_VERTICAL_AND_HORIZONTAL = '\u253C';
+
+ private static final char DOUBLE_HORIZONTAL = '\u2550';
+
+ private static final char DOUBLE_VERTICAL = '\u2551';
+
+ private static final char DOUBLE_DOWN_AND_RIGHT = '\u2554';
+
+ private static final char DOUBLE_DOWN_AND_LEFT = '\u2557';
+
+ private static final char DOUBLE_UP_AND_RIGHT = '\u255A';
+
+ private static final char DOUBLE_UP_AND_LEFT = '\u255D';
+
+ private static final char DOUBLE_VERTICAL_AND_RIGHT = '\u2560';
+
+ private static final char DOUBLE_VERTICAL_AND_LEFT = '\u2563';
+
+ private static final char DOUBLE_DOWN_AND_HORIZONTAL = '\u2566';
+
+ private static final char DOUBLE_UP_AND_HORIZONTAL = '\u2569';
+
+ private static final char DOUBLE_VERTICAL_AND_HORIZONTAL = '\u256C';
+
+ private static final char DOWN_SINGLE_AND_RIGHT_DOUBLE = '\u2552';
+
+ private static final char DOWN_DOUBLE_AND_RIGHT_SINGLE = '\u2553';
+
+ private static final char DOWN_SINGLE_AND_LEFT_DOUBLE = '\u2555';
+
+ private static final char DOWN_DOUBLE_AND_LEFT_SINGLE = '\u2556';
+
+ private static final char UP_SINGLE_AND_RIGHT_DOUBLE = '\u2558';
+
+ private static final char UP_DOUBLE_AND_RIGHT_SINGLE = '\u2559';
+
+ private static final char UP_SINGLE_AND_LEFT_DOUBLE = '\u255B';
+
+ private static final char UP_DOUBLE_AND_LEFT_SINGLE = '\u255C';
+
+ private static final char VERTICAL_SINGLE_AND_RIGHT_DOUBLE = '\u255E';
+
+ private static final char VERTICAL_DOUBLE_AND_RIGHT_SINGLE = '\u255F';
+
+ private static final char VERTICAL_SINGLE_AND_LEFT_DOUBLE = '\u2561';
+
+ private static final char VERTICAL_DOUBLE_AND_LEFT_SINGLE = '\u2562';
+
+ private static final char DOWN_SINGLE_AND_HORIZONTAL_DOUBLE = '\u2564';
+
+ private static final char DOWN_DOUBLE_AND_HORIZONTAL_SINGLE = '\u2565';
+
+ private static final char UP_SINGLE_AND_HORIZONTAL_DOUBLE = '\u2567';
+
+ private static final char UP_DOUBLE_AND_HORIZONTAL_SINGLE = '\u2568';
+
+ private static final char VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE = '\u256A';
+
+ private static final char VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE = '\u256B';
+
+ private static final char UNDEFINED = '?';
+
+ private static final int UP3 = 1;
+
+ private static final int DOWN3 = 3;
+
+ private static final int LEFT3 = 9;
+
+ private static final int RIGHT3 = 27;
+
+ private static final char[] MAP = new char[100];
+
+ static {
+ Arrays.fill(MAP, UNDEFINED);
+ MAP[0] = ' ';
+ MAP[UP3] = LIGHT_VERTICAL;
+ MAP[DOWN3] = LIGHT_VERTICAL;
+ MAP[RIGHT3] = LIGHT_HORIZONTAL;
+ MAP[LEFT3] = LIGHT_HORIZONTAL;
+ MAP[UP3 + DOWN3] = LIGHT_VERTICAL;
+ MAP[LEFT3 + RIGHT3] = LIGHT_HORIZONTAL;
+ MAP[UP3 + LEFT3] = LIGHT_UP_AND_LEFT;
+ MAP[LEFT3 + DOWN3] = LIGHT_DOWN_AND_LEFT;
+ MAP[DOWN3 + RIGHT3] = LIGHT_DOWN_AND_RIGHT;
+ MAP[UP3 + RIGHT3] = LIGHT_UP_AND_RIGHT;
+ MAP[UP3 + DOWN3 + RIGHT3] = LIGHT_VERTICAL_AND_RIGHT;
+ MAP[UP3 + LEFT3 + DOWN3] = LIGHT_VERTICAL_AND_LEFT;
+ MAP[LEFT3 + DOWN3 + RIGHT3] = LIGHT_DOWN_AND_HORIZONTAL;
+ MAP[UP3 + LEFT3 + RIGHT3] = LIGHT_UP_AND_HORIZONTAL;
+ MAP[UP3 + LEFT3 + DOWN3 + RIGHT3] = LIGHT_VERTICAL_AND_HORIZONTAL;
+ //DOUBLE
+ MAP[2 * UP3] = DOUBLE_VERTICAL;
+ MAP[2 * DOWN3] = DOUBLE_VERTICAL;
+ MAP[2 * RIGHT3] = DOUBLE_HORIZONTAL;
+ MAP[2 * LEFT3] = DOUBLE_HORIZONTAL;
+ MAP[2 * UP3 + 2 * DOWN3] = DOUBLE_VERTICAL;
+ MAP[2 * LEFT3 + 2 * RIGHT3] = DOUBLE_HORIZONTAL;
+ MAP[2 * UP3 + 2 * LEFT3] = DOUBLE_UP_AND_LEFT;
+ MAP[2 * LEFT3 + 2 * DOWN3] = DOUBLE_DOWN_AND_LEFT;
+ MAP[2 * DOWN3 + 2 * RIGHT3] = DOUBLE_DOWN_AND_RIGHT;
+ MAP[2 * UP3 + 2 * RIGHT3] = DOUBLE_UP_AND_RIGHT;
+ MAP[2 * UP3 + 2 * DOWN3 + 2 * RIGHT3] = DOUBLE_VERTICAL_AND_RIGHT;
+ MAP[2 * UP3 + 2 * DOWN3 + 2 * LEFT3] = DOUBLE_VERTICAL_AND_LEFT;
+ MAP[2 * DOWN3 + 2 * RIGHT3 + 2 * LEFT3] = DOUBLE_DOWN_AND_HORIZONTAL;
+ MAP[2 * UP3 + 2 * RIGHT3 + 2 * LEFT3] = DOUBLE_UP_AND_HORIZONTAL;
+ MAP[2 * UP3 + 2 * DOWN3 + 2 * RIGHT3 + 2 * LEFT3] = DOUBLE_VERTICAL_AND_HORIZONTAL;
+ //DOUBLE&SINGLE
+ MAP[DOWN3 + 2 * RIGHT3] = DOWN_SINGLE_AND_RIGHT_DOUBLE;
+ MAP[2 * DOWN3 + RIGHT3] = DOWN_DOUBLE_AND_RIGHT_SINGLE;
+ MAP[DOWN3 + 2 * LEFT3] = DOWN_SINGLE_AND_LEFT_DOUBLE;
+ MAP[2 * DOWN3 + LEFT3] = DOWN_DOUBLE_AND_LEFT_SINGLE;
+ MAP[UP3 + 2 * RIGHT3] = UP_SINGLE_AND_RIGHT_DOUBLE;
+ MAP[2 * UP3 + RIGHT3] = UP_DOUBLE_AND_RIGHT_SINGLE;
+ MAP[UP3 + 2 * LEFT3] = UP_SINGLE_AND_LEFT_DOUBLE;
+ MAP[2 * UP3 + LEFT3] = UP_DOUBLE_AND_LEFT_SINGLE;
+ MAP[UP3 + DOWN3 + 2 * RIGHT3] = VERTICAL_SINGLE_AND_RIGHT_DOUBLE;
+ MAP[2 * UP3 + 2 * DOWN3 + RIGHT3] = VERTICAL_DOUBLE_AND_RIGHT_SINGLE;
+ MAP[UP3 + DOWN3 + 2 * LEFT3] = VERTICAL_SINGLE_AND_LEFT_DOUBLE;
+ MAP[2 * UP3 + 2 * DOWN3 + LEFT3] = VERTICAL_DOUBLE_AND_LEFT_SINGLE;
+ MAP[DOWN3 + 2 * LEFT3 + 2 * RIGHT3] = DOWN_SINGLE_AND_HORIZONTAL_DOUBLE;
+ MAP[2 * DOWN3 + LEFT3 + RIGHT3] = DOWN_DOUBLE_AND_HORIZONTAL_SINGLE;
+ MAP[UP3 + 2 * LEFT3 + 2 * RIGHT3] = UP_SINGLE_AND_HORIZONTAL_DOUBLE;
+ MAP[2 * UP3 + LEFT3 + RIGHT3] = UP_DOUBLE_AND_HORIZONTAL_SINGLE;
+ MAP[UP3 + DOWN3 + 2 * LEFT3 + 2 * RIGHT3] = VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE;
+ MAP[2 * UP3 + 2 * DOWN3 + LEFT3 + RIGHT3] = VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE;
+ }
+
+ /**
+ * Initializes a newly created <code>SolidAndDoubleBorderElement</code>
+ * object so that it represents an empty border element.
+ */
+ public SolidAndDoubleBorderElement() {
+ }
+
+ /**
+ * Constructs a newly allocated <code>SolidAndDoubleBorderElement</code>
+ * object. Fills <code>data</code> using binary representation of
+ * <code>type</code>. If border style is EN_DOUBLE, multiplies
+ * <code>data[side]</code> by 2 for every side to distinguish EN_SOLID and
+ * EN_DOUBLE.
+ *
+ * @param style integer, representing border style.
+ * @param type binary representation of type gives <code>data</code>
+ */
+ public SolidAndDoubleBorderElement(int style, int type) {
+ super(type);
+ if (style == EN_DOUBLE) {
+ for (int i = 0; i < 4; i++) {
+ data[i] *= 2;
+ }
+ }
+ }
+
+ /**
+ * Merges with <code>sde</code>.
+ * @param sde instance of SolidAndDoubleBorderElement
+ * @return instance of AbstractBorderElement
+ */
+ public AbstractBorderElement mergeSolid(SolidAndDoubleBorderElement sde) {
+ AbstractBorderElement e = new SolidAndDoubleBorderElement(EN_SOLID, 0);
+ for (int i = 0; i < 4; i++) {
+ if (sde.getData(i) != 0) {
+ e.setData(i, sde.getData(i));
+ } else {
+ e.setData(i, data[i]);
+ }
+ }
+ return e;
+ }
+
+ /**
+ * Merges with e.
+ * @param e instance of AbstractBorderElement
+ * @return instance of AbstractBorderElement
+ */
+ public AbstractBorderElement merge(AbstractBorderElement e) {
+ AbstractBorderElement abe = this;
+ if (e instanceof SolidAndDoubleBorderElement) {
+ abe = mergeSolid((SolidAndDoubleBorderElement) e);
+ } else if (e instanceof DottedBorderElement) {
+ abe = e;
+ } else if (e instanceof DashedBorderElement) {
+ abe = e.merge(this);
+ }
+ return abe;
+ }
+
+ /**
+ * Maps to char.
+ * @return resulting mapping char
+ */
+ private char map2Char() {
+ int key = 0;
+ key += data[UP] * UP3;
+ key += data[LEFT] * LEFT3;
+ key += data[DOWN] * DOWN3;
+ key += data[RIGHT] * RIGHT3;
+ return MAP[key];
+ }
+
+ /**
+ * Modifies data to nearest normal internal representation.
+ */
+ private void modifyData() {
+ int c1 = 0;
+ int c2 = 0;
+ for (int i = 0; i < 4; i++) {
+ c1 += (data[i] == 1) ? 1 : 0;
+ c2 += (data[i] == 2) ? 1 : 0;
+ }
+ int m = c1 > c2 ? 1 : 0;
+ int[] p = {0, m, 2 * (1 - m)};
+ for (int i = 0; i < 4; i++) {
+ data[i] = p[data[i]];
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.txt.border.AbstractBorderElement#convert2Char()
+ */
+ public char convert2Char() {
+ char ch = map2Char();
+ if (ch == UNDEFINED) {
+ modifyData();
+ ch = map2Char();
+ }
+ return ch;
+ }
+}