blob: d9af7a657afeaab96f51daae92638d06cedb5021 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package com.sun.star.report.pentaho.output;
import com.sun.star.report.DataSourceFactory;
import com.sun.star.report.ImageService;
import com.sun.star.report.InputRepository;
import com.sun.star.report.OfficeToken;
import com.sun.star.report.OutputRepository;
import com.sun.star.report.ReportEngineParameterNames;
import com.sun.star.report.SDBCReportDataFactory;
import com.sun.star.report.pentaho.OfficeNamespaces;
import com.sun.star.report.pentaho.layoutprocessor.ImageElementContext;
import com.sun.star.report.pentaho.model.OfficeDocument;
import com.sun.star.report.pentaho.model.OfficeStyle;
import com.sun.star.report.pentaho.model.OfficeStyles;
import com.sun.star.report.pentaho.model.OfficeStylesCollection;
import com.sun.star.report.pentaho.styles.LengthCalculator;
import com.sun.star.report.pentaho.styles.StyleMapper;
import com.sun.org.apache.xerces.internal.parsers.DOMParser;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import java.awt.Image;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jfree.layouting.input.style.parser.CSSValueFactory;
import org.jfree.layouting.input.style.parser.StyleSheetParserUtil;
import org.jfree.layouting.input.style.values.CSSNumericType;
import org.jfree.layouting.input.style.values.CSSNumericValue;
import org.jfree.layouting.layouter.style.CSSValueResolverUtility;
import org.jfree.layouting.namespace.NamespaceDefinition;
import org.jfree.layouting.namespace.Namespaces;
import org.jfree.layouting.util.AttributeMap;
import org.jfree.layouting.util.LazyAttributeMap;
import org.jfree.report.DataFlags;
import org.jfree.report.DataSourceException;
import org.jfree.report.JFreeReportBoot;
import org.jfree.report.JFreeReportInfo;
import org.jfree.report.ReportProcessingException;
import org.jfree.report.flow.AbstractReportTarget;
import org.jfree.report.flow.ReportJob;
import org.jfree.report.flow.ReportStructureRoot;
import org.jfree.report.flow.ReportTargetUtil;
import org.jfree.report.structure.Element;
import org.jfree.report.structure.Section;
import org.jfree.report.util.AttributeNameGenerator;
import org.jfree.report.util.IntegerCache;
import org.jfree.report.util.MemoryByteArrayOutputStream;
import org.pentaho.reporting.libraries.base.util.FastStack;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.xmlns.common.AttributeList;
import org.pentaho.reporting.libraries.xmlns.writer.DefaultTagDescription;
import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter;
import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
import org.w3c.css.sac.LexicalUnit;
/**
* Todo: Document me!
*
* @author Thomas Morgner
* @since 08.03.2007
*/
public abstract class OfficeDocumentReportTarget extends AbstractReportTarget
{
protected static final Log LOGGER = LogFactory.getLog(OfficeDocumentReportTarget.class);
public static final String HORIZONTAL_POS = "horizontal-pos";
public static final String TAG_DEF_PREFIX = "com.sun.star.report.pentaho.output.";
public static final int ROLE_NONE = 0;
public static final int ROLE_REPORT_HEADER = 1;
public static final int ROLE_REPORT_FOOTER = 2;
public static final int ROLE_GROUP_HEADER = 3;
public static final int ROLE_GROUP_FOOTER = 4;
public static final int ROLE_REPEATING_GROUP_HEADER = 5;
public static final int ROLE_REPEATING_GROUP_FOOTER = 6;
public static final int ROLE_PAGE_HEADER = 7;
public static final int ROLE_PAGE_FOOTER = 8;
public static final int ROLE_DETAIL = 9;
public static final int ROLE_VARIABLES = 10;
public static final int ROLE_TEMPLATE = 11;
public static final int ROLE_SPREADSHEET_PAGE_HEADER = 12;
public static final int ROLE_SPREADSHEET_PAGE_FOOTER = 13;
public static final int STATE_IN_DOCUMENT = 0;
public static final int STATE_IN_BODY = 1;
public static final int STATE_IN_CONTENT = 2;
public static final int STATE_IN_GROUP = 3;
public static final int STATE_IN_GROUP_BODY = 4;
public static final int STATE_IN_SECTION = 5;
public static final int STATE_IN_OTHER = 6;
public static final int STATE_IN_GROUP_INSTANCE = 7;
public static final String FAILED = "Failed";
public static final String VERTICAL_POS = "vertical-pos";
private static final String ZERO_CM = "0cm";
/** the verison of the ODF specification to which generated documents
* shall conform. */
public static final String ODF_VERSION = "1.2";
protected static class BufferState
{
private final XmlWriter xmlWriter;
private final MemoryByteArrayOutputStream xmlBuffer;
private final OfficeStylesCollection stylesCollection;
protected BufferState(final XmlWriter xmlWriter,
final MemoryByteArrayOutputStream xmlBuffer,
final OfficeStylesCollection stylesCollection)
{
this.stylesCollection = stylesCollection;
this.xmlWriter = xmlWriter;
this.xmlBuffer = xmlBuffer;
}
public OfficeStylesCollection getStylesCollection()
{
return stylesCollection;
}
public XmlWriter getXmlWriter()
{
return xmlWriter;
}
public String getXmlBuffer() throws ReportProcessingException
{
try
{
final byte[] zippedData = xmlBuffer.getRaw();
final InputStreamReader reader = new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(zippedData, 0, xmlBuffer.getLength())), "UTF-16");
final StringWriter writer = new StringWriter((zippedData.length / 2) + 1);
IOUtils.getInstance().copyWriter(reader, writer);
return writer.toString();
}
catch (IOException e)
{
throw new ReportProcessingException("Failed to copy buffer", e);
}
}
public Reader getXmlAsReader() throws ReportProcessingException
{
try
{
final byte[] zippedData = xmlBuffer.getRaw();
return new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(zippedData, 0, xmlBuffer.getLength())), "UTF-16");
}
catch (IOException e)
{
throw new ReportProcessingException("Failed to copy buffer", e);
}
}
}
protected static class GroupContext
{
private final GroupContext parent;
private int iterationCount;
private boolean groupWithRepeatingSection;
protected GroupContext(final GroupContext parent)
{
this.parent = parent;
}
public GroupContext getParent()
{
return parent;
}
public int getIterationCount()
{
return iterationCount;
}
public void setIterationCount(final int iterationCount)
{
this.iterationCount = iterationCount;
}
public boolean isGroupWithRepeatingSection()
{
return groupWithRepeatingSection;
}
public void setGroupWithRepeatingSection(final boolean groupWithRepeatingSection)
{
this.groupWithRepeatingSection = groupWithRepeatingSection;
}
public String toString()
{
return "GroupContext{" + "parent=" + parent + ", iterationCount=" + iterationCount + ", groupWithRepeatingSection=" + groupWithRepeatingSection + '}';
}
}
private final FastStack states;
private int currentRole;
private final FastStack xmlWriters;
private XmlWriter rootXmlWriter;
/**
* This styles-collection contains all styles that were predefined in the report definition file. The common styles
* and the master-styles will be written unmodified, the automatic styles will be ignored.
*/
private OfficeStylesCollection predefinedStylesCollection;
/**
* This styles-collection contains all master-styles that have been generated by the report definition process. It
* also contains all automatic styles that have been generated for the page-bands (and the pagebands as well).
*/
private OfficeStylesCollection globalStylesCollection;
/**
* The content styles collection contains all automatic styles that have been generated for the normal-flow content.
*/
private OfficeStylesCollection contentStylesCollection;
private final OutputRepository outputRepository;
private final InputRepository inputRepository;
private final AttributeNameGenerator tableNameGenerator;
private final AttributeNameGenerator frameNameGenerator;
private final AttributeNameGenerator autoStyleNameGenerator;
private final String target;
private static final int INITIAL_BUFFER_SIZE = 40960;
private StyleMapper styleMapper;
private StyleSheetParserUtil styleSheetParserUtil;
private final AttributeNameGenerator imageNames;
private final ImageProducer imageProducer;
private final OleProducer oleProducer;
private GroupContext groupContext;
private static final boolean DEBUG_ELEMENTS =
JFreeReportBoot.getInstance().getExtendedConfig().getBoolProperty("com.sun.star.report.pentaho.output.DebugElements");
protected OfficeDocumentReportTarget(final ReportJob reportJob,
final ResourceManager resourceManager,
final ResourceKey baseResource,
final InputRepository inputRepository,
final OutputRepository outputRepository,
final String target,
final ImageService imageService,
final DataSourceFactory datasourcefactory)
throws ReportProcessingException
{
super(reportJob, resourceManager, baseResource);
if (imageService == null)
{
throw new NullPointerException("ImageService must not be null");
}
if (target == null)
{
throw new NullPointerException("Target-Name must not be null");
}
this.target = target;
this.tableNameGenerator = new AttributeNameGenerator();
this.frameNameGenerator = new AttributeNameGenerator();
this.autoStyleNameGenerator = new AttributeNameGenerator();
this.outputRepository = outputRepository;
this.inputRepository = inputRepository;
this.states = new FastStack();
this.xmlWriters = new FastStack();
this.imageNames = new AttributeNameGenerator();
this.imageProducer = new ImageProducer(inputRepository, outputRepository, imageService);
this.oleProducer = new OleProducer(inputRepository, outputRepository, imageService, datasourcefactory, (Integer) reportJob.getParameters().get(ReportEngineParameterNames.MAXROWS));
try
{
final ResourceManager realResourceManager = getResourceManager();
styleMapper = StyleMapper.loadInstance(realResourceManager);
}
catch (ResourceException e)
{
throw new ReportProcessingException("Failed to load style-mapper", e);
}
}
protected abstract String getTargetMimeType();
protected OutputRepository getOutputRepository()
{
return outputRepository;
}
protected InputRepository getInputRepository()
{
return inputRepository;
}
/**
* Starts the output of a new office document. This method writes the generic 'office:document-content' tag along with
* all known namespace declarations.
*
* @param report the report object.
* @throws DataSourceException if there was an error accessing the datasource
* @throws ReportProcessingException if some other error occured.
*/
public void startReport(final ReportStructureRoot report)
throws DataSourceException, ReportProcessingException
{
imageNames.reset();
this.groupContext = new GroupContext(null);
final DefaultTagDescription tagDescription = createTagDescription();
try
{
final OutputStream outputStream = outputRepository.createOutputStream(target, "text/xml");
final Writer writer = new OutputStreamWriter(outputStream, "UTF-8");
this.rootXmlWriter = new XmlWriter(writer, tagDescription);
this.rootXmlWriter.setAlwaysAddNamespace(true);
final AttributeList rootAttributes = new AttributeList();
rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS);
rootAttributes.addNamespaceDeclaration("style", OfficeNamespaces.STYLE_NS);
rootAttributes.addNamespaceDeclaration("text", OfficeNamespaces.TEXT_NS);
rootAttributes.addNamespaceDeclaration("table", OfficeNamespaces.TABLE_NS);
rootAttributes.addNamespaceDeclaration("draw", OfficeNamespaces.DRAWING_NS);
rootAttributes.addNamespaceDeclaration("fo", OfficeNamespaces.FO_NS);
rootAttributes.addNamespaceDeclaration("xlink", OfficeNamespaces.XLINK_NS);
rootAttributes.addNamespaceDeclaration("dc", OfficeNamespaces.PURL_NS);
rootAttributes.addNamespaceDeclaration("meta", OfficeNamespaces.META_NS);
rootAttributes.addNamespaceDeclaration("number", OfficeNamespaces.DATASTYLE_NS);
rootAttributes.addNamespaceDeclaration("svg", OfficeNamespaces.SVG_NS);
rootAttributes.addNamespaceDeclaration("chart", OfficeNamespaces.CHART_NS);
rootAttributes.addNamespaceDeclaration("chartooo", OfficeNamespaces.CHARTOOO_NS);
rootAttributes.addNamespaceDeclaration("dr3d", OfficeNamespaces.DR3D_NS);
rootAttributes.addNamespaceDeclaration("math", OfficeNamespaces.MATHML_NS);
rootAttributes.addNamespaceDeclaration("form", OfficeNamespaces.FORM_NS);
rootAttributes.addNamespaceDeclaration("script", OfficeNamespaces.SCRIPT_NS);
rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS);
rootAttributes.addNamespaceDeclaration("ooow", OfficeNamespaces.OOW2004_NS);
rootAttributes.addNamespaceDeclaration("oooc", OfficeNamespaces.OOC2004_NS);
rootAttributes.addNamespaceDeclaration("dom", OfficeNamespaces.XML_EVENT_NS);
rootAttributes.addNamespaceDeclaration("xforms", OfficeNamespaces.XFORMS_NS);
rootAttributes.addNamespaceDeclaration("xsd", OfficeNamespaces.XSD_NS);
rootAttributes.addNamespaceDeclaration("xsi", OfficeNamespaces.XSI_NS);
rootAttributes.addNamespaceDeclaration("grddl", OfficeNamespaces.GRDDL_NS);
rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version",
ODF_VERSION);
this.rootXmlWriter.writeXmlDeclaration("UTF-8");
this.rootXmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-content", rootAttributes, XmlWriterSupport.OPEN);
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_DOCUMENT));
autoStyleNameGenerator.reset();
tableNameGenerator.reset();
frameNameGenerator.reset();
final OfficeDocument reportDoc = (OfficeDocument) report;
predefinedStylesCollection = reportDoc.getStylesCollection();
final OfficeStyles commonStyles = predefinedStylesCollection.getCommonStyles();
if (!commonStyles.containsStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS))
{
final OfficeStyle graphicsDefaultStyle = new OfficeStyle();
graphicsDefaultStyle.setStyleFamily(OfficeToken.GRAPHIC);
graphicsDefaultStyle.setStyleName(OfficeToken.GRAPHICS);
final Element graphicProperties = produceFirstChild(graphicsDefaultStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
graphicProperties.setAttribute(OfficeNamespaces.TEXT_NS, "anchor-type", OfficeToken.PARAGRAPH);
graphicProperties.setAttribute(OfficeNamespaces.SVG_NS, "x", ZERO_CM);
graphicProperties.setAttribute(OfficeNamespaces.SVG_NS, "y", ZERO_CM);
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "wrap", "dynamic");
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "number-wrapped-paragraphs", "no-limit");
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "wrap-contour", OfficeToken.FALSE);
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top"); // changed for chart
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", OfficeToken.PARAGRAPH);
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left"); // changed for chart
graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", OfficeToken.PARAGRAPH);
commonStyles.addStyle(graphicsDefaultStyle);
}
// Make sure that later generated styles do not overwrite existing styles.
fillStyleNameGenerator(predefinedStylesCollection);
contentStylesCollection = new OfficeStylesCollection();
globalStylesCollection = new OfficeStylesCollection();
startBuffering(contentStylesCollection, true);
}
catch (IOException e)
{
throw new ReportProcessingException(FAILED, e);
}
}
protected AttributeNameGenerator getAutoStyleNameGenerator()
{
return autoStyleNameGenerator;
}
private void fillStyleNameGenerator(final OfficeStylesCollection stylesCollection)
{
final OfficeStyles commonStyles = stylesCollection.getCommonStyles();
final OfficeStyle[] allCommonStyles = commonStyles.getAllStyles();
for (int i = 0; i < allCommonStyles.length; i++)
{
final OfficeStyle style = allCommonStyles[i];
autoStyleNameGenerator.generateName(style.getStyleName());
}
final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
final OfficeStyle[] allAutoStyles = autoStyles.getAllStyles();
for (int i = 0; i < allAutoStyles.length; i++)
{
final OfficeStyle style = allAutoStyles[i];
autoStyleNameGenerator.generateName(style.getStyleName());
}
}
public OfficeStylesCollection getPredefinedStylesCollection()
{
return predefinedStylesCollection;
}
public OfficeStylesCollection getGlobalStylesCollection()
{
return globalStylesCollection;
}
public OfficeStylesCollection getContentStylesCollection()
{
return contentStylesCollection;
}
/**
* Returns the XML-Writer tag description. This description defines whether an element can have character data inside.
* Such element will disable the indention, as in that case the additional whitespaces might alter the meaning of the
* element's contents.
*
* @return the tag description library.
*/
protected DefaultTagDescription createTagDescription()
{
final DefaultTagDescription tagDescription = new DefaultTagDescription();
tagDescription.configure(JFreeReportBoot.getInstance().getGlobalConfig(),
OfficeDocumentReportTarget.TAG_DEF_PREFIX);
return tagDescription;
}
/**
* Returns the current processing state.
*
* @return the processing state.
*/
protected int getCurrentState()
{
if (states.isEmpty())
{
throw new IllegalStateException();
}
final Integer o = (Integer) states.peek();
return o;
}
/**
* Starts the processing of an element and updates the processing state. This will select an apropriate handler method
* for the call and will call one of the start* methods.
*
* @param roAttrs the attribute map for the current element
* @throws DataSourceException
* @throws ReportProcessingException
*/
public final void startElement(final AttributeMap roAttrs)
throws DataSourceException, ReportProcessingException
{
final AttributeMap attrs = new LazyAttributeMap(roAttrs);
// todo
if (DEBUG_ELEMENTS)
{
LOGGER.debug("Starting " + getCurrentState() + '/' + states.size() + ' ' + ReportTargetUtil.getNamespaceFromAttribute(attrs) + " -> " + ReportTargetUtil.getElemenTypeFromAttribute(attrs));
}
try
{
switch (getCurrentState())
{
case OfficeDocumentReportTarget.STATE_IN_DOCUMENT:
{
if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OFFICE_NS, "body", attrs))
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_BODY));
startBody(attrs);
}
else
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER));
if (!isFilteredNamespace(ReportTargetUtil.getNamespaceFromAttribute(attrs)))
{
startOther(attrs);
}
}
break;
}
case OfficeDocumentReportTarget.STATE_IN_BODY:
{
if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OFFICE_NS, "report", attrs))
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_CONTENT));
startContent(attrs);
}
else
{
throw new IllegalStateException("The 'office:body' element must have exactly one child of type 'report'");
}
break;
}
case OfficeDocumentReportTarget.STATE_IN_CONTENT:
{
// Either a ordinary section or a group ..
// A group.
if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "report-body", attrs))
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_BODY));
startGroupBody(attrs);
}
else
{
// Either a template-section, page-header, page-footer, report-header, report-footer
// or variables-section
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "template", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_TEMPLATE;
}
else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "page-header", attrs))
{
if ("spreadsheet-section".equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "role")))
{
currentRole = OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER;
}
else
{
currentRole = OfficeDocumentReportTarget.ROLE_PAGE_HEADER;
}
}
else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "page-footer", attrs))
{
if ("spreadsheet-section".equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "role")))
{
currentRole = OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER;
}
else
{
currentRole = OfficeDocumentReportTarget.ROLE_PAGE_FOOTER;
}
}
else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "report-header", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_REPORT_HEADER;
}
else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "report-footer", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_REPORT_FOOTER;
}
else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES;
}
else
{
throw new IllegalStateException("Expected either 'template', 'report-body', " + "'report-header', 'report-footer', 'variables-section', 'page-header' or 'page-footer'");
}
startReportSection(attrs, currentRole);
}
break;
}
case OfficeDocumentReportTarget.STATE_IN_GROUP_BODY:
{
// We now expect either an other group or a detail band.
if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group", attrs))
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP));
groupContext = new GroupContext(groupContext);
startGroup(attrs);
}
else
{
// Either a variables-section, or a detail-band
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "detail", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_DETAIL;
}
else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES;
}
else
{
throw new IllegalStateException("Expected either 'group', 'detail' or 'variables-section'");
}
startReportSection(attrs, currentRole);
}
break;
}
case OfficeDocumentReportTarget.STATE_IN_GROUP:
{
// A group can carry a repeating group header/footer or a group-instance section.
if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "group-instance", attrs))
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE));
startGroupInstance(attrs);
}
else
{
// repeating group header/footer, but *no* variables section
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-header", attrs) && OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeated-section")))
{
currentRole = OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER;
}
else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-footer", attrs) && OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeated-section")))
{
currentRole = OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER;
}
else
{
throw new IllegalStateException("Expected either 'group-instance', " + "'repeating group-header' or 'repeating group-footer'");
}
startReportSection(attrs, currentRole);
}
break;
}
case OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE:
{
if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "group-body", attrs))
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_BODY));
startGroupBody(attrs);
}
else
{
// Either a group-header or group-footer or variables-section
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-header", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_GROUP_HEADER;
}
else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-footer", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_GROUP_FOOTER;
}
else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs))
{
currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES;
}
else
{
throw new IllegalStateException("Expected either 'group-body', 'group-header', 'group-footer' or 'variables-section'");
}
startReportSection(attrs, currentRole);
}
break;
}
case OfficeDocumentReportTarget.STATE_IN_SECTION:
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER));
startOther(attrs);
break;
}
case OfficeDocumentReportTarget.STATE_IN_OTHER:
{
states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER));
startOther(attrs);
break;
}
default:
throw new IllegalStateException("Failure: " + getCurrentState());
}
}
catch (IOException ioe)
{
LOGGER.error("ReportProcessing failed", ioe);
throw new ReportProcessingException("Failed to write content", ioe);
}
// finally
// {
// LOGGER.debug ("Started " + getNamespaceFromAttribute(attrs) + ":" +
// getElemenTypeFromAttribute(attrs) + " -> " + getCurrentState());
// }
}
protected GroupContext getGroupContext()
{
return groupContext;
}
protected void performStyleProcessing(final AttributeMap attrs)
throws ReportProcessingException
{
final OfficeStylesCollection stylesCollection = getStylesCollection();
final OfficeStylesCollection predefCollection = getPredefinedStylesCollection();
final OfficeStylesCollection globalStylesCollection = getGlobalStylesCollection();
final String elementNamespace =
ReportTargetUtil.getNamespaceFromAttribute(attrs);
final String elementName =
ReportTargetUtil.getElemenTypeFromAttribute(attrs);
final String[] namespaces = attrs.getNameSpaces();
for (int i = 0; i < namespaces.length; i++)
{
final String attrNamespace = namespaces[i];
if (isFilteredNamespace(attrNamespace))
{
continue;
}
final Map attributes = attrs.getAttributes(attrNamespace);
final Iterator iterator = attributes.entrySet().iterator();
while (iterator.hasNext())
{
final Map.Entry entry = (Map.Entry) iterator.next();
final String attrName = (String) entry.getKey();
final String attrValue = String.valueOf(entry.getValue());
final String styleFamily = styleMapper.getStyleFamilyFor(elementNamespace, elementName, attrNamespace, attrName);
if (styleFamily == null)
{
// None of the known style attributes.
continue;
}
if (styleMapper.isListOfStyles(elementNamespace, elementName, attrNamespace, attrName))
{
// ignored for now.
LOGGER.warn("List of styles is not yet implemented.");
continue;
}
// Copy styles is only called once per style.
StyleUtilities.copyStyle(styleFamily, attrValue, stylesCollection, globalStylesCollection, predefCollection);
}
}
}
protected void startBody(final AttributeMap attrs)
throws IOException
{
getXmlWriter().writeTag(OfficeNamespaces.OFFICE_NS, "body", XmlWriterSupport.OPEN);
}
private final boolean allowBuffering(final int role)
{
return (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_TEMPLATE);
}
protected void startReportSection(final AttributeMap attrs, final int role)
throws IOException, DataSourceException, ReportProcessingException
{
if (allowBuffering(role))
{
startBuffering(new OfficeStylesCollection(), true);
}
}
protected abstract void startContent(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException;
protected void startGroup(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
final Object repeatingHeaderOrFooter = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeating-header-or-footer");
if (OfficeToken.TRUE.equals(repeatingHeaderOrFooter))
{
getGroupContext().setGroupWithRepeatingSection(true);
}
final Object iterationCount = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "iteration-count");
if (iterationCount instanceof Number)
{
final Number itNumber = (Number) iterationCount;
getGroupContext().setIterationCount(itNumber.intValue());
}
}
protected void startGroupInstance(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
}
protected void startGroupBody(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
}
protected abstract void startOther(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException;
public void processText(final String text)
throws DataSourceException, ReportProcessingException
{
try
{
final XmlWriter xmlWriter = getXmlWriter();
final BufferedReader br = new BufferedReader(new StringReader(text));
String line = br.readLine();
while (line != null)
{
xmlWriter.writeTextNormalized(line, false);
line = br.readLine();
if (line != null)
{
xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, "line-break", XmlWriterSupport.CLOSE);
}
}
}
catch (IOException e)
{
throw new ReportProcessingException(FAILED, e);
}
}
protected boolean isFilteredNamespace(final String namespace)
{
if (Namespaces.LIBLAYOUT_NAMESPACE.equals(namespace))
{
return true;
}
if (JFreeReportInfo.REPORT_NAMESPACE.equals(namespace))
{
return true;
}
if (JFreeReportInfo.REPORT_NAMESPACE.equals(namespace))
{
return true;
}
if (JFreeReportInfo.COMPATIBILITY_NAMESPACE.equals(namespace))
{
return true;
}
if (OfficeNamespaces.OOREPORT_NS.equals(namespace))
{
return true;
}
return false;
}
public void processContent(final DataFlags value)
throws DataSourceException, ReportProcessingException
{
final Object rawvalue = value.getValue();
if (rawvalue == null)
{
return;
}
// special handler for image (possibly also for URL ..)
if (rawvalue instanceof Image)
{
// do nothing yet. We should define something for that later ..
return;
}
final XmlWriter xmlWriter = getXmlWriter();
final String text = String.valueOf(rawvalue);
try
{
final BufferedReader br = new BufferedReader(new StringReader(text));
String line = br.readLine();
while (line != null)
{
xmlWriter.writeTextNormalized(line, false);
line = br.readLine();
if (line != null)
{
xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, "line-break", XmlWriterSupport.CLOSE);
}
}
}
catch (IOException e)
{
throw new ReportProcessingException(FAILED, e);
}
}
public final void endElement(final AttributeMap roAttrs)
throws DataSourceException, ReportProcessingException
{
final AttributeMap attrs = new LazyAttributeMap(roAttrs);
// final int oldState = getCurrentState();
try
{
switch (getCurrentState())
{
case OfficeDocumentReportTarget.STATE_IN_OTHER:
{
endOther(attrs);
break;
}
case OfficeDocumentReportTarget.STATE_IN_SECTION:
{
endReportSection(attrs, currentRole);
currentRole = OfficeDocumentReportTarget.ROLE_NONE;
break;
}
case OfficeDocumentReportTarget.STATE_IN_GROUP:
{
endGroup(attrs);
groupContext = groupContext.getParent();
break;
}
case OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE:
{
endGroupInstance(attrs);
break;
}
case OfficeDocumentReportTarget.STATE_IN_GROUP_BODY:
{
endGroupBody(attrs);
break;
}
case OfficeDocumentReportTarget.STATE_IN_CONTENT:
{
endContent(attrs);
break;
}
case OfficeDocumentReportTarget.STATE_IN_BODY:
{
endBody(attrs);
break;
}
case OfficeDocumentReportTarget.STATE_IN_DOCUMENT:
{
throw new IllegalStateException("This cannot be.");
}
default:
{
throw new IllegalStateException("Invalid state encountered.");
}
}
}
catch (IOException ioe)
{
throw new ReportProcessingException("IO Error while writing content",
ioe);
} finally
{
states.pop();
if (DEBUG_ELEMENTS)
{
LOGGER.debug("Finished " + getCurrentState() + "/" + states.size() + " " + ReportTargetUtil.getNamespaceFromAttribute(attrs) + ":" + ReportTargetUtil.getElemenTypeFromAttribute(attrs));
}
}
}
protected void endGroupBody(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
}
protected void endGroupInstance(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
}
public int getCurrentRole()
{
return currentRole;
}
protected abstract void endOther(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException;
protected void endReportSection(final AttributeMap attrs,
final int role)
throws IOException, DataSourceException, ReportProcessingException
{
if (allowBuffering(role))
{
finishBuffering();
}
}
protected void endGroup(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
}
protected abstract void endContent(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException;
protected void endBody(final AttributeMap attrs)
throws IOException, DataSourceException, ReportProcessingException
{
getXmlWriter().writeCloseTag();
}
public void copyMeta()
{
// now copy the meta.xml
if (getInputRepository().isReadable("meta.xml"))
{
InputStream inputStream = null;
try
{
inputStream = getInputRepository().createInputStream("meta.xml");
DOMParser dOMParser = new DOMParser();
dOMParser.parse(new InputSource(inputStream));
Document document = dOMParser.getDocument();
NodeList nl = document.getElementsByTagName("document-meta/meta/generator");
Node node = document.getFirstChild().getFirstChild().getFirstChild().getFirstChild();
String creator = node.getNodeValue();
node.setNodeValue(creator + "/report_builder");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
final OutputStream outputMetaStream = getOutputRepository().createOutputStream("meta.xml", "text/xml");
StreamResult result = new StreamResult(outputMetaStream);
DOMSource source = new DOMSource(document);
transformer.transform(source, result);
//IOUtils.getInstance().copyStreams(inputStream, outputMetaStream);
outputMetaStream.flush();
outputMetaStream.close();
}
catch (java.lang.Exception ex)
{
} finally
{
if (inputStream != null)
{
try
{
inputStream.close();
}
catch (IOException ex)
{
Logger.getLogger(OfficeDocumentReportTarget.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
public void endReport(final ReportStructureRoot report)
throws DataSourceException, ReportProcessingException
{
if (xmlWriters.size() != 1)
{
throw new IllegalStateException("Invalid writer-stack state");
}
try
{
final StylesWriter inlineStylesWriter = new StylesWriter(rootXmlWriter);
inlineStylesWriter.writeContentStyles(predefinedStylesCollection, contentStylesCollection);
final BufferState state = finishBuffering();
this.rootXmlWriter.writeStream(state.getXmlAsReader());
final OutputStream stylesOutStream =
outputRepository.createOutputStream("styles.xml", "text/xml");
final OutputStreamWriter osw =
new OutputStreamWriter(stylesOutStream, "UTF-8");
final StylesWriter stylesWriter = new StylesWriter(osw);
stylesWriter.writeGlobalStyles(predefinedStylesCollection, globalStylesCollection);
stylesWriter.close();
this.rootXmlWriter.writeCloseTag();
this.rootXmlWriter.close();
}
catch (IOException e)
{
throw new ReportProcessingException(FAILED, e);
}
}
public XmlWriter getXmlWriter()
{
final BufferState bufferState = (BufferState) xmlWriters.peek();
return bufferState.getXmlWriter();
}
public OfficeStylesCollection getStylesCollection()
{
final BufferState bufferState = (BufferState) xmlWriters.peek();
return bufferState.getStylesCollection();
}
public void startBuffering(final OfficeStylesCollection stylesCollection,
final boolean indent) throws ReportProcessingException
{
final XmlWriter currentWriter;
if (xmlWriters.isEmpty())
{
currentWriter = rootXmlWriter;
}
else
{
final BufferState bufferState = (BufferState) xmlWriters.peek();
currentWriter = bufferState.getXmlWriter();
}
try
{
final MemoryByteArrayOutputStream out =
new MemoryByteArrayOutputStream(INITIAL_BUFFER_SIZE, 256 * INITIAL_BUFFER_SIZE);
final DeflaterOutputStream deflateOut = new DeflaterOutputStream(out);
final OutputStreamWriter xmlBuffer = new OutputStreamWriter(deflateOut, "UTF-16");
// final StringWriter xmlBuffer = new StringWriter
// (OfficeDocumentReportTarget.INITIAL_BUFFER_SIZE);
final XmlWriter contentXmlWriter = new XmlWriter(xmlBuffer, createTagDescription());
contentXmlWriter.copyNamespaces(currentWriter);
if (indent)
{
contentXmlWriter.setAdditionalIndent(currentWriter.getCurrentIndentLevel());
contentXmlWriter.setWriteFinalLinebreak(true);
}
else
{
contentXmlWriter.setWriteFinalLinebreak(false);
}
contentXmlWriter.setAlwaysAddNamespace(true);
xmlWriters.push(new BufferState(contentXmlWriter, out, stylesCollection));
}
catch (IOException ioe)
{
throw new ReportProcessingException("Unable to create the buffer", ioe);
}
}
public BufferState finishBuffering() throws ReportProcessingException
{
final BufferState state = (BufferState) xmlWriters.pop();
try
{
state.getXmlWriter().close();
}
catch (IOException e)
{
LOGGER.error("ReportProcessing failed", e);
}
return state;
}
public void commit()
throws ReportProcessingException
{
// do not call flush before the report is fully finished. Every flush
// causes the Office-Backend to fully ZIP all contents (it acts like a
// 'Save' call from the UI) and that's expensive like hell
}
public NamespaceDefinition getNamespaceByUri(final String uri)
{
return null;
}
protected AttributeList buildAttributeList(final AttributeMap attrs)
{
final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
final AttributeList attrList = new AttributeList();
final String[] namespaces = attrs.getNameSpaces();
for (int i = 0; i < namespaces.length; i++)
{
final String attrNamespace = namespaces[i];
if (isFilteredNamespace(attrNamespace))
{
continue;
}
final Map localAttributes = attrs.getAttributes(attrNamespace);
final Iterator entries = localAttributes.entrySet().iterator();
while (entries.hasNext())
{
final Map.Entry entry = (Map.Entry) entries.next();
final String key = String.valueOf(entry.getKey());
if (OfficeNamespaces.TABLE_NS.equals(attrNamespace) && "name".equals(key))
{
final String tableName = String.valueOf(entry.getValue());
final String saneName = sanitizeName(tableName);
attrList.setAttribute(attrNamespace, key,
tableNameGenerator.generateName(saneName));
}
else if (OfficeNamespaces.DRAWING_NS.equals(attrNamespace) && "name".equals(key) && !"equation".equals(elementType))
{
final String objectName = String.valueOf(entry.getValue());
attrList.setAttribute(attrNamespace, key,
frameNameGenerator.generateName(objectName));
}
else
{
attrList.setAttribute(attrNamespace, key, String.valueOf(entry.getValue()));
}
}
}
return attrList;
}
protected String sanitizeName(final String name)
{
// A table name cannot contain spaces and should only contain
// ascii-characters.
if (name == null)
{
return "";
}
final char[] chars = name.toCharArray();
final StringBuffer buffer = new StringBuffer();
for (int i = 0; i < chars.length; i++)
{
final char aChar = chars[i];
if (Character.isWhitespace(aChar))
{
buffer.append('_');
}
else
{
buffer.append(aChar);
}
}
return buffer.toString();
}
/**
* Returns the length in point. This method is f**king slow, it eats half of the processing time. I surely should
* replace it with something more efficient later.
*
* @param text
* @return
*/
protected CSSNumericValue parseLength(final String text)
{
if (styleSheetParserUtil == null)
{
styleSheetParserUtil = StyleSheetParserUtil.getInstance();
}
final LexicalUnit cssValue = styleSheetParserUtil.parseLexicalStyleValue(
text);
return CSSValueFactory.createLengthValue(cssValue);
}
protected boolean isRepeatingSection()
{
return (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER || currentRole == OfficeDocumentReportTarget.ROLE_PAGE_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_VARIABLES);
}
protected OfficeStyle deriveStyle(final String styleFamily, final String styleName)
throws ReportProcessingException
{
// autogenerate a style. The style has already been added to the current
// auto-collection.
final OfficeStyle style = StyleUtilities.deriveStyle(styleFamily, styleName,
getStylesCollection(), getGlobalStylesCollection(),
getPredefinedStylesCollection(), getAutoStyleNameGenerator());
return style;
}
protected void startImageProcessing(final AttributeMap attrs)
throws ReportProcessingException
{
final Object imageData = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.IMAGE_DATA);
final boolean preserveIRI = OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.PRESERVE_IRI));
// for the first shot, do nothing fancy ..
final ImageProducer.OfficeImage image = imageProducer.produceImage(imageData, preserveIRI);
if (image != null)
{
final ImageElementContext imageContext = (ImageElementContext) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "image-context");
// When scaling, we have to create an image-style.
final CSSNumericValue width = image.getWidth(); // always in 100th of a mm
final CSSNumericValue height = image.getHeight(); // always in 100th of a mm
LOGGER.debug("Image " + imageData + " Width: " + width + ", Height: " + height);
if (width == null || height == null)
{
return;
}
CSSNumericValue imageAreaWidthVal;
CSSNumericValue imageAreaHeightVal;
CSSNumericValue posX = CSSNumericValue.createValue(CSSNumericType.CM, 0.0);
CSSNumericValue posY = CSSNumericValue.createValue(CSSNumericType.CM, 0.0);
String styleName = null;
if (imageContext != null)
{
imageAreaWidthVal = computeImageWidth(imageContext);
imageAreaHeightVal = computeImageHeight(imageContext);
if (imageAreaWidthVal == null || imageAreaHeightVal == null)
{
LOGGER.debug("Image data returned from context is invalid. Maybe this is not an image?");
return;
}
else
{
// compute the clip-area ..
final CSSNumericValue normalizedImageWidth =
CSSValueResolverUtility.convertLength(width, imageAreaWidthVal.getType());
final CSSNumericValue normalizedImageHeight =
CSSValueResolverUtility.convertLength(height, imageAreaHeightVal.getType());
final String scale = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.SCALE);
if (OfficeToken.NONE.equals(scale) && normalizedImageWidth.getValue() > 0 && normalizedImageHeight.getValue() > 0)
{
final double clipWidth = normalizedImageWidth.getValue() - imageAreaWidthVal.getValue();
final double clipHeight = normalizedImageHeight.getValue() - imageAreaHeightVal.getValue();
if (clipWidth > 0 && clipHeight > 0)
{
final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS);
final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
final StringBuffer buffer = new StringBuffer();
buffer.append("rect(");
buffer.append(clipHeight / 2);
buffer.append(imageAreaHeightVal.getType().getType());
buffer.append(' ');
buffer.append(clipWidth / 2);
buffer.append(imageAreaWidthVal.getType().getType());
buffer.append(' ');
buffer.append(clipHeight / 2);
buffer.append(imageAreaHeightVal.getType().getType());
buffer.append(' ');
buffer.append(clipWidth / 2);
buffer.append(imageAreaWidthVal.getType().getType());
buffer.append(')');
graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString());
styleName = imageStyle.getStyleName();
getStylesCollection().getAutomaticStyles().addStyle(imageStyle);
}
else if (clipWidth > 0)
{
final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS);
final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
final StringBuffer buffer = new StringBuffer();
buffer.append("rect(0cm ");
buffer.append(clipWidth / 2);
buffer.append(imageAreaWidthVal.getType().getType());
buffer.append(" 0cm ");
buffer.append(clipWidth / 2);
buffer.append(imageAreaWidthVal.getType().getType());
buffer.append(')');
graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString());
styleName = imageStyle.getStyleName();
getStylesCollection().getAutomaticStyles().addStyle(imageStyle);
imageAreaHeightVal = normalizedImageHeight;
}
else if (clipHeight > 0)
{
final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS);
final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
final StringBuffer buffer = new StringBuffer();
buffer.append("rect(");
buffer.append(clipHeight / 2);
buffer.append(imageAreaHeightVal.getType().getType());
buffer.append(" 0cm ");
buffer.append(clipHeight / 2);
buffer.append(imageAreaHeightVal.getType().getType());
buffer.append(" 0cm)");
graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString());
styleName = imageStyle.getStyleName();
getStylesCollection().getAutomaticStyles().addStyle(imageStyle);
imageAreaWidthVal = normalizedImageWidth;
}
else
{
imageAreaWidthVal = normalizedImageWidth;
imageAreaHeightVal = normalizedImageHeight;
}
}
else if (OfficeToken.ISOTROPIC.equals(scale))
{
final double[] ret = calcPaintSize(imageAreaWidthVal, imageAreaHeightVal, normalizedImageWidth, normalizedImageHeight);
posX = CSSNumericValue.createValue(imageAreaWidthVal.getType(), (imageAreaWidthVal.getValue() - ret[0]) * 0.5);
posY = CSSNumericValue.createValue(imageAreaHeightVal.getType(), (imageAreaHeightVal.getValue() - ret[1]) * 0.5);
imageAreaWidthVal = CSSNumericValue.createValue(imageAreaWidthVal.getType(), ret[0]);
imageAreaHeightVal = CSSNumericValue.createValue(imageAreaHeightVal.getType(), ret[1]);
}
}
// If we do scale, then we simply use the given image-area-size as valid image size and dont
// care about the image itself ..
}
else
{
LOGGER.debug("There is no image-context, so we have to rely on the image's natural bounds. " + "This may go awfully wrong.");
imageAreaWidthVal = image.getWidth();
imageAreaHeightVal = image.getHeight();
}
final AttributeList frameList = new AttributeList();
frameList.setAttribute(OfficeNamespaces.DRAWING_NS, "name", imageNames.generateName("Image"));
if (styleName != null)
{
frameList.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, styleName);
}
frameList.setAttribute(OfficeNamespaces.TEXT_NS, "anchor-type", OfficeToken.PARAGRAPH);
frameList.setAttribute(OfficeNamespaces.SVG_NS, "z-index", "0");
frameList.setAttribute(OfficeNamespaces.SVG_NS, "x", posX.getValue() + posX.getType().getType());
frameList.setAttribute(OfficeNamespaces.SVG_NS, "y", posY.getValue() + posY.getType().getType());
LOGGER.debug("Image " + imageData + " A-Width: " + imageAreaWidthVal + ", A-Height: " + imageAreaHeightVal);
if (imageAreaWidthVal != null)
{
frameList.setAttribute(OfficeNamespaces.SVG_NS,
"width", imageAreaWidthVal.getValue() + imageAreaWidthVal.getType().getType());
}
if (imageAreaHeightVal != null)
{
frameList.setAttribute(OfficeNamespaces.SVG_NS,
"height", imageAreaHeightVal.getValue() + imageAreaHeightVal.getType().getType());
}
final AttributeList imageList = new AttributeList();
imageList.setAttribute(OfficeNamespaces.XLINK_NS, "href", image.getEmbeddableLink());
imageList.setAttribute(OfficeNamespaces.XLINK_NS, "type", "simple");
imageList.setAttribute(OfficeNamespaces.XLINK_NS, "show", "embed");
imageList.setAttribute(OfficeNamespaces.XLINK_NS, "actuate", "onLoad");
try
{
getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, "frame", frameList, XmlWriterSupport.OPEN);
getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.IMAGE, imageList, XmlWriterSupport.CLOSE);
getXmlWriter().writeCloseTag();
}
catch (IOException ioe)
{
throw new ReportProcessingException(FAILED, ioe);
}
}
}
private CSSNumericValue computeImageWidth(final ImageElementContext imageElementContext)
{
final LengthCalculator calculator = new LengthCalculator();
final String[] strings = imageElementContext.getColStyles();
for (int i = 0; i < strings.length; i++)
{
final String styleName = strings[i];
final CSSNumericValue value = computeColumnWidth(styleName);
if (value != null)
{
calculator.add(value);
}
}
return calculator.getResult();
}
private CSSNumericValue computeImageHeight(final ImageElementContext imageElementContext)
{
final LengthCalculator calculator = new LengthCalculator();
final String[] strings = imageElementContext.getRowStyles();
for (int i = 0; i < strings.length; i++)
{
final String styleName = strings[i];
final CSSNumericValue value = computeRowHeight(styleName);
if (value != null)
{
calculator.add(value);
}
}
return calculator.getResult();
}
protected CSSNumericValue computeRowHeight(final String rowStyle)
{
final OfficeStylesCollection contentStyles = getContentStylesCollection();
final OfficeStyle style = contentStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle);
if (style != null)
{
final Element element = style.getTableRowProperties();
if (element != null)
{
final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height");
if (height != null)
{
return parseLength(height);
}
}
final String styleParent = style.getStyleParent();
if (styleParent != null)
{
return computeRowHeight(styleParent);
}
}
final OfficeStylesCollection globalStyles = getGlobalStylesCollection();
final OfficeStyle globalStyle = globalStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle);
if (globalStyle != null)
{
final Element element = globalStyle.getTableRowProperties();
if (element != null)
{
final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height");
if (height != null)
{
return parseLength(height);
}
}
final String styleParent = globalStyle.getStyleParent();
if (styleParent != null)
{
return computeRowHeight(styleParent);
}
}
final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
final OfficeStyle predefStyle = predefStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle);
if (predefStyle != null)
{
final Element element = predefStyle.getTableRowProperties();
if (element != null)
{
final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height");
if (height != null)
{
return parseLength(height);
}
}
final String styleParent = predefStyle.getStyleParent();
if (styleParent != null)
{
return computeRowHeight(styleParent);
}
}
// not found.
return null;
}
protected CSSNumericValue computeColumnWidth(final String colStyle)
{
final OfficeStylesCollection contentStyles = getContentStylesCollection();
final OfficeStyle style = contentStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle);
if (style != null)
{
final Element element = style.getTableColumnProperties();
if (element != null)
{
final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width");
if (height != null)
{
return parseLength(height);
}
}
final String styleParent = style.getStyleParent();
if (styleParent != null)
{
return computeRowHeight(styleParent);
}
}
final OfficeStylesCollection globalStyles = getGlobalStylesCollection();
final OfficeStyle globalStyle = globalStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle);
if (globalStyle != null)
{
final Element element = globalStyle.getTableColumnProperties();
if (element != null)
{
final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width");
if (height != null)
{
return parseLength(height);
}
}
final String styleParent = globalStyle.getStyleParent();
if (styleParent != null)
{
return computeRowHeight(styleParent);
}
}
final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
final OfficeStyle predefStyle = predefStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle);
if (predefStyle != null)
{
final Element element = predefStyle.getTableColumnProperties();
if (element != null)
{
final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width");
if (height != null)
{
return parseLength(height);
}
}
final String styleParent = predefStyle.getStyleParent();
if (styleParent != null)
{
return computeRowHeight(styleParent);
}
}
// not found.
return null;
}
protected Element produceFirstChild(final Section style,
final String nameSpace,
final String type)
{
Element paragraphProps = style.findFirstChild(nameSpace, type);
if (paragraphProps == null)
{
paragraphProps = new Section();
paragraphProps.setNamespace(nameSpace);
paragraphProps.setType(type);
style.addNode(paragraphProps);
}
return paragraphProps;
}
protected void startChartProcessing(final AttributeMap attrs)
throws ReportProcessingException
{
final String classId = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "class-id");
final String chartUrl = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "href");
final ArrayList masterColumns = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.MASTER_COLUMNS);
final ArrayList masterValues = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.MASTER_VALUES);
final ArrayList detailColumns = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.DETAIL_COLUMNS);
final String href = oleProducer.produceOle(chartUrl, masterColumns, masterValues, detailColumns);
final AttributeList oleList = new AttributeList();
oleList.setAttribute(OfficeNamespaces.DRAWING_NS, "class-id", classId);
oleList.setAttribute(OfficeNamespaces.XLINK_NS, "href", "./" + href);
oleList.setAttribute(OfficeNamespaces.XLINK_NS, "type", "simple");
oleList.setAttribute(OfficeNamespaces.XLINK_NS, "show", "embed");
oleList.setAttribute(OfficeNamespaces.XLINK_NS, "actuate", "onLoad");
try
{
getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.OBJECT_OLE, oleList, XmlWriterSupport.CLOSE);
}
catch (IOException ioe)
{
throw new ReportProcessingException(FAILED, ioe);
}
}
static private double[] calcPaintSize(final CSSNumericValue areaWidth, final CSSNumericValue areaHeight,
final CSSNumericValue imageWidth, final CSSNumericValue imageHeight)
{
final double ratioX = areaWidth.getValue() / imageWidth.getValue();
final double ratioY = areaHeight.getValue() / imageHeight.getValue();
final double ratioMin = Math.min(ratioX, ratioY);
double[] ret = new double[2];
ret[0] = imageWidth.getValue() * ratioMin;
ret[1] = imageHeight.getValue() * ratioMin;
return ret;
}
protected void writeNullDate() throws IOException
{
// write NULL DATE
final XmlWriter xmlWriter = getXmlWriter();
xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, "calculation-settings", null, XmlWriterSupport.OPEN);
final AttributeMap nullDateAttributes = new AttributeMap();
nullDateAttributes.setAttribute(OfficeNamespaces.TABLE_NS, "date-value", "1900-01-01");
xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, "null-date", buildAttributeList(nullDateAttributes), XmlWriterSupport.CLOSE);
xmlWriter.writeCloseTag();
}
}