001package org.apache.maven.doxia.module.itext; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import com.lowagie.text.BadElementException; 023import com.lowagie.text.ElementTags; 024import com.lowagie.text.Image; 025 026import java.awt.Color; 027import java.io.File; 028import java.io.IOException; 029import java.io.LineNumberReader; 030import java.io.StringReader; 031import java.io.StringWriter; 032import java.io.Writer; 033import java.net.MalformedURLException; 034import java.net.URL; 035import java.util.ArrayList; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Locale; 039import java.util.Map; 040import java.util.Set; 041import java.util.Stack; 042import java.util.TreeSet; 043 044import org.apache.maven.doxia.sink.Sink; 045import org.apache.maven.doxia.sink.SinkEventAttributes; 046import org.apache.maven.doxia.sink.impl.AbstractXmlSink; 047import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; 048import org.apache.maven.doxia.util.DoxiaUtils; 049import org.apache.maven.doxia.util.HtmlTools; 050 051import org.codehaus.plexus.util.IOUtil; 052import org.codehaus.plexus.util.StringUtils; 053import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; 054import org.codehaus.plexus.util.xml.XMLWriter; 055 056/** 057 * <p>A doxia Sink which produces an XML Front End document for <code>iText</code> framework.</p> 058 * Known limitations: 059 * <ul> 060 * <li>Roman lists are not supported.</li> 061 * <li>Horizontal rule is not supported with 1.3. 062 * See <a href="http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg10323.html"> 063 * http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg10323.html</a></li> 064 * <li>iText has some problems with <code>ElementTags.TABLE</code> and <code>ElementTags.TABLEFITSPAGE</code>. 065 * See <a href="http://sourceforge.net/tracker/index.php?func=detail&aid=786427&group_id=15255&atid=115255"> 066 * SourceForce Tracker</a>.</li> 067 * <li>Images could be on another page and next text on the last one.</li> 068 * </ul> 069 * 070 * @see <a href="http://www.lowagie.com/iText/tutorial/ch07.html">http://www.lowagie.com/iText/tutorial/ch07.html</a> 071 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> 072 */ 073public class ITextSink 074 extends AbstractXmlSink 075{ 076 /** This is the place where the iText DTD is located. IMPORTANT: this DTD is not uptodate! */ 077 public static final String DTD = "http://itext.sourceforge.net/itext.dtd"; 078 079 /** This is the reference to the DTD. */ 080 public static final String DOCTYPE = "ITEXT SYSTEM \"" + DTD + "\""; 081 082 /** This is the default leading for chapter title */ 083 public static final String DEFAULT_CHAPTER_TITLE_LEADING = "36.0"; 084 085 /** This is the default leading for section title */ 086 public static final String DEFAULT_SECTION_TITLE_LEADING = "24.0"; 087 088 /** The ClassLoader used */ 089 private ClassLoader currentClassLoader; 090 091 /** The action context */ 092 private SinkActionContext actionContext; 093 094 /** The Writer used */ 095 private Writer writer; 096 097 /** The XML Writer used */ 098 private final XMLWriter xmlWriter; 099 100 private boolean writeStart; 101 102 /** The Header object */ 103 private ITextHeader header; 104 105 /** The font object */ 106 private ITextFont font; 107 108 private int numberDepth = 1; 109 110 private int depth = 0; 111 112 private StringWriter tableCaptionWriter = null; 113 114 private XMLWriter tableCaptionXMLWriter = null; 115 116 /** Flag to know if an anchor is defined or not. Used as workaround for iText which needs a defined local 117 * destination. */ 118 private boolean anchorDefined = false; 119 120 /** Flag to know if an figure event is called. */ 121 private boolean figureDefined = false; 122 123 /** Keep track of the closing tags for inline events. */ 124 protected Stack<List<String>> inlineStack = new Stack<>(); 125 126 /** Map of warn messages with a String as key to describe the error type and a Set as value. 127 * Using to reduce warn messages. */ 128 private Map<String, Set<String>> warnMessages; 129 130 /** 131 * <p>Constructor for ITextSink.</p> 132 * 133 * @param writer the writer. 134 */ 135 protected ITextSink( Writer writer ) 136 { 137 this( writer, "UTF-8" ); 138 } 139 140 /** 141 * <p>Constructor for ITextSink.</p> 142 * 143 * @param writer the writer. 144 * @param encoding the encoding. 145 * @since 1.1 146 */ 147 protected ITextSink( Writer writer, String encoding ) 148 { 149 // No doctype since itext doctype is not up to date! 150 this( new PrettyPrintXMLWriter( writer, encoding, null ) ); 151 152 this.writer = writer; 153 this.writeStart = true; 154 } 155 156 /** 157 * <p>Constructor for ITextSink.</p> 158 * 159 * @param xmlWriter a pretty-printing xml writer. 160 */ 161 protected ITextSink( PrettyPrintXMLWriter xmlWriter ) 162 { 163 this.xmlWriter = xmlWriter; 164 165 this.writeStart = false; 166 167 init(); 168 } 169 170 /** 171 * Get the current classLoader 172 * 173 * @return the current class loader 174 */ 175 public ClassLoader getClassLoader() 176 { 177 return currentClassLoader; 178 } 179 180 /** 181 * Set a new class loader 182 * 183 * @param cl the class loader. 184 */ 185 public void setClassLoader( ClassLoader cl ) 186 { 187 currentClassLoader = cl; 188 } 189 190 // ---------------------------------------------------------------------- 191 // Document 192 // ---------------------------------------------------------------------- 193 194 /** 195 * {@inheritDoc} 196 */ 197 public void close() 198 { 199 IOUtil.close( writer ); 200 201 init(); 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 public void flush() 208 { 209 if ( getLog().isWarnEnabled() && this.warnMessages != null ) 210 { 211 for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() ) 212 { 213 for ( String msg : entry.getValue() ) 214 { 215 getLog().warn( msg ); 216 } 217 } 218 } 219 220 this.warnMessages = null; 221 } 222 223 // ---------------------------------------------------------------------- 224 // Header 225 // ---------------------------------------------------------------------- 226 227 /** 228 * {@inheritDoc} 229 */ 230 public void head_() 231 { 232 actionContext.release(); 233 } 234 235 /** 236 * {@inheritDoc} 237 */ 238 public void head() 239 { 240 //init(); // why? this causes DOXIA-413 241 242 actionContext.setAction( SinkActionContext.HEAD ); 243 } 244 245 /** 246 * {@inheritDoc} 247 */ 248 public void author_() 249 { 250 actionContext.release(); 251 } 252 253 /** 254 * {@inheritDoc} 255 */ 256 public void author() 257 { 258 actionContext.setAction( SinkActionContext.AUTHOR ); 259 } 260 261 /** 262 * {@inheritDoc} 263 */ 264 public void date_() 265 { 266 actionContext.release(); 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 public void date() 273 { 274 actionContext.setAction( SinkActionContext.DATE ); 275 } 276 277 /** 278 * {@inheritDoc} 279 */ 280 public void title_() 281 { 282 actionContext.release(); 283 } 284 285 /** 286 * {@inheritDoc} 287 */ 288 public void title() 289 { 290 actionContext.setAction( SinkActionContext.TITLE ); 291 } 292 293 // ---------------------------------------------------------------------- 294 // Body 295 // ---------------------------------------------------------------------- 296 297 /** 298 * {@inheritDoc} 299 */ 300 public void body_() 301 { 302 if ( writeStart ) 303 { 304 writeEndElement(); // ElementTags.CHAPTER 305 306 writeEndElement(); // ElementTags.ITEXT 307 } 308 309 actionContext.release(); 310 } 311 312 /** 313 * {@inheritDoc} 314 */ 315 public void body() 316 { 317 if ( writeStart ) 318 { 319 writeStartElement( ElementTags.ITEXT ); 320 writeAddAttribute( ElementTags.TITLE, header.getTitle() ); 321 writeAddAttribute( ElementTags.AUTHOR, header.getAuthors() ); 322 writeAddAttribute( ElementTags.CREATIONDATE, header.getDate() ); 323 writeAddAttribute( ElementTags.SUBJECT, header.getTitle() ); 324 writeAddAttribute( ElementTags.KEYWORDS, "" ); 325 writeAddAttribute( ElementTags.PRODUCER, "Generated with Doxia by " + System.getProperty( "user.name" ) ); 326 writeAddAttribute( ElementTags.PAGE_SIZE, ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) ); 327 328 writeStartElement( ElementTags.CHAPTER ); 329 writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth ); 330 writeAddAttribute( ElementTags.DEPTH, depth ); 331 writeAddAttribute( ElementTags.INDENT, "0.0" ); 332 333 writeStartElement( ElementTags.TITLE ); 334 writeAddAttribute( ElementTags.LEADING, DEFAULT_CHAPTER_TITLE_LEADING ); 335 writeAddAttribute( ElementTags.FONT, ITextFont.DEFAULT_FONT_NAME ); 336 writeAddAttribute( ElementTags.SIZE, ITextFont.getSectionFontSize( 0 ) ); 337 writeAddAttribute( ElementTags.STYLE, ITextFont.BOLD ); 338 writeAddAttribute( ElementTags.BLUE, ITextFont.DEFAULT_FONT_COLOR_BLUE ); 339 writeAddAttribute( ElementTags.GREEN, ITextFont.DEFAULT_FONT_COLOR_GREEN ); 340 writeAddAttribute( ElementTags.RED, ITextFont.DEFAULT_FONT_COLOR_RED ); 341 writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER ); 342 343// startChunk( ITextFont.DEFAULT_FONT_NAME, ITextFont.getSectionFontSize( 0 ), 344// ITextFont.BOLD, ITextFont.DEFAULT_FONT_COLOR_BLUE, ITextFont.DEFAULT_FONT_COLOR_GREEN, 345// ITextFont.DEFAULT_FONT_COLOR_RED, "top" ); 346 347 writeStartElement( ElementTags.CHUNK ); 348 writeAddAttribute( ElementTags.FONT, ITextFont.DEFAULT_FONT_NAME ); 349 writeAddAttribute( ElementTags.SIZE, ITextFont.getSectionFontSize( 0 ) ); 350 writeAddAttribute( ElementTags.STYLE, ITextFont.BOLD ); 351 writeAddAttribute( ElementTags.BLUE, ITextFont.DEFAULT_FONT_COLOR_BLUE ); 352 writeAddAttribute( ElementTags.GREEN, ITextFont.DEFAULT_FONT_COLOR_GREEN ); 353 writeAddAttribute( ElementTags.RED, ITextFont.DEFAULT_FONT_COLOR_RED ); 354// writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); 355 356 write( header.getTitle() ); 357 358 writeEndElement(); // ElementTags.CHUNK 359 360 writeEndElement(); // ElementTags.TITLE 361 } 362 363 actionContext.setAction( SinkActionContext.BODY ); 364 } 365 366 // ---------------------------------------------------------------------- 367 // Sections 368 // ---------------------------------------------------------------------- 369 370 /** 371 * {@inheritDoc} 372 */ 373 public void sectionTitle() 374 { 375 actionContext.release(); 376 } 377 378 /** 379 * {@inheritDoc} 380 */ 381 public void sectionTitle_() 382 { 383 actionContext.setAction( SinkActionContext.SECTION_TITLE ); 384 } 385 386 /** 387 * {@inheritDoc} 388 */ 389 public void section1_() 390 { 391 writeEndElement(); // ElementTags.SECTION 392 393 numberDepth--; 394 depth = 0; 395 396 actionContext.release(); 397 } 398 399 /** 400 * {@inheritDoc} 401 */ 402 public void section1() 403 { 404 numberDepth++; 405 depth = 1; 406 407 writeStartElement( ElementTags.SECTION ); 408 writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth ); 409 writeAddAttribute( ElementTags.DEPTH, depth ); 410 writeAddAttribute( ElementTags.INDENT, "0.0" ); 411 412 lineBreak(); 413 414 actionContext.setAction( SinkActionContext.SECTION_1 ); 415 } 416 417 /** 418 * {@inheritDoc} 419 */ 420 public void sectionTitle1_() 421 { 422 writeEndElement(); // ElementTags.TITLE 423 424 font.setSize( ITextFont.DEFAULT_FONT_SIZE ); 425 bold_(); 426 427 actionContext.release(); 428 } 429 430 /** 431 * {@inheritDoc} 432 */ 433 public void sectionTitle1() 434 { 435 font.setSize( ITextFont.getSectionFontSize( 1 ) ); 436 font.setColor( Color.BLACK ); 437 bold(); 438 439 writeStartElement( ElementTags.TITLE ); 440 writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING ); 441 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 442 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 443 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 444 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 445 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 446 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 447// writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve 448 449 actionContext.setAction( SinkActionContext.SECTION_TITLE_1 ); 450 } 451 452 /** 453 * {@inheritDoc} 454 */ 455 public void section2_() 456 { 457 writeEndElement(); // ElementTags.SECTION 458 459 numberDepth--; 460 depth = 0; 461 462 actionContext.release(); 463 } 464 465 /** 466 * {@inheritDoc} 467 */ 468 public void section2() 469 { 470 numberDepth++; 471 depth = 1; 472 473 writeStartElement( ElementTags.SECTION ); 474 writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth ); 475 writeAddAttribute( ElementTags.DEPTH, depth ); 476 writeAddAttribute( ElementTags.INDENT, "0.0" ); 477 478 lineBreak(); 479 480 actionContext.setAction( SinkActionContext.SECTION_2 ); 481 } 482 483 /** 484 * {@inheritDoc} 485 */ 486 public void sectionTitle2_() 487 { 488 writeEndElement(); // ElementTags.TITLE 489 490 font.setSize( ITextFont.DEFAULT_FONT_SIZE ); 491 bold_(); 492 493 actionContext.release(); 494 } 495 496 /** 497 * {@inheritDoc} 498 */ 499 public void sectionTitle2() 500 { 501 font.setSize( ITextFont.getSectionFontSize( 2 ) ); 502 font.setColor( Color.BLACK ); 503 bold(); 504 505 writeStartElement( ElementTags.TITLE ); 506 writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING ); 507 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 508 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 509 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 510 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 511 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 512 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 513// writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve 514 515 actionContext.setAction( SinkActionContext.SECTION_TITLE_2 ); 516 } 517 518 /** 519 * {@inheritDoc} 520 */ 521 public void section3_() 522 { 523 writeEndElement(); // ElementTags.SECTION 524 525 numberDepth--; 526 depth = 1; 527 528 actionContext.release(); 529 } 530 531 /** 532 * {@inheritDoc} 533 */ 534 public void section3() 535 { 536 numberDepth++; 537 depth = 1; 538 539 writeStartElement( ElementTags.SECTION ); 540 writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth ); 541 writeAddAttribute( ElementTags.DEPTH, depth ); 542 writeAddAttribute( ElementTags.INDENT, "0.0" ); 543 544 lineBreak(); 545 546 actionContext.setAction( SinkActionContext.SECTION_3 ); 547 } 548 549 /** 550 * {@inheritDoc} 551 */ 552 public void sectionTitle3_() 553 { 554 writeEndElement(); // ElementTags.TITLE 555 556 font.setSize( ITextFont.DEFAULT_FONT_SIZE ); 557 bold_(); 558 559 actionContext.release(); 560 } 561 562 /** 563 * {@inheritDoc} 564 */ 565 public void sectionTitle3() 566 { 567 font.setSize( ITextFont.getSectionFontSize( 3 ) ); 568 font.setColor( Color.BLACK ); 569 bold(); 570 571 writeStartElement( ElementTags.TITLE ); 572 writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING ); 573 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 574 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 575 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 576 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 577 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 578 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 579// writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve 580 581 actionContext.setAction( SinkActionContext.SECTION_TITLE_3 ); 582 } 583 584 /** 585 * {@inheritDoc} 586 */ 587 public void section4_() 588 { 589 writeEndElement(); // ElementTags.SECTION 590 591 numberDepth--; 592 depth = 1; 593 594 actionContext.release(); 595 } 596 597 /** 598 * {@inheritDoc} 599 */ 600 public void section4() 601 { 602 numberDepth++; 603 depth = 1; 604 605 writeStartElement( ElementTags.SECTION ); 606 writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth ); 607 writeAddAttribute( ElementTags.DEPTH, depth ); 608 writeAddAttribute( ElementTags.INDENT, "0.0" ); 609 610 lineBreak(); 611 612 actionContext.setAction( SinkActionContext.SECTION_4 ); 613 } 614 615 /** 616 * {@inheritDoc} 617 */ 618 public void sectionTitle4_() 619 { 620 writeEndElement(); // ElementTags.TITLE 621 622 font.setSize( ITextFont.DEFAULT_FONT_SIZE ); 623 bold_(); 624 625 actionContext.release(); 626 } 627 628 /** 629 * {@inheritDoc} 630 */ 631 public void sectionTitle4() 632 { 633 font.setSize( ITextFont.getSectionFontSize( 4 ) ); 634 font.setColor( Color.BLACK ); 635 bold(); 636 637 writeStartElement( ElementTags.TITLE ); 638 writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING ); 639 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 640 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 641 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 642 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 643 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 644 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 645// writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve 646 647 actionContext.setAction( SinkActionContext.SECTION_TITLE_4 ); 648 } 649 650 /** 651 * {@inheritDoc} 652 */ 653 public void section5_() 654 { 655 writeEndElement(); // ElementTags.SECTION 656 657 numberDepth--; 658 depth = 1; 659 660 actionContext.release(); 661 } 662 663 /** 664 * {@inheritDoc} 665 */ 666 public void section5() 667 { 668 numberDepth++; 669 depth = 1; 670 671 writeStartElement( ElementTags.SECTION ); 672 writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth ); 673 writeAddAttribute( ElementTags.DEPTH, depth ); 674 writeAddAttribute( ElementTags.INDENT, "0.0" ); 675 676 lineBreak(); 677 678 actionContext.setAction( SinkActionContext.SECTION_5 ); 679 } 680 681 /** 682 * {@inheritDoc} 683 */ 684 public void sectionTitle5_() 685 { 686 writeEndElement(); // ElementTags.TITLE 687 688 font.setSize( ITextFont.DEFAULT_FONT_SIZE ); 689 bold_(); 690 691 actionContext.release(); 692 } 693 694 /** 695 * {@inheritDoc} 696 */ 697 public void sectionTitle5() 698 { 699 font.setSize( ITextFont.getSectionFontSize( 5 ) ); 700 font.setColor( Color.BLACK ); 701 bold(); 702 703 writeStartElement( ElementTags.TITLE ); 704 writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING ); 705 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 706 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 707 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 708 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 709 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 710 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 711// writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve 712 713 actionContext.setAction( SinkActionContext.SECTION_TITLE_5 ); 714 } 715 716 // ---------------------------------------------------------------------- 717 // Paragraph 718 // ---------------------------------------------------------------------- 719 720 /** 721 * {@inheritDoc} 722 */ 723 public void paragraph_() 724 { 725 // Special case 726 if ( ( actionContext.getCurrentAction() == SinkActionContext.LIST_ITEM ) 727 || ( actionContext.getCurrentAction() == SinkActionContext.NUMBERED_LIST_ITEM ) 728 || ( actionContext.getCurrentAction() == SinkActionContext.DEFINITION ) ) 729 { 730 return; 731 } 732 733 writeEndElement(); // ElementTags.PARAGRAPH 734 735 actionContext.release(); 736 } 737 738 /** 739 * {@inheritDoc} 740 */ 741 public void paragraph() 742 { 743 // Special case 744 if ( ( actionContext.getCurrentAction() == SinkActionContext.LIST_ITEM ) 745 || ( actionContext.getCurrentAction() == SinkActionContext.NUMBERED_LIST_ITEM ) 746 || ( actionContext.getCurrentAction() == SinkActionContext.DEFINITION ) ) 747 { 748 return; 749 } 750 751 writeStartElement( ElementTags.PARAGRAPH ); 752 writeStartElement( ElementTags.NEWLINE ); 753 writeEndElement(); 754 755 actionContext.setAction( SinkActionContext.PARAGRAPH ); 756 } 757 758 // ---------------------------------------------------------------------- 759 // Lists 760 // ---------------------------------------------------------------------- 761 762 /** 763 * {@inheritDoc} 764 */ 765 public void list_() 766 { 767 writeEndElement(); // ElementTags.LIST 768 769 writeEndElement(); // ElementTags.CHUNK 770 771 actionContext.release(); 772 } 773 774 /** 775 * {@inheritDoc} 776 */ 777 public void list() 778 { 779 writeStartElement( ElementTags.CHUNK ); 780 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 781 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 782 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 783 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 784 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 785 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 786 787 writeStartElement( ElementTags.LIST ); 788 writeAddAttribute( ElementTags.NUMBERED, Boolean.FALSE.toString() ); 789 writeAddAttribute( ElementTags.SYMBOLINDENT, "15" ); 790 791 actionContext.setAction( SinkActionContext.LIST ); 792 } 793 794 /** 795 * {@inheritDoc} 796 */ 797 public void listItem_() 798 { 799 writeEndElement(); // ElementTags.LISTITEM 800 801 actionContext.release(); 802 } 803 804 /** 805 * {@inheritDoc} 806 */ 807 public void listItem() 808 { 809 writeStartElement( ElementTags.LISTITEM ); 810 writeAddAttribute( ElementTags.INDENTATIONLEFT, "20.0" ); 811 812 actionContext.setAction( SinkActionContext.LIST_ITEM ); 813 } 814 815 /** 816 * {@inheritDoc} 817 */ 818 public void numberedList_() 819 { 820 writeEndElement(); // ElementTags.LIST 821 822 writeEndElement(); // ElementTags.CHUNK 823 824 actionContext.release(); 825 } 826 827 /** {@inheritDoc} */ 828 public void numberedList( int numbering ) 829 { 830 writeStartElement( ElementTags.CHUNK ); 831 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 832 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 833 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 834 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 835 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 836 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 837 838 writeStartElement( ElementTags.LIST ); 839 writeAddAttribute( ElementTags.NUMBERED, Boolean.TRUE.toString() ); 840 writeAddAttribute( ElementTags.SYMBOLINDENT, "20" ); 841 842 switch ( numbering ) 843 { 844 case Sink.NUMBERING_UPPER_ALPHA: 845 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() ); 846 writeAddAttribute( ElementTags.FIRST, 'A' ); 847 break; 848 849 case Sink.NUMBERING_LOWER_ALPHA: 850 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() ); 851 writeAddAttribute( ElementTags.FIRST, 'a' ); 852 break; 853 854 // TODO Doesn't work 855 case Sink.NUMBERING_UPPER_ROMAN: 856 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() ); 857 writeAddAttribute( ElementTags.FIRST, 'I' ); 858 break; 859 860 case Sink.NUMBERING_LOWER_ROMAN: 861 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() ); 862 writeAddAttribute( ElementTags.FIRST, 'i' ); 863 break; 864 865 case Sink.NUMBERING_DECIMAL: 866 default: 867 writeAddAttribute( ElementTags.LETTERED, Boolean.FALSE.toString() ); 868 } 869 870 actionContext.setAction( SinkActionContext.NUMBERED_LIST ); 871 } 872 873 /** 874 * {@inheritDoc} 875 */ 876 public void numberedListItem_() 877 { 878 writeEndElement(); // ElementTags.LISTITEM 879 880 actionContext.release(); 881 } 882 883 /** 884 * {@inheritDoc} 885 */ 886 public void numberedListItem() 887 { 888 writeStartElement( ElementTags.LISTITEM ); 889 writeAddAttribute( ElementTags.INDENTATIONLEFT, "20" ); 890 891 actionContext.setAction( SinkActionContext.NUMBERED_LIST_ITEM ); 892 } 893 894 /** 895 * {@inheritDoc} 896 */ 897 public void definitionList_() 898 { 899 actionContext.release(); 900 } 901 902 /** 903 * {@inheritDoc} 904 */ 905 public void definitionList() 906 { 907 lineBreak(); 908 909 actionContext.setAction( SinkActionContext.DEFINITION_LIST ); 910 } 911 912 /** 913 * {@inheritDoc} 914 */ 915 public void definedTerm_() 916 { 917 font.setSize( ITextFont.DEFAULT_FONT_SIZE ); 918 bold_(); 919 920 writeEndElement(); // ElementTags.CHUNK 921 922 actionContext.release(); 923 924 lineBreak(); 925 } 926 927 /** 928 * {@inheritDoc} 929 */ 930 public void definedTerm() 931 { 932 font.setSize( ITextFont.DEFAULT_FONT_SIZE + 2 ); 933 bold(); 934 935 writeStartElement( ElementTags.CHUNK ); 936 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 937 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 938 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 939 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 940 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 941 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 942 943 actionContext.setAction( SinkActionContext.DEFINED_TERM ); 944 } 945 946 /** 947 * {@inheritDoc} 948 */ 949 public void definition_() 950 { 951 writeEndElement(); // ElementTags.CHUNK 952 953 actionContext.release(); 954 955 lineBreak(); 956 } 957 958 /** 959 * {@inheritDoc} 960 */ 961 public void definition() 962 { 963 writeStartElement( ElementTags.CHUNK ); 964 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 965 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 966 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 967 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 968 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 969 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 970 971 972 writeStartElement( ElementTags.CHUNK ); 973 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 974 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 975 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 976 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 977 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 978 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 979 980 // We need to add a non break space first to display empty string 981 write( "\u00A0" + StringUtils.repeat( " ", 16 ), false, false ); 982 983 writeEndElement(); // ElementTags.CHUNK 984 985 actionContext.setAction( SinkActionContext.DEFINITION ); 986 } 987 988 /** 989 * {@inheritDoc} 990 */ 991 public void definitionListItem_() 992 { 993 actionContext.release(); 994 } 995 996 /** 997 * {@inheritDoc} 998 */ 999 public void definitionListItem() 1000 { 1001 actionContext.setAction( SinkActionContext.DEFINITION_LIST_ITEM ); 1002 } 1003 1004 // ---------------------------------------------------------------------- 1005 // Tables 1006 // ---------------------------------------------------------------------- 1007 1008 /** 1009 * {@inheritDoc} 1010 */ 1011 public void table_() 1012 { 1013 if ( tableCaptionXMLWriter != null ) 1014 { 1015 tableCaptionXMLWriter = null; 1016 1017 writeEndElement(); // ElementTags.TABLE 1018 1019 writeEndElement(); // ElementTags.CHUNK 1020 1021 writeStartElement( ElementTags.PARAGRAPH ); 1022 writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER ); 1023 1024 write( tableCaptionWriter.toString(), true ); 1025 1026 writeEndElement(); // ElementTags.PARAGRAPH 1027 1028 tableCaptionWriter = null; 1029 } 1030 else 1031 { 1032 writeEndElement(); // ElementTags.TABLE 1033 1034 writeEndElement(); // ElementTags.CHUNK 1035 } 1036 actionContext.release(); 1037 } 1038 1039 /** 1040 * {@inheritDoc} 1041 */ 1042 public void table() 1043 { 1044 writeStartElement( ElementTags.CHUNK ); 1045 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1046 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1047 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1048 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1049 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1050 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1051 1052 writeStartElement( ElementTags.TABLE ); 1053 writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() ); 1054 writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() ); 1055 writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() ); 1056 writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() ); 1057 writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER ); 1058 writeAddAttribute( ElementTags.WIDTH, "100.0%" ); 1059 writeAddAttribute( ElementTags.TABLEFITSPAGE, Boolean.TRUE.toString() ); 1060 writeAddAttribute( ElementTags.CELLSFITPAGE, Boolean.TRUE.toString() ); 1061 writeAddAttribute( ElementTags.CELLPADDING, "10" ); 1062 //writeAddAttribute( ElementTags.COLUMNS, "2" ); 1063 1064 actionContext.setAction( SinkActionContext.TABLE ); 1065 } 1066 1067 /** 1068 * {@inheritDoc} 1069 */ 1070 public void tableCaption_() 1071 { 1072 actionContext.release(); 1073 } 1074 1075 /** 1076 * {@inheritDoc} 1077 */ 1078 public void tableCaption() 1079 { 1080 tableCaptionWriter = new StringWriter(); 1081 tableCaptionXMLWriter = new PrettyPrintXMLWriter( tableCaptionWriter ); 1082 actionContext.setAction( SinkActionContext.TABLE_CAPTION ); 1083 } 1084 1085 /** 1086 * {@inheritDoc} 1087 */ 1088 public void tableCell_() 1089 { 1090 writeEndElement(); // ElementTags.CELL 1091 1092 actionContext.release(); 1093 } 1094 1095 /** 1096 * {@inheritDoc} 1097 */ 1098 public void tableCell() 1099 { 1100 writeStartElement( ElementTags.CELL ); 1101 writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() ); 1102 writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() ); 1103 writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() ); 1104 writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() ); 1105 writeAddAttribute( ElementTags.HORIZONTALALIGN, ElementTags.ALIGN_LEFT ); 1106 1107 actionContext.setAction( SinkActionContext.TABLE_CELL ); 1108 } 1109 1110 /** {@inheritDoc} */ 1111 public void tableCell( String width ) 1112 { 1113 actionContext.setAction( SinkActionContext.TABLE_CELL ); 1114 } 1115 1116 /** 1117 * {@inheritDoc} 1118 */ 1119 public void tableHeaderCell_() 1120 { 1121 writeEndElement(); // ElementTags.CELL 1122 1123 actionContext.release(); 1124 } 1125 1126 /** 1127 * {@inheritDoc} 1128 */ 1129 public void tableHeaderCell() 1130 { 1131 writeStartElement( ElementTags.CELL ); 1132 writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() ); 1133 writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() ); 1134 writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() ); 1135 writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() ); 1136 writeAddAttribute( ElementTags.HEADER, Boolean.TRUE.toString() ); 1137 writeAddAttribute( ElementTags.BGRED, Color.GRAY.getRed() ); 1138 writeAddAttribute( ElementTags.BGBLUE, Color.GRAY.getBlue() ); 1139 writeAddAttribute( ElementTags.BGGREEN, Color.GRAY.getGreen() ); 1140 writeAddAttribute( ElementTags.HORIZONTALALIGN, ElementTags.ALIGN_CENTER ); 1141 1142 actionContext.setAction( SinkActionContext.TABLE_HEADER_CELL ); 1143 } 1144 1145 /** {@inheritDoc} */ 1146 public void tableHeaderCell( String width ) 1147 { 1148 actionContext.setAction( SinkActionContext.TABLE_HEADER_CELL ); 1149 } 1150 1151 /** 1152 * {@inheritDoc} 1153 */ 1154 public void tableRow_() 1155 { 1156 writeEndElement(); // ElementTags.ROW 1157 1158 actionContext.release(); 1159 } 1160 1161 /** 1162 * {@inheritDoc} 1163 */ 1164 public void tableRow() 1165 { 1166 writeStartElement( ElementTags.ROW ); 1167 1168 actionContext.setAction( SinkActionContext.TABLE_ROW ); 1169 } 1170 1171 /** 1172 * {@inheritDoc} 1173 */ 1174 public void tableRows_() 1175 { 1176 //writeEndElement(); // ElementTags.TABLE 1177 1178 actionContext.release(); 1179 } 1180 1181 /** {@inheritDoc} */ 1182 public void tableRows( int[] justification, boolean grid ) 1183 { 1184 // ElementTags.TABLE 1185 writeAddAttribute( ElementTags.COLUMNS, justification.length ); 1186 1187 actionContext.setAction( SinkActionContext.TABLE_ROWS ); 1188 } 1189 1190 // ---------------------------------------------------------------------- 1191 // Verbatim 1192 // ---------------------------------------------------------------------- 1193 1194 /** 1195 * {@inheritDoc} 1196 */ 1197 public void verbatim_() 1198 { 1199 writeEndElement(); // ElementTags.CELL 1200 1201 writeEndElement(); // ElementTags.ROW 1202 1203 writeEndElement(); // ElementTags.TABLE 1204 1205 writeEndElement(); // ElementTags.CHUNK 1206 1207 actionContext.release(); 1208 } 1209 1210 /** {@inheritDoc} */ 1211 public void verbatim( boolean boxed ) 1212 { 1213 // Always boxed 1214 writeStartElement( ElementTags.CHUNK ); 1215 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1216 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1217 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1218 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1219 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1220 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1221 1222 writeStartElement( ElementTags.TABLE ); 1223 writeAddAttribute( ElementTags.COLUMNS, "1" ); 1224 writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() ); 1225 writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() ); 1226 writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() ); 1227 writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() ); 1228 writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER ); 1229 writeAddAttribute( ElementTags.TABLEFITSPAGE, Boolean.TRUE.toString() ); 1230 writeAddAttribute( ElementTags.CELLSFITPAGE, Boolean.TRUE.toString() ); 1231 writeAddAttribute( ElementTags.CELLPADDING, "10" ); 1232 writeAddAttribute( ElementTags.WIDTH, "100.0%" ); 1233 1234 writeStartElement( ElementTags.ROW ); 1235 1236 writeStartElement( ElementTags.CELL ); 1237 writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() ); 1238 writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() ); 1239 writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() ); 1240 writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() ); 1241 1242 actionContext.setAction( SinkActionContext.VERBATIM ); 1243 } 1244 1245 // ---------------------------------------------------------------------- 1246 // Figures 1247 // ---------------------------------------------------------------------- 1248 1249 /** 1250 * {@inheritDoc} 1251 */ 1252 public void figure_() 1253 { 1254 writeEndElement(); // ElementTags.IMAGE 1255 1256 writeEndElement(); // ElementTags.CHUNK 1257 1258 actionContext.release(); 1259 1260 figureDefined = false; 1261 } 1262 1263 /** 1264 * {@inheritDoc} 1265 */ 1266 public void figure() 1267 { 1268 figureDefined = true; 1269 1270 writeStartElement( ElementTags.CHUNK ); 1271 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1272 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1273 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1274 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1275 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1276 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1277 1278 writeStartElement( ElementTags.IMAGE ); 1279 1280 actionContext.setAction( SinkActionContext.FIGURE ); 1281 } 1282 1283 /** 1284 * {@inheritDoc} 1285 */ 1286 public void figureCaption_() 1287 { 1288 actionContext.release(); 1289 } 1290 1291 /** 1292 * {@inheritDoc} 1293 */ 1294 public void figureCaption() 1295 { 1296 actionContext.setAction( SinkActionContext.FIGURE_CAPTION ); 1297 } 1298 1299 /** 1300 * If the <code>name</code> is a relative link, the internal link will used a System property 1301 * <code>itext.basedir</code>, or the class loader. 1302 * {@inheritDoc} 1303 */ 1304 public void figureGraphics( String name ) 1305 { 1306 String urlName = null; 1307 File nameFile = null; 1308 if ( ( name.toLowerCase( Locale.ENGLISH ).startsWith( "http://" ) ) 1309 || ( name.toLowerCase( Locale.ENGLISH ).startsWith( "https://" ) ) ) 1310 { 1311 urlName = name; 1312 } 1313 else 1314 { 1315 if ( System.getProperty( "itext.basedir" ) != null ) 1316 { 1317 try 1318 { 1319 nameFile = new File( System.getProperty( "itext.basedir" ), name ); 1320 urlName = nameFile.toURI().toURL().toString(); 1321 } 1322 catch ( MalformedURLException e ) 1323 { 1324 getLog().error( "MalformedURLException: " + e.getMessage(), e ); 1325 } 1326 } 1327 else 1328 { 1329 if ( getClassLoader() != null ) 1330 { 1331 if ( getClassLoader().getResource( name ) != null ) 1332 { 1333 urlName = getClassLoader().getResource( name ).toString(); 1334 } 1335 } 1336 else 1337 { 1338 if ( ITextSink.class.getClassLoader().getResource( name ) != null ) 1339 { 1340 urlName = ITextSink.class.getClassLoader().getResource( name ).toString(); 1341 } 1342 } 1343 } 1344 } 1345 1346 if ( urlName == null ) 1347 { 1348 String msg = 1349 "No image '" + name 1350 + "' found in the class loader. Try to call setClassLoader(ClassLoader) before."; 1351 logMessage( "imageNotFound", msg ); 1352 1353 return; 1354 } 1355 1356 if ( nameFile != null && !nameFile.exists() ) 1357 { 1358 String msg = "No image '" + nameFile + "' found in your system, check the path."; 1359 logMessage( "imageNotFound", msg ); 1360 1361 return; 1362 } 1363 1364 boolean figureCalled = figureDefined; 1365 if ( !figureCalled ) 1366 { 1367 figure(); 1368 } 1369 1370 float width = 0; 1371 float height = 0; 1372 try 1373 { 1374 Image image = Image.getInstance( new URL( urlName ) ); 1375 image.scaleToFit( ITextUtil.getDefaultPageSize().width() / 2, ITextUtil.getDefaultPageSize().height() / 2 ); 1376 width = image.plainWidth(); 1377 height = image.plainHeight(); 1378 } 1379 catch ( BadElementException e ) 1380 { 1381 getLog().error( "BadElementException: " + e.getMessage(), e ); 1382 } 1383 catch ( MalformedURLException e ) 1384 { 1385 getLog().error( "MalformedURLException: " + e.getMessage(), e ); 1386 } 1387 catch ( IOException e ) 1388 { 1389 getLog().error( "IOException: " + e.getMessage(), e ); 1390 } 1391 1392 writeAddAttribute( ElementTags.URL, urlName ); 1393 writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_MIDDLE ); 1394 writeAddAttribute( ElementTags.PLAINWIDTH, String.valueOf( width ) ); 1395 writeAddAttribute( ElementTags.PLAINHEIGHT, String.valueOf( height ) ); 1396 1397 actionContext.setAction( SinkActionContext.FIGURE_GRAPHICS ); 1398 1399 if ( !figureCalled ) 1400 { 1401 figure_(); 1402 } 1403 } 1404 1405 // ---------------------------------------------------------------------- 1406 // Fonts 1407 // ---------------------------------------------------------------------- 1408 1409 /** 1410 * {@inheritDoc} 1411 */ 1412 public void inline() 1413 { 1414 inline( null ); 1415 } 1416 1417 /** {@inheritDoc} */ 1418 public void inline( SinkEventAttributes attributes ) 1419 { 1420 List<String> tags = new ArrayList<>(); 1421 1422 if ( attributes != null ) 1423 { 1424 1425 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "italic" ) ) 1426 { 1427 font.addItalic(); 1428 tags.add( 0, "italic" ); 1429 } 1430 1431 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "bold" ) ) 1432 { 1433 font.addBold(); 1434 tags.add( 0, "bold" ); 1435 } 1436 1437 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "code" ) ) 1438 { 1439 font.setMonoSpaced( true ); 1440 tags.add( 0, "code" ); 1441 } 1442 1443 } 1444 1445 inlineStack.push( tags ); 1446 } 1447 1448 /** 1449 * {@inheritDoc} 1450 */ 1451 public void inline_() 1452 { 1453 for ( String tag: inlineStack.pop() ) 1454 { 1455 if ( "italic".equals( tag ) ) 1456 { 1457 font.removeItalic(); 1458 } 1459 else if ( "bold".equals( tag ) ) 1460 { 1461 font.removeBold(); 1462 } 1463 else if ( "code".equals( tag ) ) 1464 { 1465 font.setMonoSpaced( false ); 1466 } 1467 } 1468 } 1469 1470 /** 1471 * {@inheritDoc} 1472 */ 1473 public void bold_() 1474 { 1475 inline_(); 1476 } 1477 1478 /** 1479 * {@inheritDoc} 1480 */ 1481 public void bold() 1482 { 1483 inline( SinkEventAttributeSet.Semantics.BOLD ); 1484 } 1485 1486 /** 1487 * {@inheritDoc} 1488 */ 1489 public void italic_() 1490 { 1491 inline_(); 1492 } 1493 1494 /** 1495 * {@inheritDoc} 1496 */ 1497 public void italic() 1498 { 1499 inline( SinkEventAttributeSet.Semantics.ITALIC ); 1500 } 1501 1502 /** 1503 * {@inheritDoc} 1504 */ 1505 public void monospaced_() 1506 { 1507 inline_(); 1508 } 1509 1510 /** 1511 * {@inheritDoc} 1512 */ 1513 public void monospaced() 1514 { 1515 inline( SinkEventAttributeSet.Semantics.CODE ); 1516 } 1517 1518 // ---------------------------------------------------------------------- 1519 // Links 1520 // ---------------------------------------------------------------------- 1521 1522 /** 1523 * {@inheritDoc} 1524 */ 1525 public void link_() 1526 { 1527 writeEndElement(); // ElementTags.ANCHOR 1528 1529 font.setColor( Color.BLACK ); 1530 font.removeUnderlined(); 1531 1532 actionContext.release(); 1533 } 1534 1535 /** {@inheritDoc} */ 1536 public void link( String name ) 1537 { 1538 if ( name == null ) 1539 { 1540 throw new NullPointerException( "Link name cannot be null!" ); 1541 } 1542 1543 font.setColor( Color.BLUE ); 1544 font.addUnderlined(); 1545 1546 writeStartElement( ElementTags.ANCHOR ); 1547 if ( StringUtils.isNotEmpty( name ) && name.startsWith( "#" ) && StringUtils.isNotEmpty( header.getTitle() ) ) 1548 { 1549 name = "#" + DoxiaUtils.encodeId( header.getTitle(), true ) + "_" + name.substring( 1 ); 1550 } 1551 writeAddAttribute( ElementTags.REFERENCE, HtmlTools.escapeHTML( name ) ); 1552 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1553 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1554 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1555 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1556 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1557 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1558 1559 actionContext.setAction( SinkActionContext.LINK ); 1560 } 1561 1562 /** 1563 * {@inheritDoc} 1564 */ 1565 public void anchor_() 1566 { 1567 if ( !anchorDefined ) 1568 { 1569 // itext needs a defined local destination, we put an invisible text 1570 writeAddAttribute( ElementTags.BLUE, "255" ); 1571 writeAddAttribute( ElementTags.GREEN, "255" ); 1572 writeAddAttribute( ElementTags.RED, "255" ); 1573 1574 write( "_" ); 1575 } 1576 1577 anchorDefined = false; 1578 1579 writeEndElement(); // ElementTags.ANCHOR 1580 1581 actionContext.release(); 1582 } 1583 1584 /** {@inheritDoc} */ 1585 public void anchor( String name ) 1586 { 1587 if ( name == null ) 1588 { 1589 throw new NullPointerException( "Anchor name cannot be null!" ); 1590 } 1591 1592 if ( StringUtils.isNotEmpty( header.getTitle() ) ) 1593 { 1594 name = header.getTitle() + "_" + name; 1595 } 1596 String id = name; 1597 1598 if ( !DoxiaUtils.isValidId( id ) ) 1599 { 1600 id = DoxiaUtils.encodeId( name, true ); 1601 1602 String msg = "Modified invalid link: '" + name + "' to '" + id + "'"; 1603 logMessage( "modifiedLink", msg ); 1604 } 1605 1606 writeStartElement( ElementTags.ANCHOR ); 1607 writeAddAttribute( ElementTags.NAME, id ); 1608 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1609 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1610 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1611 1612 actionContext.setAction( SinkActionContext.ANCHOR ); 1613 } 1614 1615 // ---------------------------------------------------------------------- 1616 // Misc 1617 // ---------------------------------------------------------------------- 1618 1619 /** 1620 * {@inheritDoc} 1621 */ 1622 public void lineBreak() 1623 { 1624 // Special case for the header 1625 if ( ( actionContext.getCurrentAction() == SinkActionContext.AUTHOR ) 1626 || ( actionContext.getCurrentAction() == SinkActionContext.DATE ) 1627 || ( actionContext.getCurrentAction() == SinkActionContext.TITLE ) ) 1628 { 1629 return; 1630 } 1631 1632 writeStartElement( ElementTags.NEWLINE ); 1633 writeEndElement(); 1634 } 1635 1636 /** 1637 * {@inheritDoc} 1638 */ 1639 public void nonBreakingSpace() 1640 { 1641 write( " " ); 1642 } 1643 1644 /** 1645 * {@inheritDoc} 1646 */ 1647 public void pageBreak() 1648 { 1649 writeStartElement( ElementTags.NEWPAGE ); 1650 writeEndElement(); 1651 } 1652 1653 /** 1654 * {@inheritDoc} 1655 */ 1656 public void horizontalRule() 1657 { 1658 writeStartElement( ElementTags.PARAGRAPH ); 1659 writeAddAttribute( ElementTags.BLUE, "255" ); 1660 writeAddAttribute( ElementTags.GREEN, "255" ); 1661 writeAddAttribute( ElementTags.RED, "255" ); 1662 write( "_" ); 1663 writeEndElement(); 1664 1665 writeStartElement( ElementTags.PARAGRAPH ); 1666 writeStartElement( ElementTags.HORIZONTALRULE ); 1667 writeEndElement(); 1668 writeEndElement(); 1669 } 1670 1671 // ---------------------------------------------------------------------- 1672 // Text 1673 // ---------------------------------------------------------------------- 1674 1675 /** {@inheritDoc} */ 1676 public void rawText( String text ) 1677 { 1678 writeStartElement( ElementTags.CHUNK ); 1679 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1680 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1681 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1682 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1683 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1684 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1685 1686 write( text, false ); 1687 1688 writeEndElement(); // ElementTags.CHUNK 1689 } 1690 1691 /** {@inheritDoc} */ 1692 public void text( String text ) 1693 { 1694 if ( StringUtils.isEmpty( text ) ) 1695 { 1696 return; 1697 } 1698 1699 switch ( actionContext.getCurrentAction() ) 1700 { 1701 case SinkActionContext.AUTHOR: 1702 header.addAuthor( text ); 1703 break; 1704 1705 case SinkActionContext.DATE: 1706 header.setDate( text ); 1707 break; 1708 1709 case SinkActionContext.TITLE: 1710 header.setTitle( text ); 1711 break; 1712 1713 case SinkActionContext.TABLE_CAPTION: 1714 this.tableCaptionXMLWriter.writeText( text ); 1715 break; 1716 1717 case SinkActionContext.VERBATIM: 1718 // Used to preserve indentation and formating 1719 LineNumberReader lnr = new LineNumberReader( new StringReader( text ) ); 1720 String line; 1721 try 1722 { 1723 while ( ( line = lnr.readLine() ) != null ) 1724 { 1725 writeStartElement( ElementTags.CHUNK ); 1726 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1727 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1728 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1729 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1730 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1731 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1732 1733 write( "<![CDATA[", true ); 1734 // Special case 1735 line = StringUtils.replace( line, "<![CDATA[", "< ![CDATA[" ); 1736 line = StringUtils.replace( line, "]]>", "]] >" ); 1737 write( line, true, false ); 1738 write( "]]>", true ); 1739 1740 writeEndElement(); 1741 lineBreak(); 1742 } 1743 } 1744 catch ( IOException e ) 1745 { 1746 throw new RuntimeException( "IOException: ", e ); 1747 } 1748 break; 1749 1750 case SinkActionContext.FIGURE_CAPTION: 1751 writeAddAttribute( ElementTags.ALT, text ); 1752 break; 1753 1754 case SinkActionContext.SECTION_TITLE: 1755 case SinkActionContext.SECTION_1: 1756 case SinkActionContext.SECTION_2: 1757 case SinkActionContext.SECTION_3: 1758 case SinkActionContext.SECTION_4: 1759 case SinkActionContext.SECTION_5: 1760 case SinkActionContext.FIGURE: 1761 case SinkActionContext.FIGURE_GRAPHICS: 1762 case SinkActionContext.TABLE_ROW: 1763 case SinkActionContext.TABLE: 1764 case SinkActionContext.HEAD: 1765 case SinkActionContext.UNDEFINED: 1766 break; 1767 1768 case SinkActionContext.ANCHOR: 1769 anchorDefined = true; 1770 case SinkActionContext.PARAGRAPH: 1771 case SinkActionContext.LINK: 1772 case SinkActionContext.TABLE_CELL: 1773 case SinkActionContext.TABLE_HEADER_CELL: 1774 case SinkActionContext.DEFINITION: 1775 case SinkActionContext.DEFINED_TERM: 1776 case SinkActionContext.NUMBERED_LIST_ITEM: 1777 case SinkActionContext.LIST_ITEM: 1778 case SinkActionContext.SECTION_TITLE_5: 1779 case SinkActionContext.SECTION_TITLE_4: 1780 case SinkActionContext.SECTION_TITLE_3: 1781 case SinkActionContext.SECTION_TITLE_2: 1782 case SinkActionContext.SECTION_TITLE_1: 1783 default: 1784 writeStartElement( ElementTags.CHUNK ); 1785 writeAddAttribute( ElementTags.FONT, font.getFontName() ); 1786 writeAddAttribute( ElementTags.SIZE, font.getFontSize() ); 1787 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() ); 1788 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() ); 1789 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() ); 1790 writeAddAttribute( ElementTags.RED, font.getFontColorRed() ); 1791 1792 write( text ); 1793 1794 writeEndElement(); // ElementTags.CHUNK 1795 } 1796 } 1797 1798 /** 1799 * {@inheritDoc} 1800 * 1801 * Unkown events just log a warning message but are ignored otherwise. 1802 * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes) 1803 */ 1804 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes ) 1805 { 1806 String msg = "Unknown Sink event: '" + name + "', ignoring!"; 1807 logMessage( "unknownEvent", msg ); 1808 } 1809 1810 /** 1811 * {@inheritDoc} 1812 */ 1813 protected void init() 1814 { 1815 super.init(); 1816 1817 this.actionContext = new SinkActionContext(); 1818 this.font = new ITextFont(); 1819 this.header = new ITextHeader(); 1820 1821 this.numberDepth = 1; 1822 this.depth = 0; 1823 this.tableCaptionWriter = null; 1824 this.tableCaptionXMLWriter = null; 1825 this.anchorDefined = false; 1826 this.figureDefined = false; 1827 this.warnMessages = null; 1828 } 1829 1830 /** 1831 * Convenience method to write a starting element. 1832 * 1833 * @param tag the name of the tag 1834 */ 1835 private void writeStartElement( String tag ) 1836 { 1837 if ( tableCaptionXMLWriter == null ) 1838 { 1839 xmlWriter.startElement( tag ); 1840 } 1841 else 1842 { 1843 tableCaptionXMLWriter.startElement( tag ); 1844 } 1845 } 1846 1847 /** 1848 * Convenience method to write a key-value pair. 1849 * 1850 * @param key the name of an attribute 1851 * @param value the value of an attribute 1852 */ 1853 private void writeAddAttribute( String key, String value ) 1854 { 1855 if ( tableCaptionXMLWriter == null ) 1856 { 1857 xmlWriter.addAttribute( key, value ); 1858 } 1859 else 1860 { 1861 tableCaptionXMLWriter.addAttribute( key, value ); 1862 } 1863 } 1864 1865 /** 1866 * Convenience method to write a key-value pair. 1867 * 1868 * @param key the name of an attribute 1869 * @param value the value of an attribute 1870 */ 1871 private void writeAddAttribute( String key, int value ) 1872 { 1873 if ( tableCaptionXMLWriter == null ) 1874 { 1875 xmlWriter.addAttribute( key, String.valueOf( value ) ); 1876 } 1877 else 1878 { 1879 tableCaptionXMLWriter.addAttribute( key, String.valueOf( value ) ); 1880 } 1881 } 1882 1883 /** 1884 * Convenience method to write an end element. 1885 */ 1886 private void writeEndElement() 1887 { 1888 if ( tableCaptionXMLWriter == null ) 1889 { 1890 xmlWriter.endElement(); 1891 } 1892 else 1893 { 1894 tableCaptionXMLWriter.endElement(); 1895 } 1896 } 1897 1898 /** 1899 * {@inheritDoc} 1900 * 1901 * Convenience method to write a String 1902 */ 1903 protected void write( String aString ) 1904 { 1905 write( aString, false ); 1906 } 1907 1908 /** 1909 * Convenience method to write a String depending the escapeHtml flag 1910 * 1911 * @param aString 1912 * @param escapeHtml 1913 */ 1914 private void write( String aString, boolean escapeHtml ) 1915 { 1916 write( aString, escapeHtml, true ); 1917 } 1918 1919 /** 1920 * Convenience method to write a String depending the escapeHtml flag 1921 * 1922 * @param aString 1923 * @param escapeHtml 1924 * @param trim 1925 */ 1926 private void write( String aString, boolean escapeHtml, boolean trim ) 1927 { 1928 if ( aString == null ) 1929 { 1930 return; 1931 } 1932 1933 if ( trim ) 1934 { 1935 aString = StringUtils.replace( aString, "\n", "" ); 1936 1937 LineNumberReader lnr = new LineNumberReader( new StringReader( aString ) ); 1938 StringBuilder sb = new StringBuilder(); 1939 String line; 1940 try 1941 { 1942 while ( ( line = lnr.readLine() ) != null ) 1943 { 1944 sb.append( beautifyPhrase( line.trim() ) ); 1945 sb.append( " " ); 1946 } 1947 1948 aString = sb.toString(); 1949 } 1950 catch ( IOException e ) 1951 { 1952 // nop 1953 } 1954 if ( aString.trim().length() == 0 ) 1955 { 1956 return; 1957 } 1958 } 1959 if ( escapeHtml ) 1960 { 1961 if ( tableCaptionXMLWriter == null ) 1962 { 1963 xmlWriter.writeMarkup( aString ); 1964 } 1965 else 1966 { 1967 tableCaptionXMLWriter.writeMarkup( aString ); 1968 } 1969 } 1970 else 1971 { 1972 if ( tableCaptionXMLWriter == null ) 1973 { 1974 xmlWriter.writeText( aString ); 1975 } 1976 else 1977 { 1978 tableCaptionXMLWriter.writeText( aString ); 1979 } 1980 } 1981 } 1982 1983 /** 1984 * Convenience method to return a beautify phrase, i.e. one space between words. 1985 * 1986 * @param aString 1987 * @return a String with only one space between words 1988 */ 1989 private static String beautifyPhrase( String aString ) 1990 { 1991 String[] strings = StringUtils.split( aString, " " ); 1992 StringBuilder sb = new StringBuilder(); 1993 for ( String string : strings ) 1994 { 1995 if ( string.trim().length() != 0 ) 1996 { 1997 sb.append( string.trim() ); 1998 sb.append( " " ); 1999 } 2000 } 2001 2002 return sb.toString().trim(); 2003 } 2004 2005 private void startChunk( String fontName, int fontSize, String fontStyle, int fontColorBlue, int fontColorGreen, 2006 int fontColorRed, String localDestination ) 2007 { 2008 writeStartElement( ElementTags.CHUNK ); 2009 writeAddAttribute( ElementTags.FONT, fontName ); 2010 writeAddAttribute( ElementTags.SIZE, fontSize ); 2011 writeAddAttribute( ElementTags.STYLE, fontStyle ); 2012 writeAddAttribute( ElementTags.BLUE, fontColorBlue ); 2013 writeAddAttribute( ElementTags.GREEN, fontColorGreen ); 2014 writeAddAttribute( ElementTags.RED, fontColorRed ); 2015// writeAddAttribute( ElementTags.LOCALDESTINATION, localDestination ); 2016 } 2017 2018 /** 2019 * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>. 2020 * 2021 * @param key not null 2022 * @param msg not null 2023 * @see #close() 2024 * @since 1.1.1 2025 */ 2026 private void logMessage( String key, String msg ) 2027 { 2028 msg = "[iText Sink] " + msg; 2029 if ( getLog().isDebugEnabled() ) 2030 { 2031 getLog().debug( msg ); 2032 2033 return; 2034 } 2035 2036 if ( warnMessages == null ) 2037 { 2038 warnMessages = new HashMap<>(); 2039 } 2040 2041 Set<String> set = warnMessages.get( key ); 2042 if ( set == null ) 2043 { 2044 set = new TreeSet<>(); 2045 } 2046 set.add( msg ); 2047 warnMessages.put( key, set ); 2048 } 2049}