Logo Search packages:      
Sourcecode: qpdf version File versions  Download package

QPDF_Stream.cc

#include <qpdf/QPDF_Stream.hh>

#include <qpdf/QEXC.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/Pipeline.hh>
#include <qpdf/Pl_Flate.hh>
#include <qpdf/Pl_PNGFilter.hh>
#include <qpdf/Pl_RC4.hh>
#include <qpdf/Pl_Buffer.hh>
#include <qpdf/Pl_ASCII85Decoder.hh>
#include <qpdf/Pl_ASCIIHexDecoder.hh>
#include <qpdf/Pl_LZWDecoder.hh>

#include <qpdf/QTC.hh>
#include <qpdf/QPDF.hh>
#include <qpdf/QPDFExc.hh>
#include <qpdf/Pl_QPDFTokenizer.hh>

QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
                   QPDFObjectHandle stream_dict,
                   off_t offset, int length) :
    qpdf(qpdf),
    objid(objid),
    generation(generation),
    stream_dict(stream_dict),
    offset(offset),
    length(length)
{
    if (! stream_dict.isDictionary())
    {
      throw QEXC::Internal("stream object instantiated with non-dictionary "
                       "object for dictionary");
    }
}

QPDF_Stream::~QPDF_Stream()
{
}

std::string
QPDF_Stream::unparse()
{
    // Unparse stream objects as indirect references
    return QUtil::int_to_string(this->objid) + " " +
      QUtil::int_to_string(this->generation) + " R";
}

QPDFObjectHandle
QPDF_Stream::getDict() const
{
    return this->stream_dict;
}

PointerHolder<Buffer>
QPDF_Stream::getStreamData()
{
    Pl_Buffer buf("stream data buffer");
    if (! pipeStreamData(&buf, true, false, false))
    {
      throw QPDFExc("getStreamData called on unfilterable stream");
    }
    return buf.getBuffer();
}

bool
QPDF_Stream::filterable(std::vector<std::string>& filters,
                  int& predictor, int& columns,
                  bool& early_code_change)
{
    // Initialize values to their defaults as per the PDF spec
    predictor = 1;
    columns = 0;
    early_code_change = true;

    bool filterable = true;

    // See if we can support any decode parameters that are specified.

    QPDFObjectHandle decode_obj =
      this->stream_dict.getKey("/DecodeParms");
    if (decode_obj.isNull())
    {
      // no problem
    }
    else if (decode_obj.isDictionary())
    {
      std::set<std::string> keys = decode_obj.getKeys();
      for (std::set<std::string>::iterator iter = keys.begin();
           iter != keys.end(); ++iter)
      {
          std::string const& key = *iter;
          if (key == "/Predictor")
          {
            QPDFObjectHandle predictor_obj = decode_obj.getKey(key);
            if (predictor_obj.isInteger())
            {
                predictor = predictor_obj.getIntValue();
                if (! ((predictor == 1) || (predictor == 12)))
                {
                  filterable = false;
                }
            }
            else
            {
                filterable = false;
            }
          }
          else if (key == "/EarlyChange")
          {
            QPDFObjectHandle earlychange_obj = decode_obj.getKey(key);
            if (earlychange_obj.isInteger())
            {
                int earlychange = earlychange_obj.getIntValue();
                early_code_change = (earlychange == 1);
                if (! ((earlychange == 0) || (earlychange == 1)))
                {
                  filterable = false;
                }
            }
            else
            {
                filterable = false;
            }
          }
          else if (key == "/Columns")
          {
            QPDFObjectHandle columns_obj = decode_obj.getKey(key);
            if (columns_obj.isInteger())
            {
                columns = columns_obj.getIntValue();
            }
            else
            {
                filterable = false;
            }
          }
          else
          {
            filterable = false;
          }
      }
    }
    else
    {
      throw QPDFExc(qpdf->getFilename(), this->offset,
                  "invalid decode parameters object type for this stream");
    }

    if ((predictor > 1) && (columns == 0))
    {
      // invalid
      filterable = false;
    }

    if (! filterable)
    {
      return false;
    }

    // Check filters

    QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter");
    bool filters_okay = true;

    if (filter_obj.isNull())
    {
      // No filters
    }
    else if (filter_obj.isName())
    {
      // One filter
      filters.push_back(filter_obj.getName());
    }
    else if (filter_obj.isArray())
    {
      // Potentially multiple filters
      int n = filter_obj.getArrayNItems();
      for (int i = 0; i < n; ++i)
      {
          QPDFObjectHandle item = filter_obj.getArrayItem(i);
          if (item.isName())
          {
            filters.push_back(item.getName());
          }
          else
          {
            filters_okay = false;
          }
      }
    }
    else
    {
      filters_okay = false;
    }

    if (! filters_okay)
    {
      QTC::TC("qpdf", "QPDF_Stream invalid filter");
      throw QPDFExc(qpdf->getFilename(), this->offset,
                  "invalid filter object type for this stream");
    }

    // `filters' now contains a list of filters to be applied in
    // order.  See which ones we can support.

    for (std::vector<std::string>::iterator iter = filters.begin();
       iter != filters.end(); ++iter)
    {
      std::string const& filter = *iter;
      if (! ((filter == "/FlateDecode") ||
             (filter == "/LZWDecode") ||
             (filter == "/ASCII85Decode") ||
             (filter == "/ASCIIHexDecode")))
      {
          filterable = false;
      }
    }

    return filterable;
}

