Open Source Repository

Home /itextpdf/itextpdf-5.1.2 | Repository Home



com/itextpdf/text/pdf/CFFFont.java
/*
 * $Id: CFFFont.java 4784 2011-03-15 08:33:00Z blowagie $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2011 1T3XT BVBA
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: [email protected]
 */

/*
 * Comments by the original author, Sivan Toledo:
 * I created this class in order to add to iText the ability to utilize
 * OpenType fonts with CFF glyphs (these usually have an .otf extension).
 * The CFF font within the CFF table of the OT font might be either a CID
 * or a Type1 font. (CFF fonts may also contain multiple fonts; I do not
 * know if this is allowed in an OT table). The PDF spec, however, only
 * allow a CID font with an Identity-H or Identity-V encoding. Otherwise,
 * you are limited to an 8-bit encoding.
 * Adobe fonts come in both flavors. That is, the OTFs sometimes have
 * a CID CFF inside (for Japanese fonts), and sometimes a Type1 CFF
 * (virtually all the others, Latin/Greek/Cyrillic). So to easily use
 * all the glyphs in the latter, without creating multiple 8-bit encoding,
 * I wrote this class, whose main purpose is to convert a Type1 font inside
 * a CFF container (which might include other fonts) into a CID CFF font
 * that can be directly embeded in the PDF.
 *
 * Limitations of the current version:
 * 1. It does not extract a single CID font from a CFF that contains that
 *    particular CID along with other fonts. The Adobe Japanese OTF's that
 *    I have only have one font in the CFF table, so these can be
 *    embeded in the PDF as is.
 * 2. It does not yet subset fonts.
 * 3. It may or may not work on CFF fonts that are not within OTF's.
 *    I didn't try that. In any case, that would probably only be
 *    useful for subsetting CID fonts, not for CFF Type1 fonts (I don't
 *    think there are any available.
 * I plan to extend the class to support these three features at some
 * future time.
 */

package com.itextpdf.text.pdf;

import java.util.Iterator;
import java.util.LinkedList;

import com.itextpdf.text.ExceptionConverter;

public class CFFFont {

    static final String operatorNames[] {
        "version""Notice""FullName""FamilyName",
        "Weight""FontBBox""BlueValues""OtherBlues",
        "FamilyBlues""FamilyOtherBlues""StdHW""StdVW",
        "UNKNOWN_12""UniqueID""XUID""charset",
        "Encoding""CharStrings""Private""Subrs",
        "defaultWidthX""nominalWidthX""UNKNOWN_22""UNKNOWN_23",
        "UNKNOWN_24""UNKNOWN_25""UNKNOWN_26""UNKNOWN_27",
        "UNKNOWN_28""UNKNOWN_29""UNKNOWN_30""UNKNOWN_31",
        "Copyright""isFixedPitch""ItalicAngle""UnderlinePosition",
        "UnderlineThickness""PaintType""CharstringType""FontMatrix",
        "StrokeWidth""BlueScale""BlueShift""BlueFuzz",
        "StemSnapH""StemSnapV""ForceBold""UNKNOWN_12_15",
        "UNKNOWN_12_16""LanguageGroup""ExpansionFactor""initialRandomSeed",
        "SyntheticBase""PostScript""BaseFontName""BaseFontBlend",
        "UNKNOWN_12_24""UNKNOWN_12_25""UNKNOWN_12_26""UNKNOWN_12_27",
        "UNKNOWN_12_28""UNKNOWN_12_29""ROS""CIDFontVersion",
        "CIDFontRevision""CIDFontType""CIDCount""UIDBase",
        "FDArray""FDSelect""FontName"
    };

