#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 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& 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 keys = decode_obj.getKeys(); for (std::set::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::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 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 > 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::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; }