CosPDFOptimizer.java 11.2 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.internal.io.stream.InputByteStream
 */
package com.adobe.internal.pdftoolkit.core.cos;

import com.adobe.internal.io.stream.InputByteStream;
import com.adobe.internal.pdftoolkit.core.cos.CosArray;
import com.adobe.internal.pdftoolkit.core.cos.CosBoolean;
import com.adobe.internal.pdftoolkit.core.cos.CosDictionary;
import com.adobe.internal.pdftoolkit.core.cos.CosDocument;
import com.adobe.internal.pdftoolkit.core.cos.CosName;
import com.adobe.internal.pdftoolkit.core.cos.CosNull;
import com.adobe.internal.pdftoolkit.core.cos.CosNumeric;
import com.adobe.internal.pdftoolkit.core.cos.CosObject;
import com.adobe.internal.pdftoolkit.core.cos.CosStream;
import com.adobe.internal.pdftoolkit.core.cos.CosString;
import com.adobe.internal.pdftoolkit.core.exceptions.PDFCosParseException;
import com.adobe.internal.pdftoolkit.core.exceptions.PDFIOException;
import com.adobe.internal.pdftoolkit.core.exceptions.PDFSecurityException;
import com.adobe.internal.pdftoolkit.core.types.ASName;
import com.adobe.internal.pdftoolkit.core.types.ASString;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

final class CosPDFOptimizer {
    private static final byte kNullType = 0;
    private static final byte kIntegerType = 1;
    private static final byte kRealType = 2;
    private static final byte kBooleanType = 3;
    private static final byte kNameType = 4;
    private static final byte kStringType = 5;
    private static final byte kDictionaryType = 6;
    private static final byte kArrayType = 7;
    private static final byte kStreamType = 8;
    private CosDocument mCosDoc;
    MessageDigest mDigester;
    Digest mNullDigest;
    HashMap mObjToDigest;
    HashMap mDigestToObj;

    private CosPDFOptimizer(CosDocument cosDocument) {
        this.mCosDoc = cosDocument;
    }

    protected static CosPDFOptimizer newInstance(CosDocument cosDocument) {
        return new CosPDFOptimizer(cosDocument);
    }

    protected void freeDuplicateResources() throws PDFCosParseException, IOException, PDFIOException, PDFSecurityException {
        this.mDigester = CosPDFOptimizer.newDigesterInstance();
        if (this.mDigester == null) {
            return;
        }
        this.mNullDigest = new Digest(null);
        this.mObjToDigest = new HashMap();
        this.mDigestToObj = new HashMap();
        CosDictionary node = CosPDFOptimizer.safeGetDict(this.mCosDoc.getRoot(), ASName.k_Pages);
        this.freeDupPageTreeNode(node);
    }

    private void freeDupPageTreeNode(CosDictionary node) throws PDFCosParseException, IOException, PDFIOException, PDFSecurityException {
        if (node == null) {
            return;
        }
        CosArray kids = CosPDFOptimizer.safeGetArray(node, ASName.k_Kids);
        if (kids != null) {
            Iterator<CosObject> iter = kids.iterator();
            while (iter.hasNext()) {
                this.freeDupPageTreeNode((CosDictionary)iter.next());
            }
        } else {
            this.freeDupPageRes(CosPDFOptimizer.safeGetDict(node, ASName.k_Resources));
        }
    }

    private void freeDupPageRes(CosDictionary pageRes) throws PDFCosParseException, IOException, PDFIOException, PDFSecurityException {
        if (pageRes == null) {
            return;
        }
        Iterator<ASName> iter = pageRes.keyIterator();
        while (iter.hasNext()) {
            ASName className = iter.next();
            CosDictionary resClass = CosPDFOptimizer.safeGetDict(pageRes, className);
            this.freeDupResObj(resClass);
        }
    }

