001/* 002 * $Id: Type3Font.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.util.HashMap; 047 048import com.itextpdf.text.DocumentException; 049import com.itextpdf.text.error_messages.MessageLocalization; 050 051/** 052 * A class to support Type3 fonts. 053 */ 054public class Type3Font extends BaseFont { 055 056 private boolean[] usedSlot; 057 private IntHashtable widths3 = new IntHashtable(); 058 private HashMap<Integer, Type3Glyph> char2glyph = new HashMap<Integer, Type3Glyph>(); 059 private PdfWriter writer; 060 private float llx = Float.NaN, lly, urx, ury; 061 private PageResources pageResources = new PageResources(); 062 private boolean colorized; 063 064 /** 065 * Creates a Type3 font. 066 * @param writer the writer 067 * @param chars an array of chars corresponding to the glyphs used (not used, present for compatibility only) 068 * @param colorized if <CODE>true</CODE> the font may specify color, if <CODE>false</CODE> no color commands are allowed 069 * and only images as masks can be used 070 */ 071 public Type3Font(PdfWriter writer, char[] chars, boolean colorized) { 072 this(writer, colorized); 073 } 074 075 /** 076 * Creates a Type3 font. This implementation assumes that the /FontMatrix is 077 * [0.001 0 0 0.001 0 0] or a 1000-unit glyph coordinate system. 078 * <p> 079 * An example: 080 * <p> 081 * <pre> 082 * Document document = new Document(PageSize.A4); 083 * PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("type3.pdf")); 084 * document.open(); 085 * Type3Font t3 = new Type3Font(writer, false); 086 * PdfContentByte g = t3.defineGlyph('a', 1000, 0, 0, 750, 750); 087 * g.rectangle(0, 0, 750, 750); 088 * g.fill(); 089 * g = t3.defineGlyph('b', 1000, 0, 0, 750, 750); 090 * g.moveTo(0, 0); 091 * g.lineTo(375, 750); 092 * g.lineTo(750, 0); 093 * g.fill(); 094 * Font f = new Font(t3, 12); 095 * document.add(new Paragraph("ababab", f)); 096 * document.close(); 097 * </pre> 098 * @param writer the writer 099 * @param colorized if <CODE>true</CODE> the font may specify color, if <CODE>false</CODE> no color commands are allowed 100 * and only images as masks can be used 101 */ 102 public Type3Font(PdfWriter writer, boolean colorized) { 103 this.writer = writer; 104 this.colorized = colorized; 105 fontType = FONT_TYPE_T3; 106 usedSlot = new boolean[256]; 107 } 108 109 /** 110 * Defines a glyph. If the character was already defined it will return the same content 111 * @param c the character to match this glyph. 112 * @param wx the advance this character will have 113 * @param llx the X lower left corner of the glyph bounding box. If the <CODE>colorize</CODE> option is 114 * <CODE>true</CODE> the value is ignored 115 * @param lly the Y lower left corner of the glyph bounding box. If the <CODE>colorize</CODE> option is 116 * <CODE>true</CODE> the value is ignored 117 * @param urx the X upper right corner of the glyph bounding box. If the <CODE>colorize</CODE> option is 118 * <CODE>true</CODE> the value is ignored 119 * @param ury the Y upper right corner of the glyph bounding box. If the <CODE>colorize</CODE> option is 120 * <CODE>true</CODE> the value is ignored 121 * @return a content where the glyph can be defined 122 */ 123 public PdfContentByte defineGlyph(char c, float wx, float llx, float lly, float urx, float ury) { 124 if (c == 0 || c > 255) 125 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.char.1.doesn.t.belong.in.this.type3.font", c)); 126 usedSlot[c] = true; 127 Integer ck = Integer.valueOf(c); 128 Type3Glyph glyph = char2glyph.get(ck); 129 if (glyph != null) 130 return glyph; 131 widths3.put(c, (int)wx); 132 if (!colorized) { 133 if (Float.isNaN(this.llx)) { 134 this.llx = llx; 135 this.lly = lly; 136 this.urx = urx; 137 this.ury = ury; 138 } 139 else { 140 this.llx = Math.min(this.llx, llx); 141 this.lly = Math.min(this.lly, lly); 142 this.urx = Math.max(this.urx, urx); 143 this.ury = Math.max(this.ury, ury); 144 } 145 } 146 glyph = new Type3Glyph(writer, pageResources, wx, llx, lly, urx, ury, colorized); 147 char2glyph.put(ck, glyph); 148 return glyph; 149 } 150 151 @Override 152 public String[][] getFamilyFontName() { 153 return getFullFontName(); 154 } 155 156 @Override 157 public float getFontDescriptor(int key, float fontSize) { 158 return 0; 159 } 160 161 @Override 162 public String[][] getFullFontName() { 163 return new String[][]{{"", "", "", ""}}; 164 } 165 166 /** 167 * @since 2.0.8 168 */ 169 @Override 170 public String[][] getAllNameEntries() { 171 return new String[][]{{"4", "", "", "", ""}}; 172 } 173 174 @Override 175 public int getKerning(int char1, int char2) { 176 return 0; 177 } 178 179 @Override 180 public String getPostscriptFontName() { 181 return ""; 182 } 183 184 @Override 185 protected int[] getRawCharBBox(int c, String name) { 186 return null; 187 } 188 189 @Override 190 int getRawWidth(int c, String name) { 191 return 0; 192 } 193 194 @Override 195 public boolean hasKernPairs() { 196 return false; 197 } 198 199 @Override 200 public boolean setKerning(int char1, int char2, int kern) { 201 return false; 202 } 203 204 @Override 205 public void setPostscriptFontName(String name) { 206 } 207 208 @Override 209 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object[] params) throws com.itextpdf.text.DocumentException, java.io.IOException { 210 if (this.writer != writer) 211 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("type3.font.used.with.the.wrong.pdfwriter")); 212 213 // Get first & lastchar ... 214 int firstChar = 0; 215 while( firstChar < usedSlot.length && !usedSlot[firstChar] ) firstChar++; 216 217 if ( firstChar == usedSlot.length ) { 218 throw new DocumentException(MessageLocalization.getComposedMessage("no.glyphs.defined.for.type3.font")); 219 } 220 int lastChar = usedSlot.length - 1; 221 while( lastChar >= firstChar && !usedSlot[lastChar] ) lastChar--; 222 223 int[] widths = new int[lastChar - firstChar + 1]; 224 int[] invOrd = new int[lastChar - firstChar + 1]; 225 226 int invOrdIndx = 0, w = 0; 227 for( int u = firstChar; u<=lastChar; u++, w++ ) { 228 if ( usedSlot[u] ) { 229 invOrd[invOrdIndx++] = u; 230 widths[w] = widths3.get(u); 231 } 232 } 233 PdfArray diffs = new PdfArray(); 234 PdfDictionary charprocs = new PdfDictionary(); 235 int last = -1; 236 for (int k = 0; k < invOrdIndx; ++k) { 237 int c = invOrd[k]; 238 if (c > last) { 239 last = c; 240 diffs.add(new PdfNumber(last)); 241 } 242 ++last; 243 int c2 = invOrd[k]; 244 String s = GlyphList.unicodeToName(c2); 245 if (s == null) 246 s = "a" + c2; 247 PdfName n = new PdfName(s); 248 diffs.add(n); 249 Type3Glyph glyph = char2glyph.get(Integer.valueOf(c2)); 250 PdfStream stream = new PdfStream(glyph.toPdf(null)); 251 stream.flateCompress(compressionLevel); 252 PdfIndirectReference refp = writer.addToBody(stream).getIndirectReference(); 253 charprocs.put(n, refp); 254 } 255 PdfDictionary font = new PdfDictionary(PdfName.FONT); 256 font.put(PdfName.SUBTYPE, PdfName.TYPE3); 257 if (colorized) 258 font.put(PdfName.FONTBBOX, new PdfRectangle(0, 0, 0, 0)); 259 else 260 font.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury)); 261 font.put(PdfName.FONTMATRIX, new PdfArray(new float[]{0.001f, 0, 0, 0.001f, 0, 0})); 262 font.put(PdfName.CHARPROCS, writer.addToBody(charprocs).getIndirectReference()); 263 PdfDictionary encoding = new PdfDictionary(); 264 encoding.put(PdfName.DIFFERENCES, diffs); 265 font.put(PdfName.ENCODING, writer.addToBody(encoding).getIndirectReference()); 266 font.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar)); 267 font.put(PdfName.LASTCHAR, new PdfNumber(lastChar)); 268 font.put(PdfName.WIDTHS, writer.addToBody(new PdfArray(widths)).getIndirectReference()); 269 if (pageResources.hasResources()) 270 font.put(PdfName.RESOURCES, writer.addToBody(pageResources.getResources()).getIndirectReference()); 271 writer.addToBody(font, ref); 272 } 273 274 /** 275 * Always returns null, because you can't get the FontStream of a Type3 font. 276 * @return null 277 * @since 2.1.3 278 */ 279 @Override 280 public PdfStream getFullFontStream() { 281 return null; 282 } 283 284 285 @Override 286 byte[] convertToBytes(String text) { 287 char[] cc = text.toCharArray(); 288 byte[] b = new byte[cc.length]; 289 int p = 0; 290 for (int k = 0; k < cc.length; ++k) { 291 char c = cc[k]; 292 if (charExists(c)) 293 b[p++] = (byte)c; 294 } 295 if (b.length == p) 296 return b; 297 byte[] b2 = new byte[p]; 298 System.arraycopy(b, 0, b2, 0, p); 299 return b2; 300 } 301 302 @Override 303 byte[] convertToBytes(int char1) { 304 if (charExists(char1)) 305 return new byte[]{(byte)char1}; 306 else return new byte[0]; 307 } 308 309 @Override 310 public int getWidth(int char1) { 311 if (!widths3.containsKey(char1)) 312 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.char.1.is.not.defined.in.a.type3.font", char1)); 313 return widths3.get(char1); 314 } 315 316 @Override 317 public int getWidth(String text) { 318 char[] c = text.toCharArray(); 319 int total = 0; 320 for (int k = 0; k < c.length; ++k) 321 total += getWidth(c[k]); 322 return total; 323 } 324 325 @Override 326 public int[] getCharBBox(int c) { 327 return null; 328 } 329 330 @Override 331 public boolean charExists(int c) { 332 if (c > 0 && c < 256) { 333 return usedSlot[c]; 334 } else { 335 return false; 336 } 337 } 338 339 @Override 340 public boolean setCharAdvance(int c, int advance) { 341 return false; 342 } 343 344}