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