1 package org.apache.maven.doxia.module.fo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Writer;
24
25 import java.util.Calendar;
26 import java.util.Date;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.ResourceBundle;
31 import java.util.Stack;
32
33 import javax.swing.text.MutableAttributeSet;
34 import javax.swing.text.html.HTML.Tag;
35
36 import org.apache.maven.doxia.document.DocumentCover;
37 import org.apache.maven.doxia.document.DocumentMeta;
38 import org.apache.maven.doxia.document.DocumentModel;
39 import org.apache.maven.doxia.document.DocumentTOC;
40 import org.apache.maven.doxia.document.DocumentTOCItem;
41 import org.apache.maven.doxia.sink.SinkEventAttributes;
42 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
43 import org.apache.maven.doxia.util.DoxiaUtils;
44 import org.apache.maven.doxia.util.HtmlTools;
45
46 import org.codehaus.plexus.util.StringUtils;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class FoAggregateSink
67 extends FoSink
68 {
69
70
71
72
73
74 public static final int TOC_NONE = 0;
75
76
77
78
79
80
81 public static final int TOC_START = 1;
82
83
84
85
86
87
88 public static final int TOC_END = 2;
89
90
91 private static final String COVER_HEADER_HEIGHT = "1.5in";
92
93
94
95
96 private DocumentModel docModel;
97
98
99
100
101 private int chapter = 0;
102
103
104
105
106 private String docName;
107
108
109
110
111 private String docTitle = "";
112
113
114
115
116 private boolean ignoreText;
117
118
119
120
121 private int tocPosition;
122
123
124
125
126 private final Stack<NumberedListItem> tocStack = new Stack<>();
127
128
129
130
131
132
133 public FoAggregateSink( Writer writer )
134 {
135 super( writer );
136 }
137
138
139
140
141 public void head()
142 {
143 head( null );
144 }
145
146
147 public void head( SinkEventAttributes attributes )
148 {
149 init();
150
151 ignoreText = true;
152 }
153
154
155
156
157 public void head_()
158 {
159 ignoreText = false;
160 writeEOL();
161 }
162
163
164
165
166 public void title()
167 {
168 title( null );
169 }
170
171
172 public void title( SinkEventAttributes attributes )
173 {
174
175 }
176
177
178
179
180 public void title_()
181 {
182
183 }
184
185
186
187
188 public void author()
189 {
190 author( null );
191 }
192
193
194 public void author( SinkEventAttributes attributes )
195 {
196
197 }
198
199
200
201
202 public void author_()
203 {
204
205 }
206
207
208
209
210 public void date()
211 {
212 date( null );
213 }
214
215
216 public void date( SinkEventAttributes attributes )
217 {
218
219 }
220
221
222
223
224 public void date_()
225 {
226
227 }
228
229
230
231
232 public void body()
233 {
234 body( null );
235 }
236
237
238 public void body( SinkEventAttributes attributes )
239 {
240 chapter++;
241
242 resetSectionCounter();
243
244 startPageSequence( getHeaderText(), getFooterText() );
245
246 if ( docName == null )
247 {
248 getLog().warn( "No document root specified, local links will not be resolved correctly!" );
249 }
250 else
251 {
252 writeStartTag( BLOCK_TAG, "" );
253 }
254
255 }
256
257
258
259
260 public void body_()
261 {
262 writeEOL();
263 writeEndTag( BLOCK_TAG );
264 writeEndTag( FLOW_TAG );
265 writeEndTag( PAGE_SEQUENCE_TAG );
266
267
268 docName = null;
269 }
270
271
272
273
274
275
276 public void setDocumentTitle( String title )
277 {
278 this.docTitle = title;
279
280 if ( title == null )
281 {
282 this.docTitle = "";
283 }
284 }
285
286
287
288
289
290
291
292 public void setDocumentName( String name )
293 {
294 this.docName = getIdName( name );
295 }
296
297
298
299
300
301
302
303
304
305
306
307 public void setDocumentModel( DocumentModel model )
308 {
309 setDocumentModel( model, TOC_START );
310 }
311
312
313
314
315
316
317
318
319
320 public void setDocumentModel( DocumentModel model, int tocPos )
321 {
322 this.docModel = model;
323 if ( !( tocPos == TOC_NONE || tocPos == TOC_START || tocPos == TOC_END ) )
324 {
325 if ( getLog().isDebugEnabled() )
326 {
327 getLog().debug( "Unrecognized value for tocPosition: " + tocPos + ", using no toc." );
328 }
329 tocPos = TOC_NONE;
330 }
331 this.tocPosition = tocPos;
332
333 if ( this.docModel != null && this.docModel.getToc() != null && this.tocPosition != TOC_NONE )
334 {
335 DocumentTOCItem tocItem = new DocumentTOCItem();
336 tocItem.setName( this.docModel.getToc().getName() );
337 tocItem.setRef( "./toc" );
338 List<DocumentTOCItem> items = new LinkedList<>();
339 if ( this.tocPosition == TOC_START )
340 {
341 items.add( tocItem );
342 }
343 items.addAll( this.docModel.getToc().getItems() );
344 if ( this.tocPosition == TOC_END )
345 {
346 items.add( tocItem );
347 }
348
349 this.docModel.getToc().setItems( items );
350 }
351 }
352
353
354
355
356
357
358
359
360 private String getIdName( String name )
361 {
362 if ( StringUtils.isEmpty( name ) )
363 {
364 getLog().warn( "Empty document reference, links will not be resolved correctly!" );
365 return "";
366 }
367
368 String idName = name.replace( '\\', '/' );
369
370
371 if ( !idName.startsWith( "./" ) )
372 {
373 idName = "./" + idName;
374 }
375
376 if ( idName.substring( 2 ).lastIndexOf( "." ) != -1 )
377 {
378 idName = idName.substring( 0, idName.lastIndexOf( "." ) );
379 }
380
381 while ( idName.contains( "//" ) )
382 {
383 idName = StringUtils.replace( idName, "//", "/" );
384 }
385
386 return idName;
387 }
388
389
390
391
392
393
394 public void figureGraphics( String name )
395 {
396 figureGraphics( name, null );
397 }
398
399
400 public void figureGraphics( String src, SinkEventAttributes attributes )
401 {
402 String anchor = src;
403
404 while ( anchor.startsWith( "./" ) )
405 {
406 anchor = anchor.substring( 2 );
407 }
408
409 if ( anchor.startsWith( "../" ) && docName != null )
410 {
411 anchor = resolveLinkRelativeToBase( anchor );
412 }
413
414 super.figureGraphics( anchor, attributes );
415 }
416
417
418 public void anchor( String name )
419 {
420 anchor( name, null );
421 }
422
423
424 public void anchor( String name, SinkEventAttributes attributes )
425 {
426 if ( name == null )
427 {
428 throw new NullPointerException( "Anchor name cannot be null!" );
429 }
430
431 String anchor = name;
432
433 if ( !DoxiaUtils.isValidId( anchor ) )
434 {
435 anchor = DoxiaUtils.encodeId( name, true );
436
437 String msg = "Modified invalid anchor name: '" + name + "' to '" + anchor + "'";
438 logMessage( "modifiedLink", msg );
439 }
440
441 anchor = "#" + anchor;
442
443 if ( docName != null )
444 {
445 anchor = docName + anchor;
446 }
447
448 writeStartTag( INLINE_TAG, "id", anchor );
449 }
450
451
452 public void link( String name )
453 {
454 link( name, null );
455 }
456
457
458 public void link( String name, SinkEventAttributes attributes )
459 {
460 if ( name == null )
461 {
462 throw new NullPointerException( "Link name cannot be null!" );
463 }
464
465 if ( DoxiaUtils.isExternalLink( name ) )
466 {
467
468 writeStartTag( BASIC_LINK_TAG, "external-destination", HtmlTools.escapeHTML( name ) );
469 writeStartTag( INLINE_TAG, "href.external" );
470 return;
471 }
472
473 while ( name.contains( "//" ) )
474 {
475 name = StringUtils.replace( name, "//", "/" );
476 }
477
478 if ( DoxiaUtils.isInternalLink( name ) )
479 {
480
481 String anchor = name.substring( 1 );
482
483 if ( !DoxiaUtils.isValidId( anchor ) )
484 {
485 String tmp = anchor;
486 anchor = DoxiaUtils.encodeId( anchor, true );
487
488 String msg = "Modified invalid anchor name: '" + tmp + "' to '" + anchor + "'";
489 logMessage( "modifiedLink", msg );
490 }
491
492 if ( docName != null )
493 {
494 anchor = docName + "#" + anchor;
495 }
496
497 writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
498 writeStartTag( INLINE_TAG, "href.internal" );
499 }
500 else if ( name.startsWith( "../" ) )
501 {
502
503
504 if ( docName == null )
505 {
506
507 writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( name ) );
508 writeStartTag( INLINE_TAG, "href.internal" );
509
510 return;
511 }
512
513 String anchor = resolveLinkRelativeToBase( chopExtension( name ) );
514
515 writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
516 writeStartTag( INLINE_TAG, "href.internal" );
517 }
518 else
519 {
520
521
522 String anchor = name;
523
524 if ( anchor.startsWith( "./" ) )
525 {
526 this.link( anchor.substring( 2 ) );
527 return;
528 }
529
530 anchor = chopExtension( anchor );
531
532 String base = docName.substring( 0, docName.lastIndexOf( "/" ) );
533 anchor = base + "/" + anchor;
534
535 writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
536 writeStartTag( INLINE_TAG, "href.internal" );
537 }
538 }
539
540
541 private String resolveLinkRelativeToBase( String name )
542 {
543 String anchor = name;
544
545 String base = docName.substring( 0, docName.lastIndexOf( "/" ) );
546
547 if ( base.contains( "/" ) )
548 {
549 while ( anchor.startsWith( "../" ) )
550 {
551 base = base.substring( 0, base.lastIndexOf( "/" ) );
552
553 anchor = anchor.substring( 3 );
554
555 if ( base.lastIndexOf( "/" ) == -1 )
556 {
557 while ( anchor.startsWith( "../" ) )
558 {
559 anchor = anchor.substring( 3 );
560 }
561 break;
562 }
563 }
564 }
565
566 return base + "/" + anchor;
567 }
568
569 private String chopExtension( String name )
570 {
571 String anchor = name;
572
573 int dot = anchor.lastIndexOf( "." );
574
575 if ( dot != -1 && dot != anchor.length() && anchor.charAt( dot + 1 ) != '/' )
576 {
577 int hash = anchor.indexOf( "#", dot );
578
579 if ( hash != -1 )
580 {
581 int dot2 = anchor.indexOf( ".", hash );
582
583 if ( dot2 != -1 )
584 {
585 anchor =
586 anchor.substring( 0, dot ) + "#" + HtmlTools.encodeId( anchor.substring( hash + 1, dot2 ) );
587 }
588 else
589 {
590 anchor = anchor.substring( 0, dot ) + "#" + HtmlTools.encodeId(
591 anchor.substring( hash + 1 ) );
592 }
593 }
594 else
595 {
596 anchor = anchor.substring( 0, dot );
597 }
598 }
599
600 return anchor;
601 }
602
603
604
605
606
607
608 @Override
609 protected void writeStartTag( Tag tag, String attributeId )
610 {
611 if ( !ignoreText )
612 {
613 super.writeStartTag( tag, attributeId );
614 }
615 }
616
617
618 @Override
619 protected void writeStartTag( Tag tag, String id, String name )
620 {
621 if ( !ignoreText )
622 {
623 super.writeStartTag( tag, id, name );
624 }
625 }
626
627
628 @Override
629 protected void writeEndTag( Tag t )
630 {
631 if ( !ignoreText )
632 {
633 super.writeEndTag( t );
634 }
635 }
636
637
638 @Override
639 protected void writeEmptyTag( Tag tag, String attributeId )
640 {
641 if ( !ignoreText )
642 {
643 super.writeEmptyTag( tag, attributeId );
644 }
645 }
646
647
648 @Override
649 protected void write( String text )
650 {
651 if ( !ignoreText )
652 {
653 super.write( text );
654 }
655 }
656
657
658 @Override
659 protected void writeln( String text )
660 {
661 if ( !ignoreText )
662 {
663 super.writeln( text );
664 }
665 }
666
667
668 @Override
669 protected void content( String text )
670 {
671 if ( !ignoreText )
672 {
673 super.content( text );
674 }
675 }
676
677
678
679
680 protected void newline()
681 {
682 if ( !ignoreText )
683 {
684 writeEOL();
685 }
686 }
687
688
689
690
691
692
693
694 protected void startPageSequence( String headerText, String footerText )
695 {
696 if ( chapter == 1 )
697 {
698 startPageSequence( "0", headerText, footerText );
699 }
700 else
701 {
702 startPageSequence( "auto", headerText, footerText );
703 }
704 }
705
706
707
708
709
710
711 protected String getHeaderText()
712 {
713 return chapter + " " + docTitle;
714 }
715
716
717
718
719
720
721 protected String getFooterText()
722 {
723 int actualYear;
724 String add = " • " + getBundle( Locale.US ).getString( "footer.rights" );
725 String companyName = "";
726
727 if ( docModel != null && docModel.getMeta() != null && docModel.getMeta().isConfidential() )
728 {
729 add = add + " • " + getBundle( Locale.US ).getString( "footer.confidential" );
730 }
731
732 if ( docModel != null && docModel.getCover() != null && docModel.getCover().getCompanyName() != null )
733 {
734 companyName = docModel.getCover().getCompanyName();
735 }
736
737 if ( docModel != null && docModel.getMeta() != null && docModel.getMeta().getDate() != null )
738 {
739 Calendar date = Calendar.getInstance();
740 date.setTime( docModel.getMeta().getDate() );
741 actualYear = date.get( Calendar.YEAR );
742 }
743 else
744 {
745 actualYear = Calendar.getInstance().get( Calendar.YEAR );
746 }
747
748 return "©" + actualYear + ", " + escaped( companyName, false ) + add;
749 }
750
751
752 @Override
753 protected String getChapterString()
754 {
755 return chapter + ".";
756 }
757
758
759 @Override
760 protected void regionBefore( String headerText )
761 {
762 writeStartTag( STATIC_CONTENT_TAG, "flow-name", "xsl-region-before" );
763 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
764 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "5.625in" );
765 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.625in" );
766 writeStartTag( TABLE_BODY_TAG, "" );
767 writeStartTag( TABLE_ROW_TAG, "" );
768 writeStartTag( TABLE_CELL_TAG, "" );
769 writeStartTag( BLOCK_TAG, "header.style" );
770
771 if ( headerText != null )
772 {
773 write( headerText );
774 }
775
776 writeEndTag( BLOCK_TAG );
777 writeEndTag( TABLE_CELL_TAG );
778 writeStartTag( TABLE_CELL_TAG, "" );
779 writeStartTag( BLOCK_TAG, "page.number" );
780 writeEmptyTag( PAGE_NUMBER_TAG, "" );
781 writeEndTag( BLOCK_TAG );
782 writeEndTag( TABLE_CELL_TAG );
783 writeEndTag( TABLE_ROW_TAG );
784 writeEndTag( TABLE_BODY_TAG );
785 writeEndTag( TABLE_TAG );
786 writeEndTag( STATIC_CONTENT_TAG );
787 }
788
789
790 @Override
791 protected void regionAfter( String footerText )
792 {
793 writeStartTag( STATIC_CONTENT_TAG, "flow-name", "xsl-region-after" );
794 writeStartTag( BLOCK_TAG, "footer.style" );
795
796 if ( footerText != null )
797 {
798 write( footerText );
799 }
800
801 writeEndTag( BLOCK_TAG );
802 writeEndTag( STATIC_CONTENT_TAG );
803 }
804
805
806 @Override
807 protected void chapterHeading( String headerText, boolean chapterNumber )
808 {
809 if ( docName == null )
810 {
811 getLog().warn( "No document root specified, local links will not be resolved correctly!" );
812 writeStartTag( BLOCK_TAG, "" );
813 }
814 else
815 {
816 writeStartTag( BLOCK_TAG, "id", docName );
817 }
818
819 writeStartTag( LIST_BLOCK_TAG, "" );
820 writeStartTag( LIST_ITEM_TAG, "" );
821 writeln( "<fo:list-item-label end-indent=\"6.375in\" start-indent=\"-1in\">" );
822 writeStartTag( BLOCK_TAG, "outdented.number.style" );
823
824 if ( chapterNumber )
825 {
826 writeStartTag( BLOCK_TAG, "chapter.title" );
827 write( Integer.toString( chapter ) );
828 writeEndTag( BLOCK_TAG );
829 }
830
831 writeEndTag( BLOCK_TAG );
832 writeEndTag( LIST_ITEM_LABEL_TAG );
833 writeln( "<fo:list-item-body end-indent=\"1in\" start-indent=\"0in\">" );
834 writeStartTag( BLOCK_TAG, "chapter.title" );
835
836 if ( headerText == null )
837 {
838 text( docTitle );
839 }
840 else
841 {
842 text( headerText );
843 }
844
845 writeEndTag( BLOCK_TAG );
846 writeEndTag( LIST_ITEM_BODY_TAG );
847 writeEndTag( LIST_ITEM_TAG );
848 writeEndTag( LIST_BLOCK_TAG );
849 writeEndTag( BLOCK_TAG );
850 writeStartTag( BLOCK_TAG, "space-after.optimum", "0em" );
851 writeEmptyTag( LEADER_TAG, "chapter.rule" );
852 writeEndTag( BLOCK_TAG );
853 }
854
855
856
857
858 public void toc()
859 {
860 if ( docModel == null || docModel.getToc() == null || docModel.getToc().getItems() == null
861 || this.tocPosition == TOC_NONE )
862 {
863 return;
864 }
865
866 DocumentTOC toc = docModel.getToc();
867
868 writeln( "<fo:page-sequence master-reference=\"toc\" initial-page-number=\"1\" format=\"i\">" );
869 regionBefore( toc.getName() );
870 regionAfter( getFooterText() );
871 writeStartTag( FLOW_TAG, "flow-name", "xsl-region-body" );
872 writeStartTag( BLOCK_TAG, "id", "./toc" );
873 chapterHeading( toc.getName(), false );
874 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
875 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.45in" );
876 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.4in" );
877 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.4in" );
878 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "5in" );
879 writeStartTag( TABLE_BODY_TAG );
880
881 writeTocItems( toc.getItems(), 1 );
882
883 writeEndTag( TABLE_BODY_TAG );
884 writeEndTag( TABLE_TAG );
885 writeEndTag( BLOCK_TAG );
886 writeEndTag( FLOW_TAG );
887 writeEndTag( PAGE_SEQUENCE_TAG );
888 }
889
890 private void writeTocItems( List<DocumentTOCItem> tocItems, int level )
891 {
892 final int maxTocLevel = 4;
893
894 if ( level < 1 || level > maxTocLevel )
895 {
896 return;
897 }
898
899 tocStack.push( new NumberedListItem( NUMBERING_DECIMAL ) );
900
901 boolean printToc = ( level == 1 );
902
903 for ( DocumentTOCItem tocItem : tocItems )
904 {
905 String ref = getIdName( tocItem.getRef() );
906
907 writeStartTag( TABLE_ROW_TAG, "keep-with-next", "auto" );
908
909 if ( level > 2 )
910 {
911 for ( int i = 0; i < level - 2; i++ )
912 {
913 writeStartTag( TABLE_CELL_TAG );
914 writeSimpleTag( BLOCK_TAG );
915 writeEndTag( TABLE_CELL_TAG );
916 }
917 }
918
919 writeStartTag( TABLE_CELL_TAG, "toc.cell" );
920 writeStartTag( BLOCK_TAG, "toc.number.style" );
921
922 NumberedListItem current = tocStack.peek();
923 if ( printToc )
924 {
925
926 printToc = false;
927 }
928 else
929 {
930 current.next();
931 write( currentTocNumber() );
932 }
933
934 writeEndTag( BLOCK_TAG );
935 writeEndTag( TABLE_CELL_TAG );
936
937 String span = "3";
938
939 if ( level > 2 )
940 {
941 span = Integer.toString( 5 - level );
942 }
943
944 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", span, "toc.cell" );
945 MutableAttributeSet atts = getFoConfiguration().getAttributeSet( "toc.h" + level + ".style" );
946 atts.addAttribute( "text-align-last", "justify" );
947 writeStartTag( BLOCK_TAG, atts );
948 writeStartTag( BASIC_LINK_TAG, "internal-destination", ref );
949 text( tocItem.getName() );
950 writeEndTag( BASIC_LINK_TAG );
951 writeEmptyTag( LEADER_TAG, "toc.leader.style" );
952 writeStartTag( INLINE_TAG, "page.number" );
953 writeEmptyTag( PAGE_NUMBER_CITATION_TAG, "ref-id", ref );
954 writeEndTag( INLINE_TAG );
955 writeEndTag( BLOCK_TAG );
956 writeEndTag( TABLE_CELL_TAG );
957 writeEndTag( TABLE_ROW_TAG );
958
959 if ( tocItem.getItems() != null )
960 {
961 writeTocItems( tocItem.getItems(), level + 1 );
962 }
963 }
964
965 tocStack.pop();
966 }
967
968 private String currentTocNumber()
969 {
970 StringBuilder ch = new StringBuilder( tocStack.get( 0 ).getListItemSymbol() );
971
972 for ( int i = 1; i < tocStack.size(); i++ )
973 {
974 ch.append( tocStack.get( i ).getListItemSymbol() );
975 }
976
977 return ch.toString();
978 }
979
980
981
982
983 protected void pdfBookmarks()
984 {
985 if ( docModel == null || docModel.getToc() == null )
986 {
987 return;
988 }
989
990 writeStartTag( BOOKMARK_TREE_TAG );
991
992 renderBookmarkItems( docModel.getToc().getItems() );
993
994 writeEndTag( BOOKMARK_TREE_TAG );
995 }
996
997 private void renderBookmarkItems( List<DocumentTOCItem> items )
998 {
999 for ( DocumentTOCItem tocItem : items )
1000 {
1001 String ref = getIdName( tocItem.getRef() );
1002
1003 writeStartTag( BOOKMARK_TAG, "internal-destination", ref );
1004 writeStartTag( BOOKMARK_TITLE_TAG );
1005 text( tocItem.getName() );
1006 writeEndTag( BOOKMARK_TITLE_TAG );
1007
1008 if ( tocItem.getItems() != null )
1009 {
1010 renderBookmarkItems( tocItem.getItems() );
1011 }
1012
1013 writeEndTag( BOOKMARK_TAG );
1014 }
1015 }
1016
1017
1018
1019
1020 public void coverPage()
1021 {
1022 if ( this.docModel == null )
1023 {
1024 return;
1025 }
1026
1027 DocumentCover cover = docModel.getCover();
1028 DocumentMeta meta = docModel.getMeta();
1029
1030 if ( cover == null && meta == null )
1031 {
1032 return;
1033 }
1034
1035
1036
1037 writeStartTag( PAGE_SEQUENCE_TAG, "master-reference", "cover-page" );
1038 writeStartTag( FLOW_TAG, "flow-name", "xsl-region-body" );
1039 writeStartTag( BLOCK_TAG, "text-align", "center" );
1040 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
1041 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "3.125in" );
1042 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "3.125in" );
1043 writeStartTag( TABLE_BODY_TAG );
1044
1045 writeCoverHead( cover );
1046 writeCoverBody( cover, meta );
1047 writeCoverFooter( cover, meta );
1048
1049 writeEndTag( TABLE_BODY_TAG );
1050 writeEndTag( TABLE_TAG );
1051 writeEndTag( BLOCK_TAG );
1052 writeEndTag( FLOW_TAG );
1053 writeEndTag( PAGE_SEQUENCE_TAG );
1054 }
1055
1056 private void writeCoverHead( DocumentCover cover )
1057 {
1058 if ( cover == null )
1059 {
1060 return;
1061 }
1062
1063 String compLogo = cover.getCompanyLogo();
1064 String projLogo = cover.getProjectLogo();
1065
1066 writeStartTag( TABLE_ROW_TAG, "height", COVER_HEADER_HEIGHT );
1067 writeStartTag( TABLE_CELL_TAG );
1068
1069 if ( StringUtils.isNotEmpty( compLogo ) )
1070 {
1071 SinkEventAttributeSet atts = new SinkEventAttributeSet();
1072 atts.addAttribute( "text-align", "left" );
1073 atts.addAttribute( "vertical-align", "top" );
1074 writeStartTag( BLOCK_TAG, atts );
1075 figureGraphics( compLogo, getGraphicsAttributes( compLogo ) );
1076 writeEndTag( BLOCK_TAG );
1077 }
1078
1079 writeSimpleTag( BLOCK_TAG );
1080 writeEndTag( TABLE_CELL_TAG );
1081 writeStartTag( TABLE_CELL_TAG );
1082
1083 if ( StringUtils.isNotEmpty( projLogo ) )
1084 {
1085 SinkEventAttributeSet atts = new SinkEventAttributeSet();
1086 atts.addAttribute( "text-align", "right" );
1087 atts.addAttribute( "vertical-align", "top" );
1088 writeStartTag( BLOCK_TAG, atts );
1089 figureGraphics( projLogo, getGraphicsAttributes( projLogo ) );
1090 writeEndTag( BLOCK_TAG );
1091 }
1092
1093 writeSimpleTag( BLOCK_TAG );
1094 writeEndTag( TABLE_CELL_TAG );
1095 writeEndTag( TABLE_ROW_TAG );
1096 }
1097
1098 private void writeCoverBody( DocumentCover cover, DocumentMeta meta )
1099 {
1100 if ( cover == null && meta == null )
1101 {
1102 return;
1103 }
1104
1105 String subtitle = null;
1106 String title;
1107 String type = null;
1108 String version = null;
1109 if ( cover == null )
1110 {
1111
1112 getLog().debug( "The DocumentCover is not defined, using the DocumentMeta title as cover title." );
1113 title = meta.getTitle();
1114 }
1115 else
1116 {
1117 subtitle = cover.getCoverSubTitle();
1118 title = cover.getCoverTitle();
1119 type = cover.getCoverType();
1120 version = cover.getCoverVersion();
1121 }
1122
1123 writeln( "<fo:table-row keep-with-previous=\"always\" height=\"0.014in\">" );
1124 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1125 writeStartTag( BLOCK_TAG, "line-height", "0.014in" );
1126 writeEmptyTag( LEADER_TAG, "chapter.rule" );
1127 writeEndTag( BLOCK_TAG );
1128 writeEndTag( TABLE_CELL_TAG );
1129 writeEndTag( TABLE_ROW_TAG );
1130
1131 writeStartTag( TABLE_ROW_TAG, "height", "7.447in" );
1132 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1133 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
1134 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
1135 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
1136 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
1137
1138 writeStartTag( TABLE_BODY_TAG );
1139
1140 writeStartTag( TABLE_ROW_TAG );
1141 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "3" );
1142 writeSimpleTag( BLOCK_TAG );
1143 writeEmptyTag( BLOCK_TAG, "space-before", "3.2235in" );
1144 writeEndTag( TABLE_CELL_TAG );
1145 writeEndTag( TABLE_ROW_TAG );
1146
1147 writeStartTag( TABLE_ROW_TAG );
1148 writeStartTag( TABLE_CELL_TAG );
1149 writeEmptyTag( BLOCK_TAG, "space-after", "0.5in" );
1150 writeEndTag( TABLE_CELL_TAG );
1151
1152 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2", "cover.border.left" );
1153 writeStartTag( BLOCK_TAG, "cover.title" );
1154 text( title == null ? "" : title );
1155 writeEndTag( BLOCK_TAG );
1156 writeEndTag( TABLE_CELL_TAG );
1157 writeEndTag( TABLE_ROW_TAG );
1158
1159 writeStartTag( TABLE_ROW_TAG );
1160 writeStartTag( TABLE_CELL_TAG );
1161 writeSimpleTag( BLOCK_TAG );
1162 writeEndTag( TABLE_CELL_TAG );
1163
1164 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2", "cover.border.left.bottom" );
1165 writeStartTag( BLOCK_TAG, "cover.subtitle" );
1166 text( subtitle == null ? ( version == null ? "" : " v. " + version ) : subtitle );
1167 writeEndTag( BLOCK_TAG );
1168 writeStartTag( BLOCK_TAG, "cover.subtitle" );
1169 text( type == null ? "" : type );
1170 writeEndTag( BLOCK_TAG );
1171 writeEndTag( TABLE_CELL_TAG );
1172 writeEndTag( TABLE_ROW_TAG );
1173
1174 writeEndTag( TABLE_BODY_TAG );
1175 writeEndTag( TABLE_TAG );
1176
1177 writeEndTag( TABLE_CELL_TAG );
1178 writeEndTag( TABLE_ROW_TAG );
1179
1180 writeStartTag( TABLE_ROW_TAG, "height", "0.014in" );
1181 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1182 writeln( "<fo:block space-after=\"0.2in\" line-height=\"0.014in\">" );
1183 writeEmptyTag( LEADER_TAG, "chapter.rule" );
1184 writeEndTag( BLOCK_TAG );
1185 writeEndTag( TABLE_CELL_TAG );
1186 writeEndTag( TABLE_ROW_TAG );
1187
1188 writeStartTag( TABLE_ROW_TAG );
1189 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1190 writeSimpleTag( BLOCK_TAG );
1191 writeEmptyTag( BLOCK_TAG, "space-before", "0.2in" );
1192 writeEndTag( TABLE_CELL_TAG );
1193 writeEndTag( TABLE_ROW_TAG );
1194 }
1195
1196 private void writeCoverFooter( DocumentCover cover, DocumentMeta meta )
1197 {
1198 if ( cover == null && meta == null )
1199 {
1200 return;
1201 }
1202
1203 String date = null;
1204 String compName;
1205 if ( cover == null )
1206 {
1207
1208 getLog().debug( "The DocumentCover is not defined, using the DocumentMeta author as company name." );
1209 compName = meta.getAuthor();
1210 }
1211 else
1212 {
1213 compName = cover.getCompanyName();
1214
1215 if ( cover.getCoverdate() == null )
1216 {
1217 cover.setCoverDate( new Date() );
1218 date = cover.getCoverdate();
1219 cover.setCoverDate( null );
1220 }
1221 else
1222 {
1223 date = cover.getCoverdate();
1224 }
1225 }
1226
1227 writeStartTag( TABLE_ROW_TAG, "height", "0.3in" );
1228
1229 writeStartTag( TABLE_CELL_TAG );
1230 MutableAttributeSet att = getFoConfiguration().getAttributeSet( "cover.subtitle" );
1231 att.addAttribute( "height", "0.3in" );
1232 att.addAttribute( "text-align", "left" );
1233 writeStartTag( BLOCK_TAG, att );
1234 text( compName == null ? ( cover.getAuthor() == null ? "" : cover.getAuthor() ) : compName );
1235 writeEndTag( BLOCK_TAG );
1236 writeEndTag( TABLE_CELL_TAG );
1237
1238 writeStartTag( TABLE_CELL_TAG );
1239 att = getFoConfiguration().getAttributeSet( "cover.subtitle" );
1240 att.addAttribute( "height", "0.3in" );
1241 att.addAttribute( "text-align", "right" );
1242 writeStartTag( BLOCK_TAG, att );
1243 text( date == null ? "" : date );
1244 writeEndTag( BLOCK_TAG );
1245 writeEndTag( TABLE_CELL_TAG );
1246
1247 writeEndTag( TABLE_ROW_TAG );
1248 }
1249
1250 private ResourceBundle getBundle( Locale locale )
1251 {
1252 return ResourceBundle.getBundle( "doxia-fo", locale, this.getClass().getClassLoader() );
1253 }
1254
1255 private SinkEventAttributeSet getGraphicsAttributes( String logo )
1256 {
1257 MutableAttributeSet atts = null;
1258
1259 try
1260 {
1261 atts = DoxiaUtils.getImageAttributes( logo );
1262 }
1263 catch ( IOException e )
1264 {
1265 getLog().debug( e );
1266 }
1267
1268 if ( atts == null )
1269 {
1270 return new SinkEventAttributeSet( SinkEventAttributes.HEIGHT, COVER_HEADER_HEIGHT );
1271 }
1272
1273
1274
1275 final int maxWidth = 225;
1276
1277 if ( Integer.parseInt( atts.getAttribute( SinkEventAttributes.WIDTH ).toString() ) > maxWidth )
1278 {
1279 atts.addAttribute( "content-width", "3.125in" );
1280 }
1281
1282 return new SinkEventAttributeSet( atts );
1283 }
1284 }