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; 24 25 import com.sun.star.report.DataSourceFactory; 26 import com.sun.star.report.ImageService; 27 import com.sun.star.report.InputRepository; 28 import com.sun.star.report.OfficeToken; 29 import com.sun.star.report.OutputRepository; 30 import com.sun.star.report.ReportEngineParameterNames; 31 import com.sun.star.report.SDBCReportDataFactory; 32 import com.sun.star.report.pentaho.OfficeNamespaces; 33 import com.sun.star.report.pentaho.layoutprocessor.ImageElementContext; 34 import com.sun.star.report.pentaho.model.OfficeDocument; 35 import com.sun.star.report.pentaho.model.OfficeStyle; 36 import com.sun.star.report.pentaho.model.OfficeStyles; 37 import com.sun.star.report.pentaho.model.OfficeStylesCollection; 38 import com.sun.star.report.pentaho.styles.LengthCalculator; 39 import com.sun.star.report.pentaho.styles.StyleMapper; 40 import java.util.logging.Level; 41 import java.util.logging.Logger; 42 import javax.xml.transform.OutputKeys; 43 import javax.xml.transform.Transformer; 44 import javax.xml.transform.TransformerFactory; 45 import javax.xml.transform.dom.DOMSource; 46 import javax.xml.transform.stream.StreamResult; 47 import javax.xml.parsers.DocumentBuilderFactory; 48 import org.w3c.dom.Document; 49 import org.w3c.dom.Node; 50 import org.w3c.dom.NodeList; 51 import org.xml.sax.InputSource; 52 53 import java.awt.Image; 54 55 import java.io.BufferedReader; 56 import java.io.ByteArrayInputStream; 57 import java.io.IOException; 58 import java.io.InputStreamReader; 59 import java.io.OutputStream; 60 import java.io.OutputStreamWriter; 61 import java.io.Reader; 62 import java.io.StringReader; 63 import java.io.StringWriter; 64 import java.io.Writer; 65 import java.io.InputStream; 66 67 import java.util.ArrayList; 68 import java.util.Iterator; 69 import java.util.Map; 70 import java.util.zip.DeflaterOutputStream; 71 import java.util.zip.InflaterInputStream; 72 73 import org.apache.commons.logging.Log; 74 import org.apache.commons.logging.LogFactory; 75 76 import org.jfree.layouting.input.style.parser.CSSValueFactory; 77 import org.jfree.layouting.input.style.parser.StyleSheetParserUtil; 78 import org.jfree.layouting.input.style.values.CSSNumericType; 79 import org.jfree.layouting.input.style.values.CSSNumericValue; 80 import org.jfree.layouting.layouter.style.CSSValueResolverUtility; 81 import org.jfree.layouting.namespace.NamespaceDefinition; 82 import org.jfree.layouting.namespace.Namespaces; 83 import org.jfree.layouting.util.AttributeMap; 84 import org.jfree.layouting.util.LazyAttributeMap; 85 import org.jfree.report.DataFlags; 86 import org.jfree.report.DataSourceException; 87 import org.jfree.report.JFreeReportBoot; 88 import org.jfree.report.JFreeReportInfo; 89 import org.jfree.report.ReportProcessingException; 90 import org.jfree.report.flow.AbstractReportTarget; 91 import org.jfree.report.flow.ReportJob; 92 import org.jfree.report.flow.ReportStructureRoot; 93 import org.jfree.report.flow.ReportTargetUtil; 94 import org.jfree.report.structure.Element; 95 import org.jfree.report.structure.Section; 96 import org.jfree.report.util.AttributeNameGenerator; 97 import org.jfree.report.util.IntegerCache; 98 import org.jfree.report.util.MemoryByteArrayOutputStream; 99 100 import org.pentaho.reporting.libraries.base.util.FastStack; 101 import org.pentaho.reporting.libraries.base.util.IOUtils; 102 import org.pentaho.reporting.libraries.resourceloader.ResourceException; 103 import org.pentaho.reporting.libraries.resourceloader.ResourceKey; 104 import org.pentaho.reporting.libraries.resourceloader.ResourceManager; 105 import org.pentaho.reporting.libraries.xmlns.common.AttributeList; 106 import org.pentaho.reporting.libraries.xmlns.writer.DefaultTagDescription; 107 import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter; 108 import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport; 109 110 import org.w3c.css.sac.LexicalUnit; 111 112 /** 113 * Todo: Document me! 114 * 115 * @author Thomas Morgner 116 * @since 08.03.2007 117 */ 118 public abstract class OfficeDocumentReportTarget extends AbstractReportTarget 119 { 120 121 protected static final Log LOGGER = LogFactory.getLog(OfficeDocumentReportTarget.class); 122 public static final String HORIZONTAL_POS = "horizontal-pos"; 123 public static final String TAG_DEF_PREFIX = "com.sun.star.report.pentaho.output."; 124 public static final int ROLE_NONE = 0; 125 public static final int ROLE_REPORT_HEADER = 1; 126 public static final int ROLE_REPORT_FOOTER = 2; 127 public static final int ROLE_GROUP_HEADER = 3; 128 public static final int ROLE_GROUP_FOOTER = 4; 129 public static final int ROLE_REPEATING_GROUP_HEADER = 5; 130 public static final int ROLE_REPEATING_GROUP_FOOTER = 6; 131 public static final int ROLE_PAGE_HEADER = 7; 132 public static final int ROLE_PAGE_FOOTER = 8; 133 public static final int ROLE_DETAIL = 9; 134 public static final int ROLE_VARIABLES = 10; 135 public static final int ROLE_TEMPLATE = 11; 136 public static final int ROLE_SPREADSHEET_PAGE_HEADER = 12; 137 public static final int ROLE_SPREADSHEET_PAGE_FOOTER = 13; 138 public static final int STATE_IN_DOCUMENT = 0; 139 public static final int STATE_IN_BODY = 1; 140 public static final int STATE_IN_CONTENT = 2; 141 public static final int STATE_IN_GROUP = 3; 142 public static final int STATE_IN_GROUP_BODY = 4; 143 public static final int STATE_IN_SECTION = 5; 144 public static final int STATE_IN_OTHER = 6; 145 public static final int STATE_IN_GROUP_INSTANCE = 7; 146 public static final String FAILED = "Failed"; 147 public static final String VERTICAL_POS = "vertical-pos"; 148 private static final String ZERO_CM = "0cm"; 149 /** the version of the ODF specification to which generated documents 150 * shall conform. */ 151 public static final String ODF_VERSION = "1.2"; 152 153 protected static class BufferState 154 { 155 156 private final XmlWriter xmlWriter; 157 private final MemoryByteArrayOutputStream xmlBuffer; 158 private final OfficeStylesCollection stylesCollection; 159 BufferState(final XmlWriter xmlWriter, final MemoryByteArrayOutputStream xmlBuffer, final OfficeStylesCollection stylesCollection)160 protected BufferState(final XmlWriter xmlWriter, 161 final MemoryByteArrayOutputStream xmlBuffer, 162 final OfficeStylesCollection stylesCollection) 163 { 164 this.stylesCollection = stylesCollection; 165 this.xmlWriter = xmlWriter; 166 this.xmlBuffer = xmlBuffer; 167 } 168 getStylesCollection()169 public OfficeStylesCollection getStylesCollection() 170 { 171 return stylesCollection; 172 } 173 getXmlWriter()174 public XmlWriter getXmlWriter() 175 { 176 return xmlWriter; 177 } 178 getXmlBuffer()179 public String getXmlBuffer() throws ReportProcessingException 180 { 181 try 182 { 183 final byte[] zippedData = xmlBuffer.getRaw(); 184 final InputStreamReader reader = new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(zippedData, 0, xmlBuffer.getLength())), "UTF-16"); 185 final StringWriter writer = new StringWriter((zippedData.length / 2) + 1); 186 IOUtils.getInstance().copyWriter(reader, writer); 187 return writer.toString(); 188 } 189 catch (IOException e) 190 { 191 throw new ReportProcessingException("Failed to copy buffer", e); 192 } 193 } 194 getXmlAsReader()195 public Reader getXmlAsReader() throws ReportProcessingException 196 { 197 try 198 { 199 final byte[] zippedData = xmlBuffer.getRaw(); 200 return new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(zippedData, 0, xmlBuffer.getLength())), "UTF-16"); 201 } 202 catch (IOException e) 203 { 204 throw new ReportProcessingException("Failed to copy buffer", e); 205 } 206 } 207 } 208 209 protected static class GroupContext 210 { 211 212 private final GroupContext parent; 213 private int iterationCount; 214 private boolean groupWithRepeatingSection; 215 GroupContext(final GroupContext parent)216 protected GroupContext(final GroupContext parent) 217 { 218 this.parent = parent; 219 } 220 getParent()221 public GroupContext getParent() 222 { 223 return parent; 224 } 225 getIterationCount()226 public int getIterationCount() 227 { 228 return iterationCount; 229 } 230 setIterationCount(final int iterationCount)231 public void setIterationCount(final int iterationCount) 232 { 233 this.iterationCount = iterationCount; 234 } 235 isGroupWithRepeatingSection()236 public boolean isGroupWithRepeatingSection() 237 { 238 return groupWithRepeatingSection; 239 } 240 setGroupWithRepeatingSection(final boolean groupWithRepeatingSection)241 public void setGroupWithRepeatingSection(final boolean groupWithRepeatingSection) 242 { 243 this.groupWithRepeatingSection = groupWithRepeatingSection; 244 } 245 toString()246 public String toString() 247 { 248 return "GroupContext{" + "parent=" + parent + ", iterationCount=" + iterationCount + ", groupWithRepeatingSection=" + groupWithRepeatingSection + '}'; 249 } 250 } 251 private final FastStack states; 252 private int currentRole; 253 private final FastStack xmlWriters; 254 private XmlWriter rootXmlWriter; 255 /** 256 * This styles-collection contains all styles that were predefined in the report definition file. The common styles 257 * and the master-styles will be written unmodified, the automatic styles will be ignored. 258 */ 259 private OfficeStylesCollection predefinedStylesCollection; 260 /** 261 * This styles-collection contains all master-styles that have been generated by the report definition process. It 262 * also contains all automatic styles that have been generated for the page-bands (and the pagebands as well). 263 */ 264 private OfficeStylesCollection globalStylesCollection; 265 /** 266 * The content styles collection contains all automatic styles that have been generated for the normal-flow content. 267 */ 268 private OfficeStylesCollection contentStylesCollection; 269 private final OutputRepository outputRepository; 270 private final InputRepository inputRepository; 271 private final AttributeNameGenerator tableNameGenerator; 272 private final AttributeNameGenerator frameNameGenerator; 273 private final AttributeNameGenerator autoStyleNameGenerator; 274 private final String target; 275 private static final int INITIAL_BUFFER_SIZE = 40960; 276 private StyleMapper styleMapper; 277 private StyleSheetParserUtil styleSheetParserUtil; 278 private final AttributeNameGenerator imageNames; 279 private final ImageProducer imageProducer; 280 private final OleProducer oleProducer; 281 private GroupContext groupContext; 282 private static final boolean DEBUG_ELEMENTS = 283 JFreeReportBoot.getInstance().getExtendedConfig().getBoolProperty("com.sun.star.report.pentaho.output.DebugElements"); 284 OfficeDocumentReportTarget(final ReportJob reportJob, final ResourceManager resourceManager, final ResourceKey baseResource, final InputRepository inputRepository, final OutputRepository outputRepository, final String target, final ImageService imageService, final DataSourceFactory datasourcefactory)285 protected OfficeDocumentReportTarget(final ReportJob reportJob, 286 final ResourceManager resourceManager, 287 final ResourceKey baseResource, 288 final InputRepository inputRepository, 289 final OutputRepository outputRepository, 290 final String target, 291 final ImageService imageService, 292 final DataSourceFactory datasourcefactory) 293 throws ReportProcessingException 294 { 295 super(reportJob, resourceManager, baseResource); 296 if (imageService == null) 297 { 298 throw new NullPointerException("ImageService must not be null"); 299 } 300 if (target == null) 301 { 302 throw new NullPointerException("Target-Name must not be null"); 303 } 304 305 this.target = target; 306 307 this.tableNameGenerator = new AttributeNameGenerator(); 308 this.frameNameGenerator = new AttributeNameGenerator(); 309 this.autoStyleNameGenerator = new AttributeNameGenerator(); 310 this.outputRepository = outputRepository; 311 this.inputRepository = inputRepository; 312 this.states = new FastStack(); 313 this.xmlWriters = new FastStack(); 314 this.imageNames = new AttributeNameGenerator(); 315 316 this.imageProducer = new ImageProducer(inputRepository, outputRepository, imageService); 317 this.oleProducer = new OleProducer(inputRepository, outputRepository, imageService, datasourcefactory, (Integer) reportJob.getParameters().get(ReportEngineParameterNames.MAXROWS)); 318 319 try 320 { 321 final ResourceManager realResourceManager = getResourceManager(); 322 styleMapper = StyleMapper.loadInstance(realResourceManager); 323 } 324 catch (ResourceException e) 325 { 326 throw new ReportProcessingException("Failed to load style-mapper", e); 327 } 328 } 329 getTargetMimeType()330 protected abstract String getTargetMimeType(); 331 getOutputRepository()332 protected OutputRepository getOutputRepository() 333 { 334 return outputRepository; 335 } 336 getInputRepository()337 protected InputRepository getInputRepository() 338 { 339 return inputRepository; 340 } 341 342 /** 343 * Starts the output of a new office document. This method writes the generic 'office:document-content' tag along with 344 * all known namespace declarations. 345 * 346 * @param report the report object. 347 * @throws DataSourceException if there was an error accessing the datasource 348 * @throws ReportProcessingException if some other error occurred. 349 */ startReport(final ReportStructureRoot report)350 public void startReport(final ReportStructureRoot report) 351 throws DataSourceException, ReportProcessingException 352 { 353 imageNames.reset(); 354 this.groupContext = new GroupContext(null); 355 356 final DefaultTagDescription tagDescription = createTagDescription(); 357 try 358 { 359 final OutputStream outputStream = outputRepository.createOutputStream(target, "text/xml"); 360 final Writer writer = new OutputStreamWriter(outputStream, "UTF-8"); 361 362 this.rootXmlWriter = new XmlWriter(writer, tagDescription); 363 this.rootXmlWriter.setAlwaysAddNamespace(true); 364 365 final AttributeList rootAttributes = new AttributeList(); 366 rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS); 367 rootAttributes.addNamespaceDeclaration("style", OfficeNamespaces.STYLE_NS); 368 rootAttributes.addNamespaceDeclaration("text", OfficeNamespaces.TEXT_NS); 369 rootAttributes.addNamespaceDeclaration("table", OfficeNamespaces.TABLE_NS); 370 rootAttributes.addNamespaceDeclaration("draw", OfficeNamespaces.DRAWING_NS); 371 rootAttributes.addNamespaceDeclaration("fo", OfficeNamespaces.FO_NS); 372 rootAttributes.addNamespaceDeclaration("xlink", OfficeNamespaces.XLINK_NS); 373 rootAttributes.addNamespaceDeclaration("dc", OfficeNamespaces.PURL_NS); 374 rootAttributes.addNamespaceDeclaration("meta", OfficeNamespaces.META_NS); 375 rootAttributes.addNamespaceDeclaration("number", OfficeNamespaces.DATASTYLE_NS); 376 rootAttributes.addNamespaceDeclaration("svg", OfficeNamespaces.SVG_NS); 377 rootAttributes.addNamespaceDeclaration("chart", OfficeNamespaces.CHART_NS); 378 rootAttributes.addNamespaceDeclaration("chartooo", OfficeNamespaces.CHARTOOO_NS); 379 rootAttributes.addNamespaceDeclaration("dr3d", OfficeNamespaces.DR3D_NS); 380 rootAttributes.addNamespaceDeclaration("math", OfficeNamespaces.MATHML_NS); 381 rootAttributes.addNamespaceDeclaration("form", OfficeNamespaces.FORM_NS); 382 rootAttributes.addNamespaceDeclaration("script", OfficeNamespaces.SCRIPT_NS); 383 rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS); 384 rootAttributes.addNamespaceDeclaration("ooow", OfficeNamespaces.OOW2004_NS); 385 rootAttributes.addNamespaceDeclaration("oooc", OfficeNamespaces.OOC2004_NS); 386 rootAttributes.addNamespaceDeclaration("dom", OfficeNamespaces.XML_EVENT_NS); 387 rootAttributes.addNamespaceDeclaration("xforms", OfficeNamespaces.XFORMS_NS); 388 rootAttributes.addNamespaceDeclaration("xsd", OfficeNamespaces.XSD_NS); 389 rootAttributes.addNamespaceDeclaration("xsi", OfficeNamespaces.XSI_NS); 390 rootAttributes.addNamespaceDeclaration("grddl", OfficeNamespaces.GRDDL_NS); 391 rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version", 392 ODF_VERSION); 393 394 this.rootXmlWriter.writeXmlDeclaration("UTF-8"); 395 this.rootXmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-content", rootAttributes, XmlWriterSupport.OPEN); 396 397 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_DOCUMENT)); 398 399 autoStyleNameGenerator.reset(); 400 tableNameGenerator.reset(); 401 frameNameGenerator.reset(); 402 403 final OfficeDocument reportDoc = (OfficeDocument) report; 404 predefinedStylesCollection = reportDoc.getStylesCollection(); 405 406 final OfficeStyles commonStyles = predefinedStylesCollection.getCommonStyles(); 407 if (!commonStyles.containsStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS)) 408 { 409 final OfficeStyle graphicsDefaultStyle = new OfficeStyle(); 410 graphicsDefaultStyle.setStyleFamily(OfficeToken.GRAPHIC); 411 graphicsDefaultStyle.setStyleName(OfficeToken.GRAPHICS); 412 final Element graphicProperties = produceFirstChild(graphicsDefaultStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES); 413 graphicProperties.setAttribute(OfficeNamespaces.TEXT_NS, "anchor-type", OfficeToken.PARAGRAPH); 414 graphicProperties.setAttribute(OfficeNamespaces.SVG_NS, "x", ZERO_CM); 415 graphicProperties.setAttribute(OfficeNamespaces.SVG_NS, "y", ZERO_CM); 416 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "wrap", "dynamic"); 417 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "number-wrapped-paragraphs", "no-limit"); 418 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "wrap-contour", OfficeToken.FALSE); 419 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top"); // changed for chart 420 421 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", OfficeToken.PARAGRAPH); 422 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left"); // changed for chart 423 424 graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", OfficeToken.PARAGRAPH); 425 commonStyles.addStyle(graphicsDefaultStyle); 426 } 427 428 // Make sure that later generated styles do not overwrite existing styles. 429 fillStyleNameGenerator(predefinedStylesCollection); 430 431 contentStylesCollection = new OfficeStylesCollection(); 432 globalStylesCollection = new OfficeStylesCollection(); 433 434 startBuffering(contentStylesCollection, true); 435 } 436 catch (IOException e) 437 { 438 throw new ReportProcessingException(FAILED, e); 439 } 440 } 441 getAutoStyleNameGenerator()442 protected AttributeNameGenerator getAutoStyleNameGenerator() 443 { 444 return autoStyleNameGenerator; 445 } 446 fillStyleNameGenerator(final OfficeStylesCollection stylesCollection)447 private void fillStyleNameGenerator(final OfficeStylesCollection stylesCollection) 448 { 449 final OfficeStyles commonStyles = stylesCollection.getCommonStyles(); 450 final OfficeStyle[] allCommonStyles = commonStyles.getAllStyles(); 451 for (int i = 0; i < allCommonStyles.length; i++) 452 { 453 final OfficeStyle style = allCommonStyles[i]; 454 autoStyleNameGenerator.generateName(style.getStyleName()); 455 } 456 457 final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles(); 458 final OfficeStyle[] allAutoStyles = autoStyles.getAllStyles(); 459 for (int i = 0; i < allAutoStyles.length; i++) 460 { 461 final OfficeStyle style = allAutoStyles[i]; 462 autoStyleNameGenerator.generateName(style.getStyleName()); 463 } 464 } 465 getPredefinedStylesCollection()466 public OfficeStylesCollection getPredefinedStylesCollection() 467 { 468 return predefinedStylesCollection; 469 } 470 getGlobalStylesCollection()471 public OfficeStylesCollection getGlobalStylesCollection() 472 { 473 return globalStylesCollection; 474 } 475 getContentStylesCollection()476 public OfficeStylesCollection getContentStylesCollection() 477 { 478 return contentStylesCollection; 479 } 480 481 /** 482 * Returns the XML-Writer tag description. This description defines whether an element can have character data inside. 483 * Such element will disable the indention, as in that case the additional whitespaces might alter the meaning of the 484 * element's contents. 485 * 486 * @return the tag description library. 487 */ createTagDescription()488 protected DefaultTagDescription createTagDescription() 489 { 490 final DefaultTagDescription tagDescription = new DefaultTagDescription(); 491 tagDescription.configure(JFreeReportBoot.getInstance().getGlobalConfig(), 492 OfficeDocumentReportTarget.TAG_DEF_PREFIX); 493 return tagDescription; 494 } 495 496 /** 497 * Returns the current processing state. 498 * 499 * @return the processing state. 500 */ getCurrentState()501 protected int getCurrentState() 502 { 503 if (states.isEmpty()) 504 { 505 throw new IllegalStateException(); 506 } 507 final Integer o = (Integer) states.peek(); 508 return o; 509 } 510 511 /** 512 * Starts the processing of an element and updates the processing state. This will select an appropriate handler method 513 * for the call and will call one of the start* methods. 514 * 515 * @param roAttrs the attribute map for the current element 516 * @throws DataSourceException 517 * @throws ReportProcessingException 518 */ startElement(final AttributeMap roAttrs)519 public final void startElement(final AttributeMap roAttrs) 520 throws DataSourceException, ReportProcessingException 521 { 522 final AttributeMap attrs = new LazyAttributeMap(roAttrs); 523 // todo 524 if (DEBUG_ELEMENTS) 525 { 526 LOGGER.debug("Starting " + getCurrentState() + '/' + states.size() + ' ' + ReportTargetUtil.getNamespaceFromAttribute(attrs) + " -> " + ReportTargetUtil.getElemenTypeFromAttribute(attrs)); 527 } 528 try 529 { 530 switch (getCurrentState()) 531 { 532 case OfficeDocumentReportTarget.STATE_IN_DOCUMENT: 533 { 534 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OFFICE_NS, "body", attrs)) 535 { 536 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_BODY)); 537 startBody(attrs); 538 } 539 else 540 { 541 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER)); 542 if (!isFilteredNamespace(ReportTargetUtil.getNamespaceFromAttribute(attrs))) 543 { 544 startOther(attrs); 545 } 546 } 547 break; 548 } 549 case OfficeDocumentReportTarget.STATE_IN_BODY: 550 { 551 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OFFICE_NS, "report", attrs)) 552 { 553 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_CONTENT)); 554 startContent(attrs); 555 } 556 else 557 { 558 throw new IllegalStateException("The 'office:body' element must have exactly one child of type 'report'"); 559 } 560 break; 561 } 562 case OfficeDocumentReportTarget.STATE_IN_CONTENT: 563 { 564 // Either a ordinary section or a group .. 565 // A group. 566 if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "report-body", attrs)) 567 { 568 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_BODY)); 569 startGroupBody(attrs); 570 } 571 else 572 { 573 // Either a template-section, page-header, page-footer, report-header, report-footer 574 // or variables-section 575 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION)); 576 if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "template", attrs)) 577 { 578 currentRole = OfficeDocumentReportTarget.ROLE_TEMPLATE; 579 } 580 else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "page-header", attrs)) 581 { 582 if ("spreadsheet-section".equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "role"))) 583 { 584 currentRole = OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER; 585 } 586 else 587 { 588 currentRole = OfficeDocumentReportTarget.ROLE_PAGE_HEADER; 589 } 590 } 591 else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "page-footer", attrs)) 592 { 593 if ("spreadsheet-section".equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "role"))) 594 { 595 currentRole = OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER; 596 } 597 else 598 { 599 currentRole = OfficeDocumentReportTarget.ROLE_PAGE_FOOTER; 600 } 601 } 602 else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "report-header", attrs)) 603 { 604 currentRole = OfficeDocumentReportTarget.ROLE_REPORT_HEADER; 605 } 606 else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "report-footer", attrs)) 607 { 608 currentRole = OfficeDocumentReportTarget.ROLE_REPORT_FOOTER; 609 } 610 else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs)) 611 { 612 currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES; 613 } 614 else 615 { 616 throw new IllegalStateException("Expected either 'template', 'report-body', " + "'report-header', 'report-footer', 'variables-section', 'page-header' or 'page-footer'"); 617 } 618 startReportSection(attrs, currentRole); 619 } 620 break; 621 } 622 case OfficeDocumentReportTarget.STATE_IN_GROUP_BODY: 623 { 624 // We now expect either an other group or a detail band. 625 626 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group", attrs)) 627 { 628 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP)); 629 groupContext = new GroupContext(groupContext); 630 startGroup(attrs); 631 } 632 else 633 { 634 // Either a variables-section, or a detail-band 635 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION)); 636 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "detail", attrs)) 637 { 638 currentRole = OfficeDocumentReportTarget.ROLE_DETAIL; 639 } 640 else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs)) 641 { 642 currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES; 643 } 644 else 645 { 646 throw new IllegalStateException("Expected either 'group', 'detail' or 'variables-section'"); 647 } 648 startReportSection(attrs, currentRole); 649 } 650 break; 651 } 652 case OfficeDocumentReportTarget.STATE_IN_GROUP: 653 { 654 // A group can carry a repeating group header/footer or a group-instance section. 655 if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "group-instance", attrs)) 656 { 657 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE)); 658 startGroupInstance(attrs); 659 } 660 else 661 { 662 // repeating group header/footer, but *no* variables section 663 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION)); 664 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-header", attrs) && OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeated-section"))) 665 { 666 currentRole = OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER; 667 } 668 else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-footer", attrs) && OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeated-section"))) 669 { 670 currentRole = OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER; 671 } 672 else 673 { 674 throw new IllegalStateException("Expected either 'group-instance', " + "'repeating group-header' or 'repeating group-footer'"); 675 } 676 startReportSection(attrs, currentRole); 677 } 678 break; 679 } 680 case OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE: 681 { 682 if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "group-body", attrs)) 683 { 684 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_BODY)); 685 startGroupBody(attrs); 686 } 687 else 688 { 689 // Either a group-header or group-footer or variables-section 690 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION)); 691 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-header", attrs)) 692 { 693 currentRole = OfficeDocumentReportTarget.ROLE_GROUP_HEADER; 694 } 695 else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-footer", attrs)) 696 { 697 currentRole = OfficeDocumentReportTarget.ROLE_GROUP_FOOTER; 698 } 699 else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs)) 700 { 701 currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES; 702 } 703 else 704 { 705 throw new IllegalStateException("Expected either 'group-body', 'group-header', 'group-footer' or 'variables-section'"); 706 } 707 startReportSection(attrs, currentRole); 708 } 709 break; 710 } 711 case OfficeDocumentReportTarget.STATE_IN_SECTION: 712 { 713 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER)); 714 startOther(attrs); 715 break; 716 } 717 case OfficeDocumentReportTarget.STATE_IN_OTHER: 718 { 719 states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER)); 720 startOther(attrs); 721 break; 722 } 723 default: 724 throw new IllegalStateException("Failure: " + getCurrentState()); 725 } 726 } 727 catch (IOException ioe) 728 { 729 LOGGER.error("ReportProcessing failed", ioe); 730 throw new ReportProcessingException("Failed to write content", ioe); 731 } 732 // finally 733 // { 734 // LOGGER.debug ("Started " + getNamespaceFromAttribute(attrs) + ":" + 735 // getElemenTypeFromAttribute(attrs) + " -> " + getCurrentState()); 736 // } 737 } 738 getGroupContext()739 protected GroupContext getGroupContext() 740 { 741 return groupContext; 742 } 743 performStyleProcessing(final AttributeMap attrs)744 protected void performStyleProcessing(final AttributeMap attrs) 745 throws ReportProcessingException 746 { 747 final OfficeStylesCollection stylesCollection = getStylesCollection(); 748 final OfficeStylesCollection predefCollection = getPredefinedStylesCollection(); 749 final OfficeStylesCollection globalStylesCollection = getGlobalStylesCollection(); 750 751 final String elementNamespace = 752 ReportTargetUtil.getNamespaceFromAttribute(attrs); 753 final String elementName = 754 ReportTargetUtil.getElemenTypeFromAttribute(attrs); 755 756 final String[] namespaces = attrs.getNameSpaces(); 757 for (int i = 0; i < namespaces.length; i++) 758 { 759 final String attrNamespace = namespaces[i]; 760 if (isFilteredNamespace(attrNamespace)) 761 { 762 continue; 763 } 764 765 final Map attributes = attrs.getAttributes(attrNamespace); 766 final Iterator iterator = attributes.entrySet().iterator(); 767 while (iterator.hasNext()) 768 { 769 final Map.Entry entry = (Map.Entry) iterator.next(); 770 final String attrName = (String) entry.getKey(); 771 final String attrValue = String.valueOf(entry.getValue()); 772 773 final String styleFamily = styleMapper.getStyleFamilyFor(elementNamespace, elementName, attrNamespace, attrName); 774 if (styleFamily == null) 775 { 776 // None of the known style attributes. 777 continue; 778 } 779 780 if (styleMapper.isListOfStyles(elementNamespace, elementName, attrNamespace, attrName)) 781 { 782 // ignored for now. 783 LOGGER.warn("List of styles is not yet implemented."); 784 continue; 785 } 786 787 // Copy styles is only called once per style. 788 StyleUtilities.copyStyle(styleFamily, attrValue, stylesCollection, globalStylesCollection, predefCollection); 789 } 790 } 791 } 792 startBody(final AttributeMap attrs)793 protected void startBody(final AttributeMap attrs) 794 throws IOException 795 { 796 getXmlWriter().writeTag(OfficeNamespaces.OFFICE_NS, "body", XmlWriterSupport.OPEN); 797 } 798 allowBuffering(final int role)799 private final boolean allowBuffering(final int role) 800 { 801 return (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_TEMPLATE); 802 } 803 startReportSection(final AttributeMap attrs, final int role)804 protected void startReportSection(final AttributeMap attrs, final int role) 805 throws IOException, DataSourceException, ReportProcessingException 806 { 807 if (allowBuffering(role)) 808 { 809 startBuffering(new OfficeStylesCollection(), true); 810 } 811 } 812 startContent(final AttributeMap attrs)813 protected abstract void startContent(final AttributeMap attrs) 814 throws IOException, DataSourceException, ReportProcessingException; 815 startGroup(final AttributeMap attrs)816 protected void startGroup(final AttributeMap attrs) 817 throws IOException, DataSourceException, ReportProcessingException 818 { 819 final Object repeatingHeaderOrFooter = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeating-header-or-footer"); 820 if (OfficeToken.TRUE.equals(repeatingHeaderOrFooter)) 821 { 822 getGroupContext().setGroupWithRepeatingSection(true); 823 } 824 825 final Object iterationCount = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "iteration-count"); 826 if (iterationCount instanceof Number) 827 { 828 final Number itNumber = (Number) iterationCount; 829 getGroupContext().setIterationCount(itNumber.intValue()); 830 } 831 } 832 startGroupInstance(final AttributeMap attrs)833 protected void startGroupInstance(final AttributeMap attrs) 834 throws IOException, DataSourceException, ReportProcessingException 835 { 836 } 837 startGroupBody(final AttributeMap attrs)838 protected void startGroupBody(final AttributeMap attrs) 839 throws IOException, DataSourceException, ReportProcessingException 840 { 841 } 842 startOther(final AttributeMap attrs)843 protected abstract void startOther(final AttributeMap attrs) 844 throws IOException, DataSourceException, ReportProcessingException; 845 processText(final String text)846 public void processText(final String text) 847 throws DataSourceException, ReportProcessingException 848 { 849 try 850 { 851 final XmlWriter xmlWriter = getXmlWriter(); 852 final BufferedReader br = new BufferedReader(new StringReader(text)); 853 String line = br.readLine(); 854 while (line != null) 855 { 856 xmlWriter.writeTextNormalized(line, false); 857 line = br.readLine(); 858 if (line != null) 859 { 860 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, "line-break", XmlWriterSupport.CLOSE); 861 } 862 } 863 } 864 catch (IOException e) 865 { 866 throw new ReportProcessingException(FAILED, e); 867 } 868 } 869 isFilteredNamespace(final String namespace)870 protected boolean isFilteredNamespace(final String namespace) 871 { 872 if (Namespaces.LIBLAYOUT_NAMESPACE.equals(namespace)) 873 { 874 return true; 875 } 876 if (JFreeReportInfo.REPORT_NAMESPACE.equals(namespace)) 877 { 878 return true; 879 } 880 if (JFreeReportInfo.REPORT_NAMESPACE.equals(namespace)) 881 { 882 return true; 883 } 884 if (JFreeReportInfo.COMPATIBILITY_NAMESPACE.equals(namespace)) 885 { 886 return true; 887 } 888 if (OfficeNamespaces.OOREPORT_NS.equals(namespace)) 889 { 890 return true; 891 } 892 return false; 893 } 894 processContent(final DataFlags value)895 public void processContent(final DataFlags value) 896 throws DataSourceException, ReportProcessingException 897 { 898 final Object rawvalue = value.getValue(); 899 if (rawvalue == null) 900 { 901 return; 902 } 903 904 // special handler for image (possibly also for URL ..) 905 if (rawvalue instanceof Image) 906 { 907 // do nothing yet. We should define something for that later .. 908 return; 909 } 910 911 final XmlWriter xmlWriter = getXmlWriter(); 912 final String text = String.valueOf(rawvalue); 913 try 914 { 915 final BufferedReader br = new BufferedReader(new StringReader(text)); 916 String line = br.readLine(); 917 while (line != null) 918 { 919 xmlWriter.writeTextNormalized(line, false); 920 line = br.readLine(); 921 if (line != null) 922 { 923 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, "line-break", XmlWriterSupport.CLOSE); 924 } 925 } 926 } 927 catch (IOException e) 928 { 929 throw new ReportProcessingException(FAILED, e); 930 } 931 } 932 endElement(final AttributeMap roAttrs)933 public final void endElement(final AttributeMap roAttrs) 934 throws DataSourceException, ReportProcessingException 935 { 936 final AttributeMap attrs = new LazyAttributeMap(roAttrs); 937 // final int oldState = getCurrentState(); 938 try 939 { 940 941 switch (getCurrentState()) 942 { 943 case OfficeDocumentReportTarget.STATE_IN_OTHER: 944 { 945 endOther(attrs); 946 break; 947 } 948 case OfficeDocumentReportTarget.STATE_IN_SECTION: 949 { 950 endReportSection(attrs, currentRole); 951 currentRole = OfficeDocumentReportTarget.ROLE_NONE; 952 break; 953 } 954 case OfficeDocumentReportTarget.STATE_IN_GROUP: 955 { 956 endGroup(attrs); 957 groupContext = groupContext.getParent(); 958 break; 959 } 960 case OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE: 961 { 962 endGroupInstance(attrs); 963 break; 964 } 965 case OfficeDocumentReportTarget.STATE_IN_GROUP_BODY: 966 { 967 endGroupBody(attrs); 968 break; 969 } 970 case OfficeDocumentReportTarget.STATE_IN_CONTENT: 971 { 972 endContent(attrs); 973 break; 974 } 975 case OfficeDocumentReportTarget.STATE_IN_BODY: 976 { 977 endBody(attrs); 978 break; 979 } 980 case OfficeDocumentReportTarget.STATE_IN_DOCUMENT: 981 { 982 throw new IllegalStateException("This cannot be."); 983 } 984 default: 985 { 986 throw new IllegalStateException("Invalid state encountered."); 987 } 988 } 989 } 990 catch (IOException ioe) 991 { 992 throw new ReportProcessingException("IO Error while writing content", 993 ioe); 994 } finally 995 { 996 states.pop(); 997 998 if (DEBUG_ELEMENTS) 999 { 1000 LOGGER.debug("Finished " + getCurrentState() + "/" + states.size() + " " + ReportTargetUtil.getNamespaceFromAttribute(attrs) + ":" + ReportTargetUtil.getElemenTypeFromAttribute(attrs)); 1001 } 1002 1003 } 1004 } 1005 endGroupBody(final AttributeMap attrs)1006 protected void endGroupBody(final AttributeMap attrs) 1007 throws IOException, DataSourceException, ReportProcessingException 1008 { 1009 } 1010 endGroupInstance(final AttributeMap attrs)1011 protected void endGroupInstance(final AttributeMap attrs) 1012 throws IOException, DataSourceException, ReportProcessingException 1013 { 1014 } 1015 getCurrentRole()1016 public int getCurrentRole() 1017 { 1018 return currentRole; 1019 } 1020 endOther(final AttributeMap attrs)1021 protected abstract void endOther(final AttributeMap attrs) 1022 throws IOException, DataSourceException, ReportProcessingException; 1023 endReportSection(final AttributeMap attrs, final int role)1024 protected void endReportSection(final AttributeMap attrs, 1025 final int role) 1026 throws IOException, DataSourceException, ReportProcessingException 1027 { 1028 if (allowBuffering(role)) 1029 { 1030 finishBuffering(); 1031 } 1032 } 1033 endGroup(final AttributeMap attrs)1034 protected void endGroup(final AttributeMap attrs) 1035 throws IOException, DataSourceException, ReportProcessingException 1036 { 1037 } 1038 endContent(final AttributeMap attrs)1039 protected abstract void endContent(final AttributeMap attrs) 1040 throws IOException, DataSourceException, ReportProcessingException; 1041 endBody(final AttributeMap attrs)1042 protected void endBody(final AttributeMap attrs) 1043 throws IOException, DataSourceException, ReportProcessingException 1044 { 1045 getXmlWriter().writeCloseTag(); 1046 } 1047 copyMeta()1048 public void copyMeta() 1049 { 1050 // now copy the meta.xml 1051 if (getInputRepository().isReadable("meta.xml")) 1052 { 1053 InputStream inputStream = null; 1054 try 1055 { 1056 inputStream = getInputRepository().createInputStream("meta.xml"); 1057 Document document = DocumentBuilderFactory.newInstance() 1058 .newDocumentBuilder().parse(new InputSource(inputStream)); 1059 NodeList nl = document.getElementsByTagName("document-meta/meta/generator"); 1060 Node node = document.getFirstChild().getFirstChild().getFirstChild().getFirstChild(); 1061 String creator = node.getNodeValue(); 1062 node.setNodeValue(creator + "/report_builder"); 1063 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 1064 transformer.setOutputProperty(OutputKeys.METHOD, "xml"); 1065 1066 final OutputStream outputMetaStream = getOutputRepository().createOutputStream("meta.xml", "text/xml"); 1067 StreamResult result = new StreamResult(outputMetaStream); 1068 DOMSource source = new DOMSource(document); 1069 transformer.transform(source, result); 1070 1071 //IOUtils.getInstance().copyStreams(inputStream, outputMetaStream); 1072 outputMetaStream.flush(); 1073 outputMetaStream.close(); 1074 } 1075 catch (java.lang.Exception ex) 1076 { 1077 } finally 1078 { 1079 if (inputStream != null) 1080 { 1081 try 1082 { 1083 inputStream.close(); 1084 } 1085 catch (IOException ex) 1086 { 1087 Logger.getLogger(OfficeDocumentReportTarget.class.getName()).log(Level.SEVERE, null, ex); 1088 } 1089 } 1090 } 1091 } 1092 } 1093 endReport(final ReportStructureRoot report)1094 public void endReport(final ReportStructureRoot report) 1095 throws DataSourceException, ReportProcessingException 1096 { 1097 if (xmlWriters.size() != 1) 1098 { 1099 throw new IllegalStateException("Invalid writer-stack state"); 1100 } 1101 1102 try 1103 { 1104 final StylesWriter inlineStylesWriter = new StylesWriter(rootXmlWriter); 1105 inlineStylesWriter.writeContentStyles(predefinedStylesCollection, contentStylesCollection); 1106 1107 final BufferState state = finishBuffering(); 1108 this.rootXmlWriter.writeStream(state.getXmlAsReader()); 1109 1110 final OutputStream stylesOutStream = 1111 outputRepository.createOutputStream("styles.xml", "text/xml"); 1112 final OutputStreamWriter osw = 1113 new OutputStreamWriter(stylesOutStream, "UTF-8"); 1114 final StylesWriter stylesWriter = new StylesWriter(osw); 1115 stylesWriter.writeGlobalStyles(predefinedStylesCollection, globalStylesCollection); 1116 stylesWriter.close(); 1117 1118 this.rootXmlWriter.writeCloseTag(); 1119 this.rootXmlWriter.close(); 1120 } 1121 catch (IOException e) 1122 { 1123 throw new ReportProcessingException(FAILED, e); 1124 } 1125 } 1126 getXmlWriter()1127 public XmlWriter getXmlWriter() 1128 { 1129 final BufferState bufferState = (BufferState) xmlWriters.peek(); 1130 return bufferState.getXmlWriter(); 1131 } 1132 getStylesCollection()1133 public OfficeStylesCollection getStylesCollection() 1134 { 1135 final BufferState bufferState = (BufferState) xmlWriters.peek(); 1136 return bufferState.getStylesCollection(); 1137 } 1138 startBuffering(final OfficeStylesCollection stylesCollection, final boolean indent)1139 public void startBuffering(final OfficeStylesCollection stylesCollection, 1140 final boolean indent) throws ReportProcessingException 1141 { 1142 final XmlWriter currentWriter; 1143 if (xmlWriters.isEmpty()) 1144 { 1145 currentWriter = rootXmlWriter; 1146 } 1147 else 1148 { 1149 final BufferState bufferState = (BufferState) xmlWriters.peek(); 1150 currentWriter = bufferState.getXmlWriter(); 1151 } 1152 1153 try 1154 { 1155 final MemoryByteArrayOutputStream out = 1156 new MemoryByteArrayOutputStream(INITIAL_BUFFER_SIZE, 256 * INITIAL_BUFFER_SIZE); 1157 final DeflaterOutputStream deflateOut = new DeflaterOutputStream(out); 1158 final OutputStreamWriter xmlBuffer = new OutputStreamWriter(deflateOut, "UTF-16"); 1159 // final StringWriter xmlBuffer = new StringWriter 1160 // (OfficeDocumentReportTarget.INITIAL_BUFFER_SIZE); 1161 final XmlWriter contentXmlWriter = new XmlWriter(xmlBuffer, createTagDescription()); 1162 contentXmlWriter.copyNamespaces(currentWriter); 1163 if (indent) 1164 { 1165 contentXmlWriter.setAdditionalIndent(currentWriter.getCurrentIndentLevel()); 1166 contentXmlWriter.setWriteFinalLinebreak(true); 1167 } 1168 else 1169 { 1170 contentXmlWriter.setWriteFinalLinebreak(false); 1171 } 1172 contentXmlWriter.setAlwaysAddNamespace(true); 1173 xmlWriters.push(new BufferState(contentXmlWriter, out, stylesCollection)); 1174 } 1175 catch (IOException ioe) 1176 { 1177 throw new ReportProcessingException("Unable to create the buffer", ioe); 1178 } 1179 } 1180 finishBuffering()1181 public BufferState finishBuffering() throws ReportProcessingException 1182 { 1183 final BufferState state = (BufferState) xmlWriters.pop(); 1184 try 1185 { 1186 state.getXmlWriter().close(); 1187 } 1188 catch (IOException e) 1189 { 1190 LOGGER.error("ReportProcessing failed", e); 1191 } 1192 return state; 1193 } 1194 commit()1195 public void commit() 1196 throws ReportProcessingException 1197 { 1198 // do not call flush before the report is fully finished. Every flush 1199 // causes the Office-Backend to fully ZIP all contents (it acts like a 1200 // 'Save' call from the UI) and that's expensive like hell 1201 } 1202 getNamespaceByUri(final String uri)1203 public NamespaceDefinition getNamespaceByUri(final String uri) 1204 { 1205 return null; 1206 } 1207 buildAttributeList(final AttributeMap attrs)1208 protected AttributeList buildAttributeList(final AttributeMap attrs) 1209 { 1210 final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs); 1211 final AttributeList attrList = new AttributeList(); 1212 final String[] namespaces = attrs.getNameSpaces(); 1213 for (int i = 0; i < namespaces.length; i++) 1214 { 1215 final String attrNamespace = namespaces[i]; 1216 if (isFilteredNamespace(attrNamespace)) 1217 { 1218 continue; 1219 } 1220 1221 final Map localAttributes = attrs.getAttributes(attrNamespace); 1222 final Iterator entries = localAttributes.entrySet().iterator(); 1223 while (entries.hasNext()) 1224 { 1225 final Map.Entry entry = (Map.Entry) entries.next(); 1226 final String key = String.valueOf(entry.getKey()); 1227 if (OfficeNamespaces.TABLE_NS.equals(attrNamespace) && "name".equals(key)) 1228 { 1229 final String tableName = String.valueOf(entry.getValue()); 1230 final String saneName = sanitizeName(tableName); 1231 attrList.setAttribute(attrNamespace, key, 1232 tableNameGenerator.generateName(saneName)); 1233 } 1234 else if (OfficeNamespaces.DRAWING_NS.equals(attrNamespace) && "name".equals(key) && !"equation".equals(elementType)) 1235 { 1236 final String objectName = String.valueOf(entry.getValue()); 1237 attrList.setAttribute(attrNamespace, key, 1238 frameNameGenerator.generateName(objectName)); 1239 } 1240 else 1241 { 1242 attrList.setAttribute(attrNamespace, key, String.valueOf(entry.getValue())); 1243 } 1244 } 1245 } 1246 return attrList; 1247 } 1248 sanitizeName(final String name)1249 protected String sanitizeName(final String name) 1250 { 1251 // A table name cannot contain spaces and should only contain 1252 // ascii-characters. 1253 if (name == null) 1254 { 1255 return ""; 1256 } 1257 final char[] chars = name.toCharArray(); 1258 final StringBuffer buffer = new StringBuffer(); 1259 for (int i = 0; i < chars.length; i++) 1260 { 1261 final char aChar = chars[i]; 1262 if (Character.isWhitespace(aChar)) 1263 { 1264 buffer.append('_'); 1265 } 1266 else 1267 { 1268 buffer.append(aChar); 1269 } 1270 } 1271 return buffer.toString(); 1272 } 1273 1274 /** 1275 * Returns the length in point. This method is f**king slow, it eats half of the processing time. I surely should 1276 * replace it with something more efficient later. 1277 * 1278 * @param text 1279 * @return 1280 */ parseLength(final String text)1281 protected CSSNumericValue parseLength(final String text) 1282 { 1283 if (styleSheetParserUtil == null) 1284 { 1285 styleSheetParserUtil = StyleSheetParserUtil.getInstance(); 1286 } 1287 1288 final LexicalUnit cssValue = styleSheetParserUtil.parseLexicalStyleValue( 1289 text); 1290 return CSSValueFactory.createLengthValue(cssValue); 1291 } 1292 isRepeatingSection()1293 protected boolean isRepeatingSection() 1294 { 1295 return (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER || currentRole == OfficeDocumentReportTarget.ROLE_PAGE_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_VARIABLES); 1296 1297 } 1298 deriveStyle(final String styleFamily, final String styleName)1299 protected OfficeStyle deriveStyle(final String styleFamily, final String styleName) 1300 throws ReportProcessingException 1301 { 1302 // autogenerate a style. The style has already been added to the current 1303 // auto-collection. 1304 final OfficeStyle style = StyleUtilities.deriveStyle(styleFamily, styleName, 1305 getStylesCollection(), getGlobalStylesCollection(), 1306 getPredefinedStylesCollection(), getAutoStyleNameGenerator()); 1307 return style; 1308 } 1309 startImageProcessing(final AttributeMap attrs)1310 protected void startImageProcessing(final AttributeMap attrs) 1311 throws ReportProcessingException 1312 { 1313 final Object imageData = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.IMAGE_DATA); 1314 final boolean preserveIRI = OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.PRESERVE_IRI)); 1315 1316 // for the first shot, do nothing fancy .. 1317 final ImageProducer.OfficeImage image = imageProducer.produceImage(imageData, preserveIRI); 1318 if (image != null) 1319 { 1320 final ImageElementContext imageContext = (ImageElementContext) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "image-context"); 1321 1322 // When scaling, we have to create an image-style. 1323 final CSSNumericValue width = image.getWidth(); // always in 100th of a mm 1324 1325 final CSSNumericValue height = image.getHeight(); // always in 100th of a mm 1326 1327 LOGGER.debug("Image " + imageData + " Width: " + width + ", Height: " + height); 1328 if (width == null || height == null) 1329 { 1330 return; 1331 } 1332 1333 CSSNumericValue imageAreaWidthVal; 1334 CSSNumericValue imageAreaHeightVal; 1335 CSSNumericValue posX = CSSNumericValue.createValue(CSSNumericType.CM, 0.0); 1336 CSSNumericValue posY = CSSNumericValue.createValue(CSSNumericType.CM, 0.0); 1337 1338 String styleName = null; 1339 if (imageContext != null) 1340 { 1341 imageAreaWidthVal = computeImageWidth(imageContext); 1342 imageAreaHeightVal = computeImageHeight(imageContext); 1343 1344 if (imageAreaWidthVal == null || imageAreaHeightVal == null) 1345 { 1346 LOGGER.debug("Image data returned from context is invalid. Maybe this is not an image?"); 1347 return; 1348 } 1349 else 1350 { 1351 // compute the clip-area .. 1352 final CSSNumericValue normalizedImageWidth = 1353 CSSValueResolverUtility.convertLength(width, imageAreaWidthVal.getType()); 1354 final CSSNumericValue normalizedImageHeight = 1355 CSSValueResolverUtility.convertLength(height, imageAreaHeightVal.getType()); 1356 1357 final String scale = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.SCALE); 1358 if (OfficeToken.NONE.equals(scale) && normalizedImageWidth.getValue() > 0 && normalizedImageHeight.getValue() > 0) 1359 { 1360 final double clipWidth = normalizedImageWidth.getValue() - imageAreaWidthVal.getValue(); 1361 final double clipHeight = normalizedImageHeight.getValue() - imageAreaHeightVal.getValue(); 1362 if (clipWidth > 0 && clipHeight > 0) 1363 { 1364 final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS); 1365 final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES); 1366 final StringBuffer buffer = new StringBuffer(); 1367 buffer.append("rect("); 1368 buffer.append(clipHeight / 2); 1369 buffer.append(imageAreaHeightVal.getType().getType()); 1370 buffer.append(' '); 1371 buffer.append(clipWidth / 2); 1372 buffer.append(imageAreaWidthVal.getType().getType()); 1373 buffer.append(' '); 1374 buffer.append(clipHeight / 2); 1375 buffer.append(imageAreaHeightVal.getType().getType()); 1376 buffer.append(' '); 1377 buffer.append(clipWidth / 2); 1378 buffer.append(imageAreaWidthVal.getType().getType()); 1379 buffer.append(')'); 1380 graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString()); 1381 1382 styleName = imageStyle.getStyleName(); 1383 getStylesCollection().getAutomaticStyles().addStyle(imageStyle); 1384 } 1385 else if (clipWidth > 0) 1386 { 1387 final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS); 1388 final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES); 1389 final StringBuffer buffer = new StringBuffer(); 1390 buffer.append("rect(0cm "); 1391 buffer.append(clipWidth / 2); 1392 buffer.append(imageAreaWidthVal.getType().getType()); 1393 buffer.append(" 0cm "); 1394 buffer.append(clipWidth / 2); 1395 buffer.append(imageAreaWidthVal.getType().getType()); 1396 buffer.append(')'); 1397 graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString()); 1398 1399 styleName = imageStyle.getStyleName(); 1400 getStylesCollection().getAutomaticStyles().addStyle(imageStyle); 1401 imageAreaHeightVal = normalizedImageHeight; 1402 } 1403 else if (clipHeight > 0) 1404 { 1405 final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS); 1406 final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES); 1407 final StringBuffer buffer = new StringBuffer(); 1408 buffer.append("rect("); 1409 buffer.append(clipHeight / 2); 1410 buffer.append(imageAreaHeightVal.getType().getType()); 1411 buffer.append(" 0cm "); 1412 buffer.append(clipHeight / 2); 1413 buffer.append(imageAreaHeightVal.getType().getType()); 1414 buffer.append(" 0cm)"); 1415 graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString()); 1416 1417 styleName = imageStyle.getStyleName(); 1418 getStylesCollection().getAutomaticStyles().addStyle(imageStyle); 1419 imageAreaWidthVal = normalizedImageWidth; 1420 } 1421 else 1422 { 1423 imageAreaWidthVal = normalizedImageWidth; 1424 imageAreaHeightVal = normalizedImageHeight; 1425 } 1426 } 1427 else if (OfficeToken.ISOTROPIC.equals(scale)) 1428 { 1429 final double[] ret = calcPaintSize(imageAreaWidthVal, imageAreaHeightVal, normalizedImageWidth, normalizedImageHeight); 1430 1431 posX = CSSNumericValue.createValue(imageAreaWidthVal.getType(), (imageAreaWidthVal.getValue() - ret[0]) * 0.5); 1432 posY = CSSNumericValue.createValue(imageAreaHeightVal.getType(), (imageAreaHeightVal.getValue() - ret[1]) * 0.5); 1433 1434 imageAreaWidthVal = CSSNumericValue.createValue(imageAreaWidthVal.getType(), ret[0]); 1435 imageAreaHeightVal = CSSNumericValue.createValue(imageAreaHeightVal.getType(), ret[1]); 1436 } 1437 } 1438 // If we do scale, then we simply use the given image-area-size as valid image size and dont 1439 // care about the image itself .. 1440 } 1441 else 1442 { 1443 LOGGER.debug("There is no image-context, so we have to rely on the image's natural bounds. " + "This may go awfully wrong."); 1444 imageAreaWidthVal = image.getWidth(); 1445 imageAreaHeightVal = image.getHeight(); 1446 } 1447 1448 final AttributeList frameList = new AttributeList(); 1449 frameList.setAttribute(OfficeNamespaces.DRAWING_NS, "name", imageNames.generateName("Image")); 1450 if (styleName != null) 1451 { 1452 frameList.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, styleName); 1453 } 1454 frameList.setAttribute(OfficeNamespaces.TEXT_NS, "anchor-type", OfficeToken.PARAGRAPH); 1455 frameList.setAttribute(OfficeNamespaces.SVG_NS, "z-index", "0"); 1456 frameList.setAttribute(OfficeNamespaces.SVG_NS, "x", posX.getValue() + posX.getType().getType()); 1457 frameList.setAttribute(OfficeNamespaces.SVG_NS, "y", posY.getValue() + posY.getType().getType()); 1458 1459 1460 LOGGER.debug("Image " + imageData + " A-Width: " + imageAreaWidthVal + ", A-Height: " + imageAreaHeightVal); 1461 1462 if (imageAreaWidthVal != null) 1463 { 1464 frameList.setAttribute(OfficeNamespaces.SVG_NS, 1465 "width", imageAreaWidthVal.getValue() + imageAreaWidthVal.getType().getType()); 1466 } 1467 1468 if (imageAreaHeightVal != null) 1469 { 1470 frameList.setAttribute(OfficeNamespaces.SVG_NS, 1471 "height", imageAreaHeightVal.getValue() + imageAreaHeightVal.getType().getType()); 1472 } 1473 1474 1475 final AttributeList imageList = new AttributeList(); 1476 imageList.setAttribute(OfficeNamespaces.XLINK_NS, "href", image.getEmbeddableLink()); 1477 imageList.setAttribute(OfficeNamespaces.XLINK_NS, "type", "simple"); 1478 imageList.setAttribute(OfficeNamespaces.XLINK_NS, "show", "embed"); 1479 imageList.setAttribute(OfficeNamespaces.XLINK_NS, "actuate", "onLoad"); 1480 1481 1482 try 1483 { 1484 getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, "frame", frameList, XmlWriterSupport.OPEN); 1485 getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.IMAGE, imageList, XmlWriterSupport.CLOSE); 1486 getXmlWriter().writeCloseTag(); 1487 } 1488 catch (IOException ioe) 1489 { 1490 throw new ReportProcessingException(FAILED, ioe); 1491 } 1492 } 1493 } 1494 computeImageWidth(final ImageElementContext imageElementContext)1495 private CSSNumericValue computeImageWidth(final ImageElementContext imageElementContext) 1496 { 1497 final LengthCalculator calculator = new LengthCalculator(); 1498 final String[] strings = imageElementContext.getColStyles(); 1499 for (int i = 0; i < strings.length; i++) 1500 { 1501 final String styleName = strings[i]; 1502 final CSSNumericValue value = computeColumnWidth(styleName); 1503 if (value != null) 1504 { 1505 calculator.add(value); 1506 } 1507 } 1508 return calculator.getResult(); 1509 } 1510 computeImageHeight(final ImageElementContext imageElementContext)1511 private CSSNumericValue computeImageHeight(final ImageElementContext imageElementContext) 1512 { 1513 final LengthCalculator calculator = new LengthCalculator(); 1514 final String[] strings = imageElementContext.getRowStyles(); 1515 for (int i = 0; i < strings.length; i++) 1516 { 1517 final String styleName = strings[i]; 1518 final CSSNumericValue value = computeRowHeight(styleName); 1519 if (value != null) 1520 { 1521 calculator.add(value); 1522 } 1523 } 1524 return calculator.getResult(); 1525 } 1526 computeRowHeight(final String rowStyle)1527 protected CSSNumericValue computeRowHeight(final String rowStyle) 1528 { 1529 final OfficeStylesCollection contentStyles = getContentStylesCollection(); 1530 final OfficeStyle style = contentStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle); 1531 if (style != null) 1532 { 1533 final Element element = style.getTableRowProperties(); 1534 if (element != null) 1535 { 1536 final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height"); 1537 if (height != null) 1538 { 1539 return parseLength(height); 1540 } 1541 } 1542 1543 final String styleParent = style.getStyleParent(); 1544 if (styleParent != null) 1545 { 1546 return computeRowHeight(styleParent); 1547 } 1548 } 1549 1550 final OfficeStylesCollection globalStyles = getGlobalStylesCollection(); 1551 final OfficeStyle globalStyle = globalStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle); 1552 if (globalStyle != null) 1553 { 1554 final Element element = globalStyle.getTableRowProperties(); 1555 if (element != null) 1556 { 1557 final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height"); 1558 if (height != null) 1559 { 1560 return parseLength(height); 1561 } 1562 } 1563 final String styleParent = globalStyle.getStyleParent(); 1564 if (styleParent != null) 1565 { 1566 return computeRowHeight(styleParent); 1567 } 1568 } 1569 1570 final OfficeStylesCollection predefStyles = getPredefinedStylesCollection(); 1571 final OfficeStyle predefStyle = predefStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle); 1572 if (predefStyle != null) 1573 { 1574 final Element element = predefStyle.getTableRowProperties(); 1575 if (element != null) 1576 { 1577 final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height"); 1578 if (height != null) 1579 { 1580 return parseLength(height); 1581 } 1582 } 1583 final String styleParent = predefStyle.getStyleParent(); 1584 if (styleParent != null) 1585 { 1586 return computeRowHeight(styleParent); 1587 } 1588 } 1589 // not found. 1590 return null; 1591 } 1592 computeColumnWidth(final String colStyle)1593 protected CSSNumericValue computeColumnWidth(final String colStyle) 1594 { 1595 final OfficeStylesCollection contentStyles = getContentStylesCollection(); 1596 final OfficeStyle style = contentStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle); 1597 if (style != null) 1598 { 1599 final Element element = style.getTableColumnProperties(); 1600 if (element != null) 1601 { 1602 final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width"); 1603 if (height != null) 1604 { 1605 return parseLength(height); 1606 } 1607 } 1608 1609 final String styleParent = style.getStyleParent(); 1610 if (styleParent != null) 1611 { 1612 return computeRowHeight(styleParent); 1613 } 1614 } 1615 1616 final OfficeStylesCollection globalStyles = getGlobalStylesCollection(); 1617 final OfficeStyle globalStyle = globalStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle); 1618 if (globalStyle != null) 1619 { 1620 final Element element = globalStyle.getTableColumnProperties(); 1621 if (element != null) 1622 { 1623 final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width"); 1624 if (height != null) 1625 { 1626 return parseLength(height); 1627 } 1628 } 1629 final String styleParent = globalStyle.getStyleParent(); 1630 if (styleParent != null) 1631 { 1632 return computeRowHeight(styleParent); 1633 } 1634 } 1635 1636 final OfficeStylesCollection predefStyles = getPredefinedStylesCollection(); 1637 final OfficeStyle predefStyle = predefStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle); 1638 if (predefStyle != null) 1639 { 1640 final Element element = predefStyle.getTableColumnProperties(); 1641 if (element != null) 1642 { 1643 final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width"); 1644 if (height != null) 1645 { 1646 return parseLength(height); 1647 } 1648 } 1649 final String styleParent = predefStyle.getStyleParent(); 1650 if (styleParent != null) 1651 { 1652 return computeRowHeight(styleParent); 1653 } 1654 } 1655 // not found. 1656 return null; 1657 } 1658 produceFirstChild(final Section style, final String nameSpace, final String type)1659 protected Element produceFirstChild(final Section style, 1660 final String nameSpace, 1661 final String type) 1662 { 1663 Element paragraphProps = style.findFirstChild(nameSpace, type); 1664 if (paragraphProps == null) 1665 { 1666 paragraphProps = new Section(); 1667 paragraphProps.setNamespace(nameSpace); 1668 paragraphProps.setType(type); 1669 style.addNode(paragraphProps); 1670 } 1671 return paragraphProps; 1672 } 1673 startChartProcessing(final AttributeMap attrs)1674 protected void startChartProcessing(final AttributeMap attrs) 1675 throws ReportProcessingException 1676 { 1677 final String classId = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "class-id"); 1678 final String chartUrl = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "href"); 1679 final ArrayList masterColumns = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.MASTER_COLUMNS); 1680 final ArrayList masterValues = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.MASTER_VALUES); 1681 final ArrayList detailColumns = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.DETAIL_COLUMNS); 1682 final String href = oleProducer.produceOle(chartUrl, masterColumns, masterValues, detailColumns); 1683 1684 final AttributeList oleList = new AttributeList(); 1685 oleList.setAttribute(OfficeNamespaces.DRAWING_NS, "class-id", classId); 1686 oleList.setAttribute(OfficeNamespaces.XLINK_NS, "href", "./" + href); 1687 oleList.setAttribute(OfficeNamespaces.XLINK_NS, "type", "simple"); 1688 oleList.setAttribute(OfficeNamespaces.XLINK_NS, "show", "embed"); 1689 oleList.setAttribute(OfficeNamespaces.XLINK_NS, "actuate", "onLoad"); 1690 1691 try 1692 { 1693 getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.OBJECT_OLE, oleList, XmlWriterSupport.CLOSE); 1694 } 1695 catch (IOException ioe) 1696 { 1697 throw new ReportProcessingException(FAILED, ioe); 1698 } 1699 } 1700 calcPaintSize(final CSSNumericValue areaWidth, final CSSNumericValue areaHeight, final CSSNumericValue imageWidth, final CSSNumericValue imageHeight)1701 static private double[] calcPaintSize(final CSSNumericValue areaWidth, final CSSNumericValue areaHeight, 1702 final CSSNumericValue imageWidth, final CSSNumericValue imageHeight) 1703 { 1704 1705 final double ratioX = areaWidth.getValue() / imageWidth.getValue(); 1706 final double ratioY = areaHeight.getValue() / imageHeight.getValue(); 1707 final double ratioMin = Math.min(ratioX, ratioY); 1708 1709 double[] ret = new double[2]; 1710 ret[0] = imageWidth.getValue() * ratioMin; 1711 ret[1] = imageHeight.getValue() * ratioMin; 1712 return ret; 1713 } 1714 writeNullDate()1715 protected void writeNullDate() throws IOException 1716 { 1717 // write NULL DATE 1718 final XmlWriter xmlWriter = getXmlWriter(); 1719 xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, "calculation-settings", null, XmlWriterSupport.OPEN); 1720 final AttributeMap nullDateAttributes = new AttributeMap(); 1721 nullDateAttributes.setAttribute(OfficeNamespaces.TABLE_NS, "date-value", "1900-01-01"); 1722 xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, "null-date", buildAttributeList(nullDateAttributes), XmlWriterSupport.CLOSE); 1723 xmlWriter.writeCloseTag(); 1724 } 1725 } 1726