    private CosObject freeDupResObj(CosObject resource) throws PDFCosParseException, PDFIOException, IOException, PDFSecurityException {
        if (resource == null || resource instanceof CosNull) {
            this.updateByte(0);
            return null;
        }
        MessageDigest savedDigester = this.mDigester;
        int objNum = resource.getObjNum();
        Integer objNumInt = objNum;
        if (objNum != 0) {
            if (this.mObjToDigest.containsKey(objNumInt)) {
                byte[] localDigest = ((Digest)this.mObjToDigest.get(objNumInt)).getBytes();
                if (localDigest != null) {
                    this.mDigester.update(localDigest);
                }
                return null;
            }
            this.mDigester = CosPDFOptimizer.newDigesterInstance();
            this.mObjToDigest.put(objNumInt, this.mNullDigest);
        }
        if (resource instanceof CosNumeric) {
            this.updateNumber(resource.numberValue());
        } else if (resource instanceof CosName) {
            this.updateName(resource.nameValue());
        } else if (resource instanceof CosBoolean) {
            this.updateBoolean(resource.booleanValue());
        } else if (resource instanceof CosString) {
            this.updateString(resource.stringValue());
        } else if (resource instanceof CosDictionary) {
            if (resource instanceof CosStream) {
                this.updateByte(8);
            } else {
                this.updateByte(6);
            }
            this.updateInteger(((CosDictionary)resource).size());
            ArrayList<ASName> keys = new ArrayList<ASName>(((CosDictionary)resource).keySet());
            Collections.sort(keys);
            for (ASName name : keys) {
                this.updateName(name);
                CosObject value = ((CosDictionary)resource).get(name);
                CosObject prevValue = this.freeDupResObj(value);
                if (prevValue == null) continue;
                ((CosDictionary)resource).put(name, prevValue);
            }
            if (resource instanceof CosStream) {
                int type = 0;
                long length = 0;
                InputByteStream stream = ((CosStream)resource).getStreamEncoded();
                if (stream != null && stream.bytesAvailable() != 0) {
                    type = 1;
                    length = stream.bytesAvailable();
                } else {
                    stream = ((CosStream)resource).getStreamDecoded();
                    if (stream != null && stream.bytesAvailable() != 0) {
                        type = 2;
                        length = stream.bytesAvailable();
                    }
                }
                this.updateInteger(type);
                this.updateInteger((int)length);
                byte[] buffer = new byte[4096];
                while (length > 0) {
                    int bytesRead = stream.read(buffer, 0, buffer.length);
                    this.mDigester.update(buffer, 0, bytesRead);
                    length -= (long)bytesRead;
                }
            }
        } else if (resource instanceof CosArray) {
            this.updateByte(7);
            int size = ((CosArray)resource).size();
            this.updateInteger(size);
            for (int i = 0; i < size; ++i) {
                CosObject value = ((CosArray)resource).get(i);
                CosObject prevValue = this.freeDupResObj(value);
                if (prevValue == null) continue;
                ((CosArray)resource).set(i, prevValue);
            }
        }
        if (objNum != 0) {
            byte[] localDigest = this.mDigester.digest();
            this.mDigester = savedDigester;
            this.mDigester.update(localDigest);
            Digest digestObj = new Digest(localDigest);
            if (this.mDigestToObj.containsKey(digestObj)) {
                return (CosObject)this.mDigestToObj.get(digestObj);
            }
            this.mDigestToObj.put(digestObj, resource);
            this.mObjToDigest.put(objNumInt, digestObj);
        }
        return null;
    }

    private final void updateNumber(Number num) {
        if (num instanceof Integer) {
            this.updateByte(1);
            this.updateInteger(num.intValue());
        } else {
            this.updateByte(2);
            long bits = Double.doubleToLongBits(num.doubleValue());
            this.updateInteger((int)(bits >> 32));
            this.updateInteger((int)bits);
        }
    }

    private final void updateBoolean(boolean bool) {
        this.updateByte(3);
        if (bool) {
            this.updateByte(1);
        } else {
            this.updateByte(0);
        }
    }

    private final void updateName(ASName name) {
        this.updateByte(4);
        byte[] nameContent = name.getBytes();
        this.updateInteger(nameContent.length);
        this.updateByteArray(nameContent);
    }

    private final void updateString(ASString string) {
        this.updateByte(5);
        byte[] stringBytes = string.getBytes();
        this.updateInteger(stringBytes.length);
        this.updateByteArray(stringBytes);
    }

    private final void updateByte(byte b) {
        this.mDigester.update(b);
    }

    private final void updateByteArray(byte[] data) {
        this.mDigester.update(data);
    }

    private final void updateInteger(int num) {
        byte[] data = new byte[]{(byte)(num >> 24), (byte)(num >> 16), (byte)(num >> 8), (byte)num};
        this.updateByteArray(data);
    }

    private static final MessageDigest newDigesterInstance() {
        try {
            return MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    private static final CosDictionary safeGetDict(CosDictionary dict, ASName key) throws PDFCosParseException, PDFIOException, PDFSecurityException {
        CosObject obj = dict.get(key);
        if (obj instanceof CosDictionary) {
            return (CosDictionary)obj;
        }
        return null;
    }

    private static final CosArray safeGetArray(CosDictionary dict, ASName key) throws PDFCosParseException, PDFIOException, PDFSecurityException {
        CosObject obj = dict.get(key);
        if (obj instanceof CosArray) {
            return (CosArray)obj;
        }
        return null;
    }

    private static final class Digest {
        byte[] mDigest;

        Digest(byte[] digest) {
            this.mDigest = digest;
        }

        byte[] getBytes() {
            return this.mDigest;
        }

        public boolean equals(Object digest) {
            if (!(digest instanceof Digest)) {
                return false;
            }
            byte[] digBytes = ((Digest)digest).getBytes();
            if (this.mDigest == null && digBytes == null) {
                return true;
            }
            if (this.mDigest == null || digBytes == null) {
                return false;
            }
            int length = this.mDigest.length;
            if (digBytes.length != length) {
                return false;
            }
            for (int i = 0; i < length; ++i) {
                if (digBytes[i] == this.mDigest[i]) continue;
                return false;
            }
            return true;
        }

        public int hashCode() {
            if (this.mDigest == null) {
                return 0;
            }
            int hash = 0;
            int len = this.mDigest.length;
            for (int i = 0; i < len; ++i) {
                hash ^= this.mDigest[i] * -1640531527;
            }
            return hash;
        }
    }

}