1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 package com.sun.star.report.pentaho.output.text; 24 25 26 import com.sun.star.report.DataSourceFactory; 27 import com.sun.star.report.ImageService; 28 import com.sun.star.report.InputRepository; 29 import com.sun.star.report.OfficeToken; 30 import com.sun.star.report.OutputRepository; 31 import com.sun.star.report.pentaho.OfficeNamespaces; 32 import com.sun.star.report.pentaho.PentahoReportEngineMetaData; 33 import com.sun.star.report.pentaho.layoutprocessor.FormatValueUtility; 34 import com.sun.star.report.pentaho.model.OfficeMasterPage; 35 import com.sun.star.report.pentaho.model.OfficeMasterStyles; 36 import com.sun.star.report.pentaho.model.OfficeStyle; 37 import com.sun.star.report.pentaho.model.OfficeStyles; 38 import com.sun.star.report.pentaho.model.OfficeStylesCollection; 39 import com.sun.star.report.pentaho.model.PageSection; 40 import com.sun.star.report.pentaho.output.OfficeDocumentReportTarget; 41 import com.sun.star.report.pentaho.output.StyleUtilities; 42 import com.sun.star.report.pentaho.styles.LengthCalculator; 43 44 import java.io.IOException; 45 46 import java.io.OutputStream; 47 import java.io.OutputStreamWriter; 48 49 import java.util.ArrayList; 50 import java.util.Iterator; 51 import java.util.Map; 52 53 54 import org.jfree.layouting.input.style.values.CSSNumericValue; 55 import org.jfree.layouting.util.AttributeMap; 56 import org.jfree.report.DataSourceException; 57 import org.jfree.report.JFreeReportInfo; 58 import org.jfree.report.ReportProcessingException; 59 import org.jfree.report.flow.ReportJob; 60 import org.jfree.report.flow.ReportStructureRoot; 61 import org.jfree.report.flow.ReportTargetUtil; 62 import org.jfree.report.structure.Element; 63 import org.jfree.report.structure.Section; 64 import org.jfree.report.util.AttributeNameGenerator; 65 import org.jfree.report.util.IntegerCache; 66 67 import org.pentaho.reporting.libraries.base.util.FastStack; 68 import org.pentaho.reporting.libraries.base.util.ObjectUtilities; 69 import org.pentaho.reporting.libraries.resourceloader.ResourceKey; 70 import org.pentaho.reporting.libraries.resourceloader.ResourceManager; 71 import org.pentaho.reporting.libraries.xmlns.common.AttributeList; 72 import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter; 73 import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport; 74 75 76 /** 77 * Creation-Date: 03.07.2006, 16:28:00 78 * 79 * @author Thomas Morgner 80 */ 81 public class TextRawReportTarget extends OfficeDocumentReportTarget 82 { 83 84 private static final String ALWAYS = "always"; 85 private static final String KEEP_TOGETHER = "keep-together"; 86 private static final String KEEP_WITH_NEXT = "keep-with-next"; 87 private static final String MAY_BREAK_BETWEEN_ROWS = "may-break-between-rows"; 88 private static final String NAME = "name"; 89 private static final String NONE = "none"; 90 private static final String NORMAL = "normal"; 91 private static final String PARAGRAPH_PROPERTIES = "paragraph-properties"; 92 private static final String STANDARD = "Standard"; 93 private static final String TABLE_PROPERTIES = "table-properties"; 94 private static final String VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT = "variables_paragraph_with_next"; 95 private static final String VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT = "variables_paragraph_without_next"; 96 private static final int TABLE_LAYOUT_VARIABLES_PARAGRAPH = 0; 97 private static final int TABLE_LAYOUT_SINGLE_DETAIL_TABLE = 2; 98 private static final int CP_SETUP = 0; 99 private static final int CP_FIRST_TABLE = 1; 100 private static final int CP_NEXT_TABLE = 2; 101 // This is the initial state of the detail-band processing. It states, that we are now waiting for a 102 // detail-band to be printed. 103 private static final int DETAIL_SECTION_WAIT = 0; 104 // The first detail section has started. 105 private static final int DETAIL_SECTION_FIRST_STARTED = 1; 106 // The first detail section has been printed. 107 private static final int DETAIL_SECTION_FIRST_PRINTED = 2; 108 // An other detail section has started 109 private static final int DETAIL_SECTION_OTHER_STARTED = 3; 110 // The other detail section has been printed. 111 private static final int DETAIL_SECTION_OTHER_PRINTED = 4; 112 private boolean pageFooterOnReportFooter; 113 private boolean pageFooterOnReportHeader; 114 private boolean pageHeaderOnReportFooter; 115 private boolean pageHeaderOnReportHeader; 116 private int contentProcessingState; 117 private OfficeMasterPage currentMasterPage; 118 private final FastStack activePageContext; 119 private MasterPageFactory masterPageFactory; 120 private LengthCalculator sectionHeight; 121 private String variables; 122 private PageBreakDefinition pageBreakDefinition; 123 private VariablesDeclarations variablesDeclarations; 124 private boolean columnBreakPending; 125 private boolean sectionKeepTogether; 126 private final AttributeNameGenerator sectionNames; 127 private int detailBandProcessingState; 128 private final int tableLayoutConfig; 129 private int expectedTableRowCount; 130 private boolean firstCellSeen; 131 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)132 public TextRawReportTarget(final ReportJob reportJob, 133 final ResourceManager resourceManager, 134 final ResourceKey baseResource, 135 final InputRepository inputRepository, 136 final OutputRepository outputRepository, 137 final String target, 138 final ImageService imageService, 139 final DataSourceFactory datasourcefactory) 140 throws ReportProcessingException 141 { 142 super(reportJob, resourceManager, baseResource, inputRepository, outputRepository, target, imageService, datasourcefactory); 143 activePageContext = new FastStack(); 144 this.sectionNames = new AttributeNameGenerator(); 145 146 this.tableLayoutConfig = TABLE_LAYOUT_SINGLE_DETAIL_TABLE; 147 } 148 getTargetMimeType()149 protected String getTargetMimeType() 150 { 151 return "application/vnd.oasis.opendocument.text"; 152 } 153 154 /** 155 * Checks, whether a manual page break should be inserted at the next possible location. 156 * 157 * @return true, if a pagebreak is pending, false otherwise. 158 */ isPagebreakPending()159 private boolean isPagebreakPending() 160 { 161 return pageBreakDefinition != null; 162 } 163 isResetPageNumber()164 private boolean isResetPageNumber() 165 { 166 return pageBreakDefinition != null && pageBreakDefinition.isResetPageNumber(); 167 } 168 169 /** 170 * Defines, whether a manual pagebreak should be inserted at the next possible location. 171 * 172 * @param pageBreakDefinition the new flag value. 173 */ setPagebreakDefinition(final PageBreakDefinition pageBreakDefinition)174 private void setPagebreakDefinition(final PageBreakDefinition pageBreakDefinition) 175 { 176 this.pageBreakDefinition = pageBreakDefinition; 177 } 178 getPagebreakDefinition()179 private PageBreakDefinition getPagebreakDefinition() 180 { 181 return pageBreakDefinition; 182 } 183 184 // todo isKeepTableWithNext()185 private boolean isKeepTableWithNext() 186 { 187 final int keepTogetherState = getCurrentContext().getKeepTogether(); 188 if (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP) 189 { 190 return true; 191 } 192 193 final boolean keepWithNext; 194 keepWithNext = keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL && (detailBandProcessingState == DETAIL_SECTION_WAIT); 195 return keepWithNext; 196 } 197 isSectionPagebreakAfter(final AttributeMap attrs)198 private boolean isSectionPagebreakAfter(final AttributeMap attrs) 199 { 200 final Object forceNewPage = 201 attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page"); 202 return "after-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage); 203 } 204 isSectionPagebreakBefore(final AttributeMap attrs)205 private boolean isSectionPagebreakBefore(final AttributeMap attrs) 206 { 207 final Object forceNewPage = 208 attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page"); 209 return "before-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage); 210 } 211 getCurrentContext()212 private PageContext getCurrentContext() 213 { 214 return (PageContext) activePageContext.peek(); 215 } 216 createMasterPage(final boolean printHeader, final boolean printFooter)217 private String createMasterPage(final boolean printHeader, 218 final boolean printFooter) 219 throws ReportProcessingException 220 { 221 // create the master page for the report-header. 222 // If there is a page-header or footer in the report that gets 223 // surpressed on the report-header, we have to insert a pagebreak 224 // afterwards. 225 226 final String activePageFooter; 227 // Check, whether the report header can have a page-header 228 final PageContext context = getCurrentContext(); 229 if (printFooter) 230 { 231 activePageFooter = context.getPageFooterContent(); 232 } 233 else 234 { 235 activePageFooter = null; 236 } 237 final String activePageHeader; 238 if (printHeader) 239 { 240 // we have to insert a manual pagebreak after the report header. 241 activePageHeader = context.getPageHeaderContent(); 242 } 243 else 244 { 245 activePageHeader = null; 246 } 247 248 final String masterPageName; 249 if (currentMasterPage == null || !masterPageFactory.containsMasterPage(STANDARD, activePageHeader, activePageFooter)) 250 { 251 252 final CSSNumericValue headerSize = context.getAllHeaderSize(); 253 final CSSNumericValue footerSize = context.getAllFooterSize(); 254 255 256 currentMasterPage = masterPageFactory.createMasterPage(STANDARD, activePageHeader, activePageFooter); 257 258 // LOGGER.debug("Created a new master-page: " + currentMasterPage.getStyleName()); 259 260 // todo: Store the page-layouts as well. 261 // The page layouts are derived from a common template, but as the 262 // header-heights differ, we have to derive these beasts instead 263 // of copying them 264 265 final OfficeStylesCollection officeStylesCollection = getGlobalStylesCollection(); 266 final OfficeMasterStyles officeMasterStyles = officeStylesCollection.getMasterStyles(); 267 final String pageLayoutTemplate = currentMasterPage.getPageLayout(); 268 if (pageLayoutTemplate == null) 269 { 270 // there is no pagelayout. Create one .. 271 final String derivedLayout = masterPageFactory.createPageStyle(getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize); 272 currentMasterPage.setPageLayout(derivedLayout); 273 } 274 else 275 { 276 final String derivedLayout = masterPageFactory.derivePageStyle(pageLayoutTemplate, 277 getPredefinedStylesCollection().getAutomaticStyles(), 278 getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize); 279 currentMasterPage.setPageLayout(derivedLayout); 280 } 281 officeMasterStyles.addMasterPage(currentMasterPage); 282 masterPageName = currentMasterPage.getStyleName(); 283 } 284 else 285 { 286 // retrieve the master-page. 287 final OfficeMasterPage masterPage = masterPageFactory.getMasterPage(STANDARD, activePageHeader, activePageFooter); 288 if (ObjectUtilities.equal(masterPage.getStyleName(), currentMasterPage.getStyleName())) 289 { 290 // They are the same, 291 masterPageName = null; 292 } 293 else 294 { 295 // reuse the existing one .. 296 currentMasterPage = masterPage; 297 masterPageName = currentMasterPage.getStyleName(); 298 } 299 } 300 301 // if either the pageheader or footer are *not* printed with the 302 // report header, then this implies that we have to insert a manual 303 // pagebreak at the end of the section. 304 305 if ((!printHeader && context.getHeader() != null) || (!printFooter && context.getFooter() != null)) 306 { 307 setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber())); 308 } 309 310 return masterPageName; 311 } 312 isColumnBreakPending()313 private boolean isColumnBreakPending() 314 { 315 return columnBreakPending; 316 } 317 setColumnBreakPending(final boolean columnBreakPending)318 private void setColumnBreakPending(final boolean columnBreakPending) 319 { 320 this.columnBreakPending = columnBreakPending; 321 } 322 parseInt(final Object value)323 private Integer parseInt(final Object value) 324 { 325 if (value instanceof Number) 326 { 327 final Number n = (Number) value; 328 return IntegerCache.getInteger(n.intValue()); 329 } 330 if (value instanceof String) 331 { 332 try 333 { 334 return IntegerCache.getInteger(Integer.parseInt((String) value)); 335 } 336 catch (NumberFormatException nfe) 337 { 338 //return null; // ignore 339 } 340 } 341 return null; 342 } 343 applyColumnsToPageBand(final BufferState contents, final int numberOfColumns)344 private BufferState applyColumnsToPageBand(final BufferState contents, 345 final int numberOfColumns) 346 throws IOException, ReportProcessingException 347 { 348 if (numberOfColumns <= 1) 349 { 350 return contents; 351 } 352 startBuffering(getGlobalStylesCollection(), true); 353 // derive section style .. 354 355 // This is a rather cheap solution to the problem. In a sane world, we would have to feed the 356 // footer multiple times. Right now, we simply rely on the balacing, which should make sure that 357 // the column's content are evenly distributed. 358 final XmlWriter writer = getXmlWriter(); 359 final AttributeList attrs = new AttributeList(); 360 attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(numberOfColumns)); 361 attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section")); 362 writer.writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN); 363 for (int i = 0; i < numberOfColumns; i++) 364 { 365 writer.writeStream(contents.getXmlAsReader()); 366 } 367 368 writer.writeCloseTag(); 369 return finishBuffering(); 370 } 371 generateSectionStyle(final int columnCount)372 private String generateSectionStyle(final int columnCount) 373 { 374 final OfficeStyles automaticStyles = getStylesCollection().getAutomaticStyles(); 375 final String styleName = getAutoStyleNameGenerator().generateName("auto_section_style"); 376 377 final Section sectionProperties = new Section(); 378 sectionProperties.setNamespace(OfficeNamespaces.STYLE_NS); 379 sectionProperties.setType("section-properties"); 380 sectionProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent"); 381 sectionProperties.setAttribute(OfficeNamespaces.TEXT_NS, "dont-balance-text-columns", OfficeToken.FALSE); 382 sectionProperties.setAttribute(OfficeNamespaces.STYLE_NS, "editable", OfficeToken.FALSE); 383 384 if (columnCount > 1) 385 { 386 final Section columns = new Section(); 387 columns.setNamespace(OfficeNamespaces.STYLE_NS); 388 columns.setType("columns"); 389 columns.setAttribute(OfficeNamespaces.FO_NS, "column-count", String.valueOf(columnCount)); 390 columns.setAttribute(OfficeNamespaces.STYLE_NS, "column-gap", "0cm"); 391 sectionProperties.addNode(columns); 392 393 // final Section columnSep = new Section(); 394 // columnSep.setNamespace(OfficeNamespaces.STYLE_NS); 395 // columnSep.setType("column-sep"); 396 // columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "width", "0.035cm"); 397 // columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "color", "#000000"); 398 // columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "height", "100%"); 399 // columns.addNode(columnSep); 400 401 for (int i = 0; i < columnCount; i++) 402 { 403 final Section column = new Section(); 404 column.setNamespace(OfficeNamespaces.STYLE_NS); 405 column.setType("column"); 406 column.setAttribute(OfficeNamespaces.STYLE_NS, "rel-width", "1*"); 407 column.setAttribute(OfficeNamespaces.FO_NS, "start-indent", "0cm"); 408 column.setAttribute(OfficeNamespaces.FO_NS, "end-indent", "0cm"); 409 columns.addNode(column); 410 } 411 } 412 413 final OfficeStyle style = new OfficeStyle(); 414 style.setNamespace(OfficeNamespaces.STYLE_NS); 415 style.setType("style"); 416 style.setAttribute(OfficeNamespaces.STYLE_NS, NAME, styleName); 417 style.setAttribute(OfficeNamespaces.STYLE_NS, "family", "section"); 418 style.addNode(sectionProperties); 419 420 automaticStyles.addStyle(style); 421 return styleName; 422 } 423 424 /** 425 * Starts the output of a new office document. This method writes the generic 'office:document-content' tag along with 426 * all known namespace declarations. 427 * 428 * @param report the report object. 429 * @throws org.jfree.report.DataSourceException 430 * if there was an error accessing the datasource 431 * @throws org.jfree.report.ReportProcessingException 432 * if some other error occured. 433 */ startReport(final ReportStructureRoot report)434 public void startReport(final ReportStructureRoot report) 435 throws DataSourceException, ReportProcessingException 436 { 437 super.startReport(report); 438 variablesDeclarations = new VariablesDeclarations(); 439 detailBandProcessingState = DETAIL_SECTION_WAIT; 440 sectionNames.reset(); 441 442 pageFooterOnReportFooter = false; 443 pageFooterOnReportHeader = false; 444 pageHeaderOnReportFooter = false; 445 pageHeaderOnReportHeader = false; 446 contentProcessingState = TextRawReportTarget.CP_SETUP; 447 448 activePageContext.clear(); 449 activePageContext.push(new PageContext()); 450 451 final OfficeStylesCollection predefStyles = getPredefinedStylesCollection(); 452 masterPageFactory = new MasterPageFactory(predefStyles.getMasterStyles()); 453 454 predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(true)); 455 predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(false)); 456 } 457 createVariablesStyle(final boolean keepWithNext)458 private OfficeStyle createVariablesStyle(final boolean keepWithNext) 459 { 460 final OfficeStyle variablesSectionStyle = new OfficeStyle(); 461 variablesSectionStyle.setStyleFamily(OfficeToken.PARAGRAPH); 462 if (keepWithNext) 463 { 464 variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT); 465 } 466 else 467 { 468 variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT); 469 } 470 471 final Section paragraphProps = new Section(); 472 paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS); 473 paragraphProps.setType(PARAGRAPH_PROPERTIES); 474 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent"); 475 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "text-align", "start"); 476 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS); 477 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS); 478 paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-align", "top"); 479 variablesSectionStyle.addNode(paragraphProps); 480 481 final Section textProps = new Section(); 482 textProps.setNamespace(OfficeNamespaces.STYLE_NS); 483 textProps.setType("text-properties"); 484 textProps.setAttribute(OfficeNamespaces.FO_NS, "font-variant", NORMAL); 485 textProps.setAttribute(OfficeNamespaces.FO_NS, "text-transform", NONE); 486 textProps.setAttribute(OfficeNamespaces.FO_NS, "color", "#ffffff"); 487 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-outline", OfficeToken.FALSE); 488 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE); 489 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-style", NONE); 490 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-mode", "continuous"); 491 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-position", "0% 100%"); 492 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-name", "Tahoma"); 493 textProps.setAttribute(OfficeNamespaces.FO_NS, "font-size", "1pt"); 494 textProps.setAttribute(OfficeNamespaces.FO_NS, "letter-spacing", NORMAL); 495 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "letter-kerning", OfficeToken.FALSE); 496 textProps.setAttribute(OfficeNamespaces.FO_NS, "font-style", NORMAL); 497 textProps.setAttribute(OfficeNamespaces.FO_NS, "text-shadow", NONE); 498 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-style", NONE); 499 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-mode", "continuous"); 500 textProps.setAttribute(OfficeNamespaces.FO_NS, "font-weight", NORMAL); 501 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-rotation-angle", "0"); 502 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-emphasize", NONE); 503 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine", NONE); 504 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-start-char", ""); 505 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-end-char", ""); 506 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE); 507 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-scale", "100%"); 508 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-relief", NONE); 509 textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-display", NONE); 510 variablesSectionStyle.addNode(textProps); 511 return variablesSectionStyle; 512 } 513 startContent(final AttributeMap attrs)514 protected void startContent(final AttributeMap attrs) 515 throws IOException, DataSourceException, ReportProcessingException 516 { 517 final XmlWriter xmlWriter = getXmlWriter(); 518 xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "text", null, XmlWriterSupport.OPEN); 519 520 writeNullDate(); 521 522 // now start the buffering. We have to insert the variables declaration 523 // later .. 524 startBuffering(getStylesCollection(), true); 525 526 final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count"); 527 final Integer colCount = parseInt(columnCountRaw); 528 if (colCount != null) 529 { 530 final PageContext pageContext = getCurrentContext(); 531 pageContext.setColumnCount(colCount); 532 } 533 534 } 535 startOther(final AttributeMap attrs)536 protected void startOther(final AttributeMap attrs) 537 throws IOException, DataSourceException, ReportProcessingException 538 { 539 final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs); 540 final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs); 541 542 if (ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace)) 543 { 544 if (ObjectUtilities.equal(OfficeToken.IMAGE, elementType)) 545 { 546 startImageProcessing(attrs); 547 } 548 else if (ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType) && getCurrentRole() != ROLE_TEMPLATE) 549 { 550 startChartProcessing(attrs); 551 } 552 return; 553 } 554 else if (isFilteredNamespace(namespace)) 555 { 556 throw new IllegalStateException("This element should be hidden: " + namespace + ", " + elementType); 557 } 558 559 if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace) && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType)) 560 { 561 // Skip the columns section if the tables get merged.. 562 startBuffering(getStylesCollection(), true); 563 } 564 else 565 { 566 openSection(); 567 568 final boolean isTableNS = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace); 569 if (isTableNS) 570 { 571 if (ObjectUtilities.equal(OfficeToken.TABLE, elementType)) 572 { 573 startTable(attrs); 574 return; 575 } 576 577 if (ObjectUtilities.equal(OfficeToken.TABLE_ROW, elementType)) 578 { 579 startRow(attrs); 580 return; 581 } 582 } 583 584 585 if (ObjectUtilities.equal(OfficeNamespaces.TEXT_NS, namespace)) 586 { 587 if (ObjectUtilities.equal("variable-set", elementType)) 588 { 589 // update the variables-declaration thingie .. 590 final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME); 591 final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE); 592 final String newVarName = variablesDeclarations.produceVariable(varName, varType); 593 attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName); 594 } 595 else if (ObjectUtilities.equal("variable-get", elementType)) 596 { 597 final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME); 598 final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE); 599 final String newVarName = variablesDeclarations.produceVariable(varName, varType); 600 attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName); 601 // this one must not be written, as the DTD does not declare it. 602 // attrs.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, null); 603 } 604 } 605 606 if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null) 607 { 608 // This cannot happen as long as the report sections only contain tables. But at some point in the 609 // future they will be made of paragraphs, and then we are prepared .. 610 // LOGGER.debug("Variables-Section in own paragraph " + variables); 611 612 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH, 613 TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(), 614 getGlobalStylesCollection(), getPredefinedStylesCollection()); 615 final XmlWriter xmlWriter = getXmlWriter(); 616 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, 617 TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN); 618 xmlWriter.writeText(variables); 619 xmlWriter.writeCloseTag(); 620 variables = null; 621 } 622 623 final boolean keepTogetherOnParagraph = true; 624 625 if (keepTogetherOnParagraph) 626 { 627 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs)) 628 { 629 final int keepTogetherState = getCurrentContext().getKeepTogether(); 630 if (!firstCellSeen && (sectionKeepTogether || keepTogetherState == PageContext.KEEP_TOGETHER_GROUP)) 631 { 632 OfficeStyle style = null; 633 final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME); 634 if (styleName == null) 635 { 636 final boolean keep = (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether(); 637 final ArrayList propertyNameSpaces = new ArrayList(); 638 final ArrayList propertyNames = new ArrayList(); 639 final ArrayList propertyValues = new ArrayList(); 640 641 propertyNameSpaces.add(OfficeNamespaces.FO_NS); 642 propertyNameSpaces.add(OfficeNamespaces.FO_NS); 643 propertyNames.add(KEEP_TOGETHER); 644 propertyValues.add(ALWAYS); 645 if (keep) 646 { 647 propertyNames.add(KEEP_WITH_NEXT); 648 propertyValues.add(ALWAYS); 649 } 650 else 651 { 652 propertyNames.add(KEEP_WITH_NEXT); 653 propertyValues.add(null); 654 } 655 style = StyleUtilities.queryStyleByProperties(getStylesCollection(), OfficeToken.PARAGRAPH, PARAGRAPH_PROPERTIES, propertyNameSpaces, propertyNames, propertyValues); 656 } 657 if (style == null) 658 { 659 style = deriveStyle(OfficeToken.PARAGRAPH, styleName); 660 // Lets set the 'keep-together' flag.. 661 662 Element paragraphProps = style.getParagraphProperties(); 663 if (paragraphProps == null) 664 { 665 paragraphProps = new Section(); 666 paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS); 667 paragraphProps.setType(PARAGRAPH_PROPERTIES); 668 style.addNode(paragraphProps); 669 } 670 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS); 671 672 // We prevent pagebreaks within the two adjacent rows (this one and the next one) if 673 // either a group-wide keep-together is defined or if we haven't reached the end of the 674 // current section yet. 675 if ((keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether()) 676 { 677 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS); 678 } 679 } 680 681 attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, style.getStyleName()); 682 } 683 } 684 } 685 686 if (ObjectUtilities.equal(OfficeNamespaces.DRAWING_NS, namespace) && ObjectUtilities.equal(OfficeToken.FRAME, elementType)) 687 { 688 final String styleName = (String) attrs.getAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME); 689 final OfficeStyle predefAutoStyle = getPredefinedStylesCollection().getAutomaticStyles().getStyle(OfficeToken.GRAPHIC, styleName); 690 if (predefAutoStyle != null) 691 { 692 // special ole handling 693 final Element graphicProperties = predefAutoStyle.getGraphicProperties(); 694 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top"); 695 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left"); 696 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", "paragraph-content"); 697 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", "paragraph"); 698 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "flow-with-text", "false"); 699 graphicProperties.setAttribute(OfficeNamespaces.DRAWING_NS, "ole-draw-aspect", "1"); 700 701 // attrs.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, predefAutoStyle.getStyleName()); 702 } 703 } 704 705 // process the styles as usual 706 performStyleProcessing(attrs); 707 final XmlWriter xmlWriter = getXmlWriter(); 708 final AttributeList attrList = buildAttributeList(attrs); 709 xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN); 710 711 if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH 712 && variables != null 713 && !isRepeatingSection() 714 && ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs)) 715 { 716 //LOGGER.debug("Variables-Section in existing cell " + variables); 717 xmlWriter.writeText(variables); 718 variables = null; 719 } 720 } 721 } 722 startRow(final AttributeMap attrs)723 private void startRow(final AttributeMap attrs) 724 throws IOException, ReportProcessingException 725 { 726 firstCellSeen = false; 727 expectedTableRowCount -= 1; 728 final String rowStyle = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); 729 final CSSNumericValue rowHeight = computeRowHeight(rowStyle); 730 // LOGGER.debug("Adding row-Style: " + rowStyle + " " + rowHeight); 731 sectionHeight.add(rowHeight); 732 733 // if (expectedTableRowCount > 0) 734 // { 735 // // Some other row. Create a keep-together 736 // 737 // } 738 // else 739 // { 740 // // This is the last row before the section will end. 741 // // or (in some weird cases) There is no information when the row will end. 742 // // Anyway, if we are here, we do not create a keep-together style on the table-row .. 743 // } 744 // process the styles as usual 745 performStyleProcessing(attrs); 746 747 final AttributeList attrList = buildAttributeList(attrs); 748 getXmlWriter().writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrList, XmlWriterSupport.OPEN); 749 } 750 startTable(final AttributeMap attrs)751 private void startTable(final AttributeMap attrs) 752 throws ReportProcessingException, IOException 753 { 754 final Integer trc = (Integer) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "table-row-count"); 755 if (trc == null) 756 { 757 expectedTableRowCount = -1; 758 } 759 else 760 { 761 expectedTableRowCount = trc; 762 } 763 764 if (isSectionPagebreakBefore(attrs)) 765 { 766 // force a pagebreak .. 767 setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber())); 768 } 769 770 // its a table. This means, it is a root-level element 771 final PageBreakDefinition breakDefinition; 772 String masterPageName = null; 773 final int currentRole = getCurrentRole(); 774 if (contentProcessingState == TextRawReportTarget.CP_FIRST_TABLE) 775 { 776 contentProcessingState = TextRawReportTarget.CP_NEXT_TABLE; 777 778 // Processing the report header now. 779 if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_HEADER) 780 { 781 breakDefinition = new PageBreakDefinition(isResetPageNumber()); 782 masterPageName = createMasterPage(pageHeaderOnReportHeader, pageFooterOnReportHeader); 783 if (masterPageName == null) 784 { 785 // we should always have a master-page ... 786 masterPageName = currentMasterPage.getStyleName(); 787 } 788 } 789 else if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_FOOTER) 790 { 791 breakDefinition = new PageBreakDefinition(isResetPageNumber()); 792 masterPageName = createMasterPage(pageHeaderOnReportFooter, pageFooterOnReportFooter); 793 if (masterPageName == null && isSectionPagebreakBefore(attrs)) 794 { 795 // If we have a manual pagebreak, then activate the current master-page again. 796 masterPageName = currentMasterPage.getStyleName(); 797 } 798 // But we skip this (and therefore the resulting pagebreak) if there is no manual break 799 // and no other condition that would force an break. 800 } 801 else if (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER) 802 { 803 breakDefinition = null; 804 // no pagebreaks .. 805 } 806 else if (currentMasterPage == null || isPagebreakPending()) 807 { 808 // Must be the first table, as we have no master-page yet. 809 masterPageName = createMasterPage(true, true); 810 setPagebreakDefinition(null); 811 if (masterPageName == null) 812 { 813 // we should always have a master-page ... 814 masterPageName = currentMasterPage.getStyleName(); 815 } 816 breakDefinition = new PageBreakDefinition(isResetPageNumber()); 817 } 818 else 819 { 820 breakDefinition = null; 821 } 822 } 823 else if (isPagebreakPending() && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER) 824 { 825 // Derive an automatic style for the pagebreak. 826 // LOGGER.debug("Manual pagebreak (within the section): " + getCurrentRole()); 827 breakDefinition = getPagebreakDefinition(); 828 setPagebreakDefinition(null); 829 masterPageName = createMasterPage(true, true); 830 if (masterPageName == null || isSectionPagebreakBefore(attrs)) 831 { 832 // If we have a manual pagebreak, then activate the current master-page again. 833 masterPageName = currentMasterPage.getStyleName(); 834 } 835 } 836 else 837 { 838 breakDefinition = null; 839 } 840 841 final XmlWriter xmlWriter = getXmlWriter(); 842 if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && masterPageName != null) 843 { 844 // close the last table-tag, we will open a new one 845 xmlWriter.writeCloseTag(); 846 // Reset the detail-state to 'started' so that the table's columns get printed now. 847 detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED; 848 } 849 850 if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null) 851 { 852 if (masterPageName != null) 853 { 854 // write a paragraph that uses the VARIABLES_HIDDEN_STYLE as 855 // primary style. Derive that one and add the manual pagebreak. 856 // The predefined style already has the 'keep-together' flags set. 857 // LOGGER.debug("Variables-Section with new Master-Page " + variables + " " + masterPageName); 858 859 final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT); 860 style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName); 861 if (breakDefinition.isResetPageNumber()) 862 { 863 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES); 864 paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1"); 865 } 866 if (isColumnBreakPending()) 867 { 868 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES); 869 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column"); 870 setColumnBreakPending(false); 871 } 872 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN); 873 874 masterPageName = null; 875 //breakDefinition = null; 876 } 877 else if (isColumnBreakPending()) 878 { 879 setColumnBreakPending(false); 880 881 final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT); 882 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES); 883 paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1"); 884 885 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN); 886 } 887 else 888 { 889 // Write a paragraph without adding the pagebreak. We can reuse the global style, but we have to make 890 // sure that the style is part of the current 'auto-style' collection. 891 // LOGGER.debug("Variables-Section " + variables); 892 893 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH, 894 TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(), 895 getGlobalStylesCollection(), getPredefinedStylesCollection()); 896 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, 897 TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN); 898 } 899 xmlWriter.writeText(variables); 900 xmlWriter.writeCloseTag(); 901 variables = null; 902 } 903 904 final boolean keepWithNext = isKeepTableWithNext(); 905 final boolean localKeepTogether = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER)); 906 final boolean tableMergeActive = isTableMergeActive(); 907 this.sectionKeepTogether = tableMergeActive && localKeepTogether; 908 909 // Check, whether we have a reason to derive a style... 910 if (masterPageName != null || (!tableMergeActive && (localKeepTogether || keepWithNext)) || isColumnBreakPending()) 911 { 912 final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); 913 final OfficeStyle style = deriveStyle("table", styleName); 914 915 if (masterPageName != null) 916 { 917 // LOGGER.debug("Starting a new MasterPage: " + masterPageName); 918 // Patch the current styles. 919 // This usually only happens on Table-Styles or Paragraph-Styles 920 style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName); 921 if (breakDefinition.isResetPageNumber()) 922 { 923 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES); 924 paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1"); 925 } 926 } 927 if (isColumnBreakPending()) 928 { 929 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES); 930 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column"); 931 setColumnBreakPending(false); 932 } 933 934 // Inhibit breaks inside the table only if it has been defined and if we do not create one single 935 // big detail section. In that case, this flag would be invalid and would cause layout-errors. 936 if (!tableMergeActive) 937 { 938 if (localKeepTogether) 939 { 940 final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES); 941 tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE); 942 } 943 } 944 else 945 { 946 if (detailBandProcessingState == DETAIL_SECTION_WAIT) 947 { 948 detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED; 949 } 950 else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED) 951 { 952 detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED; 953 } 954 } 955 if (keepWithNext) 956 { 957 boolean addKeepWithNext = true; 958 if (currentRole == ROLE_GROUP_FOOTER) 959 { 960 addKeepWithNext = isParentKeepTogether(); 961 } 962 963 final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES); 964 tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE); 965 if (addKeepWithNext) 966 { 967 tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS); 968 // A keep-with-next does not work, if the may-break-betweek rows is not set to false .. 969 } 970 } 971 attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName()); 972 // no need to copy the styles, this was done while deriving the 973 // style .. 974 } 975 else 976 { 977 // Check, whether we may be able to skip the table. 978 if (tableMergeActive) 979 { 980 if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED) 981 { 982 // Skip the whole thing .. 983 return; 984 } 985 else if (detailBandProcessingState == DETAIL_SECTION_WAIT) 986 { 987 if (keepWithNext) 988 { 989 final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME); 990 991 final OfficeStyle style = deriveStyle(OfficeToken.TABLE, styleName); 992 final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES); 993 // A keep-with-next does not work, if the may-break-betweek rows is not set to false .. 994 tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE); 995 final String hasGroupFooter = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "has-group-footer"); 996 if (hasGroupFooter != null && hasGroupFooter.equals(OfficeToken.TRUE)) 997 { 998 tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS); 999 } 1000 1001 attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName()); 1002 } 1003 detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED; 1004 } 1005 else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED) 1006 { 1007 detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED; 1008 } 1009 } 1010 1011 // process the styles as usual 1012 performStyleProcessing(attrs); 1013 } 1014 1015 final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs); 1016 final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs); 1017 final AttributeList attrList = buildAttributeList(attrs); 1018 xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN); 1019 } 1020 isParentKeepTogether()1021 private boolean isParentKeepTogether() 1022 { 1023 PageContext context = getCurrentContext(); 1024 if (context != null) 1025 { 1026 context = context.getParent(); 1027 if (context != null) 1028 { 1029 return context.getKeepTogether() == PageContext.KEEP_TOGETHER_GROUP; 1030 } 1031 } 1032 return false; 1033 } 1034 isTableMergeActive()1035 private boolean isTableMergeActive() 1036 { 1037 return getCurrentRole() == ROLE_DETAIL && tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE; 1038 } 1039 openSection()1040 private void openSection() 1041 throws IOException 1042 { 1043 if (isRepeatingSection()) 1044 { 1045 // repeating sections have other ways of defining columns .. 1046 return; 1047 } 1048 if (getCurrentRole() == ROLE_TEMPLATE || getCurrentRole() == ROLE_SPREADSHEET_PAGE_HEADER || getCurrentRole() == ROLE_SPREADSHEET_PAGE_FOOTER) 1049 { 1050 // the template section would break the multi-column stuff and we dont open up sections there 1051 // anyway .. 1052 return; 1053 } 1054 1055 final PageContext pageContext = getCurrentContext(); 1056 final Integer columnCount = pageContext.getColumnCount(); 1057 if (columnCount != null && !pageContext.isSectionOpen()) 1058 { 1059 final AttributeList attrs = new AttributeList(); 1060 attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(columnCount)); 1061 attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section")); 1062 getXmlWriter().writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN); 1063 1064 pageContext.setSectionOpen(true); 1065 } 1066 1067 } 1068 startReportSection(final AttributeMap attrs, final int role)1069 protected void startReportSection(final AttributeMap attrs, final int role) 1070 throws IOException, DataSourceException, ReportProcessingException 1071 { 1072 sectionHeight = new LengthCalculator(); 1073 if (role == OfficeDocumentReportTarget.ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER) 1074 { 1075 // Start buffering with an dummy styles-collection, so that the global styles dont get polluted .. 1076 startBuffering(new OfficeStylesCollection(), true); 1077 } 1078 else if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER) 1079 { 1080 startBuffering(getGlobalStylesCollection(), true); 1081 pageHeaderOnReportHeader = PageSection.isPrintWithReportHeader(attrs); 1082 pageHeaderOnReportFooter = PageSection.isPrintWithReportFooter(attrs); 1083 } 1084 else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER) 1085 { 1086 startBuffering(getGlobalStylesCollection(), true); 1087 pageFooterOnReportHeader = PageSection.isPrintWithReportHeader(attrs); 1088 pageFooterOnReportFooter = PageSection.isPrintWithReportFooter(attrs); 1089 } 1090 else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER) 1091 { 1092 startBuffering(getGlobalStylesCollection(), true); 1093 } 1094 else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES) 1095 { 1096 startBuffering(getGlobalStylesCollection(), false); 1097 } 1098 else 1099 { 1100 contentProcessingState = TextRawReportTarget.CP_FIRST_TABLE; 1101 if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER) 1102 { 1103 // if we have a repeating header, then skip the first one .. 1104 // if this is a repeating footer, skip the last one. This means, 1105 // we have to buffer all group footers and wait for the next section.. 1106 startBuffering(getContentStylesCollection(), true); 1107 } 1108 1109 if (role != OfficeDocumentReportTarget.ROLE_DETAIL) 1110 { 1111 // reset the detail-state. The flag will be updated on startTable and endOther(Table) if the 1112 // current role is ROLE_DETAIL 1113 detailBandProcessingState = DETAIL_SECTION_WAIT; 1114 } 1115 } 1116 } 1117 startGroup(final AttributeMap attrs)1118 protected void startGroup(final AttributeMap attrs) 1119 throws IOException, DataSourceException, ReportProcessingException 1120 { 1121 super.startGroup(attrs); 1122 final PageContext pageContext = new PageContext(getCurrentContext()); 1123 activePageContext.push(pageContext); 1124 1125 final Object resetPageNumber = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "reset-page-number"); 1126 if (OfficeToken.TRUE.equals(resetPageNumber)) 1127 { 1128 setPagebreakDefinition(new PageBreakDefinition(true)); 1129 } 1130 1131 final Object keepTogether = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER); 1132 if ("whole-group".equals(keepTogether)) 1133 { 1134 pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_GROUP); 1135 } 1136 else if ("with-first-detail".equals(keepTogether) && pageContext.getKeepTogether() != PageContext.KEEP_TOGETHER_GROUP) 1137 { 1138 pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_FIRST_DETAIL); 1139 } 1140 1141 final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count"); 1142 final Integer colCount = parseInt(columnCountRaw); 1143 if (colCount != null) 1144 { 1145 pageContext.setColumnCount(colCount); 1146 } 1147 1148 final Object newColumn = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "start-new-column"); 1149 if (OfficeToken.TRUE.equals(newColumn)) 1150 { 1151 setColumnBreakPending(true); 1152 } 1153 } 1154 startGroupInstance(final AttributeMap attrs)1155 protected void startGroupInstance(final AttributeMap attrs) 1156 throws IOException, DataSourceException, ReportProcessingException 1157 { 1158 if (getGroupContext().isGroupWithRepeatingSection()) 1159 { 1160 setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber())); 1161 } 1162 } 1163 endGroup(final AttributeMap attrs)1164 protected void endGroup(final AttributeMap attrs) 1165 throws IOException, DataSourceException, ReportProcessingException 1166 { 1167 if (getGroupContext().isGroupWithRepeatingSection()) 1168 { 1169 setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber())); 1170 } 1171 1172 super.endGroup(attrs); 1173 finishSection(); 1174 1175 activePageContext.pop(); 1176 } 1177 finishSection()1178 private void finishSection() 1179 throws ReportProcessingException 1180 { 1181 final PageContext pageContext = getCurrentContext(); 1182 if (pageContext.isSectionOpen()) 1183 { 1184 pageContext.setSectionOpen(false); 1185 try 1186 { 1187 getXmlWriter().writeCloseTag(); 1188 } 1189 catch (IOException e) 1190 { 1191 throw new ReportProcessingException("IOError", e); 1192 } 1193 } 1194 } 1195 endReportSection(final AttributeMap attrs, final int role)1196 protected void endReportSection(final AttributeMap attrs, final int role) 1197 throws IOException, DataSourceException, ReportProcessingException 1198 { 1199 if (role == ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER) 1200 { 1201 finishBuffering(); 1202 return; 1203 } 1204 1205 final CSSNumericValue result = sectionHeight.getResult(); 1206 if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER) 1207 { 1208 final PageContext pageContext = getCurrentContext(); 1209 pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result); 1210 } 1211 else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER) 1212 { 1213 final PageContext pageContext = getCurrentContext(); 1214 pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result); 1215 } 1216 else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER) 1217 { 1218 final PageContext pageContext = getCurrentContext(); 1219 pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result); 1220 } 1221 else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER) 1222 { 1223 final PageContext pageContext = getCurrentContext(); 1224 pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result); 1225 } 1226 else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES) 1227 { 1228 if (variables == null) 1229 { 1230 variables = finishBuffering().getXmlBuffer(); 1231 } 1232 else 1233 { 1234 variables += finishBuffering().getXmlBuffer(); 1235 } 1236 } 1237 else if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER) 1238 { 1239 final String headerText = finishBuffering().getXmlBuffer(); 1240 final int iterationCount = getGroupContext().getParent().getIterationCount(); 1241 final boolean repeat = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "repeat-section")); 1242 if (!repeat || iterationCount > 0) 1243 { 1244 getXmlWriter().writeText(headerText); 1245 } 1246 } 1247 else if (role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER) 1248 { 1249 final String footerText = finishBuffering().getXmlBuffer(); 1250 // how do we detect whether this is the last group footer? 1251 getXmlWriter().writeText(footerText); 1252 } 1253 1254 } 1255 endReport(final ReportStructureRoot report)1256 public void endReport(final ReportStructureRoot report) 1257 throws DataSourceException, ReportProcessingException 1258 { 1259 super.endReport(report); 1260 variablesDeclarations = null; 1261 1262 try 1263 { 1264 // Write the settings .. 1265 final AttributeList rootAttributes = new AttributeList(); 1266 rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS); 1267 rootAttributes.addNamespaceDeclaration("config", OfficeNamespaces.CONFIG); 1268 rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS); 1269 rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version", 1270 OfficeDocumentReportTarget.ODF_VERSION); 1271 final OutputStream outputStream = getOutputRepository().createOutputStream("settings.xml", "text/xml"); 1272 final XmlWriter xmlWriter = new XmlWriter(new OutputStreamWriter(outputStream, "UTF-8"), createTagDescription()); 1273 xmlWriter.setAlwaysAddNamespace(true); 1274 xmlWriter.writeXmlDeclaration("UTF-8"); 1275 xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-settings", rootAttributes, XmlWriterSupport.OPEN); 1276 xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "settings", XmlWriterSupport.OPEN); 1277 xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item-set", NAME, "ooo:configuration-settings", XmlWriterSupport.OPEN); 1278 1279 final AttributeList configAttributes = new AttributeList(); 1280 configAttributes.setAttribute(OfficeNamespaces.CONFIG, NAME, "TableRowKeep"); 1281 configAttributes.setAttribute(OfficeNamespaces.CONFIG, "type", "boolean"); 1282 xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item", configAttributes, XmlWriterSupport.OPEN); 1283 xmlWriter.writeText(OfficeToken.TRUE); 1284 xmlWriter.writeCloseTag(); 1285 1286 xmlWriter.writeCloseTag(); 1287 xmlWriter.writeCloseTag(); 1288 xmlWriter.writeCloseTag(); 1289 xmlWriter.close(); 1290 1291 copyMeta(); 1292 } 1293 catch (IOException ioe) 1294 { 1295 throw new ReportProcessingException("Failed to write settings document"); 1296 } 1297 } 1298 endOther(final AttributeMap attrs)1299 protected void endOther(final AttributeMap attrs) 1300 throws IOException, DataSourceException, ReportProcessingException 1301 { 1302 final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs); 1303 final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs); 1304 1305 final boolean isInternalNS = ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace); 1306 final boolean isTableNs = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace); 1307 if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType)) 1308 { 1309 finishBuffering(); 1310 return; 1311 } 1312 1313 if (isInternalNS && (ObjectUtilities.equal(OfficeToken.IMAGE, elementType) || ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType))) 1314 { 1315 return; 1316 } 1317 1318 final XmlWriter xmlWriter = getXmlWriter(); 1319 if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) && !isRepeatingSection()) 1320 { 1321 if (variables != null) 1322 { 1323 // This cannot happen as long as the report sections only contain tables. But at some point in the 1324 // future they will be made of paragraphs, and then we are prepared .. 1325 //LOGGER.debug("Variables-Section " + variables); 1326 final String tag; 1327 if (sectionKeepTogether && expectedTableRowCount > 0) 1328 { 1329 tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT; 1330 } 1331 else 1332 { 1333 tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT; 1334 } 1335 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH, 1336 tag, getStylesCollection(), 1337 getGlobalStylesCollection(), getPredefinedStylesCollection()); 1338 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, 1339 tag, XmlWriterSupport.OPEN); 1340 xmlWriter.writeText(variables); 1341 xmlWriter.writeCloseTag(); 1342 variables = null; 1343 } 1344 /** 1345 // Only generate the empty paragraph, if we have to add the keep-together .. 1346 else if (cellEmpty && expectedTableRowCount > 0 && 1347 sectionKeepTogether && !firstCellSeen) 1348 { 1349 // we have no variables .. 1350 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH, 1351 TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(), 1352 getGlobalStylesCollection(), getPredefinedStylesCollection()); 1353 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, 1354 TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.CLOSE); 1355 } 1356 */ 1357 } 1358 1359 if (isTableNs && (ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) || ObjectUtilities.equal(OfficeToken.COVERED_TABLE_CELL, elementType))) 1360 { 1361 firstCellSeen = true; 1362 } 1363 if (isTableNs && ObjectUtilities.equal(OfficeToken.TABLE, elementType)) 1364 { 1365 if (getCurrentRole() == ROLE_DETAIL) 1366 { 1367 if (!isTableMergeActive()) 1368 { 1369 // We do not merge the detail bands, so an ordinary close will do. 1370 xmlWriter.writeCloseTag(); 1371 } 1372 else if (detailBandProcessingState == DETAIL_SECTION_FIRST_STARTED) 1373 { 1374 final int keepTogetherState = getCurrentContext().getKeepTogether(); 1375 if (keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL) 1376 { 1377 xmlWriter.writeCloseTag(); 1378 detailBandProcessingState = DETAIL_SECTION_FIRST_PRINTED; 1379 } 1380 else 1381 { 1382 detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED; 1383 } 1384 } 1385 else if (detailBandProcessingState == DETAIL_SECTION_OTHER_STARTED) 1386 { 1387 detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED; 1388 } 1389 } 1390 else 1391 { 1392 xmlWriter.writeCloseTag(); 1393 } 1394 if (isSectionPagebreakAfter(attrs)) 1395 { 1396 setPagebreakDefinition(new PageBreakDefinition(false)); 1397 } 1398 } 1399 else 1400 { 1401 xmlWriter.writeCloseTag(); 1402 } 1403 } 1404 endGroupBody(final AttributeMap attrs)1405 protected void endGroupBody(final AttributeMap attrs) 1406 throws IOException, DataSourceException, ReportProcessingException 1407 { 1408 if (tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED) 1409 { 1410 // closes the table .. 1411 final XmlWriter xmlWriter = getXmlWriter(); 1412 xmlWriter.writeCloseTag(); 1413 detailBandProcessingState = DETAIL_SECTION_WAIT; 1414 } 1415 1416 } 1417 endContent(final AttributeMap attrs)1418 protected void endContent(final AttributeMap attrs) 1419 throws IOException, DataSourceException, ReportProcessingException 1420 { 1421 finishSection(); 1422 final BufferState bodyText = finishBuffering(); 1423 final XmlWriter writer = getXmlWriter(); 1424 1425 final Map definedMappings = variablesDeclarations.getDefinedMappings(); 1426 if (!definedMappings.isEmpty()) 1427 { 1428 writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decls", XmlWriterSupport.OPEN); 1429 final Iterator mappingsIt = definedMappings.entrySet().iterator(); 1430 while (mappingsIt.hasNext()) 1431 { 1432 final Map.Entry entry = (Map.Entry) mappingsIt.next(); 1433 final AttributeList entryList = new AttributeList(); 1434 entryList.setAttribute(OfficeNamespaces.TEXT_NS, NAME, (String) entry.getKey()); 1435 entryList.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, (String) entry.getValue()); 1436 writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decl", entryList, XmlWriterSupport.CLOSE); 1437 } 1438 writer.writeCloseTag(); 1439 } 1440 1441 writer.writeStream(bodyText.getXmlAsReader()); 1442 writer.setLineEmpty(true); 1443 writer.writeCloseTag(); 1444 } 1445 getExportDescriptor()1446 public String getExportDescriptor() 1447 { 1448 return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_TEXT; 1449 } 1450 } 1451