bool
QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter,
                      bool normalize, bool compress)
{
    std::vector<std::string> filters;
    int predictor = 1;
    int columns = 0;
    bool early_code_change = true;
    if (filter)
    {
      filter = filterable(filters, predictor, columns, early_code_change);
    }

    if (pipeline == 0)
    {
      QTC::TC("qpdf", "QPDF_Stream pipeStreamData with null pipeline");
      return filter;
    }

    // Construct the pipeline in reverse order.  Force pipelines we
    // create to be deleted when this function finishes.
    std::vector<PointerHolder<Pipeline> > to_delete;

    if (filter)
    {
      if (compress)
      {
          pipeline = new Pl_Flate("compress object stream", pipeline,
                            Pl_Flate::a_deflate);
          to_delete.push_back(pipeline);
      }

      if (normalize)
      {
          pipeline = new Pl_QPDFTokenizer("normalizer", pipeline);
          to_delete.push_back(pipeline);
      }

      for (std::vector<std::string>::reverse_iterator iter = filters.rbegin();
           iter != filters.rend(); ++iter)
      {
          std::string const& filter = *iter;
          if (filter == "/FlateDecode")
          {
            if (predictor == 12)
            {
                QTC::TC("qpdf", "QPDF_Stream PNG filter");
                pipeline = new Pl_PNGFilter(
                  "png decode", pipeline, Pl_PNGFilter::a_decode,
                  columns, 0 /* not used */);
                to_delete.push_back(pipeline);
            }

            pipeline = new Pl_Flate("stream inflate",
                              pipeline, Pl_Flate::a_inflate);
            to_delete.push_back(pipeline);
          }
          else if (filter == "/ASCII85Decode")
          {
            pipeline = new Pl_ASCII85Decoder("ascii85 decode", pipeline);
            to_delete.push_back(pipeline);
          }
          else if (filter == "/ASCIIHexDecode")
          {
            pipeline = new Pl_ASCIIHexDecoder("asciiHex decode", pipeline);
            to_delete.push_back(pipeline);
          }
          else if (filter == "/LZWDecode")
          {
            pipeline = new Pl_LZWDecoder("lzw decode", pipeline,
                                   early_code_change);
            to_delete.push_back(pipeline);
          }
          else
          {
            throw QEXC::Internal("QPDFStream: unknown filter "
                             "encountered after check");
          }
      }
    }

    QPDF::Pipe::pipeStreamData(this->qpdf, this->objid, this->generation,
                         this->offset, this->length,
                         this->stream_dict, pipeline);

    return filter;
}

Generated by  Doxygen 1.6.0   Back to index