aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/META-INF/services/org.apache.fop.fo.ElementMapping1
-rw-r--r--src/java/org/apache/fop/apps/FopFactory.java45
-rw-r--r--src/java/org/apache/fop/area/Area.java62
-rw-r--r--src/java/org/apache/fop/area/RegionViewport.java7
-rw-r--r--src/java/org/apache/fop/fo/ElementMappingRegistry.java8
-rw-r--r--src/java/org/apache/fop/fo/FOTreeBuilder.java9
-rw-r--r--src/java/org/apache/fop/fo/FObj.java41
-rw-r--r--src/java/org/apache/fop/fo/PropertyList.java17
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java13
-rw-r--r--src/java/org/apache/fop/fo/extensions/OldExtensionElementMapping.java36
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/render/RendererContextConstants.java6
-rw-r--r--src/java/org/apache/fop/render/xml/XMLRenderer.java18
-rw-r--r--src/java/org/apache/fop/util/QName.java113
-rw-r--r--src/java/org/apache/fop/util/UnitConv.java118
-rw-r--r--src/sandbox/META-INF/services/org.apache.fop.render.XMLHandler1
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java52
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java97
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java43
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java502
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java430
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java137
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java143
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java815
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java92
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java144
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/PCLStream.java55
-rw-r--r--src/sandbox/org/apache/fop/render/pcl/package.html4
-rw-r--r--test/java/org/apache/fop/UtilityCodeTestSuite.java2
-rw-r--r--test/java/org/apache/fop/util/UnitConvTestCase.java45
31 files changed, 2911 insertions, 156 deletions
diff --git a/src/java/META-INF/services/org.apache.fop.fo.ElementMapping b/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
index b182c3a99..58bae87a3 100644
--- a/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
+++ b/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
@@ -2,6 +2,7 @@ org.apache.fop.fo.FOElementMapping
org.apache.fop.fo.extensions.svg.SVGElementMapping
org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping
org.apache.fop.fo.extensions.ExtensionElementMapping
+org.apache.fop.fo.extensions.OldExtensionElementMapping
org.apache.fop.fo.extensions.xmp.XMPElementMapping
org.apache.fop.fo.extensions.xmp.RDFElementMapping
org.apache.fop.render.ps.extensions.PSExtensionElementMapping \ No newline at end of file
diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java
index a0acb36a3..152d75829 100644
--- a/src/java/org/apache/fop/apps/FopFactory.java
+++ b/src/java/org/apache/fop/apps/FopFactory.java
@@ -22,17 +22,23 @@ import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
+import org.xml.sax.SAXException;
+
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.hyphenation.HyphenationTreeResolver;
@@ -41,7 +47,6 @@ import org.apache.fop.layoutmgr.LayoutManagerMaker;
import org.apache.fop.render.RendererFactory;
import org.apache.fop.render.XMLHandlerRegistry;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
-import org.xml.sax.SAXException;
/**
* Factory class which instantiates new Fop and FOUserAgent instances. This class also holds
@@ -110,6 +115,8 @@ public class FopFactory {
/** Optional overriding LayoutManagerMaker */
private LayoutManagerMaker lmMakerOverride = null;
+
+ private Set ignoredNamespaces = new java.util.HashSet();
/**
* Main constructor.
@@ -431,6 +438,40 @@ public class FopFactory {
this.pageWidth = pageWidth;
}
+ /**
+ * Adds a namespace to the set of ignored namespaces.
+ * If FOP encounters a namespace which it cannot handle, it issues a warning except if this
+ * namespace is in the ignored set.
+ * @param namespaceURI the namespace URI
+ */
+ public void ignoreNamespace(String namespaceURI) {
+ this.ignoredNamespaces.add(namespaceURI);
+ }
+
+ /**
+ * Adds a collection of namespaces to the set of ignored namespaces.
+ * If FOP encounters a namespace which it cannot handle, it issues a warning except if this
+ * namespace is in the ignored set.
+ * @param namespaceURIs the namespace URIs
+ */
+ public void ignoreNamespaces(Collection namespaceURIs) {
+ this.ignoredNamespaces.addAll(namespaceURIs);
+ }
+
+ /**
+ * Indicates whether a namespace URI is on the ignored list.
+ * @param namespaceURI the namespace URI
+ * @return true if the namespace is ignored by FOP
+ */
+ public boolean isNamespaceIgnored(String namespaceURI) {
+ return this.ignoredNamespaces.contains(namespaceURI);
+ }
+
+ /** @return the set of namespaces that are ignored by FOP */
+ public Set getIgnoredNamespace() {
+ return Collections.unmodifiableSet(this.ignoredNamespaces);
+ }
+
//------------------------------------------- Configuration stuff
/**
diff --git a/src/java/org/apache/fop/area/Area.java b/src/java/org/apache/fop/area/Area.java
index b0916abda..51c95f0ca 100644
--- a/src/java/org/apache/fop/area/Area.java
+++ b/src/java/org/apache/fop/area/Area.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,13 +20,15 @@ package org.apache.fop.area;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.Map;
-import java.util.HashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.datatypes.ColorType;
import org.apache.fop.traits.BorderProps;
+import org.apache.fop.util.QName;
// If the area appears more than once in the output
// or if the area has external data it is cached
@@ -129,8 +131,11 @@ public class Area implements Serializable {
/**
* Traits for this area stored in a HashMap
*/
- protected HashMap props = null;
+ protected Map props = null;
+ /** Foreign attributes */
+ protected Map foreignAttributes = null;
+
/**
* logging instance
*/
@@ -462,6 +467,57 @@ public class Area implements Serializable {
}
}
+ /**
+ * Sets a foreign attribute.
+ * @param name the qualified name of the attribute
+ * @param value the attribute value
+ */
+ public void setForeignAttribute(QName name, String value) {
+ if (this.foreignAttributes == null) {
+ this.foreignAttributes = new java.util.HashMap();
+ }
+ this.foreignAttributes.put(name, value);
+ }
+
+ /**
+ * Set foreign attributes from a Map.
+ * @param atts a Map with attributes (keys: QName, values: String)
+ */
+ public void setForeignAttributes(Map atts) {
+ if (atts.size() == 0) {
+ return;
+ }
+ Iterator iter = atts.keySet().iterator();
+ while (iter.hasNext()) {
+ QName qName = (QName)iter.next();
+ String value = (String)atts.get(qName);
+ //The casting is only to ensure type safety (too bad we can't use generics, yet)
+ setForeignAttribute(qName, value);
+ }
+ }
+
+ /**
+ * Returns the value of a foreign attribute on the area.
+ * @param name the qualified name of the attribute
+ * @return the attribute value or null if it isn't set
+ */
+ public String getForeignAttributeValue(QName name) {
+ if (this.foreignAttributes != null) {
+ return (String)this.foreignAttributes.get(name);
+ } else {
+ return null;
+ }
+ }
+
+ /** @return the foreign attributes associated with this area */
+ public Map getForeignAttributes() {
+ if (this.foreignAttributes != null) {
+ return Collections.unmodifiableMap(this.foreignAttributes);
+ } else {
+ return Collections.EMPTY_MAP;
+ }
+ }
+
/** @see java.lang.Object#toString() */
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
diff --git a/src/java/org/apache/fop/area/RegionViewport.java b/src/java/org/apache/fop/area/RegionViewport.java
index ff3146e17..23017fded 100644
--- a/src/java/org/apache/fop/area/RegionViewport.java
+++ b/src/java/org/apache/fop/area/RegionViewport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -115,7 +115,10 @@ public class RegionViewport extends Area implements Cloneable {
RegionViewport rv = new RegionViewport((Rectangle2D)viewArea.clone());
rv.regionReference = (RegionReference)regionReference.clone();
if (props != null) {
- rv.props = (HashMap)props.clone();
+ rv.props = new HashMap(props);
+ }
+ if (foreignAttributes != null) {
+ rv.foreignAttributes = new HashMap(foreignAttributes);
}
return rv;
}
diff --git a/src/java/org/apache/fop/fo/ElementMappingRegistry.java b/src/java/org/apache/fop/fo/ElementMappingRegistry.java
index 8ab5a3d98..9bb0ab19b 100644
--- a/src/java/org/apache/fop/fo/ElementMappingRegistry.java
+++ b/src/java/org/apache/fop/fo/ElementMappingRegistry.java
@@ -163,4 +163,12 @@ public class ElementMappingRegistry {
return mapping.getDOMImplementation();
}
+ /**
+ * Indicates whether a namespace is known to FOP.
+ * @param namespaceURI the namespace URI
+ * @return true if the namespace is known.
+ */
+ public boolean isKnownNamespace(String namespaceURI) {
+ return this.namespaces.containsKey(namespaceURI);
+ }
}
diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java
index 2711bd169..f64bae87a 100644
--- a/src/java/org/apache/fop/fo/FOTreeBuilder.java
+++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java
@@ -304,6 +304,10 @@ public class FOTreeBuilder extends DefaultHandler {
try {
foNode = fobjMaker.make(currentFObj);
+ if (rootFObj == null) {
+ rootFObj = (Root) foNode;
+ rootFObj.setFOEventHandler(foEventHandler);
+ }
propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList);
foNode.startOfNode();
@@ -325,10 +329,7 @@ public class FOTreeBuilder extends DefaultHandler {
delegate = subHandler;
}
- if (rootFObj == null) {
- rootFObj = (Root) foNode;
- rootFObj.setFOEventHandler(foEventHandler);
- } else {
+ if (currentFObj != null) {
currentFObj.addChildNode(foNode);
}
diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java
index af45735e6..44da27900 100644
--- a/src/java/org/apache/fop/fo/FObj.java
+++ b/src/java/org/apache/fop/fo/FObj.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
package org.apache.fop.fo;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -29,6 +30,7 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.properties.PropertyMaker;
+import org.apache.fop.util.QName;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
@@ -46,6 +48,9 @@ public abstract class FObj extends FONode implements Constants {
/** The list of extension attachments, null if none */
private List extensionAttachments = null;
+ /** The map of foreign attributes, null if none */
+ private Map foreignAttributes = null;
+
/** Used to indicate if this FO is either an Out Of Line FO (see rec)
or a descendant of one. Used during validateChildNode() FO
validation.
@@ -473,6 +478,40 @@ public abstract class FObj extends FONode implements Constants {
return extensionAttachments;
}
}
+
+ /**
+ * Adds a foreign attribute to this FObj.
+ * @param uri the namespace URI
+ * @param qName the fully qualified name
+ * @param value the attribute value
+ * @todo Handle this over FOP's property mechanism so we can use inheritance.
+ */
+ public void addForeignAttribute(String uri,
+ String qName, String value) {
+ if (qName == null) {
+ throw new NullPointerException("Parameter qName must not be null");
+ }
+ if (foreignAttributes == null) {
+ foreignAttributes = new java.util.HashMap();
+ }
+ String localName = qName;
+ String prefix = null;
+ int p = localName.indexOf(':');
+ if (p > 0) {
+ prefix = localName.substring(0, p);
+ localName = localName.substring(p + 1);
+ }
+ foreignAttributes.put(new QName(uri, prefix, localName), value);
+ }
+
+ /** @return the map of foreign attributes */
+ public Map getForeignAttributes() {
+ if (foreignAttributes == null) {
+ return Collections.EMPTY_MAP;
+ } else {
+ return foreignAttributes;
+ }
+ }
}
diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java
index 7d4b0132d..5bf00346a 100644
--- a/src/java/org/apache/fop/fo/PropertyList.java
+++ b/src/java/org/apache/fop/fo/PropertyList.java
@@ -24,7 +24,7 @@ import org.xml.sax.Attributes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FopFactory;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
import org.apache.fop.fo.properties.CommonAccessibility;
@@ -295,13 +295,20 @@ public abstract class PropertyList {
}
String attributeNS;
+ FopFactory factory = getFObj().getUserAgent().getFactory();
for (int i = 0; i < attributes.getLength(); i++) {
/* convert all attributes with the same namespace as the fo element for this fObj */
- attributeNS = attributes.getURI(i);
- if (attributeNS.length() == 0 || attributeNS.equals(fobj.getNamespaceURI())) {
- attributeName = attributes.getQName(i);
- attributeValue = attributes.getValue(i);
+ attributeNS = attributes.getURI(i);
+ attributeName = attributes.getQName(i);
+ attributeValue = attributes.getValue(i);
+ if (attributeNS.length() == 0) {
convertAttributeToProperty(attributes, attributeName, attributeValue);
+ } else if (!factory.isNamespaceIgnored(attributeNS)) {
+ if (factory.getElementMappingRegistry().isKnownNamespace(attributeNS)) {
+ getFObj().addForeignAttribute(attributeNS, attributeName, attributeValue);
+ } else {
+ handleInvalidProperty(attributeName);
+ }
}
}
}
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
index 310b61550..5f823d2a3 100644
--- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
+++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,16 +19,17 @@
package org.apache.fop.fo.extensions;
import org.apache.fop.fo.ElementMapping;
+import org.apache.fop.fo.UnknownXMLObj;
import java.util.HashMap;
/**
- * Element mapping for the pdf bookmark extension.
- * This sets up the mapping for the classes that handle the
- * pdf bookmark extension.
+ * Element mapping for FOP's proprietary extension to XSL-FO.
*/
public class ExtensionElementMapping extends ElementMapping {
- public static String URI = "http://xml.apache.org/fop/extensions";
+
+ /** The FOP extension namespace URI */
+ public static final String URI = "http://xmlgraphics.apache.org/fop/extensions";
/**
* Constructor.
@@ -43,6 +44,8 @@ public class ExtensionElementMapping extends ElementMapping {
protected void initialize() {
if (foObjs == null) {
foObjs = new HashMap();
+ foObjs.put("outline", new UnknownXMLObj.Maker(URI));
+ foObjs.put("label", new UnknownXMLObj.Maker(URI));
}
}
}
diff --git a/src/java/org/apache/fop/fo/extensions/OldExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/OldExtensionElementMapping.java
new file mode 100644
index 000000000..40ff485e5
--- /dev/null
+++ b/src/java/org/apache/fop/fo/extensions/OldExtensionElementMapping.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fo.extensions;
+
+/**
+ * Element mapping for the old FOP extension namespace. It is simply mapped to the new namespace.
+ */
+public class OldExtensionElementMapping extends ExtensionElementMapping {
+
+ /** The old FOP extension namespace URI (FOP 0.20.5 and earlier) */
+ public static final String URI = "http://xml.apache.org/fop/extensions";
+
+ /**
+ * Constructor.
+ */
+ public OldExtensionElementMapping() {
+ namespaceURI = URI;
+ }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
index 5fc39972a..d8ded23fe 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -373,4 +373,12 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
&& isFinished());
}
+ /**
+ * Transfers foreign attributes from the formatting object to the area.
+ * @param targetArea the area to set the attributes on
+ */
+ protected void transferForeignAttributes(Area targetArea) {
+ Map atts = getFObj().getForeignAttributes();
+ targetArea.setForeignAttributes(atts);
+ }
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
index a02e2363d..cfe74ae4f 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
@@ -209,6 +209,7 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage
Area viewportArea = getChildArea();
TraitSetter.setProducerID(viewportArea, fobj.getId());
+ transferForeignAttributes(viewportArea);
Viewport vp = new Viewport(viewportArea);
TraitSetter.setProducerID(vp, fobj.getId());
diff --git a/src/java/org/apache/fop/render/RendererContextConstants.java b/src/java/org/apache/fop/render/RendererContextConstants.java
index c744a82a1..742d56f68 100644
--- a/src/java/org/apache/fop/render/RendererContextConstants.java
+++ b/src/java/org/apache/fop/render/RendererContextConstants.java
@@ -44,4 +44,10 @@ public interface RendererContextConstants {
/** The configuration for the XMLHandler. */
String HANDLER_CONFIGURATION = "cfg";
+ /**
+ * An optional Map (keys: QName, values: String) with attributes containing additional hints
+ * for rendering.
+ */
+ String FOREIGN_ATTRIBUTES = "foreign-attributes";
+
}
diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java
index 00b204ee0..e97068968 100644
--- a/src/java/org/apache/fop/render/xml/XMLRenderer.java
+++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java
@@ -45,6 +45,7 @@ import org.apache.fop.render.PrintRenderer;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.XMLHandler;
+import org.apache.fop.util.QName;
import org.apache.fop.util.XMLizable;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FOPException;
@@ -279,6 +280,16 @@ public class XMLRenderer extends PrintRenderer {
* @param name name of the attribute
* @param value value of the attribute
*/
+ protected void addAttribute(QName name, String value) {
+ atts.addAttribute(name.getNamespaceURI(), name.getLocalName(), name.getQName(),
+ CDATA, value);
+ }
+
+ /**
+ * Adds a new attribute to the protected member variable "atts".
+ * @param name name of the attribute
+ * @param value value of the attribute
+ */
protected void addAttribute(String name, int value) {
addAttribute(name, Integer.toString(value));
}
@@ -376,6 +387,13 @@ public class XMLRenderer extends PrintRenderer {
}
}
}
+
+ //Transfer foreign attributes
+ Iterator iter = area.getForeignAttributes().entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry)iter.next();
+ addAttribute((QName)entry.getKey(), (String)entry.getValue());
+ }
}
private String createString(Rectangle2D rect) {
diff --git a/src/java/org/apache/fop/util/QName.java b/src/java/org/apache/fop/util/QName.java
new file mode 100644
index 000000000..ccfe05638
--- /dev/null
+++ b/src/java/org/apache/fop/util/QName.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import java.io.Serializable;
+
+/**
+ * Represents a qualified name of an XML element or an XML attribute.
+ * <p>
+ * Note: This class allows to carry a namespace prefix but it is not used in the equals() and
+ * hashCode() methods.
+ */
+public class QName implements Serializable {
+
+ private static final long serialVersionUID = -5225376740044770690L;
+
+ private String namespaceURI;
+ private String localName;
+ private String prefix;
+ private int hashCode;
+
+ /**
+ * Main constructor.
+ * @param namespaceURI the namespace URI
+ * @param prefix the namespace prefix, may be null
+ * @param localName the local name
+ */
+ public QName(String namespaceURI, String prefix, String localName) {
+ if (localName == null) {
+ throw new NullPointerException("Parameter localName must not be null");
+ }
+ if (localName.length() == 0) {
+ throw new IllegalArgumentException("Parameter localName must not be empty");
+ }
+ this.namespaceURI = namespaceURI;
+ this.prefix = prefix;
+ this.localName = localName;
+ this.hashCode = toHashString().hashCode();
+ }
+
+ /** @return the namespace URI */
+ public String getNamespaceURI() {
+ return this.namespaceURI;
+ }
+
+ /** @return the namespace prefix */
+ public String getPrefix() {
+ return this.prefix;
+ }
+
+ /** @return the local name */
+ public String getLocalName() {
+ return this.localName;
+ }
+
+ /** @return the fully qualified name */
+ public String getQName() {
+ return getPrefix() != null ? getPrefix() + ':' + getLocalName() : getLocalName();
+ }
+
+ /** @see java.lang.Object#hashCode() */
+ public int hashCode() {
+ return this.hashCode;
+ }
+
+ /** @see java.lang.Object#equals(java.lang.Object) */
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ } else if (obj == this) {
+ return true;
+ } else {
+ if (obj instanceof QName) {
+ QName other = (QName)obj;
+ if ((getNamespaceURI() == null && other.getNamespaceURI() == null)
+ || getNamespaceURI().equals(other.getNamespaceURI())) {
+ return getLocalName().equals(other.getLocalName());
+ }
+ }
+ }
+ return false;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ return prefix != null
+ ? (prefix + ":" + localName)
+ : toHashString();
+ }
+
+ private String toHashString() {
+ return (namespaceURI != null
+ ? ("{" + namespaceURI + "}" + localName)
+ : localName);
+ }
+
+}
diff --git a/src/java/org/apache/fop/util/UnitConv.java b/src/java/org/apache/fop/util/UnitConv.java
new file mode 100644
index 000000000..0c16cdc3f
--- /dev/null
+++ b/src/java/org/apache/fop/util/UnitConv.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id: FixedLength.java 279656 2005-09-08 22:06:48Z pietsch $ */
+
+package org.apache.fop.util;
+
+/**
+ * Utility class for unit conversions.
+ */
+public final class UnitConv {
+
+ /** conversion factory from millimeters to inches. */
+ public static final float IN2MM = 25.4f;
+
+ /** conversion factory from centimeters to inches. */
+ public static final float IN2CM = 2.54f;
+
+ /** conversion factory from inches to points. */
+ public static final int IN2PT = 72;
+
+ /**
+ * Converts millimeters (mm) to points (pt)
+ * @param mm the value in mm
+ * @return the value in pt
+ */
+ public static double mm2pt(double mm) {
+ return mm * IN2PT / IN2MM;
+ }
+
+ /**
+ * Converts millimeters (mm) to millipoints (mpt)
+ * @param mm the value in mm
+ * @return the value in mpt
+ */
+ public static double mm2mpt(double mm) {
+ return mm * 1000 * IN2PT / IN2MM;
+ }
+
+ /**
+ * Converts points (pt) to millimeters (mm)
+ * @param pt the value in pt
+ * @return the value in mm
+ */
+ public static double pt2mm(double pt) {
+ return pt * IN2MM / IN2PT;
+ }
+
+ /**
+ * Converts millimeters (mm) to inches (in)
+ * @param mm the value in mm
+ * @return the value in inches
+ */
+ public static double mm2in(double mm) {
+ return mm / IN2MM;
+ }
+
+ /**
+ * Converts inches (in) to millimeters (mm)
+ * @param in the value in inches
+ * @return the value in mm
+ */
+ public static double in2mm(double in) {
+ return in * IN2MM;
+ }
+
+ /**
+ * Converts inches (in) to millipoints (mpt)
+ * @param in the value in inches
+ * @return the value in mpt
+ */
+ public static double in2mpt(double in) {
+ return in * IN2PT * 1000;
+ }
+
+ /**
+ * Converts millipoints (mpt) to inches (in)
+ * @param mpt the value in mpt
+ * @return the value in inches
+ */
+ public static double mpt2in(double mpt) {
+ return mpt / IN2PT / 1000;
+ }
+
+ /**
+ * Converts millimeters (mm) to pixels (px)
+ * @param mm the value in mm
+ * @param resolution the resolution in dpi (dots per inch)
+ * @return the value in pixels
+ */
+ public static int mm2px(double mm, int resolution) {
+ return (int)Math.round(mm2in(mm) * resolution);
+ }
+
+ /**
+ * Converts millipoints (mpt) to pixels (px)
+ * @param mpt the value in mpt
+ * @param resolution the resolution in dpi (dots per inch)
+ * @return the value in pixels
+ */
+ public static int mpt2px(double mpt, int resolution) {
+ return (int)Math.round(mpt2in(mpt) * resolution);
+ }
+
+}
diff --git a/src/sandbox/META-INF/services/org.apache.fop.render.XMLHandler b/src/sandbox/META-INF/services/org.apache.fop.render.XMLHandler
new file mode 100644
index 000000000..cbb7eaa67
--- /dev/null
+++ b/src/sandbox/META-INF/services/org.apache.fop.render.XMLHandler
@@ -0,0 +1 @@
+org.apache.fop.render.pcl.PCLSVGHandler
diff --git a/src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java b/src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java
new file mode 100644
index 000000000..43580446f
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/DefaultMonochromeBitmapConverter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.awt.RenderingHints;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorConvertOp;
+import java.awt.image.RenderedImage;
+
+/**
+ * Default implementation of the MonochromeBitmapConverter which uses the Java Class Library
+ * to convert grayscale bitmaps to monochrome bitmaps.
+ */
+public class DefaultMonochromeBitmapConverter implements
+ MonochromeBitmapConverter {
+
+ /** @see MonochromeBitmapConverter#setHint(java.lang.String, java.lang.String) */
+ public void setHint(String name, String value) {
+ //ignore, not supported
+ }
+
+ /** @see MonochromeBitmapConverter#convertToMonochrome(java.awt.image.BufferedImage) */
+ public RenderedImage convertToMonochrome(BufferedImage img) {
+ BufferedImage buf = new BufferedImage(img.getWidth(), img.getHeight(),
+ BufferedImage.TYPE_BYTE_BINARY);
+ RenderingHints hints = new RenderingHints(null);
+ //This hint doesn't seem to make a difference :-(
+ hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
+ ColorConvertOp op = new ColorConvertOp(
+ ColorSpace.getInstance(ColorSpace.CS_GRAY), hints);
+ op.filter(img, buf);
+ return buf;
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java b/src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java
new file mode 100644
index 000000000..4818b2b1d
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/JAIMonochromeBitmapConverter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.ParameterBlock;
+
+import javax.media.jai.ColorCube;
+import javax.media.jai.ImageLayout;
+import javax.media.jai.JAI;
+import javax.media.jai.KernelJAI;
+import javax.media.jai.LookupTableJAI;
+import javax.media.jai.PlanarImage;
+
+/**
+ * Implementation of the MonochromeBitmapConverter which uses Java Advanced Imaging (JAI)
+ * to convert grayscale bitmaps to monochrome bitmaps. JAI provides better dithering options
+ * including error diffusion dithering.
+ * <p>
+ * If you call setHint("quality", "true") on the instance you can enabled error diffusion
+ * dithering which produces a nicer result but is also a lot slower.
+ */
+public class JAIMonochromeBitmapConverter implements
+ MonochromeBitmapConverter {
+
+ private boolean isErrorDiffusion = false;
+
+ /** @see MonochromeBitmapConverter#setHint(java.lang.String, java.lang.String) */
+ public void setHint(String name, String value) {
+ if ("quality".equalsIgnoreCase(name)) {
+ isErrorDiffusion = "true".equalsIgnoreCase(value);
+ }
+ }
+
+ /** @see MonochromeBitmapConverter#convertToMonochrome(java.awt.image.BufferedImage) */
+ public RenderedImage convertToMonochrome(BufferedImage img) {
+ if (img.getColorModel().getColorSpace().getNumComponents() != 1) {
+ throw new IllegalArgumentException("Source image must be a grayscale image!");
+ }
+
+ // Load the ParameterBlock for the dithering operation
+ // and set the operation name.
+ ParameterBlock pb = new ParameterBlock();
+ pb.addSource(img);
+ String opName = null;
+ if (isErrorDiffusion) {
+ opName = "errordiffusion";
+ LookupTableJAI lut = new LookupTableJAI(new byte[] {(byte)0x00, (byte)0xff});
+ pb.add(lut);
+ pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG);
+ } else {
+ opName = "ordereddither";
+ //Create the color cube.
+ ColorCube colorMap = ColorCube.createColorCube(DataBuffer.TYPE_BYTE,
+ 0, new int[] {2});
+ pb.add(colorMap);
+ pb.add(KernelJAI.DITHER_MASK_441);
+ }
+
+ //Create an image layout for a monochrome b/w image
+ ImageLayout layout = new ImageLayout();
+ byte[] map = new byte[] {(byte)0x00, (byte)0xff};
+ ColorModel cm = new IndexColorModel(1, 2, map, map, map);
+ layout.setColorModel(cm);
+
+ // Create a hint containing the layout.
+ RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
+
+ // Dither the image.
+ PlanarImage dst = JAI.create(opName, pb, hints);
+
+ //Convert it to a BufferedImage
+ return dst.getAsBufferedImage();
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java b/src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java
new file mode 100644
index 000000000..f2db9d798
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/MonochromeBitmapConverter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * Interface for converters that convert grayscale images to monochrome (1-bit) bitmap images.
+ */
+public interface MonochromeBitmapConverter {
+
+ /**
+ * Sets a hint to the implementation
+ * @param name the name of the hint
+ * @param value the value
+ */
+ void setHint(String name, String value);
+
+ /**
+ * Converts a grayscale bitmap image to a monochrome (1-bit) b/w bitmap image.
+ * @param img the grayscale image
+ * @return the converted monochrome image
+ */
+ RenderedImage convertToMonochrome(BufferedImage img);
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java b/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java
new file mode 100644
index 000000000..e0d3f9305
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.color.ColorSpace;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorConvertOp;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+
+/**
+ * This class provides methods for generating PCL print files.
+ */
+public class PCLGenerator {
+
+ /** The ESC (escape) character */
+ public static final char ESC = '\033';
+
+ /** A list of all supported resolutions in PCL (values in dpi) */
+ public static final int[] PCL_RESOLUTIONS = new int[] {75, 100, 150, 200, 300, 600};
+
+ private final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
+ private final DecimalFormat df2 = new DecimalFormat("0.##", symbols);
+ private final DecimalFormat df4 = new DecimalFormat("0.####", symbols);
+
+ private OutputStream out;
+
+ /**
+ * Main constructor.
+ * @param out the OutputStream to write the PCL stream to
+ */
+ public PCLGenerator(OutputStream out) {
+ this.out = out;
+ }
+
+ /** @return the OutputStream that this generator writes to */
+ public OutputStream getOutputStream() {
+ return this.out;
+ }
+
+ /**
+ * Writes a PCL escape command to the output stream.
+ * @param cmd the command (without the ESCAPE character)
+ * @throws IOException In case of an I/O error
+ */
+ public void writeCommand(String cmd) throws IOException {
+ out.write(27); //ESC
+ out.write(cmd.getBytes("US-ASCII"));
+ }
+
+ /**
+ * Writes raw text (in ISO-8859-1 encoding) to the output stream.
+ * @param s the text
+ * @throws IOException In case of an I/O error
+ */
+ public void writeText(String s) throws IOException {
+ out.write(s.getBytes("ISO-8859-1"));
+ }
+
+ /**
+ * Formats a double value with two decimal positions for PCL output.
+ *
+ * @param value value to format
+ * @return the formatted value
+ */
+ public final String formatDouble2(double value) {
+ return df2.format(value);
+ }
+
+ /**
+ * Formats a double value with four decimal positions for PCL output.
+ *
+ * @param value value to format
+ * @return the formatted value
+ */
+ public final String formatDouble4(double value) {
+ return df4.format(value);
+ }
+
+ /**
+ * Sends the universal end of language command (UEL).
+ * @throws IOException In case of an I/O error
+ */
+ public void universalEndOfLanguage() throws IOException {
+ writeCommand("%-12345X");
+ }
+
+ /**
+ * Resets the printer and restores the user default environment.
+ * @throws IOException In case of an I/O error
+ */
+ public void resetPrinter() throws IOException {
+ writeCommand("E");
+ }
+
+ /**
+ * Sends the job separation command.
+ * @throws IOException In case of an I/O error
+ */
+ public void separateJobs() throws IOException {
+ writeCommand("&l1T");
+ }
+
+ /**
+ * Sends the form feed character.
+ * @throws IOException In case of an I/O error
+ */
+ public void formFeed() throws IOException {
+ out.write(12); //=OC ("FF", Form feed)
+ }
+
+ /**
+ * Clears the horizontal margins.
+ * @throws IOException In case of an I/O error
+ */
+ public void clearHorizontalMargins() throws IOException {
+ writeCommand("9");
+ }
+
+ /**
+ * The Top Margin command designates the number of lines between
+ * the top of the logical page and the top of the text area.
+ * @param numberOfLines the number of lines (See PCL specification for details)
+ * @throws IOException In case of an I/O error
+ */
+ public void setTopMargin(int numberOfLines) throws IOException {
+ writeCommand("&l" + numberOfLines + "E");
+ }
+
+ /**
+ * Sets the cursor to a new absolute coordinate.
+ * @param x the X coordinate (in millipoints)
+ * @param y the Y coordinate (in millipoints)
+ * @throws IOException In case of an I/O error
+ */
+ public void setCursorPos(int x, int y) throws IOException {
+ writeCommand("*p" + (x / 100) + "h" + (y / 100) + "V");
+ }
+
+ /**
+ * Generate a filled rectangle
+ *
+ * @param x the x position of left edge in millipoints
+ * @param y the y position of top edge in millipoints
+ * @param w the width in millipoints
+ * @param h the height in millipoints
+ * @param col the fill color
+ * @throws IOException In case of an I/O error
+ */
+ protected void fillRect(int x, int y, int w, int h, Color col) throws IOException {
+ if ((w == 0) || (h == 0)) {
+ return;
+ }
+ if (h < 0) {
+ h *= -1;
+ } else {
+ //y += h;
+ }
+
+ int xpos = (x / 100);
+ if (xpos < 0) {
+ //A negative x coordinate can lead to a displaced rectangle (xpos must be >= 0)
+ w += x;
+ xpos = 0;
+ }
+ writeCommand("*v1O");
+ writeCommand("&a" + formatDouble4(xpos) + "h"
+ + formatDouble4(y / 100) + "V");
+ writeCommand("*c" + formatDouble4(w / 100) + "h"
+ + formatDouble4(h / 100) + "V");
+ int lineshade = convertToPCLShade(col);
+ writeCommand("*c" + lineshade + "G");
+ writeCommand("*c2P");
+ // Reset pattern transparency mode.
+ writeCommand("*v0O");
+ }
+
+ /**
+ * Sets the pattern transparency mode.
+ * @param transparent true if transparent, false for opaque
+ * @throws IOException In case of an I/O error
+ */
+ public void setPatternTransparencyMode(boolean transparent) throws IOException {
+ if (transparent) {
+ writeCommand("*v0O");
+ } else {
+ writeCommand("*v1O");
+ }
+ }
+
+ /**
+ * Convert an RGB color value to a grayscale from 0 to 100.
+ * @param r the red component
+ * @param g the green component
+ * @param b the blue component
+ * @return the gray value
+ */
+ public final int convertToGray(int r, int g, int b) {
+ return (r * 30 + g * 59 + b * 11) / 100;
+ }
+
+ /**
+ * Convert a Color value to a PCL shade value (0-100).
+ * @param col the color
+ * @return the PCL shade value (100=black)
+ */
+ public final int convertToPCLShade(Color col) {
+ float gray = convertToGray(col.getRed(), col.getGreen(), col.getBlue()) / 255f;
+ return (int)(100 - (gray * 100f));
+ }
+
+ /**
+ * Select the current pattern
+ * @param patternID the pattern ID (<ESC>*c#G command)
+ * @param pattern the pattern type (<ESC>*v#T command)
+ * @throws IOException In case of an I/O error
+ */
+ public void selectCurrentPattern(int patternID, int pattern) throws IOException {
+ writeCommand("*c" + patternID + "G");
+ writeCommand("*v" + pattern + "T");
+ }
+
+ /**
+ * Indicates whether an image is a monochrome (b/w) image.
+ * @param img the image
+ * @return true if it's a monochrome image
+ */
+ public static boolean isMonochromeImage(RenderedImage img) {
+ ColorModel cm = img.getColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ return icm.getMapSize() == 2;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Indicates whether an image is a grayscale image.
+ * @param img the image
+ * @return true if it's a grayscale image
+ */
+ public static boolean isGrayscaleImage(RenderedImage img) {
+ return (img.getColorModel().getColorSpace().getNumComponents() == 1);
+ }
+
+ private MonochromeBitmapConverter createMonochromeBitmapConverter() {
+ MonochromeBitmapConverter converter = null;
+ try {
+ String clName = "org.apache.fop.render.pcl.JAIMonochromeBitmapConverter";
+ Class clazz = Class.forName(clName);
+ converter = (MonochromeBitmapConverter)clazz.newInstance();
+ } catch (ClassNotFoundException cnfe) {
+ // Class was not compiled so is not available. Simply ignore.
+ } catch (LinkageError le) {
+ // This can happen if fop was build with support for a
+ // particular provider (e.g. a binary fop distribution)
+ // but the required support files (i.e. JAI) are not
+ // available in the current runtime environment.
+ // Simply continue with the backup implementation.
+ } catch (InstantiationException e) {
+ // Problem instantiating the class, simply continue with the backup implementation
+ } catch (IllegalAccessException e) {
+ // Problem instantiating the class, simply continue with the backup implementation
+ }
+ if (converter == null) {
+ converter = new DefaultMonochromeBitmapConverter();
+ }
+ return converter;
+ }
+
+ private int calculatePCLResolution(int resolution) {
+ return calculatePCLResolution(resolution, false);
+ }
+
+ /**
+ * Calculates the ideal PCL resolution for a given resolution.
+ * @param resolution the input resolution
+ * @param increased true if you want to go to a higher resolution, for example if you
+ * convert grayscale or color images to monochrome images so dithering has
+ * a chance to generate better quality.
+ * @return the resulting PCL resolution (one of 75, 100, 150, 200, 300, 600)
+ */
+ private int calculatePCLResolution(int resolution, boolean increased) {
+ for (int i = PCL_RESOLUTIONS.length - 2; i >= 0; i--) {
+ if (resolution > PCL_RESOLUTIONS[i]) {
+ int idx = i + 1;
+ if (idx < PCL_RESOLUTIONS.length - 2) {
+ idx += increased ? 2 : 0;
+ } else if (idx < PCL_RESOLUTIONS.length - 1) {
+ idx += increased ? 1 : 0;
+ }
+ return PCL_RESOLUTIONS[idx];
+ }
+ }
+ return PCL_RESOLUTIONS[increased ? 2 : 0];
+ }
+
+ private boolean isValidPCLResolution(int resolution) {
+ return resolution == calculatePCLResolution(resolution);
+ }
+
+ private Dimension getAdjustedDimension(Dimension orgDim, int orgResolution,
+ int pclResolution) {
+ if (orgResolution == pclResolution) {
+ return orgDim;
+ } else {
+ Dimension result = new Dimension();
+ result.width = (int)Math.round((double)orgDim.width * pclResolution / orgResolution);
+ result.height = (int)Math.round((double)orgDim.height * pclResolution / orgResolution);
+ return result;
+ }
+ }
+
+ /**
+ * Paint a bitmap at the current cursor position. The bitmap is converted to a monochrome
+ * (1-bit) bitmap image.
+ * @param img the bitmap image
+ * @param resolution the original resolution of the image (in dpi)
+ * @throws IOException In case of an I/O error
+ */
+ public void paintBitmap(RenderedImage img, int resolution) throws IOException {
+ boolean monochrome = isMonochromeImage(img);
+ if (!monochrome) {
+ int effResolution = calculatePCLResolution(resolution, true);
+ Dimension orgDim = new Dimension(img.getWidth(), img.getHeight());
+ Dimension effDim = getAdjustedDimension(orgDim, resolution, effResolution);
+ boolean scaled = !orgDim.equals(effDim);
+ BufferedImage src = null;
+ if (img instanceof BufferedImage && !scaled) {
+ if (!isGrayscaleImage(img)) {
+ src = new BufferedImage(effDim.width, effDim.height,
+ BufferedImage.TYPE_BYTE_GRAY);
+ ColorConvertOp op = new ColorConvertOp(
+ ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
+ op.filter((BufferedImage)img, src);
+ } else {
+ src = (BufferedImage)img;
+ }
+ }
+ if (src == null) {
+ src = new BufferedImage(effDim.width, effDim.height,
+ BufferedImage.TYPE_BYTE_GRAY);
+ Graphics2D g2d = src.createGraphics();
+ try {
+ AffineTransform at = new AffineTransform();
+ double sx = effDim.getWidth() / orgDim.getWidth();
+ double sy = effDim.getHeight() / orgDim.getHeight();
+ at.scale(sx, sy);
+ g2d.drawRenderedImage(img, at);
+ } finally {
+ g2d.dispose();
+ }
+ }
+ MonochromeBitmapConverter converter = createMonochromeBitmapConverter();
+ converter.setHint("quality", "false");
+
+ long start = System.currentTimeMillis();
+ BufferedImage buf = (BufferedImage)converter.convertToMonochrome(src);
+ long duration = System.currentTimeMillis() - start;
+ System.out.println(duration + " ms");
+
+ RenderedImage red = buf;
+ paintMonochromeBitmap(red, effResolution);
+ } else {
+ int effResolution = calculatePCLResolution(resolution);
+ paintMonochromeBitmap(img, effResolution);
+ }
+ }
+
+ /**
+ * Paint a bitmap at the current cursor position. The bitmap must be a monochrome
+ * (1-bit) bitmap image.
+ * @param img the bitmap image (must be 1-bit b/w)
+ * @param resolution the resolution of the image (must be a PCL resolution)
+ * @throws IOException In case of an I/O error
+ */
+ public void paintMonochromeBitmap(RenderedImage img, int resolution) throws IOException {
+ if (!isValidPCLResolution(resolution)) {
+ throw new IllegalArgumentException("Invalid PCL resolution: " + resolution);
+ }
+ writeCommand("*t" + resolution + "R");
+ writeCommand("*r0f" + img.getHeight() + "t" + img.getWidth() + "s1A");
+ Raster raster = img.getData();
+ boolean monochrome = isMonochromeImage(img);
+ if (!monochrome) {
+ throw new IllegalArgumentException("img must be a monochrome image");
+ }
+
+ int x = 0;
+ int y = 0;
+ int imgw = img.getWidth();
+ int imgh = img.getHeight();
+ int bytewidth = (imgw / 8);
+ if ((imgw % 8) != 0) {
+ bytewidth++;
+ }
+ byte ib;
+ byte[] rle = new byte[bytewidth * 2]; //compressed (RLE)
+ byte[] uncompressed = new byte[bytewidth]; //uncompressed
+ int lastcount = -1;
+ byte lastbyte = 0;
+ int rlewidth = 0;
+ /*
+ int xres = (iw * 72000) / w;
+ int yres = (ih * 72000) / h;
+ int resolution = xres;
+ if (yres > xres)
+ resolution = yres;
+
+ if (resolution > 300)
+ resolution = 600;
+ else if (resolution > 150)
+ resolution = 300;
+ else if (resolution > 100)
+ resolution = 150;
+ else if (resolution > 75)
+ resolution = 100;
+ else
+ resolution = 75;
+ */
+
+ // Transfer graphics data
+ for (y = 0; y < imgh; y++) {
+ ib = 0;
+ for (x = 0; x < imgw; x++) {
+ int sample = raster.getSample(x, y, 0);
+ //Set image bit for black
+ if ((sample == 0)) {
+ ib |= (1 << (7 - (x % 8)));
+ }
+
+ //RLE encoding
+ if ((x % 8) == 7 || ((x + 1) == imgw)) {
+ if (rlewidth < bytewidth) {
+ if (lastcount >= 0) {
+ if (ib == lastbyte) {
+ lastcount++;
+ } else {
+ rle[rlewidth++] = (byte)(lastcount & 0xFF);
+ rle[rlewidth++] = lastbyte;
+ lastbyte = ib;
+ lastcount = 0;
+ }
+ } else {
+ lastbyte = ib;
+ lastcount = 0;
+ }
+ if (lastcount == 255 || ((x + 1) == imgw)) {
+ rle[rlewidth++] = (byte)(lastcount & 0xFF);
+ rle[rlewidth++] = lastbyte;
+ lastbyte = 0;
+ lastcount = -1;
+ }
+ }
+ uncompressed[x / 8] = ib;
+ ib = 0;
+ }
+ }
+ if (rlewidth < bytewidth) {
+ writeCommand("*b1m" + rlewidth + "W");
+ this.out.write(rle, 0, rlewidth);
+ } else {
+ writeCommand("*b0m" + bytewidth + "W");
+ this.out.write(uncompressed);
+ }
+ lastcount = -1;
+ rlewidth = 0;
+ }
+
+ // End raster graphics
+ writeCommand("*rB");
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java
new file mode 100644
index 000000000..5e976505f
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.io.IOException;
+import java.text.AttributedCharacterIterator;
+
+import org.apache.fop.util.UnitConv;
+import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
+import org.apache.xmlgraphics.java2d.GraphicContext;
+
+/**
+ * Graphics2D implementation implementing PCL and HP GL/2.
+ */
+public class PCLGraphics2D extends AbstractGraphics2D {
+
+ /** The PCL generator */
+ protected PCLGenerator gen;
+
+ /**
+ * Create a new PCLGraphics2D.
+ * @param gen the PCL Generator to paint with
+ */
+ public PCLGraphics2D(PCLGenerator gen) {
+ super(true);
+ this.gen = gen;
+ }
+
+ /**
+ * Copy constructor
+ * @param g parent PCLGraphics2D
+ */
+ public PCLGraphics2D(PCLGraphics2D g) {
+ super(true);
+ this.gen = g.gen;
+ }
+
+ /** @see java.awt.Graphics#create() */
+ public Graphics create() {
+ return new PCLGraphics2D(this);
+ }
+
+ /** @see java.awt.Graphics#dispose() */
+ public void dispose() {
+ this.gen = null;
+ }
+
+ /**
+ * Sets the GraphicContext
+ * @param c GraphicContext to use
+ */
+ public void setGraphicContext(GraphicContext c) {
+ this.gc = c;
+ }
+
+ /**
+ * Central handler for IOExceptions for this class.
+ * @param ioe IOException to handle
+ */
+ public void handleIOException(IOException ioe) {
+ //TODO Surely, there's a better way to do this.
+ ioe.printStackTrace();
+ }
+
+ /** @see java.awt.Graphics2D#getDeviceConfiguration() */
+ public GraphicsConfiguration getDeviceConfiguration() {
+ return GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getDefaultConfiguration();
+ }
+
+ /**
+ * Applies a new Stroke object.
+ * @param stroke Stroke object to use
+ * @throws IOException In case of an I/O problem
+ */
+ protected void applyStroke(Stroke stroke) throws IOException {
+ if (stroke instanceof BasicStroke) {
+ BasicStroke bs = (BasicStroke)stroke;
+
+ float[] da = bs.getDashArray();
+ if (da != null) {
+
+ gen.writeText("UL1,");
+ for (int idx = 0, len = Math.min(20, da.length); idx < len; idx++) {
+ gen.writeText(gen.formatDouble4(da[idx]));
+ if (idx < da.length - 1) {
+ gen.writeText(",");
+ }
+ }
+ gen.writeText(";");
+ /* TODO Dash phase NYI
+ float offset = bs.getDashPhase();
+ gen.writeln(gen.formatDouble4(offset) + " setdash");
+ */
+ gen.writeText("LT1;");
+ } else {
+ gen.writeText("LT;");
+ }
+
+ gen.writeText("LA1"); //line cap
+ int ec = bs.getEndCap();
+ switch (ec) {
+ case BasicStroke.CAP_BUTT:
+ gen.writeText(",1");
+ break;
+ case BasicStroke.CAP_ROUND:
+ gen.writeText(",4");
+ break;
+ case BasicStroke.CAP_SQUARE:
+ gen.writeText(",2");
+ break;
+ default: System.err.println("Unsupported line cap: " + ec);
+ }
+
+ gen.writeText(",2"); //line join
+ int lj = bs.getLineJoin();
+ switch (lj) {
+ case BasicStroke.JOIN_MITER:
+ gen.writeText(",1");
+ break;
+ case BasicStroke.JOIN_ROUND:
+ gen.writeText(",4");
+ break;
+ case BasicStroke.JOIN_BEVEL:
+ gen.writeText(",5");
+ break;
+ default: System.err.println("Unsupported line join: " + lj);
+ }
+
+ float ml = bs.getMiterLimit();
+ gen.writeText(",3" + gen.formatDouble4(ml));
+
+ float lw = bs.getLineWidth();
+ Point2D ptSrc = new Point2D.Double(lw, 0);
+ //Pen widths are set as absolute metric values (WU0;)
+ Point2D ptDest = getTransform().transform(ptSrc, null);
+ double transDist = UnitConv.pt2mm(ptDest.distance(0, 0));
+ //System.out.println("--" + ptDest.distance(0, 0) + " " + transDist);
+ gen.writeText(";PW" + gen.formatDouble4(transDist) + ";");
+
+ } else {
+ System.err.println("Unsupported Stroke: " + stroke.getClass().getName());
+ }
+ }
+
+ /**
+ * Applies a new Paint object.
+ * @param paint Paint object to use
+ * @throws IOException In case of an I/O problem
+ */
+ protected void applyPaint(Paint paint) throws IOException {
+ if (paint instanceof Color) {
+ Color col = (Color)paint;
+ int shade = gen.convertToPCLShade(col);
+ gen.writeText("TR0;FT10," + shade + ";");
+ } else {
+ System.err.println("Unsupported Paint: " + paint.getClass().getName());
+ }
+ }
+
+ /** @see java.awt.Graphics2D#draw(java.awt.Shape) */
+ public void draw(Shape s) {
+ try {
+ AffineTransform trans = getTransform();
+
+ Shape imclip = getClip();
+ //writeClip(imclip);
+ //establishColor(getColor());
+
+ applyPaint(getPaint());
+ applyStroke(getStroke());
+
+ //gen.writeln("newpath");
+ PathIterator iter = s.getPathIterator(trans);
+ processPathIterator(iter);
+ gen.writeText("EP;");
+ } catch (IOException ioe) {
+ handleIOException(ioe);
+ }
+ }
+
+ /** @see java.awt.Graphics2D#fill(java.awt.Shape) */
+ public void fill(Shape s) {
+ try {
+ AffineTransform trans = getTransform();
+ Shape imclip = getClip();
+ //writeClip(imclip);
+
+ //establishColor(getColor());
+
+ applyPaint(getPaint());
+
+ PathIterator iter = s.getPathIterator(trans);
+ processPathIterator(iter);
+ int fillMethod = (iter.getWindingRule() == PathIterator.WIND_EVEN_ODD ? 0 : 1);
+ gen.writeText("FP" + fillMethod + ";");
+ } catch (IOException ioe) {
+ handleIOException(ioe);
+ }
+ }
+
+ /**
+ * Processes a path iterator generating the nexessary painting operations.
+ * @param iter PathIterator to process
+ * @throws IOException In case of an I/O problem.
+ */
+ public void processPathIterator(PathIterator iter) throws IOException {
+ double[] vals = new double[6];
+ boolean penDown = false;
+ boolean hasFirst = false;
+ double x = 0, firstX = 0;
+ double y = 0, firstY = 0;
+ boolean pendingPM0 = true;
+ penUp();
+ while (!iter.isDone()) {
+ int type = iter.currentSegment(vals);
+ if (type == PathIterator.SEG_CLOSE) {
+ hasFirst = false;
+ /*
+ if (firstX != x && firstY != y) {
+ plotAbsolute(firstX, firstY);
+ }*/
+ //penUp();
+ gen.writeText("PM1;");
+ iter.next();
+ continue;
+ }
+ if (type == PathIterator.SEG_MOVETO) {
+ if (penDown) {
+ penUp();
+ penDown = false;
+ }
+ } else {
+ if (!penDown) {
+ penDown();
+ penDown = true;
+ }
+ }
+ switch (type) {
+ case PathIterator.SEG_CUBICTO:
+ x = vals[4];
+ y = vals[5];
+ bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y);
+ break;
+ case PathIterator.SEG_LINETO:
+ x = vals[0];
+ y = vals[1];
+ plotAbsolute(x, y);
+ break;
+ case PathIterator.SEG_MOVETO:
+ x = vals[0];
+ y = vals[1];
+ plotAbsolute(x, y);
+ break;
+ case PathIterator.SEG_QUADTO:
+ double originX = x;
+ double originY = y;
+ x = vals[2];
+ y = vals[3];
+ quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y);
+ break;
+ case PathIterator.SEG_CLOSE:
+ break;
+ default:
+ break;
+ }
+ if (pendingPM0) {
+ pendingPM0 = false;
+ gen.writeText("PM;");
+ }
+ if (!hasFirst) {
+ firstX = x;
+ firstY = y;
+ }
+ iter.next();
+ }
+ gen.writeText("PM2;");
+ }
+
+ private void plotAbsolute(double x, double y) throws IOException {
+ gen.writeText("PA" + gen.formatDouble4(x) + ","
+ + gen.formatDouble4(y) + ";");
+ }
+
+ private void bezierAbsolute(double x1, double y1, double x2, double y2, double x3, double y3)
+ throws IOException {
+ gen.writeText("BZ" + gen.formatDouble4(x1) + ","
+ + gen.formatDouble4(y1) + ","
+ + gen.formatDouble4(x2) + ","
+ + gen.formatDouble4(y2) + ","
+ + gen.formatDouble4(x3) + ","
+ + gen.formatDouble4(y3) + ";");
+ }
+
+ private void quadraticBezierAbsolute(double originX, double originY,
+ double x1, double y1, double x2, double y2)
+ throws IOException {
+ //Quadratic Bezier curve can be mapped to a normal bezier curve
+ //See http://pfaedit.sourceforge.net/bezier.html
+ double nx1 = originX + (2.0 / 3.0) * (x1 - originX);
+ double ny1 = originY + (2.0 / 3.0) * (y1 - originY);
+
+ double nx2 = nx1 + (1.0 / 3.0) * (x2 - originX);
+ double ny2 = ny1 + (1.0 / 3.0) * (y2 - originY);
+
+ bezierAbsolute(nx1, ny1, nx2, ny2, x2, y2);
+ }
+
+ private void penDown() throws IOException {
+ gen.writeText("PD;");
+ }
+
+ private void penUp() throws IOException {
+ gen.writeText("PU;");
+ }
+
+ /** @see java.awt.Graphics2D#drawString(java.lang.String, float, float) */
+ public void drawString(String s, float x, float y) {
+ // TODO Auto-generated method stub
+ System.err.println("drawString NYI");
+ }
+
+ /** @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float) */
+ public void drawString(AttributedCharacterIterator iterator, float x,
+ float y) {
+ // TODO Auto-generated method stub
+ System.err.println("drawString NYI");
+ }
+
+ /**
+ * @see java.awt.Graphics2D#drawRenderedImage(java.awt.image.RenderedImage,
+ * java.awt.geom.AffineTransform)
+ */
+ public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
+ // TODO Auto-generated method stub
+ System.err.println("drawRenderedImage NYI");
+ }
+
+ /**
+ * @see java.awt.Graphics2D#drawRenderableImage(java.awt.image.renderable.RenderableImage,
+ * java.awt.geom.AffineTransform)
+ */
+ public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+ // TODO Auto-generated method stub
+ System.err.println("drawRenderedImage NYI");
+ }
+
+ /**
+ * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int,
+ * java.awt.image.ImageObserver)
+ */
+ public boolean drawImage(Image img, int x, int y, int width, int height,
+ ImageObserver observer) {
+ // TODO Auto-generated method stub
+ System.err.println("drawImage NYI");
+ return false;
+ }
+
+ /**
+ * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver)
+ */
+ public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
+ // TODO Auto-generated method stub
+ System.err.println("drawImage NYI");
+ return false;
+ }
+
+ /** @see java.awt.Graphics#copyArea(int, int, int, int, int, int) */
+ public void copyArea(int x, int y, int width, int height, int dx, int dy) {
+ // TODO Auto-generated method stub
+ System.err.println("copyArea NYI");
+ }
+
+ /** @see java.awt.Graphics#setXORMode(java.awt.Color) */
+ public void setXORMode(Color c1) {
+ // TODO Auto-generated method stub
+ System.err.println("setXORMode NYI");
+ }
+
+ /**
+ * Used to create proper font metrics
+ */
+ private Graphics2D fmg;
+
+ {
+ BufferedImage bi = new BufferedImage(1, 1,
+ BufferedImage.TYPE_INT_ARGB);
+
+ fmg = bi.createGraphics();
+ }
+
+ /** @see java.awt.Graphics#getFontMetrics(java.awt.Font) */
+ public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
+ return fmg.getFontMetrics(f);
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java
new file mode 100644
index 000000000..268cc24ec
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+import org.apache.fop.render.Graphics2DAdapter;
+import org.apache.fop.render.Graphics2DImagePainter;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.util.UnitConv;
+import org.apache.xmlgraphics.java2d.GraphicContext;
+
+/**
+ * Graphics2DAdapter implementation for PCL and HP GL/2.
+ */
+public class PCLGraphics2DAdapter implements Graphics2DAdapter {
+
+ /**
+ * Main constructor
+ */
+ public PCLGraphics2DAdapter() {
+ }
+
+ /** @see org.apache.fop.render.Graphics2DAdapter */
+ public void paintImage(Graphics2DImagePainter painter,
+ RendererContext context,
+ int x, int y, int width, int height) throws IOException {
+ PCLRendererContext pclContext = PCLRendererContext.wrapRendererContext(context);
+ PCLRenderer pcl = (PCLRenderer)context.getRenderer();
+ PCLGenerator gen = pcl.gen;
+
+ // get the 'width' and 'height' attributes of the SVG document
+ Dimension dim = painter.getImageSize();
+ float imw = (float)dim.getWidth();
+ float imh = (float)dim.getHeight();
+
+ boolean paintAsBitmap = pclContext.paintAsBitmap();
+ if (paintAsBitmap) {
+ int resolution = 300; //TODO not hard-coded, please!
+ int bmw = UnitConv.mpt2px(pclContext.getWidth(), resolution);
+ int bmh = UnitConv.mpt2px(pclContext.getHeight(), resolution);
+ BufferedImage bi = new BufferedImage(
+ bmw, bmh,
+ BufferedImage.TYPE_INT_RGB);
+ Graphics2D g2d = bi.createGraphics();
+ try {
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_OFF);
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
+ g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
+ RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
+ g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY);
+ g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
+ RenderingHints.VALUE_COLOR_RENDER_QUALITY);
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+ g2d.setRenderingHint(RenderingHints.KEY_DITHERING,
+ RenderingHints.VALUE_DITHER_ENABLE);
+ g2d.setBackground(Color.white);
+ g2d.setColor(Color.black);
+ g2d.clearRect(0, 0, bmw, bmh);
+ double sx = (double)bmw / pclContext.getWidth() * 1000;
+ double sy = (double)bmh / pclContext.getHeight() * 1000;
+ g2d.scale(sx, sy);
+
+ //Paint the SVG on the BufferedImage
+ Rectangle2D area = new Rectangle2D.Double(
+ 0.0, 0.0, pclContext.getWidth(), pclContext.getHeight());
+ painter.paint(g2d, area);
+ } finally {
+ g2d.dispose();
+ }
+
+ pcl.moveTo(x, y);
+ gen.paintBitmap(bi, resolution);
+ } else {
+ pcl.saveGraphicsState();
+ GraphicContext ctx = (GraphicContext)pcl.getGraphicContext().clone();
+
+ // Clip to the image area.
+ //gen.writeln("newpath");
+ //gen.defineRect(fx, fy, fwidth, fheight);
+ //gen.writeln("clip");
+
+ AffineTransform prepareHPGL2 = new AffineTransform();
+ //prepareHPGL2.scale(1, 1);
+ ctx.setTransform(prepareHPGL2);
+
+ pcl.moveTo(x, y);
+ gen.writeCommand("*c" + gen.formatDouble4(width / 100f) + "x"
+ + gen.formatDouble4(height / 100f) + "Y");
+ gen.writeCommand("*c0T");
+ gen.writeCommand("%0B");
+ gen.writeText("IN;");
+ gen.writeText("SP1;");
+ //One Plotter unit is 0.025mm!
+ double scale = imw / UnitConv.mm2pt(imw * 0.025);
+ gen.writeText("SC0," + gen.formatDouble4(scale)
+ + ",0,-" + gen.formatDouble4(scale) + ",2;");
+ gen.writeText("IR0,100,0,100;");
+ gen.writeText("PU;PA0,0;");
+ PCLGraphics2D graphics = new PCLGraphics2D(gen);
+ graphics.setGraphicContext(ctx);
+ Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);
+ painter.paint(graphics, area);
+
+ gen.writeCommand("%0A");
+ pcl.restoreGraphicsState();
+ }
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java b/src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java
new file mode 100644
index 000000000..40a0eddd7
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLPageDefinition.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.fop.util.UnitConv;
+
+/**
+ * This class represents a page format with PCL-specific properties.
+ */
+public class PCLPageDefinition {
+
+ private static List pageDefinitions;
+
+ private String name;
+ private long width; //in mpt
+ private long height; //in mpt
+ private int logicalPageXOffset; //in mpt
+ private boolean landscape;
+
+ static {
+ createPageDefinitions();
+ }
+
+ public PCLPageDefinition(String name, long width, long height, int logicalPageXOffset) {
+ this(name, width, height, logicalPageXOffset, false);
+ }
+
+ public PCLPageDefinition(String name, long width, long height, int logicalPageXOffset,
+ boolean landscape) {
+ this.name = name;
+ this.width = width;
+ this.height = height;
+ this.logicalPageXOffset = logicalPageXOffset;
+ this.landscape = landscape;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public boolean isLandscapeFormat() {
+ return this.landscape;
+ }
+
+ public int getLogicalPageXOffset() {
+ return this.logicalPageXOffset;
+ }
+
+ public boolean matches(long width, long height, int errorMargin) {
+ return (Math.abs(this.width - width) < errorMargin)
+ && (Math.abs(this.height - height) < errorMargin);
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ return getName();
+ }
+
+ public static PCLPageDefinition getPageDefinition(long width, long height, int errorMargin) {
+ Iterator iter = pageDefinitions.iterator();
+ while (iter.hasNext()) {
+ PCLPageDefinition def = (PCLPageDefinition)iter.next();
+ if (def.matches(width, height, errorMargin)) {
+ return def;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Converts an offset values for logical pages to millipoints. The values are given as pixels
+ * in a 300dpi environment.
+ * @param offset the offset as given in the PCL 5 specification (under "Printable Area")
+ * @return the converted value in millipoints
+ */
+ private static int convertLogicalPageXOffset(int offset) {
+ return (int)Math.round(((double)offset) * 72000 / 300);
+ }
+
+ private static void createPageDefinitions() {
+ pageDefinitions = new java.util.ArrayList();
+ pageDefinitions.add(new PCLPageDefinition("Letter",
+ Math.round(UnitConv.in2mpt(8.5)), Math.round(UnitConv.in2mpt(11)),
+ convertLogicalPageXOffset(75)));
+ pageDefinitions.add(new PCLPageDefinition("Legal",
+ Math.round(UnitConv.in2mpt(8.5)), Math.round(UnitConv.in2mpt(14)),
+ convertLogicalPageXOffset(75)));
+ pageDefinitions.add(new PCLPageDefinition("Executive",
+ Math.round(UnitConv.in2mpt(7.25)), Math.round(UnitConv.in2mpt(10.5)),
+ convertLogicalPageXOffset(75)));
+ pageDefinitions.add(new PCLPageDefinition("Ledger",
+ Math.round(UnitConv.in2mpt(11)), Math.round(UnitConv.in2mpt(17)),
+ convertLogicalPageXOffset(75)));
+ pageDefinitions.add(new PCLPageDefinition("A4",
+ Math.round(UnitConv.mm2mpt(210)), Math.round(UnitConv.mm2mpt(297)),
+ convertLogicalPageXOffset(71)));
+ pageDefinitions.add(new PCLPageDefinition("A3",
+ Math.round(UnitConv.mm2mpt(297)), Math.round(UnitConv.mm2mpt(420)),
+ convertLogicalPageXOffset(71)));
+
+ //TODO Add envelope definitions
+
+ pageDefinitions.add(new PCLPageDefinition("LetterL",
+ Math.round(UnitConv.in2mpt(11)), Math.round(UnitConv.in2mpt(8.5)),
+ convertLogicalPageXOffset(60)));
+ pageDefinitions.add(new PCLPageDefinition("LegalL",
+ Math.round(UnitConv.in2mpt(14)), Math.round(UnitConv.in2mpt(8.5)),
+ convertLogicalPageXOffset(60)));
+ pageDefinitions.add(new PCLPageDefinition("ExecutiveL",
+ Math.round(UnitConv.in2mpt(10.5)), Math.round(UnitConv.in2mpt(7.25)),
+ convertLogicalPageXOffset(60)));
+ pageDefinitions.add(new PCLPageDefinition("LedgerL",
+ Math.round(UnitConv.in2mpt(17)), Math.round(UnitConv.in2mpt(11)),
+ convertLogicalPageXOffset(60)));
+ pageDefinitions.add(new PCLPageDefinition("A4L",
+ Math.round(UnitConv.mm2mpt(297)), Math.round(UnitConv.mm2mpt(210)),
+ convertLogicalPageXOffset(59), true));
+ pageDefinitions.add(new PCLPageDefinition("A3L",
+ Math.round(UnitConv.mm2mpt(420)), Math.round(UnitConv.mm2mpt(297)),
+ convertLogicalPageXOffset(59)));
+ }
+
+
+}
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java b/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java
index 58f4c16fb..04e619822 100644
--- a/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,53 +19,109 @@
package org.apache.fop.render.pcl;
// FOP
+import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.BlockViewport;
import org.apache.fop.area.CTM;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.Trait.Color;
+import org.apache.fop.area.inline.AbstractTextArea;
+import org.apache.fop.area.inline.ForeignObject;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.SpaceArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.Viewport;
+import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.image.EPSImage;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.image.ImageFactory;
+import org.apache.fop.image.XMLImage;
+import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.PrintRenderer;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.RendererContextConstants;
+import org.apache.fop.traits.BorderProps;
+import org.apache.xmlgraphics.java2d.GraphicContext;
+import org.w3c.dom.Document;
// Java
+import java.awt.Rectangle;
+import java.awt.color.ColorSpace;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
/**
- * Renderer that renders areas to PCL
- * Created by Arthur E Welch III while at M&I EastPoint Technology
- * Donated by EastPoint to the Apache FOP project March 2, 2001.
+ * Renderer for the PCL 5 printer language. It also uses HP GL/2 for certain graphic elements.
*/
public class PCLRenderer extends PrintRenderer {
/** The MIME type for PCL */
public static final String MIME_TYPE = MimeConstants.MIME_PCL_ALT;
- /**
- * the current stream to add PCL commands to
- */
- protected PCLStream currentStream;
-
- private int pageHeight = 7920;
-
- // These variables control the virtual paggination functionality.
- private int curdiv = 0;
- private int divisions = -1;
- private int paperheight = -1; // Paper height in decipoints?
- private int orientation = -1; // -1=default/unknown, 0=portrait, 1=landscape.
- private int topmargin = -1; // Top margin in decipoints?
- private int leftmargin = -1; // Left margin in decipoints?
- private int fullmargin = 0;
- private final boolean debug = false;
-
- private int xoffset = -180; // X Offset to allow for PCL implicit 1/4" left margin.
-
- private java.util.Hashtable options;
+ /** The OutputStream to write the PCL stream to */
+ protected OutputStream out;
+
+ /** The PCL generator */
+ protected PCLGenerator gen;
+ private boolean ioTrouble = false;
+ private Stack graphicContextStack = new Stack();
+ private GraphicContext graphicContext = new GraphicContext();
+
/**
* Create the PCL renderer
*/
public PCLRenderer() {
}
- public void setFont(String name, float size) {
+ /**
+ * Central exception handler for I/O exceptions.
+ * @param ioe IOException to handle
+ */
+ protected void handleIOTrouble(IOException ioe) {
+ if (!ioTrouble) {
+ log.error("Error while writing to target file", ioe);
+ ioTrouble = true;
+ }
+ }
+
+ /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
+ public Graphics2DAdapter getGraphics2DAdapter() {
+ return new PCLGraphics2DAdapter();
+ }
+
+ /** @return the GraphicContext used to track coordinate system transformations */
+ public GraphicContext getGraphicContext() {
+ return this.graphicContext;
+ }
+
+ /**
+ * Sets the current font (NOTE: Hard-coded font mappings ATM!)
+ * @param name the font name (internal F* names for now)
+ * @param size the font size
+ * @throws IOException if an I/O problem occurs
+ */
+ public void setFont(String name, float size) throws IOException {
int fontcode = 0;
if (name.length() > 1 && name.charAt(0) == 'F') {
try {
@@ -74,113 +130,113 @@ public class PCLRenderer extends PrintRenderer {
log.error(e);
}
}
+ String formattedSize = gen.formatDouble2(size / 1000);
switch (fontcode) {
case 1: // F1 = Helvetica
- // currentStream.add("\033(8U\033(s1p" + (size / 1000) + "v0s0b24580T");
+ // gen.writeCommand("(8U");
+ // gen.writeCommand("(s1p" + formattedSize + "v0s0b24580T");
// Arial is more common among PCL5 printers than Helvetica - so use Arial
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v0s0b16602T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v0s0b16602T");
break;
case 2: // F2 = Helvetica Oblique
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v1s0b16602T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v1s0b16602T");
break;
case 3: // F3 = Helvetica Bold
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v0s3b16602T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v0s3b16602T");
break;
case 4: // F4 = Helvetica Bold Oblique
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v1s3b16602T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v1s3b16602T");
break;
case 5: // F5 = Times Roman
- // currentStream.add("\033(8U\033(s1p" + (size / 1000) + "v0s0b25093T");
+ // gen.writeCommand("(8U");
+ // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T");
// Times New is more common among PCL5 printers than Times - so use Times New
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v0s0b16901T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v0s0b16901T");
break;
case 6: // F6 = Times Italic
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v1s0b16901T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v1s0b16901T");
break;
case 7: // F7 = Times Bold
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v0s3b16901T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v0s3b16901T");
break;
case 8: // F8 = Times Bold Italic
- currentStream.add("\033(0N\033(s1p" + (size / 1000)
- + "v1s3b16901T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s1p" + formattedSize + "v1s3b16901T");
break;
case 9: // F9 = Courier
- currentStream.add("\033(0N\033(s0p"
- + (120.01f / (size / 1000.00f)) + "h0s0b4099T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f))
+ + "h0s0b4099T");
break;
case 10: // F10 = Courier Oblique
- currentStream.add("\033(0N\033(s0p"
- + (120.01f / (size / 1000.00f)) + "h1s0b4099T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f))
+ + "h1s0b4099T");
break;
case 11: // F11 = Courier Bold
- currentStream.add("\033(0N\033(s0p"
- + (120.01f / (size / 1000.00f)) + "h0s3b4099T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f))
+ + "h0s3b4099T");
break;
case 12: // F12 = Courier Bold Oblique
- currentStream.add("\033(0N\033(s0p"
- + (120.01f / (size / 1000.00f)) + "h1s3b4099T");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s0p" + gen.formatDouble2(120.01f / (size / 1000.00f))
+ + "h1s3b4099T");
break;
case 13: // F13 = Symbol
- currentStream.add("\033(19M\033(s1p" + (size / 1000)
- + "v0s0b16686T");
+ gen.writeCommand("(19M");
+ gen.writeCommand("(s1p" + formattedSize + "v0s0b16686T");
// ECMA Latin 1 Symbol Set in Times Roman???
- // currentStream.add("\033(9U\033(s1p" + (size / 1000) + "v0s0b25093T");
+ // gen.writeCommand("(9U");
+ // gen.writeCommand("(s1p" + formattedSize + "v0s0b25093T");
break;
case 14: // F14 = Zapf Dingbats
- currentStream.add("\033(14L\033(s1p" + (size / 1000)
- + "v0s0b45101T");
+ gen.writeCommand("(14L");
+ gen.writeCommand("(s1p" + formattedSize + "v0s0b45101T");
break;
default:
- currentStream.add("\033(0N\033(s" + (size / 1000) + "V");
+ gen.writeCommand("(0N");
+ gen.writeCommand("(s" + formattedSize + "V");
break;
}
}
/** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */
public void startRenderer(OutputStream outputStream) throws IOException {
- log.info("rendering areas to PCL");
- log.fatal("The PCL Renderer is non-functional at this time. Please help resurrect it!");
- currentStream = new PCLStream(outputStream);
+ log.debug("Rendering areas to PCL...");
+ this.out = outputStream;
+ this.gen = new PCLGenerator(out);
- // Set orientation.
- if (orientation > -1) {
- currentStream.add("\033&l" + orientation + "O");
- } else {
- currentStream.add("\033&l0O");
- }
- if (orientation == 1 || orientation == 3) {
- xoffset = -144;
- } else {
- xoffset = -180;
- }
-
- // Reset the margins.
- currentStream.add("\033" + "9\033&l0E");
+ gen.universalEndOfLanguage();
+ gen.resetPrinter();
}
/** @see org.apache.fop.render.Renderer#stopRenderer() */
public void stopRenderer() throws IOException {
+ gen.separateJobs();
+ gen.resetPrinter();
+ gen.universalEndOfLanguage();
}
/** @see org.apache.fop.render.AbstractRenderer */
@@ -189,17 +245,624 @@ public class PCLRenderer extends PrintRenderer {
}
/**
+ * @see org.apache.fop.render.AbstractRenderer#renderPage(org.apache.fop.area.PageViewport)
+ */
+ public void renderPage(PageViewport page) throws IOException, FOPException {
+ saveGraphicsState();
+ final long pagewidth = Math.round(page.getViewArea().getWidth());
+ final long pageheight = Math.round(page.getViewArea().getHeight());
+ selectPageFormat(pagewidth, pageheight);
+
+ if (false) { //TODO DEBUG CODE! Remove me.
+ //gen.fillRect(0, 0, (int)pagewidth, (int)pageheight, java.awt.Color.yellow);
+ //gen.fillRect(5000, 5000, (int)pagewidth - 10000, (int)pageheight - 10000, java.awt.Color.yellow);
+ //gen.fillRect(10000, 10000, (int)pagewidth / 4 - 20000, (int)pageheight / 4 - 20000, java.awt.Color.red);
+ for (int i = 0; i < 29; i++) {
+ if (i % 2 == 0) {
+ int w = (int)(10 * 2.835 * 1000);
+ Point2D p = transformedPoint(i * w, 0);
+ gen.fillRect((int)p.getX(), (int)p.getY(), w, w, java.awt.Color.yellow);
+ }
+ }
+ }
+
+ super.renderPage(page);
+ gen.formFeed();
+ restoreGraphicsState();
+ }
+
+ private void selectPageFormat(long pagewidth, long pageheight) throws IOException {
+
+ PCLPageDefinition pageDef = PCLPageDefinition.getPageDefinition(
+ pagewidth, pageheight, 1000);
+ if (pageDef != null) {
+ // Adjust for the offset into the logical page
+ graphicContext.translate(-pageDef.getLogicalPageXOffset(), 0);
+ if (pageDef.isLandscapeFormat()) {
+ gen.writeCommand("&l1O"); //Orientation
+ } else {
+ gen.writeCommand("&l0O"); //Orientation
+ }
+ } else {
+ // Adjust for the offset into the logical page
+ // X Offset to allow for PCL implicit 1/4" left margin (= 180 decipoints)
+ graphicContext.translate(-18000, 18000);
+ gen.writeCommand("&l0O"); //Orientation
+ }
+ gen.clearHorizontalMargins();
+ gen.setTopMargin(0);
+ }
+
+ /** Saves the current graphics state on the stack. */
+ protected void saveGraphicsState() {
+ graphicContextStack.push(graphicContext);
+ graphicContext = (GraphicContext)graphicContext.clone();
+ }
+
+ /** Restores the last graphics state from the stack. */
+ protected void restoreGraphicsState() {
+ graphicContext = (GraphicContext)graphicContextStack.pop();
+ }
+
+ /**
+ * Clip an area. write a clipping operation given coordinates in the current
+ * transform. Coordinates are in points.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width of the area
+ * @param height the height of the area
+ */
+ protected void clipRect(float x, float y, float width, float height) {
+ //PCL cannot clip (only HP GL/2 can)
+ }
+
+ /**
* @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
*/
protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
- // TODO Auto-generated method stub
+ saveGraphicsState();
+ AffineTransform at = new AffineTransform(ctm.toArray());
+ log.debug("startVPArea: " + at);
+ graphicContext.transform(at);
}
/**
* @see org.apache.fop.render.AbstractRenderer#endVParea()
*/
protected void endVParea() {
- // TODO Auto-generated method stub
+ restoreGraphicsState();
+ }
+
+ /**
+ * Handle block traits.
+ * The block could be any sort of block with any positioning
+ * so this should render the traits such as border and background
+ * in its position.
+ *
+ * @param block the block to render the traits
+ */
+ protected void handleBlockTraits(Block block) {
+ int borderPaddingStart = block.getBorderAndPaddingWidthStart();
+ int borderPaddingBefore = block.getBorderAndPaddingWidthBefore();
+
+ float startx = currentIPPosition / 1000f;
+ float starty = currentBPPosition / 1000f;
+ float width = block.getIPD() / 1000f;
+ float height = block.getBPD() / 1000f;
+
+ startx += block.getStartIndent() / 1000f;
+ startx -= block.getBorderAndPaddingWidthStart() / 1000f;
+
+ width += borderPaddingStart / 1000f;
+ width += block.getBorderAndPaddingWidthEnd() / 1000f;
+ height += borderPaddingBefore / 1000f;
+ height += block.getBorderAndPaddingWidthAfter() / 1000f;
+
+ drawBackAndBorders(block, startx, starty, width, height);
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
+ */
+ protected void renderText(TextArea area) {
+ //renderInlineAreaBackAndBorders(area);
+ String fontname = getInternalFontNameForArea(area);
+ int fontsize = area.getTraitAsInteger(Trait.FONT_SIZE);
+
+ //Determine position
+ //int saveIP = currentIPPosition;
+ //int saveBP = currentBPPosition;
+ int rx = currentIPPosition + area.getBorderAndPaddingWidthStart();
+ int bl = currentBPPosition + area.getOffset() + area.getBaselineOffset();
+
+ try {
+ setFont(fontname, fontsize);
+ Color col = (Color)area.getTrait(Trait.COLOR);
+ //this.currentFill = col;
+ if (col != null) {
+ //useColor(ct);
+ gen.setPatternTransparencyMode(false);
+ gen.selectCurrentPattern(gen.convertToPCLShade(col.getAWTColor()), 2);
+ }
+
+ saveGraphicsState();
+ updatePrintDirection();
+ graphicContext.translate(rx, bl);
+ moveTo(0, 0);
+
+ super.renderText(area); //Updates IPD
+
+ //renderTextDecoration(tf, fontsize, area, bl, rx);
+ restoreGraphicsState();
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
+ }
+ }
+
+ void moveTo(int x, int y) throws IOException {
+ Point2D transPoint = transformedPoint(x, y);
+ gen.writeCommand("&a" + gen.formatDouble2(transPoint.getX() / 100) + "h"
+ + gen.formatDouble2(transPoint.getY() / 100) + "V");
+ }
+
+ private void updatePrintDirection() throws IOException {
+ AffineTransform at = graphicContext.getTransform();
+ if (log.isDebugEnabled()) {
+ log.debug(at.getScaleX() + " " + at.getScaleY() + " "
+ + at.getShearX() + " " + at.getShearY() );
+ }
+ if (at.getScaleX() == 0 && at.getScaleY() == 0
+ && at.getShearX() == 1 && at.getShearY() == -1) {
+ gen.writeCommand("&a90P");
+ } else if (at.getScaleX() == -1 && at.getScaleY() == -1
+ && at.getShearX() == 0 && at.getShearY() == 0) {
+ gen.writeCommand("&a180P");
+ } else if (at.getScaleX() == 0 && at.getScaleY() == 0
+ && at.getShearX() == -1 && at.getShearY() == 1) {
+ gen.writeCommand("&a270P");
+ } else {
+ gen.writeCommand("&a0P");
+ }
+ }
+
+ private Point2D transformedPoint(float x, float y) {
+ return transformedPoint(Math.round(x), Math.round(y));
+ }
+
+ private Point2D transformedPoint(int x, int y) {
+ AffineTransform at = graphicContext.getTransform();
+ if (log.isDebugEnabled()) {
+ log.debug("Current transform: " + at);
+ }
+ Point2D orgPoint = new Point2D.Float(x, y);
+ Point2D transPoint = new Point2D.Float();
+ at.transform(orgPoint, transPoint);
+ return transPoint;
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderWord(org.apache.fop.area.inline.WordArea)
+ */
+ protected void renderWord(WordArea word) {
+ //Font font = getFontFromArea(word.getParentArea());
+
+ String s = word.getWord();
+
+ try {
+ gen.writeText(s);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
+ }
+
+ super.renderWord(word);
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderSpace(org.apache.fop.area.inline.SpaceArea)
+ */
+ protected void renderSpace(SpaceArea space) {
+ AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
+ String s = space.getSpace();
+ char sp = s.charAt(0);
+ Font font = getFontFromArea(textArea);
+
+ int tws = (space.isAdjustable()
+ ? ((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
+ + 2 * textArea.getTextLetterSpaceAdjust()
+ : 0);
+
+ double dx = (font.getCharWidth(sp) + tws) / 100f;
+ try {
+ gen.writeCommand("&a+" + gen.formatDouble2(dx) + "H");
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
+ }
+ super.renderSpace(space);
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderViewport(org.apache.fop.area.inline.Viewport)
+ */
+ public void renderViewport(Viewport viewport) {
+
+ float x = currentIPPosition / 1000f;
+ float y = (currentBPPosition + viewport.getOffset()) / 1000f;
+ float width = viewport.getIPD() / 1000f;
+ float height = viewport.getBPD() / 1000f;
+ // TODO: Calculate the border rect correctly.
+ float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f;
+ float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f;
+ float bpwidth = borderPaddingStart
+ + (viewport.getBorderAndPaddingWidthEnd() / 1000f);
+ float bpheight = borderPaddingBefore
+ + (viewport.getBorderAndPaddingWidthAfter() / 1000f);
+
+ drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight);
+
+ if (viewport.getClip()) {
+ saveGraphicsState();
+
+ clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height);
+ }
+ super.renderViewport(viewport);
+
+ if (viewport.getClip()) {
+ restoreGraphicsState();
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
+ */
+ protected void renderBlockViewport(BlockViewport bv, List children) {
+ // clip and position viewport if necessary
+
+ // save positions
+ int saveIP = currentIPPosition;
+ int saveBP = currentBPPosition;
+ //String saveFontName = currentFontName;
+
+ CTM ctm = bv.getCTM();
+ int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
+ int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
+ float x, y;
+ x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
+ y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
+ //This is the content-rect
+ float width = (float)bv.getIPD() / 1000f;
+ float height = (float)bv.getBPD() / 1000f;
+
+
+ if (bv.getPositioning() == Block.ABSOLUTE
+ || bv.getPositioning() == Block.FIXED) {
+
+ currentIPPosition = bv.getXOffset();
+ currentBPPosition = bv.getYOffset();
+
+ //For FIXED, we need to break out of the current viewports to the
+ //one established by the page. We save the state stack for restoration
+ //after the block-container has been painted. See below.
+ List breakOutList = null;
+ if (bv.getPositioning() == Block.FIXED) {
+ //breakOutList = breakOutOfStateStack();
+ }
+
+ CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
+ ctm = tempctm.multiply(ctm);
+
+ //Adjust for spaces (from margin or indirectly by start-indent etc.
+ x += bv.getSpaceStart() / 1000f;
+ currentIPPosition += bv.getSpaceStart();
+
+ y += bv.getSpaceBefore() / 1000f;
+ currentBPPosition += bv.getSpaceBefore();
+
+ float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
+ float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
+
+ drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
+
+ //Now adjust for border/padding
+ currentIPPosition += borderPaddingStart;
+ currentBPPosition += borderPaddingBefore;
+
+ Rectangle2D clippingRect = null;
+ if (bv.getClip()) {
+ clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
+ bv.getIPD(), bv.getBPD());
+ }
+
+ startVParea(ctm, clippingRect);
+ currentIPPosition = 0;
+ currentBPPosition = 0;
+ renderBlocks(bv, children);
+ endVParea();
+
+ if (breakOutList != null) {
+ //restoreStateStackAfterBreakOut(breakOutList);
+ }
+
+ currentIPPosition = saveIP;
+ currentBPPosition = saveBP;
+ } else {
+
+ currentBPPosition += bv.getSpaceBefore();
+
+ //borders and background in the old coordinate system
+ handleBlockTraits(bv);
+
+ //Advance to start of content area
+ currentIPPosition += bv.getStartIndent();
+
+ CTM tempctm = new CTM(containingIPPosition, currentBPPosition);
+ ctm = tempctm.multiply(ctm);
+
+ //Now adjust for border/padding
+ currentBPPosition += borderPaddingBefore;
+
+ Rectangle2D clippingRect = null;
+ if (bv.getClip()) {
+ clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
+ bv.getIPD(), bv.getBPD());
+ }
+
+ startVParea(ctm, clippingRect);
+ currentIPPosition = 0;
+ currentBPPosition = 0;
+ renderBlocks(bv, children);
+ endVParea();
+
+ currentIPPosition = saveIP;
+ currentBPPosition = saveBP;
+
+ currentBPPosition += (int)(bv.getAllocBPD());
+ }
+ //currentFontName = saveFontName;
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
+ */
+ public void renderImage(Image image, Rectangle2D pos) {
+ String url = ImageFactory.getURL(image.getURL());
+ ImageFactory fact = userAgent.getFactory().getImageFactory();
+ FopImage fopimage = fact.getImage(url, userAgent);
+ if (fopimage == null) {
+ return;
+ }
+ if (!fopimage.load(FopImage.DIMENSIONS)) {
+ return;
+ }
+ String mime = fopimage.getMimeType();
+ if ("text/xml".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+ return;
+ }
+ Document doc = ((XMLImage) fopimage).getDocument();
+ String ns = ((XMLImage) fopimage).getNameSpace();
+
+ renderDocument(doc, ns, pos, image.getForeignAttributes());
+ } else if ("image/svg+xml".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+ return;
+ }
+ Document doc = ((XMLImage) fopimage).getDocument();
+ String ns = ((XMLImage) fopimage).getNameSpace();
+
+ renderDocument(doc, ns, pos, image.getForeignAttributes());
+ } else if (fopimage instanceof EPSImage) {
+ log.warn("EPS images are not supported by this renderer");
+ } else {
+ if (!fopimage.load(FopImage.BITMAP)) {
+ log.error("Bitmap image could not be processed: " + fopimage);
+ return;
+ }
+ byte[] imgmap = fopimage.getBitmaps();
+
+ ColorModel cm = new ComponentColorModel(
+ ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
+ new int[] {8, 8, 8},
+ false, false,
+ ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
+ int imgw = fopimage.getWidth();
+ int imgh = fopimage.getHeight();
+ SampleModel sampleModel = new PixelInterleavedSampleModel(
+ DataBuffer.TYPE_BYTE, imgw, imgh, 3, imgw * 3, new int[] {0, 1, 2});
+ DataBuffer dbuf = new DataBufferByte(imgmap, imgw * imgh * 3);
+
+ WritableRaster raster = Raster.createWritableRaster(sampleModel,
+ dbuf, null);
+
+ // Combine the color model and raster into a buffered image
+ RenderedImage img = new BufferedImage(cm, raster, false, null);
+
+ try {
+ moveTo(this.currentIPPosition + (int)pos.getX(),
+ this.currentBPPosition + (int)pos.getY());
+ int resolution = (int)Math.round(Math.max(fopimage.getHorizontalResolution(),
+ fopimage.getVerticalResolution()));
+ gen.paintBitmap(img, resolution);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
+ }
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
+ */
+ public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
+ Document doc = fo.getDocument();
+ String ns = fo.getNameSpace();
+ renderDocument(doc, ns, pos, fo.getForeignAttributes());
+ }
+
+ /**
+ * Renders an XML document (SVG for example).
+ * @param doc the DOM Document containing the XML document to be rendered
+ * @param ns the namespace URI for the XML document
+ * @param pos the position for the generated graphic/image
+ * @param foreignAttributes the foreign attributes containing rendering hints, or null
+ */
+ public void renderDocument(Document doc, String ns, Rectangle2D pos, Map foreignAttributes) {
+ RendererContext context;
+ context = new RendererContext(this, MIME_TYPE);
+ context.setUserAgent(userAgent);
+
+ context.setProperty(RendererContextConstants.WIDTH,
+ new Integer((int) pos.getWidth()));
+ context.setProperty(RendererContextConstants.HEIGHT,
+ new Integer((int) pos.getHeight()));
+ context.setProperty(RendererContextConstants.XPOS,
+ new Integer(currentIPPosition + (int) pos.getX()));
+ context.setProperty(RendererContextConstants.YPOS,
+ new Integer(currentBPPosition + (int) pos.getY()));
+ context.setProperty(RendererContextConstants.PAGE_VIEWPORT,
+ getCurrentPageViewport());
+ if (foreignAttributes != null) {
+ context.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, foreignAttributes);
+ }
+
+ renderXML(context, doc, ns);
+ }
+
+ /**
+ * Draw the background and borders. This draws the background and border
+ * traits for an area given the position.
+ *
+ * @param area the area whose traits are used
+ * @param startx the start x position
+ * @param starty the start y position
+ * @param width the width of the area
+ * @param height the height of the area
+ */
+ protected void drawBackAndBorders(Area area, float startx, float starty,
+ float width, float height) {
+ try {
+ updatePrintDirection();
+ BorderProps bpsBefore = (BorderProps) area.getTrait(Trait.BORDER_BEFORE);
+ BorderProps bpsAfter = (BorderProps) area.getTrait(Trait.BORDER_AFTER);
+ BorderProps bpsStart = (BorderProps) area.getTrait(Trait.BORDER_START);
+ BorderProps bpsEnd = (BorderProps) area.getTrait(Trait.BORDER_END);
+
+ // draw background
+ Trait.Background back;
+ back = (Trait.Background) area.getTrait(Trait.BACKGROUND);
+ if (back != null) {
+
+ // Calculate padding rectangle
+ float sx = startx;
+ float sy = starty;
+ float paddRectWidth = width;
+ float paddRectHeight = height;
+
+ if (bpsStart != null) {
+ sx += bpsStart.width / 1000f;
+ paddRectWidth -= bpsStart.width / 1000f;
+ }
+ if (bpsBefore != null) {
+ sy += bpsBefore.width / 1000f;
+ paddRectHeight -= bpsBefore.width / 1000f;
+ }
+ if (bpsEnd != null) {
+ paddRectWidth -= bpsEnd.width / 1000f;
+ }
+ if (bpsAfter != null) {
+ paddRectHeight -= bpsAfter.width / 1000f;
+ }
+
+ if (back.getColor() != null) {
+ Point2D p = transformedPoint(sx * 1000, sy * 1000);
+ gen.fillRect((int)p.getX(), (int)p.getY(),
+ (int)paddRectWidth * 1000, (int)paddRectHeight * 1000,
+ back.getColor().getAWTColor());
+ }
+
+ // background image
+ if (back.getFopImage() != null) {
+ FopImage fopimage = back.getFopImage();
+ if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
+ saveGraphicsState();
+ clipRect(sx, sy, paddRectWidth, paddRectHeight);
+ int horzCount = (int) ((paddRectWidth * 1000 / fopimage
+ .getIntrinsicWidth()) + 1.0f);
+ int vertCount = (int) ((paddRectHeight * 1000 / fopimage
+ .getIntrinsicHeight()) + 1.0f);
+ if (back.getRepeat() == EN_NOREPEAT) {
+ horzCount = 1;
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATX) {
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATY) {
+ horzCount = 1;
+ }
+ // change from points to millipoints
+ sx *= 1000;
+ sy *= 1000;
+ if (horzCount == 1) {
+ sx += back.getHoriz();
+ }
+ if (vertCount == 1) {
+ sy += back.getVertical();
+ }
+ for (int x = 0; x < horzCount; x++) {
+ for (int y = 0; y < vertCount; y++) {
+ // place once
+ Rectangle2D pos;
+ pos = new Rectangle2D.Float(sx
+ + (x * fopimage.getIntrinsicWidth()), sy
+ + (y * fopimage.getIntrinsicHeight()),
+ fopimage.getIntrinsicWidth(), fopimage
+ .getIntrinsicHeight());
+ //putImage(back.getURL(), pos); // TODO test
+ }
+ }
+ restoreGraphicsState();
+ } else {
+ log.warn(
+ "Can't find background image: " + back.getURL());
+ }
+ }
+ }
+/*
+ // draw border
+ // BORDER_BEFORE
+ if (bpsBefore != null) {
+ int borderWidth = (int) Math.round((bpsBefore.width / 1000f));
+ state.updateColor(bpsBefore.color);
+ state.getGraph().fillRect((int) startx, (int) starty, (int) width,
+ borderWidth);
+ }
+ // BORDER_AFTER
+ if (bpsAfter != null) {
+ int borderWidth = (int) Math.round((bpsAfter.width / 1000f));
+ float sy = starty + height;
+ state.updateColor(bpsAfter.color);
+ state.getGraph().fillRect((int) startx,
+ (int) (starty + height - borderWidth), (int) width,
+ borderWidth);
+ }
+ // BORDER_START
+ if (bpsStart != null) {
+ int borderWidth = (int) Math.round((bpsStart.width / 1000f));
+ state.updateColor(bpsStart.color);
+ state.getGraph().fillRect((int) startx, (int) starty, borderWidth,
+ (int) height);
+ }
+ // BORDER_END
+ if (bpsEnd != null) {
+ int borderWidth = (int) Math.round((bpsEnd.width / 1000f));
+ float sx = startx + width;
+ state.updateColor(bpsEnd.color);
+ state.getGraph().fillRect((int) (startx + width - borderWidth),
+ (int) starty, borderWidth, (int) height);
+ }
+ */
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
+ }
}
+
}
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java b/src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java
new file mode 100644
index 000000000..73e27bcac
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLRendererContext.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.util.QName;
+
+/**
+ * Wrapper on the RendererContext to access the information structure for drawing
+ * the XML document.
+ */
+public class PCLRendererContext {
+
+ private RendererContext context;
+
+ /**
+ * Wrap the render context to allow easier access to its values.
+ *
+ * @param context the renderer context
+ * @return the PCL-specific renderer context wrapper
+ */
+ public static PCLRendererContext wrapRendererContext(RendererContext context) {
+ PCLRendererContext pcli = new PCLRendererContext(context);
+ return pcli;
+ }
+
+ /**
+ * Main constructor
+ * @param context the RendererContent instance
+ */
+ public PCLRendererContext(RendererContext context) {
+ this.context = context;
+ }
+
+ /** @return the currentXPosition */
+ public int getCurrentXPosition() {
+ return ((Integer)context.getProperty(PCLSVGHandler.XPOS)).intValue();
+ }
+
+ /** @return the currentYPosition */
+ public int getCurrentYPosition() {
+ return ((Integer)context.getProperty(PCLSVGHandler.YPOS)).intValue();
+ }
+
+ /** @return the width of the image */
+ public int getWidth() {
+ return ((Integer)context.getProperty(PCLSVGHandler.WIDTH)).intValue();
+ }
+
+ /** @return the height of the image */
+ public int getHeight() {
+ return ((Integer)context.getProperty(PCLSVGHandler.HEIGHT)).intValue();
+ }
+
+ /** @return the handler configuration */
+ public Configuration getHandlerConfiguration() {
+ return (Configuration)context.getProperty(PCLSVGHandler.HANDLER_CONFIGURATION);
+ }
+
+ /** @return the foreign attributes */
+ public Map getForeignAttributes() {
+ return (Map)context.getProperty(PCLSVGHandler.FOREIGN_ATTRIBUTES);
+ }
+
+ /** @return true if the SVG image should be rendered as a bitmap */
+ public boolean paintAsBitmap() {
+ QName qName = new QName(ExtensionElementMapping.URI, null, "conversion-mode");
+ return getForeignAttributes() != null
+ && "bitmap".equals(getForeignAttributes().get(qName));
+ }
+
+} \ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java b/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java
new file mode 100644
index 000000000..2e187d482
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+// Java
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+// DOM
+import org.w3c.dom.Document;
+
+// Batik
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.gvt.GraphicsNode;
+
+// FOP
+import org.apache.fop.render.Graphics2DAdapter;
+import org.apache.fop.render.Graphics2DImagePainter;
+import org.apache.fop.render.Renderer;
+import org.apache.fop.render.RendererContextConstants;
+import org.apache.fop.render.XMLHandler;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.svg.SVGUserAgent;
+
+// Commons-Logging
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * PCL XML handler for SVG. Uses Apache Batik for SVG processing.
+ * This handler handles XML for foreign objects when rendering to HP GL/2.
+ * It renders SVG to HP GL/2 using the PCLGraphics2D.
+ *
+ * @version $Id$
+ */
+public class PCLSVGHandler implements XMLHandler, RendererContextConstants {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PCLSVGHandler.class);
+
+ /**
+ * Create a new XML handler for use by the PCL renderer.
+ */
+ public PCLSVGHandler() {
+ }
+
+ /** @see org.apache.fop.render.XMLHandler */
+ public void handleXML(RendererContext context,
+ Document doc, String ns) throws Exception {
+ PCLRendererContext pclContext = PCLRendererContext.wrapRendererContext(context);
+
+ if (SVGDOMImplementation.SVG_NAMESPACE_URI.equals(ns)) {
+ renderSVGDocument(context, doc, pclContext);
+ }
+ }
+
+ /**
+ * Render the SVG document.
+ * @param context the renderer context
+ * @param doc the SVG document
+ * @param pclContext the information of the current context
+ */
+ protected void renderSVGDocument(final RendererContext context,
+ final Document doc, final PCLRendererContext pclContext) {
+ int x = pclContext.getCurrentXPosition();
+ int y = pclContext.getCurrentYPosition();
+
+ Graphics2DImagePainter painter = new Graphics2DImagePainter() {
+
+ public void paint(Graphics2D g2d, Rectangle2D area) {
+ SVGUserAgent ua = new SVGUserAgent(
+ context.getUserAgent().getSourcePixelUnitToMillimeter(),
+ new AffineTransform());
+ GVTBuilder builder = new GVTBuilder();
+ BridgeContext ctx = new BridgeContext(ua);
+
+ GraphicsNode root;
+ try {
+ root = builder.build(ctx, doc);
+
+ // If no viewbox is defined in the svg file, a viewbox of 100x100 is
+ // assumed, as defined in SVGUserAgent.getViewportSize()
+ float iw = (float) ctx.getDocumentSize().getWidth() * 1000f;
+ float ih = (float) ctx.getDocumentSize().getHeight() * 1000f;
+ float w = (float) area.getWidth();
+ float h = (float) area.getHeight();
+ g2d.scale(w / iw, h / ih);
+
+ root.paint(g2d);
+ } catch (Exception e) {
+ log.error("SVG graphic could not be built: "
+ + e.getMessage(), e);
+ return;
+ }
+ }
+
+ public Dimension getImageSize() {
+ return new Dimension(pclContext.getWidth(), pclContext.getHeight());
+ }
+
+ };
+
+ try {
+ Graphics2DAdapter adapter = context.getRenderer().getGraphics2DAdapter();
+ adapter.paintImage(painter, context,
+ x, y, pclContext.getWidth(), pclContext.getHeight());
+ } catch (IOException ioe) {
+ ((PCLRenderer)context.getRenderer()).handleIOTrouble(ioe);
+ }
+ }
+
+ /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
+ public boolean supportsRenderer(Renderer renderer) {
+ return (renderer instanceof PCLRenderer);
+ }
+
+ /** @see org.apache.fop.render.XMLHandler#getNamespace() */
+ public String getNamespace() {
+ return SVGDOMImplementation.SVG_NAMESPACE_URI;
+ }
+
+}
+
diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLStream.java b/src/sandbox/org/apache/fop/render/pcl/PCLStream.java
deleted file mode 100644
index 34150f16a..000000000
--- a/src/sandbox/org/apache/fop/render/pcl/PCLStream.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 1999-2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.render.pcl;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-public class PCLStream {
-
- private OutputStream out = null;
- private boolean doOutput = true;
-
- public PCLStream(OutputStream os) {
- out = os;
- }
-
- public void add(String str) {
- if (!doOutput) {
- return;
- }
-
- byte buff[] = new byte[str.length()];
- int countr;
- int len = str.length();
- for (countr = 0; countr < len; countr++) {
- buff[countr] = (byte)str.charAt(countr);
- }
- try {
- out.write(buff);
- } catch (IOException e) {
- throw new RuntimeException(e.toString());
- }
- }
-
- public void setDoOutput(boolean doout) {
- doOutput = doout;
- }
-
-}
diff --git a/src/sandbox/org/apache/fop/render/pcl/package.html b/src/sandbox/org/apache/fop/render/pcl/package.html
index feec0572e..ba47bd289 100644
--- a/src/sandbox/org/apache/fop/render/pcl/package.html
+++ b/src/sandbox/org/apache/fop/render/pcl/package.html
@@ -1,5 +1,5 @@
<!--
- Copyright 2005 The Apache Software Foundation
+ Copyright 2005-2006 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,6 +17,6 @@
<HTML>
<TITLE>org.apache.fop.render.pcl Package</TITLE>
<BODY>
-<P>PCL Renderer</P>
+<P>PCL Renderer (Supports PCL5 and HP GL/2)</P>
</BODY>
</HTML> \ No newline at end of file
diff --git a/test/java/org/apache/fop/UtilityCodeTestSuite.java b/test/java/org/apache/fop/UtilityCodeTestSuite.java
index cce534e92..09a2ef982 100644
--- a/test/java/org/apache/fop/UtilityCodeTestSuite.java
+++ b/test/java/org/apache/fop/UtilityCodeTestSuite.java
@@ -21,6 +21,7 @@ package org.apache.fop;
import org.apache.fop.traits.BorderPropsTestCase;
import org.apache.fop.traits.TraitColorTestCase;
import org.apache.fop.util.PDFNumberTestCase;
+import org.apache.fop.util.UnitConvTestCase;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -39,6 +40,7 @@ public class UtilityCodeTestSuite {
"Test suite for FOP's utility classes");
//$JUnit-BEGIN$
suite.addTest(new TestSuite(PDFNumberTestCase.class));
+ suite.addTest(new TestSuite(UnitConvTestCase.class));
suite.addTest(new TestSuite(TraitColorTestCase.class));
suite.addTest(new TestSuite(BorderPropsTestCase.class));
//$JUnit-END$
diff --git a/test/java/org/apache/fop/util/UnitConvTestCase.java b/test/java/org/apache/fop/util/UnitConvTestCase.java
new file mode 100644
index 000000000..32d92473d
--- /dev/null
+++ b/test/java/org/apache/fop/util/UnitConvTestCase.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import junit.framework.TestCase;
+
+/**
+ * Test class for UnitConv.
+ */
+public class UnitConvTestCase extends TestCase {
+
+ /**
+ * Test all kinds of unit conversions.
+ * @throws Exception if the test fails
+ */
+ public void testConversions() throws Exception {
+ assertEquals("in2mm", 25.4, UnitConv.in2mm(1), 0.00001);
+ assertEquals("mm2in", 1.0, UnitConv.mm2in(25.4), 0.00001);
+ assertEquals("mm2pt", 841.890, UnitConv.mm2pt(297), 0.001 / 2); //height of an A4 page
+ assertEquals("mm2mpt", 841890, UnitConv.mm2mpt(297), 1.0 / 2);
+ assertEquals("pt2mm", 297, UnitConv.pt2mm(841.890), 0.0001);
+ assertEquals("in2mpt", 792000, UnitConv.in2mpt(11.0), 1.0 / 2); //height of a letter page
+ assertEquals("mpt2in", 11.0, UnitConv.mpt2in(792000), 0.01 / 2); //height of a letter page
+
+ assertEquals("mm2px/72dpi", 842, UnitConv.mm2px(297, 72), 0.0001);
+ assertEquals("mm2px/300dpi", 3508, UnitConv.mm2px(297, 300), 0.0001);
+ }
+
+} \ No newline at end of file