/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ package com.sun.star.report.pentaho.output.text; import com.sun.star.report.DataSourceFactory; import com.sun.star.report.ImageService; import com.sun.star.report.InputRepository; import com.sun.star.report.OfficeToken; import com.sun.star.report.OutputRepository; import com.sun.star.report.pentaho.OfficeNamespaces; import com.sun.star.report.pentaho.PentahoReportEngineMetaData; import com.sun.star.report.pentaho.layoutprocessor.FormatValueUtility; import com.sun.star.report.pentaho.model.OfficeMasterPage; import com.sun.star.report.pentaho.model.OfficeMasterStyles; import com.sun.star.report.pentaho.model.OfficeStyle; import com.sun.star.report.pentaho.model.OfficeStyles; import com.sun.star.report.pentaho.model.OfficeStylesCollection; import com.sun.star.report.pentaho.model.PageSection; import com.sun.star.report.pentaho.output.OfficeDocumentReportTarget; import com.sun.star.report.pentaho.output.StyleUtilities; import com.sun.star.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 * * @author Thomas Morgner */ 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 // suppressed 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 occurred. */ 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 propertyNameSpaces = new ArrayList(); final ArrayList propertyNames = new ArrayList(); final ArrayList propertyValues = new ArrayList(); 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; } }