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