# COSFilterInputStream.java

    /*
     * 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.
     */
    package org.apache.pdfbox.pdmodel.interactive.digitalsignature;

    import java.io.ByteArrayInputStream;
    import java.io.FilterInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Arrays;

    import org.apache.pdfbox.io.IOUtils;

    /**
     * A filtered stream that includes the bytes that are in the (begin,length) intervals passed in the
     * constructor.
     *
     * @author boix_jor
     *
     */
    public class COSFilterInputStream extends FilterInputStream
    {
        private int[][] ranges;
        private int range;
        private long position = 0;

        public COSFilterInputStream(InputStream in, int[] byteRange)
        {
            super(in);
            calculateRanges(byteRange);
        }

        public COSFilterInputStream(byte[] in, int[] byteRange)
        {
            this(new ByteArrayInputStream(in), byteRange);
        }

        @Override
        public int read() throws IOException
        {
            if ((this.range == -1 || getRemaining() <= 0) && !nextRange())
            {
                return -1; // EOF
            }
            int result = super.read();
            this.position++;
            return result;
        }

        @Override
        public int read(byte[] b) throws IOException
        {
            return read(b, 0, b.length);
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException
        {
            if ((this.range == -1 || getRemaining() <= 0) && !nextRange())
            {
                return -1; // EOF
            }
            int bytesRead = super.read(b, off, (int) Math.min(len, getRemaining()));
            this.position += bytesRead;
            return bytesRead;
        }

        public byte[] toByteArray() throws IOException
        {
            return IOUtils.toByteArray(this);
        }

        private void calculateRanges(int[] byteRange)
        {
            this.ranges = new int[byteRange.length / 2][];
            for (int i = 0; i < byteRange.length / 2; i++)
            {
                this.ranges[i] = new int[] { byteRange[i * 2], byteRange[i * 2] + byteRange[i * 2 + 1] };
            }
            this.range = -1;
        }

        private long getRemaining()
        {
            return this.ranges[this.range][1] - this.position;
        }

        private boolean nextRange() throws IOException
        {
            if (this.range + 1 < this.ranges.length)
            {
                this.range++;
                while (this.position < this.ranges[this.range][0])
                {
                    long skipped = super.skip(this.ranges[this.range][0] - this.position);
                    if (skipped == 0)
                    {
                        throw new IOException("FilterInputStream.skip() returns 0, range: " +
                                Arrays.toString(this.ranges[this.range]));
                    }
                    this.position += skipped;
                }
                return true;
            }
            else
            {
                return false;
            }
        }
    }