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.pdf;
17  
18  import java.awt.Color;
19  import java.io.IOException;
20  import java.io.Reader;
21  import java.io.StringReader;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Properties;
25  import java.util.regex.Matcher;
26  import java.util.regex.Pattern;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  import com.lowagie.text.Chunk;
32  import com.lowagie.text.DocumentException;
33  import com.lowagie.text.Element;
34  import com.lowagie.text.ElementTags;
35  import com.lowagie.text.Font;
36  import com.lowagie.text.ListItem;
37  import com.lowagie.text.PageSize;
38  import com.lowagie.text.Paragraph;
39  import com.lowagie.text.Phrase;
40  import com.lowagie.text.Rectangle;
41  import com.lowagie.text.html.simpleparser.StyleSheet;
42  import com.lowagie.text.pdf.BaseFont;
43  import com.lowagie.text.pdf.PdfPTable;
44  
45  /***
46   * <p>
47   * KNOWN ISSUE 1. Japanese character is unavailable in PdfPTable During parsing,
48   * HTMLWorker uses FactoryProperties static methods. It will create the font
49   * object by FactoryProperties#getFont, and we can specify fontname as "face"
50   * style, though the encoding is fixed to "BaseFont.WINANSI", that is CP1252,
51   * Latin1. Thus, at least, we could not get the objects which can display
52   * Japanese character soon after parsing phase even if Japanese fonts are
53   * registered by FontFactory#register and its name are given to "face" style. To
54   * make matters complicated, HTMLWorker uses PdfPTable to convert &lt;table&gt;
55   * and wrapper class IncTable for PdfPTable, and IncCell for PdfPCell. In
56   * IncCell, it adds Element by PdfPCell#addElement methods. PdfPCell#addElement
57   * methods doesn't keep Phrase or Chunk object in its instances but directly
58   * calles ColumnText#addElement. ColumnText#addElement adds element into its
59   * protected LinkedList and it seems to be no way provided to change that added
60   * element property. => This solved by to create extended class CJKHTMLWorker
61   * and CJKFactoryProperties for HTMLWorker and FactoryProperties.
62   * </p>
63   * 
64   * @author someda
65   */
66  public class PdfUtils {
67  
68  	// Mincho Font properties	
69  	public static Font FONT_JA_GOTHIC;
70  	private static final String FONTNAME_JA_GOTHIC = "HeiseiKakuGo-W5";
71  	private static final String FONTNAME_JA_GOTHIC_ENCODING = "UniJIS-UCS2-H";
72  	
73  	// Gothic Font properties
74  	public static Font FONT_JA_MINCHO;		
75  	private static final String FONTNAME_JA_MINCHO = "HeiseiMin-W3";
76  	private static final String FONTNAME_JA_MINCHO_ENCODING = "UniJIS-UCS2-HW-H";
77  	
78  	public static Font FONT_ERROR;
79  	
80  	public static BaseFont BASEFONT_GOTHIC;
81  	public static BaseFont BASEFONT_MINCHO;
82  	
83  	public static final String DEFAULT_PDF_PAGESTYLE = "v-n-A4";
84  	
85  	private static final String COLOR_BLACK_HEX = "000000";
86  	
87  	private static final Pattern HTML_TAG_PATTERN = Pattern.compile("<//w+.*>");
88  	
89  	private static Log log_ = LogFactory.getLog(PdfUtils.class);
90  	
91  	static{
92  		try{			
93  			BASEFONT_GOTHIC = BaseFont.createFont(FONTNAME_JA_GOTHIC,FONTNAME_JA_GOTHIC_ENCODING,BaseFont.NOT_EMBEDDED);
94  			BASEFONT_MINCHO = BaseFont.createFont(FONTNAME_JA_MINCHO,FONTNAME_JA_MINCHO_ENCODING,BaseFont.NOT_EMBEDDED);			
95  			FONT_JA_GOTHIC = new Font(BASEFONT_GOTHIC, 12,Font.NORMAL);
96  			FONT_JA_MINCHO = new Font(BASEFONT_MINCHO,12,Font.NORMAL);			
97  			FONT_ERROR = new Font(BASEFONT_GOTHIC,12,Font.BOLD,Color.RED);			
98  		}catch(IOException ioe){
99  			ioe.printStackTrace();
100 		}catch(DocumentException de){
101 			de.printStackTrace();
102 		}
103 	}	
104 	
105 	public static Properties getDefaultfontProperties(Font defaultFont){
106 		
107 		Properties props = new Properties();
108 		props.setProperty(ElementTags.FONT,defaultFont.getFamilyname());
109 		
110 		// cannot get exact font encoding name set by init(),
111 		// defaultFont_.getBaseFont().getEncoding() returns "UnicodeBigUnmarked".
112 		if(defaultFont.equals(FONT_JA_GOTHIC)){
113 			props.setProperty(ElementTags.ENCODING,FONTNAME_JA_GOTHIC_ENCODING);
114 		}else if(defaultFont.equals(FONT_JA_MINCHO)){
115 			props.setProperty(ElementTags.ENCODING,FONTNAME_JA_MINCHO_ENCODING);			
116 		}
117 		props.setProperty(ElementTags.EMBEDDED,String.valueOf(defaultFont.getBaseFont().isEmbedded()));
118 		props.setProperty(ElementTags.STYLE,String.valueOf(defaultFont.style()));
119 		props.setProperty(ElementTags.SIZE,String.valueOf(defaultFont.size()));
120 		props.setProperty(ElementTags.COLOR,COLOR_BLACK_HEX);
121 
122 		return props;
123 	}
124 		
125 	public static Properties getChapterfontProperties(Font defaultFont) {
126 		Properties props = getDefaultfontProperties(defaultFont);
127 		props.setProperty(ElementTags.SIZE, String.valueOf(18));
128 		return props;
129 	}
130 		
131 	public static Properties getSectionfontProperties(Font defaultFont, int depth){
132 		Properties props = getDefaultfontProperties(defaultFont);
133 		if(depth > 2){
134 			props.setProperty(ElementTags.SIZE,String.valueOf(14));
135 		}else{
136 			props.setProperty(ElementTags.SIZE,String.valueOf(16));
137 		}
138 		return props;		
139 	}	
140 	
141 	// ----- [Start] PDF converter methods -----
142 	public static Object convertHTMLtoPDF(String s){		
143 		
144 		if(s != null){
145 			Matcher m = HTML_TAG_PATTERN.matcher(s);
146 			if(m.find()){
147 				return parseHTMLtoPDF(s);
148 			}else{
149 				return new Chunk(s,FONT_JA_GOTHIC);
150 			}						
151 		}
152 		return null;
153 	}	
154 	
155 	public static Object parseHTMLtoPDF(String s) {
156 		Reader reader = new StringReader(s);
157 		try{
158 			return parseHTMLtoPDF(reader);
159 		}finally{
160 			if (reader != null) {
161 				try {
162 					reader.close();
163 				} catch (IOException ioe) {
164 					log_.error("failed to close reader object");
165 				}
166 			}
167 		}
168 	}
169 
170 	public static Object parseHTMLtoPDF(Reader reader) {
171 
172 		List list = null;
173 		float leading = FONT_JA_GOTHIC.size() + 3;
174 		float listspacing = 2;
175 		try {
176 			list = CJKHTMLWorker.parseToList(reader, getCJKStyleSheet());
177 			for (Iterator i = list.iterator(); i.hasNext();) {
178 				Element e = (Element) i.next();
179 
180 				List chunks;
181 				if ((chunks = e.getChunks()) != null) {
182 					for (Iterator j = chunks.iterator(); j.hasNext();) {
183 						Chunk c = (Chunk) j.next();
184 						c.setFont(FONT_JA_GOTHIC);
185 					}
186 				}
187 				if (e instanceof Paragraph) {
188 					((Paragraph) e).setLeading(leading);
189 				} else if (e instanceof PdfPTable) {
190 					((PdfPTable) e).setSpacingBefore(leading);
191 				} else if (e instanceof com.lowagie.text.List) {
192 					com.lowagie.text.List l = (com.lowagie.text.List) e;
193 					for (Iterator j = l.getItems().iterator(); j.hasNext();) {
194 						Object listItem = j.next();						
195 						if(listItem instanceof ListItem){
196 							ListItem li = (ListItem) listItem;
197 							li.setSpacingBefore(listspacing);							
198 						}
199 					}
200 				}
201 			}
202 		} catch (IOException ioe) {
203 			log_.error("failed to parse HTML object");
204 		} 
205 		return list;
206 	}
207 
208 	// ----- [End] PDF converter methods -----
209 
210 	public static String getStringResult(Object obj){
211 		
212 		String ret = null;		
213 		if(obj instanceof String){
214 			ret = (String)obj;
215 		}else if(obj instanceof Chunk){
216 			Chunk c = (Chunk)obj;
217 			ret = c.content();
218 		}else if(obj instanceof Phrase){
219 			StringBuffer buf = new StringBuffer();
220 			Phrase p = (Phrase) obj;
221 			for(int i=0;i<p.size();i++){
222 				Chunk c = (Chunk)p.get(i);
223 				buf.append(c.content());
224 			}
225 			ret = buf.toString();
226 		}		
227 		return ret;
228 	}
229 	
230 	
231 	/*
232 	 * Style properties made of 3 parts,
233 	 *   vertical-or-horizontal (first parts)
234 	 *   normal-or-simple style (second parts)
235 	 *   paper size (the last part)
236 	 * and, it is joined by "-" string.
237 	 * For example, the default settings
238 	 *   v-n-A4
239 	 * indicates, vertical, normal style, A4 PDF document.  
240 	 */
241 	public static Rectangle getRectangle(String style){
242 		
243 		Rectangle rec = PageSize.A4; // default page size
244 		if(style.endsWith("B5")){
245 			rec = PageSize.B5;
246 		}			
247 		return (style.startsWith("h-"))? rec.rotate() : rec;		
248 	}
249 	
250 	public static boolean isSimpleStyle(String style){
251 		return style.indexOf("-s-") != -1;		
252 	}
253 	
254 	
255 	// ----- [Start] private methods -----
256 	private static StyleSheet getCJKStyleSheet(){
257 		StyleSheet cjkstyle = new StyleSheet();
258 		
259 		cjkstyle.loadTagStyle("table", "border", "0.5");
260 		cjkstyle.loadTagStyle("table", "face", FONTNAME_JA_GOTHIC);
261 
262 		// these two style used in CJKFactoryProperties
263 		cjkstyle.loadTagStyle("table", "cjk_encoding", FONTNAME_JA_GOTHIC_ENCODING);
264 		cjkstyle.loadTagStyle("table", "cjk_embeded", "false");
265 
266 		cjkstyle.loadTagStyle("th", "bgcolor", "silver");
267 		cjkstyle.loadTagStyle("th", "font", "bold");	
268 		
269 		return cjkstyle;
270 	}
271 	
272 }