| 1 28 29 37 package net.sf.jasperreports.engine.export; 38 39 import java.awt.Color ; 40 import java.awt.Graphics2D ; 41 import java.awt.font.TextAttribute ; 42 import java.awt.geom.AffineTransform ; 43 import java.awt.geom.Dimension2D ; 44 import java.awt.geom.Rectangle2D ; 45 import java.awt.image.BufferedImage ; 46 import java.io.File ; 47 import java.io.FileOutputStream ; 48 import java.io.IOException ; 49 import java.io.OutputStream ; 50 import java.text.AttributedCharacterIterator ; 51 import java.util.Collection ; 52 import java.util.HashMap ; 53 import java.util.Iterator ; 54 import java.util.LinkedList ; 55 import java.util.List ; 56 import java.util.Map ; 57 58 import net.sf.jasperreports.engine.JRAbstractExporter; 59 import net.sf.jasperreports.engine.JRAlignment; 60 import net.sf.jasperreports.engine.JRAnchor; 61 import net.sf.jasperreports.engine.JRBox; 62 import net.sf.jasperreports.engine.JRElement; 63 import net.sf.jasperreports.engine.JRException; 64 import net.sf.jasperreports.engine.JRExporterParameter; 65 import net.sf.jasperreports.engine.JRFont; 66 import net.sf.jasperreports.engine.JRGraphicElement; 67 import net.sf.jasperreports.engine.JRHyperlink; 68 import net.sf.jasperreports.engine.JRImage; 69 import net.sf.jasperreports.engine.JRImageRenderer; 70 import net.sf.jasperreports.engine.JRLine; 71 import net.sf.jasperreports.engine.JRPrintAnchor; 72 import net.sf.jasperreports.engine.JRPrintElement; 73 import net.sf.jasperreports.engine.JRPrintEllipse; 74 import net.sf.jasperreports.engine.JRPrintFrame; 75 import net.sf.jasperreports.engine.JRPrintGraphicElement; 76 import net.sf.jasperreports.engine.JRPrintHyperlink; 77 import net.sf.jasperreports.engine.JRPrintImage; 78 import net.sf.jasperreports.engine.JRPrintLine; 79 import net.sf.jasperreports.engine.JRPrintPage; 80 import net.sf.jasperreports.engine.JRPrintRectangle; 81 import net.sf.jasperreports.engine.JRPrintText; 82 import net.sf.jasperreports.engine.JRRenderable; 83 import net.sf.jasperreports.engine.JRRuntimeException; 84 import net.sf.jasperreports.engine.JRTextElement; 85 import net.sf.jasperreports.engine.JasperPrint; 86 import net.sf.jasperreports.engine.base.JRBaseFont; 87 import net.sf.jasperreports.engine.util.BreakIteratorSplitCharacter; 88 import net.sf.jasperreports.engine.util.JRLoader; 89 import net.sf.jasperreports.engine.util.JRProperties; 90 import net.sf.jasperreports.engine.util.JRStyledText; 91 92 import com.lowagie.text.Chunk; 93 import com.lowagie.text.Document; 94 import com.lowagie.text.DocumentException; 95 import com.lowagie.text.Element; 96 import com.lowagie.text.Font; 97 import com.lowagie.text.FontFactory; 98 import com.lowagie.text.Image; 99 import com.lowagie.text.Phrase; 100 import com.lowagie.text.Rectangle; 101 import com.lowagie.text.SplitCharacter; 102 import com.lowagie.text.pdf.BaseFont; 103 import com.lowagie.text.pdf.ColumnText; 104 import com.lowagie.text.pdf.FontMapper; 105 import com.lowagie.text.pdf.PdfContentByte; 106 import com.lowagie.text.pdf.PdfDestination; 107 import com.lowagie.text.pdf.PdfOutline; 108 import com.lowagie.text.pdf.PdfTemplate; 109 import com.lowagie.text.pdf.PdfWriter; 110 111 112 128 public class JRPdfExporter extends JRAbstractExporter 129 { 130 131 138 public static final String PDF_FORCE_SVG_SHAPES = JRProperties.PROPERTY_PREFIX + "export.pdf.force.svg.shapes"; 139 140 private static final String EMPTY_BOOKMARK_TITLE = ""; 141 142 145 protected static final String JR_PAGE_ANCHOR_PREFIX = "JR_PAGE_ANCHOR_"; 146 147 protected static boolean fontsRegistered = false; 148 149 152 protected Document document = null; 153 protected PdfContentByte pdfContentByte = null; 154 155 protected Document imageTesterDocument = null; 156 protected PdfContentByte imageTesterPdfContentByte = null; 157 158 protected JRExportProgressMonitor progressMonitor = null; 159 160 protected int reportIndex = 0; 161 162 165 protected boolean isCreatingBatchModeBookmarks = false; 166 protected boolean isCompressed = false; 167 protected boolean isEncrypted = false; 168 protected boolean is128BitKey = false; 169 protected String userPassword = null; 170 protected String ownerPassword = null; 171 protected int permissions = 0; 172 protected Character pdfVersion = null; 173 174 177 protected Map loadedImagesMap = null; 178 protected Image pxImage = null; 179 180 private BookmarkStack bookmarkStack = null; 181 182 private Map fontMap = null; 183 184 private boolean forceSvgShapes = true; 185 private SplitCharacter splitCharacter; 186 protected JRHyperlinkProducerFactory hyperlinkProducerFactory; 187 188 private String pdfJavaScript; 189 190 193 protected Image getPxImage() 194 { 195 if (pxImage == null) 196 { 197 try 198 { 199 pxImage = 200 Image.getInstance( 201 JRLoader.loadBytesFromLocation("net/sf/jasperreports/engine/images/pixel.GIF", null) 202 ); 203 } 204 catch(Exception e) 205 { 206 throw new JRRuntimeException(e); 207 } 208 } 209 210 return pxImage; 211 } 212 213 214 217 public void exportReport() throws JRException 218 { 219 registerFonts(); 220 221 progressMonitor = (JRExportProgressMonitor)parameters.get(JRExporterParameter.PROGRESS_MONITOR); 222 223 224 setOffset(); 225 226 try 227 { 228 229 setExportContext(); 230 231 232 setInput(); 233 234 235 if (!isModeBatch) 236 { 237 setPageRange(); 238 } 239 240 Boolean isCreatingBatchModeBookmarksParameter = (Boolean )parameters.get(JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS); 241 if(isCreatingBatchModeBookmarksParameter != null){ 242 isCreatingBatchModeBookmarks = isCreatingBatchModeBookmarksParameter.booleanValue(); 243 } 244 245 Boolean isCompressedParameter = (Boolean )parameters.get(JRPdfExporterParameter.IS_COMPRESSED); 246 if (isCompressedParameter != null) 247 { 248 isCompressed = isCompressedParameter.booleanValue(); 249 } 250 251 Boolean isEncryptedParameter = (Boolean )parameters.get(JRPdfExporterParameter.IS_ENCRYPTED); 252 if (isEncryptedParameter != null) 253 { 254 isEncrypted = isEncryptedParameter.booleanValue(); 255 } 256 257 Boolean is128BitKeyParameter = (Boolean )parameters.get(JRPdfExporterParameter.IS_128_BIT_KEY); 258 if (is128BitKeyParameter != null) 259 { 260 is128BitKey = is128BitKeyParameter.booleanValue(); 261 } 262 263 userPassword = (String )parameters.get(JRPdfExporterParameter.USER_PASSWORD); 264 ownerPassword = (String )parameters.get(JRPdfExporterParameter.OWNER_PASSWORD); 265 266 Integer permissionsParameter = (Integer )parameters.get(JRPdfExporterParameter.PERMISSIONS); 267 if (permissionsParameter != null) 268 { 269 permissions = permissionsParameter.intValue(); 270 } 271 272 pdfVersion = (Character ) parameters.get(JRPdfExporterParameter.PDF_VERSION); 273 274 fontMap = (Map ) parameters.get(JRExporterParameter.FONT_MAP); 275 276 setForceSvgShapes(); 277 setSplitCharacter(); 278 setHyperlinkProducerFactory(); 279 280 pdfJavaScript = (String )parameters.get(JRPdfExporterParameter.PDF_JAVASCRIPT); 281 282 OutputStream os = (OutputStream )parameters.get(JRExporterParameter.OUTPUT_STREAM); 283 if (os != null) 284 { 285 exportReportToStream(os); 286 } 287 else 288 { 289 File destFile = (File )parameters.get(JRExporterParameter.OUTPUT_FILE); 290 if (destFile == null) 291 { 292 String fileName = (String )parameters.get(JRExporterParameter.OUTPUT_FILE_NAME); 293 if (fileName != null) 294 { 295 destFile = new File (fileName); 296 } 297 else 298 { 299 throw new JRException("No output specified for the exporter."); 300 } 301 } 302 303 try 304 { 305 os = new FileOutputStream (destFile); 306 exportReportToStream(os); 307 os.flush(); 308 } 309 catch (IOException e) 310 { 311 throw new JRException("Error trying to export to file : " + destFile, e); 312 } 313 finally 314 { 315 if (os != null) 316 { 317 try 318 { 319 os.close(); 320 } 321 catch(IOException e) 322 { 323 } 324 } 325 } 326 } 327 } 328 finally 329 { 330 resetExportContext(); 331 } 332 } 333 334 335 protected void setForceSvgShapes() 336 { 337 Boolean forceSvgShapesParam = (Boolean ) parameters.get(JRPdfExporterParameter.FORCE_SVG_SHAPES); 338 if (forceSvgShapesParam == null) 339 { 340 forceSvgShapes = JRProperties.getBooleanProperty(PDF_FORCE_SVG_SHAPES); 341 } 342 else 343 { 344 forceSvgShapes = forceSvgShapesParam.booleanValue(); 345 } 346 } 347 348 349 protected void setSplitCharacter() 350 { 351 boolean useFillSplitCharacter; 352 Boolean useFillSplitCharacterParam = (Boolean ) parameters.get(JRPdfExporterParameter.FORCE_LINEBREAK_POLICY); 353 if (useFillSplitCharacterParam == null) 354 { 355 useFillSplitCharacter = JRProperties.getBooleanProperty(JRProperties.PDF_FORCE_LINEBREAK_POLICY); 356 } 357 else 358 { 359 useFillSplitCharacter = useFillSplitCharacterParam.booleanValue(); 360 } 361 362 if (useFillSplitCharacter) 363 { 364 splitCharacter = new BreakIteratorSplitCharacter(); 365 } 366 } 367 368 369 protected void setHyperlinkProducerFactory() 370 { 371 hyperlinkProducerFactory = (JRHyperlinkProducerFactory) parameters.get(JRPdfExporterParameter.HYPERLINK_PRODUCER_FACTORY); 372 } 373 374 375 378 protected void exportReportToStream(OutputStream os) throws JRException 379 { 380 382 document = 383 new Document( 384 new Rectangle( 385 jasperPrint.getPageWidth(), 386 jasperPrint.getPageHeight() 387 ) 388 ); 389 390 imageTesterDocument = 391 new Document( 392 new Rectangle( 393 10, 10 ) 396 ); 397 398 try 399 { 400 PdfWriter pdfWriter = PdfWriter.getInstance(document, os); 401 pdfWriter.setCloseStream(false); 402 403 if (pdfVersion != null) 404 pdfWriter.setPdfVersion(pdfVersion.charValue()); 405 406 if (isCompressed) 407 pdfWriter.setFullCompression(); 408 409 if (isEncrypted) 410 { 411 pdfWriter.setEncryption( 412 is128BitKey, 413 userPassword, 414 ownerPassword, 415 permissions 416 ); 417 } 418 419 String title = (String )parameters.get(JRPdfExporterParameter.METADATA_TITLE); 422 if( title != null ) 423 document.addTitle(title); 424 425 String author = (String )parameters.get(JRPdfExporterParameter.METADATA_AUTHOR); 426 if( author != null ) 427 document.addAuthor(author); 428 429 String subject = (String )parameters.get(JRPdfExporterParameter.METADATA_SUBJECT); 430 if( subject != null ) 431 document.addSubject(subject); 432 433 String keywords = (String )parameters.get(JRPdfExporterParameter.METADATA_KEYWORDS); 434 if( keywords != null ) 435 document.addKeywords(keywords); 436 437 String creator = (String )parameters.get(JRPdfExporterParameter.METADATA_CREATOR); 438 if( creator != null ) 439 document.addCreator(creator); 440 else 441 document.addCreator("JasperReports (" + jasperPrint.getName() + ")"); 442 443 document.open(); 444 445 if(pdfJavaScript != null) 446 pdfWriter.addJavaScript(pdfJavaScript); 447 448 pdfContentByte = pdfWriter.getDirectContent(); 449 450 initBookmarks(); 451 452 PdfWriter imageTesterPdfWriter = 453 PdfWriter.getInstance( 454 imageTesterDocument, 455 new NullOutputStream() ); 457 imageTesterDocument.open(); 458 imageTesterDocument.newPage(); 459 imageTesterPdfContentByte = imageTesterPdfWriter.getDirectContent(); 460 imageTesterPdfContentByte.setLiteral("\n"); 461 462 for(reportIndex = 0; reportIndex < jasperPrintList.size(); reportIndex++) 463 { 464 jasperPrint = (JasperPrint)jasperPrintList.get(reportIndex); 465 loadedImagesMap = new HashMap (); 466 document.setPageSize(new Rectangle(jasperPrint.getPageWidth(), jasperPrint.getPageHeight())); 467 468 List pages = jasperPrint.getPages(); 469 if (pages != null && pages.size() > 0) 470 { 471 if (isModeBatch) 472 { 473 document.newPage(); 474 475 if( isCreatingBatchModeBookmarks ){ 476 addBookmark(0, jasperPrint.getName(), 0, 0); 478 } 479 480 startPageIndex = 0; 481 endPageIndex = pages.size() - 1; 482 } 483 484 Chunk chunk = null; 485 ColumnText colText = null; 486 JRPrintPage page = null; 487 for(int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++) 488 { 489 if (Thread.currentThread().isInterrupted()) 490 { 491 throw new JRException("Current thread interrupted."); 492 } 493 494 page = (JRPrintPage)pages.get(pageIndex); 495 496 document.newPage(); 497 498 pdfContentByte = pdfWriter.getDirectContent(); 499 500 pdfContentByte.setLineCap(2); 502 chunk = new Chunk(" "); 503 chunk.setLocalDestination(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + (pageIndex + 1)); 504 505 colText = new ColumnText(pdfContentByte); 506 colText.setSimpleColumn( 507 new Phrase(chunk), 508 0, 509 jasperPrint.getPageHeight(), 510 1, 511 1, 512 0, 513 Element.ALIGN_LEFT 514 ); 515 516 colText.go(); 517 518 519 exportPage(page); 520 } 521 } 522 else 523 { 524 document.newPage(); 525 pdfContentByte = pdfWriter.getDirectContent(); 526 pdfContentByte.setLiteral("\n"); 527 } 528 } 529 } 530 catch(DocumentException e) 531 { 532 throw new JRException("PDF Document error : " + jasperPrint.getName(), e); 533 } 534 catch(IOException e) 535 { 536 throw new JRException("Error generating PDF report : " + jasperPrint.getName(), e); 537 } 538 finally 539 { 540 document.close(); 541 imageTesterDocument.close(); 542 } 543 544 } 546 547 548 551 protected void exportPage(JRPrintPage page) throws JRException, DocumentException, IOException  552 { 553 Collection elements = page.getElements(); 554 exportElements(elements); 555 556 if (progressMonitor != null) 557 { 558 progressMonitor.afterPageExport(); 559 } 560 } 561 562 563 protected void exportElements(Collection elements) throws DocumentException, IOException , JRException 564 { 565 if (elements != null && elements.size() > 0) 566 { 567 JRPrintElement element; 568 for(Iterator it = elements.iterator(); it.hasNext();) 569 { 570 element = (JRPrintElement)it.next(); 571 572 if (element instanceof JRPrintLine) 573 { 574 exportLine((JRPrintLine)element); 575 } 576 else if (element instanceof JRPrintRectangle) 577 { 578 exportRectangle((JRPrintRectangle)element); 579 } 580 else if (element instanceof JRPrintEllipse) 581 { 582 exportEllipse((JRPrintEllipse)element); 583 } 584 else if (element instanceof JRPrintImage) 585 { 586 exportImage((JRPrintImage)element); 587 } 588 else if (element instanceof JRPrintText) 589 { 590 exportText((JRPrintText)element); 591 } 592 else if (element instanceof JRPrintFrame) 593 { 594 exportFrame((JRPrintFrame) element); 595 } 596 } 597 } 598 } 599 600 601 604 protected void exportLine(JRPrintLine line) 605 { 606 if (line.getPen() != JRGraphicElement.PEN_NONE) 607 { 608 pdfContentByte.setRGBColorStroke( 609 line.getForecolor().getRed(), 610 line.getForecolor().getGreen(), 611 line.getForecolor().getBlue() 612 ); 613 614 switch (line.getPen()) 615 { 616 case JRGraphicElement.PEN_DOTTED : 617 { 618 pdfContentByte.setLineWidth(1f); 619 pdfContentByte.setLineDash(5f, 3f, 0f); 620 break; 621 } 622 case JRGraphicElement.PEN_4_POINT : 623 { 624 pdfContentByte.setLineWidth(4f); 625 pdfContentByte.setLineDash(0f); 626 break; 627 } 628 case JRGraphicElement.PEN_2_POINT : 629 { 630 pdfContentByte.setLineWidth(2f); 631 pdfContentByte.setLineDash(0f); 632 break; 633 } 634 case JRGraphicElement.PEN_NONE : 635 { 636 break; 638 } 639 case JRGraphicElement.PEN_THIN : 640 { 641 pdfContentByte.setLineWidth(0.5f); 642 pdfContentByte.setLineDash(0f); 643 break; 644 } 645 case JRGraphicElement.PEN_1_POINT : 646 default : 647 { 648 pdfContentByte.setLineWidth(1f); 649 pdfContentByte.setLineDash(0f); 650 break; 651 } 652 } 653 654 if (line.getDirection() == JRLine.DIRECTION_TOP_DOWN) 655 { 656 pdfContentByte.moveTo( 657 line.getX() + getOffsetX(), 658 jasperPrint.getPageHeight() - line.getY() - getOffsetY() 659 ); 660 pdfContentByte.lineTo( 661 line.getX() + getOffsetX() + line.getWidth() - 1, 662 jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() + 1 663 ); 664 } 665 else 666 { 667 pdfContentByte.moveTo( 668 line.getX() + getOffsetX(), 669 jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() + 1 670 ); 671 pdfContentByte.lineTo( 672 line.getX() + getOffsetX() + line.getWidth() - 1, 673 jasperPrint.getPageHeight() - line.getY() - getOffsetY() 674 ); 675 } 676 677 pdfContentByte.stroke(); 678 679 pdfContentByte.setLineDash(0f); 680 } 681 } 682 683 684 687 protected void exportRectangle(JRPrintRectangle rectangle) 688 { 689 pdfContentByte.setRGBColorStroke( 690 rectangle.getForecolor().getRed(), 691 rectangle.getForecolor().getGreen(), 692 rectangle.getForecolor().getBlue() 693 ); 694 pdfContentByte.setRGBColorFill( 695 rectangle.getBackcolor().getRed(), 696 rectangle.getBackcolor().getGreen(), 697 rectangle.getBackcolor().getBlue() 698 ); 699 700 float borderCorrection = prepareGraphicElement(rectangle); 701 702 if (rectangle.getMode() == JRElement.MODE_OPAQUE) 703 { 704 pdfContentByte.roundRectangle( 705 rectangle.getX() + getOffsetX() - borderCorrection, 706 jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() - borderCorrection + 1, 707 rectangle.getWidth() + 2 * borderCorrection - 1, 708 rectangle.getHeight() + 2 * borderCorrection - 1, 709 rectangle.getRadius() 710 ); 711 712 if (rectangle.getPen() == JRGraphicElement.PEN_DOTTED) 713 { 714 pdfContentByte.fill(); 715 716 pdfContentByte.roundRectangle( 717 rectangle.getX() + getOffsetX(), 718 jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() + 1, 719 rectangle.getWidth() - 1, 720 rectangle.getHeight() - 1, 721 rectangle.getRadius() 722 ); 723 pdfContentByte.stroke(); 724 } 725 else 726 { 727 pdfContentByte.fillStroke(); 728 } 729 } 730 else 731 { 732 if (rectangle.getPen() != JRGraphicElement.PEN_NONE) 733 { 734 pdfContentByte.roundRectangle( 735 rectangle.getX() + getOffsetX() - borderCorrection, 736 jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() - borderCorrection + 1, 737 rectangle.getWidth() + 2 * borderCorrection - 1, 738 rectangle.getHeight() + 2 * borderCorrection - 1, 739 rectangle.getRadius() |