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
67
68
69 public class FoAggregateSink
70 extends FoSink
71 {
72
73
74
75
76
77 public static final int TOC_NONE = 0;
78
79
80
81
82
83
84 public static final int TOC_START = 1;
85
86
87
88
89
90
91 public static final 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, "" );
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 if ( docName == null )
868 {
869 getLog().warn( "No document root specified, local links will not be resolved correctly!" );
870 writeStartTag( BLOCK_TAG, "" );
871 }
872 else
873 {
874 writeStartTag( BLOCK_TAG, "id", docName );
875 }
876
877 writeStartTag( LIST_BLOCK_TAG, "" );
878 writeStartTag( LIST_ITEM_TAG, "" );
879 writeln( "<fo:list-item-label end-indent=\"6.375in\" start-indent=\"-1in\">" );
880 writeStartTag( BLOCK_TAG, "outdented.number.style" );
881
882 if ( chapterNumber )
883 {
884 writeStartTag( BLOCK_TAG, "chapter.title" );
885 write( Integer.toString( chapter ) );
886 writeEndTag( BLOCK_TAG );
887 }
888
889 writeEndTag( BLOCK_TAG );
890 writeEndTag( LIST_ITEM_LABEL_TAG );
891 writeln( "<fo:list-item-body end-indent=\"1in\" start-indent=\"0in\">" );
892 writeStartTag( BLOCK_TAG, "chapter.title" );
893
894 if ( headerText == null )
895 {
896 text( docTitle );
897 }
898 else
899 {
900 text( headerText );
901 }
902
903 writeEndTag( BLOCK_TAG );
904 writeEndTag( LIST_ITEM_BODY_TAG );
905 writeEndTag( LIST_ITEM_TAG );
906 writeEndTag( LIST_BLOCK_TAG );
907 writeEndTag( BLOCK_TAG );
908 writeStartTag( BLOCK_TAG, "space-after.optimum", "0em" );
909 writeEmptyTag( LEADER_TAG, "chapter.rule" );
910 writeEndTag( BLOCK_TAG );
911 }
912
913
914
915
916 public void toc()
917 {
918 if ( docModel == null || docModel.getToc() == null || docModel.getToc().getItems() == null
919 || this.tocPosition == TOC_NONE )
920 {
921 return;
922 }
923
924 DocumentTOC toc = docModel.getToc();
925
926 writeln( "<fo:page-sequence master-reference=\"toc\" initial-page-number=\"1\" format=\"i\">" );
927 regionBefore( toc.getName() );
928 regionAfter( getFooterText() );
929 writeStartTag( FLOW_TAG, "flow-name", "xsl-region-body" );
930 writeStartTag( BLOCK_TAG, "id", "./toc" );
931 chapterHeading( toc.getName(), false );
932 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
933 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.45in" );
934 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.4in" );
935 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.4in" );
936 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "5in" );
937 writeStartTag( TABLE_BODY_TAG );
938
939 writeTocItems( toc.getItems(), 1 );
940
941 writeEndTag( TABLE_BODY_TAG );
942 writeEndTag( TABLE_TAG );
943 writeEndTag( BLOCK_TAG );
944 writeEndTag( FLOW_TAG );
945 writeEndTag( PAGE_SEQUENCE_TAG );
946 }
947
948 private void writeTocItems( List<DocumentTOCItem> tocItems, int level )
949 {
950 final int maxTocLevel = 4;
951
952 if ( level < 1 || level > maxTocLevel )
953 {
954 return;
955 }
956
957 tocStack.push( new NumberedListItem( NUMBERING_DECIMAL ) );
958
959 boolean printToc = ( level == 1 );
960
961 for ( DocumentTOCItem tocItem : tocItems )
962 {
963 String ref = getIdName( tocItem.getRef() );
964
965 writeStartTag( TABLE_ROW_TAG, "keep-with-next", "auto" );
966
967 if ( level > 2 )
968 {
969 for ( int i = 0; i < level - 2; i++ )
970 {
971 writeStartTag( TABLE_CELL_TAG );
972 writeSimpleTag( BLOCK_TAG );
973 writeEndTag( TABLE_CELL_TAG );
974 }
975 }
976
977 writeStartTag( TABLE_CELL_TAG, "toc.cell" );
978 writeStartTag( BLOCK_TAG, "toc.number.style" );
979
980 NumberedListItem current = tocStack.peek();
981 if ( printToc )
982 {
983
984 printToc = false;
985 }
986 else
987 {
988 current.next();
989 write( currentTocNumber() );
990 }
991
992 writeEndTag( BLOCK_TAG );
993 writeEndTag( TABLE_CELL_TAG );
994
995 String span = "3";
996
997 if ( level > 2 )
998 {
999 span = Integer.toString( 5 - level );
1000 }
1001
1002 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", span, "toc.cell" );
1003 MutableAttributeSet atts = getFoConfiguration().getAttributeSet( "toc.h" + level + ".style" );
1004 atts.addAttribute( "text-align-last", "justify" );
1005 writeStartTag( BLOCK_TAG, atts );
1006 writeStartTag( BASIC_LINK_TAG, "internal-destination", ref );
1007 text( tocItem.getName() );
1008 writeEndTag( BASIC_LINK_TAG );
1009 writeEmptyTag( LEADER_TAG, "toc.leader.style" );
1010 writeStartTag( INLINE_TAG, "page.number" );
1011 writeEmptyTag( PAGE_NUMBER_CITATION_TAG, "ref-id", ref );
1012 writeEndTag( INLINE_TAG );
1013 writeEndTag( BLOCK_TAG );
1014 writeEndTag( TABLE_CELL_TAG );
1015 writeEndTag( TABLE_ROW_TAG );
1016
1017 if ( tocItem.getItems() != null )
1018 {
1019 writeTocItems( tocItem.getItems(), level + 1 );
1020 }
1021 }
1022
1023 tocStack.pop();
1024 }
1025
1026 private String currentTocNumber()
1027 {
1028 StringBuilder ch = new StringBuilder( tocStack.get( 0 ).getListItemSymbol() );
1029
1030 for ( int i = 1; i < tocStack.size(); i++ )
1031 {
1032 ch.append( tocStack.get( i ).getListItemSymbol() );
1033 }
1034
1035 return ch.toString();
1036 }
1037
1038
1039
1040
1041
1042
1043 protected void pdfBookmarks()
1044 {
1045 if ( docModel == null || docModel.getToc() == null )
1046 {
1047 return;
1048 }
1049
1050 writeStartTag( BOOKMARK_TREE_TAG );
1051
1052 renderBookmarkItems( docModel.getToc().getItems() );
1053
1054 writeEndTag( BOOKMARK_TREE_TAG );
1055 }
1056
1057 private void renderBookmarkItems( List<DocumentTOCItem> items )
1058 {
1059 for ( DocumentTOCItem tocItem : items )
1060 {
1061 String ref = getIdName( tocItem.getRef() );
1062
1063 writeStartTag( BOOKMARK_TAG, "internal-destination", ref );
1064 writeStartTag( BOOKMARK_TITLE_TAG );
1065 text( tocItem.getName() );
1066 writeEndTag( BOOKMARK_TITLE_TAG );
1067
1068 if ( tocItem.getItems() != null )
1069 {
1070 renderBookmarkItems( tocItem.getItems() );
1071 }
1072
1073 writeEndTag( BOOKMARK_TAG );
1074 }
1075 }
1076
1077
1078
1079
1080 public void coverPage()
1081 {
1082 if ( this.docModel == null )
1083 {
1084 return;
1085 }
1086
1087 DocumentCover cover = docModel.getCover();
1088 DocumentMeta meta = docModel.getMeta();
1089
1090 if ( cover == null && meta == null )
1091 {
1092 return;
1093 }
1094
1095
1096
1097 writeStartTag( PAGE_SEQUENCE_TAG, "master-reference", "cover-page" );
1098 writeStartTag( FLOW_TAG, "flow-name", "xsl-region-body" );
1099 writeStartTag( BLOCK_TAG, "text-align", "center" );
1100 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
1101 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "3.125in" );
1102 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "3.125in" );
1103 writeStartTag( TABLE_BODY_TAG );
1104
1105 writeCoverHead( cover );
1106 writeCoverBody( cover, meta );
1107 writeCoverFooter( cover, meta );
1108
1109 writeEndTag( TABLE_BODY_TAG );
1110 writeEndTag( TABLE_TAG );
1111 writeEndTag( BLOCK_TAG );
1112 writeEndTag( FLOW_TAG );
1113 writeEndTag( PAGE_SEQUENCE_TAG );
1114 }
1115
1116 private void writeCoverHead( DocumentCover cover )
1117 {
1118 if ( cover == null )
1119 {
1120 return;
1121 }
1122
1123 String compLogo = cover.getCompanyLogo();
1124 String projLogo = cover.getProjectLogo();
1125
1126 writeStartTag( TABLE_ROW_TAG, "height", COVER_HEADER_HEIGHT );
1127 writeStartTag( TABLE_CELL_TAG );
1128
1129 if ( StringUtils.isNotEmpty( compLogo ) )
1130 {
1131 SinkEventAttributeSet atts = new SinkEventAttributeSet();
1132 atts.addAttribute( "text-align", "left" );
1133 atts.addAttribute( "vertical-align", "top" );
1134 writeStartTag( BLOCK_TAG, atts );
1135 figureGraphics( compLogo, getGraphicsAttributes( compLogo ) );
1136 writeEndTag( BLOCK_TAG );
1137 }
1138
1139 writeSimpleTag( BLOCK_TAG );
1140 writeEndTag( TABLE_CELL_TAG );
1141 writeStartTag( TABLE_CELL_TAG );
1142
1143 if ( StringUtils.isNotEmpty( projLogo ) )
1144 {
1145 SinkEventAttributeSet atts = new SinkEventAttributeSet();
1146 atts.addAttribute( "text-align", "right" );
1147 atts.addAttribute( "vertical-align", "top" );
1148 writeStartTag( BLOCK_TAG, atts );
1149 figureGraphics( projLogo, getGraphicsAttributes( projLogo ) );
1150 writeEndTag( BLOCK_TAG );
1151 }
1152
1153 writeSimpleTag( BLOCK_TAG );
1154 writeEndTag( TABLE_CELL_TAG );
1155 writeEndTag( TABLE_ROW_TAG );
1156 }
1157
1158 private void writeCoverBody( DocumentCover cover, DocumentMeta meta )
1159 {
1160 if ( cover == null && meta == null )
1161 {
1162 return;
1163 }
1164
1165 String subtitle = null;
1166 String title = null;
1167 String type = null;
1168 String version = null;
1169 if ( cover == null )
1170 {
1171
1172 getLog().debug( "The DocumentCover is not defined, using the DocumentMeta title as cover title." );
1173 title = meta.getTitle();
1174 }
1175 else
1176 {
1177 subtitle = cover.getCoverSubTitle();
1178 title = cover.getCoverTitle();
1179 type = cover.getCoverType();
1180 version = cover.getCoverVersion();
1181 }
1182
1183 writeln( "<fo:table-row keep-with-previous=\"always\" height=\"0.014in\">" );
1184 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1185 writeStartTag( BLOCK_TAG, "line-height", "0.014in" );
1186 writeEmptyTag( LEADER_TAG, "chapter.rule" );
1187 writeEndTag( BLOCK_TAG );
1188 writeEndTag( TABLE_CELL_TAG );
1189 writeEndTag( TABLE_ROW_TAG );
1190
1191 writeStartTag( TABLE_ROW_TAG, "height", "7.447in" );
1192 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1193 writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
1194 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
1195 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
1196 writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
1197
1198 writeStartTag( TABLE_BODY_TAG );
1199
1200 writeStartTag( TABLE_ROW_TAG );
1201 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "3" );
1202 writeSimpleTag( BLOCK_TAG );
1203 writeEmptyTag( BLOCK_TAG, "space-before", "3.2235in" );
1204 writeEndTag( TABLE_CELL_TAG );
1205 writeEndTag( TABLE_ROW_TAG );
1206
1207 writeStartTag( TABLE_ROW_TAG );
1208 writeStartTag( TABLE_CELL_TAG );
1209 writeEmptyTag( BLOCK_TAG, "space-after", "0.5in" );
1210 writeEndTag( TABLE_CELL_TAG );
1211
1212 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2", "cover.border.left" );
1213 writeStartTag( BLOCK_TAG, "cover.title" );
1214 text( title == null ? "" : title );
1215 writeEndTag( BLOCK_TAG );
1216 writeEndTag( TABLE_CELL_TAG );
1217 writeEndTag( TABLE_ROW_TAG );
1218
1219 writeStartTag( TABLE_ROW_TAG );
1220 writeStartTag( TABLE_CELL_TAG );
1221 writeSimpleTag( BLOCK_TAG );
1222 writeEndTag( TABLE_CELL_TAG );
1223
1224 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2", "cover.border.left.bottom" );
1225 writeStartTag( BLOCK_TAG, "cover.subtitle" );
1226 text( subtitle == null ? ( version == null ? "" : " v. " + version ) : subtitle );
1227 writeEndTag( BLOCK_TAG );
1228 writeStartTag( BLOCK_TAG, "cover.subtitle" );
1229 text( type == null ? "" : type );
1230 writeEndTag( BLOCK_TAG );
1231 writeEndTag( TABLE_CELL_TAG );
1232 writeEndTag( TABLE_ROW_TAG );
1233
1234 writeEndTag( TABLE_BODY_TAG );
1235 writeEndTag( TABLE_TAG );
1236
1237 writeEndTag( TABLE_CELL_TAG );
1238 writeEndTag( TABLE_ROW_TAG );
1239
1240 writeStartTag( TABLE_ROW_TAG, "height", "0.014in" );
1241 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1242 writeln( "<fo:block space-after=\"0.2in\" line-height=\"0.014in\">" );
1243 writeEmptyTag( LEADER_TAG, "chapter.rule" );
1244 writeEndTag( BLOCK_TAG );
1245 writeEndTag( TABLE_CELL_TAG );
1246 writeEndTag( TABLE_ROW_TAG );
1247
1248 writeStartTag( TABLE_ROW_TAG );
1249 writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
1250 writeSimpleTag( BLOCK_TAG );
1251 writeEmptyTag( BLOCK_TAG, "space-before", "0.2in" );
1252 writeEndTag( TABLE_CELL_TAG );
1253 writeEndTag( TABLE_ROW_TAG );
1254 }
1255
1256 private void writeCoverFooter( DocumentCover cover, DocumentMeta meta )
1257 {
1258 if ( cover == null && meta == null )
1259 {
1260 return;
1261 }
1262
1263 String date = null;
1264 String compName = null;
1265 if ( cover == null )
1266 {
1267
1268 getLog().debug( "The DocumentCover is not defined, using the DocumentMeta author as company name." );
1269 compName = meta.getAuthor();
1270 }
1271 else
1272 {
1273 compName = cover.getCompanyName();
1274
1275 if ( cover.getCoverdate() == null )
1276 {
1277 cover.setCoverDate( new Date() );
1278 date = cover.getCoverdate();
1279 cover.setCoverDate( null );
1280 }
1281 else
1282 {
1283 date = cover.getCoverdate();
1284 }
1285 }
1286
1287 writeStartTag( TABLE_ROW_TAG, "height", "0.3in" );
1288
1289 writeStartTag( TABLE_CELL_TAG );
1290 MutableAttributeSet att = getFoConfiguration().getAttributeSet( "cover.subtitle" );
1291 att.addAttribute( "height", "0.3in" );
1292 att.addAttribute( "text-align", "left" );
1293 writeStartTag( BLOCK_TAG, att );
1294 text( compName == null ? ( cover.getAuthor() == null ? "" : cover.getAuthor() ) : compName );
1295 writeEndTag( BLOCK_TAG );
1296 writeEndTag( TABLE_CELL_TAG );
1297
1298 writeStartTag( TABLE_CELL_TAG );
1299 att = getFoConfiguration().getAttributeSet( "cover.subtitle" );
1300 att.addAttribute( "height", "0.3in" );
1301 att.addAttribute( "text-align", "right" );
1302 writeStartTag( BLOCK_TAG, att );
1303 text( date == null ? "" : date );
1304 writeEndTag( BLOCK_TAG );
1305 writeEndTag( TABLE_CELL_TAG );
1306
1307 writeEndTag( TABLE_ROW_TAG );
1308 }
1309
1310 private ResourceBundle getBundle( Locale locale )
1311 {
1312 return ResourceBundle.getBundle( "doxia-fo", locale, this.getClass().getClassLoader() );
1313 }
1314
1315 private SinkEventAttributeSet getGraphicsAttributes( String logo )
1316 {
1317 MutableAttributeSet atts = null;
1318
1319 try
1320 {
1321 atts = DoxiaUtils.getImageAttributes( logo );
1322 }
1323 catch ( IOException e )
1324 {
1325 getLog().debug( e );
1326 }
1327
1328 if ( atts == null )
1329 {
1330 return new SinkEventAttributeSet( new String[]{ SinkEventAttributes.HEIGHT, COVER_HEADER_HEIGHT } );
1331 }
1332
1333
1334
1335 final int maxWidth = 225;
1336
1337 if ( Integer.parseInt( atts.getAttribute( SinkEventAttributes.WIDTH ).toString() ) > maxWidth )
1338 {
1339 atts.addAttribute( "content-width", "3.125in" );
1340 }
1341
1342 return new SinkEventAttributeSet( atts );
1343 }
1344 }