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