summaryrefslogtreecommitdiff
path: root/reportbuilder/java/org/libreoffice/report/pentaho/output
diff options
context:
space:
mode:
Diffstat (limited to 'reportbuilder/java/org/libreoffice/report/pentaho/output')
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/ImageProducer.java488
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/OfficeDocumentReportTarget.java1722
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/OleProducer.java126
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/StyleUtilities.java605
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/StylesWriter.java380
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportProcessor.java96
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportTarget.java247
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportProcessor.java109
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java959
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/text/MasterPageFactory.java407
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageBreakDefinition.java39
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageContext.java218
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportProcessor.java106
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportTarget.java1444
-rw-r--r--reportbuilder/java/org/libreoffice/report/pentaho/output/text/VariablesDeclarations.java98
15 files changed, 7044 insertions, 0 deletions
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/ImageProducer.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/ImageProducer.java
new file mode 100644
index 000000000000..c4d545c49d85
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/ImageProducer.java
@@ -0,0 +1,488 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output;
+
+import com.sun.star.awt.Size;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.ReportExecutionException;
+import org.libreoffice.report.pentaho.DefaultNameGenerator;
+
+import java.awt.Image;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import java.sql.Blob;
+import java.sql.SQLException;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.jfree.layouting.input.style.values.CSSNumericType;
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+
+import org.pentaho.reporting.libraries.base.util.IOUtils;
+import org.pentaho.reporting.libraries.base.util.PngEncoder;
+import org.pentaho.reporting.libraries.base.util.WaitingImageObserver;
+
+
+/**
+ * This class manages the images embedded in a report.
+ *
+ * @since 31.03.2007
+ */
+public class ImageProducer
+{
+
+ private static final Log LOGGER = LogFactory.getLog(ImageProducer.class);
+
+ public static class OfficeImage
+ {
+
+ private final CSSNumericValue width;
+ private final CSSNumericValue height;
+ private final String embeddableLink;
+
+ public OfficeImage(final String embeddableLink, final CSSNumericValue width, final CSSNumericValue height)
+ {
+ this.embeddableLink = embeddableLink;
+ this.width = width;
+ this.height = height;
+ }
+
+ public CSSNumericValue getWidth()
+ {
+ return width;
+ }
+
+ public CSSNumericValue getHeight()
+ {
+ return height;
+ }
+
+ public String getEmbeddableLink()
+ {
+ return embeddableLink;
+ }
+ }
+
+ private static class ByteDataImageKey
+ {
+
+ private final byte[] keyData;
+ private Integer hashCode;
+
+ protected ByteDataImageKey(final byte[] keyData)
+ {
+ if (keyData == null)
+ {
+ throw new NullPointerException();
+ }
+ this.keyData = keyData;
+ }
+
+ public boolean equals(final Object o)
+ {
+ if (this != o)
+ {
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ final ByteDataImageKey key = (ByteDataImageKey) o;
+ if (!Arrays.equals(keyData, key.keyData))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ if (hashCode != null)
+ {
+ return hashCode;
+ }
+
+ final int length = Math.min(keyData.length, 512);
+ int hashValue = 0;
+ for (int i = 0; i < length; i++)
+ {
+ final byte b = keyData[i];
+ hashValue = b + hashValue * 23;
+ }
+ this.hashCode = hashValue;
+ return hashValue;
+ }
+ }
+ private final Map<Object,OfficeImage> imageCache;
+ private final InputRepository inputRepository;
+ private final OutputRepository outputRepository;
+ private final ImageService imageService;
+
+ public ImageProducer(final InputRepository inputRepository,
+ final OutputRepository outputRepository,
+ final ImageService imageService)
+ {
+ if (inputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (outputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (imageService == null)
+ {
+ throw new NullPointerException();
+ }
+
+ this.inputRepository = inputRepository;
+ this.outputRepository = outputRepository;
+ this.imageService = imageService;
+ this.imageCache = new HashMap<Object,OfficeImage>();
+ }
+
+ /**
+ * Image-Data can be one of the following types: String, URL, URI, byte-array, blob.
+ *
+ * @param imageData
+ * @param preserveIRI
+ * @return
+ */
+ public OfficeImage produceImage(final Object imageData,
+ final boolean preserveIRI)
+ {
+
+ LOGGER.debug("Want to produce image " + imageData);
+ if (imageData instanceof String)
+ {
+ return produceFromString((String) imageData, preserveIRI);
+ }
+
+ if (imageData instanceof URL)
+ {
+ return produceFromURL((URL) imageData, preserveIRI);
+ }
+
+ if (imageData instanceof Blob)
+ {
+ return produceFromBlob((Blob) imageData);
+ }
+
+ if (imageData instanceof byte[])
+ {
+ return produceFromByteArray((byte[]) imageData);
+ }
+
+ if (imageData instanceof Image)
+ {
+ return produceFromImage((Image) imageData);
+ }
+ // not usable ..
+ return null;
+ }
+
+ private OfficeImage produceFromImage(final Image image)
+ {
+ // quick caching ... use a weak list ...
+ final WaitingImageObserver obs = new WaitingImageObserver(image);
+ obs.waitImageLoaded();
+
+ final PngEncoder encoder = new PngEncoder(image, PngEncoder.ENCODE_ALPHA, PngEncoder.FILTER_NONE, 5);
+ final byte[] data = encoder.pngEncode();
+ return produceFromByteArray(data);
+ }
+
+ private OfficeImage produceFromBlob(final Blob blob)
+ {
+ try
+ {
+ final InputStream inputStream = blob.getBinaryStream();
+ final int length = (int) blob.length();
+
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream(length);
+ try
+ {
+ IOUtils.getInstance().copyStreams(inputStream, bout);
+ } finally
+ {
+ inputStream.close();
+ }
+ return produceFromByteArray(bout.toByteArray());
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Failed to produce image from Blob", e);
+ }
+ catch (SQLException e)
+ {
+ LOGGER.warn("Failed to produce image from Blob", e);
+ }
+ return null;
+ }
+
+ private OfficeImage produceFromByteArray(final byte[] data)
+ {
+ final ByteDataImageKey imageKey = new ByteDataImageKey(data);
+ final OfficeImage o = imageCache.get(imageKey);
+ if (o != null)
+ {
+ return o;
+ }
+
+ try
+ {
+ final String mimeType = imageService.getMimeType(data);
+ final Size dims = imageService.getImageSize(data);
+
+ // copy the image into the local output-storage
+ // todo: Implement data-fingerprinting so that we can detect the mime-type
+ final OutputRepository storage = outputRepository.openOutputRepository("Pictures", null);
+ final DefaultNameGenerator nameGenerator = new DefaultNameGenerator(storage);
+ final String name = nameGenerator.generateName("image", mimeType);
+ final OutputStream outputStream = storage.createOutputStream(name, mimeType);
+ final ByteArrayInputStream bin = new ByteArrayInputStream(data);
+
+ try
+ {
+ IOUtils.getInstance().copyStreams(bin, outputStream);
+ } finally
+ {
+ outputStream.close();
+ storage.closeOutputRepository();
+ }
+
+ final CSSNumericValue widthVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.Width / 100.0);
+ final CSSNumericValue heightVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.Height / 100.0);
+ final OfficeImage officeImage = new OfficeImage("Pictures/" + name, widthVal, heightVal);
+ imageCache.put(imageKey, officeImage);
+ return officeImage;
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Failed to load image from local input-repository", e);
+ }
+ catch (ReportExecutionException e)
+ {
+ LOGGER.warn("Failed to create image from local input-repository", e);
+ }
+ return null;
+ }
+
+ private OfficeImage produceFromString(final String source,
+ final boolean preserveIRI)
+ {
+
+ try
+ {
+ final URL url = new URL(source);
+ return produceFromURL(url, preserveIRI);
+ }
+ catch (MalformedURLException e)
+ {
+ // ignore .. but we had to try this ..
+ }
+
+ final OfficeImage o = imageCache.get(source);
+ if (o != null)
+ {
+ return o;
+ }
+
+ // Next, check whether this is a local path.
+ if (inputRepository.isReadable(source))
+ {
+ // cool, the file exists. Let's try to read it.
+ try
+ {
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
+ final InputStream inputStream = inputRepository.createInputStream(source);
+ try
+ {
+ IOUtils.getInstance().copyStreams(inputStream, bout);
+ } finally
+ {
+ inputStream.close();
+ }
+ final byte[] data = bout.toByteArray();
+ final Size dims = imageService.getImageSize(data);
+ final String mimeType = imageService.getMimeType(data);
+
+ final CSSNumericValue widthVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.Width / 100.0);
+ final CSSNumericValue heightVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.Height / 100.0);
+
+ final String filename = copyToOutputRepository(mimeType, data);
+ final OfficeImage officeImage = new OfficeImage(filename, widthVal, heightVal);
+ imageCache.put(source, officeImage);
+ return officeImage;
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Failed to load image from local input-repository", e);
+ }
+ catch (ReportExecutionException e)
+ {
+ LOGGER.warn("Failed to create image from local input-repository", e);
+ }
+ }
+ else
+ {
+ try
+ {
+ URI rootURI = new URI(inputRepository.getRootURL());
+ final URI uri = rootURI.resolve(source);
+ return produceFromURL(uri.toURL(), preserveIRI);
+ }
+ catch (URISyntaxException ex)
+ {
+ }
+ catch (MalformedURLException e)
+ {
+ // ignore .. but we had to try this ..
+ }
+ }
+
+ // Return the image as broken image instead ..
+ final OfficeImage officeImage = new OfficeImage(source, null, null);
+ imageCache.put(source, officeImage);
+ return officeImage;
+ }
+
+ private OfficeImage produceFromURL(final URL url,
+ final boolean preserveIRI)
+ {
+ final String urlString = url.toString();
+ URI uri = null;
+ try
+ {
+ uri = new URI(urlString);
+ }
+ catch (URISyntaxException ex)
+ {
+ Logger.getLogger(ImageProducer.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ final OfficeImage o = imageCache.get(uri);
+ if (o != null)
+ {
+ return o;
+ }
+
+ try
+ {
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
+ final URLConnection urlConnection = url.openConnection();
+ final InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream());
+ try
+ {
+ IOUtils.getInstance().copyStreams(inputStream, bout);
+ } finally
+ {
+ inputStream.close();
+ }
+ final byte[] data = bout.toByteArray();
+
+ final Size dims = imageService.getImageSize(data);
+ final String mimeType = imageService.getMimeType(data);
+ final CSSNumericValue widthVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.Width / 100.0);
+ final CSSNumericValue heightVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.Height / 100.0);
+
+ if (preserveIRI)
+ {
+ final OfficeImage retval = new OfficeImage(urlString, widthVal, heightVal);
+ imageCache.put(uri, retval);
+ return retval;
+ }
+
+ final String name = copyToOutputRepository(mimeType, data);
+ final OfficeImage officeImage = new OfficeImage(name, widthVal, heightVal);
+ imageCache.put(uri, officeImage);
+ return officeImage;
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Failed to load image from local input-repository" + e);
+ }
+ catch (ReportExecutionException e)
+ {
+ LOGGER.warn("Failed to create image from local input-repository" + e);
+ }
+
+ if (!preserveIRI)
+ {
+ final OfficeImage image = new OfficeImage(urlString, null, null);
+ imageCache.put(uri, image);
+ return image;
+ }
+
+ // OK, everything failed; the image is not - repeat it - not usable.
+ return null;
+ }
+
+ private String copyToOutputRepository(final String urlMimeType, final byte[] data)
+ throws IOException, ReportExecutionException
+ {
+ final String mimeType;
+ if (urlMimeType == null)
+ {
+ mimeType = imageService.getMimeType(data);
+ }
+ else
+ {
+ mimeType = urlMimeType;
+ }
+
+ // copy the image into the local output-storage
+ final OutputRepository storage = outputRepository.openOutputRepository("Pictures", null);
+ final DefaultNameGenerator nameGenerator = new DefaultNameGenerator(storage);
+ final String name = nameGenerator.generateName("image", mimeType);
+ final OutputStream outputStream = storage.createOutputStream(name, mimeType);
+ final ByteArrayInputStream bin = new ByteArrayInputStream(data);
+
+ try
+ {
+ IOUtils.getInstance().copyStreams(bin, outputStream);
+ } finally
+ {
+ outputStream.close();
+ storage.closeOutputRepository();
+ }
+ return "Pictures/" + name;
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/OfficeDocumentReportTarget.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/OfficeDocumentReportTarget.java
new file mode 100644
index 000000000000..8d34a5d6cfb2
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/OfficeDocumentReportTarget.java
@@ -0,0 +1,1722 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OfficeToken;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.ReportEngineParameterNames;
+import org.libreoffice.report.SDBCReportDataFactory;
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.layoutprocessor.ImageElementContext;
+import org.libreoffice.report.pentaho.model.OfficeDocument;
+import org.libreoffice.report.pentaho.model.OfficeStyle;
+import org.libreoffice.report.pentaho.model.OfficeStyles;
+import org.libreoffice.report.pentaho.model.OfficeStylesCollection;
+import org.libreoffice.report.pentaho.styles.LengthCalculator;
+import org.libreoffice.report.pentaho.styles.StyleMapper;
+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 javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+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!
+ *
+ * @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 = "org.libreoffice.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("org.libreoffice.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");
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ Document document = dBuilder.parse(new InputSource(inputStream));
+
+ 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();
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/OleProducer.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/OleProducer.java
new file mode 100644
index 000000000000..2010a189be1d
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/OleProducer.java
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.JobProperties;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.ReportEngineParameterNames;
+import org.libreoffice.report.ReportExecutionException;
+import org.libreoffice.report.ReportJobDefinition;
+import org.libreoffice.report.pentaho.DefaultNameGenerator;
+import org.libreoffice.report.pentaho.PentahoReportEngine;
+import org.libreoffice.report.pentaho.PentahoReportEngineMetaData;
+
+import java.io.IOException;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class OleProducer
+{
+
+ private static final Log LOGGER = LogFactory.getLog(OleProducer.class);
+ private final InputRepository inputRepository;
+ private final OutputRepository outputRepository;
+ private final DefaultNameGenerator nameGenerator;
+ private final DataSourceFactory dataSourceFactory;
+ private final ImageService imageService;
+ private final Integer maxRows;
+
+ public OleProducer(final InputRepository inputRepository,
+ final OutputRepository outputRepository, final ImageService imageService, final DataSourceFactory dataSourceFactory, final Integer maxRows)
+ {
+ if (inputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (outputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+
+ this.inputRepository = inputRepository;
+ this.outputRepository = outputRepository;
+ this.nameGenerator = new DefaultNameGenerator(outputRepository);
+ this.dataSourceFactory = dataSourceFactory;
+ this.imageService = imageService;
+ this.maxRows = maxRows;
+ }
+
+ String produceOle(final String source, final List masterColumns, final List masterValues, final List detailColumns)
+ {
+ InputRepository subInputRepository = null;
+ OutputRepository subOutputRepository = null;
+ String output = "";
+ try
+ {
+ subInputRepository = inputRepository.openInputRepository(source);
+ output = nameGenerator.generateStorageName("Object", null);
+ subOutputRepository = outputRepository.openOutputRepository(output, PentahoReportEngineMetaData.OPENDOCUMENT_CHART);
+ try
+ {
+
+ final PentahoReportEngine engine = new PentahoReportEngine();
+ final ReportJobDefinition definition = engine.createJobDefinition();
+ final JobProperties procParms = definition.getProcessingParameters();
+
+ procParms.setProperty(ReportEngineParameterNames.INPUT_REPOSITORY, subInputRepository);
+ procParms.setProperty(ReportEngineParameterNames.OUTPUT_REPOSITORY, subOutputRepository);
+ procParms.setProperty(ReportEngineParameterNames.INPUT_NAME, "content.xml");
+ procParms.setProperty(ReportEngineParameterNames.OUTPUT_NAME, "content.xml");
+ procParms.setProperty(ReportEngineParameterNames.CONTENT_TYPE, PentahoReportEngineMetaData.OPENDOCUMENT_CHART);
+ procParms.setProperty(ReportEngineParameterNames.INPUT_DATASOURCE_FACTORY, dataSourceFactory);
+ procParms.setProperty(ReportEngineParameterNames.INPUT_MASTER_COLUMNS, masterColumns);
+ procParms.setProperty(ReportEngineParameterNames.INPUT_MASTER_VALUES, masterValues);
+ procParms.setProperty(ReportEngineParameterNames.INPUT_DETAIL_COLUMNS, detailColumns);
+ procParms.setProperty(ReportEngineParameterNames.IMAGE_SERVICE, imageService);
+ procParms.setProperty(ReportEngineParameterNames.MAXROWS, maxRows);
+
+ engine.createJob(definition).execute();
+ }
+ catch (ReportExecutionException ex)
+ {
+ LOGGER.error("ReportProcessing failed", ex);
+ }
+ catch (IOException ex)
+ {
+ LOGGER.error("ReportProcessing failed", ex);
+ }
+ }
+ catch (IOException ex)
+ {
+ LOGGER.error("ReportProcessing failed", ex);
+ } finally
+ {
+ if (subInputRepository != null)
+ {
+ subInputRepository.closeInputRepository();
+ }
+ if (subOutputRepository != null)
+ {
+ subOutputRepository.closeOutputRepository();
+ }
+ }
+ return output;
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/StyleUtilities.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/StyleUtilities.java
new file mode 100644
index 000000000000..da568078afb5
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/StyleUtilities.java
@@ -0,0 +1,605 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output;
+
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.model.DataStyle;
+import org.libreoffice.report.pentaho.model.FontFaceDeclsSection;
+import org.libreoffice.report.pentaho.model.FontFaceElement;
+import org.libreoffice.report.pentaho.model.OfficeStyle;
+import org.libreoffice.report.pentaho.model.OfficeStyles;
+import org.libreoffice.report.pentaho.model.OfficeStylesCollection;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.structure.Element;
+import org.jfree.report.structure.Section;
+import org.jfree.report.util.AttributeNameGenerator;
+
+
+/**
+ * Todo: Document me!
+ *
+ * @since 13.03.2007
+ */
+public class StyleUtilities
+{
+
+ private static final Log LOGGER = LogFactory.getLog(StyleUtilities.class);
+ private static final String STYLE = "style";
+
+ private StyleUtilities()
+ {
+ }
+
+ /**
+ * Copies the specififed style (keyed by its family and name) into the current styles collection. This copies the
+ * style and all inherited styles into the target collection. Inherited common styles will be always be added to the
+ * common collection (which will be written into the 'styles.xml' later).
+ * <p/>
+ * This method does nothing if the specified style already exists in the styles collection.
+ *
+ * @param styleFamily the family of the style to copy
+ * @param styleName the unique name of the style.
+ * @param stylesCollection the current styles collection
+ * @param commonCollection the global styles collection
+ * @param predefCollection the predefined styles from where to copy the styles.
+ * @throws ReportProcessingException if the style copying failed.
+ */
+ public static void copyStyle(final String styleFamily,
+ final String styleName,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection commonCollection,
+ final OfficeStylesCollection predefCollection)
+ throws ReportProcessingException
+ {
+ copyStyle(styleFamily, styleName, stylesCollection,
+ commonCollection, predefCollection, new HashSet<String>());
+ }
+
+ /**
+ * Copies the specififed style (keyed by its family and name) into the current styles collection. This copies the
+ * style and all inherited styles into the target collection. Inherited common styles will be always be added to the
+ * common collection (which will be written into the 'styles.xml' later).
+ * <p/>
+ * This method does nothing if the specified style already exists in the styles collection.
+ *
+ * @param styleFamily the family of the style to copy
+ * @param styleName the unique name of the style.
+ * @param stylesCollection the current styles collection
+ * @param commonCollection the global styles collection
+ * @param predefCollection the predefined styles from where to copy the styles.
+ * @param inheritanceTracker a collection of all styles that have been touched. This is used to prevent infinite
+ * loops and duplicates.
+ * @throws ReportProcessingException if the style copying failed.
+ */
+ private static void copyStyle(final String styleFamily,
+ final String styleName,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection commonCollection,
+ final OfficeStylesCollection predefCollection,
+ final Set<String> inheritanceTracker)
+ throws ReportProcessingException
+ {
+ if (inheritanceTracker.contains(styleName))
+ {
+ return;
+ }
+ inheritanceTracker.add(styleName);
+
+ if (stylesCollection.containsStyle(styleFamily, styleName) || commonCollection.getCommonStyles().containsStyle(styleFamily, styleName))
+ {
+ // fine, there's already a copy of the stylesheet.
+ return;
+ }
+
+ final OfficeStyle predefCommonStyle =
+ predefCollection.getCommonStyles().getStyle(styleFamily, styleName);
+ if (predefCommonStyle != null)
+ {
+ // so we have an style from the predefined collection.
+ // copy it an add it to the current stylescollection
+ final OfficeStyles commonStyles = commonCollection.getCommonStyles();
+
+ copyStyleInternal(predefCommonStyle, commonStyles, stylesCollection,
+ commonCollection, predefCollection, styleFamily, inheritanceTracker);
+ return;
+ }
+
+ final OfficeStyle predefAutoStyle =
+ predefCollection.getAutomaticStyles().getStyle(styleFamily, styleName);
+ if (predefAutoStyle != null)
+ {
+ // so we have an style from the predefined collection.
+ // copy it an add it to the current stylescollection
+ final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
+ copyStyleInternal(predefAutoStyle, autoStyles, stylesCollection,
+ commonCollection, predefCollection, styleFamily, inheritanceTracker);
+ return;
+ }
+
+ // There is no automatic style either. Now this means that someone
+ // messed up the fileformat. Lets create a new empty style for this.
+ final OfficeStyle autostyle = new OfficeStyle();
+ autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ autostyle.setType(STYLE);
+ autostyle.setStyleFamily(styleFamily);
+ autostyle.setStyleName(styleName);
+
+ final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
+ autoStyles.addStyle(autostyle);
+ }
+
+ private static OfficeStyle copyStyleInternal(
+ final OfficeStyle predefCommonStyle,
+ final OfficeStyles styles,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection commonCollection,
+ final OfficeStylesCollection predefCollection,
+ final String styleFamily,
+ final Set<String> inheritanceTracker)
+ throws ReportProcessingException
+ {
+ try
+ {
+ final OfficeStyle preStyle = (OfficeStyle) predefCommonStyle.clone();
+ styles.addStyle(preStyle);
+ performFontFaceProcessing(preStyle, stylesCollection, predefCollection);
+ performDataStyleProcessing(preStyle, stylesCollection, predefCollection);
+
+ // Lookup the parent style ..
+ final String styleParent = preStyle.getStyleParent();
+ final OfficeStyle inherited =
+ stylesCollection.getStyle(styleFamily, styleParent);
+ if (inherited != null)
+ {
+ // OK, recurse (and hope that we dont run into an infinite loop) ..
+ copyStyle(styleFamily, styleParent, stylesCollection,
+ commonCollection, predefCollection, inheritanceTracker);
+ }
+ else if (styleParent != null)
+ {
+ LOGGER.warn("Inconsistent styles: " + styleFamily + ":" + styleParent + " does not exist.");
+ }
+ return preStyle;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new ReportProcessingException("Failed to derive a stylesheet", e);
+ }
+ }
+
+ private static void performFontFaceProcessing(final OfficeStyle style,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection predefCollection)
+ throws ReportProcessingException
+ {
+ final Element textProperties = style.getTextProperties();
+ if (textProperties == null)
+ {
+ return;
+ }
+
+ try
+ {
+ final FontFaceDeclsSection currentFonts = stylesCollection.getFontFaceDecls();
+ final FontFaceDeclsSection predefFonts = predefCollection.getFontFaceDecls();
+
+ final String fontName = (String) textProperties.getAttribute(OfficeNamespaces.STYLE_NS, "font-name");
+ if (fontName != null && !currentFonts.containsFont(fontName))
+ {
+ final FontFaceElement element = predefFonts.getFontFace(fontName);
+ if (element != null)
+ {
+ currentFonts.addFontFace((FontFaceElement) element.clone());
+ }
+ }
+
+ final String fontNameAsian = (String) textProperties.getAttribute(OfficeNamespaces.STYLE_NS,
+ "font-name-asian");
+ if (fontNameAsian != null && !currentFonts.containsFont(fontNameAsian))
+ {
+ final FontFaceElement element = predefFonts.getFontFace(
+ fontNameAsian);
+ if (element != null)
+ {
+ currentFonts.addFontFace((FontFaceElement) element.clone());
+ }
+ }
+
+ final String fontNameComplex = (String) textProperties.getAttribute(OfficeNamespaces.STYLE_NS,
+ "font-name-complex");
+ if (fontNameComplex != null && !currentFonts.containsFont(fontNameComplex))
+ {
+ final FontFaceElement element = predefFonts.getFontFace(
+ fontNameComplex);
+ if (element != null)
+ {
+ currentFonts.addFontFace((FontFaceElement) element.clone());
+ }
+ }
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new ReportProcessingException("Failed to clone font-face element");
+ }
+ }
+
+ private static void performDataStyleProcessing(final OfficeStyle style,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection predefCollection)
+ throws ReportProcessingException
+ {
+ final Section derivedStyle = performDataStyleProcessing(style, stylesCollection, predefCollection, "data-style-name");
+ if (derivedStyle != null)
+ {
+ try
+ {
+ final Section styleMap = (Section) derivedStyle.findFirstChild(OfficeNamespaces.STYLE_NS, "map");
+ if (styleMap != null)
+ {
+ performDataStyleProcessing(styleMap, stylesCollection, predefCollection, "apply-style-name");
+ }
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+
+ private static Section performDataStyleProcessing(final Section style,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection predefCollection,
+ final String attributeName)
+ throws ReportProcessingException
+ {
+ final Object attribute = style.getAttribute(OfficeNamespaces.STYLE_NS, attributeName);
+ final DataStyle derivedStyle;
+ if (attribute != null)
+ {
+ final String styleName = String.valueOf(attribute);
+ if (!stylesCollection.getAutomaticStyles().containsDataStyle(styleName) && !stylesCollection.getCommonStyles().containsDataStyle(styleName))
+ {
+ try
+ {
+ final OfficeStyles automaticStyles = predefCollection.getAutomaticStyles();
+ final DataStyle autoDataStyle = automaticStyles.getDataStyle(styleName);
+ if (autoDataStyle != null)
+ {
+ derivedStyle = (DataStyle) autoDataStyle.clone();
+ stylesCollection.getAutomaticStyles().addDataStyle(derivedStyle);
+ }
+ else
+ {
+ final OfficeStyles commonStyles = predefCollection.getCommonStyles();
+ final DataStyle commonDataStyle = commonStyles.getDataStyle(styleName);
+ if (commonDataStyle != null)
+ {
+ derivedStyle = (DataStyle) commonDataStyle.clone();
+ stylesCollection.getCommonStyles().addDataStyle(derivedStyle);
+ }
+ else
+ {
+ LOGGER.warn("Dangling data style: " + styleName);
+ derivedStyle = null;
+ }
+ }
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new ReportProcessingException("Failed to copy style. This should not have happened.");
+ }
+ }
+ else
+ {
+ derivedStyle = null;
+ }
+ }
+ else
+ {
+ derivedStyle = null;
+ }
+ return derivedStyle;
+ }
+
+ /**
+ * Derives the named style. If the style is a common style, a new automatic style is generated and inserted into the
+ * given stylesCollection. If the named style is an automatic style, the style is copied and inserted as new automatic
+ * style.
+ * <p/>
+ * After the style has been created, the style's inheritance hierarchy will be copied as well.
+ * <p/>
+ * If there is no style with the given name and family, a new empty automatic style will be created.
+ *
+ * @param styleFamily the family of the style to copy
+ * @param styleName the unique name of the style.
+ * @param stylesCollection the current styles collection
+ * @param commonCollection the global styles collection
+ * @param predefCollection the predefined styles from where to copy the styles.
+ * @param generator the style-name-generator of the current report-target
+ * @return the derived style instance.
+ * @throws ReportProcessingException if the style copying failed.
+ */
+ public static OfficeStyle deriveStyle(final String styleFamily,
+ final String styleName,
+ final OfficeStylesCollection stylesCollection,
+ final OfficeStylesCollection commonCollection,
+ final OfficeStylesCollection predefCollection,
+ final AttributeNameGenerator generator)
+ throws ReportProcessingException
+ {
+ if (styleFamily == null)
+ {
+ throw new NullPointerException("StyleFamily must not be null");
+ }
+ if (styleName != null)
+ {
+
+ final OfficeStyle currentAuto =
+ stylesCollection.getAutomaticStyles().getStyle(styleFamily,
+ styleName);
+ if (currentAuto != null)
+ {
+ // handle an automatic style ..
+ final OfficeStyle derivedStyle =
+ deriveAutomaticStyle(currentAuto, styleFamily, styleName,
+ generator, commonCollection, predefCollection);
+ stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
+ return derivedStyle;
+ }
+
+ final OfficeStyle currentCommon =
+ stylesCollection.getCommonStyles().getStyle(styleFamily, styleName);
+ if (currentCommon != null)
+ {
+ // handle an common style ..
+ final OfficeStyle derivedStyle =
+ deriveCommonStyle(currentCommon, styleFamily, styleName,
+ generator, commonCollection, predefCollection);
+ stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
+ return derivedStyle;
+ }
+
+// final OfficeStyle commonAuto =
+// commonCollection.getAutomaticStyles().getStyle(styleFamily,
+// styleName);
+// if (commonAuto != null)
+// {
+// // handle an automatic style ..
+// final OfficeStyle derivedStyle =
+// deriveAutomaticStyle(commonAuto, styleFamily, styleName,
+// generator, commonCollection, predefCollection);
+// stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
+// return derivedStyle;
+// }
+
+ final OfficeStyle commonCommon =
+ commonCollection.getCommonStyles().getStyle(styleFamily, styleName);
+ if (commonCommon != null)
+ {
+ // handle an common style ..
+ final OfficeStyle derivedStyle =
+ deriveCommonStyle(commonCommon, styleFamily, styleName,
+ generator, commonCollection, predefCollection);
+ stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
+ return derivedStyle;
+ }
+
+ final OfficeStyle predefAuto =
+ predefCollection.getAutomaticStyles().getStyle(styleFamily,
+ styleName);
+ if (predefAuto != null)
+ {
+ // handle an automatic style ..
+ final OfficeStyle derivedStyle =
+ deriveAutomaticStyle(predefAuto, styleFamily, styleName,
+ generator, commonCollection, predefCollection);
+ stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
+ return derivedStyle;
+ }
+
+ final OfficeStyle predefCommon =
+ predefCollection.getCommonStyles().getStyle(styleFamily, styleName);
+ if (predefCommon != null)
+ {
+ // handle an common style ..
+ final OfficeStyle derivedStyle =
+ deriveCommonStyle(predefCommon, styleFamily, styleName,
+ generator, commonCollection, predefCollection);
+ stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
+ return derivedStyle;
+ }
+ }
+
+ // No such style. Create a new one ..
+ final OfficeStyle autostyle = new OfficeStyle();
+ autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ autostyle.setType(STYLE);
+ autostyle.setStyleFamily(styleFamily);
+ if (styleName != null)
+ {
+ autostyle.setStyleName(styleName);
+ }
+ else
+ {
+ autostyle.setStyleName(generator.generateName("derived_anonymous"));
+ }
+
+ final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
+ autoStyles.addStyle(autostyle);
+ return autostyle;
+ }
+
+ private static OfficeStyle deriveCommonStyle(final OfficeStyle commonStyle,
+ final String styleFamily,
+ final String styleName,
+ final AttributeNameGenerator nameGenerator,
+ final OfficeStylesCollection commonCollection,
+ final OfficeStylesCollection predefCollection)
+ throws ReportProcessingException
+ {
+ final OfficeStyle autostyle = new OfficeStyle();
+ autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ autostyle.setType(STYLE);
+ autostyle.setStyleFamily(styleFamily);
+ autostyle.setStyleName(nameGenerator.generateName("derived_" + styleName));
+ autostyle.setStyleParent(styleName);
+
+ // now copy the common style ..
+ final OfficeStyles commonStyles = commonCollection.getCommonStyles();
+ if (!commonStyles.containsStyle(styleFamily, styleName))
+ {
+ copyStyleInternal(commonStyle, commonStyles,
+ commonCollection, commonCollection, predefCollection,
+ styleFamily, new HashSet<String>());
+ }
+ return autostyle;
+ }
+
+ private static OfficeStyle deriveAutomaticStyle(final OfficeStyle commonStyle,
+ final String styleFamily,
+ final String styleName,
+ final AttributeNameGenerator nameGenerator,
+ final OfficeStylesCollection commonCollection,
+ final OfficeStylesCollection predefCollection)
+ throws ReportProcessingException
+ {
+ try
+ {
+ final OfficeStyle autostyle = (OfficeStyle) commonStyle.clone();
+ autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ autostyle.setType(STYLE);
+ autostyle.setStyleFamily(styleFamily);
+ autostyle.setStyleName(nameGenerator.generateName("derived_auto_" + styleName));
+
+
+ final String parent = autostyle.getStyleParent();
+ if (parent != null)
+ {
+ copyStyle(styleFamily, parent, commonCollection, commonCollection,
+ predefCollection);
+ }
+ return autostyle;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new ReportProcessingException(
+ "Deriving the style failed. Clone error: ", e);
+ }
+ }
+
+ public static String queryStyle(final OfficeStylesCollection predefCollection,
+ final String styleFamily,
+ final String styleName,
+ final String sectionName,
+ final String propertyNamespace,
+ final String propertyName)
+ {
+ return queryStyle(predefCollection, styleFamily,
+ styleName, sectionName, propertyNamespace, propertyName, new HashSet<String>());
+ }
+
+ public static OfficeStyle queryStyleByProperties(final OfficeStylesCollection predefCollection,
+ final String styleFamily,
+ final String sectionName,
+ final ArrayList<?> propertyNamespace,
+ final ArrayList<?> propertyName,
+ final ArrayList<?> propertyValues)
+ {
+ if (propertyNamespace.size() != propertyName.size())
+ {
+ return null;
+ }
+ final OfficeStyle[] styles = predefCollection.getAutomaticStyles().getAllStyles();
+ for (int i = 0; i < styles.length; i++)
+ {
+ final OfficeStyle officeStyle = styles[i];
+ if (officeStyle.getStyleFamily().equals(styleFamily))
+ {
+ final Element section = officeStyle.findFirstChild(OfficeNamespaces.STYLE_NS, sectionName);
+ if (section != null)
+ {
+ int j = 0;
+ for (; j < propertyNamespace.size(); j++)
+ {
+ final String ns = (String) propertyNamespace.get(j);
+ final String prop = (String) propertyName.get(j);
+ final Object obj = section.getAttribute(ns, prop);
+ final Object value = propertyValues.get(j);
+ if (obj == null || value == null)
+ {
+ continue;
+ }
+ if (!propertyValues.get(j).equals(obj))
+ {
+ break;
+ }
+ }
+ if (j == propertyName.size())
+ {
+ return officeStyle;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String queryStyle(final OfficeStylesCollection predefCollection,
+ final String styleFamily,
+ final String styleName,
+ final String sectionName,
+ final String propertyNamespace,
+ final String propertyName,
+ final Set<String> seenStyles)
+ {
+ if (seenStyles.contains(styleName))
+ {
+ return null;
+ }
+ seenStyles.add(styleName);
+
+ final OfficeStyle style = predefCollection.getStyle(styleFamily, styleName);
+ if (style == null)
+ {
+ return null; // no such style
+
+ }
+ final Element section = style.findFirstChild(OfficeNamespaces.STYLE_NS, sectionName);
+ if (section != null)
+ {
+ final Object attribute = section.getAttribute(propertyNamespace, propertyName);
+ if (attribute != null)
+ {
+ return String.valueOf(attribute);
+ }
+ }
+ final String parent = style.getStyleParent();
+ if (parent == null)
+ {
+ return null;
+ }
+ return queryStyle(predefCollection, styleFamily, parent, sectionName, propertyNamespace, propertyName, seenStyles);
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/StylesWriter.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/StylesWriter.java
new file mode 100644
index 000000000000..64257d50d160
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/StylesWriter.java
@@ -0,0 +1,380 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output;
+
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.model.DataStyle;
+import org.libreoffice.report.pentaho.model.FontFaceDeclsSection;
+import org.libreoffice.report.pentaho.model.FontFaceElement;
+import org.libreoffice.report.pentaho.model.OfficeMasterPage;
+import org.libreoffice.report.pentaho.model.OfficeMasterStyles;
+import org.libreoffice.report.pentaho.model.OfficeStyle;
+import org.libreoffice.report.pentaho.model.OfficeStyles;
+import org.libreoffice.report.pentaho.model.OfficeStylesCollection;
+import org.libreoffice.report.pentaho.model.PageLayout;
+import org.libreoffice.report.pentaho.model.RawText;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.jfree.layouting.namespace.Namespaces;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.report.JFreeReportBoot;
+import org.jfree.report.JFreeReportInfo;
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.structure.Element;
+import org.jfree.report.structure.Node;
+import org.jfree.report.structure.Section;
+import org.jfree.report.structure.StaticText;
+
+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;
+
+
+/**
+ * This class writes the style collection into a 'styles.xml' document.
+ *
+ * @since 09.03.2007
+ */
+public class StylesWriter
+{
+
+ private final XmlWriter xmlWriter;
+ private final boolean writeOpeningTag;
+
+ public StylesWriter(final Writer writer)
+ {
+ final DefaultTagDescription tagDescription = new DefaultTagDescription();
+ tagDescription.configure(JFreeReportBoot.getInstance().getGlobalConfig(),
+ OfficeDocumentReportTarget.TAG_DEF_PREFIX);
+
+ this.xmlWriter = new XmlWriter(writer, tagDescription);
+ this.xmlWriter.setAlwaysAddNamespace(true);
+ this.writeOpeningTag = true;
+ }
+
+ public StylesWriter(final XmlWriter xmlWriter)
+ {
+ this.xmlWriter = xmlWriter;
+ this.writeOpeningTag = false;
+ }
+
+ public void writeContentStyles(final OfficeStylesCollection predefined,
+ final OfficeStylesCollection globals)
+ throws IOException, ReportProcessingException
+ {
+ writeFontFaces(new OfficeStylesCollection[]
+ {
+ globals
+ });
+ writeAutomaticStylesSection(new OfficeStylesCollection[]
+ {
+ globals
+ });
+ }
+
+ public void writeGlobalStyles(final OfficeStylesCollection predefined,
+ final OfficeStylesCollection globals)
+ throws IOException, ReportProcessingException
+ {
+ if (writeOpeningTag)
+ {
+ performWriteRootTag();
+ }
+
+ writeFontFaces(new OfficeStylesCollection[]
+ {
+ globals
+ });
+ writeCommonStylesSection(new OfficeStylesCollection[]
+ {
+ globals
+ });
+ writeAutomaticStylesSection(new OfficeStylesCollection[]
+ {
+ globals
+ });
+ writeMasterStylesSection(new OfficeStylesCollection[]
+ {
+ globals
+ });
+
+ if (writeOpeningTag)
+ {
+ xmlWriter.writeCloseTag();
+ }
+ }
+
+ private void writeMasterStylesSection(final OfficeStylesCollection[] osc)
+ throws IOException
+ {
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "master-styles", XmlWriterSupport.OPEN);
+ for (int sci = 0; sci < osc.length; sci++)
+ {
+ final OfficeStylesCollection collection = osc[sci];
+ final OfficeMasterStyles officeStyles = collection.getMasterStyles();
+ final OfficeMasterPage[] officeMasterPages = officeStyles.getAllMasterPages();
+ for (int i = 0; i < officeMasterPages.length; i++)
+ {
+ final OfficeMasterPage masterPage = officeMasterPages[i];
+ writeSection(masterPage);
+ }
+
+ writeSectionChildren(officeStyles.getOtherNodes().getNodeArray());
+ }
+
+ xmlWriter.writeCloseTag();
+ }
+
+ private void writeCommonStylesSection(final OfficeStylesCollection[] osc)
+ throws IOException
+ {
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "styles", XmlWriterSupport.OPEN);
+
+ for (int sci = 0; sci < osc.length; sci++)
+ {
+ final OfficeStylesCollection collection = osc[sci];
+ final OfficeStyles officeStyles = collection.getCommonStyles();
+ writeStyles(officeStyles);
+ }
+
+ xmlWriter.writeCloseTag();
+ }
+
+ private void writeAutomaticStylesSection(final OfficeStylesCollection[] osc)
+ throws IOException
+ {
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "automatic-styles", XmlWriterSupport.OPEN);
+ for (int sci = 0; sci < osc.length; sci++)
+ {
+ final OfficeStylesCollection collection = osc[sci];
+ final OfficeStyles officeStyles = collection.getAutomaticStyles();
+ writeStyles(officeStyles);
+ }
+
+ xmlWriter.writeCloseTag();
+ }
+
+ private void writeFontFaces(final OfficeStylesCollection[] osc)
+ throws IOException
+ {
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "font-face-decls", XmlWriterSupport.OPEN);
+
+ final TreeMap<String,FontFaceElement> fontFaces = new TreeMap<String,FontFaceElement>();
+ for (int sci = 0; sci < osc.length; sci++)
+ {
+ final OfficeStylesCollection collection = osc[sci];
+ final FontFaceDeclsSection fontFaceDecls = collection.getFontFaceDecls();
+ final FontFaceElement[] fontFaceElements = fontFaceDecls.getAllFontFaces();
+ for (int i = 0; i < fontFaceElements.length; i++)
+ {
+ final FontFaceElement element = fontFaceElements[i];
+ fontFaces.put(element.getStyleName(), element);
+ }
+ }
+
+ final Iterator<FontFaceElement> values = fontFaces.values().iterator();
+ while (values.hasNext())
+ {
+ final FontFaceElement element = values.next();
+ writeElement(element);
+ }
+
+ xmlWriter.writeCloseTag();
+ }
+
+ private void writeStyles(final OfficeStyles styles)
+ throws IOException
+ {
+ final OfficeStyle[] allStyles = styles.getAllStyles();
+ for (int i = 0; i < allStyles.length; i++)
+ {
+ final OfficeStyle style = allStyles[i];
+ writeSection(style);
+ }
+
+ final DataStyle[] allDataStyles = styles.getAllDataStyles();
+ for (int i = 0; i < allDataStyles.length; i++)
+ {
+ final DataStyle style = allDataStyles[i];
+ writeSection(style);
+ }
+
+ final PageLayout[] allPageStyles = styles.getAllPageStyles();
+ for (int i = 0; i < allPageStyles.length; i++)
+ {
+ final PageLayout style = allPageStyles[i];
+ writeSection(style);
+ }
+
+ writeSectionChildren(styles.getOtherStyles());
+ }
+
+ private void writeElement(final Element element)
+ throws IOException
+ {
+ final String type = element.getType();
+ final String namespace = element.getNamespace();
+ final AttributeList attrList = buildAttributeList(element.getAttributeMap());
+ xmlWriter.writeTag(namespace, type, attrList, XmlWriterSupport.CLOSE);
+ }
+
+ private void writeSection(final Section section)
+ throws IOException
+ {
+ final String type = section.getType();
+ final String namespace = section.getNamespace();
+ final AttributeList attrList = buildAttributeList(section.getAttributeMap());
+ if (section.getNodeCount() == 0)
+ {
+ xmlWriter.writeTag(namespace, type, attrList, XmlWriterSupport.CLOSE);
+ return;
+ }
+
+ xmlWriter.writeTag(namespace, type, attrList, XmlWriterSupport.OPEN);
+ writeSectionChildren(section.getNodeArray());
+
+ xmlWriter.writeCloseTag();
+ }
+
+ private void writeSectionChildren(final Node[] nodes)
+ throws IOException
+ {
+ for (int i = 0; i < nodes.length; i++)
+ {
+ final Node node = nodes[i];
+ if (node instanceof Section)
+ {
+ writeSection((Section) node);
+ }
+ else if (node instanceof Element)
+ {
+ writeElement((Element) node);
+ }
+ else if (node instanceof RawText)
+ {
+ final RawText text = (RawText) node;
+ xmlWriter.writeText(text.getText());
+ }
+ else if (node instanceof StaticText)
+ {
+ final StaticText text = (StaticText) node;
+ xmlWriter.writeTextNormalized(text.getText(), false);
+ }
+ }
+ }
+
+ protected AttributeList buildAttributeList(final AttributeMap 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());
+ attrList.setAttribute(attrNamespace, key, String.valueOf(entry.getValue()));
+ }
+ }
+ return attrList;
+ }
+
+ protected boolean isFilteredNamespace(final String namespace)
+ {
+ if (Namespaces.LIBLAYOUT_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;
+ }
+
+ private void performWriteRootTag()
+ throws IOException
+ {
+ 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",
+ OfficeDocumentReportTarget.ODF_VERSION);
+
+ this.xmlWriter.writeXmlDeclaration("UTF-8");
+ this.xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS,
+ "document-styles", rootAttributes, XmlWriterSupport.OPEN);
+ }
+
+ public void close()
+ throws IOException
+ {
+ xmlWriter.close();
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportProcessor.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportProcessor.java
new file mode 100644
index 000000000000..f8256bc6ff57
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.chart;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OutputRepository;
+
+import org.jfree.report.DataSourceException;
+import org.jfree.report.ReportDataFactoryException;
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.flow.ReportJob;
+import org.jfree.report.flow.ReportStructureRoot;
+import org.jfree.report.flow.ReportTarget;
+import org.jfree.report.flow.SinglePassReportProcessor;
+
+import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
+
+/**
+ *
+ */
+public class ChartRawReportProcessor extends SinglePassReportProcessor
+{
+
+ private final OutputRepository outputRepository;
+ private final String targetName;
+ private final InputRepository inputRepository;
+ private final ImageService imageService;
+ private final DataSourceFactory dataSourceFactory;
+
+ public ChartRawReportProcessor(final InputRepository inputRepository,
+ final OutputRepository outputRepository,
+ final String targetName,
+ final ImageService imageService,
+ final DataSourceFactory dataSourceFactory)
+ {
+ if (inputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (outputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (targetName == null)
+ {
+ throw new NullPointerException();
+ }
+ if (imageService == null)
+ {
+ throw new NullPointerException();
+ }
+ if (dataSourceFactory == null)
+ {
+ throw new NullPointerException();
+ }
+ this.targetName = targetName;
+ this.inputRepository = inputRepository;
+ this.outputRepository = outputRepository;
+ this.imageService = imageService;
+ this.dataSourceFactory = dataSourceFactory;
+ }
+
+ protected ReportTarget createReportTarget(final ReportJob job)
+ throws ReportProcessingException
+ {
+ final ReportStructureRoot report = job.getReportStructureRoot();
+ final ResourceManager resourceManager = report.getResourceManager();
+
+ return new ChartRawReportTarget(job, resourceManager, report.getBaseResource(),
+ inputRepository, outputRepository, targetName, imageService, dataSourceFactory);
+ }
+
+ public void processReport(final ReportJob job) throws ReportDataFactoryException, DataSourceException,
+ ReportProcessingException
+ {
+ final ReportTarget reportTarget = createReportTarget(job);
+ processReportRun(job, reportTarget);
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportTarget.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportTarget.java
new file mode 100644
index 000000000000..7b0a2819a22b
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/chart/ChartRawReportTarget.java
@@ -0,0 +1,247 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.chart;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OfficeToken;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.PentahoReportEngineMetaData;
+import org.libreoffice.report.pentaho.output.OfficeDocumentReportTarget;
+
+import java.io.IOException;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.report.DataFlags;
+import org.jfree.report.DataSourceException;
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.flow.ReportJob;
+import org.jfree.report.flow.ReportStructureRoot;
+import org.jfree.report.flow.ReportTargetUtil;
+
+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.XmlWriter;
+import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
+
+public class ChartRawReportTarget extends OfficeDocumentReportTarget
+{
+
+ private boolean inFilterElements = false;
+ private boolean tableRowsStarted = false;
+ private int tableCount = 0;
+ private int closeTags = 0;
+
+ public ChartRawReportTarget(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, inputRepository, outputRepository, target, imageService, dataSourceFactory);
+ }
+
+ protected String getTargetMimeType()
+ {
+ return "application/vnd.oasis.opendocument.chart";
+ }
+
+ protected String getStartContent()
+ {
+ return "chart";
+ }
+
+ public String getExportDescriptor()
+ {
+ return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_CHART;
+ }
+
+ protected void startContent(final AttributeMap attrs) throws IOException, DataSourceException, ReportProcessingException
+ {
+ inFilterElements = false;
+ closeTags = 0;
+ tableCount = 0;
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, getStartContent(), null, XmlWriterSupport.OPEN);
+ writeNullDate();
+ ++closeTags;
+ }
+
+ protected void endContent(final AttributeMap attrs) throws IOException, DataSourceException, ReportProcessingException
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ while (closeTags > 0)
+ {
+ xmlWriter.writeCloseTag();
+ --closeTags;
+ }
+ }
+
+ protected void startReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ protected void endReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ protected void startOther(final AttributeMap attrs) throws IOException, DataSourceException, ReportProcessingException
+ {
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ if (!isFilteredNamespace(namespace))
+ {
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ try
+ {
+ processElement(attrs, namespace, elementType);
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+ }
+
+ private boolean isFiltered(final String elementType)
+ {
+ return OfficeToken.TABLE_HEADER_COLUMNS.equals(elementType) || OfficeToken.TABLE_HEADER_ROWS.equals(elementType) || OfficeToken.TABLE_COLUMNS.equals(elementType);
+ }
+
+ protected void endOther(final AttributeMap attrs) throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (tableRowsStarted && getCurrentRole() == ROLE_TEMPLATE)
+ {
+ return;
+ }
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ if (!isFilteredNamespace(namespace))
+ {
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ // if this is the report namespace, write out a table definition ..
+ if (OfficeNamespaces.TABLE_NS.equals(namespace))
+ {
+ if (OfficeToken.TABLE.equals(elementType) || OfficeToken.TABLE_ROWS.equals(elementType))
+ {
+ return;
+ }
+ else if (isFiltered(elementType))
+ {
+ inFilterElements = false;
+ if (tableCount > 1)
+ {
+ return;
+ }
+ }
+ }
+ else if (OfficeNamespaces.CHART_NS.equals(namespace) && "chart".equals(elementType))
+ {
+ return;
+ }
+ if (inFilterElements && tableCount > 1)
+ {
+ return;
+ }
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ --closeTags;
+ }
+ }
+
+ public void processContent(final DataFlags value)
+ throws DataSourceException, ReportProcessingException
+ {
+ if (!(tableRowsStarted && getCurrentRole() == ROLE_TEMPLATE))
+ {
+ super.processContent(value);
+ }
+ }
+
+ private void processElement(final AttributeMap attrs, final String namespace, final String elementType)
+ throws IOException, ReportProcessingException
+ {
+ if (tableRowsStarted && getCurrentRole() == ROLE_TEMPLATE)
+ {
+ return;
+ }
+ if (OfficeNamespaces.TABLE_NS.equals(namespace))
+ {
+ if (OfficeToken.TABLE.equals(elementType))
+ {
+ tableCount += 1;
+ if (tableCount > 1)
+ {
+ return;
+ }
+ }
+ else if (OfficeToken.TABLE_ROWS.equals(elementType))
+ {
+ if (tableCount > 1)
+ {
+ return;
+ }
+ tableRowsStarted = true;
+ }
+ else if (isFiltered(elementType))
+ {
+ inFilterElements = true;
+ if (tableCount > 1)
+ {
+ return;
+ }
+ }
+ }
+ if (inFilterElements && tableCount > 1)
+ {
+ return;
+ }
+
+ // All styles have to be processed or you will loose the paragraph-styles and inline text-styles.
+ // ..
+ performStyleProcessing(attrs);
+
+ final AttributeList attrList = buildAttributeList(attrs);
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriter.OPEN);
+ ++closeTags;
+ }
+ // /////////////////////////////////////////////////////////////////////////
+
+ public void processText(final String text) throws DataSourceException, ReportProcessingException
+ {
+ if (inFilterElements && tableCount > 1)
+ {
+ return;
+ }
+ super.processText(text);
+ }
+
+ public void endReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ super.endReport(report);
+ copyMeta();
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportProcessor.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportProcessor.java
new file mode 100644
index 000000000000..e61194797c70
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportProcessor.java
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.spreadsheet;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.pentaho.PentahoFormulaContext;
+
+import org.jfree.report.DataSourceException;
+import org.jfree.report.ReportDataFactoryException;
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.data.ReportContextImpl;
+import org.jfree.report.flow.AbstractReportProcessor;
+import org.jfree.report.flow.ReportContext;
+import org.jfree.report.flow.ReportJob;
+import org.jfree.report.flow.ReportStructureRoot;
+import org.jfree.report.flow.ReportTarget;
+
+import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
+
+public class SpreadsheetRawReportProcessor extends AbstractReportProcessor
+{
+
+ private final OutputRepository outputRepository;
+ private final String targetName;
+ private final InputRepository inputRepository;
+ private final ImageService imageService;
+ private final DataSourceFactory dataSourceFactory;
+
+ public SpreadsheetRawReportProcessor(final InputRepository inputRepository,
+ final OutputRepository outputRepository,
+ final String targetName,
+ final ImageService imageService,
+ final DataSourceFactory dataSourceFactory)
+ {
+ if (outputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (targetName == null)
+ {
+ throw new NullPointerException();
+ }
+ if (imageService == null)
+ {
+ throw new NullPointerException();
+ }
+ if (inputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (dataSourceFactory == null)
+ {
+ throw new NullPointerException();
+ }
+
+ this.targetName = targetName;
+ this.inputRepository = inputRepository;
+ this.outputRepository = outputRepository;
+ this.imageService = imageService;
+ this.dataSourceFactory = dataSourceFactory;
+ }
+
+ protected ReportTarget createReportTarget(final ReportJob job) throws ReportProcessingException
+ {
+ final ReportStructureRoot report = job.getReportStructureRoot();
+ final ResourceManager resourceManager = report.getResourceManager();
+ return new SpreadsheetRawReportTarget(job, resourceManager, report.getBaseResource(), inputRepository, outputRepository, targetName, imageService, dataSourceFactory);
+ }
+
+ public void processReport(final ReportJob job) throws ReportDataFactoryException, DataSourceException,
+ ReportProcessingException
+ {
+ final ReportTarget reportTarget = createReportTarget(job);
+ // first run: collect table cell sizes for all tables
+ processReportRun(job, reportTarget);
+ // second run: uses table cell data to output a single uniform table
+ processReportRun(job, reportTarget);
+ }
+
+ protected ReportContext createReportContext(final ReportJob job,
+ final ReportTarget target)
+ {
+ final ReportContext context = super.createReportContext(job, target);
+ if (context instanceof ReportContextImpl)
+ {
+ final ReportContextImpl impl = (ReportContextImpl) context;
+ impl.setFormulaContext(new PentahoFormulaContext(impl.getFormulaContext(), job.getConfiguration()));
+ }
+ return context;
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java
new file mode 100644
index 000000000000..163818cf0aa4
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java
@@ -0,0 +1,959 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.spreadsheet;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OfficeToken;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.PentahoReportEngineMetaData;
+import org.libreoffice.report.pentaho.model.OfficeMasterPage;
+import org.libreoffice.report.pentaho.model.OfficeMasterStyles;
+import org.libreoffice.report.pentaho.model.OfficeStyle;
+import org.libreoffice.report.pentaho.model.OfficeStyles;
+import org.libreoffice.report.pentaho.model.OfficeStylesCollection;
+import org.libreoffice.report.pentaho.model.PageSection;
+import org.libreoffice.report.pentaho.output.OfficeDocumentReportTarget;
+import org.libreoffice.report.pentaho.output.StyleUtilities;
+import org.libreoffice.report.pentaho.output.text.MasterPageFactory;
+import org.libreoffice.report.pentaho.styles.LengthCalculator;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jfree.layouting.input.style.values.CSSNumericType;
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.report.DataFlags;
+import org.jfree.report.DataSourceException;
+import org.jfree.report.JFreeReportInfo;
+import org.jfree.report.ReportProcessingException;
+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.IntegerCache;
+
+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.XmlWriter;
+import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
+
+
+/**
+ * Creation-Date: 03.11.2007
+ *
+ */
+public class SpreadsheetRawReportTarget extends OfficeDocumentReportTarget
+{
+
+ private static final String[] FOPROPS = new String[]
+ {
+ "letter-spacing", "font-variant", "text-transform"
+ };
+ private static final String NUMBERCOLUMNSSPANNED = "number-columns-spanned";
+ private static final String[] STYLEPROPS = new String[]
+ {
+ "text-combine", "font-pitch-complex", "text-rotation-angle", "font-name", "text-blinking", "letter-kerning", "text-combine-start-char", "text-combine-end-char", "text-position", "text-scale"
+ };
+ private static final int CELL_WIDTH_FACTOR = 10000;
+ private static final String TRANSPARENT = "transparent";
+ private boolean paragraphFound = false;
+ private boolean paragraphHandled = false;
+
+ /**
+ * This class represents a column boundary, not in width, but it's actual boundary location. One of the motivations
+ * for creating this class was to be able to record the boundaries for each incoming table while consuming as few
+ * objects/memory as possible.
+ */
+ private static class ColumnBoundary implements Comparable<ColumnBoundary>
+ {
+
+ private final Set<Integer> tableIndices;
+ private final long boundary;
+
+ private ColumnBoundary(final long boundary)
+ {
+ this.tableIndices = new HashSet<Integer>();
+ this.boundary = boundary;
+ }
+
+ public void addTableIndex(final int table)
+ {
+ tableIndices.add(IntegerCache.getInteger(table));
+ }
+
+ public float getBoundary()
+ {
+ return boundary;
+ }
+
+ public boolean isContainedByTable(final int table)
+ {
+ final Integer index = IntegerCache.getInteger(table);
+ return tableIndices.contains(index);
+ }
+
+ public int compareTo(final ColumnBoundary arg0)
+ {
+ if (arg0.equals(this))
+ {
+ return 0;
+ }
+ if (arg0 instanceof ColumnBoundary)
+ {
+ if (boundary > arg0.boundary)
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ return 1;
+ }
+
+ public boolean equals(final Object obj)
+ {
+ return obj instanceof ColumnBoundary && ((ColumnBoundary) obj).boundary == boundary;
+ }
+
+ public int hashCode()
+ {
+ assert false : "hashCode not designed";
+ return 42; // any arbitrary constant will do
+ }
+ }
+ private String tableBackgroundColor; // null means transparent ...
+ private static final ColumnBoundary[] EMPTY_COLBOUNDS = new ColumnBoundary[0];
+ private boolean elementBoundaryCollectionPass;
+ private boolean oleHandled;
+ private final List<ColumnBoundary> columnBoundaryList;
+ private long currentRowBoundaryMarker;
+ private ColumnBoundary[] sortedBoundaryArray;
+ private ColumnBoundary[] boundariesForTableArray;
+ private int tableCounter;
+ private int columnCounter;
+ private int columnSpanCounter;
+ private int currentSpan = 0;
+ private String unitsOfMeasure;
+ final private List<AttributeMap> shapes;
+ final private List<AttributeMap> ole;
+ final private List<CSSNumericValue> rowHeights;
+
+ public SpreadsheetRawReportTarget(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, inputRepository, outputRepository, target, imageService, dataSourceFactory);
+ columnBoundaryList = new ArrayList<ColumnBoundary>();
+ elementBoundaryCollectionPass = true;
+ rowHeights = new ArrayList<CSSNumericValue>();
+ shapes = new ArrayList<AttributeMap>();
+ ole = new ArrayList<AttributeMap>();
+ oleHandled = false;
+ }
+
+ public void startOther(final AttributeMap attrs) throws DataSourceException, ReportProcessingException
+ {
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.OBJECT_OLE, attrs))
+ {
+ if (isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ ole.add(attrs);
+ }
+ oleHandled = true;
+ return;
+ }
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ if (isRepeatingSection() || isFilteredNamespace(namespace))
+ {
+ return;
+ }
+
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ if (OfficeNamespaces.TEXT_NS.equals(namespace) && OfficeToken.P.equals(elementType) && !paragraphHandled)
+ {
+ paragraphFound = true;
+ return;
+ }
+
+ if (OfficeNamespaces.DRAWING_NS.equals(namespace) && OfficeToken.FRAME.equals(elementType))
+ {
+ if (isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ final LengthCalculator len = new LengthCalculator();
+ for (int i = 0; i < rowHeights.size(); i++)
+ {
+ len.add(rowHeights.get(i));
+ // val += (rowHeights.get(i)).getValue();
+ }
+
+ rowHeights.clear();
+ final CSSNumericValue currentRowHeight = len.getResult();
+ rowHeights.add(currentRowHeight);
+ attrs.setAttribute(OfficeNamespaces.DRAWING_NS, "z-index", String.valueOf(shapes.size()));
+ final String y = (String) attrs.getAttribute(OfficeNamespaces.SVG_NS, "y");
+ if (y != null)
+ {
+ len.add(parseLength(y));
+ final CSSNumericValue currentY = len.getResult();
+ attrs.setAttribute(OfficeNamespaces.SVG_NS, "y", currentY.getValue() + currentY.getType().getType());
+ }
+ shapes.add(attrs);
+ }
+ return;
+ }
+ if (oleHandled)
+ {
+ if (isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ ole.add(attrs);
+ }
+ return;
+ }
+
+ // if this is the report namespace, write out a table definition ..
+ if (OfficeNamespaces.TABLE_NS.equals(namespace) && OfficeToken.TABLE.equals(elementType))
+ {
+ // whenever we see a new table, we increment our tableCounter
+ // this is used to keep tracked of the boundary conditions per table
+ tableCounter++;
+ }
+
+ if (isElementBoundaryCollectionPass())
+ {
+ collectBoundaryForElement(attrs);
+ }
+ else
+ // if (!isElementBoundaryCollectionPass())
+ {
+ try
+ {
+ processElement(attrs, namespace, elementType);
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+ }
+
+ protected void startReportSection(final AttributeMap attrs, final int role) throws IOException, DataSourceException, ReportProcessingException
+ {
+ if ((role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER) && (!PageSection.isPrintWithReportHeader(attrs) || !PageSection.isPrintWithReportFooter(attrs)))
+ {
+ startBuffering(new OfficeStylesCollection(), true);
+ }
+ else
+ {
+ super.startReportSection(attrs, role);
+ }
+ }
+
+ protected void endReportSection(final AttributeMap attrs, final int role) throws IOException, DataSourceException, ReportProcessingException
+ {
+ if ((role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER) && (!PageSection.isPrintWithReportHeader(attrs) || !PageSection.isPrintWithReportFooter(attrs)))
+ {
+ finishBuffering();
+ }
+ else
+ {
+ super.endReportSection(attrs, role);
+ }
+ }
+
+ private void handleParagraph()
+ {
+ if (paragraphFound)
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, null, XmlWriterSupport.OPEN);
+ paragraphHandled = true;
+ paragraphFound = false;
+ }
+ catch (IOException ex)
+ {
+ LOGGER.error("ReportProcessing failed", ex);
+ }
+ }
+ }
+
+ private void processElement(final AttributeMap attrs, final String namespace, final String elementType)
+ throws IOException, ReportProcessingException
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE, attrs))
+ {
+ // a new table means we must clear our "calculated" table boundary array cache
+ boundariesForTableArray = null;
+
+ final String tableStyle = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ if (tableStyle == null)
+ {
+ tableBackgroundColor = null;
+ }
+ else
+ {
+ final Object raw = StyleUtilities.queryStyle(getPredefinedStylesCollection(), OfficeToken.TABLE, tableStyle,
+ "table-properties", OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR);
+ if (raw == null || TRANSPARENT.equals(raw))
+ {
+ tableBackgroundColor = null;
+ }
+ else
+ {
+ tableBackgroundColor = String.valueOf(raw);
+ }
+ }
+ return;
+ }
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMN, attrs) || ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS, attrs))
+ {
+ return;
+ }
+
+ // covered-table-cell elements may appear in the input from row or column spans. In the event that we hit a
+ // column-span we simply ignore these elements because we are going to adjust the span to fit the uniform table.
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.COVERED_TABLE_CELL, attrs))
+ {
+ if (columnSpanCounter > 0)
+ {
+ columnSpanCounter--;
+ }
+
+ if (columnSpanCounter == 0)
+ {
+ // if we weren't expecting a covered-table-cell, let's use it, it's probably from a row-span
+ columnCounter++;
+ final int span = getColumnSpanForCell(tableCounter, columnCounter, 1);
+ // use the calculated span for the column in the uniform table to create any additional covered-table-cell
+ // elements
+ for (int i = 0; i < span; i++)
+ {
+ xmlWriter.writeTag(namespace, OfficeToken.COVERED_TABLE_CELL, null, XmlWriter.CLOSE);
+ }
+ }
+ return;
+ }
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrs))
+ {
+ // a new row means our column counter gets reset
+ columnCounter = 0;
+ // Lets make sure the color of the table is ok ..
+ if (tableBackgroundColor != null)
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ final OfficeStyle style = deriveStyle(OfficeToken.TABLE_ROW, styleName);
+ Element tableRowProperties = style.getTableRowProperties();
+ if (tableRowProperties == null)
+ {
+ tableRowProperties = new Section();
+ tableRowProperties.setNamespace(OfficeNamespaces.STYLE_NS);
+ tableRowProperties.setType("table-row-properties");
+ tableRowProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, tableBackgroundColor);
+ style.addNode(tableRowProperties);
+ }
+ else
+ {
+ final Object oldValue = tableRowProperties.getAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR);
+ if (oldValue == null || TRANSPARENT.equals(oldValue))
+ {
+ tableRowProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, tableBackgroundColor);
+ }
+ }
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ }
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_CELL, attrs))
+ {
+ columnCounter++;
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ if (styleName != null)
+ {
+ final OfficeStyle cellStyle = getPredefinedStylesCollection().getStyle(OfficeToken.TABLE_CELL, styleName);
+ if (cellStyle != null)
+ {
+ final Section textProperties = (Section) cellStyle.getTextProperties();
+ if (textProperties != null)
+ {
+ for (String i : FOPROPS)
+ {
+ textProperties.setAttribute(OfficeNamespaces.FO_NS, i, null);
+ }
+ textProperties.setAttribute(OfficeNamespaces.TEXT_NS, "display", null);
+ for (String i : STYLEPROPS)
+ {
+ textProperties.setAttribute(OfficeNamespaces.STYLE_NS, i, null);
+ }
+ }
+ final Section props = (Section) cellStyle.getTableCellProperties();
+ if (props != null)
+ {
+ final Object raw = props.getAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR);
+ if (TRANSPARENT.equals(raw))
+ {
+ props.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, null);
+ // cellStyle.removeNode(props);
+ }
+ }
+ }
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, styleName);
+ }
+
+ final String numColSpanStr = (String) attrs.getAttribute(namespace, NUMBERCOLUMNSSPANNED);
+ int initialColumnSpan = columnSpanCounter = 1;
+ if (numColSpanStr != null)
+ {
+ initialColumnSpan = Integer.parseInt(numColSpanStr);
+ columnSpanCounter = initialColumnSpan;
+ }
+ final int span = getColumnSpanForCell(tableCounter, columnCounter, initialColumnSpan);
+ if (initialColumnSpan > 1)
+ {
+ // add the initial column span to our column counter index (subtract 1, since it is counted by default)
+ columnCounter += initialColumnSpan - 1;
+ }
+
+ // if (span < initialColumnSpan)
+ // {
+ // // ColumnBoundary cbs[] = getBoundariesForTable(tableCounter);
+ // // for (int i = 0; i < cbs.length; i++)
+ // // {
+ // // System.out.print(cbs[i].getBoundary() + " ");
+ // // }
+ // // System.out.println();
+ //
+ // LOGGER.error("A cell cannot span less than the declared columns: Declared=" + initialColumnSpan + " Computed="
+ // + span);
+ // }
+
+ // there's no point to create number-columns-spanned attributes if we only span 1 column
+ if (span > 1)
+ {
+ attrs.setAttribute(namespace, NUMBERCOLUMNSSPANNED, "" + span);
+ currentSpan = span;
+ }
+ // we must also generate "covered-table-cell" elements for each column spanned
+ // but we'll do this in the endElement, after we close this OfficeToken.TABLE_CELL
+ }
+
+ // All styles have to be processed or you will loose the paragraph-styles and inline text-styles.
+ // ..
+ performStyleProcessing(attrs);
+
+ final AttributeList attrList = buildAttributeList(attrs);
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriter.OPEN);
+ // System.out.println("elementType = " + elementType);
+ }
+
+ private void collectBoundaryForElement(final AttributeMap attrs)
+ {
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS, attrs))
+ {
+ // A table row resets the column counter.
+ resetCurrentRowBoundaryMarker();
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMN, attrs))
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ if (styleName == null)
+ {
+ // This should not happen, but if it does, we will ignore that cell.
+ return;
+ }
+
+ final OfficeStyle style = getPredefinedStylesCollection().getStyle(OfficeToken.TABLE_COLUMN, styleName);
+ if (style == null)
+ {
+ // Now this is very bad. It means that there is no style defined with the given name.
+ return;
+ }
+
+ final Element tableColumnProperties = style.getTableColumnProperties();
+ String widthStr = (String) tableColumnProperties.getAttribute("column-width");
+ widthStr = widthStr.substring(0, widthStr.indexOf(getUnitsOfMeasure(widthStr)));
+ final float val = Float.parseFloat(widthStr) * CELL_WIDTH_FACTOR;
+ addColumnWidthToRowBoundaryMarker((long) val);
+ ColumnBoundary currentRowBoundary = new ColumnBoundary(getCurrentRowBoundaryMarker());
+ final List<ColumnBoundary> columnBoundaryList_ = getColumnBoundaryList();
+ final int idx = columnBoundaryList_.indexOf(currentRowBoundary);
+ if (idx == -1)
+ {
+ columnBoundaryList_.add(currentRowBoundary);
+ }
+ else
+ {
+ currentRowBoundary = columnBoundaryList_.get(idx);
+ }
+ currentRowBoundary.addTableIndex(tableCounter);
+ }
+ }
+
+ private String getUnitsOfMeasure(final String str)
+ {
+ if (unitsOfMeasure == null || "".equals(unitsOfMeasure))
+ {
+ if (str == null || "".equals(str))
+ {
+ unitsOfMeasure = "cm";
+ return unitsOfMeasure;
+ }
+
+ // build units of measure, set it
+ int i = str.length() - 1;
+ for (; i >= 0; i--)
+ {
+ final char c = str.charAt(i);
+ if (Character.isDigit(c) || c == '.' || c == ',')
+ {
+ break;
+ }
+ }
+ unitsOfMeasure = str.substring(i + 1);
+ }
+ return unitsOfMeasure;
+ }
+
+ private void createTableShapes() throws ReportProcessingException
+ {
+ if (!shapes.isEmpty())
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ // at this point we need to generate the table-columns section based on our boundary table
+ // <table:shapes>
+ // <draw:frame />
+ // ..
+ // </table:shapes>
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.SHAPES, null, XmlWriterSupport.OPEN);
+
+
+ for (int i = 0; i < shapes.size(); i++)
+ {
+ final AttributeMap attrs = shapes.get(i);
+ final AttributeList attrList = buildAttributeList(attrs);
+ attrList.removeAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME);
+ xmlWriter.writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.FRAME, attrList, XmlWriterSupport.OPEN);
+ startChartProcessing(ole.get(i));
+
+ xmlWriter.writeCloseTag();
+ }
+ xmlWriter.writeCloseTag();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+ }
+
+ private void createTableColumns() throws ReportProcessingException
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ // at this point we need to generate the table-columns section based on our boundary table
+ // <table-columns>
+ // <table-column style-name="coX"/>
+ // ..
+ // </table-columns>
+ // the first boundary is '0' which is a placeholder so we will ignore it
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS, null, XmlWriterSupport.OPEN);
+
+ // blow away current column styles
+ // start processing at i=1 because we added a boundary for "0" which is virtual
+ final ColumnBoundary[] cba = getSortedColumnBoundaryArray();
+ for (int i = 1; i < cba.length; i++)
+ {
+ final ColumnBoundary cb = cba[i];
+ float columnWidth = cb.getBoundary();
+ if (i > 1)
+ {
+ columnWidth -= cba[i - 1].getBoundary();
+ }
+ columnWidth = columnWidth / CELL_WIDTH_FACTOR;
+ final OfficeStyle style = deriveStyle(OfficeToken.TABLE_COLUMN, ("co" + i + "_"));
+ final Section tableColumnProperties = new Section();
+ tableColumnProperties.setType("table-column-properties");
+ tableColumnProperties.setNamespace(style.getNamespace());
+ final String width = String.format("%f", columnWidth);
+ tableColumnProperties.setAttribute(style.getNamespace(),
+ "column-width", width + getUnitsOfMeasure(null));
+ style.addNode(tableColumnProperties);
+
+ final AttributeList myAttrList = new AttributeList();
+ myAttrList.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMN, myAttrList, XmlWriterSupport.CLOSE);
+ }
+ xmlWriter.writeCloseTag();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+
+ protected void endOther(final AttributeMap attrs) throws DataSourceException, ReportProcessingException
+ {
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.OBJECT_OLE, attrs) || oleHandled)
+ {
+ oleHandled = false;
+ return;
+ }
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrs) && isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ rowHeights.add(computeRowHeight(styleName));
+ }
+
+ if (isRepeatingSection() || isElementBoundaryCollectionPass())
+ {
+ return;
+ }
+
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ if (isFilteredNamespace(namespace))
+ {
+ return;
+ }
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ if (OfficeNamespaces.DRAWING_NS.equals(namespace) && OfficeToken.FRAME.equals(elementType))
+ {
+ return;
+ }
+
+ // if this is the report namespace, write out a table definition ..
+ if (OfficeNamespaces.TABLE_NS.equals(namespace) && (OfficeToken.TABLE.equals(elementType) || OfficeToken.COVERED_TABLE_CELL.equals(elementType) || OfficeToken.TABLE_COLUMN.equals(elementType) || OfficeToken.TABLE_COLUMNS.equals(elementType)))
+ {
+ return;
+ }
+
+ if (!paragraphHandled && OfficeNamespaces.TEXT_NS.equals(namespace) && OfficeToken.P.equals(elementType))
+ {
+ if (!paragraphHandled)
+ {
+ return;
+ }
+
+ paragraphHandled = false;
+ }
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ // table-cell elements may have a number-columns-spanned attribute which indicates how many
+ // 'covered-table-cell' elements we need to generate
+ generateCoveredTableCells(attrs);
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+
+ private void generateCoveredTableCells(final AttributeMap attrs) throws IOException
+ {
+ if (!ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_CELL, attrs))
+ {
+ return;
+ }
+
+ // do this after we close the tag
+ final XmlWriter xmlWriter = getXmlWriter();
+ // final Object attribute = attrs.getAttribute(OfficeNamespaces.TABLE_NS,NUMBERCOLUMNSSPANNED);
+ // final int span = TextUtilities.parseInt((String) attribute, 0);
+ final int span = currentSpan;
+ currentSpan = 0;
+ for (int i = 1; i < span; i++)
+ {
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.COVERED_TABLE_CELL, null, XmlWriter.CLOSE);
+ }
+ }
+
+ public String getExportDescriptor()
+ {
+ return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_SPREADSHEET;
+ }
+
+ // /////////////////////////////////////////////////////////////////////////
+ public void processText(final String text) throws DataSourceException, ReportProcessingException
+ {
+ if (!(isRepeatingSection() || isElementBoundaryCollectionPass()))
+ {
+ handleParagraph();
+ super.processText(text);
+ }
+ }
+
+ public void processContent(final DataFlags value) throws DataSourceException, ReportProcessingException
+ {
+ if (!(isRepeatingSection() || isElementBoundaryCollectionPass()))
+ {
+ handleParagraph();
+ super.processContent(value);
+ }
+ }
+
+ protected String getStartContent()
+ {
+ return "spreadsheet";
+ }
+
+ protected void startContent(final AttributeMap attrs) throws IOException, DataSourceException,
+ ReportProcessingException
+ {
+ if (!isElementBoundaryCollectionPass())
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, getStartContent(), null, XmlWriterSupport.OPEN);
+
+ writeNullDate();
+
+ final AttributeMap tableAttributes = new AttributeMap();
+ tableAttributes.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.NAMESPACE_ATTRIBUTE, OfficeNamespaces.TABLE_NS);
+ tableAttributes.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.TYPE_ATTRIBUTE, OfficeToken.TABLE);
+ tableAttributes.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, generateInitialTableStyle());
+ tableAttributes.setAttribute(OfficeNamespaces.TABLE_NS, "name", "Report");
+
+ performStyleProcessing(tableAttributes);
+
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE, buildAttributeList(tableAttributes), XmlWriterSupport.OPEN);
+ createTableShapes();
+ createTableColumns();
+ }
+ }
+
+ private String generateInitialTableStyle() throws ReportProcessingException
+ {
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ final OfficeStyles commonStyles = predefStyles.getAutomaticStyles();
+ if (!commonStyles.containsStyle(OfficeToken.TABLE, "Initial_Table"))
+ {
+ final String masterPageName = createMasterPage();
+
+ final OfficeStyle tableStyle = new OfficeStyle();
+ tableStyle.setStyleFamily(OfficeToken.TABLE);
+ tableStyle.setStyleName("Initial_Table");
+ tableStyle.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
+ final Element tableProperties = produceFirstChild(tableStyle, OfficeNamespaces.STYLE_NS, "table-properties");
+ tableProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, TRANSPARENT);
+ commonStyles.addStyle(tableStyle);
+ }
+ return "Initial_Table";
+ }
+
+ private String createMasterPage() throws ReportProcessingException
+ {
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ final MasterPageFactory masterPageFactory = new MasterPageFactory(predefStyles.getMasterStyles());
+ final OfficeMasterPage masterPage;
+ if (!masterPageFactory.containsMasterPage("Standard", null, null))
+ {
+ masterPage = masterPageFactory.createMasterPage("Standard", null, null);
+
+ final CSSNumericValue zeroLength = CSSNumericValue.createValue(CSSNumericType.CM, 0);
+ final String pageLayoutTemplate = masterPage.getPageLayout();
+ if (pageLayoutTemplate == null)
+ {
+ // there is no pagelayout. Create one ..
+ final String derivedLayout = masterPageFactory.createPageStyle(getGlobalStylesCollection().getAutomaticStyles(), zeroLength, zeroLength);
+ masterPage.setPageLayout(derivedLayout);
+ }
+ else
+ {
+ final String derivedLayout = masterPageFactory.derivePageStyle(pageLayoutTemplate,
+ getPredefinedStylesCollection().getAutomaticStyles(),
+ getGlobalStylesCollection().getAutomaticStyles(), zeroLength, zeroLength);
+ masterPage.setPageLayout(derivedLayout);
+ }
+
+ final OfficeStylesCollection officeStylesCollection = getGlobalStylesCollection();
+ final OfficeMasterStyles officeMasterStyles = officeStylesCollection.getMasterStyles();
+ officeMasterStyles.addMasterPage(masterPage);
+ }
+ else
+ {
+ masterPage = masterPageFactory.getMasterPage("Standard", null, null);
+ }
+ return masterPage.getStyleName();
+ }
+
+ protected void endContent(final AttributeMap attrs) throws IOException, DataSourceException,
+ ReportProcessingException
+ {
+ // todo
+ if (!isElementBoundaryCollectionPass())
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ xmlWriter.writeCloseTag();
+ }
+ }
+
+ public void endReport(final ReportStructureRoot report) throws DataSourceException, ReportProcessingException
+ {
+ super.endReport(report);
+ setElementBoundaryCollectionPass(false);
+ resetTableCounter();
+ columnCounter = 0;
+ copyMeta();
+ }
+
+ private boolean isElementBoundaryCollectionPass()
+ {
+ return elementBoundaryCollectionPass;
+ }
+
+ private void setElementBoundaryCollectionPass(final boolean elementBoundaryCollectionPass)
+ {
+ this.elementBoundaryCollectionPass = elementBoundaryCollectionPass;
+ }
+
+ private ColumnBoundary[] getSortedColumnBoundaryArray()
+ {
+ if (sortedBoundaryArray == null)
+ {
+ getColumnBoundaryList().add(new ColumnBoundary(0));
+ sortedBoundaryArray = getColumnBoundaryList().toArray(new ColumnBoundary[getColumnBoundaryList().size()]);
+ Arrays.sort(sortedBoundaryArray);
+ }
+ return sortedBoundaryArray;
+ }
+
+ private List<ColumnBoundary> getColumnBoundaryList()
+ {
+ return columnBoundaryList;
+ }
+
+ private void addColumnWidthToRowBoundaryMarker(final long width)
+ {
+ currentRowBoundaryMarker += width;
+ }
+
+ private long getCurrentRowBoundaryMarker()
+ {
+ return currentRowBoundaryMarker;
+ }
+
+ private void resetTableCounter()
+ {
+ tableCounter = 0;
+ }
+
+ private void resetCurrentRowBoundaryMarker()
+ {
+ currentRowBoundaryMarker = 0;
+ }
+
+ private ColumnBoundary[] getBoundariesForTable(final int table)
+ {
+ if (boundariesForTableArray == null)
+ {
+ final List<ColumnBoundary> boundariesForTable = new ArrayList<ColumnBoundary>();
+ final List<ColumnBoundary> boundaryList = getColumnBoundaryList();
+ for (int i = 0; i < boundaryList.size(); i++)
+ {
+ final ColumnBoundary b = boundaryList.get(i);
+ if (b.isContainedByTable(table))
+ {
+ boundariesForTable.add(b);
+ }
+ }
+ boundariesForTableArray = boundariesForTable.toArray(new ColumnBoundary[boundariesForTable.size()]);
+ Arrays.sort(boundariesForTableArray);
+ }
+ return boundariesForTableArray;
+ }
+
+ private int getColumnSpanForCell(final int table, final int col, final int initialColumnSpan)
+ {
+ final ColumnBoundary[] globalBoundaries = getSortedColumnBoundaryArray();
+ final ColumnBoundary[] tableBoundaries = getBoundariesForTable(table);
+ // how many column boundaries in the globalBoundaries list fall between the currentRowWidth and the next boundary
+ // for the current row
+
+ float cellBoundary = tableBoundaries[col - 1].getBoundary();
+ float cellWidth = tableBoundaries[col - 1].getBoundary();
+
+ if (col > 1)
+ {
+ cellWidth = cellWidth - tableBoundaries[col - 2].getBoundary();
+ }
+
+ if (initialColumnSpan > 1)
+ {
+ // ok we've got some additional spanning specified on the input
+ final int index = (col - 1) + (initialColumnSpan - 1);
+ cellWidth += tableBoundaries[index].getBoundary() - tableBoundaries[col - 1].getBoundary();
+ cellBoundary = tableBoundaries[index].getBoundary();
+ }
+
+ int beginBoundaryIndex = 0;
+ int endBoundaryIndex = globalBoundaries.length - 1;
+ for (int i = 0; i < globalBoundaries.length; i++)
+ {
+ // find beginning boundary
+ if (globalBoundaries[i].getBoundary() <= cellBoundary - cellWidth)
+ {
+ beginBoundaryIndex = i;
+ }
+ if (globalBoundaries[i].getBoundary() <= cellBoundary)
+ {
+ endBoundaryIndex = i;
+ }
+ }
+ final int span = endBoundaryIndex - beginBoundaryIndex;
+ // span will be zero for the first column, so we adjust it to 1
+ if (span == 0)
+ {
+ return 1;
+ }
+ // System.out.println("table = " + table + " col = " + col + " rowBoundaries.length = " + tableBoundaries.length + "
+ // cellWidth = " + cellWidth + " span = " + span);
+ return span;
+ }
+
+ protected String getTargetMimeType()
+ {
+ return "application/vnd.oasis.opendocument.spreadsheet";
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/text/MasterPageFactory.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/MasterPageFactory.java
new file mode 100644
index 000000000000..4bc0ada7038c
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/MasterPageFactory.java
@@ -0,0 +1,407 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.text;
+
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.model.OfficeMasterPage;
+import org.libreoffice.report.pentaho.model.OfficeMasterStyles;
+import org.libreoffice.report.pentaho.model.OfficeStyles;
+import org.libreoffice.report.pentaho.model.PageLayout;
+import org.libreoffice.report.pentaho.model.RawText;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.structure.Element;
+import org.jfree.report.structure.Section;
+import org.jfree.report.util.AttributeNameGenerator;
+
+
+/**
+ * Todo: Document me!
+ *
+ * @since 14.03.2007
+ */
+public class MasterPageFactory
+{
+
+ private static class MasterPageFactoryKey
+ {
+
+ private final String template;
+ private final String pageHeader;
+ private final String pageFooter;
+
+ public MasterPageFactoryKey(final String template,
+ final String pageHeader,
+ final String pageFooter)
+ {
+ this.template = template;
+ this.pageHeader = pageHeader;
+ this.pageFooter = pageFooter;
+ }
+
+ public boolean equals(final Object o)
+ {
+ if (this != o)
+ {
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ final MasterPageFactoryKey that = (MasterPageFactoryKey) o;
+
+ if (pageFooter != null ? !pageFooter.equals(
+ that.pageFooter) : that.pageFooter != null)
+ {
+ return false;
+ }
+ if (pageHeader != null ? !pageHeader.equals(
+ that.pageHeader) : that.pageHeader != null)
+ {
+ return false;
+ }
+ if (template != null ? !template.equals(
+ that.template) : that.template != null)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int result = (template != null ? template.hashCode() : 0);
+ result = 31 * result + (pageHeader != null ? pageHeader.hashCode() : 0);
+ result = 31 * result + (pageFooter != null ? pageFooter.hashCode() : 0);
+ return result;
+ }
+
+ public String getTemplate()
+ {
+ return template;
+ }
+
+ public String getPageHeader()
+ {
+ return pageHeader;
+ }
+
+ public String getPageFooter()
+ {
+ return pageFooter;
+ }
+ }
+
+ private static class PageLayoutKey
+ {
+
+ private final String templateName;
+ private final CSSNumericValue headerHeight;
+ private final CSSNumericValue footerHeight;
+
+ public PageLayoutKey(final String templateName,
+ final CSSNumericValue headerHeight,
+ final CSSNumericValue footerHeight)
+ {
+ this.templateName = templateName;
+ this.headerHeight = headerHeight;
+ this.footerHeight = footerHeight;
+ }
+
+ public String getTemplateName()
+ {
+ return templateName;
+ }
+
+ public CSSNumericValue getHeaderHeight()
+ {
+ return headerHeight;
+ }
+
+ public CSSNumericValue getFooterHeight()
+ {
+ return footerHeight;
+ }
+
+ public boolean equals(final Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ final PageLayoutKey key = (PageLayoutKey) o;
+
+ if (footerHeight != null ? !footerHeight.equals(
+ key.footerHeight) : key.footerHeight != null)
+ {
+ return false;
+ }
+ if (headerHeight != null ? !headerHeight.equals(
+ key.headerHeight) : key.headerHeight != null)
+ {
+ return false;
+ }
+ return !(templateName != null ? !templateName.equals(
+ key.templateName) : key.templateName != null);
+
+ }
+
+ public int hashCode()
+ {
+ int result;
+ result = (templateName != null ? templateName.hashCode() : 0);
+ result = 31 * result + (headerHeight != null ? headerHeight.hashCode() : 0);
+ result = 31 * result + (footerHeight != null ? footerHeight.hashCode() : 0);
+ return result;
+ }
+ }
+ // todo: Patch the page-layout ...
+ private static final String DEFAULT_PAGE_NAME = "Default";
+ private final OfficeMasterStyles predefinedStyles;
+ private final AttributeNameGenerator masterPageNameGenerator;
+ private final Map<MasterPageFactoryKey,OfficeMasterPage> masterPages;
+ private final AttributeNameGenerator pageLayoutNameGenerator;
+ private final Map<PageLayoutKey,String> pageLayouts;
+
+ public MasterPageFactory(final OfficeMasterStyles predefinedStyles)
+ {
+ this.predefinedStyles = predefinedStyles;
+ this.masterPages = new HashMap<MasterPageFactoryKey,OfficeMasterPage>();
+ this.masterPageNameGenerator = new AttributeNameGenerator();
+ this.pageLayouts = new HashMap<PageLayoutKey,String>();
+ this.pageLayoutNameGenerator = new AttributeNameGenerator();
+ }
+
+ public OfficeMasterPage getMasterPage(final String template,
+ final String pageHeader,
+ final String pageFooter)
+ {
+ final MasterPageFactoryKey key =
+ new MasterPageFactoryKey(template, pageHeader, pageFooter);
+ return masterPages.get(key);
+ }
+
+ public boolean containsMasterPage(final String template,
+ final String pageHeader,
+ final String pageFooter)
+ {
+ final MasterPageFactoryKey key =
+ new MasterPageFactoryKey(template, pageHeader, pageFooter);
+ return masterPages.containsKey(key);
+ }
+
+ public OfficeMasterPage createMasterPage(final String template,
+ final String pageHeader,
+ final String pageFooter)
+ {
+ final MasterPageFactoryKey key =
+ new MasterPageFactoryKey(template, pageHeader, pageFooter);
+ final OfficeMasterPage cached = masterPages.get(key);
+ if (cached != null)
+ {
+ return cached;
+ }
+
+ final String targetName = (masterPages.isEmpty()) ? "Standard" : template;
+
+ OfficeMasterPage predef = predefinedStyles.getMasterPage(template);
+ if (predef == null)
+ {
+ // This is a 'magic' name ..
+ // todo: It could be that this should be called 'Standard' instead
+ predef = predefinedStyles.getMasterPage(MasterPageFactory.DEFAULT_PAGE_NAME);
+ }
+
+ if (predef != null)
+ {
+ try
+ {
+ // derive
+ final OfficeMasterPage derived = (OfficeMasterPage) predef.clone();
+ return setupMasterPage(derived, targetName, pageHeader, pageFooter,
+ key);
+ }
+ catch (CloneNotSupportedException cne)
+ {
+ throw new IllegalStateException("Implementation error: Unable to derive page", cne);
+ }
+ }
+
+ final OfficeMasterPage masterPage = new OfficeMasterPage();
+ masterPage.setNamespace(OfficeNamespaces.STYLE_NS);
+ masterPage.setType("master-page");
+ return setupMasterPage(masterPage, targetName, pageHeader, pageFooter, key);
+ }
+
+ private OfficeMasterPage setupMasterPage(final OfficeMasterPage derived,
+ final String targetName,
+ final String pageHeader,
+ final String pageFooter,
+ final MasterPageFactoryKey key)
+ {
+ derived.setStyleName(masterPageNameGenerator.generateName(targetName));
+ masterPages.put(key, derived);
+
+ if (pageHeader != null)
+ {
+ final Section header = new Section();
+ header.setNamespace(OfficeNamespaces.STYLE_NS);
+ header.setType("header");
+ header.addNode(new RawText(pageHeader));
+ derived.addNode(header);
+ }
+
+ if (pageFooter != null)
+ {
+ final Section footer = new Section();
+ footer.setNamespace(OfficeNamespaces.STYLE_NS);
+ footer.setType("footer");
+ footer.addNode(new RawText(pageFooter));
+ derived.addNode(footer);
+ }
+
+ return derived;
+ }
+
+ public String createPageStyle(final OfficeStyles commonStyles,
+ final CSSNumericValue headerHeight,
+ final CSSNumericValue footerHeight)
+ {
+ final PageLayoutKey key =
+ new PageLayoutKey(null, headerHeight, footerHeight);
+ final PageLayout derived = new PageLayout();
+ final String name = pageLayoutNameGenerator.generateName("autogenerated");
+ derived.setStyleName(name);
+ commonStyles.addPageStyle(derived);
+
+ if (headerHeight != null)
+ {
+ final Section headerStyle = new Section();
+ headerStyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ headerStyle.setType("header-style");
+ derived.addNode(headerStyle);
+ MasterPageFactory.applyHeaderFooterHeight(headerStyle, headerHeight);
+ }
+
+ if (footerHeight != null)
+ {
+ final Section footerStyle = new Section();
+ footerStyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ footerStyle.setType("footer-style");
+ derived.addNode(footerStyle);
+ MasterPageFactory.applyHeaderFooterHeight(footerStyle, footerHeight);
+ }
+ pageLayouts.put(key, name);
+ return name;
+ }
+
+ public String derivePageStyle(final String pageStyleTemplate,
+ final OfficeStyles predefined,
+ final OfficeStyles commonStyles,
+ final CSSNumericValue headerHeight,
+ final CSSNumericValue footerHeight)
+ throws ReportProcessingException
+ {
+ if (pageStyleTemplate == null)
+ {
+ throw new NullPointerException("A style-name must be given");
+ }
+
+ final PageLayoutKey key =
+ new PageLayoutKey(pageStyleTemplate, headerHeight, footerHeight);
+ final String pageLayoutName = pageLayouts.get(key);
+ if (pageLayoutName != null)
+ {
+ // there's already a suitable version included.
+ return pageLayoutName;
+ }
+
+ final PageLayout original = predefined.getPageStyle(pageStyleTemplate);
+ if (original == null)
+ {
+ throw new ReportProcessingException("Invalid page-layout '" + pageStyleTemplate + "', will not continue.");
+ }
+
+ try
+ {
+ final PageLayout derived = (PageLayout) original.clone();
+ final String name = pageLayoutNameGenerator.generateName(
+ pageStyleTemplate);
+ derived.setStyleName(name);
+ commonStyles.addPageStyle(derived);
+
+ if (headerHeight != null)
+ {
+ Section headerStyle = derived.getHeaderStyle();
+ if (headerStyle == null)
+ {
+ headerStyle = new Section();
+ headerStyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ headerStyle.setType("header-style");
+ derived.addNode(headerStyle);
+ }
+ MasterPageFactory.applyHeaderFooterHeight(headerStyle, headerHeight);
+ }
+
+ if (footerHeight != null)
+ {
+ Section footerStyle = derived.getFooterStyle();
+ if (footerStyle == null)
+ {
+ footerStyle = new Section();
+ footerStyle.setNamespace(OfficeNamespaces.STYLE_NS);
+ footerStyle.setType("footer-style");
+ derived.addNode(footerStyle);
+ }
+
+ MasterPageFactory.applyHeaderFooterHeight(footerStyle, footerHeight);
+ }
+ pageLayouts.put(key, name);
+ return name;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new IllegalStateException("Clone failed.", e);
+ }
+ }
+
+ private static void applyHeaderFooterHeight(final Section headerFooterStyle,
+ final CSSNumericValue style)
+ {
+ Element headerFooterProps = headerFooterStyle.findFirstChild(OfficeNamespaces.STYLE_NS, "header-footer-properties");
+ if (headerFooterProps == null)
+ {
+ headerFooterProps = new Section();
+ headerFooterProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ headerFooterProps.setType("header-footer-properties");
+ headerFooterStyle.addNode(headerFooterProps);
+ }
+ headerFooterProps.setAttribute(OfficeNamespaces.SVG_NS, "height", style.getValue() + style.getType().getType());
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageBreakDefinition.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageBreakDefinition.java
new file mode 100644
index 000000000000..e92e644eca4f
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageBreakDefinition.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.text;
+
+/**
+ * Todo: Document me!
+ *
+ * @since 24.03.2007
+ */
+public class PageBreakDefinition
+{
+
+ private final boolean resetPageNumber;
+
+ public PageBreakDefinition(final boolean resetPageNumber)
+ {
+ this.resetPageNumber = resetPageNumber;
+ }
+
+ public boolean isResetPageNumber()
+ {
+ return resetPageNumber;
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageContext.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageContext.java
new file mode 100644
index 000000000000..157dec4bf017
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/PageContext.java
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.text;
+
+import org.libreoffice.report.pentaho.styles.LengthCalculator;
+
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+
+/**
+ * Todo: Document me!
+ *
+ * @since 24.03.2007
+ */
+public class PageContext
+{
+
+ public static final int KEEP_TOGETHER_OFF = 0;
+ public static final int KEEP_TOGETHER_GROUP = 1;
+ public static final int KEEP_TOGETHER_FIRST_DETAIL = 2;
+ private PageContext parent;
+ private String header;
+ private CSSNumericValue headerHeight;
+ private String footer;
+ private CSSNumericValue footerHeight;
+ private int keepTogether;
+ private Integer columnCount = null;
+ private boolean sectionOpen;
+
+ public PageContext()
+ {
+ this(null);
+ }
+
+ public PageContext(final PageContext parent)
+ {
+ this.parent = parent;
+ if (parent != null)
+ {
+ this.keepTogether = parent.getKeepTogether();
+ }
+ }
+
+ public int getActiveColumns()
+ {
+ PageContext pc = this;
+ while (pc != null)
+ {
+ // TODO: IS this code correct? Why not columnCount = pc.getColumnCount(); ?
+ if (columnCount != null)
+ {
+ return columnCount;
+ }
+ pc = pc.getParent();
+ }
+ return 1;
+ }
+
+ public void setColumnCount(final Integer columnCount)
+ {
+ this.columnCount = columnCount;
+ }
+
+ public Integer getColumnCount()
+ {
+ return columnCount;
+ }
+
+ public String getHeader()
+ {
+ return header;
+ }
+
+ public void setHeader(final String header, final CSSNumericValue height)
+ {
+ this.header = header;
+ this.headerHeight = height;
+ }
+
+ public String getFooter()
+ {
+ return footer;
+ }
+
+ public CSSNumericValue getHeaderHeight()
+ {
+ return headerHeight;
+ }
+
+ public CSSNumericValue getFooterHeight()
+ {
+ return footerHeight;
+ }
+
+ public void setFooter(final String footer, final CSSNumericValue height)
+ {
+ this.footer = footer;
+ this.footerHeight = height;
+ }
+
+ public int getKeepTogether()
+ {
+ return keepTogether;
+ }
+
+ public void setKeepTogether(final int keepTogether)
+ {
+ this.keepTogether = keepTogether;
+ }
+
+ public PageContext getParent()
+ {
+ return parent;
+ }
+
+ public CSSNumericValue getAllFooterSize()
+ {
+ if (parent == null)
+ {
+ return footerHeight;
+ }
+
+ final LengthCalculator lnc = new LengthCalculator();
+ PageContext pc = this;
+ while (pc != null)
+ {
+ lnc.add(pc.getFooterHeight());
+ pc = pc.getParent();
+ }
+ return lnc.getResult();
+ }
+
+ public CSSNumericValue getAllHeaderSize()
+ {
+ if (parent == null)
+ {
+ return headerHeight;
+ }
+
+ final LengthCalculator lnc = new LengthCalculator();
+ PageContext pc = this;
+ while (pc != null)
+ {
+ lnc.add(pc.getHeaderHeight());
+ pc = pc.getParent();
+ }
+ return lnc.getResult();
+ }
+
+ public String getPageFooterContent()
+ {
+ if (parent == null)
+ {
+ return getFooter();
+ }
+
+ final StringBuffer b = new StringBuffer();
+
+ PageContext pc = this;
+ while (pc != null)
+ {
+ final String footer_ = pc.getFooter();
+ if (footer_ != null)
+ {
+ b.append(footer_);
+ }
+ pc = pc.getParent();
+ }
+
+ if (b.length() != 0)
+ {
+ return b.toString();
+ }
+ return null;
+ }
+
+ public String getPageHeaderContent()
+ {
+ if (parent == null)
+ {
+ return getHeader();
+ }
+
+ final StringBuffer b = new StringBuffer();
+ b.append(parent.getPageHeaderContent());
+ b.append(getHeader());
+
+ if (b.length() != 0)
+ {
+ return b.toString();
+ }
+ return null;
+ }
+
+ public boolean isSectionOpen()
+ {
+ return sectionOpen;
+ }
+
+ public void setSectionOpen(final boolean sectionOpen)
+ {
+ this.sectionOpen = sectionOpen;
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportProcessor.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportProcessor.java
new file mode 100644
index 000000000000..b0dd06eab9ff
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportProcessor.java
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.text;
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.pentaho.PentahoFormulaContext;
+
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.data.ReportContextImpl;
+import org.jfree.report.flow.ReportContext;
+import org.jfree.report.flow.ReportJob;
+import org.jfree.report.flow.ReportStructureRoot;
+import org.jfree.report.flow.ReportTarget;
+import org.jfree.report.flow.SinglePassReportProcessor;
+
+import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
+
+/**
+ * Creation-Date: 03.07.2006, 17:08:25
+ *
+ */
+public class TextRawReportProcessor extends SinglePassReportProcessor
+{
+
+ private final OutputRepository outputRepository;
+ private final String targetName;
+ private final InputRepository inputRepository;
+ private final ImageService imageService;
+ private final DataSourceFactory dataSourceFactory;
+
+ public TextRawReportProcessor(final InputRepository inputRepository,
+ final OutputRepository outputRepository,
+ final String targetName,
+ final ImageService imageService,
+ final DataSourceFactory dataSourceFactory)
+ {
+ if (inputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (outputRepository == null)
+ {
+ throw new NullPointerException();
+ }
+ if (targetName == null)
+ {
+ throw new NullPointerException();
+ }
+ if (imageService == null)
+ {
+ throw new NullPointerException();
+ }
+ if (dataSourceFactory == null)
+ {
+ throw new NullPointerException();
+ }
+
+ this.targetName = targetName;
+ this.inputRepository = inputRepository;
+ this.outputRepository = outputRepository;
+ this.imageService = imageService;
+ this.dataSourceFactory = dataSourceFactory;
+ }
+
+ protected ReportTarget createReportTarget(final ReportJob job)
+ throws ReportProcessingException
+ {
+ final ReportStructureRoot report = job.getReportStructureRoot();
+ final ResourceManager resourceManager = report.getResourceManager();
+
+ return new TextRawReportTarget(job, resourceManager, report.getBaseResource(),
+ inputRepository, outputRepository, targetName, imageService, dataSourceFactory);
+ }
+
+ protected ReportContext createReportContext(final ReportJob job,
+ final ReportTarget target)
+ {
+ final ReportContext context = super.createReportContext(job, target);
+ if (context instanceof ReportContextImpl)
+ {
+ final ReportContextImpl impl = (ReportContextImpl) context;
+ impl.setFormulaContext(new PentahoFormulaContext(impl.getFormulaContext(), job.getConfiguration()));
+ }
+ return context;
+ }
+}
+
+
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportTarget.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportTarget.java
new file mode 100644
index 000000000000..9532fcc7a190
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/TextRawReportTarget.java
@@ -0,0 +1,1444 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.text;
+
+
+import org.libreoffice.report.DataSourceFactory;
+import org.libreoffice.report.ImageService;
+import org.libreoffice.report.InputRepository;
+import org.libreoffice.report.OfficeToken;
+import org.libreoffice.report.OutputRepository;
+import org.libreoffice.report.pentaho.OfficeNamespaces;
+import org.libreoffice.report.pentaho.PentahoReportEngineMetaData;
+import org.libreoffice.report.pentaho.layoutprocessor.FormatValueUtility;
+import org.libreoffice.report.pentaho.model.OfficeMasterPage;
+import org.libreoffice.report.pentaho.model.OfficeMasterStyles;
+import org.libreoffice.report.pentaho.model.OfficeStyle;
+import org.libreoffice.report.pentaho.model.OfficeStyles;
+import org.libreoffice.report.pentaho.model.OfficeStylesCollection;
+import org.libreoffice.report.pentaho.model.PageSection;
+import org.libreoffice.report.pentaho.output.OfficeDocumentReportTarget;
+import org.libreoffice.report.pentaho.output.StyleUtilities;
+import org.libreoffice.report.pentaho.styles.LengthCalculator;
+
+import java.io.IOException;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.report.DataSourceException;
+import org.jfree.report.JFreeReportInfo;
+import org.jfree.report.ReportProcessingException;
+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.pentaho.reporting.libraries.base.util.FastStack;
+import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
+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.XmlWriter;
+import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
+
+
+/**
+ * Creation-Date: 03.07.2006, 16:28:00
+ *
+ */
+public class TextRawReportTarget extends OfficeDocumentReportTarget
+{
+
+ private static final String ALWAYS = "always";
+ private static final String KEEP_TOGETHER = "keep-together";
+ private static final String KEEP_WITH_NEXT = "keep-with-next";
+ private static final String MAY_BREAK_BETWEEN_ROWS = "may-break-between-rows";
+ private static final String NAME = "name";
+ private static final String NONE = "none";
+ private static final String NORMAL = "normal";
+ private static final String PARAGRAPH_PROPERTIES = "paragraph-properties";
+ private static final String STANDARD = "Standard";
+ private static final String TABLE_PROPERTIES = "table-properties";
+ private static final String VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT = "variables_paragraph_with_next";
+ private static final String VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT = "variables_paragraph_without_next";
+ private static final int TABLE_LAYOUT_VARIABLES_PARAGRAPH = 0;
+ private static final int TABLE_LAYOUT_SINGLE_DETAIL_TABLE = 2;
+ private static final int CP_SETUP = 0;
+ private static final int CP_FIRST_TABLE = 1;
+ private static final int CP_NEXT_TABLE = 2;
+ // This is the initial state of the detail-band processing. It states, that we are now waiting for a
+ // detail-band to be printed.
+ private static final int DETAIL_SECTION_WAIT = 0;
+ // The first detail section has started.
+ private static final int DETAIL_SECTION_FIRST_STARTED = 1;
+ // The first detail section has been printed.
+ private static final int DETAIL_SECTION_FIRST_PRINTED = 2;
+ // An other detail section has started
+ private static final int DETAIL_SECTION_OTHER_STARTED = 3;
+ // The other detail section has been printed.
+ private static final int DETAIL_SECTION_OTHER_PRINTED = 4;
+ private boolean pageFooterOnReportFooter;
+ private boolean pageFooterOnReportHeader;
+ private boolean pageHeaderOnReportFooter;
+ private boolean pageHeaderOnReportHeader;
+ private int contentProcessingState;
+ private OfficeMasterPage currentMasterPage;
+ private final FastStack activePageContext;
+ private MasterPageFactory masterPageFactory;
+ private LengthCalculator sectionHeight;
+ private String variables;
+ private PageBreakDefinition pageBreakDefinition;
+ private VariablesDeclarations variablesDeclarations;
+ private boolean columnBreakPending;
+ private boolean sectionKeepTogether;
+ private final AttributeNameGenerator sectionNames;
+ private int detailBandProcessingState;
+ private final int tableLayoutConfig;
+ private int expectedTableRowCount;
+ private boolean firstCellSeen;
+
+ public TextRawReportTarget(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, inputRepository, outputRepository, target, imageService, datasourcefactory);
+ activePageContext = new FastStack();
+ this.sectionNames = new AttributeNameGenerator();
+
+ this.tableLayoutConfig = TABLE_LAYOUT_SINGLE_DETAIL_TABLE;
+ }
+
+ protected String getTargetMimeType()
+ {
+ return "application/vnd.oasis.opendocument.text";
+ }
+
+ /**
+ * Checks, whether a manual page break should be inserted at the next possible location.
+ *
+ * @return true, if a pagebreak is pending, false otherwise.
+ */
+ private boolean isPagebreakPending()
+ {
+ return pageBreakDefinition != null;
+ }
+
+ private boolean isResetPageNumber()
+ {
+ return pageBreakDefinition != null && pageBreakDefinition.isResetPageNumber();
+ }
+
+ /**
+ * Defines, whether a manual pagebreak should be inserted at the next possible location.
+ *
+ * @param pageBreakDefinition the new flag value.
+ */
+ private void setPagebreakDefinition(final PageBreakDefinition pageBreakDefinition)
+ {
+ this.pageBreakDefinition = pageBreakDefinition;
+ }
+
+ private PageBreakDefinition getPagebreakDefinition()
+ {
+ return pageBreakDefinition;
+ }
+
+ // todo
+ private boolean isKeepTableWithNext()
+ {
+ final int keepTogetherState = getCurrentContext().getKeepTogether();
+ if (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP)
+ {
+ return true;
+ }
+
+ final boolean keepWithNext;
+ keepWithNext = keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL && (detailBandProcessingState == DETAIL_SECTION_WAIT);
+ return keepWithNext;
+ }
+
+ private boolean isSectionPagebreakAfter(final AttributeMap attrs)
+ {
+ final Object forceNewPage =
+ attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page");
+ return "after-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage);
+ }
+
+ private boolean isSectionPagebreakBefore(final AttributeMap attrs)
+ {
+ final Object forceNewPage =
+ attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page");
+ return "before-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage);
+ }
+
+ private PageContext getCurrentContext()
+ {
+ return (PageContext) activePageContext.peek();
+ }
+
+ private String createMasterPage(final boolean printHeader,
+ final boolean printFooter)
+ throws ReportProcessingException
+ {
+ // create the master page for the report-header.
+ // If there is a page-header or footer in the report that gets
+ // surpressed on the report-header, we have to insert a pagebreak
+ // afterwards.
+
+ final String activePageFooter;
+ // Check, whether the report header can have a page-header
+ final PageContext context = getCurrentContext();
+ if (printFooter)
+ {
+ activePageFooter = context.getPageFooterContent();
+ }
+ else
+ {
+ activePageFooter = null;
+ }
+ final String activePageHeader;
+ if (printHeader)
+ {
+ // we have to insert a manual pagebreak after the report header.
+ activePageHeader = context.getPageHeaderContent();
+ }
+ else
+ {
+ activePageHeader = null;
+ }
+
+ final String masterPageName;
+ if (currentMasterPage == null || !masterPageFactory.containsMasterPage(STANDARD, activePageHeader, activePageFooter))
+ {
+
+ final CSSNumericValue headerSize = context.getAllHeaderSize();
+ final CSSNumericValue footerSize = context.getAllFooterSize();
+
+
+ currentMasterPage = masterPageFactory.createMasterPage(STANDARD, activePageHeader, activePageFooter);
+
+// LOGGER.debug("Created a new master-page: " + currentMasterPage.getStyleName());
+
+ // todo: Store the page-layouts as well.
+ // The page layouts are derived from a common template, but as the
+ // header-heights differ, we have to derive these beasts instead
+ // of copying them
+
+ final OfficeStylesCollection officeStylesCollection = getGlobalStylesCollection();
+ final OfficeMasterStyles officeMasterStyles = officeStylesCollection.getMasterStyles();
+ final String pageLayoutTemplate = currentMasterPage.getPageLayout();
+ if (pageLayoutTemplate == null)
+ {
+ // there is no pagelayout. Create one ..
+ final String derivedLayout = masterPageFactory.createPageStyle(getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize);
+ currentMasterPage.setPageLayout(derivedLayout);
+ }
+ else
+ {
+ final String derivedLayout = masterPageFactory.derivePageStyle(pageLayoutTemplate,
+ getPredefinedStylesCollection().getAutomaticStyles(),
+ getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize);
+ currentMasterPage.setPageLayout(derivedLayout);
+ }
+ officeMasterStyles.addMasterPage(currentMasterPage);
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ else
+ {
+ // retrieve the master-page.
+ final OfficeMasterPage masterPage = masterPageFactory.getMasterPage(STANDARD, activePageHeader, activePageFooter);
+ if (ObjectUtilities.equal(masterPage.getStyleName(), currentMasterPage.getStyleName()))
+ {
+ // They are the same,
+ masterPageName = null;
+ }
+ else
+ {
+ // reuse the existing one ..
+ currentMasterPage = masterPage;
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ }
+
+ // if either the pageheader or footer are *not* printed with the
+ // report header, then this implies that we have to insert a manual
+ // pagebreak at the end of the section.
+
+ if ((!printHeader && context.getHeader() != null) || (!printFooter && context.getFooter() != null))
+ {
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+
+ return masterPageName;
+ }
+
+ private boolean isColumnBreakPending()
+ {
+ return columnBreakPending;
+ }
+
+ private void setColumnBreakPending(final boolean columnBreakPending)
+ {
+ this.columnBreakPending = columnBreakPending;
+ }
+
+ private Integer parseInt(final Object value)
+ {
+ if (value instanceof Number)
+ {
+ final Number n = (Number) value;
+ return IntegerCache.getInteger(n.intValue());
+ }
+ if (value instanceof String)
+ {
+ try
+ {
+ return IntegerCache.getInteger(Integer.parseInt((String) value));
+ }
+ catch (NumberFormatException nfe)
+ {
+ //return null; // ignore
+ }
+ }
+ return null;
+ }
+
+ private BufferState applyColumnsToPageBand(final BufferState contents,
+ final int numberOfColumns)
+ throws IOException, ReportProcessingException
+ {
+ if (numberOfColumns <= 1)
+ {
+ return contents;
+ }
+ startBuffering(getGlobalStylesCollection(), true);
+ // derive section style ..
+
+ // This is a rather cheap solution to the problem. In a sane world, we would have to feed the
+ // footer multiple times. Right now, we simply rely on the balacing, which should make sure that
+ // the column's content are evenly distributed.
+ final XmlWriter writer = getXmlWriter();
+ final AttributeList attrs = new AttributeList();
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(numberOfColumns));
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section"));
+ writer.writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN);
+ for (int i = 0; i < numberOfColumns; i++)
+ {
+ writer.writeStream(contents.getXmlAsReader());
+ }
+
+ writer.writeCloseTag();
+ return finishBuffering();
+ }
+
+ private String generateSectionStyle(final int columnCount)
+ {
+ final OfficeStyles automaticStyles = getStylesCollection().getAutomaticStyles();
+ final String styleName = getAutoStyleNameGenerator().generateName("auto_section_style");
+
+ final Section sectionProperties = new Section();
+ sectionProperties.setNamespace(OfficeNamespaces.STYLE_NS);
+ sectionProperties.setType("section-properties");
+ sectionProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent");
+ sectionProperties.setAttribute(OfficeNamespaces.TEXT_NS, "dont-balance-text-columns", OfficeToken.FALSE);
+ sectionProperties.setAttribute(OfficeNamespaces.STYLE_NS, "editable", OfficeToken.FALSE);
+
+ if (columnCount > 1)
+ {
+ final Section columns = new Section();
+ columns.setNamespace(OfficeNamespaces.STYLE_NS);
+ columns.setType("columns");
+ columns.setAttribute(OfficeNamespaces.FO_NS, "column-count", String.valueOf(columnCount));
+ columns.setAttribute(OfficeNamespaces.STYLE_NS, "column-gap", "0cm");
+ sectionProperties.addNode(columns);
+
+// final Section columnSep = new Section();
+// columnSep.setNamespace(OfficeNamespaces.STYLE_NS);
+// columnSep.setType("column-sep");
+// columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "width", "0.035cm");
+// columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "color", "#000000");
+// columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "height", "100%");
+// columns.addNode(columnSep);
+
+ for (int i = 0; i < columnCount; i++)
+ {
+ final Section column = new Section();
+ column.setNamespace(OfficeNamespaces.STYLE_NS);
+ column.setType("column");
+ column.setAttribute(OfficeNamespaces.STYLE_NS, "rel-width", "1*");
+ column.setAttribute(OfficeNamespaces.FO_NS, "start-indent", "0cm");
+ column.setAttribute(OfficeNamespaces.FO_NS, "end-indent", "0cm");
+ columns.addNode(column);
+ }
+ }
+
+ final OfficeStyle style = new OfficeStyle();
+ style.setNamespace(OfficeNamespaces.STYLE_NS);
+ style.setType("style");
+ style.setAttribute(OfficeNamespaces.STYLE_NS, NAME, styleName);
+ style.setAttribute(OfficeNamespaces.STYLE_NS, "family", "section");
+ style.addNode(sectionProperties);
+
+ automaticStyles.addStyle(style);
+ return styleName;
+ }
+
+ /**
+ * 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 org.jfree.report.DataSourceException
+ * if there was an error accessing the datasource
+ * @throws org.jfree.report.ReportProcessingException
+ * if some other error occured.
+ */
+ public void startReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ super.startReport(report);
+ variablesDeclarations = new VariablesDeclarations();
+ detailBandProcessingState = DETAIL_SECTION_WAIT;
+ sectionNames.reset();
+
+ pageFooterOnReportFooter = false;
+ pageFooterOnReportHeader = false;
+ pageHeaderOnReportFooter = false;
+ pageHeaderOnReportHeader = false;
+ contentProcessingState = TextRawReportTarget.CP_SETUP;
+
+ activePageContext.clear();
+ activePageContext.push(new PageContext());
+
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ masterPageFactory = new MasterPageFactory(predefStyles.getMasterStyles());
+
+ predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(true));
+ predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(false));
+ }
+
+ private OfficeStyle createVariablesStyle(final boolean keepWithNext)
+ {
+ final OfficeStyle variablesSectionStyle = new OfficeStyle();
+ variablesSectionStyle.setStyleFamily(OfficeToken.PARAGRAPH);
+ if (keepWithNext)
+ {
+ variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
+ }
+ else
+ {
+ variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT);
+ }
+
+ final Section paragraphProps = new Section();
+ paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ paragraphProps.setType(PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent");
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "text-align", "start");
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-align", "top");
+ variablesSectionStyle.addNode(paragraphProps);
+
+ final Section textProps = new Section();
+ textProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ textProps.setType("text-properties");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-variant", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "text-transform", NONE);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "color", "#ffffff");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-outline", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-style", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-mode", "continuous");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-position", "0% 100%");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-name", "Tahoma");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-size", "1pt");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "letter-spacing", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "letter-kerning", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-style", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "text-shadow", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-style", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-mode", "continuous");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-weight", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-rotation-angle", "0");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-emphasize", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-start-char", "");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-end-char", "");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-scale", "100%");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-relief", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-display", NONE);
+ variablesSectionStyle.addNode(textProps);
+ return variablesSectionStyle;
+ }
+
+ protected void startContent(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "text", null, XmlWriterSupport.OPEN);
+
+ writeNullDate();
+
+ // now start the buffering. We have to insert the variables declaration
+ // later ..
+ startBuffering(getStylesCollection(), true);
+
+ final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count");
+ final Integer colCount = parseInt(columnCountRaw);
+ if (colCount != null)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setColumnCount(colCount);
+ }
+
+ }
+
+ protected void startOther(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+
+ if (ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace))
+ {
+ if (ObjectUtilities.equal(OfficeToken.IMAGE, elementType))
+ {
+ startImageProcessing(attrs);
+ }
+ else if (ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType) && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ startChartProcessing(attrs);
+ }
+ return;
+ }
+ else if (isFilteredNamespace(namespace))
+ {
+ throw new IllegalStateException("This element should be hidden: " + namespace + ", " + elementType);
+ }
+
+ if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace) && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType))
+ {
+ // Skip the columns section if the tables get merged..
+ startBuffering(getStylesCollection(), true);
+ }
+ else
+ {
+ openSection();
+
+ final boolean isTableNS = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace);
+ if (isTableNS)
+ {
+ if (ObjectUtilities.equal(OfficeToken.TABLE, elementType))
+ {
+ startTable(attrs);
+ return;
+ }
+
+ if (ObjectUtilities.equal(OfficeToken.TABLE_ROW, elementType))
+ {
+ startRow(attrs);
+ return;
+ }
+ }
+
+
+ if (ObjectUtilities.equal(OfficeNamespaces.TEXT_NS, namespace))
+ {
+ if (ObjectUtilities.equal("variable-set", elementType))
+ {
+ // update the variables-declaration thingie ..
+ final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME);
+ final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE);
+ final String newVarName = variablesDeclarations.produceVariable(varName, varType);
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName);
+ }
+ else if (ObjectUtilities.equal("variable-get", elementType))
+ {
+ final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME);
+ final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE);
+ final String newVarName = variablesDeclarations.produceVariable(varName, varType);
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName);
+ // this one must not be written, as the DTD does not declare it.
+ // attrs.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, null);
+ }
+ }
+
+ if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null)
+ {
+ // This cannot happen as long as the report sections only contain tables. But at some point in the
+ // future they will be made of paragraphs, and then we are prepared ..
+ // LOGGER.debug("Variables-Section in own paragraph " + variables);
+
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN);
+ xmlWriter.writeText(variables);
+ xmlWriter.writeCloseTag();
+ variables = null;
+ }
+
+ final boolean keepTogetherOnParagraph = true;
+
+ if (keepTogetherOnParagraph)
+ {
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs))
+ {
+ final int keepTogetherState = getCurrentContext().getKeepTogether();
+ if (!firstCellSeen && (sectionKeepTogether || keepTogetherState == PageContext.KEEP_TOGETHER_GROUP))
+ {
+ OfficeStyle style = null;
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME);
+ if (styleName == null)
+ {
+ final boolean keep = (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether();
+ final ArrayList<String> propertyNameSpaces = new ArrayList<String>();
+ final ArrayList<String> propertyNames = new ArrayList<String>();
+ final ArrayList<String> propertyValues = new ArrayList<String>();
+
+ propertyNameSpaces.add(OfficeNamespaces.FO_NS);
+ propertyNameSpaces.add(OfficeNamespaces.FO_NS);
+ propertyNames.add(KEEP_TOGETHER);
+ propertyValues.add(ALWAYS);
+ if (keep)
+ {
+ propertyNames.add(KEEP_WITH_NEXT);
+ propertyValues.add(ALWAYS);
+ }
+ else
+ {
+ propertyNames.add(KEEP_WITH_NEXT);
+ propertyValues.add(null);
+ }
+ style = StyleUtilities.queryStyleByProperties(getStylesCollection(), OfficeToken.PARAGRAPH, PARAGRAPH_PROPERTIES, propertyNameSpaces, propertyNames, propertyValues);
+ }
+ if (style == null)
+ {
+ style = deriveStyle(OfficeToken.PARAGRAPH, styleName);
+ // Lets set the 'keep-together' flag..
+
+ Element paragraphProps = style.getParagraphProperties();
+ if (paragraphProps == null)
+ {
+ paragraphProps = new Section();
+ paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ paragraphProps.setType(PARAGRAPH_PROPERTIES);
+ style.addNode(paragraphProps);
+ }
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS);
+
+ // We prevent pagebreaks within the two adjacent rows (this one and the next one) if
+ // either a group-wide keep-together is defined or if we haven't reached the end of the
+ // current section yet.
+ if ((keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether())
+ {
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ }
+ }
+
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ }
+ }
+ }
+
+ if (ObjectUtilities.equal(OfficeNamespaces.DRAWING_NS, namespace) && ObjectUtilities.equal(OfficeToken.FRAME, elementType))
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME);
+ final OfficeStyle predefAutoStyle = getPredefinedStylesCollection().getAutomaticStyles().getStyle(OfficeToken.GRAPHIC, styleName);
+ if (predefAutoStyle != null)
+ {
+ // special ole handling
+ final Element graphicProperties = predefAutoStyle.getGraphicProperties();
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", "paragraph-content");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", "paragraph");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "flow-with-text", "false");
+ graphicProperties.setAttribute(OfficeNamespaces.DRAWING_NS, "ole-draw-aspect", "1");
+
+ // attrs.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, predefAutoStyle.getStyleName());
+ }
+ }
+
+ // process the styles as usual
+ performStyleProcessing(attrs);
+ final XmlWriter xmlWriter = getXmlWriter();
+ final AttributeList attrList = buildAttributeList(attrs);
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN);
+
+ if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH
+ && variables != null
+ && !isRepeatingSection()
+ && ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs))
+ {
+ //LOGGER.debug("Variables-Section in existing cell " + variables);
+ xmlWriter.writeText(variables);
+ variables = null;
+ }
+ }
+ }
+
+ private void startRow(final AttributeMap attrs)
+ throws IOException, ReportProcessingException
+ {
+ firstCellSeen = false;
+ expectedTableRowCount -= 1;
+ final String rowStyle = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ final CSSNumericValue rowHeight = computeRowHeight(rowStyle);
+ // LOGGER.debug("Adding row-Style: " + rowStyle + " " + rowHeight);
+ sectionHeight.add(rowHeight);
+
+// if (expectedTableRowCount > 0)
+// {
+// // Some other row. Create a keep-together
+//
+// }
+// else
+// {
+// // This is the last row before the section will end.
+// // or (in some weird cases) There is no information when the row will end.
+// // Anyway, if we are here, we do not create a keep-together style on the table-row ..
+// }
+ // process the styles as usual
+ performStyleProcessing(attrs);
+
+ final AttributeList attrList = buildAttributeList(attrs);
+ getXmlWriter().writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrList, XmlWriterSupport.OPEN);
+ }
+
+ private void startTable(final AttributeMap attrs)
+ throws ReportProcessingException, IOException
+ {
+ final Integer trc = (Integer) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "table-row-count");
+ if (trc == null)
+ {
+ expectedTableRowCount = -1;
+ }
+ else
+ {
+ expectedTableRowCount = trc;
+ }
+
+ if (isSectionPagebreakBefore(attrs))
+ {
+ // force a pagebreak ..
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+
+ // its a table. This means, it is a root-level element
+ final PageBreakDefinition breakDefinition;
+ String masterPageName = null;
+ final int currentRole = getCurrentRole();
+ if (contentProcessingState == TextRawReportTarget.CP_FIRST_TABLE)
+ {
+ contentProcessingState = TextRawReportTarget.CP_NEXT_TABLE;
+
+ // Processing the report header now.
+ if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_HEADER)
+ {
+ breakDefinition = new PageBreakDefinition(isResetPageNumber());
+ masterPageName = createMasterPage(pageHeaderOnReportHeader, pageFooterOnReportHeader);
+ if (masterPageName == null)
+ {
+ // we should always have a master-page ...
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ }
+ else if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_FOOTER)
+ {
+ breakDefinition = new PageBreakDefinition(isResetPageNumber());
+ masterPageName = createMasterPage(pageHeaderOnReportFooter, pageFooterOnReportFooter);
+ if (masterPageName == null && isSectionPagebreakBefore(attrs))
+ {
+ // If we have a manual pagebreak, then activate the current master-page again.
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ // But we skip this (and therefore the resulting pagebreak) if there is no manual break
+ // and no other condition that would force an break.
+ }
+ else if (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ breakDefinition = null;
+ // no pagebreaks ..
+ }
+ else if (currentMasterPage == null || isPagebreakPending())
+ {
+ // Must be the first table, as we have no master-page yet.
+ masterPageName = createMasterPage(true, true);
+ setPagebreakDefinition(null);
+ if (masterPageName == null)
+ {
+ // we should always have a master-page ...
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ breakDefinition = new PageBreakDefinition(isResetPageNumber());
+ }
+ else
+ {
+ breakDefinition = null;
+ }
+ }
+ else if (isPagebreakPending() && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ // Derive an automatic style for the pagebreak.
+// LOGGER.debug("Manual pagebreak (within the section): " + getCurrentRole());
+ breakDefinition = getPagebreakDefinition();
+ setPagebreakDefinition(null);
+ masterPageName = createMasterPage(true, true);
+ if (masterPageName == null || isSectionPagebreakBefore(attrs))
+ {
+ // If we have a manual pagebreak, then activate the current master-page again.
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ }
+ else
+ {
+ breakDefinition = null;
+ }
+
+ final XmlWriter xmlWriter = getXmlWriter();
+ if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && masterPageName != null)
+ {
+ // close the last table-tag, we will open a new one
+ xmlWriter.writeCloseTag();
+ // Reset the detail-state to 'started' so that the table's columns get printed now.
+ detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
+ }
+
+ if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null)
+ {
+ if (masterPageName != null)
+ {
+ // write a paragraph that uses the VARIABLES_HIDDEN_STYLE as
+ // primary style. Derive that one and add the manual pagebreak.
+ // The predefined style already has the 'keep-together' flags set.
+// LOGGER.debug("Variables-Section with new Master-Page " + variables + " " + masterPageName);
+
+ final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
+ style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
+ if (breakDefinition.isResetPageNumber())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
+ }
+ if (isColumnBreakPending())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column");
+ setColumnBreakPending(false);
+ }
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN);
+
+ masterPageName = null;
+ //breakDefinition = null;
+ }
+ else if (isColumnBreakPending())
+ {
+ setColumnBreakPending(false);
+
+ final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
+
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN);
+ }
+ else
+ {
+ // Write a paragraph without adding the pagebreak. We can reuse the global style, but we have to make
+ // sure that the style is part of the current 'auto-style' collection.
+// LOGGER.debug("Variables-Section " + variables);
+
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN);
+ }
+ xmlWriter.writeText(variables);
+ xmlWriter.writeCloseTag();
+ variables = null;
+ }
+
+ final boolean keepWithNext = isKeepTableWithNext();
+ final boolean localKeepTogether = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER));
+ final boolean tableMergeActive = isTableMergeActive();
+ this.sectionKeepTogether = tableMergeActive && localKeepTogether;
+
+ // Check, whether we have a reason to derive a style...
+ if (masterPageName != null || (!tableMergeActive && (localKeepTogether || keepWithNext)) || isColumnBreakPending())
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ final OfficeStyle style = deriveStyle("table", styleName);
+
+ if (masterPageName != null)
+ {
+// LOGGER.debug("Starting a new MasterPage: " + masterPageName);
+ // Patch the current styles.
+ // This usually only happens on Table-Styles or Paragraph-Styles
+ style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
+ if (breakDefinition.isResetPageNumber())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
+ }
+ }
+ if (isColumnBreakPending())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column");
+ setColumnBreakPending(false);
+ }
+
+ // Inhibit breaks inside the table only if it has been defined and if we do not create one single
+ // big detail section. In that case, this flag would be invalid and would cause layout-errors.
+ if (!tableMergeActive)
+ {
+ if (localKeepTogether)
+ {
+ final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
+ tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
+ }
+ }
+ else
+ {
+ if (detailBandProcessingState == DETAIL_SECTION_WAIT)
+ {
+ detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED;
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED)
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
+ }
+ }
+ if (keepWithNext)
+ {
+ boolean addKeepWithNext = true;
+ if (currentRole == ROLE_GROUP_FOOTER)
+ {
+ addKeepWithNext = isParentKeepTogether();
+ }
+
+ final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
+ tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
+ if (addKeepWithNext)
+ {
+ tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ // A keep-with-next does not work, if the may-break-betweek rows is not set to false ..
+ }
+ }
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ // no need to copy the styles, this was done while deriving the
+ // style ..
+ }
+ else
+ {
+ // Check, whether we may be able to skip the table.
+ if (tableMergeActive)
+ {
+ if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED)
+ {
+ // Skip the whole thing ..
+ return;
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_WAIT)
+ {
+ if (keepWithNext)
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+
+ final OfficeStyle style = deriveStyle(OfficeToken.TABLE, styleName);
+ final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
+ // A keep-with-next does not work, if the may-break-betweek rows is not set to false ..
+ tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
+ final String hasGroupFooter = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "has-group-footer");
+ if (hasGroupFooter != null && hasGroupFooter.equals(OfficeToken.TRUE))
+ {
+ tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ }
+
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ }
+ detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED;
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED)
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
+ }
+ }
+
+ // process the styles as usual
+ performStyleProcessing(attrs);
+ }
+
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ final AttributeList attrList = buildAttributeList(attrs);
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN);
+ }
+
+ private boolean isParentKeepTogether()
+ {
+ PageContext context = getCurrentContext();
+ if (context != null)
+ {
+ context = context.getParent();
+ if (context != null)
+ {
+ return context.getKeepTogether() == PageContext.KEEP_TOGETHER_GROUP;
+ }
+ }
+ return false;
+ }
+
+ private boolean isTableMergeActive()
+ {
+ return getCurrentRole() == ROLE_DETAIL && tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE;
+ }
+
+ private void openSection()
+ throws IOException
+ {
+ if (isRepeatingSection())
+ {
+ // repeating sections have other ways of defining columns ..
+ return;
+ }
+ if (getCurrentRole() == ROLE_TEMPLATE || getCurrentRole() == ROLE_SPREADSHEET_PAGE_HEADER || getCurrentRole() == ROLE_SPREADSHEET_PAGE_FOOTER)
+ {
+ // the template section would break the multi-column stuff and we dont open up sections there
+ // anyway ..
+ return;
+ }
+
+ final PageContext pageContext = getCurrentContext();
+ final Integer columnCount = pageContext.getColumnCount();
+ if (columnCount != null && !pageContext.isSectionOpen())
+ {
+ final AttributeList attrs = new AttributeList();
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(columnCount));
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section"));
+ getXmlWriter().writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN);
+
+ pageContext.setSectionOpen(true);
+ }
+
+ }
+
+ protected void startReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ sectionHeight = new LengthCalculator();
+ if (role == OfficeDocumentReportTarget.ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER)
+ {
+ // Start buffering with an dummy styles-collection, so that the global styles dont get polluted ..
+ startBuffering(new OfficeStylesCollection(), true);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER)
+ {
+ startBuffering(getGlobalStylesCollection(), true);
+ pageHeaderOnReportHeader = PageSection.isPrintWithReportHeader(attrs);
+ pageHeaderOnReportFooter = PageSection.isPrintWithReportFooter(attrs);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER)
+ {
+ startBuffering(getGlobalStylesCollection(), true);
+ pageFooterOnReportHeader = PageSection.isPrintWithReportHeader(attrs);
+ pageFooterOnReportFooter = PageSection.isPrintWithReportFooter(attrs);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ startBuffering(getGlobalStylesCollection(), true);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES)
+ {
+ startBuffering(getGlobalStylesCollection(), false);
+ }
+ else
+ {
+ contentProcessingState = TextRawReportTarget.CP_FIRST_TABLE;
+ if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER)
+ {
+ // if we have a repeating header, then skip the first one ..
+ // if this is a repeating footer, skip the last one. This means,
+ // we have to buffer all group footers and wait for the next section..
+ startBuffering(getContentStylesCollection(), true);
+ }
+
+ if (role != OfficeDocumentReportTarget.ROLE_DETAIL)
+ {
+ // reset the detail-state. The flag will be updated on startTable and endOther(Table) if the
+ // current role is ROLE_DETAIL
+ detailBandProcessingState = DETAIL_SECTION_WAIT;
+ }
+ }
+ }
+
+ protected void startGroup(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ super.startGroup(attrs);
+ final PageContext pageContext = new PageContext(getCurrentContext());
+ activePageContext.push(pageContext);
+
+ final Object resetPageNumber = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "reset-page-number");
+ if (OfficeToken.TRUE.equals(resetPageNumber))
+ {
+ setPagebreakDefinition(new PageBreakDefinition(true));
+ }
+
+ final Object keepTogether = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER);
+ if ("whole-group".equals(keepTogether))
+ {
+ pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_GROUP);
+ }
+ else if ("with-first-detail".equals(keepTogether) && pageContext.getKeepTogether() != PageContext.KEEP_TOGETHER_GROUP)
+ {
+ pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_FIRST_DETAIL);
+ }
+
+ final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count");
+ final Integer colCount = parseInt(columnCountRaw);
+ if (colCount != null)
+ {
+ pageContext.setColumnCount(colCount);
+ }
+
+ final Object newColumn = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "start-new-column");
+ if (OfficeToken.TRUE.equals(newColumn))
+ {
+ setColumnBreakPending(true);
+ }
+ }
+
+ protected void startGroupInstance(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (getGroupContext().isGroupWithRepeatingSection())
+ {
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+ }
+
+ protected void endGroup(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (getGroupContext().isGroupWithRepeatingSection())
+ {
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+
+ super.endGroup(attrs);
+ finishSection();
+
+ activePageContext.pop();
+ }
+
+ private void finishSection()
+ throws ReportProcessingException
+ {
+ final PageContext pageContext = getCurrentContext();
+ if (pageContext.isSectionOpen())
+ {
+ pageContext.setSectionOpen(false);
+ try
+ {
+ getXmlWriter().writeCloseTag();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException("IOError", e);
+ }
+ }
+ }
+
+ protected void endReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (role == ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER)
+ {
+ finishBuffering();
+ return;
+ }
+
+ final CSSNumericValue result = sectionHeight.getResult();
+ if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES)
+ {
+ if (variables == null)
+ {
+ variables = finishBuffering().getXmlBuffer();
+ }
+ else
+ {
+ variables += finishBuffering().getXmlBuffer();
+ }
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER)
+ {
+ final String headerText = finishBuffering().getXmlBuffer();
+ final int iterationCount = getGroupContext().getParent().getIterationCount();
+ final boolean repeat = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "repeat-section"));
+ if (!repeat || iterationCount > 0)
+ {
+ getXmlWriter().writeText(headerText);
+ }
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER)
+ {
+ final String footerText = finishBuffering().getXmlBuffer();
+ // how do we detect whether this is the last group footer?
+ getXmlWriter().writeText(footerText);
+ }
+
+ }
+
+ public void endReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ super.endReport(report);
+ variablesDeclarations = null;
+
+ try
+ {
+ // Write the settings ..
+ final AttributeList rootAttributes = new AttributeList();
+ rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS);
+ rootAttributes.addNamespaceDeclaration("config", OfficeNamespaces.CONFIG);
+ rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS);
+ rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version",
+ OfficeDocumentReportTarget.ODF_VERSION);
+ final OutputStream outputStream = getOutputRepository().createOutputStream("settings.xml", "text/xml");
+ final XmlWriter xmlWriter = new XmlWriter(new OutputStreamWriter(outputStream, "UTF-8"), createTagDescription());
+ xmlWriter.setAlwaysAddNamespace(true);
+ xmlWriter.writeXmlDeclaration("UTF-8");
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-settings", rootAttributes, XmlWriterSupport.OPEN);
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "settings", XmlWriterSupport.OPEN);
+ xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item-set", NAME, "ooo:configuration-settings", XmlWriterSupport.OPEN);
+
+ final AttributeList configAttributes = new AttributeList();
+ configAttributes.setAttribute(OfficeNamespaces.CONFIG, NAME, "TableRowKeep");
+ configAttributes.setAttribute(OfficeNamespaces.CONFIG, "type", "boolean");
+ xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item", configAttributes, XmlWriterSupport.OPEN);
+ xmlWriter.writeText(OfficeToken.TRUE);
+ xmlWriter.writeCloseTag();
+
+ xmlWriter.writeCloseTag();
+ xmlWriter.writeCloseTag();
+ xmlWriter.writeCloseTag();
+ xmlWriter.close();
+
+ copyMeta();
+ }
+ catch (IOException ioe)
+ {
+ throw new ReportProcessingException("Failed to write settings document");
+ }
+ }
+
+ protected void endOther(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+
+ final boolean isInternalNS = ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace);
+ final boolean isTableNs = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace);
+ if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType))
+ {
+ finishBuffering();
+ return;
+ }
+
+ if (isInternalNS && (ObjectUtilities.equal(OfficeToken.IMAGE, elementType) || ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType)))
+ {
+ return;
+ }
+
+ final XmlWriter xmlWriter = getXmlWriter();
+ if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) && !isRepeatingSection())
+ {
+ if (variables != null)
+ {
+ // This cannot happen as long as the report sections only contain tables. But at some point in the
+ // future they will be made of paragraphs, and then we are prepared ..
+ //LOGGER.debug("Variables-Section " + variables);
+ final String tag;
+ if (sectionKeepTogether && expectedTableRowCount > 0)
+ {
+ tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT;
+ }
+ else
+ {
+ tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT;
+ }
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ tag, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ tag, XmlWriterSupport.OPEN);
+ xmlWriter.writeText(variables);
+ xmlWriter.writeCloseTag();
+ variables = null;
+ }
+ /**
+ // Only generate the empty paragraph, if we have to add the keep-together ..
+ else if (cellEmpty && expectedTableRowCount > 0 &&
+ sectionKeepTogether && !firstCellSeen)
+ {
+ // we have no variables ..
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.CLOSE);
+ }
+ */
+ }
+
+ if (isTableNs && (ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) || ObjectUtilities.equal(OfficeToken.COVERED_TABLE_CELL, elementType)))
+ {
+ firstCellSeen = true;
+ }
+ if (isTableNs && ObjectUtilities.equal(OfficeToken.TABLE, elementType))
+ {
+ if (getCurrentRole() == ROLE_DETAIL)
+ {
+ if (!isTableMergeActive())
+ {
+ // We do not merge the detail bands, so an ordinary close will do.
+ xmlWriter.writeCloseTag();
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_FIRST_STARTED)
+ {
+ final int keepTogetherState = getCurrentContext().getKeepTogether();
+ if (keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL)
+ {
+ xmlWriter.writeCloseTag();
+ detailBandProcessingState = DETAIL_SECTION_FIRST_PRINTED;
+ }
+ else
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED;
+ }
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_OTHER_STARTED)
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED;
+ }
+ }
+ else
+ {
+ xmlWriter.writeCloseTag();
+ }
+ if (isSectionPagebreakAfter(attrs))
+ {
+ setPagebreakDefinition(new PageBreakDefinition(false));
+ }
+ }
+ else
+ {
+ xmlWriter.writeCloseTag();
+ }
+ }
+
+ protected void endGroupBody(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED)
+ {
+ // closes the table ..
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ detailBandProcessingState = DETAIL_SECTION_WAIT;
+ }
+
+ }
+
+ protected void endContent(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ finishSection();
+ final BufferState bodyText = finishBuffering();
+ final XmlWriter writer = getXmlWriter();
+
+ final Map definedMappings = variablesDeclarations.getDefinedMappings();
+ if (!definedMappings.isEmpty())
+ {
+ writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decls", XmlWriterSupport.OPEN);
+ final Iterator mappingsIt = definedMappings.entrySet().iterator();
+ while (mappingsIt.hasNext())
+ {
+ final Map.Entry entry = (Map.Entry) mappingsIt.next();
+ final AttributeList entryList = new AttributeList();
+ entryList.setAttribute(OfficeNamespaces.TEXT_NS, NAME, (String) entry.getKey());
+ entryList.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, (String) entry.getValue());
+ writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decl", entryList, XmlWriterSupport.CLOSE);
+ }
+ writer.writeCloseTag();
+ }
+
+ writer.writeStream(bodyText.getXmlAsReader());
+ writer.setLineEmpty(true);
+ writer.writeCloseTag();
+ }
+
+ public String getExportDescriptor()
+ {
+ return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_TEXT;
+ }
+}
diff --git a/reportbuilder/java/org/libreoffice/report/pentaho/output/text/VariablesDeclarations.java b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/VariablesDeclarations.java
new file mode 100644
index 000000000000..05fe8911b880
--- /dev/null
+++ b/reportbuilder/java/org/libreoffice/report/pentaho/output/text/VariablesDeclarations.java
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+package org.libreoffice.report.pentaho.output.text;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.jfree.report.util.AttributeNameGenerator;
+
+
+/**
+ * A collection that holds all used variables. A variable is primarily keyed by
+ * its original name. If a variable contains more than one type, it is also
+ * keyed by the type.
+ *
+ * @since 26.03.2007
+ */
+public class VariablesDeclarations
+{
+
+ private final AttributeNameGenerator nameGenerator;
+ private final Map<String, HashMap<String,String> > variables;
+
+ public VariablesDeclarations()
+ {
+ variables = new HashMap<String, HashMap<String,String> >();
+ nameGenerator = new AttributeNameGenerator();
+ }
+
+ public String produceVariable(final String name,
+ final String type)
+ {
+ HashMap<String,String> holder = variables.get(name);
+ if (holder == null)
+ {
+ holder = new HashMap<String,String>();
+ variables.put(name, holder);
+ }
+
+ final String mapping = holder.get(type);
+ if (mapping != null)
+ {
+ return mapping;
+ }
+ final String result = nameGenerator.generateName(name);
+ if (holder.isEmpty())
+ {
+ // create the default mapping as well..
+ holder.put(null, name);
+ holder.put("time", name);
+ holder.put("date", name);
+ holder.put("datetime", name);
+ holder.put("float", name);
+ holder.put("string", name);
+ holder.put("boolean", name);
+ }
+ holder.put(type, name);
+ return result;
+ }
+
+ public Map<String,String> getDefinedMappings()
+ {
+ final HashMap<String,String> mappings = new HashMap<String,String>();
+ final Iterator<HashMap<String,String>> vars = variables.values().iterator();
+ while (vars.hasNext())
+ {
+ final HashMap<String,String> types = vars.next();
+ final Iterator<Map.Entry<String,String>> varsByType = types.entrySet().iterator();
+ while (varsByType.hasNext())
+ {
+ final Map.Entry<String,String> entry = varsByType.next();
+ final String type = entry.getKey();
+ if (type != null)
+ {
+ final String varName = entry.getValue();
+ mappings.put(varName, type);
+ }
+ }
+ }
+ return mappings;
+ }
+}