001/*
002 * $Id: FontDetails.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.io.UnsupportedEncodingException;
047import java.util.HashMap;
048
049import com.itextpdf.text.ExceptionConverter;
050import com.itextpdf.text.Utilities;
051
052/**
053 * Each font in the document will have an instance of this class
054 * where the characters used will be represented.
055 *
056 * @author  Paulo Soares
057 */
058class FontDetails {
059
060    /**
061     * The indirect reference to this font
062     */
063    PdfIndirectReference indirectReference;
064    /**
065     * The font name that appears in the document body stream
066     */
067    PdfName fontName;
068    /**
069     * The font
070     */
071    BaseFont baseFont;
072    /**
073     * The font if it's an instance of <CODE>TrueTypeFontUnicode</CODE>
074     */
075    TrueTypeFontUnicode ttu;
076    /**
077     * The font if it's an instance of <CODE>CJKFont</CODE>
078     */
079    CJKFont cjkFont;
080    /**
081     * The array used with single byte encodings
082     */
083    byte shortTag[];
084    /**
085     * The map used with double byte encodings. The key is Integer(glyph) and
086     * the value is int[]{glyph, width, Unicode code}
087     */
088    HashMap<Integer, int[]> longTag;
089    /**
090     * IntHashtable with CIDs of CJK glyphs that are used in the text.
091     */
092    IntHashtable cjkTag;
093    /**
094     * The font type
095     */
096    int fontType;
097    /**
098     * <CODE>true</CODE> if the font is symbolic
099     */
100    boolean symbolic;
101    /**
102     * Indicates if only a subset of the glyphs and widths for that particular
103     * encoding should be included in the document.
104     */
105    protected boolean subset = true;
106
107    /**
108     * Each font used in a document has an instance of this class.
109     * This class stores the characters used in the document and other
110     * specifics unique to the current working document.
111     * @param fontName the font name
112     * @param indirectReference the indirect reference to the font
113     * @param baseFont the <CODE>BaseFont</CODE>
114     */
115    FontDetails(PdfName fontName, PdfIndirectReference indirectReference, BaseFont baseFont) {
116        this.fontName = fontName;
117        this.indirectReference = indirectReference;
118        this.baseFont = baseFont;
119        fontType = baseFont.getFontType();
120        switch (fontType) {
121            case BaseFont.FONT_TYPE_T1:
122            case BaseFont.FONT_TYPE_TT:
123                shortTag = new byte[256];
124                break;
125            case BaseFont.FONT_TYPE_CJK:
126                cjkTag = new IntHashtable();
127                cjkFont = (CJKFont)baseFont;
128                break;
129            case BaseFont.FONT_TYPE_TTUNI:
130                longTag = new HashMap<Integer, int[]>();
131                ttu = (TrueTypeFontUnicode)baseFont;
132                symbolic = baseFont.isFontSpecific();
133                break;
134        }
135    }
136
137    /**
138     * Gets the indirect reference to this font.
139     * @return the indirect reference to this font
140     */
141    PdfIndirectReference getIndirectReference() {
142        return indirectReference;
143    }
144
145    /**
146     * Gets the font name as it appears in the document body.
147     * @return the font name
148     */
149    PdfName getFontName() {
150        return fontName;
151    }
152
153    /**
154     * Gets the <CODE>BaseFont</CODE> of this font.
155     * @return the <CODE>BaseFont</CODE> of this font
156     */
157    BaseFont getBaseFont() {
158        return baseFont;
159    }
160
161    /**
162     * Converts the text into bytes to be placed in the document.
163     * The conversion is done according to the font and the encoding and the characters
164     * used are stored.
165     * @param text the text to convert
166     * @return the conversion
167     */
168    byte[] convertToBytes(String text) {
169        byte b[] = null;
170        switch (fontType) {
171            case BaseFont.FONT_TYPE_T3:
172                return baseFont.convertToBytes(text);
173            case BaseFont.FONT_TYPE_T1:
174            case BaseFont.FONT_TYPE_TT: {
175                b = baseFont.convertToBytes(text);
176                int len = b.length;
177                for (int k = 0; k < len; ++k)
178                    shortTag[b[k] & 0xff] = 1;
179                break;
180            }
181            case BaseFont.FONT_TYPE_CJK: {
182                int len = text.length();
183                for (int k = 0; k < len; ++k)
184                    cjkTag.put(cjkFont.getCidCode(text.charAt(k)), 0);
185                b = baseFont.convertToBytes(text);
186                break;
187            }
188            case BaseFont.FONT_TYPE_DOCUMENT: {
189                b = baseFont.convertToBytes(text);
190                break;
191            }
192            case BaseFont.FONT_TYPE_TTUNI: {
193                try {
194                    int len = text.length();
195                    int metrics[] = null;
196                    char glyph[] = new char[len];
197                    int i = 0;
198                    if (symbolic) {
199                        b = PdfEncodings.convertToBytes(text, "symboltt");
200                        len = b.length;
201                        for (int k = 0; k < len; ++k) {
202                            metrics = ttu.getMetricsTT(b[k] & 0xff);
203                            if (metrics == null)
204                                continue;
205                            longTag.put(Integer.valueOf(metrics[0]), new int[]{metrics[0], metrics[1], ttu.getUnicodeDifferences(b[k] & 0xff)});
206                            glyph[i++] = (char)metrics[0];
207                        }
208                    }
209                    else {
210                        for (int k = 0; k < len; ++k) {
211                                int val;
212                                if (Utilities.isSurrogatePair(text, k)) {
213                                        val = Utilities.convertToUtf32(text, k);
214                                        k++;
215                                }
216                                else {
217                                        val = text.charAt(k);
218                                }
219                                metrics = ttu.getMetricsTT(val);
220                                if (metrics == null)
221                                        continue;
222                                int m0 = metrics[0];
223                                Integer gl = Integer.valueOf(m0);
224                                if (!longTag.containsKey(gl))
225                                        longTag.put(gl, new int[]{m0, metrics[1], val});
226                                glyph[i++] = (char)m0;
227                        }
228                    }
229                    String s = new String(glyph, 0, i);
230                    b = s.getBytes(CJKFont.CJK_ENCODING);
231                }
232                catch (UnsupportedEncodingException e) {
233                    throw new ExceptionConverter(e);
234                }
235                break;
236            }
237        }
238        return b;
239    }
240
241    /**
242     * Writes the font definition to the document.
243     * @param writer the <CODE>PdfWriter</CODE> of this document
244     */
245    void writeFont(PdfWriter writer) {
246        try {
247            switch (fontType) {
248                case BaseFont.FONT_TYPE_T3:
249                    baseFont.writeFont(writer, indirectReference, null);
250                    break;
251                case BaseFont.FONT_TYPE_T1:
252                case BaseFont.FONT_TYPE_TT: {
253                    int firstChar;
254                    int lastChar;
255                    for (firstChar = 0; firstChar < 256; ++firstChar) {
256                        if (shortTag[firstChar] != 0)
257                            break;
258                    }
259                    for (lastChar = 255; lastChar >= firstChar; --lastChar) {
260                        if (shortTag[lastChar] != 0)
261                            break;
262                    }
263                    if (firstChar > 255) {
264                        firstChar = 255;
265                        lastChar = 255;
266                    }
267                    baseFont.writeFont(writer, indirectReference, new Object[]{Integer.valueOf(firstChar), Integer.valueOf(lastChar), shortTag, Boolean.valueOf(subset)});
268                    break;
269                }
270                case BaseFont.FONT_TYPE_CJK:
271                    baseFont.writeFont(writer, indirectReference, new Object[]{cjkTag});
272                    break;
273                case BaseFont.FONT_TYPE_TTUNI:
274                    baseFont.writeFont(writer, indirectReference, new Object[]{longTag, Boolean.valueOf(subset)});
275                    break;
276            }
277        }
278        catch(Exception e) {
279            throw new ExceptionConverter(e);
280        }
281    }
282
283    /**
284     * Indicates if all the glyphs and widths for that particular
285     * encoding should be included in the document.
286     * @return <CODE>false</CODE> to include all the glyphs and widths.
287     */
288    public boolean isSubset() {
289        return subset;
290    }
291
292    /**
293     * Indicates if all the glyphs and widths for that particular
294     * encoding should be included in the document. Set to <CODE>false</CODE>
295     * to include all.
296     * @param subset new value of property subset
297     */
298    public void setSubset(boolean subset) {
299        this.subset = subset;
300    }
301}