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.File;
23 import java.io.IOException;
24 import java.io.PrintWriter;
25 import java.io.StringWriter;
26 import java.io.Writer;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.LinkedList;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.Stack;
33 import java.util.TreeSet;
34
35 import javax.swing.text.MutableAttributeSet;
36 import javax.swing.text.html.HTML.Attribute;
37 import javax.swing.text.html.HTML.Tag;
38
39 import org.apache.maven.doxia.sink.AbstractXmlSink;
40 import org.apache.maven.doxia.sink.SinkEventAttributeSet;
41 import org.apache.maven.doxia.sink.SinkEventAttributes;
42 import org.apache.maven.doxia.sink.SinkUtils;
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 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class FoSink
64 extends AbstractXmlSink
65 implements FoMarkup
66 {
67
68 private final PrintWriter out;
69
70
71 private final Stack<NumberedListItem> listStack;
72
73
74 private final FoConfiguration config;
75
76
77 private int section = 0;
78
79
80 private int subsection = 0;
81
82
83 private int subsubsection = 0;
84
85
86 private boolean verbatim;
87
88
89 private boolean inFigure;
90
91 private final String encoding;
92
93 private final String languageId;
94
95
96 private final LinkedList<Boolean> tableGridStack;
97
98
99 private final LinkedList<int[]> cellJustifStack;
100
101
102 private final LinkedList<Boolean> isCellJustifStack;
103
104
105 private final LinkedList<Integer> cellCountStack;
106
107
108 private final LinkedList<StringWriter> tableContentWriterStack;
109
110 private final LinkedList<StringWriter> tableCaptionWriterStack;
111
112 private final LinkedList<PrettyPrintXMLWriter> tableCaptionXMLWriterStack;
113
114
115 private final LinkedList<String> tableCaptionStack;
116
117
118
119 protected Map<String, Set<String>> warnMessages;
120
121
122
123
124
125
126
127 protected FoSink( Writer writer )
128 {
129 this( writer, "UTF-8" );
130 }
131
132
133
134
135
136
137
138
139 protected FoSink( Writer writer, String encoding )
140 {
141 this( writer, encoding, null );
142 }
143
144
145
146
147
148
149
150
151
152
153
154 protected FoSink( Writer writer, String encoding, String languageId )
155 {
156 if ( writer == null )
157 {
158 throw new NullPointerException( "Null writer in FO Sink!" );
159 }
160
161 this.out = new PrintWriter( writer );
162 this.encoding = encoding;
163 this.languageId = languageId;
164 this.config = new FoConfiguration();
165
166 this.listStack = new Stack<NumberedListItem>();
167 this.tableGridStack = new LinkedList<Boolean>();
168 this.cellJustifStack = new LinkedList<int[]>();
169 this.isCellJustifStack = new LinkedList<Boolean>();
170 this.cellCountStack = new LinkedList<Integer>();
171 this.tableContentWriterStack = new LinkedList<StringWriter>();
172 this.tableCaptionWriterStack = new LinkedList<StringWriter>();
173 this.tableCaptionXMLWriterStack = new LinkedList<PrettyPrintXMLWriter>();
174 this.tableCaptionStack = new LinkedList<String>();
175
176 setNameSpace( "fo" );
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191 public void load( File configFile )
192 throws IOException
193 {
194 config.load( configFile );
195 }
196
197
198 public void head( SinkEventAttributes attributes )
199 {
200 init();
201
202 startPageSequence( "0", null, null );
203 }
204
205
206 public void head()
207 {
208 head( null );
209 }
210
211
212 public void head_()
213 {
214 writeEOL();
215 }
216
217
218 public void title( SinkEventAttributes attributes )
219 {
220 writeStartTag( BLOCK_TAG, "doc.header.title" );
221 }
222
223
224 public void title()
225 {
226 title( null );
227 }
228
229
230 public void title_()
231 {
232 writeEndTag( BLOCK_TAG );
233 writeEOL();
234 }
235
236
237 public void author( SinkEventAttributes attributes )
238 {
239 writeStartTag( BLOCK_TAG, "doc.header.author" );
240 }
241
242
243 public void author()
244 {
245 author( null );
246 }
247
248
249 public void author_()
250 {
251 writeEndTag( BLOCK_TAG );
252 writeEOL();
253 }
254
255
256 public void date( SinkEventAttributes attributes )
257 {
258 writeStartTag( BLOCK_TAG, "doc.header.date" );
259 }
260
261
262 public void date()
263 {
264 date( null );
265 }
266
267
268 public void date_()
269 {
270 writeEndTag( BLOCK_TAG );
271 writeEOL();
272 }
273
274
275 public void body( SinkEventAttributes attributes )
276 {
277
278 }
279
280
281 public void body()
282 {
283 body( null );
284 }
285
286
287 public void body_()
288 {
289 writeEOL();
290 writeEndTag( FLOW_TAG );
291 writeEOL();
292 writeEndTag( PAGE_SEQUENCE_TAG );
293 writeEOL();
294 endDocument();
295 }
296
297
298
299
300
301
302 public void sectionTitle()
303 {
304
305 }
306
307
308 public void sectionTitle_()
309 {
310
311 }
312
313
314 public void section( int level, SinkEventAttributes attributes )
315 {
316 if ( level == SECTION_LEVEL_1 )
317 {
318 section++;
319 subsection = 0;
320 subsubsection = 0;
321 }
322 else if ( level == SECTION_LEVEL_2 )
323 {
324 subsection++;
325 subsubsection = 0;
326 }
327 else if ( level == SECTION_LEVEL_3 )
328 {
329 subsubsection++;
330 }
331
332 onSection();
333 }
334
335
336 public void section_( int level )
337 {
338 onSection_();
339 }
340
341
342 public void sectionTitle( int level, SinkEventAttributes attributes )
343 {
344 onSectionTitle( level );
345 }
346
347
348 public void sectionTitle_( int level )
349 {
350 onSectionTitle_();
351 }
352
353
354 public void section1()
355 {
356 section( SECTION_LEVEL_1, null );
357 }
358
359
360 public void sectionTitle1()
361 {
362 sectionTitle( SECTION_LEVEL_1, null );
363 }
364
365
366 public void sectionTitle1_()
367 {
368 sectionTitle_( SECTION_LEVEL_1 );
369 }
370
371
372 public void section1_()
373 {
374 section_( SECTION_LEVEL_1 );
375 }
376
377
378 public void section2()
379 {
380 section( SECTION_LEVEL_2, null );
381 }
382
383
384 public void sectionTitle2()
385 {
386 sectionTitle( SECTION_LEVEL_2, null );
387 }
388
389
390 public void sectionTitle2_()
391 {
392 sectionTitle_( SECTION_LEVEL_2 );
393 }
394
395
396 public void section2_()
397 {
398 section_( SECTION_LEVEL_2 );
399 }
400
401
402 public void section3()
403 {
404 section( SECTION_LEVEL_3, null );
405 }
406
407
408 public void sectionTitle3()
409 {
410 sectionTitle( SECTION_LEVEL_3, null );
411 }
412
413
414 public void sectionTitle3_()
415 {
416 sectionTitle_( SECTION_LEVEL_3 );
417 }
418
419
420 public void section3_()
421 {
422 section_( SECTION_LEVEL_3 );
423 }
424
425
426 public void section4()
427 {
428 section( SECTION_LEVEL_4, null );
429 }
430
431
432 public void sectionTitle4()
433 {
434 sectionTitle( SECTION_LEVEL_4, null );
435 }
436
437
438 public void sectionTitle4_()
439 {
440 sectionTitle_( SECTION_LEVEL_4 );
441 }
442
443
444 public void section4_()
445 {
446 section_( SECTION_LEVEL_4 );
447 }
448
449
450 public void section5()
451 {
452 section( SECTION_LEVEL_5, null );
453 }
454
455
456 public void sectionTitle5()
457 {
458 sectionTitle( SECTION_LEVEL_5, null );
459 }
460
461
462 public void sectionTitle5_()
463 {
464 sectionTitle_( SECTION_LEVEL_5 );
465 }
466
467
468 public void section5_()
469 {
470 section_( SECTION_LEVEL_5 );
471 }
472
473
474 private void onSection()
475 {
476 writeEOL();
477 writeStartTag( BLOCK_TAG, "body.text" );
478 }
479
480
481
482
483
484
485 private void onSectionTitle( int depth )
486 {
487 StringBuffer title = new StringBuffer( 16 );
488
489 title.append( getChapterString() );
490
491 writeEOL();
492 if ( depth == SECTION_LEVEL_1 )
493 {
494 writeStartTag( BLOCK_TAG, "body.h1" );
495 title.append( section ).append( " " );
496 }
497 else if ( depth == SECTION_LEVEL_2 )
498 {
499 writeStartTag( BLOCK_TAG, "body.h2" );
500 title.append( section ).append( "." );
501 title.append( subsection ).append( " " );
502 }
503 else if ( depth == SECTION_LEVEL_3 )
504 {
505 writeStartTag( BLOCK_TAG, "body.h3" );
506 title.append( section ).append( "." );
507 title.append( subsection ).append( "." );
508 title.append( subsubsection ).append( " " );
509 }
510 else if ( depth == SECTION_LEVEL_4 )
511 {
512 writeStartTag( BLOCK_TAG, "body.h4" );
513 }
514 else
515 {
516 writeStartTag( BLOCK_TAG, "body.h5" );
517 }
518
519 write( title.toString() );
520 }
521
522
523 private void onSectionTitle_()
524 {
525 writeEndTag( BLOCK_TAG );
526 writeEOL();
527 }
528
529
530 private void onSection_()
531 {
532 writeEndTag( BLOCK_TAG );
533 writeEOL();
534 }
535
536
537
538
539
540 protected void resetSectionCounter()
541 {
542 this.section = 0;
543 }
544
545
546
547
548
549
550
551 protected String getChapterString()
552 {
553 return "";
554 }
555
556
557
558
559
560
561 public void list( SinkEventAttributes attributes )
562 {
563 writeEOL();
564 writeStartTag( LIST_BLOCK_TAG, "list" );
565 }
566
567
568 public void list()
569 {
570 list( null );
571 }
572
573
574 public void list_()
575 {
576 writeEndTag( LIST_BLOCK_TAG );
577 writeEOL();
578 }
579
580
581 public void listItem( SinkEventAttributes attributes )
582 {
583 writeStartTag( LIST_ITEM_TAG, "list.item" );
584 writeStartTag( LIST_ITEM_LABEL_TAG );
585 writeStartTag( BLOCK_TAG );
586 write( "•" );
587 writeEndTag( BLOCK_TAG );
588 writeEndTag( LIST_ITEM_LABEL_TAG );
589 writeEOL();
590 writeStartTag( LIST_ITEM_BODY_TAG, "list.item" );
591 writeEOL();
592 writeStartTag( BLOCK_TAG );
593 }
594
595
596 public void listItem()
597 {
598 listItem( null );
599 }
600
601
602 public void listItem_()
603 {
604 writeEndTag( BLOCK_TAG );
605 writeEOL();
606 writeEndTag( LIST_ITEM_BODY_TAG );
607 writeEOL();
608 writeEndTag( LIST_ITEM_TAG );
609 writeEOL();
610 }
611
612
613 public void numberedList( int numbering, SinkEventAttributes attributes )
614 {
615 this.listStack.push( new NumberedListItem( numbering ) );
616 writeEOL();
617 writeStartTag( LIST_BLOCK_TAG, "list" );
618 }
619
620
621 public void numberedList( int numbering )
622 {
623 numberedList( numbering, null );
624 }
625
626
627 public void numberedList_()
628 {
629 this.listStack.pop();
630 writeEndTag( LIST_BLOCK_TAG );
631 writeEOL();
632 }
633
634
635 public void numberedListItem( SinkEventAttributes attributes )
636 {
637 NumberedListItem current = this.listStack.peek();
638 current.next();
639
640 writeStartTag( LIST_ITEM_TAG, "list.item" );
641
642 writeEOL();
643 writeStartTag( LIST_ITEM_LABEL_TAG );
644 writeEOL();
645 writeStartTag( BLOCK_TAG );
646 write( current.getListItemSymbol() );
647 writeEndTag( BLOCK_TAG );
648 writeEOL();
649 writeEndTag( LIST_ITEM_LABEL_TAG );
650 writeEOL();
651
652 writeStartTag( LIST_ITEM_BODY_TAG, "list.item" );
653 writeEOL();
654 writeStartTag( BLOCK_TAG );
655 }
656
657
658 public void numberedListItem()
659 {
660 numberedListItem( null );
661 }
662
663
664 public void numberedListItem_()
665 {
666 writeEndTag( BLOCK_TAG );
667 writeEOL();
668 writeEndTag( LIST_ITEM_BODY_TAG );
669 writeEOL();
670 writeEndTag( LIST_ITEM_TAG );
671 writeEOL();
672 }
673
674
675 public void definitionList( SinkEventAttributes attributes )
676 {
677 writeEOL();
678 writeStartTag( BLOCK_TAG, "dl" );
679 }
680
681
682 public void definitionList()
683 {
684 definitionList( null );
685 }
686
687
688 public void definitionList_()
689 {
690 writeEndTag( BLOCK_TAG );
691 writeEOL();
692 }
693
694
695 public void definitionListItem( SinkEventAttributes attributes )
696 {
697
698 }
699
700
701 public void definitionListItem()
702 {
703 definitionListItem( null );
704 }
705
706
707 public void definitionListItem_()
708 {
709
710 }
711
712
713 public void definedTerm( SinkEventAttributes attributes )
714 {
715 writeStartTag( BLOCK_TAG, "dt" );
716 }
717
718
719 public void definedTerm()
720 {
721 definedTerm( null );
722 }
723
724
725 public void definedTerm_()
726 {
727 writeEndTag( BLOCK_TAG );
728 writeEOL();
729 }
730
731
732 public void definition( SinkEventAttributes attributes )
733 {
734 writeEOL();
735 writeStartTag( BLOCK_TAG, "dd" );
736 }
737
738
739 public void definition()
740 {
741 definition( null );
742 }
743
744
745 public void definition_()
746 {
747 writeEndTag( BLOCK_TAG );
748 writeEOL();
749 }
750
751
752 public void figure( SinkEventAttributes attributes )
753 {
754 this.inFigure = true;
755 writeEOL();
756 writeStartTag( BLOCK_TAG, "figure.display" );
757 }
758
759
760 public void figure()
761 {
762 figure( null );
763 }
764
765
766 public void figure_()
767 {
768 this.inFigure = false;
769 writeEndTag( BLOCK_TAG );
770 writeEOL();
771 }
772
773
774 public void figureGraphics( String name )
775 {
776 figureGraphics( name, null );
777 }
778
779
780 public void figureGraphics( String src, SinkEventAttributes attributes )
781 {
782 MutableAttributeSet atts = config.getAttributeSet( "figure.graphics" );
783 atts.addAttribute( Attribute.SRC.toString(), src );
784
785
786
787 final String[] valids = new String[] {"content-height", "content-width", "height", "width"};
788 final MutableAttributeSet filtered = SinkUtils.filterAttributes( attributes, valids );
789
790 if ( filtered != null )
791 {
792 atts.addAttributes( filtered );
793 }
794
795 writeln( "<fo:external-graphic" + SinkUtils.getAttributeString( atts ) + "/>" );
796 }
797
798
799
800
801
802
803 protected boolean isFigure()
804 {
805 return this.inFigure;
806 }
807
808
809 public void figureCaption( SinkEventAttributes attributes )
810 {
811 writeStartTag( BLOCK_TAG, "figure.caption" );
812 }
813
814
815 public void figureCaption()
816 {
817 figureCaption( null );
818 }
819
820
821 public void figureCaption_()
822 {
823 writeEndTag( BLOCK_TAG );
824 writeEOL();
825 }
826
827
828 public void paragraph()
829 {
830 paragraph( null );
831 }
832
833
834 public void paragraph( SinkEventAttributes attributes )
835 {
836 MutableAttributeSet atts = config.getAttributeSet( "normal.paragraph" );
837
838 if ( attributes != null && attributes.isDefined( SinkEventAttributes.ALIGN ) )
839 {
840 atts.addAttribute( "text-align", attributes.getAttribute( SinkEventAttributes.ALIGN ) );
841 }
842
843 writeEOL();
844 writeStartTag( BLOCK_TAG, atts );
845 }
846
847
848 public void paragraph_()
849 {
850 writeEndTag( BLOCK_TAG );
851 writeEOL();
852 }
853
854
855 public void verbatim( SinkEventAttributes attributes )
856 {
857 this.verbatim = true;
858
859 boolean boxed = false;
860
861 if ( attributes != null && attributes.isDefined( SinkEventAttributes.DECORATION ) )
862 {
863 boxed =
864 "boxed".equals( attributes.getAttribute( SinkEventAttributes.DECORATION ).toString() );
865 }
866
867 if ( boxed )
868 {
869 writeStartTag( BLOCK_TAG, "body.source" );
870 }
871 else
872 {
873 writeStartTag( BLOCK_TAG, "body.pre" );
874 }
875 }
876
877
878 public void verbatim( boolean boxed )
879 {
880 verbatim( boxed ? SinkEventAttributeSet.BOXED : null );
881 }
882
883
884 public void verbatim_()
885 {
886 this.verbatim = false;
887 writeEndTag( BLOCK_TAG );
888 writeEOL();
889 }
890
891
892 public void horizontalRule( SinkEventAttributes attributes )
893 {
894 writeEOL();
895 writeEOL();
896 writeStartTag( BLOCK_TAG );
897 writeEmptyTag( LEADER_TAG, "body.rule" );
898 writeEndTag( BLOCK_TAG );
899 writeEOL();
900 }
901
902
903 public void horizontalRule()
904 {
905 horizontalRule( null );
906 }
907
908
909 public void pageBreak()
910 {
911 writeEmptyTag( BLOCK_TAG, "break-before", "page" );
912 writeEOL();
913 }
914
915
916 public void table( SinkEventAttributes attributes )
917 {
918 writeEOL();
919 writeStartTag( BLOCK_TAG, "table.padding" );
920
921
922
923
924 this.tableContentWriterStack.addLast( new StringWriter() );
925 writeStartTag( TABLE_TAG, "table.layout" );
926 }
927
928
929 public void table()
930 {
931 table( null );
932 }
933
934
935 public void table_()
936 {
937 String content = this.tableContentWriterStack.removeLast().toString();
938
939 StringBuffer sb = new StringBuffer();
940 int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
941 for ( int i = 0; i < cellCount; i++ )
942 {
943 sb.append( "<fo:table-column column-width=\"proportional-column-width(1)\"/>" );
944 sb.append( EOL );
945 }
946
947 int index = content.indexOf( ">" ) + 1;
948 writeln( content.substring( 0, index ) );
949 write( sb.toString() );
950 write( content.substring( index ) );
951
952 writeEndTag( TABLE_TAG );
953 writeEOL();
954
955
956
957
958 writeEndTag( BLOCK_TAG );
959 writeEOL();
960
961 if ( !this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null )
962 {
963 paragraph( SinkEventAttributeSet.CENTER );
964 write( this.tableCaptionStack.removeLast().toString() );
965 paragraph_();
966 }
967 }
968
969
970 public void tableRows( int[] justification, boolean grid )
971 {
972 this.tableGridStack.addLast( Boolean.valueOf( grid ) );
973 this.cellJustifStack.addLast( justification );
974 this.isCellJustifStack.addLast( Boolean.valueOf( true ) );
975 this.cellCountStack.addLast( new Integer( 0 ) );
976 writeEOL();
977 writeStartTag( TABLE_BODY_TAG );
978 }
979
980
981 public void tableRows_()
982 {
983 this.tableGridStack.removeLast();
984 this.cellJustifStack.removeLast();
985 this.isCellJustifStack.removeLast();
986 writeEndTag( TABLE_BODY_TAG );
987 writeEOL();
988 }
989
990
991 public void tableRow( SinkEventAttributes attributes )
992 {
993
994 writeStartTag( TABLE_ROW_TAG, "table.body.row" );
995 this.cellCountStack.removeLast();
996 this.cellCountStack.addLast( new Integer( 0 ) );
997 }
998
999
1000 public void tableRow()
1001 {
1002 tableRow( null );
1003 }
1004
1005
1006 public void tableRow_()
1007 {
1008 writeEndTag( TABLE_ROW_TAG );
1009 writeEOL();
1010 }
1011
1012
1013 public void tableCell( SinkEventAttributes attributes )
1014 {
1015 tableCell( false, attributes );
1016 }
1017
1018
1019 public void tableCell()
1020 {
1021 tableCell( (SinkEventAttributes) null );
1022 }
1023
1024
1025 public void tableCell( String width )
1026 {
1027
1028 tableCell();
1029 }
1030
1031
1032 public void tableHeaderCell( SinkEventAttributes attributes )
1033 {
1034 tableCell( true, attributes );
1035 }
1036
1037
1038 public void tableHeaderCell()
1039 {
1040 tableHeaderCell( (SinkEventAttributes) null );
1041 }
1042
1043
1044 public void tableHeaderCell( String width )
1045 {
1046
1047 tableHeaderCell();
1048 }
1049
1050
1051
1052
1053
1054
1055
1056 private void tableCell( boolean headerRow, SinkEventAttributes attributes )
1057 {
1058 MutableAttributeSet cellAtts = headerRow
1059 ? config.getAttributeSet( "table.heading.cell" )
1060 : config.getAttributeSet( "table.body.cell" );
1061
1062
1063 int cellCount = Integer.parseInt( this.cellCountStack.getLast().toString() );
1064 cellAtts.addAttribute( "column-number", String.valueOf( cellCount + 1 ) );
1065
1066 if ( this.tableGridStack.getLast().equals( Boolean.TRUE ) )
1067 {
1068 cellAtts.addAttributes( config.getAttributeSet( "table.body.cell.grid" ) );
1069 }
1070
1071 MutableAttributeSet blockAtts = headerRow
1072 ? config.getAttributeSet( "table.heading.block" )
1073 : config.getAttributeSet( "table.body.block" );
1074
1075 String justif = null;
1076 if ( attributes == null )
1077 {
1078 attributes = new SinkEventAttributeSet( 0 );
1079 }
1080
1081 if ( attributes.isDefined( Attribute.ALIGN.toString() ) )
1082 {
1083 justif = attributes.getAttribute( Attribute.ALIGN.toString() ).toString();
1084 }
1085
1086 int[] cellJustif = this.cellJustifStack.getLast();
1087 if ( justif == null && cellJustif != null && cellJustif.length > 0
1088 && this.isCellJustifStack.getLast().equals( Boolean.TRUE ) )
1089 {
1090 switch ( cellJustif[Math.min( cellCount, cellJustif.length - 1 )] )
1091 {
1092 case JUSTIFY_LEFT:
1093 justif = "left";
1094 break;
1095 case JUSTIFY_RIGHT:
1096 justif = "right";
1097 break;
1098 case JUSTIFY_CENTER:
1099 default:
1100 justif = "center";
1101 }
1102 }
1103
1104 if ( justif != null )
1105 {
1106 blockAtts.addAttribute( "text-align", justif );
1107 }
1108
1109 writeStartTag( TABLE_CELL_TAG, cellAtts );
1110 writeEOL();
1111 writeStartTag( BLOCK_TAG, blockAtts );
1112 writeEOL();
1113 }
1114
1115
1116 public void tableCell_()
1117 {
1118 writeEndTag( BLOCK_TAG );
1119 writeEOL();
1120 writeEndTag( TABLE_CELL_TAG );
1121 writeEOL();
1122
1123 if ( this.isCellJustifStack.getLast().equals( Boolean.TRUE ) )
1124 {
1125 int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
1126 this.cellCountStack.addLast( new Integer( ++cellCount ) );
1127 }
1128 }
1129
1130
1131 public void tableHeaderCell_()
1132 {
1133 tableCell_();
1134 }
1135
1136
1137 public void tableCaption( SinkEventAttributes attributes )
1138 {
1139 StringWriter sw = new StringWriter();
1140 this.tableCaptionWriterStack.addLast( sw );
1141 this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) );
1142
1143
1144
1145
1146
1147
1148 }
1149
1150
1151 public void tableCaption()
1152 {
1153 tableCaption( null );
1154 }
1155
1156
1157 public void tableCaption_()
1158 {
1159 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
1160 {
1161 this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() );
1162 this.tableCaptionXMLWriterStack.removeLast();
1163 }
1164
1165
1166 }
1167
1168
1169 public void anchor( String name, SinkEventAttributes attributes )
1170 {
1171 if ( name == null )
1172 {
1173 throw new NullPointerException( "Anchor name cannot be null!" );
1174 }
1175
1176 String anchor = name;
1177
1178 if ( !DoxiaUtils.isValidId( anchor ) )
1179 {
1180 anchor = DoxiaUtils.encodeId( name, true );
1181
1182 String msg = "Modified invalid anchor name: '" + name + "' to '" + anchor + "'";
1183 logMessage( "modifiedLink", msg );
1184 }
1185
1186 anchor = "#" + name;
1187
1188 writeStartTag( INLINE_TAG, "id", anchor );
1189 }
1190
1191
1192 public void anchor( String name )
1193 {
1194 anchor( name, null );
1195 }
1196
1197
1198 public void anchor_()
1199 {
1200 writeEndTag( INLINE_TAG );
1201 }
1202
1203
1204 public void link( String name, SinkEventAttributes attributes )
1205 {
1206 if ( name == null )
1207 {
1208 throw new NullPointerException( "Link name cannot be null!" );
1209 }
1210
1211 if ( DoxiaUtils.isExternalLink( name ) )
1212 {
1213 writeStartTag( BASIC_LINK_TAG, "external-destination", HtmlTools.escapeHTML( name ) );
1214 writeStartTag( INLINE_TAG, "href.external" );
1215 }
1216 else if ( DoxiaUtils.isInternalLink( name ) )
1217 {
1218 String anchor = name.substring( 1 );
1219
1220 if ( !DoxiaUtils.isValidId( anchor ) )
1221 {
1222 anchor = DoxiaUtils.encodeId( anchor, true );
1223
1224 String msg = "Modified invalid anchor name: '" + name + "' to '" + anchor + "'";
1225 logMessage( "modifiedLink", msg );
1226 }
1227
1228 anchor = "#" + anchor;
1229
1230 writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
1231 writeStartTag( INLINE_TAG, "href.internal" );
1232 }
1233 else
1234 {
1235
1236 String anchor = name;
1237
1238 writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
1239 writeStartTag( INLINE_TAG, "href.internal" );
1240 }
1241 }
1242
1243
1244 public void link( String name )
1245 {
1246 link( name, null );
1247 }
1248
1249
1250 public void link_()
1251 {
1252 writeEndTag( INLINE_TAG );
1253 writeEndTag( BASIC_LINK_TAG );
1254 }
1255
1256
1257 public void italic()
1258 {
1259 writeStartTag( INLINE_TAG, "italic" );
1260 }
1261
1262
1263 public void italic_()
1264 {
1265 writeEndTag( INLINE_TAG );
1266 }
1267
1268
1269 public void bold()
1270 {
1271 writeStartTag( INLINE_TAG, "bold" );
1272 }
1273
1274
1275 public void bold_()
1276 {
1277 writeEndTag( INLINE_TAG );
1278 }
1279
1280
1281 public void monospaced()
1282 {
1283 writeStartTag( INLINE_TAG, "monospace" );
1284 }
1285
1286
1287 public void monospaced_()
1288 {
1289 writeEndTag( INLINE_TAG );
1290 }
1291
1292
1293 public void lineBreak( SinkEventAttributes attributes )
1294 {
1295 writeEOL();
1296 writeEOL();
1297 writeSimpleTag( BLOCK_TAG );
1298 }
1299
1300
1301 public void lineBreak()
1302 {
1303 lineBreak( null );
1304 }
1305
1306
1307 public void nonBreakingSpace()
1308 {
1309 write( " " );
1310 }
1311
1312
1313 public void text( String text, SinkEventAttributes attributes )
1314 {
1315 content( text );
1316 }
1317
1318
1319 public void text( String text )
1320 {
1321 text( text, null );
1322 }
1323
1324
1325 public void rawText( String text )
1326 {
1327 write( text );
1328 }
1329
1330
1331 public void flush()
1332 {
1333 out.flush();
1334 }
1335
1336
1337 public void close()
1338 {
1339 out.close();
1340
1341 if ( getLog().isWarnEnabled() && this.warnMessages != null )
1342 {
1343 for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() )
1344 {
1345 for ( String msg : entry.getValue() )
1346 {
1347 getLog().warn( msg );
1348 }
1349 }
1350
1351 this.warnMessages = null;
1352 }
1353
1354 init();
1355 }
1356
1357
1358
1359
1360
1361
1362
1363 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
1364 {
1365 String msg = "Unknown Sink event: '" + name + "', ignoring!";
1366 logMessage( "unknownEvent", msg );
1367 }
1368
1369
1370 public void comment( String comment )
1371 {
1372
1373 if ( StringUtils.isNotEmpty( comment ) && comment.indexOf( "--" ) != -1 )
1374 {
1375 String originalComment = comment;
1376
1377 while ( comment.indexOf( "--" ) != -1 )
1378 {
1379 comment = StringUtils.replace( comment, "--", "- -" );
1380 }
1381
1382 String msg = "Modified invalid comment: '" + originalComment + "' to '" + comment + "'";
1383 logMessage( "modifyComment", msg );
1384 }
1385
1386 StringBuffer buf = new StringBuffer( comment.length() + 9 );
1387
1388 buf.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS ).append( SPACE );
1389 buf.append( comment );
1390 buf.append( SPACE ).append( MINUS ).append( MINUS ).append( GREATER_THAN );
1391
1392 write( buf.toString() );
1393 }
1394
1395
1396
1397
1398 public void beginDocument()
1399 {
1400 write( "<?xml version=\"1.0\"" );
1401 if ( encoding != null )
1402 {
1403 write( " encoding=\"" + encoding + "\"" );
1404 }
1405 write( "?>" );
1406 writeEOL();
1407
1408 MutableAttributeSet atts = new SinkEventAttributeSet();
1409 atts.addAttribute( "xmlns:" + getNameSpace(), FO_NAMESPACE );
1410
1411 if ( languageId != null )
1412 {
1413 atts.addAttribute( "language", languageId );
1414 }
1415
1416 writeStartTag( ROOT_TAG, atts );
1417
1418 writeStartTag( LAYOUT_MASTER_SET_TAG );
1419
1420 writeStartTag( SIMPLE_PAGE_MASTER_TAG, "layout.master.set.cover-page" );
1421 writeEmptyTag( REGION_BODY_TAG, "layout.master.set.cover-page.region-body" );
1422 writeEndTag( SIMPLE_PAGE_MASTER_TAG );
1423 writeEOL();
1424
1425 writeStartTag( SIMPLE_PAGE_MASTER_TAG, "layout.master.set.toc" );
1426 writeEmptyTag( REGION_BODY_TAG, "layout.master.set.toc.region-body" );
1427 writeEmptyTag( REGION_BEFORE_TAG, "layout.master.set.toc.region-before" );
1428 writeEmptyTag( REGION_AFTER_TAG, "layout.master.set.toc.region-after" );
1429 writeEndTag( SIMPLE_PAGE_MASTER_TAG );
1430 writeEOL();
1431
1432 writeStartTag( SIMPLE_PAGE_MASTER_TAG, "layout.master.set.body" );
1433 writeEmptyTag( REGION_BODY_TAG, "layout.master.set.body.region-body" );
1434 writeEmptyTag( REGION_BEFORE_TAG, "layout.master.set.body.region-before" );
1435 writeEmptyTag( REGION_AFTER_TAG, "layout.master.set.body.region-after" );
1436 writeEndTag( SIMPLE_PAGE_MASTER_TAG );
1437 writeEOL();
1438
1439 writeEndTag( LAYOUT_MASTER_SET_TAG );
1440 writeEOL();
1441
1442 pdfBookmarks();
1443 }
1444
1445
1446
1447
1448 public void endDocument()
1449 {
1450 writeEndTag( ROOT_TAG );
1451 writeEOL();
1452
1453 flush();
1454 close();
1455 }
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466 protected FoConfiguration getFoConfiguration()
1467 {
1468 return config;
1469 }
1470
1471
1472
1473
1474
1475
1476
1477 protected void writeStartTag( Tag tag, String attributeId )
1478 {
1479 writeEOL();
1480 writeStartTag( tag, config.getAttributeSet( attributeId ) );
1481 }
1482
1483
1484
1485
1486
1487
1488
1489
1490 protected void writeStartTag( Tag tag, String id, String name )
1491 {
1492 writeEOL();
1493 MutableAttributeSet att = new SinkEventAttributeSet( new String[] {id, name} );
1494
1495 writeStartTag( tag, att );
1496 }
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506 protected void writeStartTag( Tag tag, String id, String name, String attributeId )
1507 {
1508 MutableAttributeSet att = config.getAttributeSet( attributeId );
1509
1510
1511 if ( att.isDefined( id ) )
1512 {
1513 att.removeAttribute( id );
1514 }
1515
1516 att.addAttribute( id, name );
1517
1518 writeEOL();
1519 writeStartTag( tag, att );
1520 }
1521
1522
1523
1524
1525
1526
1527
1528
1529 protected void writeEmptyTag( Tag tag, String id, String name )
1530 {
1531 MutableAttributeSet att = new SinkEventAttributeSet( new String[] {id, name} );
1532
1533 writeEOL();
1534 writeSimpleTag( tag, att );
1535 }
1536
1537
1538
1539
1540
1541
1542
1543 protected void writeEmptyTag( Tag tag, String attributeId )
1544 {
1545 writeEOL();
1546 writeSimpleTag( tag, config.getAttributeSet( attributeId ) );
1547 }
1548
1549
1550
1551
1552
1553
1554 protected void write( String text )
1555 {
1556 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
1557 {
1558 this.tableCaptionXMLWriterStack.getLast().writeText( unifyEOLs( text ) );
1559 }
1560 else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null )
1561 {
1562 this.tableContentWriterStack.getLast().write( unifyEOLs( text ) );
1563 }
1564 else
1565 {
1566 out.write( unifyEOLs( text ) );
1567 }
1568 }
1569
1570
1571
1572
1573
1574
1575 protected void writeln( String text )
1576 {
1577 write( text );
1578 writeEOL();
1579 }
1580
1581
1582
1583
1584
1585
1586 protected void content( String text )
1587 {
1588 write( escaped( text, verbatim ) );
1589 }
1590
1591
1592
1593
1594
1595
1596
1597
1598 public static String escaped( String text, boolean verb )
1599 {
1600 int length = text.length();
1601 StringBuffer buffer = new StringBuffer( length );
1602
1603 for ( int i = 0; i < length; ++i )
1604 {
1605 char c = text.charAt( i );
1606 switch ( c )
1607 {
1608 case ' ':
1609 if ( verb )
1610 {
1611 buffer.append( " " );
1612 }
1613 else
1614 {
1615 buffer.append( c );
1616 }
1617 break;
1618 case '<':
1619 buffer.append( "<" );
1620 break;
1621 case '>':
1622 buffer.append( ">" );
1623 break;
1624 case '&':
1625 buffer.append( "&" );
1626 break;
1627 case '\n':
1628 buffer.append( EOL );
1629 if ( verb )
1630 {
1631 buffer.append( "<fo:block/>" + EOL );
1632 }
1633 break;
1634 default:
1635 if ( needsSymbolFont( c ) )
1636 {
1637
1638 buffer.append( "<fo:inline font-family=\"Symbol\">" ).append( c ).append( "</fo:inline>" );
1639 }
1640 else
1641 {
1642 buffer.append( c );
1643 }
1644 }
1645 }
1646
1647 return buffer.toString();
1648 }
1649
1650
1651 protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
1652 {
1653 if ( this.tableCaptionXMLWriterStack.isEmpty() )
1654 {
1655 super.writeStartTag ( t, att, isSimpleTag );
1656 }
1657 else
1658 {
1659 String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString();
1660 this.tableCaptionXMLWriterStack.getLast().startElement( tag );
1661
1662 if ( att != null )
1663 {
1664 Enumeration<?> names = att.getAttributeNames();
1665 while ( names.hasMoreElements() )
1666 {
1667 Object key = names.nextElement();
1668 Object value = att.getAttribute( key );
1669
1670 this.tableCaptionXMLWriterStack.getLast().addAttribute( key.toString(), value.toString() );
1671 }
1672 }
1673
1674 if ( isSimpleTag )
1675 {
1676 this.tableCaptionXMLWriterStack.getLast().endElement();
1677 }
1678 }
1679 }
1680
1681
1682 protected void writeEndTag( Tag t )
1683 {
1684 if ( this.tableCaptionXMLWriterStack.isEmpty() )
1685 {
1686 super.writeEndTag( t );
1687 }
1688 else
1689 {
1690 this.tableCaptionXMLWriterStack.getLast().endElement();
1691 }
1692 }
1693
1694 private static final char UPPER_ALPHA = 0x391;
1695 private static final char PIV = 0x3d6;
1696 private static final char OLINE = 0x203e;
1697 private static final char DIAMS = 0x2666;
1698 private static final char EURO = 0x20ac;
1699 private static final char TRADE = 0x2122;
1700 private static final char PRIME = 0x2032;
1701 private static final char PPRIME = 0x2033;
1702
1703 private static boolean needsSymbolFont( char c )
1704 {
1705
1706
1707
1708
1709 return ( c >= UPPER_ALPHA && c <= PIV )
1710 || ( c == PRIME || c == PPRIME )
1711 || ( c >= OLINE && c <= DIAMS && c != EURO && c != TRADE );
1712 }
1713
1714
1715
1716
1717
1718
1719
1720
1721 protected void startPageSequence( String initPageNumber, String headerText, String footerText )
1722 {
1723 writeln( "<fo:page-sequence initial-page-number=\"" + initPageNumber + "\" master-reference=\"body\">" );
1724 regionBefore( headerText );
1725 regionAfter( footerText );
1726 writeln( "<fo:flow flow-name=\"xsl-region-body\">" );
1727 chapterHeading( null, true );
1728 }
1729
1730
1731
1732
1733
1734
1735 protected void regionBefore( String headerText )
1736 {
1737
1738 }
1739
1740
1741
1742
1743
1744
1745 protected void regionAfter( String footerText )
1746 {
1747
1748 }
1749
1750
1751
1752
1753
1754
1755
1756 protected void chapterHeading( String headerText, boolean chapterNumber )
1757 {
1758
1759 }
1760
1761
1762
1763
1764 protected void pdfBookmarks()
1765 {
1766
1767 }
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777 protected void logMessage( String key, String msg )
1778 {
1779 msg = "[FO Sink] " + msg;
1780 if ( getLog().isDebugEnabled() )
1781 {
1782 getLog().debug( msg );
1783
1784 return;
1785 }
1786
1787 if ( warnMessages == null )
1788 {
1789 warnMessages = new HashMap<String, Set<String>>();
1790 }
1791
1792 Set<String> set = warnMessages.get( key );
1793 if ( set == null )
1794 {
1795 set = new TreeSet<String>();
1796 }
1797 set.add( msg );
1798 warnMessages.put( key, set );
1799 }
1800
1801
1802 protected void init()
1803 {
1804 super.init();
1805
1806 this.listStack.clear();
1807 this.tableGridStack.clear();
1808 this.cellJustifStack.clear();
1809 this.isCellJustifStack.clear();
1810 this.cellCountStack.clear();
1811 this.tableContentWriterStack.clear();
1812 this.tableCaptionWriterStack.clear();
1813 this.tableCaptionXMLWriterStack.clear();
1814 this.tableCaptionStack.clear();
1815
1816 this.section = 0;
1817 this.subsection = 0;
1818 this.subsubsection = 0;
1819 this.verbatim = false;
1820 this.inFigure = false;
1821 this.warnMessages = null;
1822 }
1823 }