View Javadoc

1   /*
2    * Copyright 2004-2006 the Seasar Foundation and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.tuigwaa.cms.core.wiki.base;
17  
18  import java.awt.Color;
19  import java.io.ByteArrayOutputStream;
20  import java.io.IOException;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Properties;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.seasar.tuigwaa.cms.core.CmsConstants;
33  import org.seasar.tuigwaa.cms.core.CmsRequest;
34  import org.seasar.tuigwaa.cms.core.CmsResponse;
35  import org.seasar.tuigwaa.cms.core.Resource;
36  import org.seasar.tuigwaa.cms.core.pdf.PdfCmsPageEvents;
37  import org.seasar.tuigwaa.cms.core.pdf.PdfUtils;
38  import org.seasar.tuigwaa.cms.core.wiki.WikiContext;
39  import org.seasar.tuigwaa.cms.core.wiki.engine.Node;
40  import org.seasar.tuigwaa.cms.core.wiki.engine.SimpleNode;
41  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAlias;
42  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAlign;
43  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAnnotation;
44  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAnyOther;
45  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiArgs;
46  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiBlockPlugin;
47  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiCSVTable;
48  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiDefineList;
49  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiDefinedWord;
50  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiDeleteline;
51  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiErrors;
52  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiExcerpt;
53  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiExplanationWord;
54  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiFloatAlign;
55  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiGenerateTree;
56  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiHeading;
57  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiHorizontalline;
58  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiInlinePlugin;
59  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiInterwiki;
60  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiLetters;
61  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiLink;
62  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiList;
63  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiListMember;
64  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiPagename;
65  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiParagraph;
66  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiParserVisitor;
67  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiPreshaped;
68  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiSkipToNewline;
69  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiStrongItalic;
70  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiSyntaxError;
71  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiTable;
72  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiTablecolumn;
73  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiTablemember;
74  import org.seasar.tuigwaa.plugin.Plugin;
75  import org.seasar.tuigwaa.plugin.PluginException;
76  import org.seasar.tuigwaa.plugin.PluginRequest;
77  import org.seasar.tuigwaa.system.Constants;
78  import org.seasar.tuigwaa.system.TgwSecurityException;
79  
80  import com.isenshi.util.extlib.CJKSplitCharacter;
81  import com.lowagie.text.Anchor;
82  import com.lowagie.text.Annotation;
83  import com.lowagie.text.BadElementException;
84  import com.lowagie.text.Cell;
85  import com.lowagie.text.Chapter;
86  import com.lowagie.text.Chunk;
87  import com.lowagie.text.DocWriter;
88  import com.lowagie.text.Document;
89  import com.lowagie.text.DocumentException;
90  import com.lowagie.text.Element;
91  import com.lowagie.text.ElementTags;
92  import com.lowagie.text.Font;
93  import com.lowagie.text.FontFactory;
94  import com.lowagie.text.Image;
95  import com.lowagie.text.ListItem;
96  import com.lowagie.text.Paragraph;
97  import com.lowagie.text.Phrase;
98  import com.lowagie.text.Rectangle;
99  import com.lowagie.text.Section;
100 import com.lowagie.text.SplitCharacter;
101 import com.lowagie.text.Table;
102 import com.lowagie.text.markup.MarkupParser;
103 import com.lowagie.text.markup.MarkupTags;
104 import com.lowagie.text.pdf.PdfPTable;
105 import com.lowagie.text.pdf.PdfPageEvent;
106 import com.lowagie.text.pdf.PdfWriter;
107 import com.lowagie.text.rtf.RtfWriter2;
108 
109 /***
110  * Using parsed wiki node tree, generates PDF or RTF documents. This class
111  * totally depends on iText ver. 1.3.
112  * 
113  * KNOWN ISSUES : (2005/11) 1. Cannot specify Japanese character in annotation
114  * contents. Even if setting Japanese font, the result will not contain Japanese
115  * Sentence.
116  * 
117  * @author someda
118  */
119 public class PDFWikiVisitor implements WikiParserVisitor {
120 
121 	private static final float LIST_INDENT = 10;
122 
123 	private static final float PAGEMARGIN_TOP = 50;
124 
125 	private static final float PAGEMARGIN_RIGHT = 50;
126 
127 	private static final float PAGEMARGIN_BOTTOM = 50;
128 
129 	private static final float PAGEMARGIN_LEFT = 50;
130 
131 	private static final float TABLE_PADDING = 3f;
132 
133 	private static final float TABLE_BORDERWIDTH = 0.5f;
134 
135 	private static final float TABLE_WIDTH = 100f;
136 
137 	// Number of depth is now 3 because, current wiki rule allows
138 	// 3-level headings.
139 	private static final int SECTION_NUMBEROFDEPTH = 3;
140 
141 	private static final float SECTION_INDENT = 10;
142 
143 	private static final String CREATER_APPLICATION = "WebUDA Tuigwaa";
144 
145 	private Document doc_;
146 
147 	private DocWriter writer_;
148 
149 	private ByteArrayOutputStream baos_ = new ByteArrayOutputStream();
150 
151 	private List chapterList = null;
152 
153 	private Section currentSection_ = null;
154 
155 	private Section lastSection_ = null;
156 
157 	private Map pushedMap_ = null;
158 
159 	private CmsRequest request_;
160 
161 	private CmsResponse response_;
162 
163 	private WikiConfiguration config_;
164 
165 	private WikiContext ctx;
166 
167 	private Font defaultFont_;
168 
169 	private float paragraphSpacing_;
170 
171 	private Log log_ = LogFactory.getLog(getClass());
172 
173 	private SplitCharacter split = new CJKSplitCharacter();
174 
175 	public PDFWikiVisitor(CmsRequest request, CmsResponse response,
176 			WikiConfiguration config) {
177 		this.request_ = request;
178 		this.response_ = response;
179 		this.config_ = config;
180 		this.ctx = config.getWikiContext();
181 
182 		try {
183 			init();
184 		} catch (DocumentException de) {
185 			log_
186 					.error("failed to initialize the document : "
187 							+ de.getMessage());
188 			throw new RuntimeException(de);
189 		}
190 	}
191 
192 	private void init() throws DocumentException {
193 
194 		Resource resource = request_.getPage().getResource();
195 		String style = resource.getProperty(CmsConstants.PROPERTY_PDFSTYLE);
196 		if (style == null)
197 			style = PdfUtils.DEFAULT_PDF_PAGESTYLE;
198 
199 		Rectangle rec = PdfUtils.getRectangle(style);
200 		doc_ = new Document(rec, PAGEMARGIN_LEFT, PAGEMARGIN_RIGHT,
201 				PAGEMARGIN_TOP, PAGEMARGIN_BOTTOM);
202 
203 		defaultFont_ = PdfUtils.FONT_JA_MINCHO;
204 		paragraphSpacing_ = defaultFont_.size();
205 
206 		if (request_.isPDFRequest()) {
207 			writer_ = PdfWriter.getInstance(doc_, baos_);
208 			String pagePath = resource.getPath();
209 			PdfPageEvent event = new PdfCmsPageEvents(pagePath, defaultFont_,
210 					style);
211 			((PdfWriter) writer_).setPageEvent(event);
212 		} else if (request_.isRTFRequest()) {
213 			writer_ = RtfWriter2.getInstance(doc_, baos_);
214 		} else {
215 			throw new IllegalStateException(
216 					"request type should be PDF or RTF.");
217 		}
218 
219 		doc_.addAuthor(resource.getModificationUser());
220 		doc_.addCreationDate();
221 		doc_.addSubject(request_.getPage().getResource().getPath());
222 		doc_.addCreator(CREATER_APPLICATION);
223 
224 		doc_.open();
225 	}
226 
227 	public Object visit(SimpleNode node, Object data) {
228 		return null;
229 	}
230 
231 	public Object visit(WikiSyntaxError node, Object data) {
232 		return getError(node.letter);
233 	}
234 
235 	public Object visit(WikiSkipToNewline node, Object data) {
236 		Chunk c = new Chunk(node.letter, defaultFont_);
237 		c.setSplitCharacter(split);
238 		return c;
239 	}
240 
241 	/***
242 	 * Main method, all other method in this class should not be called.
243 	 */
244 	public Object visit(WikiGenerateTree node, Object data) {
245 
246 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
247 			Object o = node.jjtGetChild(i).jjtAccept(this, data);
248 			addDocument(o, node.jjtGetChild(i));
249 		}
250 
251 		/***
252 		 * NOTE: iText Document#add method writes the contents on-the-fly to
253 		 * OutputStream, thus, using chapterList to avoid null contents
254 		 * appeared.
255 		 */
256 		if (chapterList != null) {
257 			int n = 0;
258 			for (Iterator i = chapterList.iterator(); i.hasNext();) {
259 				Element e = (Element) i.next();
260 				List list = null;
261 				if (pushedMap_ != null)
262 					list = (List) pushedMap_.get(new Integer(n));
263 
264 				try {
265 					doc_.add(e);
266 					if (list != null) {
267 						for (Iterator j = list.iterator(); j.hasNext();) {
268 							Element elm = (Element) j.next();
269 							doc_.add(elm);
270 						}
271 					}
272 				} catch (DocumentException de) {
273 					de.printStackTrace();
274 				}
275 				n++;
276 			}
277 		}
278 
279 		doc_.close();
280 		byte[] b = null;
281 		try {
282 			b = baos_.toByteArray();
283 			baos_.close();
284 		} catch (IOException ioe) {
285 			ioe.printStackTrace();
286 		}
287 		return b;
288 	}
289 
290 	private void addDocument(Object obj, Node node) {
291 
292 		try {
293 			if (currentSection_ != null && !(node instanceof WikiHeading)) {
294 				if (obj != null) {
295 					try {
296 						currentSection_.add(obj);
297 					} catch (ClassCastException cce) {
298 						log_.error("couldn't add " + obj
299 								+ " to current section caused by "
300 								+ cce.getMessage());
301 					}
302 				}
303 			} else {
304 				if (obj != null) {
305 					try {
306 						doc_.add((Element) obj);
307 					} catch (ClassCastException cce) {
308 						log_.error("couldn't add " + obj
309 								+ " to document caused by " + cce.getMessage());
310 					}
311 				}
312 			}
313 		} catch (DocumentException de) {
314 			de.printStackTrace();
315 		}
316 	}
317 
318 	private void pushObject(Element e) {
319 		if (pushedMap_ == null)
320 			pushedMap_ = new HashMap();
321 
322 		if (currentSection_ == null) {
323 			try {
324 				doc_.add(e);
325 			} catch (DocumentException de) {
326 				log_.error("failed to add element " + de.getMessage());
327 			}
328 		} else {
329 			List list;
330 			Integer n = new Integer(chapterList.size() - 1);
331 			if ((list = (List) pushedMap_.get(n)) == null) {
332 				list = new ArrayList();
333 				pushedMap_.put(n, list);
334 			}
335 			list.add(e);
336 		}
337 	}
338 
339 	public Object visit(WikiParagraph node, Object data) {
340 		Paragraph p = new Paragraph();
341 		p.setLeading(20f);
342 		p.setFirstLineIndent(defaultFont_.size());
343 		p.setSpacingBefore(paragraphSpacing_);
344 		p.setSpacingAfter(paragraphSpacing_);
345 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
346 			Object o = node.jjtGetChild(i).jjtAccept(this, data);
347 			if (o != null)
348 				p.add(o);
349 		}
350 		return p;
351 	}
352 
353 	public Object visit(WikiExcerpt node, Object data) {
354 
355 		Properties props = PdfUtils.getDefaultfontProperties(defaultFont_);
356 		props.setProperty(ElementTags.STYLE, MarkupTags.CSS_ITALIC);
357 
358 		Paragraph p = new Paragraph(props);
359 		p.setIndentationLeft(20 * node.level);
360 		p.setSpacingAfter(paragraphSpacing_);
361 		p.setSpacingBefore(paragraphSpacing_);
362 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
363 			// if child is paragraph, not generate <p> tag
364 			if (node.jjtGetChild(i) instanceof WikiParagraph) {
365 				WikiParagraph c = (WikiParagraph) node.jjtGetChild(i);
366 				for (int j = 0; j < c.jjtGetNumChildren(); j++) {
367 					Object o = c.jjtGetChild(j).jjtAccept(this, data);
368 					if (o != null)
369 						p.add(o);
370 				}
371 
372 			} else {
373 				Object o = node.jjtGetChild(i).jjtAccept(this, data);
374 				if (o != null)
375 					p.add(o);
376 			}
377 		}
378 		return p;
379 	}
380 
381 	public Object visit(WikiList node, Object data) {
382 
383 		int curtype = 0;
384 		int curlevel = 0;
385 		int pretype = 0;
386 		int prelevel = 0;
387 		com.lowagie.text.List[] state = new com.lowagie.text.List[3];
388 
389 		Element e = new Paragraph();
390 		com.lowagie.text.List curlist = null;
391 		com.lowagie.text.List parentlist = null;
392 
393 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
394 
395 			if (node.jjtGetChild(i) instanceof WikiListMember) {
396 				WikiListMember child = (WikiListMember) node.jjtGetChild(i);
397 
398 				curtype = child.type;
399 				curlevel = child.level;
400 
401 				// upward level change
402 				if (curlevel > prelevel) {
403 					parentlist = curlist;
404 
405 					curlist = startList(curtype);
406 					state[curlevel - 1] = curlist;
407 
408 					if (parentlist != null) {
409 						parentlist.add(curlist);
410 					} else {
411 						parentlist = curlist;
412 						((Paragraph) e).add(curlist);
413 					}
414 				}
415 
416 				// downward level change
417 				if (curlevel < prelevel) {
418 
419 					if (state[curlevel - 1] != null
420 							&& state[curlevel - 1].size() > 0
421 							&& isSametype(state[curlevel - 1], curtype)) {
422 						curlist = state[curlevel - 1];
423 					} else {
424 						curlist = startList(curtype);
425 						for (int j = curlevel - 2; j >= 0; j--) {
426 							if (state[j] != null && state[j].size() > 0) {
427 								parentlist = state[j];
428 								parentlist.add(curlist);
429 								break;
430 							}
431 						}
432 						if (curlevel == 1) {
433 							((Paragraph) e).add(curlist);
434 						}
435 					}
436 				}
437 
438 				// not level change but type change
439 				if (curlevel == prelevel && curtype != pretype) {
440 					curlist = startList(curtype);
441 
442 					if (curlevel == 1) {
443 						((Paragraph) e).add(curlist);
444 					} else {
445 						parentlist.add(curlist);
446 					}
447 				}
448 
449 				// change state
450 				if (curlevel != prelevel || curtype != pretype) {
451 					pretype = curtype;
452 					prelevel = curlevel;
453 				}
454 				curlist.add(child.jjtAccept(this, data));
455 			}
456 		}
457 		return e;
458 	}
459 
460 	private com.lowagie.text.List startList(int type) {
461 		com.lowagie.text.List e = null;
462 		if (type == WikiHelper.LIST_TYPE_NORMAL) {
463 			e = new com.lowagie.text.List(false, LIST_INDENT);
464 		} else if (type == WikiHelper.LIST_TYPE_NUMERICAL) {
465 			e = new com.lowagie.text.List(true, LIST_INDENT);
466 		}
467 		return e;
468 	}
469 
470 	private boolean isSametype(com.lowagie.text.List list, int type) {
471 		return (!list.isNumbered() && type == WikiHelper.LIST_TYPE_NORMAL)
472 				|| (list.isNumbered() && type == WikiHelper.LIST_TYPE_NUMERICAL);
473 	}
474 
475 	public Object visit(WikiListMember node, Object data) {
476 		ListItem e = new ListItem();
477 		e.setIndentationLeft(LIST_INDENT);
478 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
479 			e.add(node.jjtGetChild(i).jjtAccept(this, data));
480 		}
481 		return e;
482 	}
483 
484 	public Object visit(WikiDefineList node, Object data) {
485 		Paragraph p = new Paragraph();
486 		p.setSpacingBefore(5f);
487 		p.setSpacingAfter(5f);
488 		p.setIndentationLeft(5f);
489 		p.add(Chunk.NEWLINE);
490 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
491 			Object o = node.jjtGetChild(i).jjtAccept(this, data);
492 			if (o != null)
493 				p.add(o);
494 		}
495 		return p;
496 	}
497 
498 	public Object visit(WikiDefinedWord node, Object data) {
499 
500 		Phrase p = null;
501 		if (node.jjtGetNumChildren() > 0) {
502 			Properties props = PdfUtils.getDefaultfontProperties(defaultFont_);
503 			props.setProperty(ElementTags.STYLE, MarkupTags.CSS_BOLD);
504 			p = new Phrase(props);
505 			for (int i = 0; i < node.jjtGetNumChildren(); i++) {
506 				Object o = node.jjtGetChild(i).jjtAccept(this, data);
507 				if (o != null)
508 					p.add(o);
509 			}
510 			p.add(Chunk.NEWLINE);
511 		}
512 		return p;
513 	}
514 
515 	public Object visit(WikiExplanationWord node, Object data) {
516 		Paragraph p = new Paragraph();
517 		p.setSpacingAfter(5f);
518 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
519 
520 			Node child = node.jjtGetChild(i);
521 			if (child instanceof WikiParagraph) {
522 				for (int j = 0; j < child.jjtGetNumChildren(); j++) {
523 					Object o = child.jjtGetChild(j).jjtAccept(this, data);
524 					if (o != null)
525 						p.add(o);
526 				}
527 			} else {
528 				Object o = node.jjtGetChild(i).jjtAccept(this, data);
529 				if (o != null)
530 					p.add(o);
531 			}
532 		}
533 		p.add(Chunk.NEWLINE);
534 		return p;
535 	}
536 
537 	public Object visit(WikiPreshaped node, Object data) {
538 
539 		Table p = null;
540 		try {
541 			p = new Table(1);
542 			p.setPadding(TABLE_PADDING);
543 			p.setBorderWidth(TABLE_BORDERWIDTH);
544 			p.setWidth(TABLE_WIDTH);
545 			p.disableBorderSide(Rectangle.LEFT);
546 			p.disableBorderSide(Rectangle.RIGHT);
547 			p.endHeaders();
548 			Cell c = new Cell();
549 			c.disableBorderSide(Rectangle.LEFT);
550 			c.disableBorderSide(Rectangle.RIGHT);
551 
552 			Paragraph ph = new Paragraph(PdfUtils
553 					.getDefaultfontProperties(defaultFont_));
554 
555 			float indent = 0f;
556 			if (currentSection_ != null) { // important
557 				indent = (currentSection_.depth() - 1) * SECTION_INDENT;
558 			}
559 
560 			if (node.jjtGetParent() instanceof Paragraph) {
561 				indent += ((Paragraph) node.jjtGetParent()).indentationLeft();
562 			}
563 
564 			if (indent > 0f)
565 				ph.setIndentationLeft(indent);
566 
567 			for (int i = 0; i < node.jjtGetNumChildren(); i++) {
568 				ph.add(node.jjtGetChild(i).jjtAccept(this, data));
569 				ph.add(Chunk.NEWLINE);
570 			}
571 			c.add(ph);
572 			p.addCell(c);
573 		} catch (BadElementException bee) {
574 			bee.printStackTrace();
575 		}
576 		return p;
577 	}
578 
579 	public Object visit(WikiTable node, Object data) {
580 		return processTable(node, data);
581 	}
582 
583 	private Object processTable(SimpleNode node, Object data) {
584 
585 		VisitorUtils.prepareWikiTable(node, data);
586 		int prenum = 0;
587 		Table t = null;
588 
589 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
590 
591 			int colnum = node.jjtGetChild(i).jjtGetNumChildren();
592 
593 			if (node.jjtGetChild(i) instanceof WikiTablemember) {
594 
595 				if (colnum != prenum) {
596 
597 					if (t != null) {
598 						addDocument(t, node);
599 					}
600 
601 					try {
602 						t = new Table(colnum);
603 						t.setPadding(TABLE_PADDING);
604 						t.setBorderWidth(TABLE_BORDERWIDTH);
605 						t.setWidth(TABLE_WIDTH);
606 						t.setCellsFitPage(true);
607 						t.setOffset(10f);
608 						// addDocument(t,node);
609 						// pushObject(t);
610 					} catch (BadElementException bee) {
611 						// in case colnum is not 1
612 						bee.printStackTrace();
613 					}
614 				}
615 			} else { // in case WikiError
616 				prenum = 0;
617 				continue;
618 			}
619 			prenum = colnum;
620 			node.jjtGetChild(i).jjtAccept(this, t);
621 		}
622 		return t;
623 	}
624 
625 	public Object visit(WikiTablecolumn node, Object data) {
626 
627 		Cell cell = null;
628 		if (!node.iscolspan && !node.isrowspan) {
629 			cell = new Cell();
630 			Properties props = (data instanceof Properties) ? (Properties) data
631 					: PdfUtils.getDefaultfontProperties(defaultFont_);
632 			props.setProperty(ElementTags.SIZE, "10.0");
633 
634 			if (node.align != null)
635 				cell.setHorizontalAlignment(getAlign(node.align));
636 
637 			String bgcolor = null;
638 			if ((bgcolor = (String) props.remove(ElementTags.BACKGROUNDCOLOR)) != null) {
639 				cell.setBackgroundColor(MarkupParser.decodeColor(bgcolor));
640 			}
641 
642 			if (node.bgcolor != null)
643 				cell.setBackgroundColor(VisitorUtils.getColorByString(
644 						node.bgcolor, false));
645 
646 			if (node.color != null) {
647 				Color c = VisitorUtils.getColorByString(node.color, true);
648 				if (c != null) {
649 					props.setProperty(ElementTags.RED, String.valueOf(c
650 							.getRed()));
651 					props.setProperty(ElementTags.GREEN, String.valueOf(c
652 							.getGreen()));
653 					props.setProperty(ElementTags.BLUE, String.valueOf(c
654 							.getBlue()));
655 				}
656 			}
657 
658 			if (node.size != null)
659 				props.setProperty(ElementTags.SIZE, node.size);
660 
661 			if (node.colspannum > 0)
662 				cell.setColspan(++node.colspannum);
663 
664 			if (node.rowspannum > 0) {
665 				cell.setRowspan(++node.rowspannum);
666 			}
667 
668 			try {
669 				Phrase p = getChildPhrase(node, props, 0);
670 				Paragraph ph = new Paragraph(p);
671 
672 				float indent = 0f;
673 				if (currentSection_ != null) { // important
674 					indent = (currentSection_.depth() - 1) * SECTION_INDENT;
675 				}
676 
677 				if (indent > 0)
678 					ph.setIndentationLeft(indent);
679 
680 				cell.addElement(ph);
681 			} catch (BadElementException bee) {
682 				bee.printStackTrace();
683 			}
684 		}
685 		return cell;
686 	}
687 
688 	public Object visit(WikiTablemember node, Object data) {
689 
690 		if (data instanceof Table) {
691 			Table t = (Table) data;
692 			for (int i = 0; i < node.jjtGetNumChildren(); i++) {
693 				Properties p = PdfUtils.getDefaultfontProperties(defaultFont_);
694 				if (node.type == WikiHelper.TABLE_TYPE_HEADER) {
695 					p.setProperty(ElementTags.STYLE, MarkupTags.CSS_BOLD);
696 					p.setProperty(ElementTags.BACKGROUNDCOLOR, "#C0C0C0");
697 				}
698 
699 				Object o = node.jjtGetChild(i).jjtAccept(this, p);
700 				if (o != null) {
701 					t.addCell((Cell) o);
702 				}
703 			}
704 		}
705 		return null;
706 	}
707 
708 	public Object visit(WikiCSVTable node, Object data) {
709 		return processTable(node, data);
710 	}
711 
712 	public Object visit(WikiHeading node, Object data) {
713 
714 		Element e = null;
715 		Paragraph title;
716 
717 		if (node.level == 1 || currentSection_ == null) {
718 			title = new Paragraph(PdfUtils
719 					.getChapterfontProperties(defaultFont_));
720 			title.setLeading(20f);
721 			title.setSpacingAfter(40f);
722 			title.add(getChildPhrase(node, data, 0));
723 			if (chapterList == null)
724 				chapterList = new ArrayList();
725 
726 			// should add Chapter object to Document object at creating
727 			// new Chapter object to avoid memory starvation for large document
728 			// ??
729 			e = new Chapter(title, chapterList.size() + 1);
730 			currentSection_ = (Section) e;
731 			chapterList.add(e);
732 		} else {
733 			title = new Paragraph(PdfUtils.getSectionfontProperties(
734 					defaultFont_, node.level));
735 			title.setLeading(20f);
736 			title.add(getChildPhrase(node, data, 0));
737 			Section sec = null;
738 			if (node.level == 2) {
739 				sec = ((Section) chapterList.get(chapterList.size() - 1))
740 						.addSection(SECTION_INDENT, title,
741 								SECTION_NUMBEROFDEPTH);
742 			} else { // in case level-3
743 				int currentDepth = currentSection_.depth();
744 				if (currentDepth == SECTION_NUMBEROFDEPTH) {
745 					sec = lastSection_.addSection(SECTION_INDENT, title,
746 							SECTION_NUMBEROFDEPTH);
747 				} else {
748 					lastSection_ = currentSection_;
749 					sec = currentSection_.addSection(SECTION_INDENT, title,
750 							SECTION_NUMBEROFDEPTH);
751 				}
752 			}
753 			if (sec != null)
754 				currentSection_ = sec;
755 		}
756 		return null;
757 	}
758 
759 	public Object visit(WikiAlign node, Object data) {
760 		Paragraph p = new Paragraph();
761 		String align = node.image.substring(0, node.image.length() - 1);
762 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
763 			Object o = node.jjtGetChild(i).jjtAccept(this, data);
764 			if (o != null)
765 				p.add(o);
766 		}
767 		p.setAlignment(getAlign(align));
768 		return p;
769 	}
770 
771 	public Object visit(WikiFloatAlign node, Object data) {
772 		Paragraph p = new Paragraph();
773 		String align = node.image.substring(1, node.image.length() - 1);
774 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
775 			Object o = node.jjtGetChild(i).jjtAccept(this, data);
776 			if (o != null)
777 				p.add(o);
778 		}
779 		p.setAlignment(getAlign(align));
780 		return p;
781 	}
782 
783 	public Object visit(WikiHorizontalline node, Object data) {
784 
785 		// PDF dependent to use generic tag, though it is rather smart than
786 		// using Chunk#setHorizontalScaling with "-" character.
787 		Chunk c = new Chunk("\u00a0");
788 		c.setGenericTag(PdfCmsPageEvents.GENERICTAG_HORIZONTAL);
789 		Paragraph p = new Paragraph(c);
790 		return p;
791 	}
792 
793 	public Object visit(WikiBlockPlugin node, Object data) {
794 
795 		String name = node.name;
796 		boolean ispluginused = false;
797 		Object obj = null;
798 
799 		PluginRequest prequest = VisitorUtils.createPluginRequest(node);		
800 		String childStr = getChildAsString(node,data,0);		
801 		if(childStr != null && !Constants.LINEBREAK_CODE.equals(childStr)){
802 			prequest.setChild(WikiHelper.deleteParenthesis(childStr,"{","}"));
803 		}
804 
805 		if (config_.isPluginLoaded(name)) {
806 			Plugin plugin = config_.getPlugin(name);
807 			try {
808 				obj = plugin.service(request_, response_, prequest);
809 				ispluginused = true;
810 			} catch (PluginException pe) {
811 				log_.error(pe.getMessage());
812 			} catch (Exception e) {
813 				e.printStackTrace();
814 				// some exception handling framework needed
815 			}
816 		}
817 
818 		if (obj instanceof List && !(obj instanceof Phrase)) { // in case
819 			// VisitorUtils#parseHTMLtoPdf
820 			// being used
821 			List list = (List) obj;
822 			for (Iterator i = list.iterator(); i.hasNext();) {
823 				Element e = (Element) i.next();
824 
825 				if (e instanceof PdfPTable) {
826 					pushObject(e);
827 				} else {
828 					addDocument(e, node);
829 				}
830 			}
831 			return null; // important !!
832 		}
833 
834 		if (!ispluginused)
835 			obj = new Chunk("#" + prequest.toString(), defaultFont_);
836 		return obj;
837 	}
838 
839 	public Object visit(WikiLetters node, Object data) {
840 
841 		Element e = null;
842 
843 		if (node.isURL && !WikiHelper.isImage(node.letter)) {
844 			e = new Anchor(node.letter);
845 			((Anchor) e).setReference(node.letter);
846 		}
847 
848 		if (node.isAnchor) {
849 			String id = (node.letter.startsWith("#")) ? node.letter
850 					.substring(1) : node.letter;
851 			e = new Chunk("\u00a0");// normal space will be ignored and
852 			((Chunk) e).setLocalDestination(id);
853 		}
854 
855 		if (node.isWikiname) {
856 			try {
857 				if (ctx.isPageExist(node.letter, request_)) {
858 					URL url = ctx.getURLByName(node.letter, request_);
859 					e = new Anchor(node.letter);
860 					((Anchor) e).setReference(url.toString());
861 				}
862 			} catch (TgwSecurityException tse) {
863 				e = new Chunk(node.letter, defaultFont_);
864 			}
865 
866 		}
867 
868 		if (node.isNewline) {
869 			e = Chunk.NEWLINE;
870 		}
871 
872 		if (node.isURL && WikiHelper.isImage(node.letter)) {
873 			try {
874 				Image img = Image.getInstance(new URL(node.letter));
875 				img.setAlignment(Image.MIDDLE | Image.TEXTWRAP);
876 				return img;
877 			} catch (BadElementException bee) {
878 				bee.printStackTrace();
879 			} catch (MalformedURLException mue) {
880 				mue.printStackTrace();
881 			} catch (IOException ioe) {
882 				ioe.printStackTrace();
883 			}
884 		}
885 
886 		if (e == null) {
887 			if (data instanceof Properties) {
888 				Properties props = PdfUtils
889 						.getDefaultfontProperties(defaultFont_);
890 				props.putAll((Properties) data);
891 				Font f = FontFactory.getFont(props);
892 				e = new Chunk(node.letter, f);
893 			} else {
894 				e = new Chunk(node.letter, defaultFont_);
895 				((Chunk) e).setSplitCharacter(split);
896 			}
897 		}
898 		return e;
899 	}
900 
901 	public Object visit(WikiStrongItalic node, Object data) {
902 
903 		Properties props = PdfUtils.getDefaultfontProperties(defaultFont_);
904 		StringBuffer buf = new StringBuffer();
905 		if (VisitorUtils.isBold(node))
906 			buf.append(MarkupTags.CSS_BOLD);
907 		if (VisitorUtils.isItalic(node))
908 			buf.append(MarkupTags.CSS_ITALIC);
909 
910 		String style = buf.toString();
911 		if (style != null && !style.equals(""))
912 			props.setProperty(ElementTags.STYLE, buf.toString());
913 
914 		// font-style propagation
915 		Phrase p = new Phrase(props);
916 
917 		String pre = VisitorUtils.getAppendString(node, true);
918 		String post = VisitorUtils.getAppendString(node, false);
919 
920 		if (pre != null)
921 			p.add(new Chunk(pre, defaultFont_));
922 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
923 			p.add(node.jjtGetChild(i).jjtAccept(this, data));
924 		}
925 		if (post != null)
926 			p.add(new Chunk(post, defaultFont_));
927 		return p;
928 	}
929 
930 	public Object visit(WikiDeleteline node, Object data) {
931 
932 		Properties props = PdfUtils.getDefaultfontProperties(defaultFont_);
933 		props.setProperty(ElementTags.STYLE, MarkupTags.CSS_LINETHROUGH);
934 
935 		// font-style propagation
936 		Phrase p = new Phrase(props);
937 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
938 			p.add(node.jjtGetChild(i).jjtAccept(this, data));
939 		}
940 		return p;
941 	}
942 
943 	public Object visit(WikiAnnotation node, Object data) {
944 
945 		Properties p = PdfUtils.getDefaultfontProperties(defaultFont_);
946 		p.setProperty(ElementTags.TITLE, "annotation" + node.num);
947 		p.setProperty(ElementTags.CONTENT, getChildAsString(node, data, 0));
948 		Annotation a = new Annotation(p);
949 		// Annotation a = new Annotation("annotation" +
950 		// node.num,getChildAsString(node,data,0));
951 		// a.setMarkupAttributes(getDefaultfontProperties());
952 		return a;
953 	}
954 
955 	// not implemented
956 	public Object visit(WikiInterwiki node, Object data) {
957 		return new Chunk("[[" + node.image + "]]", defaultFont_);
958 	}
959 
960 	public Object visit(WikiLink node, Object data) {
961 		String[] s = WikiHelper.split(node.image, WikiHelper.LINK_DELIMITER);
962 		Anchor a = new Anchor(s[0], defaultFont_);
963 		a.setReference(s[1]);
964 		return a;
965 	}
966 
967 	public Object visit(WikiAlias node, Object data) {
968 		String[] s = WikiHelper.split(node.image, WikiHelper.ALIAS_DELIMITER);
969 
970 		Element e = null;
971 		if (node.islink) { // URL case, same as WikiLink
972 			e = new Anchor(s[0], defaultFont_);
973 			((Anchor) e).setReference(s[1]);
974 		} else {
975 			String[] t = WikiHelper.split(s[1], WikiHelper.ANCHOR_MARK);
976 			try {
977 				if (t[0] == null || t[0].equals("")) {// only anchors, local
978 					// reference
979 					e = new Anchor(s[0], defaultFont_);
980 					((Anchor) e).setReference(WikiHelper.ANCHOR_MARK + t[1]);
981 				} else if (ctx.isPageExist(t[0], request_)) {
982 					URL url = ctx.getURLByName(t[0], request_);
983 					String ref = url.toString();
984 					if (t[1] != null && !t[1].equals(""))
985 						ref += WikiHelper.ANCHOR_MARK + t[1];
986 
987 					e = new Anchor(s[0], defaultFont_);
988 					((Anchor) e).setReference(ref);
989 				} else {
990 					e = new Chunk(s[0], defaultFont_);
991 				}
992 			} catch (TgwSecurityException tse) {
993 				e = new Chunk(s[0], defaultFont_);
994 
995 			}
996 		}
997 		return e;
998 	}
999 
1000 	public Object visit(WikiPagename node, Object data) {
1001 		String[] s = WikiHelper.split(node.image, WikiHelper.ANCHOR_MARK);
1002 		Element e = null;
1003 		try {
1004 			if (ctx.isPageExist(s[0], request_)) {
1005 				URL url = config_.getWikiContext().getURLByName(s[0], request_);
1006 				e = new Anchor(s[0], defaultFont_);
1007 				String ref = url.toString();
1008 				if (s[1] != null && !s[1].equals(""))
1009 					ref = ref + WikiHelper.ANCHOR_MARK + s[1];
1010 				((Anchor) e).setReference(ref);
1011 			}
1012 		} catch (TgwSecurityException tse) {
1013 			e = new Chunk(s[0], defaultFont_);
1014 		}
1015 		return e;
1016 	}
1017 
1018 	public Object visit(WikiInlinePlugin node, Object data) {
1019 		String name = node.name;
1020 		boolean ispluginused = false;
1021 		int inlinestart = 0;
1022 		Object obj = null;
1023 
1024 		PluginRequest prequest = VisitorUtils.createPluginRequest(node);
1025 		if (prequest.getArgs() != null)
1026 			inlinestart = 1;
1027 		Phrase childphrase = getChildPhrase(node, data, inlinestart);
1028 		prequest.setChild(childphrase);
1029 
1030 		if (config_.isPluginLoaded(name)) {
1031 			Plugin plugin = config_.getPlugin(name);
1032 			try {
1033 				obj = plugin.service(request_, response_, prequest);
1034 				ispluginused = true;
1035 			} catch (PluginException pe) {
1036 				log_.error(pe.getMessage());
1037 			} catch (Exception e) {
1038 				e.printStackTrace();
1039 				// some exception handling framework needed
1040 			}
1041 		}
1042 
1043 		if (obj instanceof List && !(obj instanceof Phrase)) { // in case
1044 			// VisitorUtils#parseHTMLtoPdf
1045 			// being used
1046 			List list = (List) obj;
1047 			for (Iterator i = list.iterator(); i.hasNext();) {
1048 				Element e = (Element) i.next();
1049 
1050 				if (e instanceof PdfPTable) {
1051 					pushObject(e);
1052 				} else {
1053 					addDocument(e, node);
1054 				}
1055 			}
1056 			obj = new Chunk("\u00a0");
1057 		}
1058 
1059 		if (!ispluginused) {
1060 			if ("amp".equals(name)) {
1061 				obj = new Chunk("\u0026", defaultFont_);
1062 
1063 			} else {
1064 				obj = new Chunk("&" + prequest.toString() + ";", defaultFont_);
1065 			}
1066 		}
1067 		return obj;
1068 	}
1069 
1070 	public Object visit(WikiArgs node, Object data) {
1071 		return null;
1072 	}
1073 
1074 	public Object visit(WikiErrors node, Object data) {
1075 		return getError(node.letter);
1076 	}
1077 
1078 	private Phrase getChildPhrase(SimpleNode node, Object data, int idx) {
1079 		Phrase p = new Phrase();
1080 		for (int i = idx; i < node.jjtGetNumChildren(); i++) {
1081 			p.add(node.jjtGetChild(i).jjtAccept(this, data));
1082 		}
1083 		return p;
1084 	}
1085 
1086 	private String getChildAsString(SimpleNode node, Object data, int idx) {
1087 		StringBuffer buf = new StringBuffer();
1088 		for (int i = idx; i < node.jjtGetNumChildren(); i++) {
1089 			Object o = node.jjtGetChild(i).jjtAccept(this, data);
1090 			if (o instanceof Chunk) {
1091 				buf.append(((Chunk) o).content());
1092 			} else if (o instanceof Phrase) {
1093 				for (Iterator itr = ((Phrase) o).getChunks().iterator(); itr
1094 						.hasNext();) {
1095 					buf.append(((Chunk) itr.next()).content());
1096 				}
1097 			} else {
1098 			}
1099 		}
1100 		return buf.toString();
1101 	}
1102 
1103 	// ----- [Start] static methods -----
1104 
1105 	private static int getAlign(String s) {
1106 		int align = Element.ALIGN_LEFT; // default
1107 
1108 		String ls = s.toLowerCase().trim();
1109 		if (ls.startsWith("left")) {
1110 			align = Element.ALIGN_LEFT;
1111 		} else if (ls.equals("center")) {
1112 			align = Element.ALIGN_CENTER;
1113 		} else if (ls.startsWith("right")) {
1114 			align = Element.ALIGN_RIGHT;
1115 		}
1116 		return align;
1117 	}
1118 
1119 	private static Chunk getError(String str) {
1120 		return new Chunk(str, PdfUtils.FONT_ERROR);
1121 	}
1122 
1123 	public Object visit(WikiAnyOther node, Object data) {
1124 		return new Chunk(node.letter, defaultFont_);
1125 	}
1126 
1127 	// ----- [End] static methods -----
1128 }