diff options
Diffstat (limited to 'reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java')
-rw-r--r-- | reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java | 1730 |
1 files changed, 1730 insertions, 0 deletions
diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java b/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java new file mode 100644 index 000000000000..cdc8eae1b73b --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java @@ -0,0 +1,1730 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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(); + } +} |