    static final String standardStrings[] {
        // Automatically generated from Appendix A of the CFF specification; do
        // not edit. Size should be 391.
        ".notdef""space""exclam""quotedbl""numbersign""dollar",
        "percent""ampersand""quoteright""parenleft""parenright",
        "asterisk""plus""comma""hyphen""period""slash""zero""one",
        "two""three""four""five""six""seven""eight""nine""colon",
        "semicolon""less""equal""greater""question""at""A""B""C",
        "D""E""F""G""H""I""J""K""L""M""N""O""P""Q""R",
        "S""T""U""V""W""X""Y""Z""bracketleft""backslash",
        "bracketright""asciicircum""underscore""quoteleft""a""b""c",
        "d""e""f""g""h""i""j""k""l""m""n""o""p""q""r",
        "s""t""u""v""w""x""y""z""braceleft""bar""braceright",
        "asciitilde""exclamdown""cent""sterling""fraction""yen",
        "florin""section""currency""quotesingle""quotedblleft",
        "guillemotleft""guilsinglleft""guilsinglright""fi""fl""endash",
        "dagger""daggerdbl""periodcentered""paragraph""bullet",
        "quotesinglbase""quotedblbase""quotedblright""guillemotright",
        "ellipsis""perthousand""questiondown""grave""acute""circumflex",
        "tilde""macron""breve""dotaccent""dieresis""ring""cedilla",
        "hungarumlaut""ogonek""caron""emdash""AE""ordfeminine""Lslash",
        "Oslash""OE""ordmasculine""ae""dotlessi""lslash""oslash""oe",
        "germandbls""onesuperior""logicalnot""mu""trademark""Eth",
        "onehalf""plusminus""Thorn""onequarter""divide""brokenbar",
        "degree""thorn""threequarters""twosuperior""registered""minus",
        "eth""multiply""threesuperior""copyright""Aacute""Acircumflex",
        "Adieresis""Agrave""Aring""Atilde""Ccedilla""Eacute",
        "Ecircumflex""Edieresis""Egrave""Iacute""Icircumflex""Idieresis",
        "Igrave""Ntilde""Oacute""Ocircumflex""Odieresis""Ograve",
        "Otilde""Scaron""Uacute""Ucircumflex""Udieresis""Ugrave",
        "Yacute""Ydieresis""Zcaron""aacute""acircumflex""adieresis",
        "agrave""aring""atilde""ccedilla""eacute""ecircumflex",
        "edieresis""egrave""iacute""icircumflex""idieresis""igrave",
        "ntilde""oacute""ocircumflex""odieresis""ograve""otilde",
        "scaron""uacute""ucircumflex""udieresis""ugrave""yacute",
        "ydieresis""zcaron""exclamsmall""Hungarumlautsmall",
        "dollaroldstyle""dollarsuperior""ampersandsmall""Acutesmall",
        "parenleftsuperior""parenrightsuperior""twodotenleader",
        "onedotenleader""zerooldstyle""oneoldstyle""twooldstyle",
        "threeoldstyle""fouroldstyle""fiveoldstyle""sixoldstyle",
        "sevenoldstyle""eightoldstyle""nineoldstyle""commasuperior",
        "threequartersemdash""periodsuperior""questionsmall""asuperior",
        "bsuperior""centsuperior""dsuperior""esuperior""isuperior",
        "lsuperior""msuperior""nsuperior""osuperior""rsuperior",
        "ssuperior""tsuperior""ff""ffi""ffl""parenleftinferior",
        "parenrightinferior""Circumflexsmall""hyphensuperior""Gravesmall",
        "Asmall""Bsmall""Csmall""Dsmall""Esmall""Fsmall""Gsmall",
        "Hsmall""Ismall""Jsmall""Ksmall""Lsmall""Msmall""Nsmall",
        "Osmall""Psmall""Qsmall""Rsmall""Ssmall""Tsmall""Usmall",
        "Vsmall""Wsmall""Xsmall""Ysmall""Zsmall""colonmonetary",
        "onefitted""rupiah""Tildesmall""exclamdownsmall""centoldstyle",
        "Lslashsmall""Scaronsmall""Zcaronsmall""Dieresissmall""Brevesmall",
        "Caronsmall""Dotaccentsmall""Macronsmall""figuredash",
        "hypheninferior""Ogoneksmall""Ringsmall""Cedillasmall",
        "questiondownsmall""oneeighth""threeeighths""fiveeighths",
        "seveneighths""onethird""twothirds""zerosuperior""foursuperior",
        "fivesuperior""sixsuperior""sevensuperior""eightsuperior",
        "ninesuperior""zeroinferior""oneinferior""twoinferior",
        "threeinferior""fourinferior""fiveinferior""sixinferior",
        "seveninferior""eightinferior""nineinferior""centinferior",
        "dollarinferior""periodinferior""commainferior""Agravesmall",
        "Aacutesmall""Acircumflexsmall""Atildesmall""Adieresissmall",
        "Aringsmall""AEsmall""Ccedillasmall""Egravesmall""Eacutesmall",
        "Ecircumflexsmall""Edieresissmall""Igravesmall""Iacutesmall",
        "Icircumflexsmall""Idieresissmall""Ethsmall""Ntildesmall",
        "Ogravesmall""Oacutesmall""Ocircumflexsmall""Otildesmall",
        "Odieresissmall""OEsmall""Oslashsmall""Ugravesmall""Uacutesmall",
        "Ucircumflexsmall""Udieresissmall""Yacutesmall""Thornsmall",
        "Ydieresissmall""001.000""001.001""001.002""001.003""Black",
        "Bold""Book""Light""Medium""Regular""Roman""Semibold"
    };

