diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/context/StyleReference.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/context/StyleReference.java
index 4a8fa442b..d488acc6a 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/context/StyleReference.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/context/StyleReference.java
@@ -85,8 +85,29 @@ public class StyleReference {
* @param userAgent PARAM
*/
public StyleReference(UserAgentCallback userAgent) {
+ this(userAgent, null);
+ }
+
+
+ /**
+ * A docx4j extension to standard FS, allowing extra stylesheets
+ * (ie that aren't explicit, either linked or embedded) to be specified
+ * and applied. This is useful, for example, for adding CSS which
+ * represent WordML styles in the target package, which can be
+ * used via @class. OpenDoPE uses this in its XHTML import.
+ * It could also be useful in a web-based editing scenario.
+ */
+ private StylesheetInfo[] extraCSS;
+
+ /**
+ * Default constructor for initializing members.
+ *
+ * @param userAgent PARAM
+ */
+ public StyleReference(UserAgentCallback userAgent, StylesheetInfo[] extraCSS) {
_uac = userAgent;
_stylesheetFactory = new StylesheetFactoryImpl(userAgent);
+ this.extraCSS = extraCSS;
}
/**
@@ -263,8 +284,11 @@ private List getStylesheets() {
refs[i].setUri(null);
}
}
+ infos.addAll(Arrays.asList(refs));
}
- infos.addAll(Arrays.asList(refs));
+
+ // docx4j addition
+ handleExtraCSS( infos, inlineStyleCount);
// TODO: here we should also get user stylesheet from userAgent
@@ -273,7 +297,36 @@ private List getStylesheets() {
return infos;
}
-
+
+ /**
+ * Dynamically add any extra CSS
+ *
+ * @param infos
+ * @param inlineStyleCount
+ * @since 3.0
+ */
+ private void handleExtraCSS(List infos, int inlineStyleCount) {
+
+ if (extraCSS != null) {
+ for (int i = 0; i < extraCSS.length; i++) {
+ String uri;
+
+ if (! extraCSS[i].isInline()) {
+ uri = _uac.resolveURI(extraCSS[i].getUri());
+ extraCSS[i].setUri(uri);
+ } else {
+ // Expect that extra CSS will be effectively inline
+ extraCSS[i].setUri(_uac.getBaseURL() + "#inline_style_" + (++inlineStyleCount));
+ Stylesheet sheet = _stylesheetFactory.parse(
+ new StringReader(extraCSS[i].getContent()), extraCSS[i]);
+ extraCSS[i].setStylesheet(sheet);
+ extraCSS[i].setUri(null);
+ }
+ }
+ infos.addAll(Arrays.asList(extraCSS));
+ }
+ }
+
public void removeStyle(Element e) {
if (_matcher != null) {
_matcher.removeStyle(e);
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/constants/IdentValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/constants/IdentValue.java
index ff7b6e4e1..bf7c1bd5a 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/constants/IdentValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/constants/IdentValue.java
@@ -22,6 +22,7 @@
import java.util.HashMap;
import java.util.Map;
+import org.w3c.dom.css.CSSPrimitiveValue;
import org.xhtmlrenderer.css.parser.FSColor;
import org.xhtmlrenderer.css.style.CssContext;
import org.xhtmlrenderer.css.style.FSDerivedValue;
@@ -55,6 +56,16 @@
public class IdentValue implements FSDerivedValue {
private static int maxAssigned = 0;
+ // JBH
+ private CSSPrimitiveValue cssPrimitiveValue;
+ public void setCSSPrimitiveValue(CSSPrimitiveValue cssPrimitiveValue) {
+ this.cssPrimitiveValue = cssPrimitiveValue;
+ }
+
+ public CSSPrimitiveValue getCSSPrimitiveValue() {
+ return cssPrimitiveValue;
+ }
+
/**
* Description of the Field
*/
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CalculatedStyle.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CalculatedStyle.java
index 5bf48e9e4..fcb96af44 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CalculatedStyle.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CalculatedStyle.java
@@ -628,6 +628,39 @@ private String genStyleKey() {
}
+ public String toStringMine() {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < _derivedValuesById.length; i++) {
+ CSSName name = CSSName.getByID(i);
+ FSDerivedValue val = this.valueByName(name); // walks parents as necessary to get the value
+ //FSDerivedValue val = _derivedValuesById[i];
+ if (val != null) {
+ sb.append(name.toString()+ ": " + val.asString() );
+ } else {
+// sb.append(name.toString() );
+// sb.append("(no prop assigned in this pos)");
+ }
+ sb.append("; ");
+ }
+ return sb.toString();
+
+ }
+
+ public String getDisplayMine() {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < _derivedValuesById.length; i++) {
+ CSSName name = CSSName.getByID(i);
+ if (name.equals(CSSName.DISPLAY) ) {
+ return _derivedValuesById[i].asString();
+ }
+ }
+ return null;
+ }
+
+ public FSDerivedValue[] getDerivedValues() {
+ return _derivedValuesById;
+ }
+
public RectPropertySet getCachedPadding() {
if (_padding == null) {
throw new XRRuntimeException("No padding property cached yet; should have called getPropertyRect() at least once before.");
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/DerivedValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/DerivedValue.java
index 46fc8c34f..db34856e3 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/DerivedValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/DerivedValue.java
@@ -32,13 +32,22 @@ public abstract class DerivedValue implements FSDerivedValue {
private short _cssSacUnitType;
+ private CSSPrimitiveValue cssPrimitiveValue;
+ public CSSPrimitiveValue getCSSPrimitiveValue() {
+ return cssPrimitiveValue;
+ }
+
protected DerivedValue() {}
protected DerivedValue(
CSSName name,
+ CSSPrimitiveValue cssPrimitiveValue,
short cssSACUnitType,
String cssText,
String cssStringValue) {
+
+ this.cssPrimitiveValue = cssPrimitiveValue;
+
this._cssSacUnitType = cssSACUnitType;
if ( cssText == null ) {
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ColorValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ColorValue.java
index e8f1aa923..df541f917 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ColorValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ColorValue.java
@@ -29,7 +29,7 @@ public class ColorValue extends DerivedValue {
private FSColor _color;
public ColorValue(CSSName name, PropertyValue value) {
- super(name, value.getPrimitiveType(), value.getCssText(), value.getCssText());
+ super(name, value, value.getPrimitiveType(), value.getCssText(), value.getCssText());
_color = value.getFSColor();
}
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/DerivedValueFactory.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/DerivedValueFactory.java
index bb412ba37..4ee16df44 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/DerivedValueFactory.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/DerivedValueFactory.java
@@ -46,6 +46,7 @@ public static FSDerivedValue newDerivedValue(
if (ident == null) {
ident = IdentValue.getByIdentString(value.getStringValue());
}
+ ident.setCSSPrimitiveValue(value);
return ident;
case PropertyValue.VALUE_TYPE_STRING:
return new StringValue(cssName, value);
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/FunctionValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/FunctionValue.java
index 2c038a7bb..2519b692f 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/FunctionValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/FunctionValue.java
@@ -28,7 +28,7 @@ public class FunctionValue extends DerivedValue {
private FSFunction _function;
public FunctionValue(CSSName name, PropertyValue value) {
- super(name, value.getPrimitiveType(), value.getCssText(), value.getCssText());
+ super(name, value, value.getPrimitiveType(), value.getCssText(), value.getCssText());
_function = value.getFunction();
}
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/LengthValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/LengthValue.java
index 27877fa6b..98661ae34 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/LengthValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/LengthValue.java
@@ -50,7 +50,7 @@ public class LengthValue extends DerivedValue {
private short _lengthPrimitiveType;
public LengthValue(CalculatedStyle style, CSSName name, PropertyValue value) {
- super(name, value.getPrimitiveType(), value.getCssText(), value.getCssText());
+ super(name, value, value.getPrimitiveType(), value.getCssText(), value.getCssText());
_style = style;
_lengthAsFloat = value.getFloatValue();
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ListValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ListValue.java
index df77bf8e8..3011f4651 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ListValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/ListValue.java
@@ -30,7 +30,7 @@ public class ListValue extends DerivedValue {
private List _values;
public ListValue(CSSName name, PropertyValue value) {
- super(name, value.getPrimitiveType(), value.getCssText(), value.getCssText());
+ super(name, value, value.getPrimitiveType(), value.getCssText(), value.getCssText());
_values = value.getValues();
}
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/NumberValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/NumberValue.java
index 663a514f9..207bd5a84 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/NumberValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/NumberValue.java
@@ -28,7 +28,7 @@ public class NumberValue extends DerivedValue {
private float _floatValue;
public NumberValue(CSSName cssName, PropertyValue value) {
- super(cssName, value.getPrimitiveType(), value.getCssText(), value.getCssText());
+ super(cssName, value, value.getPrimitiveType(), value.getCssText(), value.getCssText());
_floatValue = value.getFloatValue();
}
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/StringValue.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/StringValue.java
index fef1d7af4..56f43baf2 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/StringValue.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/derived/StringValue.java
@@ -29,7 +29,7 @@ public class StringValue extends DerivedValue {
private String[] _stringAsArray;
public StringValue(CSSName name, PropertyValue value) {
- super(name, value.getPrimitiveType(), value.getCssText(), value.getStringValue());
+ super(name, value, value.getPrimitiveType(), value.getCssText(), value.getStringValue());
if (value.getStringArrayValue() != null) {
_stringAsArray = value.getStringArrayValue();
}
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/BoxBuilder.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/BoxBuilder.java
index 617c184c3..f08900a3d 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/BoxBuilder.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/BoxBuilder.java
@@ -1212,16 +1212,21 @@ private static void createChildren(
child = createInlineBox(text.toString(), parent, parentStyle, textNode);
*/
- child = createInlineBox(textNode.getData(), parent, parentStyle, textNode);
-
- InlineBox iB = (InlineBox) child;
- iB.setEndsHere(true);
- if (previousIB == null) {
- iB.setStartsHere(true);
+ if ("script".equals(parent.getLocalName())) {
+ // This is an important change, for us. It gets rid of a lot of extra unwanted parents inserted
+ //System.out.println("Skipping empty text");
} else {
- previousIB.setEndsHere(false);
+ child = createInlineBox(textNode.getData(), parent, parentStyle, textNode);
+
+ InlineBox iB = (InlineBox) child;
+ iB.setEndsHere(true);
+ if (previousIB == null) {
+ iB.setStartsHere(true);
+ } else {
+ previousIB.setEndsHere(false);
+ }
+ previousIB = iB;
}
- previousIB = iB;
} else if(nodeType == Node.ENTITY_REFERENCE_NODE) {
EntityReference entityReference = (EntityReference)working;
child = createInlineBox(entityReference.getTextContent(), parent, parentStyle, null);
@@ -1241,7 +1246,12 @@ private static void createChildren(
}
} while ((working = working.getNextSibling()) != null);
}
- if (needStartText || needEndText) {
+ if ("br".equals(parent.getNodeName())
+ //|| "a".equals(parent.getNodeName())
+ ) {
+ // avoid double node being inserted for br!
+ // TODO: understand needStartText/needEndText so this isn't necessary
+ } else if (needStartText || needEndText) {
InlineBox iB = createInlineBox("", parent, parentStyle, null);
iB.setStartsHere(needStartText);
iB.setEndsHere(needEndText);
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java
index 44a264854..534975793 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java
@@ -98,6 +98,7 @@ public class SharedContext {
private Rectangle temp_canvas;
private LineBreakingStrategy lineBreakingStrategy = new DefaultLineBreakingStrategy();
+ private int _realContentWidth = 0;
public SharedContext() {
}
@@ -630,6 +631,16 @@ public LineBreakingStrategy getLineBreakingStrategy() {
public void setLineBreakingStrategy(LineBreakingStrategy lineBreakingStrategy) {
this.lineBreakingStrategy = lineBreakingStrategy;
}
+
+ public int getRealContentWidth(){
+ return _realContentWidth;
+ }
+
+
+ public int setRealContentWidth(int newContentWidth){
+ _realContentWidth = newContentWidth > _realContentWidth ? newContentWidth : _realContentWidth;
+ return _realContentWidth;
+ }
}
/*
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableBox.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableBox.java
index 58b2e8b1f..e595d7ad0 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableBox.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableBox.java
@@ -49,6 +49,9 @@
// manage colspans and the details of the table layout algorithms). Many kudos
// to the KHTML developers for making such an amazing piece of software!
public class TableBox extends BlockBox {
+
+ public TableBox() {}
+
private final List _columns = new ArrayList();
private int[] _columnPos;
private TableLayout _tableLayout;
@@ -197,7 +200,7 @@ public List getColumns() {
return _columns;
}
- private void recalcSections(LayoutContext c) {
+ public void recalcSections(LayoutContext c) {
ensureChildren(c);
for (Iterator i = getChildIterator(); i.hasNext(); ) {
TableSectionBox section = (TableSectionBox)i.next();
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableCellBox.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableCellBox.java
index 1f87989d7..0e177ded1 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableCellBox.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/newtable/TableCellBox.java
@@ -149,7 +149,7 @@ public TableBox getTable() {
return _table;
}
- protected TableSectionBox getSection() {
+ public TableSectionBox getSection() {
if (_section == null) {
_section = (TableSectionBox)getParent().getParent();
}
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/BlockBox.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/BlockBox.java
index a2a42b808..157259545 100755
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/BlockBox.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/BlockBox.java
@@ -770,12 +770,18 @@ private void addBoxID(LayoutContext c) {
c.addBoxId(id, this);
}
}
+ c.getSharedContext().setRealContentWidth(getMinWidth());
}
public void layout(LayoutContext c) {
layout(c, 0);
}
+ private RectPropertySet margin;
+ public RectPropertySet getMargin() {
+ return margin;
+ }
+
public void layout(LayoutContext c, int contentStart) {
CalculatedStyle style = getStyle();
@@ -820,7 +826,7 @@ public void layout(LayoutContext c, int contentStart) {
}
BorderPropertySet border = getBorder(c);
- RectPropertySet margin = getMargin(c);
+ margin = getMargin(c);
RectPropertySet padding = getPadding(c);
// save height in case fixed height
diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/resource/XMLResource.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/resource/XMLResource.java
index 46205fe46..b62882e85 100644
--- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/resource/XMLResource.java
+++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/resource/XMLResource.java
@@ -283,6 +283,9 @@ private void setParserFeatures(XMLReader xmlReader) {
xmlReader.setFeature("http://xml.org/sax/features/validation", false);
// perf: namespaces
xmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
+
+ // prevent behaviour: " no local mapping. Parser will probably pull from network."
+ xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (SAXException s) {
// nothing to do--some parsers will not allow setting features
XRLog.load(Level.WARNING, "Could not set validation/namespace features for XML parser," +
diff --git a/flying-saucer-docx/pom.xml b/flying-saucer-docx/pom.xml
new file mode 100644
index 000000000..560a0339a
--- /dev/null
+++ b/flying-saucer-docx/pom.xml
@@ -0,0 +1,62 @@
+
+
+
+ 4.0.0
+
+
+ org.xhtmlrenderer
+ flying-saucer-parent
+ 9.1.11
+
+
+ flying-saucer-docx
+
+ jar
+
+ Flying Saucer DOCX Rendering
+ Flying Saucer is a CSS 2.1 renderer written in Java. This artifact supports DOCX output.
+
+
+
+ GNU Lesser General Public License (LGPL), version 2.1 or later
+ http://www.gnu.org/licenses/lgpl.html
+
+
+
+
+
+ bintray
+ https://api.bintray.com/maven/flyingsaucerproject/maven/org.xhtmlrenderer:flying-saucer-docx
+
+
+
+
+
+ com.lowagie
+ itext
+ 2.1.7
+
+
+ org.xhtmlrenderer
+ flying-saucer-pdf
+ ${project.version}
+
+
+ org.xhtmlrenderer
+ flying-saucer-core
+ ${project.version}
+
+
+
+
+
+
+ ../
+ ${project.build.outputDirectory}/META-INF
+
+ LICENSE*
+
+
+
+
+
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4JFSImage.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4JFSImage.java
new file mode 100644
index 000000000..af9f7643f
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4JFSImage.java
@@ -0,0 +1,60 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+
+//import org.xhtmlrenderer.extend.FSImage;
+//import com.lowagie.text.Image;
+
+public class Docx4JFSImage {
+// implements FSImage, Cloneable {
+
+ byte[] image;
+ public byte[] getBytes() {
+ return image;
+ }
+
+ public Docx4JFSImage(byte[] bytes) {
+ image = bytes;
+ }
+
+// public int getWidth() {
+// throw new NotImplementedException();
+// }
+//
+// public int getHeight() {
+// throw new NotImplementedException();
+// }
+//
+// public void scale(int width, int height) {
+// throw new NotImplementedException();
+// }
+//
+// public Image getImage() {
+// throw new NotImplementedException();
+// }
+//
+// public Object clone() {
+// throw new NotImplementedException();
+// }
+
+
+
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jDocxOutputDevice.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jDocxOutputDevice.java
new file mode 100644
index 000000000..429f0827a
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jDocxOutputDevice.java
@@ -0,0 +1,165 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.awt.Rectangle;
+import java.awt.RenderingHints.Key;
+import java.awt.Shape;
+import java.awt.Stroke;
+
+import org.xhtmlrenderer.css.parser.FSColor;
+import org.xhtmlrenderer.extend.FSImage;
+import org.xhtmlrenderer.render.AbstractOutputDevice;
+import org.xhtmlrenderer.render.BlockBox;
+import org.xhtmlrenderer.render.FSFont;
+import org.xhtmlrenderer.render.InlineText;
+import org.xhtmlrenderer.render.RenderingContext;
+
+public class Docx4jDocxOutputDevice extends AbstractOutputDevice {
+
+ @Override
+ public void draw(Shape s) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void drawBorderLine(Shape bounds, int side, int width, boolean solid) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void drawSelection(RenderingContext c, InlineText inlineText) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void paintReplacedElement(RenderingContext c, BlockBox box) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setFont(FSFont font) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setColor(FSColor color) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void drawOval(int x, int y, int width, int height) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void drawBorderLine(Rectangle bounds, int side, int width, boolean solid) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void drawImage(FSImage image, int x, int y) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void fill(Shape s) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void fillRect(int x, int y, int width, int height) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void fillOval(int x, int y, int width, int height) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void clip(Shape s) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Shape getClip() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setClip(Shape s) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void translate(double tx, double ty) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setStroke(Stroke s) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Stroke getStroke() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Object getRenderingHint(Key key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setRenderingHint(Key key, Object value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isSupportsSelection() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isSupportsCMYKColors() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ protected void drawLine(int x1, int y1, int x2, int y2) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void drawString(String string, float x, float y, Object object) {
+ // TODO Auto-generated method stub
+ //System.out.println(string);
+
+ }
+
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFSFont.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFSFont.java
new file mode 100644
index 000000000..a06108fa8
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFSFont.java
@@ -0,0 +1,41 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import org.xhtmlrenderer.render.FSFont;
+
+public class Docx4jFSFont implements FSFont {
+
+ private Docx4jFontDescription _font;
+ private float _size;
+
+ public Docx4jFSFont(Docx4jFontDescription font, float size) {
+ _font = font;
+ _size = size;
+ }
+
+ public float getSize2D() {
+ return _size;
+ }
+
+ public Docx4jFontDescription getFontDescription() {
+ return _font;
+ }
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontContext.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontContext.java
new file mode 100644
index 000000000..7bfcca3a3
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontContext.java
@@ -0,0 +1,26 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2006 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import org.xhtmlrenderer.extend.FontContext;
+
+public class Docx4jFontContext implements FontContext {
+
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontDescription.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontDescription.java
new file mode 100644
index 000000000..c1602c1aa
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontDescription.java
@@ -0,0 +1,139 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import org.xhtmlrenderer.css.constants.IdentValue;
+
+import com.lowagie.text.pdf.BaseFont;
+
+public class Docx4jFontDescription {
+
+ private IdentValue _style;
+ private int _weight;
+
+ private BaseFont _font;
+
+ private float _underlinePosition;
+ private float _underlineThickness;
+
+ private float _yStrikeoutSize;
+ private float _yStrikeoutPosition;
+
+ private boolean _isFromFontFace;
+
+ public Docx4jFontDescription() {
+ }
+
+ public Docx4jFontDescription(BaseFont font) {
+ this(font, IdentValue.NORMAL, 400);
+ }
+
+ public Docx4jFontDescription(BaseFont font, IdentValue style, int weight) {
+ _font = font;
+ _style = style;
+ _weight = weight;
+ setMetricDefaults();
+ }
+
+ public BaseFont getFont() {
+ return _font;
+ }
+
+ public void setFont(BaseFont font) {
+ _font = font;
+ }
+
+ public int getWeight() {
+ return _weight;
+ }
+
+ public void setWeight(int weight) {
+ _weight = weight;
+ }
+
+ public IdentValue getStyle() {
+ return _style;
+ }
+
+ public void setStyle(IdentValue style) {
+ _style = style;
+ }
+
+ /**
+ * @see #getUnderlinePosition()
+ */
+ public float getUnderlinePosition() {
+ return _underlinePosition;
+ }
+
+ /**
+ * This refers to the top of the underline stroke
+ */
+ public void setUnderlinePosition(float underlinePosition) {
+ _underlinePosition = underlinePosition;
+ }
+
+ public float getUnderlineThickness() {
+ return _underlineThickness;
+ }
+
+ public void setUnderlineThickness(float underlineThickness) {
+ _underlineThickness = underlineThickness;
+ }
+
+ public float getYStrikeoutPosition() {
+ return _yStrikeoutPosition;
+ }
+
+ public void setYStrikeoutPosition(float strikeoutPosition) {
+ _yStrikeoutPosition = strikeoutPosition;
+ }
+
+ public float getYStrikeoutSize() {
+ return _yStrikeoutSize;
+ }
+
+ public void setYStrikeoutSize(float strikeoutSize) {
+ _yStrikeoutSize = strikeoutSize;
+ }
+
+ private void setMetricDefaults() {
+ _underlinePosition = -50;
+ _underlineThickness = 50;
+
+ int[] box = _font.getCharBBox('x');
+ if (box != null) {
+ _yStrikeoutPosition = box[3] / 2 + 50;
+ _yStrikeoutSize = 100;
+ } else {
+ // Do what the JDK does, size will be calculated by ITextTextRenderer
+ _yStrikeoutPosition = _font.getFontDescriptor(BaseFont.BBOXURY, 1000f) / 3.0f;
+ }
+ }
+
+ public boolean isFromFontFace() {
+ return _isFromFontFace;
+ }
+
+ public void setFromFontFace(boolean isFromFontFace) {
+ _isFromFontFace = isFromFontFace;
+ }
+ }
+
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontFamily.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontFamily.java
new file mode 100644
index 000000000..ca4a38aaf
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontFamily.java
@@ -0,0 +1,156 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.xhtmlrenderer.css.constants.IdentValue;
+
+public class Docx4jFontFamily {
+
+ private String _name;
+ private List _fontDescriptions;
+
+ public Docx4jFontFamily() {
+ }
+
+ public List getFontDescriptions() {
+ return _fontDescriptions;
+ }
+
+ public void addFontDescription(Docx4jFontDescription descr) {
+ if (_fontDescriptions == null) {
+ _fontDescriptions = new ArrayList();
+ }
+ _fontDescriptions.add(descr);
+ Collections.sort(_fontDescriptions,
+ new Comparator() {
+ public int compare(Object o1, Object o2) {
+ Docx4jFontDescription f1 = (Docx4jFontDescription)o1;
+ Docx4jFontDescription f2 = (Docx4jFontDescription)o2;
+ return f1.getWeight() - f2.getWeight();
+ }
+ });
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public void setName(String name) {
+ _name = name;
+ }
+
+ public Docx4jFontDescription match(int desiredWeight, IdentValue style) {
+ if (_fontDescriptions == null) {
+ throw new RuntimeException("fontDescriptions is null");
+ }
+
+ List candidates = new ArrayList();
+
+ for (Iterator i = _fontDescriptions.iterator(); i.hasNext(); ) {
+ Docx4jFontDescription description = (Docx4jFontDescription)i.next();
+
+ if (description.getStyle() == style) {
+ candidates.add(description);
+ }
+ }
+
+ if (candidates.size() == 0) {
+ if (style == IdentValue.ITALIC) {
+ return match(desiredWeight, IdentValue.OBLIQUE);
+ } else if (style == IdentValue.OBLIQUE) {
+ return match(desiredWeight, IdentValue.NORMAL);
+ } else {
+ candidates.addAll(_fontDescriptions);
+ }
+ }
+
+ Docx4jFontDescription[] matches = (Docx4jFontDescription[])
+ candidates.toArray(new Docx4jFontDescription[candidates.size()]);
+ Docx4jFontDescription result;
+
+ result = findByWeight(matches, desiredWeight, SM_EXACT);
+
+ if (result != null) {
+ return result;
+ } else {
+ if (desiredWeight <= 500) {
+ return findByWeight(matches, desiredWeight, SM_LIGHTER_OR_DARKER);
+ } else {
+ return findByWeight(matches, desiredWeight, SM_DARKER_OR_LIGHTER);
+ }
+ }
+ }
+
+ private static final int SM_EXACT = 1;
+ private static final int SM_LIGHTER_OR_DARKER = 2;
+ private static final int SM_DARKER_OR_LIGHTER = 3;
+
+ private Docx4jFontDescription findByWeight(Docx4jFontDescription[] matches,
+ int desiredWeight, int searchMode) {
+ if (searchMode == SM_EXACT) {
+ for (int i = 0; i < matches.length; i++) {
+ Docx4jFontDescription descr = matches[i];
+ if (descr.getWeight() == desiredWeight) {
+ return descr;
+ }
+ }
+ return null;
+ } else if (searchMode == SM_LIGHTER_OR_DARKER){
+ int offset = 0;
+ Docx4jFontDescription descr = null;
+ for (offset = 0; offset < matches.length; offset++) {
+ descr = matches[offset];
+ if (descr.getWeight() > desiredWeight) {
+ break;
+ }
+ }
+
+ if (offset > 0 && descr.getWeight() > desiredWeight) {
+ return matches[offset-1];
+ } else {
+ return descr;
+ }
+
+ } else if (searchMode == SM_DARKER_OR_LIGHTER) {
+ int offset = 0;
+ Docx4jFontDescription descr = null;
+ for (offset = matches.length - 1; offset >= 0; offset--) {
+ descr = matches[offset];
+ if (descr.getWeight() < desiredWeight) {
+ break;
+ }
+ }
+
+ if (offset != matches.length - 1 && descr.getWeight() < desiredWeight) {
+ return matches[offset+1];
+ } else {
+ return descr;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontResolver.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontResolver.java
new file mode 100644
index 000000000..d12c7e679
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jFontResolver.java
@@ -0,0 +1,233 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.xhtmlrenderer.css.constants.IdentValue;
+import org.xhtmlrenderer.css.value.FontSpecification;
+import org.xhtmlrenderer.extend.FontResolver;
+import org.xhtmlrenderer.layout.SharedContext;
+import org.xhtmlrenderer.render.FSFont;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.pdf.BaseFont;
+
+public class Docx4jFontResolver implements FontResolver {
+
+ private Map _fontFamilies = createInitialFontMap();
+
+ private Map _fontCache = new HashMap();
+
+ private final SharedContext _sharedContext;
+
+ public Docx4jFontResolver(SharedContext sharedContext) {
+ _sharedContext = sharedContext;
+ }
+
+ public void flushCache() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public FSFont resolveFont(SharedContext renderingContext, FontSpecification spec) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private FSFont resolveFont(SharedContext ctx, String[] families, float size,
+ IdentValue weight, IdentValue style, IdentValue variant) {
+
+ if (! (style == IdentValue.NORMAL || style == IdentValue.OBLIQUE
+ || style == IdentValue.ITALIC)) {
+ style = IdentValue.NORMAL;
+ }
+ if (families != null) {
+ for (int i = 0; i < families.length; i++) {
+ FSFont font = resolveFont(ctx, families[i], size, weight, style, variant);
+ if (font != null) {
+ return font;
+ }
+ }
+ }
+
+ return resolveFont(ctx, "Serif", size, weight, style, variant);
+ }
+
+ private FSFont resolveFont(SharedContext ctx, String fontFamily, float size, IdentValue weight, IdentValue style, IdentValue variant) {
+
+ String normalizedFontFamily = normalizeFontFamily(fontFamily);
+
+ String cacheKey = getHashName(normalizedFontFamily, weight, style);
+ Docx4jFontDescription result = (Docx4jFontDescription)_fontCache.get(cacheKey);
+ if (result != null) {
+ return new Docx4jFSFont(result, size);
+ }
+
+ Docx4jFontFamily family = (Docx4jFontFamily)_fontFamilies.get(normalizedFontFamily);
+ if (family != null) {
+ result = family.match(convertWeightToInt(weight), style);
+ if (result != null) {
+ _fontCache.put(cacheKey, result);
+ return new Docx4jFSFont(result, size);
+ }
+ }
+
+ return null;
+ }
+
+ protected static String getHashName(
+ String name, IdentValue weight, IdentValue style) {
+ return name + "-" + weight + "-" + style;
+ }
+
+ private String normalizeFontFamily(String fontFamily) {
+ String result = fontFamily;
+ // strip off the "s if they are there
+ if (result.startsWith("\"")) {
+ result = result.substring(1);
+ }
+ if (result.endsWith("\"")) {
+ result = result.substring(0, result.length() - 1);
+ }
+
+ // normalize the font name
+ if (result.equalsIgnoreCase("serif")) {
+ result = "Serif";
+ }
+ else if (result.equalsIgnoreCase("sans-serif")) {
+ result = "SansSerif";
+ }
+ else if (result.equalsIgnoreCase("monospace")) {
+ result = "Monospaced";
+ }
+
+ return result;
+ }
+
+ private int convertWeightToInt(IdentValue weight) {
+ if (weight == IdentValue.NORMAL) {
+ return 400;
+ } else if (weight == IdentValue.BOLD) {
+ return 700;
+ } else if (weight == IdentValue.FONT_WEIGHT_100) {
+ return 100;
+ } else if (weight == IdentValue.FONT_WEIGHT_200) {
+ return 200;
+ } else if (weight == IdentValue.FONT_WEIGHT_300) {
+ return 300;
+ } else if (weight == IdentValue.FONT_WEIGHT_400) {
+ return 400;
+ } else if (weight == IdentValue.FONT_WEIGHT_500) {
+ return 500;
+ } else if (weight == IdentValue.FONT_WEIGHT_600) {
+ return 600;
+ } else if (weight == IdentValue.FONT_WEIGHT_700) {
+ return 700;
+ } else if (weight == IdentValue.FONT_WEIGHT_800) {
+ return 800;
+ } else if (weight == IdentValue.FONT_WEIGHT_900) {
+ return 900;
+ } else if (weight == IdentValue.LIGHTER) {
+ // FIXME
+ return 400;
+ } else if (weight == IdentValue.BOLDER) {
+ // FIXME
+ return 700;
+ }
+ throw new IllegalArgumentException();
+ }
+
+
+ private static Map createInitialFontMap() {
+ HashMap result = new HashMap();
+
+ try {
+ addCourier(result);
+ addTimes(result);
+ addHelvetica(result);
+ } catch (DocumentException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+
+ return result;
+ }
+
+ private static BaseFont createFont(String name) throws DocumentException, IOException {
+ return BaseFont.createFont(name, "winansi", true);
+ }
+
+ private static void addCourier(HashMap result) throws DocumentException, IOException {
+ Docx4jFontFamily courier = new Docx4jFontFamily();
+ courier.setName("Courier");
+
+ courier.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.COURIER_BOLDOBLIQUE), IdentValue.OBLIQUE, 700));
+ courier.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.COURIER_OBLIQUE), IdentValue.OBLIQUE, 400));
+ courier.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.COURIER_BOLD), IdentValue.NORMAL, 700));
+ courier.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.COURIER), IdentValue.NORMAL, 400));
+
+ result.put("DialogInput", courier);
+ result.put("Monospaced", courier);
+ result.put("Courier", courier);
+ }
+
+ private static void addTimes(HashMap result) throws DocumentException, IOException {
+ Docx4jFontFamily times = new Docx4jFontFamily();
+ times.setName("Times");
+
+ times.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.TIMES_BOLDITALIC), IdentValue.ITALIC, 700));
+ times.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.TIMES_ITALIC), IdentValue.ITALIC, 400));
+ times.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.TIMES_BOLD), IdentValue.NORMAL, 700));
+ times.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.TIMES_ROMAN), IdentValue.NORMAL, 400));
+
+ result.put("Serif", times);
+ result.put("TimesRoman", times);
+ }
+
+ private static void addHelvetica(HashMap result) throws DocumentException, IOException {
+ Docx4jFontFamily helvetica = new Docx4jFontFamily();
+ helvetica.setName("Helvetica");
+
+ helvetica.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.HELVETICA_BOLDOBLIQUE), IdentValue.OBLIQUE, 700));
+ helvetica.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.HELVETICA_OBLIQUE), IdentValue.OBLIQUE, 400));
+ helvetica.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.HELVETICA_BOLD), IdentValue.NORMAL, 700));
+ helvetica.addFontDescription(new Docx4jFontDescription(
+ createFont(BaseFont.HELVETICA), IdentValue.NORMAL, 400));
+
+ result.put("Dialog", helvetica);
+ result.put("SansSerif", helvetica);
+ }
+
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jReplacedElement.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jReplacedElement.java
new file mode 100644
index 000000000..b421be9c4
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jReplacedElement.java
@@ -0,0 +1,29 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2007 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import org.xhtmlrenderer.extend.ReplacedElement;
+import org.xhtmlrenderer.render.BlockBox;
+import org.xhtmlrenderer.render.RenderingContext;
+
+public interface Docx4jReplacedElement extends ReplacedElement
+{
+ public void paint(RenderingContext c, Docx4jDocxOutputDevice outputDevice, BlockBox box);
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jReplacedElementFactory.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jReplacedElementFactory.java
new file mode 100644
index 000000000..4a2e18639
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jReplacedElementFactory.java
@@ -0,0 +1,134 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2006 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.xhtmlrenderer.extend.FSImage;
+import org.xhtmlrenderer.extend.ReplacedElement;
+import org.xhtmlrenderer.extend.ReplacedElementFactory;
+import org.xhtmlrenderer.extend.UserAgentCallback;
+import org.xhtmlrenderer.layout.LayoutContext;
+import org.xhtmlrenderer.render.BlockBox;
+import org.xhtmlrenderer.simple.extend.FormSubmissionListener;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+public class Docx4jReplacedElementFactory implements ReplacedElementFactory {
+ private Docx4jDocxOutputDevice _outputDevice;
+
+ private Map _radioButtonsByElem = new HashMap();
+ private Map _radioButtonsByName = new HashMap();
+
+ public Docx4jReplacedElementFactory(Docx4jDocxOutputDevice outputDevice) {
+ _outputDevice = outputDevice;
+ }
+
+ public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box,
+ UserAgentCallback uac, int cssWidth, int cssHeight) {
+ Element e = box.getElement();
+ if (e == null) {
+ return null;
+ }
+
+ String nodeName = e.getNodeName();
+ if (nodeName.equals("img")) {
+ FSImage fsImage = uac.getImageResource(e.getAttribute("src")).getImage();
+ if (fsImage != null) {
+ if (cssWidth != -1 || cssHeight != -1) {
+ fsImage.scale(cssWidth, cssHeight);
+ }
+ return null; //new ITextImageElement(fsImage);
+ }
+ /*
+ } else if (nodeName.equals("input")) {
+ String type = e.getAttribute("type");
+ if (type.equals("hidden")) {
+ return new EmptyReplacedElement(0, 0);
+ } else if (type.equals("checkbox")) {
+ return new CheckboxFormField(c, box, cssWidth, cssHeight);
+ } else if (type.equals("radio")) {
+ RadioButtonFormField result = new RadioButtonFormField(
+ this, c, box, cssWidth, cssHeight);
+ saveResult(e, result);
+ return result;
+ } else {
+ return new TextFormField(c, box, cssWidth, cssHeight);
+ }
+ } else if (nodeName.equals("select")) {
+ return new SelectFormField(c, box, cssWidth, cssHeight);
+ } else if (isTextarea(e)) {
+ return new TextAreaFormField(c, box, cssWidth, cssHeight);
+ */
+// } else if (nodeName.equals("bookmark")) {
+// // HACK Add box as named anchor and return placeholder
+// BookmarkElement result = new BookmarkElement();
+// if (e.hasAttribute("name")) {
+// String name = e.getAttribute("name");
+// c.addBoxId(name, box);
+// result.setAnchorName(name);
+// }
+// return result;
+ }
+
+ return null;
+ }
+
+ private boolean isTextarea(Element e) {
+ if (! e.getNodeName().equals("textarea")) {
+ return false;
+ }
+
+ Node n = e.getFirstChild();
+ while (n != null) {
+ short nodeType = n.getNodeType();
+ if (nodeType != Node.TEXT_NODE && nodeType != Node.CDATA_SECTION_NODE) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ public void reset() {
+ _radioButtonsByElem = new HashMap();
+ _radioButtonsByName = new HashMap();
+ }
+
+
+
+ public List getRadioButtons(String name) {
+ return (List)_radioButtonsByName.get(name);
+ }
+
+ public void setFormSubmissionListener(FormSubmissionListener listener) {
+ // nothing to do, form submission is handled by pdf readers
+ }
+
+ public void remove(Element e) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jTextRenderer.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jTextRenderer.java
new file mode 100644
index 000000000..ba2baf45e
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jTextRenderer.java
@@ -0,0 +1,115 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2006 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.awt.Rectangle;
+
+import org.xhtmlrenderer.extend.FSGlyphVector;
+import org.xhtmlrenderer.extend.FontContext;
+import org.xhtmlrenderer.extend.OutputDevice;
+import org.xhtmlrenderer.extend.TextRenderer;
+import org.xhtmlrenderer.pdf.ITextFSFont;
+import org.xhtmlrenderer.pdf.ITextFSFontMetrics;
+import org.xhtmlrenderer.pdf.ITextFontResolver.FontDescription;
+import org.xhtmlrenderer.render.FSFont;
+import org.xhtmlrenderer.render.FSFontMetrics;
+import org.xhtmlrenderer.render.JustificationInfo;
+
+import com.lowagie.text.pdf.BaseFont;
+
+public class Docx4jTextRenderer implements TextRenderer {
+ private static float TEXT_MEASURING_DELTA = 0.01f;
+
+ public void setup(FontContext context) {
+ }
+
+ public void drawString(OutputDevice outputDevice, String string, float x, float y) {
+ ((Docx4jDocxOutputDevice)outputDevice).drawString(string, x, y, null);
+ }
+
+ public void drawString(
+ OutputDevice outputDevice, String string, float x, float y, JustificationInfo info) {
+ ((Docx4jDocxOutputDevice)outputDevice).drawString(string, x, y, info);
+ }
+
+ public FSFontMetrics getFSFontMetrics(FontContext context, FSFont font, String string) {
+ FontDescription descr = ((ITextFSFont)font).getFontDescription();
+ BaseFont bf = descr.getFont();
+ float size = font.getSize2D();
+ ITextFSFontMetrics result = new ITextFSFontMetrics();
+ result.setAscent(bf.getFontDescriptor(BaseFont.BBOXURY, size));
+ result.setDescent(-bf.getFontDescriptor(BaseFont.BBOXLLY, size));
+
+ result.setStrikethroughOffset(-descr.getYStrikeoutPosition() / 1000f * size);
+ if (descr.getYStrikeoutSize() != 0) {
+ result.setStrikethroughThickness(descr.getYStrikeoutSize() / 1000f * size);
+ } else {
+ result.setStrikethroughThickness(size / 12.0f);
+ }
+
+ result.setUnderlineOffset(-descr.getUnderlinePosition() / 1000f * size);
+ result.setUnderlineThickness(descr.getUnderlineThickness() / 1000f * size);
+
+ return result;
+ }
+
+ public int getWidth(FontContext context, FSFont font, String string) {
+ BaseFont bf = ((ITextFSFont)font).getFontDescription().getFont();
+ float result = bf.getWidthPoint(string, font.getSize2D());
+ if (result - Math.floor(result) < TEXT_MEASURING_DELTA) {
+ return (int)result;
+ } else {
+ return (int)Math.ceil(result);
+ }
+ }
+
+ public void setFontScale(float scale) {
+ }
+
+ public float getFontScale() {
+ return 1.0f;
+ }
+
+ public void setSmoothingThreshold(float fontsize) {
+ }
+
+ public int getSmoothingLevel() {
+ return 0;
+ }
+
+ public void setSmoothingLevel(int level) {
+ }
+
+ public Rectangle getGlyphBounds(OutputDevice outputDevice, FSFont font, FSGlyphVector fsGlyphVector, int index, float x, float y) {
+ throw new UnsupportedOperationException();
+ }
+
+ public float[] getGlyphPositions(OutputDevice outputDevice, FSFont font, FSGlyphVector fsGlyphVector) {
+ throw new UnsupportedOperationException();
+ }
+
+ public FSGlyphVector getGlyphVector(OutputDevice outputDevice, FSFont font, String string) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void drawGlyphVector(OutputDevice outputDevice, FSGlyphVector vector, float x, float y) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jUserAgent.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jUserAgent.java
new file mode 100644
index 000000000..719f64201
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/Docx4jUserAgent.java
@@ -0,0 +1,93 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2004, 2005 Torbj�rn Gannholm
+ * Copyright (c) 2006 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.xhtmlrenderer.extend.FSImage;
+import org.xhtmlrenderer.layout.SharedContext;
+import org.xhtmlrenderer.resource.ImageResource;
+import org.xhtmlrenderer.swing.NaiveUserAgent;
+import org.xhtmlrenderer.util.XRLog;
+
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfReader;
+
+public class Docx4jUserAgent extends NaiveUserAgent {
+
+// private static final int IMAGE_CACHE_CAPACITY = 32;
+//
+// private SharedContext _sharedContext;
+//
+// private final Docx4jDocxOutputDevice _outputDevice;
+//
+// public Docx4jUserAgent(Docx4jDocxOutputDevice outputDevice) {
+// super(IMAGE_CACHE_CAPACITY);
+// _outputDevice = outputDevice;
+// }
+
+ protected byte[] readStream(InputStream is) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream(is.available());
+ byte[] buf = new byte[10240];
+ int i;
+ while ( (i = is.read(buf)) != -1) {
+ out.write(buf, 0, i);
+ }
+ out.close();
+ return out.toByteArray();
+ }
+
+ public Docx4JFSImage getDocx4JImageResource(String uri) {
+
+ InputStream is = resolveAndOpenStream(uri);
+ if (is != null) {
+ try {
+ return new Docx4JFSImage(readStream(is));
+ } catch (Exception e) {
+ XRLog.exception("Can't read image file; unexpected problem for URI '" + uri + "'", e);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ return null;
+ }
+
+// private void scaleToOutputResolution(Image image) {
+// float factor = _sharedContext.getDotsPerPixel();
+// image.scaleAbsolute(image.getPlainWidth() * factor, image.getPlainHeight() * factor);
+// }
+//
+// public SharedContext getSharedContext() {
+// return _sharedContext;
+// }
+//
+// public void setSharedContext(SharedContext sharedContext) {
+// _sharedContext = sharedContext;
+// }
+}
diff --git a/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/DocxRenderer.java b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/DocxRenderer.java
new file mode 100644
index 000000000..03a8eb6e2
--- /dev/null
+++ b/flying-saucer-docx/src/main/java/org/xhtmlrenderer/docx/DocxRenderer.java
@@ -0,0 +1,258 @@
+/*
+ * {{{ header & license
+ * Copyright (c) 2006 Wisconsin Court System
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * }}}
+ */
+package org.xhtmlrenderer.docx;
+
+import java.awt.Rectangle;
+
+import org.xhtmlrenderer.context.StyleReference;
+import org.xhtmlrenderer.css.sheet.StylesheetInfo;
+import org.xhtmlrenderer.extend.NamespaceHandler;
+import org.xhtmlrenderer.extend.UserInterface;
+import org.xhtmlrenderer.layout.BoxBuilder;
+import org.xhtmlrenderer.layout.Layer;
+import org.xhtmlrenderer.layout.LayoutContext;
+import org.xhtmlrenderer.layout.SharedContext;
+import org.xhtmlrenderer.pdf.ITextFontContext;
+import org.xhtmlrenderer.pdf.ITextFontResolver;
+import org.xhtmlrenderer.render.BlockBox;
+import org.xhtmlrenderer.render.PageBox;
+import org.xhtmlrenderer.render.ViewportBox;
+import org.xhtmlrenderer.simple.extend.XhtmlNamespaceHandler;
+import org.xhtmlrenderer.util.Configuration;
+import org.w3c.dom.CharacterData;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+public class DocxRenderer {
+
+ // These two defaults combine to produce an effective resolution of 96 px to the inch
+ private static final float DEFAULT_DOTS_PER_POINT = 20f * 4f / 3f;
+ private static final int DEFAULT_DOTS_PER_PIXEL = 20;
+ // DPI is then set = 72 * dotsPerPoint
+
+// private static final float DEFAULT_DOTS_PER_POINT = 20f;
+
+
+ private final SharedContext _sharedContext;
+ private final Docx4jDocxOutputDevice _outputDevice;
+
+ private Docx4jUserAgent userAgent;
+ public Docx4jUserAgent getDocx4jUserAgent() {
+ return userAgent;
+ }
+
+ private Document _doc;
+
+
+ private BlockBox _root;
+ public BlockBox getRootBox() {
+ return _root;
+ }
+
+ private LayoutContext _layoutContext;
+
+ public LayoutContext getLayoutContext() {
+ return _layoutContext;
+ }
+
+ private final float _dotsPerPoint;
+
+ public DocxRenderer() {
+ this(new Docx4jUserAgent(), null, DEFAULT_DOTS_PER_POINT, DEFAULT_DOTS_PER_PIXEL);
+ }
+
+ public DocxRenderer(String extraCSS) {
+
+ this(new Docx4jUserAgent(), readCSS(extraCSS), DEFAULT_DOTS_PER_POINT, DEFAULT_DOTS_PER_PIXEL);
+ }
+
+ public DocxRenderer(String extraCSS, float dotsPerPoint) {
+ this(new Docx4jUserAgent(), extraCSS == null ? null : readCSS(extraCSS), dotsPerPoint, DEFAULT_DOTS_PER_PIXEL);
+ }
+
+ private static StylesheetInfo[] readCSS(String css) {
+ // adapted from org.xhtmlrenderer.simple.extend.XhtmlCssOnlyNamespaceHandler
+
+ String media = "all";
+ StylesheetInfo info = new StylesheetInfo();
+ info.setMedia(media);
+
+ info.setType("text/css");
+ info.setTitle("Word styles");
+ info.setOrigin(StylesheetInfo.AUTHOR);
+
+ info.setContent(css);
+
+ StylesheetInfo[] array = { info };
+
+ return array;
+ }
+
+
+ public DocxRenderer(Docx4jUserAgent userAgent) {
+ this(userAgent, null, DEFAULT_DOTS_PER_POINT, DEFAULT_DOTS_PER_PIXEL);
+ }
+
+ public DocxRenderer(float dotsPerPoint, int dotsPerPixel) {
+ this(new Docx4jUserAgent(), null, dotsPerPoint, dotsPerPixel);
+ }
+
+ public DocxRenderer(Docx4jUserAgent userAgent, StylesheetInfo[] extraCSS, float dotsPerPoint, int dotsPerPixel) {
+ _dotsPerPoint = dotsPerPoint;
+
+ _outputDevice = new Docx4jDocxOutputDevice();
+
+// userAgent = new Docx4jUserAgent(_outputDevice);
+ this.userAgent = userAgent;
+ _sharedContext = new SharedContext();
+ _sharedContext.setUserAgentCallback(userAgent);
+ _sharedContext.setCss(new StyleReference(userAgent, extraCSS));
+// userAgent.setSharedContext(_sharedContext);
+// _outputDevice.setSharedContext(_sharedContext);
+
+ /* Fonts
+ *
+ * We need them in order to calculate size of
+ * table cells etc. (which is presumably
+ * important for conversion of fixed width tables).
+ *
+ * Thinking re font resolution:-
+ *
+ * I don't really want a dependency on:
+ *
+ com.lowagie
+ itext
+ 2.1.7
+ *
+ * So it is desirable to have a font resolver
+ * which uses the docx4j font stuff
+ * (which is mainly FOP's EmbedFontInfo).
+ *
+ * When the time comes to make this,
+ * package org.docx4j.fonts should probably be
+ * made into a separate project
+ * (so xhtmlrenderer isn't dependent on docx4j).
+ *
+ * It is expedient to use ITextFontResolver
+ * (and to pay the dependency cost), so that
+ * this release can focus on getting cell widths
+ * right.
+ *
+ * In a later release, I'll try to get rid
+ * of the iText dependency.
+ *
+ */
+ ITextFontResolver fontResolver = new ITextFontResolver(_sharedContext);
+ _sharedContext.setFontResolver(fontResolver);
+
+// Docx4jFontResolver fontResolver = new Docx4jFontResolver(_sharedContext);
+// _sharedContext.setFontResolver(fontResolver);
+
+ Docx4jReplacedElementFactory replacedElementFactory =
+ new Docx4jReplacedElementFactory(_outputDevice);
+ _sharedContext.setReplacedElementFactory(replacedElementFactory);
+
+ _sharedContext.setTextRenderer(new Docx4jTextRenderer());
+ _sharedContext.setDPI(72*_dotsPerPoint);
+ _sharedContext.setDotsPerPixel(dotsPerPixel);
+ _sharedContext.setPrint(true);
+ _sharedContext.setInteractive(false);
+ }
+
+ public SharedContext getSharedContext() {
+ return _sharedContext;
+ }
+
+
+ public Document loadDocument(final String uri) {
+ return _sharedContext.getUac().getXMLResource(uri).getDocument();
+ }
+
+ public void setDocument(Document doc, String url) {
+ setDocument(doc, url, new XhtmlNamespaceHandler());
+ }
+
+ private void setDocument(Document doc, String url, NamespaceHandler nsh) {
+ _doc = doc;
+
+// getFontResolver().flushFontFaceFonts();
+
+ _sharedContext.reset();
+ if (Configuration.isTrue("xr.cache.stylesheets", true)) {
+ _sharedContext.getCss().flushStyleSheets();
+ } else {
+ _sharedContext.getCss().flushAllStyleSheets();
+ }
+ _sharedContext.setBaseURL(url);
+ _sharedContext.setNamespaceHandler(nsh);
+ _sharedContext.getCss().setDocumentContext(
+ _sharedContext, _sharedContext.getNamespaceHandler(),
+ doc, new NullUserInterface());
+// getFontResolver().importFontFaces(_sharedContext.getCss().getFontFaceRules());
+ }
+
+
+ public void layout() {
+ LayoutContext c = newLayoutContext();
+ BlockBox root = BoxBuilder.createRootBox(c, _doc);
+ root.setContainingBlock(new ViewportBox(getInitialExtents(c)));
+ root.layout(c);
+
+// Dimension dim = root.getLayer().getPaintingDimension(c);
+// root.getLayer().trimEmptyPages(c, dim.height);
+// root.getLayer().layoutPages(c);
+
+ _root = root;
+ _layoutContext = c;
+ }
+
+ private Rectangle getInitialExtents(LayoutContext c) {
+ PageBox first = Layer.createPageBox(c, "first");
+
+ return new Rectangle(0, 0, first.getContentWidth(c), first.getContentHeight(c));
+ }
+
+
+ private LayoutContext newLayoutContext() {
+ LayoutContext result = _sharedContext.newLayoutContextInstance();
+ result.setFontContext(new ITextFontContext());
+
+ _sharedContext.getTextRenderer().setup(result.getFontContext());
+
+ return result;
+ }
+
+
+ private static final class NullUserInterface implements UserInterface {
+ public boolean isHover(Element e) {
+ return false;
+ }
+
+ public boolean isActive(Element e) {
+ return false;
+ }
+
+ public boolean isFocus(Element e) {
+ return false;
+ }
+ }
+}
diff --git a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java
index 8d0f643ed..0c1a824b0 100644
--- a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java
+++ b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java
@@ -63,6 +63,7 @@
import org.xhtmlrenderer.extend.OutputDevice;
import org.xhtmlrenderer.layout.SharedContext;
import org.xhtmlrenderer.pdf.ITextFontResolver.FontDescription;
+import org.xhtmlrenderer.pdf.util.TextUtilPDF;
import org.xhtmlrenderer.render.AbstractOutputDevice;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.render.Box;
@@ -517,7 +518,7 @@ public void drawString(String s, float x, float y, JustificationInfo info) {
}
cb.setTextMatrix((float) mx[0], b, c, (float) mx[3], (float) mx[4], (float) mx[5]);
if (info == null) {
- cb.showText(s);
+ cb.showText(TextUtilPDF.bidifyString(s));
} else {
PdfTextArray array = makeJustificationArray(s, info);
cb.showText(array);
diff --git a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/util/TextUtilPDF.java b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/util/TextUtilPDF.java
new file mode 100644
index 000000000..3526262c4
--- /dev/null
+++ b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/util/TextUtilPDF.java
@@ -0,0 +1,53 @@
+package org.xhtmlrenderer.pdf.util;
+
+import java.text.Bidi;
+
+public class TextUtilPDF {
+
+
+ /**
+ * This method transforms a left to right string into a Bidirectional string.
+ *
+ * @param String text
+ * @return String
+ */
+ public static String bidifyString(String s){
+ Bidi bidi = new Bidi(s, Bidi.DIRECTION_LEFT_TO_RIGHT);
+
+ int count = bidi.getRunCount();
+ byte[] levels = new byte[count];
+ Integer[] runs = new Integer[count];
+
+ for (int i = 0; i < count; i++)
+ {
+ levels[i] = (byte)bidi.getRunLevel(i);
+ runs[i] = i;
+ }
+
+ Bidi.reorderVisually(levels, 0, runs, 0, count);
+
+ StringBuilder result = new StringBuilder();
+
+ for (int i = 0; i < count; i++)
+ {
+ int index = runs[i];
+ int start = bidi.getRunStart(index);
+ int end = bidi.getRunLimit(index);
+ int level = levels[index];
+
+ if ((level & 1) != 0)
+ {
+ for (; --end >= start;)
+ {
+ result.append(s.charAt(end));
+ }
+ }
+ else
+ {
+ result.append(s, start, end);
+ }
+ }
+ return result.toString();
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 57beef1ad..24f1981cf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
pom
Flying Saucer
- Flying Saucer is a CSS 2.1 renderer written in Java. It supports Java2D, PDF, and SWT output.
+ Flying Saucer is a CSS 2.1 renderer written in Java. It supports Java2D, PDF, DOCX and SWT output.
http://code.google.com/p/flying-saucer/
@@ -37,6 +37,7 @@
flying-saucer-pdf
flying-saucer-pdf-osgi
flying-saucer-pdf-itext5
+ flying-saucer-docx
flying-saucer-log4j
flying-saucer-swt
flying-saucer-swt-examples