]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2252: OpenType CFF support for FOP; patch submitted by Robert Meyer (eclipse...
authorLuis Bernardo <lbernardo@apache.org>
Wed, 14 Aug 2013 22:42:28 +0000 (22:42 +0000)
committerLuis Bernardo <lbernardo@apache.org>
Wed, 14 Aug 2013 22:42:28 +0000 (22:42 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1514079 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/fonts/cff/CFFDataReader.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFEncryptionJCE.java

diff --git a/src/java/org/apache/fop/fonts/cff/CFFDataReader.java b/src/java/org/apache/fop/fonts/cff/CFFDataReader.java
new file mode 100644 (file)
index 0000000..66126fb
--- /dev/null
@@ -0,0 +1,927 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts.cff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.fontbox.cff.CFFDataInput;
+import org.apache.fontbox.cff.CFFOperator;
+
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.OTFFile;
+
+/**
+ * A class to read the CFF data from an OTF CFF font file.
+ */
+public class CFFDataReader {
+    private CFFDataInput cffData;
+
+    private byte[] header;
+    private CFFIndexData nameIndex;
+    private CFFIndexData topDICTIndex;
+    private CFFIndexData stringIndex;
+    private CFFIndexData charStringIndex;
+    private CFFIndexData globalIndexSubr;
+    private CFFIndexData localIndexSubr;
+    private CustomEncoding encoding;
+    private FDSelect fdSelect;
+    private List<FontDict> fdFonts;
+
+    private static final int DOUBLE_BYTE_OPERATOR = 12;
+    private static final int NUM_STANDARD_STRINGS = 391;
+
+    /** Commonly used parsed dictionaries */
+    private LinkedHashMap<String, DICTEntry> topDict;
+
+    public CFFDataReader() {
+
+    }
+
+    /**
+     * Constructor for the CFF data reader which accepts the CFF byte data
+     * as an argument.
+     * @param cffDataArray A byte array which holds the CFF data
+     */
+    public CFFDataReader(byte[] cffDataArray) throws IOException {
+        cffData = new CFFDataInput(cffDataArray);
+        readCFFData();
+    }
+
+    /**
+     * Constructor for the CFF data reader which accepts a FontFileReader object
+     * which points to the original font file as an argument.
+     * @param fontFile The font file as represented by a FontFileReader object
+     */
+    public CFFDataReader(FontFileReader fontFile) throws IOException {
+        cffData = new CFFDataInput(OTFFile.getCFFData(fontFile));
+        readCFFData();
+    }
+
+    private void readCFFData() throws IOException {
+        header = readHeader();
+        nameIndex = readIndex();
+        topDICTIndex = readIndex();
+        topDict = parseDictData(topDICTIndex.getData());
+        stringIndex = readIndex();
+        globalIndexSubr = readIndex();
+        charStringIndex = readCharStringIndex();
+        encoding = readEncoding();
+        fdSelect = readFDSelect();
+        localIndexSubr = readLocalIndexSubrs();
+        fdFonts = parseCIDData();
+    }
+
+    public Map<String, DICTEntry> getPrivateDict(DICTEntry privateEntry) throws IOException {
+        return parseDictData(getPrivateDictBytes(privateEntry));
+    }
+
+    public byte[] getPrivateDictBytes(DICTEntry privateEntry) throws IOException {
+        int privateLength = privateEntry.getOperands().get(0).intValue();
+        int privateOffset = privateEntry.getOperands().get(1).intValue();
+        return getCFFOffsetBytes(privateOffset, privateLength);
+    }
+
+    /**
+     * Retrieves a number of bytes from the CFF data stream
+     * @param offset The offset of the bytes to retrieve
+     * @param length The number of bytes to retrieve
+     * @return Returns a byte array of requested bytes
+     * @throws IOException Throws an IO Exception if an error occurs
+     */
+    private byte[] getCFFOffsetBytes(int offset, int length) throws IOException {
+        cffData.setPosition(offset);
+        return cffData.readBytes(length);
+    }
+
+    /**
+     * Parses the dictionary data and returns a map of objects for each entry
+     * @param dictData The data for the dictionary data
+     * @return Returns a map of type DICTEntry identified by the operand name
+     * @throws IOException Throws an IO Exception if an error occurs
+     */
+    public LinkedHashMap<String, DICTEntry> parseDictData(byte[] dictData) throws IOException {
+        LinkedHashMap<String, DICTEntry> dictEntries = new LinkedHashMap<String, DICTEntry>();
+        List<Number> operands = new ArrayList<Number>();
+        List<Integer> operandLengths = new ArrayList<Integer>();
+        int lastOperandLength = 0;
+        for (int i = 0; i < dictData.length; i++) {
+            int readByte = dictData[i] & 0xFF;
+            if (readByte < 28) {
+                int[] operator = new int[(readByte == DOUBLE_BYTE_OPERATOR) ? 2 : 1];
+                if (readByte == DOUBLE_BYTE_OPERATOR) {
+                    operator[0] = dictData[i];
+                    operator[1] = dictData[i + 1];
+                    i++;
+                } else {
+                    operator[0] = dictData[i];
+                }
+                String operatorName = "";
+                CFFOperator tempOp = null;
+                if (operator.length > 1) {
+                    tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0], operator[1]));
+                } else {
+                    tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0]));
+                }
+                if (tempOp != null) {
+                    operatorName = tempOp.getName();
+                }
+                DICTEntry newEntry = new DICTEntry();
+                newEntry.setOperator(operator);
+                newEntry.setOperands(new ArrayList<Number>(operands));
+                newEntry.setOperatorName(operatorName);
+                newEntry.setOffset(i - lastOperandLength);
+                newEntry.setOperandLength(lastOperandLength);
+                newEntry.setOperandLengths(new ArrayList<Integer>(operandLengths));
+                byte[] byteData = new byte[lastOperandLength + operator.length];
+                System.arraycopy(dictData, i - operator.length - (lastOperandLength - 1),
+                        byteData, 0, operator.length + lastOperandLength);
+                newEntry.setByteData(byteData);
+                dictEntries.put(operatorName, newEntry);
+                operands.clear();
+                operandLengths.clear();
+                lastOperandLength = 0;
+            } else {
+                if (readByte >= 32 && readByte <= 246) {
+                    operands.add(readByte - 139);
+                    lastOperandLength += 1;
+                    operandLengths.add(1);
+                } else if (readByte >= 247 && readByte <= 250) {
+                    operands.add((readByte - 247) * 256 + (dictData[i + 1] & 0xFF) + 108);
+                    lastOperandLength += 2;
+                    operandLengths.add(2);
+                    i++;
+                } else if (readByte >= 251 && readByte <= 254) {
+                    operands.add(-(readByte - 251) * 256 - (dictData[i + 1] & 0xFF) - 108);
+                    lastOperandLength += 2;
+                    operandLengths.add(2);
+                    i++;
+                } else if (readByte == 28) {
+                    operands.add((dictData[i + 1] & 0xFF) << 8 | (dictData[i + 2] & 0xFF));
+                    lastOperandLength += 3;
+                    operandLengths.add(3);
+                    i += 2;
+                } else if (readByte == 29) {
+                    operands.add((dictData[i + 1] & 0xFF) << 24 | (dictData[i + 2] & 0xFF) << 16
+                            | (dictData[i + 3] & 0xFF) << 8 | (dictData[i + 4] & 0xFF));
+                    lastOperandLength += 5;
+                    operandLengths.add(5);
+                    i += 4;
+                } else if (readByte == 30) {
+                    boolean terminatorFound = false;
+                    StringBuilder realNumber = new StringBuilder();
+                    int byteCount = 1;
+                    do {
+                        byte nibblesByte = dictData[++i];
+                        byteCount++;
+                        terminatorFound = readNibble(realNumber, (nibblesByte >> 4) & 0x0F);
+                        if (!terminatorFound) {
+                            terminatorFound = readNibble(realNumber, nibblesByte & 0x0F);
+                        }
+                    } while (!terminatorFound);
+                    operands.add(Double.valueOf(realNumber.toString()));
+                    lastOperandLength += byteCount;
+                    operandLengths.add(byteCount);
+                }
+            }
+        }
+        return dictEntries;
+    }
+
+    private boolean readNibble(StringBuilder realNumber, int nibble) {
+        if (nibble <= 0x9) {
+            realNumber.append(nibble);
+        } else {
+            switch (nibble) {
+            case 0xa: realNumber.append("."); break;
+            case 0xb: realNumber.append("E"); break;
+            case 0xc: realNumber.append("E-"); break;
+            case 0xd: break;
+            case 0xe: realNumber.append("-"); break;
+            case 0xf: return true;
+            default:  throw new AssertionError("Unexpected nibble value");
+            }
+        }
+        return false;
+    }
+
+    /**
+     * A class containing data for a dictionary entry
+     */
+    public static class DICTEntry {
+        private int[] operator;
+        private List<Number> operands;
+        private List<Integer> operandLengths;
+        private String operatorName;
+        private int offset;
+        private int operandLength;
+        private byte[] data = new byte[0];
+
+        public void setOperator(int[] operator) {
+            this.operator = operator;
+        }
+
+        public int[] getOperator() {
+            return this.operator;
+        }
+
+        public void setOperands(List<Number> operands) {
+            this.operands = operands;
+        }
+
+        public List<Number> getOperands() {
+            return this.operands;
+        }
+
+        public void setOperatorName(String operatorName) {
+            this.operatorName = operatorName;
+        }
+
+        public String getOperatorName() {
+            return this.operatorName;
+        }
+
+        public void setOffset(int offset) {
+            this.offset = offset;
+        }
+
+        public int getOffset() {
+            return this.offset;
+        }
+
+        public void setOperandLength(int operandLength) {
+            this.operandLength = operandLength;
+        }
+
+        public int getOperandLength() {
+            return this.operandLength;
+        }
+
+        public void setByteData(byte[] data) {
+            this.data = data.clone();
+        }
+
+        public byte[] getByteData() {
+            return data.clone();
+        }
+
+        public void setOperandLengths(List<Integer> operandLengths) {
+            this.operandLengths = operandLengths;
+        }
+
+        public List<Integer> getOperandLengths() {
+            return operandLengths;
+        }
+    }
+
+    private byte[] readHeader() throws IOException {
+        //Read known header
+        byte[] fixedHeader = cffData.readBytes(4);
+        int hdrSize = (fixedHeader[2] & 0xFF);
+        byte[] extra = cffData.readBytes(hdrSize - 4);
+        byte[] header = new byte[hdrSize];
+        for (int i = 0; i < fixedHeader.length; i++) {
+            header[i] = fixedHeader[i];
+        }
+        for (int i = 4; i < extra.length; i++) {
+            header[i] = extra[i - 4];
+        }
+        return header;
+    }
+
+    /**
+     * Reads a CFF index object are the specified offset position
+     * @param offset The position of the index object to read
+     * @return Returns an object representing the index
+     * @throws IOException Throws an IO Exception if an error occurs
+     */
+    public CFFIndexData readIndex(int offset) throws IOException {
+        cffData.setPosition(offset);
+        return readIndex();
+    }
+
+    private CFFIndexData readIndex() throws IOException {
+        return readIndex(cffData);
+    }
+
+    /**
+     * Reads an index from the current position of the CFFDataInput object
+     * @param input The object holding the CFF byte data
+     * @return Returns an object representing the index
+     * @throws IOException Throws an IO Exception if an error occurs
+     */
+    public CFFIndexData readIndex(CFFDataInput input) throws IOException {
+        CFFIndexData nameIndex = new CFFIndexData();
+        if (input != null) {
+            int origPos = input.getPosition();
+            nameIndex.parseIndexHeader(input);
+            int tableSize = input.getPosition() - origPos;
+            nameIndex.setByteData(input.getPosition() - tableSize, tableSize);
+        }
+        return nameIndex;
+    }
+
+    /**
+     * Retrieves the SID for the given GID object
+     * @param charsetOffset The offset of the charset data
+     * @param GID The GID for which to retrieve the SID
+     * @return Returns the SID as an integer
+     */
+    public int getSIDFromGID(int charsetOffset, int gid) throws IOException {
+        if (gid == 0) {
+            return 0;
+        }
+        cffData.setPosition(charsetOffset);
+        int charsetFormat = cffData.readCard8();
+        switch (charsetFormat) {
+        case 0: //Adjust for .notdef character
+                 cffData.setPosition(cffData.getPosition() + (--gid * 2));
+                 return cffData.readSID();
+        case 1: return getSIDFromGIDFormat(gid, 1);
+        case 2: return getSIDFromGIDFormat(gid, 2);
+        default: return 0;
+        }
+    }
+
+    private int getSIDFromGIDFormat(int gid, int format) throws IOException {
+        int glyphCount = 0;
+        while (true) {
+            int oldGlyphCount = glyphCount;
+            int start = cffData.readSID();
+            glyphCount += ((format == 1) ? cffData.readCard8() : cffData.readCard16()) + 1;
+            if (gid <= glyphCount) {
+                return start + (gid - oldGlyphCount) - 1;
+            }
+        }
+    }
+
+    public byte[] getHeader() {
+        return header.clone();
+    }
+
+    public CFFIndexData getNameIndex() {
+        return nameIndex;
+    }
+
+    public CFFIndexData getTopDictIndex() {
+        return topDICTIndex;
+    }
+
+    public LinkedHashMap<String, DICTEntry> getTopDictEntries() {
+        return topDict;
+    }
+
+    public CFFIndexData getStringIndex() {
+        return stringIndex;
+    }
+
+    public CFFIndexData getGlobalIndexSubr() {
+        return globalIndexSubr;
+    }
+
+    public CFFIndexData getLocalIndexSubr() {
+        return localIndexSubr;
+    }
+
+    public CFFIndexData getCharStringIndex() {
+        return charStringIndex;
+    }
+
+    public CFFDataInput getCFFData() {
+        return cffData;
+    }
+
+    public CustomEncoding getEncoding() {
+        return encoding;
+    }
+
+    public FDSelect getFDSelect() {
+        return fdSelect;
+    }
+
+    public List<FontDict> getFDFonts() {
+        return fdFonts;
+    }
+
+    public CFFDataInput getLocalSubrsForGlyph(int glyph) throws IOException {
+        //Subsets are currently written using a Format0 FDSelect
+        FDSelect fontDictionary = getFDSelect();
+        if (fontDictionary instanceof Format0FDSelect) {
+            Format0FDSelect fdSelect = (Format0FDSelect)fontDictionary;
+            int found = fdSelect.getFDIndexes()[glyph];
+            FontDict font = getFDFonts().get(found);
+            byte[] localSubrData = font.getLocalSubrData().getByteData();
+            if (localSubrData != null) {
+                return new CFFDataInput(localSubrData);
+            } else {
+                return null;
+            }
+        } else if (fontDictionary instanceof Format3FDSelect) {
+            Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary;
+            int index = 0;
+            for (int first : fdSelect.getRanges().keySet()) {
+                if (first > glyph) {
+                    break;
+                }
+                index++;
+            }
+            FontDict font = getFDFonts().get(index);
+            byte[] localSubrsData = font.getLocalSubrData().getByteData();
+            if (localSubrsData != null) {
+                return new CFFDataInput(localSubrsData);
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Parses the char string index from the CFF byte data
+     * @param offset The offset to the char string index
+     * @return Returns the char string index object
+     * @throws IOException Throws an IO Exception if an error occurs
+     */
+    public CFFIndexData readCharStringIndex() throws IOException {
+        int offset = topDict.get("CharStrings").getOperands().get(0).intValue();
+        cffData.setPosition(offset);
+        return readIndex();
+    }
+
+    private CustomEncoding readEncoding() throws IOException {
+        CustomEncoding foundEncoding = null;
+        if (topDict.get("Encoding") != null) {
+            int offset = topDict.get("Encoding").getOperands().get(0).intValue();
+            if (offset != 0 && offset != 1) {
+                //No need to set the offset as we are reading the data sequentially.
+                int format = cffData.readCard8();
+                int numEntries = cffData.readCard8();
+                switch (format) {
+                case 0:
+                    foundEncoding = readFormat0Encoding(format, numEntries);
+                    break;
+                case 1:
+                    foundEncoding = readFormat1Encoding(format, numEntries);
+                    break;
+                default: break;
+                }
+            }
+        }
+        return foundEncoding;
+    }
+
+    private Format0Encoding readFormat0Encoding(int format, int numEntries)
+            throws IOException {
+        Format0Encoding newEncoding = new Format0Encoding();
+        newEncoding.setFormat(format);
+        newEncoding.setNumEntries(numEntries);
+        int[] codes = new int[numEntries];
+        for (int i = 0; i < numEntries; i++) {
+            codes[i] = cffData.readCard8();
+        }
+        newEncoding.setCodes(codes);
+        return newEncoding;
+    }
+
+    private Format1Encoding readFormat1Encoding(int format, int numEntries)
+            throws IOException {
+        Format1Encoding newEncoding = new Format1Encoding();
+        newEncoding.setFormat(format);
+        newEncoding.setNumEntries(numEntries);
+        LinkedHashMap<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
+        for (int i = 0; i < numEntries; i++) {
+            int first = cffData.readCard8();
+            int left = cffData.readCard8();
+            ranges.put(first, left);
+        }
+        newEncoding.setRanges(ranges);
+        return newEncoding;
+    }
+
+    private FDSelect readFDSelect() throws IOException {
+        FDSelect fdSelect = null;
+        DICTEntry fdSelectEntry = topDict.get("FDSelect");
+        if (fdSelectEntry != null) {
+            int fdOffset = fdSelectEntry.getOperands().get(0).intValue();
+            cffData.setPosition(fdOffset);
+            int format = cffData.readCard8();
+            switch (format) {
+            case 0:
+                fdSelect = readFormat0FDSelect();
+                break;
+            case 3:
+                fdSelect = readFormat3FDSelect();
+                break;
+            default:
+            }
+        }
+        return fdSelect;
+    }
+
+    private Format0FDSelect readFormat0FDSelect() throws IOException {
+        Format0FDSelect newFDs = new Format0FDSelect();
+        newFDs.setFormat(0);
+        int glyphCount = charStringIndex.getNumObjects();
+        int[] fds = new int[glyphCount];
+        for (int i = 0; i < glyphCount; i++) {
+            fds[i] = cffData.readCard8();
+        }
+        newFDs.setFDIndexes(fds);
+        return newFDs;
+    }
+
+    private Format3FDSelect readFormat3FDSelect() throws IOException {
+        Format3FDSelect newFDs = new Format3FDSelect();
+        newFDs.setFormat(3);
+        int rangeCount = cffData.readCard16();
+        newFDs.setRangeCount(rangeCount);
+        LinkedHashMap<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
+        for (int i = 0; i < rangeCount; i++) {
+            int first = cffData.readCard16();
+            int fd = cffData.readCard8();
+            ranges.put(first, fd);
+        }
+        newFDs.setRanges(ranges);
+        newFDs.setSentinelGID(cffData.readCard16());
+        return newFDs;
+    }
+
+    private List<FontDict> parseCIDData() throws IOException {
+        ArrayList<FontDict> fdFonts = new ArrayList<FontDict>();
+        if (topDict.get("ROS") != null) {
+            DICTEntry fdArray = topDict.get("FDArray");
+            if (fdArray != null) {
+                int fdIndex = fdArray.getOperands().get(0).intValue();
+                CFFIndexData fontDicts = readIndex(fdIndex);
+                for (int i = 0; i < fontDicts.getNumObjects(); i++) {
+                    FontDict newFontDict = new FontDict();
+
+                    byte[] fdData = fontDicts.getValue(i);
+                    LinkedHashMap<String, DICTEntry> fdEntries = parseDictData(fdData);
+                    newFontDict.setByteData(fontDicts.getValuePosition(i), fontDicts.getValueLength(i));
+                    DICTEntry fontFDEntry = fdEntries.get("FontName");
+                    newFontDict.setFontName(getString(fontFDEntry.getOperands().get(0).intValue()));
+                    DICTEntry privateFDEntry = fdEntries.get("Private");
+                    if (privateFDEntry != null) {
+                        newFontDict = setFDData(privateFDEntry, newFontDict);
+                    }
+
+                    fdFonts.add(newFontDict);
+                }
+            }
+        }
+        return fdFonts;
+    }
+
+    private FontDict setFDData(DICTEntry privateFDEntry, FontDict newFontDict) throws IOException {
+        int privateFDLength = privateFDEntry.getOperands().get(0).intValue();
+        int privateFDOffset = privateFDEntry.getOperands().get(1).intValue();
+        cffData.setPosition(privateFDOffset);
+        byte[] privateDict = cffData.readBytes(privateFDLength);
+        newFontDict.setPrivateDictData(privateFDOffset, privateFDLength);
+        LinkedHashMap<String, DICTEntry> privateEntries = parseDictData(privateDict);
+        DICTEntry subroutines = privateEntries.get("Subrs");
+        if (subroutines != null) {
+            CFFIndexData localSubrs = readIndex(privateFDOffset
+                    + subroutines.getOperands().get(0).intValue());
+            newFontDict.setLocalSubrData(localSubrs);
+        } else {
+            newFontDict.setLocalSubrData(new CFFIndexData());
+        }
+        return newFontDict;
+    }
+
+    private String getString(int sid) throws IOException {
+        return new String(stringIndex.getValue(sid - NUM_STANDARD_STRINGS));
+    }
+
+    private CFFIndexData readLocalIndexSubrs() throws IOException {
+        CFFIndexData localSubrs = null;
+        DICTEntry privateEntry = topDict.get("Private");
+        if (privateEntry != null) {
+            int length = privateEntry.getOperands().get(0).intValue();
+            int offset = privateEntry.getOperands().get(1).intValue();
+            cffData.setPosition(offset);
+            byte[] privateData = cffData.readBytes(length);
+            LinkedHashMap<String, DICTEntry> privateDict = parseDictData(privateData);
+            DICTEntry localSubrsEntry = privateDict.get("Subrs");
+            if (localSubrsEntry != null) {
+                int localOffset = offset + localSubrsEntry.getOperands().get(0).intValue();
+                cffData.setPosition(localOffset);
+                localSubrs = readIndex();
+            }
+        }
+        return localSubrs;
+    }
+
+    /**
+     * Parent class which provides the ability to retrieve byte data from
+     * a sub-table.
+     */
+    public class CFFSubTable {
+        private DataLocation dataLocation = new DataLocation();
+
+        public void setByteData(int position, int length) {
+            dataLocation = new DataLocation(position, length);
+        }
+
+        public byte[] getByteData() throws IOException {
+            int oldPos = cffData.getPosition();
+            try {
+                cffData.setPosition(dataLocation.getDataPosition());
+                return cffData.readBytes(dataLocation.getDataLength());
+            } finally {
+                cffData.setPosition(oldPos);
+            }
+        }
+    }
+
+    /**
+     * An object used to hold index data from the CFF data
+     */
+    public class CFFIndexData extends CFFSubTable {
+        private int numObjects;
+        private int offSize;
+        private int[] offsets = new int[0];
+        private DataLocation dataLocation = new DataLocation();
+
+        public void setNumObjects(int numObjects) {
+            this.numObjects = numObjects;
+        }
+
+        public int getNumObjects() {
+            return this.numObjects;
+        }
+
+        public void setOffSize(int offSize) {
+            this.offSize = offSize;
+        }
+
+        public int getOffSize() {
+            return this.offSize;
+        }
+
+        public void setOffsets(int[] offsets) {
+            this.offsets = offsets.clone();
+        }
+
+        public int[] getOffsets() {
+            return offsets.clone();
+        }
+
+        public void setData(int position, int length) {
+            dataLocation = new DataLocation(position, length);
+        }
+
+        public byte[] getData() throws IOException {
+            int origPos = cffData.getPosition();
+            try {
+                cffData.setPosition(dataLocation.getDataPosition());
+                return cffData.readBytes(dataLocation.getDataLength());
+            } finally {
+                cffData.setPosition(origPos);
+            }
+        }
+
+        /**
+         * Parses index data from an index object found within the CFF byte data
+         * @param cffData A byte array containing the CFF data
+         * @throws IOException Throws an IO Exception if an error occurs
+         */
+        public void parseIndexHeader(CFFDataInput cffData) throws IOException {
+            setNumObjects(cffData.readCard16());
+            setOffSize(cffData.readOffSize());
+            int[] offsets = new int[getNumObjects() + 1];
+            byte[] bytes;
+            //Fills the offsets array
+            for (int i = 0; i <= getNumObjects(); i++) {
+                switch (getOffSize()) {
+                case 1:
+                    offsets[i] = cffData.readCard8();
+                    break;
+                case 2:
+                    offsets[i] = cffData.readCard16();
+                    break;
+                case 3:
+                    bytes = cffData.readBytes(3);
+                    offsets[i] = ((bytes[0] & 0xFF) << 16) + ((bytes[1] & 0xFF) << 8) + (bytes[2] & 0xFF);
+                    break;
+                case 4:
+                    bytes = cffData.readBytes(4);
+                    offsets[i] = ((bytes[0] & 0xFF) << 24) + ((bytes[1] & 0xFF) << 16)
+                            + ((bytes[2] & 0xFF) << 8) + (bytes[3] & 0xFF);
+                    break;
+                default: continue;
+                }
+            }
+            setOffsets(offsets);
+            int position = cffData.getPosition();
+            int dataSize = offsets[offsets.length - 1] - offsets[0];
+
+            cffData.setPosition(cffData.getPosition() + dataSize);
+            setData(position, dataSize);
+        }
+
+        /**
+         * Retrieves data from the index data
+         * @param index The index position of the data to retrieve
+         * @return Returns the byte data for the given index
+         * @throws IOException Throws an IO Exception if an error occurs
+         */
+        public byte[] getValue(int index) throws IOException {
+            int oldPos = cffData.getPosition();
+            try {
+                cffData.setPosition(dataLocation.getDataPosition() + (offsets[index] - 1));
+                return cffData.readBytes(offsets[index + 1] - offsets[index]);
+            } finally {
+                cffData.setPosition(oldPos);
+            }
+        }
+
+        public int getValuePosition(int index) {
+            return dataLocation.getDataPosition() + (offsets[index] - 1);
+        }
+
+        public int getValueLength(int index) {
+            return offsets[index + 1] - offsets[index];
+        }
+    }
+
+    public abstract class CustomEncoding {
+        private int format;
+        private int numEntries;
+
+        public void setFormat(int format) {
+            this.format = format;
+        }
+
+        public int getFormat() {
+            return format;
+        }
+
+        public void setNumEntries(int numEntries) {
+            this.numEntries = numEntries;
+        }
+
+        public int getNumEntries() {
+            return numEntries;
+        }
+    }
+
+    public class Format0Encoding extends CustomEncoding {
+        private int[] codes = new int[0];
+
+        public void setCodes(int[] codes) {
+            this.codes = codes.clone();
+        }
+
+        public int[] getCodes() {
+            return codes.clone();
+        }
+    }
+
+    public class Format1Encoding extends CustomEncoding {
+        private LinkedHashMap<Integer, Integer> ranges;
+
+        public void setRanges(LinkedHashMap<Integer, Integer> ranges) {
+            this.ranges = ranges;
+        }
+
+        public LinkedHashMap<Integer, Integer> getRanges() {
+            return ranges;
+        }
+    }
+
+    public class FDSelect {
+        private int format;
+
+        public void setFormat(int format) {
+            this.format = format;
+        }
+
+        public int getFormat() {
+            return format;
+        }
+    }
+
+    public class Format0FDSelect extends FDSelect {
+        private int[] fds = new int[0];
+
+        public void setFDIndexes(int[] fds) {
+            this.fds = fds.clone();
+        }
+
+        public int[] getFDIndexes() {
+            return fds.clone();
+        }
+    }
+
+    public class Format3FDSelect extends FDSelect {
+        private int rangeCount;
+        private LinkedHashMap<Integer, Integer> ranges;
+        private int sentinelGID;
+
+        public void setRangeCount(int rangeCount) {
+            this.rangeCount = rangeCount;
+        }
+
+        public int getRangeCount() {
+            return rangeCount;
+        }
+
+        public void setRanges(LinkedHashMap<Integer, Integer> ranges) {
+            this.ranges = ranges;
+        }
+
+        public LinkedHashMap<Integer, Integer> getRanges() {
+            return ranges;
+        }
+
+        public void setSentinelGID(int sentinelGID) {
+            this.sentinelGID = sentinelGID;
+        }
+
+        public int getSentinelGID() {
+            return sentinelGID;
+        }
+    }
+
+    public class FontDict extends CFFSubTable {
+        private String fontName;
+        private DataLocation dataLocation = new DataLocation();
+        private CFFIndexData localSubrData;
+
+        public void setFontName(String groupName) {
+            this.fontName = groupName;
+        }
+
+        public String getFontName() {
+            return fontName;
+        }
+
+        public void setPrivateDictData(int position, int length) {
+            dataLocation = new DataLocation(position, length);
+        }
+
+        public byte[] getPrivateDictData() throws IOException {
+            int origPos = cffData.getPosition();
+            try {
+                cffData.setPosition(dataLocation.getDataPosition());
+                return cffData.readBytes(dataLocation.getDataLength());
+            } finally {
+                cffData.setPosition(origPos);
+            }
+        }
+
+        public void setLocalSubrData(CFFIndexData localSubrData) {
+            this.localSubrData = localSubrData;
+        }
+
+        public CFFIndexData getLocalSubrData() {
+            return localSubrData;
+        }
+    }
+
+    private static class DataLocation {
+        private int dataPosition;
+        private int dataLength;
+
+        public DataLocation() {
+            dataPosition = 0;
+            dataLength = 0;
+        }
+
+        public DataLocation(int position, int length) {
+            this.dataPosition = position;
+            this.dataLength = length;
+        }
+
+        public int getDataPosition() {
+            return dataPosition;
+        }
+
+        public int getDataLength() {
+            return dataLength;
+        }
+    }
+}
index 8fd8d34447544cad8fefad2f32c0612af8b59f44..2fe9f29e03f3318b5ba85bb5cb3595fe4c195d2f 100644 (file)
@@ -449,13 +449,14 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
 
     private class Rev5Engine extends InitializationEngine {
 
-        protected byte[] userValidationSalt = new byte[8];
-        protected byte[] userKeySalt = new byte[8];
-        protected byte[] ownerValidationSalt = new byte[8];
-        protected byte[] ownerKeySalt = new byte[8];
-        protected byte[] ueValue;
-        protected byte[] oeValue;
-        protected final boolean encryptMetadata;
+        // private SecureRandom random = new SecureRandom();
+        private byte[] userValidationSalt = new byte[8];
+        private byte[] userKeySalt = new byte[8];
+        private byte[] ownerValidationSalt = new byte[8];
+        private byte[] ownerKeySalt = new byte[8];
+        private byte[] ueValue;
+        private byte[] oeValue;
+        private final boolean encryptMetadata;
 
         Rev5Engine(EncryptionSettings encryptionSettings) {
             super(encryptionSettings);
@@ -562,7 +563,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
         /**
          * Algorithm 3.8-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3)
          */
-        protected void computeUEValue() {
+        private void computeUEValue() {
             digest.reset();
             byte[] prepared = preparedUserPassword;
             byte[] concatenated = new byte[prepared.length + 8];
@@ -576,7 +577,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
         /**
          * Algorithm 3.9-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3)
          */
-        protected void computeOEValue() {
+        private void computeOEValue() {
             digest.reset();
             byte[] prepared = preparedOwnerPassword;
             byte[] concatenated = new byte[prepared.length + 56];
@@ -614,86 +615,6 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
         }
     }
 
-    private class Rev6Engine extends Rev5Engine {
-
-        private MessageDigest digest384;
-        private MessageDigest digest512;
-
-        Rev6Engine(EncryptionSettings encryptionSettings) {
-            super(encryptionSettings);
-            try {
-                digest384 = MessageDigest.getInstance("SHA-384");
-                digest512 = MessageDigest.getInstance("SHA-512");
-            } catch (NoSuchAlgorithmException e) {
-                throw new UnsupportedOperationException(e.getMessage());
-            }
-        }
-
-        @Override
-        protected void computeUValue() {
-            byte[] userBytes = new byte[16];
-            random.nextBytes(userBytes);
-            System.arraycopy(userBytes, 0, userValidationSalt, 0, 8);
-            System.arraycopy(userBytes, 8, userKeySalt, 0, 8);
-            digest.reset();
-            byte[] prepared = preparedUserPassword;
-            byte[] concatenated = new byte[prepared.length + 8];
-            System.arraycopy(prepared, 0, concatenated, 0, prepared.length);
-            System.arraycopy(userValidationSalt, 0, concatenated, prepared.length, 8);
-            digest.update(concatenated);
-            byte[] block = digest.digest();
-            int blockSize = 32;
-            byte[] key = new byte[16];
-            byte[] iv = new byte[16];
-            int length = prepared.length + blockSize;
-            byte[] data = new byte[length * 64];
-            for (int i = 0; i < 64 || i < data[length * 64 - 1] + 32; i++) {
-                System.arraycopy(block, 0, key, 0, 16);
-                System.arraycopy(block, 16, iv, 0, 16);
-                for (int j = 0; j < 64; j++) {
-                    System.arraycopy(prepared, 0, data, j * length, prepared.length);
-                    System.arraycopy(block, 0, data, j * length + prepared.length, blockSize);
-                }
-                try {
-                    final Cipher cipher = PDFEncryptionJCE.initCipher(key, false, iv);
-                    data = cipher.doFinal(data);
-                } catch (IllegalBlockSizeException e) {
-                    throw new IllegalStateException(e.getMessage());
-                } catch (BadPaddingException e) {
-                    throw new IllegalStateException(e.getMessage());
-                }
-                int sum = 0;
-                for (int k = 0; k < 16; k++) {
-                    sum += data[k];
-                }
-                blockSize = 32 + (sum % 3) * 16;
-                switch (blockSize) {
-                case 32:
-                    digest.reset();
-                    digest.update(data);
-                    block = digest.digest();
-                    break;
-                case 48:
-                    digest384.reset();
-                    digest384.update(data);
-                    block = digest384.digest();
-                    break;
-                case 64:
-                    digest512.reset();
-                    digest512.update(data);
-                    block = digest512.digest();
-                    break;
-                default:
-                    // not possible
-                    break;
-                }
-                length = prepared.length + blockSize;
-                data = new byte[length * 64];
-            }
-        }
-
-    }
-
     private class EncryptionFilter extends PDFFilter {
 
         private int streamNumber;