001/*
002 * $Id: DocumentFont.java 4859 2011-05-08 11:02:19Z psoares33 $
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.IOException;
047import java.util.HashMap;
048
049import com.itextpdf.text.DocumentException;
050import com.itextpdf.text.ExceptionConverter;
051import com.itextpdf.text.pdf.fonts.cmaps.CMap;
052import com.itextpdf.text.pdf.fonts.cmaps.CMapParser;
053import java.io.ByteArrayInputStream;
054
055/**
056 *
057 * @author  psoares
058 */
059public class DocumentFont extends BaseFont {
060    // code, [glyph, width]
061    private HashMap<Integer, int[]> metrics = new HashMap<Integer, int[]>();
062    private String fontName;
063    private PRIndirectReference refFont;
064    private PdfDictionary font;
065    private IntHashtable uni2byte = new IntHashtable();
066    private IntHashtable diffmap;
067    private float Ascender = 800;
068    private float CapHeight = 700;
069    private float Descender = -200;
070    private float ItalicAngle = 0;
071    private float llx = -50;
072    private float lly = -200;
073    private float urx = 100;
074    private float ury = 900;
075    private boolean isType0 = false;
076
077    private BaseFont cjkMirror;
078
079    private static String cjkNames[] = {"HeiseiMin-W3", "HeiseiKakuGo-W5", "STSong-Light", "MHei-Medium",
080        "MSung-Light", "HYGoThic-Medium", "HYSMyeongJo-Medium", "MSungStd-Light", "STSongStd-Light",
081        "HYSMyeongJoStd-Medium", "KozMinPro-Regular"};
082
083    private static String cjkEncs[] = {"UniJIS-UCS2-H", "UniJIS-UCS2-H", "UniGB-UCS2-H", "UniCNS-UCS2-H",
084        "UniCNS-UCS2-H", "UniKS-UCS2-H", "UniKS-UCS2-H", "UniCNS-UCS2-H", "UniGB-UCS2-H",
085        "UniKS-UCS2-H", "UniJIS-UCS2-H"};
086
087    private static String cjkNames2[] = {"MSungStd-Light", "STSongStd-Light", "HYSMyeongJoStd-Medium", "KozMinPro-Regular"};
088
089    private static String cjkEncs2[] = {"UniCNS-UCS2-H", "UniGB-UCS2-H", "UniKS-UCS2-H", "UniJIS-UCS2-H",
090        "UniCNS-UTF16-H", "UniGB-UTF16-H", "UniKS-UTF16-H", "UniJIS-UTF16-H"};
091
092    private static final int stdEnc[] = {
093        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
094        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
095        32,33,34,35,36,37,38,8217,40,41,42,43,44,45,46,47,
096        48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
097        64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
098        80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
099        8216,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
100        112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,0,
101        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
102        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
103        0,161,162,163,8260,165,402,167,164,39,8220,171,8249,8250,64257,64258,
104        0,8211,8224,8225,183,0,182,8226,8218,8222,8221,187,8230,8240,0,191,
105        0,96,180,710,732,175,728,729,168,0,730,184,0,733,731,711,
106        8212,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
107        0,198,0,170,0,0,0,0,321,216,338,186,0,0,0,0,
108        0,230,0,0,0,305,0,0,322,248,339,223,0,0,0,0};
109
110    /** Creates a new instance of DocumentFont */
111    DocumentFont(PRIndirectReference refFont) {
112        encoding = "";
113        fontSpecific = false;
114        this.refFont = refFont;
115        fontType = FONT_TYPE_DOCUMENT;
116        font = (PdfDictionary)PdfReader.getPdfObject(refFont);
117        PdfName baseFont = font.getAsName(PdfName.BASEFONT);
118        fontName = baseFont != null ? PdfName.decodeName(baseFont.toString()) : "Unspecified Font Name";
119        PdfName subType = font.getAsName(PdfName.SUBTYPE);
120        if (PdfName.TYPE1.equals(subType) || PdfName.TRUETYPE.equals(subType))
121            doType1TT();
122        else {
123            for (int k = 0; k < cjkNames.length; ++k) {
124                if (fontName.startsWith(cjkNames[k])) {
125                    fontName = cjkNames[k];
126                    try {
127                        cjkMirror = BaseFont.createFont(fontName, cjkEncs[k], false);
128                    }
129                    catch (Exception e) {
130                        throw new ExceptionConverter(e);
131                    }
132                    return;
133                }
134            }
135            PdfName encodingName = font.getAsName(PdfName.ENCODING);
136            if (encodingName != null){
137                String enc = PdfName.decodeName(encodingName.toString());
138                for (int k = 0; k < cjkEncs2.length; ++k) {
139                    if (enc.startsWith(cjkEncs2[k])) {
140                        try {
141                            if (k > 3)
142                                k -= 4;
143                            cjkMirror = BaseFont.createFont(cjkNames2[k], cjkEncs2[k], false);
144                        }
145                        catch (Exception e) {
146                            throw new ExceptionConverter(e);
147                        }
148                        return;
149                    }
150                }
151                if (PdfName.TYPE0.equals(subType) && enc.equals("Identity-H")) {
152                    processType0(font);
153                    isType0 = true;
154                }
155            }
156        }
157    }
158
159    private void processType0(PdfDictionary font) {
160        try {
161            PdfObject toUniObject = PdfReader.getPdfObjectRelease(font.get(PdfName.TOUNICODE));
162            PdfArray df = (PdfArray)PdfReader.getPdfObjectRelease(font.get(PdfName.DESCENDANTFONTS));
163            PdfDictionary cidft = (PdfDictionary)PdfReader.getPdfObjectRelease(df.getPdfObject(0));
164            PdfNumber dwo = (PdfNumber)PdfReader.getPdfObjectRelease(cidft.get(PdfName.DW));
165            int dw = 1000;
166            if (dwo != null)
167                dw = dwo.intValue();
168            IntHashtable widths = readWidths((PdfArray)PdfReader.getPdfObjectRelease(cidft.get(PdfName.W)));
169            PdfDictionary fontDesc = (PdfDictionary)PdfReader.getPdfObjectRelease(cidft.get(PdfName.FONTDESCRIPTOR));
170            fillFontDesc(fontDesc);
171            if (toUniObject instanceof PRStream){
172                fillMetrics(PdfReader.getStreamBytes((PRStream)toUniObject), widths, dw);
173            }
174
175        } catch (Exception e) {
176            throw new ExceptionConverter(e);
177        }
178    }
179
180    private IntHashtable readWidths(PdfArray ws) {
181        IntHashtable hh = new IntHashtable();
182        if (ws == null)
183            return hh;
184        for (int k = 0; k < ws.size(); ++k) {
185            int c1 = ((PdfNumber)PdfReader.getPdfObjectRelease(ws.getPdfObject(k))).intValue();
186            PdfObject obj = PdfReader.getPdfObjectRelease(ws.getPdfObject(++k));
187            if (obj.isArray()) {
188                PdfArray a2 = (PdfArray)obj;
189                for (int j = 0; j < a2.size(); ++j) {
190                    int c2 = ((PdfNumber)PdfReader.getPdfObjectRelease(a2.getPdfObject(j))).intValue();
191                    hh.put(c1++, c2);
192                }
193            }
194            else {
195                int c2 = ((PdfNumber)obj).intValue();
196                int w = ((PdfNumber)PdfReader.getPdfObjectRelease(ws.getPdfObject(++k))).intValue();
197                for (; c1 <= c2; ++c1)
198                    hh.put(c1, w);
199            }
200        }
201        return hh;
202    }
203
204    private String decodeString(PdfString ps) {
205        if (ps.isHexWriting())
206            return PdfEncodings.convertToString(ps.getBytes(), "UnicodeBigUnmarked");
207        else
208            return ps.toUnicodeString();
209    }
210
211    private void fillMetrics(byte[] touni, IntHashtable widths, int dw) {
212        try {
213            PdfContentParser ps = new PdfContentParser(new PRTokeniser(touni));
214            PdfObject ob = null;
215            boolean notFound = true;
216            int nestLevel = 0;
217            while ((notFound || nestLevel > 0) && (ob = ps.readPRObject()) != null) {
218                if (ob.type() == PdfContentParser.COMMAND_TYPE) {
219                        if (ob.toString().equals("begin")) {
220                                notFound = false;
221                                nestLevel++;
222                        }
223                        else if (ob.toString().equals("end")) {
224                                nestLevel--;
225                        }
226                        else if (ob.toString().equals("beginbfchar")) {
227                        while (true) {
228                            PdfObject nx = ps.readPRObject();
229                            if (nx.toString().equals("endbfchar"))
230                                break;
231                            String cid = decodeString((PdfString)nx);
232                            String uni = decodeString((PdfString)ps.readPRObject());
233                            if (uni.length() == 1) {
234                                int cidc = cid.charAt(0);
235                                int unic = uni.charAt(uni.length() - 1);
236                                int w = dw;
237                                if (widths.containsKey(cidc))
238                                    w = widths.get(cidc);
239                                metrics.put(Integer.valueOf(unic), new int[]{cidc, w});
240                            }
241                        }
242                    }
243                    else if (ob.toString().equals("beginbfrange")) {
244                        while (true) {
245                            PdfObject nx = ps.readPRObject();
246                            if (nx.toString().equals("endbfrange"))
247                                break;
248                            String cid1 = decodeString((PdfString)nx);
249                            String cid2 = decodeString((PdfString)ps.readPRObject());
250                            int cid1c = cid1.charAt(0);
251                            int cid2c = cid2.charAt(0);
252                            PdfObject ob2 = ps.readPRObject();
253                            if (ob2.isString()) {
254                                String uni = decodeString((PdfString)ob2);
255                                if (uni.length() == 1) {
256                                    int unic = uni.charAt(uni.length() - 1);
257                                    for (; cid1c <= cid2c; cid1c++, unic++) {
258                                        int w = dw;
259                                        if (widths.containsKey(cid1c))
260                                            w = widths.get(cid1c);
261                                        metrics.put(Integer.valueOf(unic), new int[]{cid1c, w});
262                                    }
263                                }
264                            }
265                            else {
266                                PdfArray a = (PdfArray)ob2;
267                                for (int j = 0; j < a.size(); ++j, ++cid1c) {
268                                    String uni = decodeString(a.getAsString(j));
269                                    if (uni.length() == 1) {
270                                        int unic = uni.charAt(uni.length() - 1);
271                                        int w = dw;
272                                        if (widths.containsKey(cid1c))
273                                            w = widths.get(cid1c);
274                                        metrics.put(Integer.valueOf(unic), new int[]{cid1c, w});
275                                    }
276                                }
277                            }
278                        }
279                    }
280                }
281            }
282        }
283        catch (Exception e) {
284            throw new ExceptionConverter(e);
285        }
286    }
287
288    private void doType1TT() {
289        PdfObject enc = PdfReader.getPdfObject(font.get(PdfName.ENCODING));
290        if (enc == null)
291            fillEncoding(null);
292        else {
293            if (enc.isName())
294                fillEncoding((PdfName)enc);
295            else {
296                PdfDictionary encDic = (PdfDictionary)enc;
297                enc = PdfReader.getPdfObject(encDic.get(PdfName.BASEENCODING));
298                if (enc == null)
299                    fillEncoding(null);
300                else
301                    fillEncoding((PdfName)enc);
302                PdfArray diffs = encDic.getAsArray(PdfName.DIFFERENCES);
303                if (diffs != null) {
304                    CMap toUnicode = null;
305                    diffmap = new IntHashtable();
306                    int currentNumber = 0;
307                    for (int k = 0; k < diffs.size(); ++k) {
308                        PdfObject obj = diffs.getPdfObject(k);
309                        if (obj.isNumber())
310                            currentNumber = ((PdfNumber)obj).intValue();
311                        else {
312                            int c[] = GlyphList.nameToUnicode(PdfName.decodeName(((PdfName)obj).toString()));
313                            if (c != null && c.length > 0) {
314                                uni2byte.put(c[0], currentNumber);
315                                diffmap.put(c[0], currentNumber);
316                            }
317                            else {
318                                if (toUnicode == null) {
319                                    toUnicode = processToUnicode();
320                                    if (toUnicode == null) {
321                                        toUnicode = new CMap();
322                                    }
323                                }
324                                final String unicode = toUnicode.lookup(new byte[]{(byte) currentNumber}, 0, 1);
325                                if ((unicode != null) && (unicode.length() == 1)) {
326                                    this.uni2byte.put(unicode.charAt(0), currentNumber);
327                                    this.diffmap.put(unicode.charAt(0), currentNumber);
328                                }
329                            }
330                            ++currentNumber;
331                        }
332                    }
333                }
334            }
335        }
336        PdfArray newWidths = font.getAsArray(PdfName.WIDTHS);
337        PdfNumber first = font.getAsNumber(PdfName.FIRSTCHAR);
338        PdfNumber last = font.getAsNumber(PdfName.LASTCHAR);
339        if (BuiltinFonts14.containsKey(fontName)) {
340            BaseFont bf;
341            try {
342                bf = BaseFont.createFont(fontName, WINANSI, false);
343            }
344            catch (Exception e) {
345                throw new ExceptionConverter(e);
346            }
347            int e[] = uni2byte.toOrderedKeys();
348            for (int k = 0; k < e.length; ++k) {
349                int n = uni2byte.get(e[k]);
350                widths[n] = bf.getRawWidth(n, GlyphList.unicodeToName(e[k]));
351            }
352            if (diffmap != null) { //widths for diffmap must override existing ones
353                e = diffmap.toOrderedKeys();
354                for (int k = 0; k < e.length; ++k) {
355                    int n = diffmap.get(e[k]);
356                    widths[n] = bf.getRawWidth(n, GlyphList.unicodeToName(e[k]));
357                }
358                diffmap = null;
359            }
360            Ascender = bf.getFontDescriptor(ASCENT, 1000);
361            CapHeight = bf.getFontDescriptor(CAPHEIGHT, 1000);
362            Descender = bf.getFontDescriptor(DESCENT, 1000);
363            ItalicAngle = bf.getFontDescriptor(ITALICANGLE, 1000);
364            llx = bf.getFontDescriptor(BBOXLLX, 1000);
365            lly = bf.getFontDescriptor(BBOXLLY, 1000);
366            urx = bf.getFontDescriptor(BBOXURX, 1000);
367            ury = bf.getFontDescriptor(BBOXURY, 1000);
368        }
369        if (first != null && last != null && newWidths != null) {
370            int f = first.intValue();
371            for (int k = 0; k < newWidths.size(); ++k) {
372                widths[f + k] = newWidths.getAsNumber(k).intValue();
373            }
374        }
375        fillFontDesc(font.getAsDict(PdfName.FONTDESCRIPTOR));
376    }
377
378    private CMap processToUnicode() {
379        CMap cmapRet = null;
380        PdfObject toUni = PdfReader.getPdfObjectRelease(this.font.get(PdfName.TOUNICODE));
381        if (toUni instanceof PRStream) {
382            try {
383                byte[] touni = PdfReader.getStreamBytes((PRStream)toUni);
384                CMapParser cmapParser = new CMapParser();
385                cmapRet = cmapParser.parse(new ByteArrayInputStream(touni));
386            } catch (Exception e) {
387            }
388        }
389        return cmapRet;
390    }
391
392    private void fillFontDesc(PdfDictionary fontDesc) {
393        if (fontDesc == null)
394            return;
395        PdfNumber v = fontDesc.getAsNumber(PdfName.ASCENT);
396        if (v != null)
397            Ascender = v.floatValue();
398        v = fontDesc.getAsNumber(PdfName.CAPHEIGHT);
399        if (v != null)
400            CapHeight = v.floatValue();
401        v = fontDesc.getAsNumber(PdfName.DESCENT);
402        if (v != null)
403            Descender = v.floatValue();
404        v = fontDesc.getAsNumber(PdfName.ITALICANGLE);
405        if (v != null)
406            ItalicAngle = v.floatValue();
407        PdfArray bbox = fontDesc.getAsArray(PdfName.FONTBBOX);
408        if (bbox != null) {
409            llx = bbox.getAsNumber(0).floatValue();
410            lly = bbox.getAsNumber(1).floatValue();
411            urx = bbox.getAsNumber(2).floatValue();
412            ury = bbox.getAsNumber(3).floatValue();
413            if (llx > urx) {
414                float t = llx;
415                llx = urx;
416                urx = t;
417            }
418            if (lly > ury) {
419                float t = lly;
420                lly = ury;
421                ury = t;
422            }
423        }
424    }
425
426    private void fillEncoding(PdfName encoding) {
427        if (PdfName.MAC_ROMAN_ENCODING.equals(encoding) || PdfName.WIN_ANSI_ENCODING.equals(encoding)) {
428            byte b[] = new byte[256];
429            for (int k = 0; k < 256; ++k)
430                b[k] = (byte)k;
431            String enc = WINANSI;
432            if (PdfName.MAC_ROMAN_ENCODING.equals(encoding))
433                enc = MACROMAN;
434            String cv = PdfEncodings.convertToString(b, enc);
435            char arr[] = cv.toCharArray();
436            for (int k = 0; k < 256; ++k) {
437                uni2byte.put(arr[k], k);
438            }
439        }
440        else {
441            for (int k = 0; k < 256; ++k) {
442                uni2byte.put(stdEnc[k], k);
443            }
444        }
445    }
446
447    /** Gets the family name of the font. If it is a True Type font
448     * each array element will have {Platform ID, Platform Encoding ID,
449     * Language ID, font name}. The interpretation of this values can be
450     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
451     * For the other fonts the array has a single element with {"", "", "",
452     * font name}.
453     * @return the family name of the font
454     *
455     */
456    @Override
457    public String[][] getFamilyFontName() {
458        return getFullFontName();
459    }
460
461    /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
462     * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>,
463     * <CODE>ITALICANGLE</CODE>, <CODE>BBOXLLX</CODE>, <CODE>BBOXLLY</CODE>, <CODE>BBOXURX</CODE>
464     * and <CODE>BBOXURY</CODE>.
465     * @param key the parameter to be extracted
466     * @param fontSize the font size in points
467     * @return the parameter in points
468     *
469     */
470    @Override
471    public float getFontDescriptor(int key, float fontSize) {
472        if (cjkMirror != null)
473            return cjkMirror.getFontDescriptor(key, fontSize);
474        switch (key) {
475            case AWT_ASCENT:
476            case ASCENT:
477                return Ascender * fontSize / 1000;
478            case CAPHEIGHT:
479                return CapHeight * fontSize / 1000;
480            case AWT_DESCENT:
481            case DESCENT:
482                return Descender * fontSize / 1000;
483            case ITALICANGLE:
484                return ItalicAngle;
485            case BBOXLLX:
486                return llx * fontSize / 1000;
487            case BBOXLLY:
488                return lly * fontSize / 1000;
489            case BBOXURX:
490                return urx * fontSize / 1000;
491            case BBOXURY:
492                return ury * fontSize / 1000;
493            case AWT_LEADING:
494                return 0;
495            case AWT_MAXADVANCE:
496                return (urx - llx) * fontSize / 1000;
497        }
498        return 0;
499    }
500
501    /** Gets the full name of the font. If it is a True Type font
502     * each array element will have {Platform ID, Platform Encoding ID,
503     * Language ID, font name}. The interpretation of this values can be
504     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
505     * For the other fonts the array has a single element with {"", "", "",
506     * font name}.
507     * @return the full name of the font
508     *
509     */
510    @Override
511    public String[][] getFullFontName() {
512        return new String[][]{{"", "", "", fontName}};
513    }
514
515    /** Gets all the entries of the names-table. If it is a True Type font
516     * each array element will have {Name ID, Platform ID, Platform Encoding ID,
517     * Language ID, font name}. The interpretation of this values can be
518     * found in the Open Type specification, chapter 2, in the 'name' table.<br>
519     * For the other fonts the array has a single element with {"4", "", "", "",
520     * font name}.
521     * @return the full name of the font
522     * @since 2.0.8
523     */
524    @Override
525    public String[][] getAllNameEntries() {
526        return new String[][]{{"4", "", "", "", fontName}};
527    }
528
529    /** Gets the kerning between two Unicode chars.
530     * @param char1 the first char
531     * @param char2 the second char
532     * @return the kerning to be applied
533     *
534     */
535    @Override
536    public int getKerning(int char1, int char2) {
537        return 0;
538    }
539
540    /** Gets the postscript font name.
541     * @return the postscript font name
542     *
543     */
544    @Override
545    public String getPostscriptFontName() {
546        return fontName;
547    }
548
549    /** Gets the width from the font according to the Unicode char <CODE>c</CODE>
550     * or the <CODE>name</CODE>. If the <CODE>name</CODE> is null it's a symbolic font.
551     * @param c the unicode char
552     * @param name the glyph name
553     * @return the width of the char
554     *
555     */
556    @Override
557    int getRawWidth(int c, String name) {
558        return 0;
559    }
560
561    /** Checks if the font has any kerning pairs.
562     * @return <CODE>true</CODE> if the font has any kerning pairs
563     *
564     */
565    @Override
566    public boolean hasKernPairs() {
567        return false;
568    }
569
570    /** Outputs to the writer the font dictionaries and streams.
571     * @param writer the writer for this document
572     * @param ref the font indirect reference
573     * @param params several parameters that depend on the font type
574     * @throws IOException on error
575     * @throws DocumentException error in generating the object
576     *
577     */
578    @Override
579    void writeFont(PdfWriter writer, PdfIndirectReference ref, Object[] params) throws DocumentException, IOException {
580    }
581
582    /**
583     * Always returns null.
584     * @return  null
585     * @since   2.1.3
586     */
587    @Override
588    public PdfStream getFullFontStream() {
589        return null;
590    }
591
592    /**
593     * Gets the width of a <CODE>char</CODE> in normalized 1000 units.
594     * @param char1 the unicode <CODE>char</CODE> to get the width of
595     * @return the width in normalized 1000 units
596     */
597    @Override
598    public int getWidth(int char1) {
599        if (cjkMirror != null)
600            return cjkMirror.getWidth(char1);
601        else if (isType0) {
602            int[] ws = metrics.get(Integer.valueOf(char1));
603            if (ws != null)
604                return ws[1];
605            else
606                return 0;
607        }
608        else
609            return super.getWidth(char1);
610    }
611
612    @Override
613    public int getWidth(String text) {
614        if (cjkMirror != null)
615            return cjkMirror.getWidth(text);
616        else if (isType0) {
617            char[] chars = text.toCharArray();
618            int len = chars.length;
619            int total = 0;
620            for (int k = 0; k < len; ++k) {
621                int[] ws = metrics.get(Integer.valueOf(chars[k]));
622                if (ws != null)
623                    total += ws[1];
624            }
625            return total;
626        }
627        else
628            return super.getWidth(text);
629    }
630
631    @Override
632    byte[] convertToBytes(String text) {
633        if (cjkMirror != null)
634            return PdfEncodings.convertToBytes(text, CJKFont.CJK_ENCODING);
635        else if (isType0) {
636            char[] chars = text.toCharArray();
637            int len = chars.length;
638            byte[] b = new byte[len * 2];
639            int bptr = 0;
640            for (int k = 0; k < len; ++k) {
641                int[] ws = metrics.get(Integer.valueOf(chars[k]));
642                if (ws != null) {
643                    int g = ws[0];
644                    b[bptr++] = (byte)(g / 256);
645                    b[bptr++] = (byte)g;
646                }
647            }
648            if (bptr == b.length)
649                return b;
650            else {
651                byte[] nb = new byte[bptr];
652                System.arraycopy(b, 0, nb, 0, bptr);
653                return nb;
654            }
655        }
656        else {
657            char cc[] = text.toCharArray();
658            byte b[] = new byte[cc.length];
659            int ptr = 0;
660            for (int k = 0; k < cc.length; ++k) {
661                if (uni2byte.containsKey(cc[k]))
662                    b[ptr++] = (byte)uni2byte.get(cc[k]);
663            }
664            if (ptr == b.length)
665                return b;
666            else {
667                byte[] b2 = new byte[ptr];
668                System.arraycopy(b, 0, b2, 0, ptr);
669                return b2;
670            }
671        }
672    }
673
674    @Override
675    byte[] convertToBytes(int char1) {
676        if (cjkMirror != null)
677            return PdfEncodings.convertToBytes((char)char1, CJKFont.CJK_ENCODING);
678        else if (isType0) {
679            int[] ws = metrics.get(Integer.valueOf(char1));
680            if (ws != null) {
681                int g = ws[0];
682                return new byte[]{(byte)(g / 256), (byte)g};
683            }
684            else
685                return new byte[0];
686        }
687        else {
688            if (uni2byte.containsKey(char1))
689                return new byte[]{(byte)uni2byte.get(char1)};
690            else
691                return new byte[0];
692        }
693    }
694
695    PdfIndirectReference getIndirectReference() {
696        return refFont;
697    }
698
699    @Override
700    public boolean charExists(int c) {
701        if (cjkMirror != null)
702            return cjkMirror.charExists(c);
703        else if (isType0) {
704            return metrics.containsKey(Integer.valueOf(c));
705        }
706        else
707            return super.charExists(c);
708    }
709
710    /**
711     * Sets the font name that will appear in the pdf font dictionary.
712     * It does nothing in this case as the font is already in the document.
713     * @param name the new font name
714     */
715    @Override
716    public void setPostscriptFontName(String name) {
717    }
718
719    @Override
720    public boolean setKerning(int char1, int char2, int kern) {
721        return false;
722    }
723
724    @Override
725    public int[] getCharBBox(int c) {
726        return null;
727    }
728
729    @Override
730    protected int[] getRawCharBBox(int c, String name) {
731        return null;
732    }
733
734    /**
735     * Exposes the unicode - > CID map that is constructed from the font's encoding
736     * @return the unicode to CID map
737     * @since 2.1.7
738     */
739    IntHashtable getUni2Byte(){
740        return uni2byte;
741    }
742
743    /**
744     * Gets the difference map
745     * @return the difference map
746     * @since 5.0.5
747     */
748    IntHashtable getDiffmap() {
749        return diffmap;
750    }
751}