    //private String[] strings;
    public String getString(char sid) {
        if (sid < standardStrings.lengthreturn standardStrings[sid];
        if (sid >= standardStrings.length+stringOffsets.length-1return null;
        int j = sid - standardStrings.length;
        //java.lang.System.err.println("going for "+j);
        int p = getPosition();
        seek(stringOffsets[j]);
        StringBuffer s = new StringBuffer();
        for (int k=stringOffsets[j]; k<stringOffsets[j+1]; k++) {
            s.append(getCard8());
        }
        seek(p);
        return s.toString();
    }

    char getCard8() {
        try {
            byte i = buf.readByte();
            return (char)(i & 0xff);
        }
        catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }

    char getCard16() {
        try {
            return buf.readChar();
        }
        catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }

    int getOffset(int offSize) {
        int offset = 0;
        for (int i=0; i<offSize; i++) {
            offset *= 256;
            offset += getCard8();
        }
        return offset;
    }

    void seek(int offset) {
        try {
            buf.seek(offset);
        }
        catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }

    short getShort() {
        try {
            return buf.readShort();
        }
        catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }

    int getInt() {
        try {
            return buf.readInt();
        }
        catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }

    int getPosition() {
        try {
            return buf.getFilePointer();
        }
        catch (Exception e) {
            throw new ExceptionConverter(e);
        }
    }
    int nextIndexOffset;
    // read the offsets in the next index
    // data structure, convert to global
    // offsets, and return them.
    // Sets the nextIndexOffset.
    int[] getIndex(int nextIndexOffset) {
        int count, indexOffSize;

        seek(nextIndexOffset);
        count = getCard16();
        int[] offsets = new int[count+1];

        if (count==0) {
            offsets[0= -1;
            nextIndexOffset += 2// TODO death store to local var .. should this be this.nextIndexOffset ?
            return offsets;
        }

        indexOffSize = getCard8();

        for (int j=0; j<=count; j++) {
          //nextIndexOffset = ofset to relative segment
            offsets[j= nextIndexOffset
      //2-> count in the index header. 1->offset size in index header
            2+1
      //offset array size * offset size
            (count+1)*indexOffSize
      //???zero <-> one base
            1
      // read object offset relative to object array base
            + getOffset(indexOffSize);
        }
        //nextIndexOffset = offsets[count];
        return offsets;
    }

    protected String   key;
    protected Object[] args      = new Object[48];
    protected int      arg_count = 0;

    protected void getDictItem() {
        for (int i=0; i<arg_count; i++args[i]=null;
        arg_count = 0;
        key = null;
        boolean gotKey = false;

        while (!gotKey) {
            char b0 = getCard8();
            if (b0 == 29) {
                int item = getInt();
                args[arg_count= Integer.valueOf(item);
                arg_count++;
                //System.err.println(item+" ");
                continue;
            }
            if (b0 == 28) {
                short item = getShort();
                args[arg_count= Integer.valueOf(item);
                arg_count++;
                //System.err.println(item+" ");
                continue;
            }
            if (b0 >= 32 && b0 <= 246) {
                byte item = (byte) (b0-139);
                args[arg_count= Integer.valueOf(item);
                arg_count++;
                //System.err.println(item+" ");
                continue;
            }
            if (b0 >= 247 && b0 <= 250) {
                char b1 = getCard8();
                short item = (short) ((b0-247)*256+b1+108);
                args[arg_count= Integer.valueOf(item);
                arg_count++;
                //System.err.println(item+" ");
                continue;
            }
            if (b0 >= 251 && b0 <= 254) {
                char b1 = getCard8();
                short item = (short) (-(b0-251)*256-b1-108);
                args[arg_count= Integer.valueOf(item);
                arg_count++;
                //System.err.println(item+" ");
                continue;
            }
            if (b0 == 30) {
                StringBuilder item = new StringBuilder("");
                boolean done = false;
                char buffer = 0;
                byte avail = 0;
                int  nibble = 0;
                while (!done) {
                    // get a nibble
                    if (avail==0) { buffer = getCard8(); avail=2}
                    if (avail==1) { nibble = buffer / 16; avail--; }
                    if (avail==2) { nibble = buffer % 16; avail--; }
                    switch (nibble) {
                        case 0xa: item.append("."break;
                        case 0xb: item.append("E"break;
                        case 0xc: item.append("E-")break;
                        case 0xe: item.append("-"break;
                        case 0xf: done=true   break;
                        default:
                            if (nibble >= && nibble <= 9)
                              item.append(String.valueOf(nibble));
                            else {
                                item.append("<NIBBLE ERROR: ").append(nibble).append('>');
                                done = true;
                            }
                            break;
                    }
                }
                args[arg_count= item.toString();
                arg_count++;
                //System.err.println(" real=["+item+"]");
                continue;
            }
            if (b0 <= 21) {
                gotKey=true;
                if (b0 != 12key = operatorNames[b0];
                else key = operatorNames[32 + getCard8()];
                //for (int i=0; i<arg_count; i++)
                //  System.err.print(args[i].toString()+" ");
                //System.err.println(key+" ;");
                continue;
            }
        }
    }

    /** List items for the linked list that builds the new CID font.
     */

    protected static abstract class Item {
        protected int myOffset = -1;
        /** remember the current offset and increment by item's size in bytes. */
        public void increment(int[] currentOffset) {
            myOffset = currentOffset[0];
        }
        /** Emit the byte stream for this item. */
        public void emit(byte[] buffer) {}
        /** Fix up cross references to this item (applies only to markers). */
        public void xref() {}
    }

    protected static abstract class OffsetItem extends Item {
        public int value;
        /** set the value of an offset item that was initially unknown.
         * It will be fixed up latex by a call to xref on some marker.
         */
        public void set(int offset) { this.value = offset; }
    }


    /** A range item.
     */

    protected static final class RangeItem extends Item {
        public int offset, length;
        private RandomAccessFileOrArray buf;
        public RangeItem(RandomAccessFileOrArray buf, int offset, int length) {
            this.offset = offset;
            this.length = length;
            this.buf = buf;
        }
        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= length;
        }
        @Override
        public void emit(byte[] buffer) {
            //System.err.println("range emit offset "+offset+" size="+length);
            try {
                buf.seek(offset);
                for (int i=myOffset; i<myOffset+length; i++)
                    buffer[i= buf.readByte();
            }
            catch (Exception e) {
                throw new ExceptionConverter(e);
            }
            //System.err.println("finished range emit");
        }
    }

    /** An index-offset item for the list.
     * The size denotes the required size in the CFF. A positive
     * value means that we need a specific size in bytes (for offset arrays)
     * and a negative value means that this is a dict item that uses a
     * variable-size representation.
     */
    static protected final class IndexOffsetItem extends OffsetItem {
        public final int size;
        public IndexOffsetItem(int size, int value) {this.size=size; this.value=value;}
        public IndexOffsetItem(int size) {this.size=size; }

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= size;
        }
        @Override
        public void emit(byte[] buffer) {
            int i=0;
            switch (size) {
                case 4:
                    buffer[myOffset+i(byte) (value >>> 24 0xff);
                    i++;
                case 3:
                    buffer[myOffset+i(byte) (value >>> 16 0xff);
                    i++;
                case 2:
                    buffer[myOffset+i(byte) (value >>>  0xff);
                    i++;
                case 1:
                    buffer[myOffset+i(byte) (value >>>  0xff);
                    i++;
            }
            /*
            int mask = 0xff;
            for (int i=size-1; i>=0; i--) {
                buffer[myOffset+i] = (byte) (value & mask);
                mask <<= 8;
            }
             */
        }
    }

    static protected final class IndexBaseItem extends Item {
        public IndexBaseItem() {}
    }

    static protected final class IndexMarkerItem extends Item {
        private OffsetItem offItem;
        private IndexBaseItem indexBase;
        public IndexMarkerItem(OffsetItem offItem, IndexBaseItem indexBase) {
            this.offItem   = offItem;
            this.indexBase = indexBase;
        }
        @Override
        public void xref() {
            //System.err.println("index marker item, base="+indexBase.myOffset+" my="+this.myOffset);
            offItem.set(this.myOffset-indexBase.myOffset+1);
        }
    }
    /**
     * TODO To change the template for this generated type comment go to
     * Window - Preferences - Java - Code Generation - Code and Comments
     */
    static protected final class SubrMarkerItem extends Item {
        private OffsetItem offItem;
        private IndexBaseItem indexBase;
        public SubrMarkerItem(OffsetItem offItem, IndexBaseItem indexBase) {
            this.offItem   = offItem;
            this.indexBase = indexBase;
        }
        @Override
        public void xref() {
            //System.err.println("index marker item, base="+indexBase.myOffset+" my="+this.myOffset);
            offItem.set(this.myOffset-indexBase.myOffset);
        }
    }


    /** an unknown offset in a dictionary for the list.
     * We will fix up the offset later; for now, assume it's large.
     */
    static protected final class DictOffsetItem extends OffsetItem {
        public final int size;
        public DictOffsetItem() {this.size=5}

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= size;
        }
        // this is incomplete!
        @Override
        public void emit(byte[] buffer) {
            if (size==5) {
                buffer[myOffset]   29;
                buffer[myOffset+1(byte) (value >>> 24 0xff);
                buffer[myOffset+2(byte) (value >>> 16 0xff);
                buffer[myOffset+3(byte) (value >>>  0xff);
                buffer[myOffset+4(byte) (value >>>  0xff);
            }
        }
    }

  /** Card24 item.
     */

    static protected final class UInt24Item extends Item {
        public int value;
        public UInt24Item(int value) {this.value=value;}

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= 3;
        }
        // this is incomplete!
        @Override
        public void emit(byte[] buffer) {
          buffer[myOffset+0(byte) (value >>> 16 0xff);
            buffer[myOffset+1(byte) (value >>> 0xff);
            buffer[myOffset+2(byte) (value >>> 0xff);
        }
    }

    /** Card32 item.
     */

    static protected final class UInt32Item extends Item {
        public int value;
        public UInt32Item(int value) {this.value=value;}

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= 4;
        }
        // this is incomplete!
        @Override
        public void emit(byte[] buffer) {
          buffer[myOffset+0(byte) (value >>> 24 0xff);
          buffer[myOffset+1(byte) (value >>> 16 0xff);
            buffer[myOffset+2(byte) (value >>> 0xff);
            buffer[myOffset+3(byte) (value >>> 0xff);
        }
    }

    /** A SID or Card16 item.
     */

    static protected final class UInt16Item extends Item {
        public char value;
        public UInt16Item(char value) {this.value=value;}

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= 2;
        }
        // this is incomplete!
        @Override
        public void emit(byte[] buffer) {
            buffer[myOffset+0(byte) (value >>> 0xff);
            buffer[myOffset+1(byte) (value >>> 0xff);
        }
    }

    /** A Card8 item.
     */

    static protected final class UInt8Item extends Item {
        public char value;
        public UInt8Item(char value) {this.value=value;}

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= 1;
        }
        // this is incomplete!
        @Override
        public void emit(byte[] buffer) {
            buffer[myOffset+0(byte) (value >>> 0xff);
        }
    }

    static protected final class StringItem extends Item {
        public String s;
        public StringItem(String s) {this.s=s;}

        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= s.length();
        }
        @Override
        public void emit(byte[] buffer) {
            for (int i=0; i<s.length(); i++)
                buffer[myOffset+i(byte) (s.charAt(i0xff);
        }
    }


    /** A dictionary number on the list.
     * This implementation is inefficient: it doesn't use the variable-length
     * representation.
     */

    static protected final class DictNumberItem extends Item {
        public final int value;
        public int size = 5;
        public DictNumberItem(int value) {this.value=value;}
        @Override
        public void increment(int[] currentOffset) {
            super.increment(currentOffset);
            currentOffset[0+= size;
        }
        // this is incomplete!
        @Override
        public void emit(byte[] buffer) {
            if (size==5) {
                buffer[myOffset]   29;
                buffer[myOffset+1(byte) (value >>> 24 0xff);
                buffer[myOffset+2(byte) (value >>> 16 0xff);
                buffer[myOffset+3(byte) (value >>>  0xff);
                buffer[myOffset+4(byte) (value >>>  0xff);
            }
        }
    }

    /** An offset-marker item for the list.
     * It is used to mark an offset and to set the offset list item.
     */

    static protected final class MarkerItem extends Item {
        OffsetItem p;
        public MarkerItem(OffsetItem pointerToMarker) {p=pointerToMarker;}
        @Override
        public void xref() {
            p.set(this.myOffset);
        }
    }

    /** a utility that creates a range item for an entire index
     *
     @param indexOffset where the index is
     @return a range item representing the entire index
     */

    protected RangeItem getEntireIndexRange(int indexOffset) {
        seek(indexOffset);
        int count = getCard16();
        if (count==0) {
            return new RangeItem(buf,indexOffset,2);
        else {
            int indexOffSize = getCard8();
            seek(indexOffset+2+1+count*indexOffSize);
            int size = getOffset(indexOffSize)-1;
            return new RangeItem(buf,indexOffset,
            2+1+(count+1)*indexOffSize+size);
        }
    }


    /** get a single CID font. The PDF architecture (1.4)
     * supports 16-bit strings only with CID CFF fonts, not
     * in Type-1 CFF fonts, so we convert the font to CID if
     * it is in the Type-1 format.
     * Two other tasks that we need to do are to select
     * only a single font from the CFF package (this again is
     * a PDF restriction) and to subset the CharStrings glyph
     * description.
     */


    public byte[] getCID(String fontName)
    //throws java.io.FileNotFoundException
    {
        int j;
        for (j=0; j<fonts.length; j++)
            if (fontName.equals(fonts[j].name)) break;
        if (j==fonts.lengthreturn null;

        LinkedList<Item> l = new LinkedList<Item>();

        // copy the header

        seek(0);

        int major = getCard8();
        int minor = getCard8();
        int hdrSize = getCard8();
        int offSize = getCard8();
        nextIndexOffset = hdrSize;

        l.addLast(new RangeItem(buf,0,hdrSize));

        int nglyphs=-1, nstrings=-1;
        if ! fonts[j].isCID ) {
            // count the glyphs
            seek(fonts[j].charstringsOffset);
            nglyphs = getCard16();
            seek(stringIndexOffset);
            nstrings = getCard16()+standardStrings.length;
            //System.err.println("number of glyphs = "+nglyphs);
        }

        // create a name index

        l.addLast(new UInt16Item((char)1))// count
        l.addLast(new UInt8Item((char)1))// offSize
        l.addLast(new UInt8Item((char)1))// first offset
        l.addLast(new UInt8Item((char)( 1+fonts[j].name.length() )));
        l.addLast(new StringItem(fonts[j].name));

        // create the topdict Index


        l.addLast(new UInt16Item((char)1))// count
        l.addLast(new UInt8Item((char)2))// offSize
        l.addLast(new UInt16Item((char)1))// first offset
        OffsetItem topdictIndex1Ref = new IndexOffsetItem(2);
        l.addLast(topdictIndex1Ref);
        IndexBaseItem topdictBase = new IndexBaseItem();
        l.addLast(topdictBase);

        /*
        int maxTopdictLen = (topdictOffsets[j+1]-topdictOffsets[j])
                            + 9*2 // at most 9 new keys
                            + 8*5 // 8 new integer arguments
                            + 3*2;// 3 new SID arguments
         */

        //int    topdictNext = 0;
        //byte[] topdict = new byte[maxTopdictLen];

        OffsetItem charsetRef     = new DictOffsetItem();
        OffsetItem charstringsRef = new DictOffsetItem();
        OffsetItem fdarrayRef     = new DictOffsetItem();
        OffsetItem fdselectRef    = new DictOffsetItem();

        if !fonts[j].isCID ) {
            // create a ROS key
            l.addLast(new DictNumberItem(nstrings));
            l.addLast(new DictNumberItem(nstrings+1));
            l.addLast(new DictNumberItem(0));
            l.addLast(new UInt8Item((char)12));
            l.addLast(new UInt8Item((char)30));
            // create a CIDCount key
            l.addLast(new DictNumberItem(nglyphs));
            l.addLast(new UInt8Item((char)12));
            l.addLast(new UInt8Item((char)34));
            // What about UIDBase (12,35)? Don't know what is it.
            // I don't think we need FontName; the font I looked at didn't have it.
        }

        // create an FDArray key
        l.addLast(fdarrayRef);
        l.addLast(new UInt8Item((char)12));
        l.addLast(new UInt8Item((char)36));
        // create an FDSelect key
        l.addLast(fdselectRef);
        l.addLast(new UInt8Item((char)12));
        l.addLast(new UInt8Item((char)37));
        // create an charset key
        l.addLast(charsetRef);
        l.addLast(new UInt8Item((char)15));
        // create a CharStrings key
        l.addLast(charstringsRef);
        l.addLast(new UInt8Item((char)17));

        seek(topdictOffsets[j]);
        while (getPosition() < topdictOffsets[j+1]) {
            int p1 = getPosition();
            getDictItem();
            int p2 = getPosition();
            if (key=="Encoding"
            || key=="Private"
            || key=="FDSelect"
            || key=="FDArray"
            || key=="charset"
            || key=="CharStrings"
            ) {
                // just drop them
            else {
                l.add(new RangeItem(buf,p1,p2-p1));
            }
        }

        l.addLast(new IndexMarkerItem(topdictIndex1Ref,topdictBase));

        // Copy the string index and append new strings.
        // We need 3 more strings: Registry, Ordering, and a FontName for one FD.
        // The total length is at most "Adobe"+"Identity"+63 = 76

        if (fonts[j].isCID) {
            l.addLast(getEntireIndexRange(stringIndexOffset));
        else {
            String fdFontName = fonts[j].name+"-OneRange";
            if (fdFontName.length() 127)
                fdFontName = fdFontName.substring(0,127);
            String extraStrings = "Adobe"+"Identity"+fdFontName;

            int origStringsLen = stringOffsets[stringOffsets.length-1]
            - stringOffsets[0];
            int stringsBaseOffset = stringOffsets[0]-1;

            byte stringsIndexOffSize;
            if (origStringsLen+extraStrings.length() <= 0xffstringsIndexOffSize = 1;
            else if (origStringsLen+extraStrings.length() <= 0xffffstringsIndexOffSize = 2;
            else if (origStringsLen+extraStrings.length() <= 0xffffffstringsIndexOffSize = 3;
            else stringsIndexOffSize = 4;

            l.addLast(new UInt16Item((char)(stringOffsets.length-1+3)))// count
            l.addLast(new UInt8Item((char)stringsIndexOffSize))// offSize
            for (int stringOffset : stringOffsets)
                l.addLast(new IndexOffsetItem(stringsIndexOffSize,
                stringOffset-stringsBaseOffset));
            int currentStringsOffset = stringOffsets[stringOffsets.length-1]
            - stringsBaseOffset;
            //l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            currentStringsOffset += "Adobe".length();
            l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            currentStringsOffset += "Identity".length();
            l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));
            currentStringsOffset += fdFontName.length();
            l.addLast(new IndexOffsetItem(stringsIndexOffSize,currentStringsOffset));

            l.addLast(new RangeItem(buf,stringOffsets[0],origStringsLen));
            l.addLast(new StringItem(extraStrings));
        }

        // copy the global subroutine index

        l.addLast(getEntireIndexRange(gsubrIndexOffset));

        // deal with fdarray, fdselect, and the font descriptors

        if (fonts[j].isCID) {
            // copy the FDArray, FDSelect, charset
        else {
            // create FDSelect
            l.addLast(new MarkerItem(fdselectRef));
            l.addLast(new UInt8Item((char)3))// format identifier
            l.addLast(new UInt16Item((char)1))// nRanges

            l.addLast(new UInt16Item((char)0))// Range[0].firstGlyph
            l.addLast(new UInt8Item((char)0))// Range[0].fd

            l.addLast(new UInt16Item((char)nglyphs))// sentinel

            // recreate a new charset
            // This format is suitable only for fonts without subsetting

            l.addLast(new MarkerItem(charsetRef));
            l.addLast(new UInt8Item((char)2))// format identifier

            l.addLast(new UInt16Item((char)1))// first glyph in range (ignore .notdef)
            l.addLast(new UInt16Item((char)(nglyphs-1)))// nLeft
            // now all are covered, the data structure is complete.

            // create a font dict index (fdarray)

            l.addLast(new MarkerItem(fdarrayRef));
            l.addLast(new UInt16Item((char)1));
            l.addLast(new UInt8Item((char)1))// offSize
            l.addLast(new UInt8Item((char)1))// first offset

            OffsetItem privateIndex1Ref = new IndexOffsetItem(1);
            l.addLast(privateIndex1Ref);
            IndexBaseItem privateBase = new IndexBaseItem();
            l.addLast(privateBase);

            // looking at the PS that acrobat generates from a PDF with
            // a CFF opentype font embedded with an identity-H encoding,
            // it seems that it does not need a FontName.
            //l.addLast(new DictNumberItem((standardStrings.length+(stringOffsets.length-1)+2)));
            //l.addLast(new UInt8Item((char)12));
            //l.addLast(new UInt8Item((char)38)); // FontName

            l.addLast(new DictNumberItem(fonts[j].privateLength));
            OffsetItem privateRef = new DictOffsetItem();
            l.addLast(privateRef);
            l.addLast(new UInt8Item((char)18))// Private

            l.addLast(new IndexMarkerItem(privateIndex1Ref,privateBase));

            // copy the private index & local subroutines

            l.addLast(new MarkerItem(privateRef));
            // copy the private dict and the local subroutines.
            // the length of the private dict seems to NOT include
            // the local subroutines.
            l.addLast(new RangeItem(buf,fonts[j].privateOffset,fonts[j].privateLength));
            if (fonts[j].privateSubrs >= 0) {
                //System.err.println("has subrs="+fonts[j].privateSubrs+" ,len="+fonts[j].privateLength);
                l.addLast(getEntireIndexRange(fonts[j].privateSubrs));
            }
        }

        // copy the charstring index

        l.addLast(new MarkerItem(charstringsRef));
        l.addLast(getEntireIndexRange(fonts[j].charstringsOffset));

        // now create the new CFF font

        int[] currentOffset = new int[1];
        currentOffset[00;

        Iterator<Item> listIter = l.iterator();
        while listIter.hasNext() ) {
            Item item = listIter.next();
            item.increment(currentOffset);
        }

        listIter = l.iterator();
        while listIter.hasNext() ) {
            Item item = listIter.next();
            item.xref();
        }

        int size = currentOffset[0];
        byte[] b = new byte[size];

        listIter = l.iterator();
        while listIter.hasNext() ) {
            Item item = listIter.next();
            item.emit(b);
        }

        return b;
    }


    public boolean isCID(String fontName) {
        int j;
        for (j=0; j<fonts.length; j++)
            if (fontName.equals(fonts[j].name)) return fonts[j].isCID;
        return false;
    }

    public boolean exists(String fontName) {
        int j;
        for (j=0; j<fonts.length; j++)
            if (fontName.equals(fonts[j].name)) return true;
        return false;
    }


    public String[] getNames() {
        String[] names = new Stringfonts.length ];
        for (int i=0; i<fonts.length; i++)
            names[i= fonts[i].name;
        return names;
    }
    /**
     * A random Access File or an array
     */
    protected RandomAccessFileOrArray buf;
    private int offSize;

    protected int nameIndexOffset;
    protected int topdictIndexOffset;
    protected int stringIndexOffset;
    protected int gsubrIndexOffset;
    protected int[] nameOffsets;
    protected int[] topdictOffsets;
    protected int[] stringOffsets;
    protected int[] gsubrOffsets;

    /**
     * TODO Changed from private to protected by Ygal&Oren
     */
    protected final class Font {
        public String    name;
        public String    fullName;
        public boolean   isCID = false;
        public int       privateOffset     = -1// only if not CID
        public int       privateLength     = -1// only if not CID
        public int       privateSubrs      = -1;
        public int       charstringsOffset = -1;
        public int       encodingOffset    = -1;
        public int       charsetOffset     = -1;
        public int       fdarrayOffset     = -1// only if CID
        public int       fdselectOffset    = -1// only if CID
        public int[]     fdprivateOffsets;
        public int[]     fdprivateLengths;
        public int[]     fdprivateSubrs;

        // Added by Oren & Ygal
        public int nglyphs;
        public int nstrings;
        public int CharsetLength;
        public int[]    charstringsOffsets;
        public int[]    charset;
        public int[]   FDSelect;
        public int FDSelectLength;
        public int FDSelectFormat;
        public int     CharstringType = 2;
        public int FDArrayCount;
        public int FDArrayOffsize;
        public int[] FDArrayOffsets;
        public int[] PrivateSubrsOffset;
        public int[][] PrivateSubrsOffsetsArray;
        public int[]       SubrsOffsets;
    }
    // Changed from private to protected by Ygal&Oren
    protected Font[] fonts;

    public CFFFont(RandomAccessFileOrArray inputbuffer) {

        //System.err.println("CFF: nStdString = "+standardStrings.length);
        buf = inputbuffer;
        seek(0);

        int major, minor;
        major = getCard8();
        minor = getCard8();

        //System.err.println("CFF Major-Minor = "+major+"-"+minor);

        int hdrSize = getCard8();

        offSize = getCard8();

        //System.err.println("offSize = "+offSize);

        //int count, indexOffSize, indexOffset, nextOffset;

        nameIndexOffset    = hdrSize;
        nameOffsets        = getIndex(nameIndexOffset);
        topdictIndexOffset = nameOffsets[nameOffsets.length-1];
        topdictOffsets     = getIndex(topdictIndexOffset);
        stringIndexOffset  = topdictOffsets[topdictOffsets.length-1];
        stringOffsets      = getIndex(stringIndexOffset);
        gsubrIndexOffset   = stringOffsets[stringOffsets.length-1];
        gsubrOffsets       = getIndex(gsubrIndexOffset);

        fonts = new Font[nameOffsets.length-1];

        // now get the name index

        /*
        names             = new String[nfonts];
        privateOffset     = new int[nfonts];
        charsetOffset     = new int[nfonts];
        encodingOffset    = new int[nfonts];
        charstringsOffset = new int[nfonts];
        fdarrayOffset     = new int[nfonts];
        fdselectOffset    = new int[nfonts];
         */

        for (int j=0; j<nameOffsets.length-1; j++) {
            fonts[jnew Font();
            seek(nameOffsets[j]);
            fonts[j].name = "";
            for (int k=nameOffsets[j]; k<nameOffsets[j+1]; k++) {
                fonts[j].name += getCard8();
            }
            //System.err.println("name["+j+"]=<"+fonts[j].name+">");
        }

        // string index

        //strings = new String[stringOffsets.length-1];
        /*
        System.err.println("std strings = "+standardStrings.length);
        System.err.println("fnt strings = "+(stringOffsets.length-1));
        for (char j=0; j<standardStrings.length+(stringOffsets.length-1); j++) {
            //seek(stringOffsets[j]);
            //strings[j] = "";
            //for (int k=stringOffsets[j]; k<stringOffsets[j+1]; k++) {
            //  strings[j] += (char)getCard8();
            //}
            System.err.println("j="+(int)j+" <? "+(standardStrings.length+(stringOffsets.length-1)));
            System.err.println("strings["+(int)j+"]=<"+getString(j)+">");
        }
         */

        // top dict

        for (int j=0; j<topdictOffsets.length-1; j++) {
            seek(topdictOffsets[j]);
            while (getPosition() < topdictOffsets[j+1]) {
                getDictItem();
                if (key=="FullName") {
                    //System.err.println("getting fullname sid = "+((Integer)args[0]).intValue());
                    fonts[j].fullName = getString((char)((Integer)args[0]).intValue());
                    //System.err.println("got it");
                else if (key=="ROS")
                    fonts[j].isCID = true;
                else if (key=="Private") {
                    fonts[j].privateLength  = ((Integer)args[0]).intValue();
                    fonts[j].privateOffset  = ((Integer)args[1]).intValue();
                }
                else if (key=="charset"){
                    fonts[j].charsetOffset = ((Integer)args[0]).intValue();

                }
//                else if (key=="Encoding"){
//                    int encOffset = ((Integer)args[0]).intValue();
//                    if (encOffset > 0) {
//                        fonts[j].encodingOffset = encOffset;
//                        ReadEncoding(fonts[j].encodingOffset);
//                    }
//                }
                else if (key=="CharStrings") {
                    fonts[j].charstringsOffset = ((Integer)args[0]).intValue();
                    //System.err.println("charstrings "+fonts[j].charstringsOffset);
                    // Added by Oren & Ygal
                    int p = getPosition();
                    fonts[j].charstringsOffsets = getIndex(fonts[j].charstringsOffset);
                    seek(p);
                else if (key=="FDArray")
                    fonts[j].fdarrayOffset = ((Integer)args[0]).intValue();
                else if (key=="FDSelect")
                    fonts[j].fdselectOffset = ((Integer)args[0]).intValue();
                else if (key=="CharstringType")
                  fonts[j].CharstringType = ((Integer)args[0]).intValue();
            }

            // private dict
            if (fonts[j].privateOffset >= 0) {
                //System.err.println("PRIVATE::");
                seek(fonts[j].privateOffset);
                while (getPosition() < fonts[j].privateOffset+fonts[j].privateLength) {
                    getDictItem();
                    if (key=="Subrs")
                      //Add the private offset to the lsubrs since the offset is
                      // relative to the beginning of the PrivateDict
                        fonts[j].privateSubrs = ((Integer)args[0]).intValue()+fonts[j].privateOffset;
                }
            }

            // fdarray index
            if (fonts[j].fdarrayOffset >= 0) {
                int[] fdarrayOffsets = getIndex(fonts[j].fdarrayOffset);

                fonts[j].fdprivateOffsets = new int[fdarrayOffsets.length-1];
                fonts[j].fdprivateLengths = new int[fdarrayOffsets.length-1];

                //System.err.println("FD Font::");

                for (int k=0; k<fdarrayOffsets.length-1; k++) {
                    seek(fdarrayOffsets[k]);
                    while (getPosition() < fdarrayOffsets[k+1]) {
                        getDictItem();
                        if (key=="Private") {
                            fonts[j].fdprivateLengths[k]  ((Integer)args[0]).intValue();
                            fonts[j].fdprivateOffsets[k]  ((Integer)args[1]).intValue();
                        }
                    }
                }
            }
        }
        //System.err.println("CFF: done");
    }

    // ADDED BY Oren & Ygal

    void ReadEncoding(int nextIndexOffset){
      int format;
      seek(nextIndexOffset);
      format = getCard8();
    }
}