/* * Copyright (C) 2004-2012 Geometer Plus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include #include #include #include "PdfObject.h" PdfObject::~PdfObject() { } shared_ptr PdfIntegerObject::integerObject(int value) { if ((value < 0) || (value >= 256)) { return new PdfIntegerObject(value); } else { static shared_ptr* table = new shared_ptr[256]; if (table[value].isNull()) { table[value] = new PdfIntegerObject(value); } return table[value]; } } PdfIntegerObject::PdfIntegerObject(int value) : myValue(value) { std::cerr << "PdfIntegerObject " << value << "\n"; } int PdfIntegerObject::value() const { return myValue; } PdfObject::Type PdfIntegerObject::type() const { return INTEGER_NUMBER; } shared_ptr PdfBooleanObject::TRUE() { static shared_ptr value = new PdfBooleanObject(true); return value; } shared_ptr PdfBooleanObject::FALSE() { static shared_ptr value = new PdfBooleanObject(false); return value; } PdfBooleanObject::PdfBooleanObject(bool value) : myValue(value) { std::cerr << "PdfBooleanObject " << value << "\n"; } bool PdfBooleanObject::value() const { return myValue; } PdfObject::Type PdfBooleanObject::type() const { return BOOLEAN; } PdfStringObject::PdfStringObject(const std::string &value) : myValue(value) { std::cerr << "PdfStringObject " << value << "\n"; } PdfObject::Type PdfStringObject::type() const { return STRING; } std::map > PdfNameObject::ourObjectMap; shared_ptr PdfNameObject::nameObject(const std::string &id) { // TODO: process escaped characters std::map >::const_iterator it = ourObjectMap.find(id); if (it != ourObjectMap.end()) { return it->second; } std::cerr << "PdfNameObject " << id << "\n"; shared_ptr object = new PdfNameObject(); ourObjectMap.insert(std::make_pair(id, object)); return object; } PdfNameObject::PdfNameObject() { } PdfObject::Type PdfNameObject::type() const { return NAME; } PdfDictionaryObject::PdfDictionaryObject() { } void PdfDictionaryObject::setObject(shared_ptr id, shared_ptr object) { myMap[id] = object; } shared_ptr PdfDictionaryObject::operator[](shared_ptr id) const { std::map,shared_ptr >::const_iterator it = myMap.find(id); return (it != myMap.end()) ? it->second : 0; } shared_ptr PdfDictionaryObject::operator[](const std::string &id) const { return operator[](PdfNameObject::nameObject(id)); } PdfObject::Type PdfDictionaryObject::type() const { return DICTIONARY; } PdfArrayObject::PdfArrayObject() { } void PdfArrayObject::addObject(shared_ptr object) { myVector.push_back(object); } shared_ptr PdfArrayObject::popLast() { if (!myVector.empty()) { shared_ptr last = myVector.back(); myVector.pop_back(); return last; } return 0; } int PdfArrayObject::size() const { return myVector.size(); } shared_ptr PdfArrayObject::operator[](int index) const { return myVector[index]; } PdfObject::Type PdfArrayObject::type() const { return ARRAY; } PdfObjectReference::PdfObjectReference(int number, int generation) : myNumber(number), myGeneration(generation) { } int PdfObjectReference::number() const { return myNumber; } int PdfObjectReference::generation() const { return myGeneration; } PdfObject::Type PdfObjectReference::type() const { return REFERENCE; } PdfStreamObject::PdfStreamObject(const PdfDictionaryObject &dictionary, ZLInputStream &dataStream) { char ch; skipWhiteSpaces(dataStream, ch); shared_ptr length = dictionary["Length"]; if (!length.isNull() && (length->type() == INTEGER_NUMBER)) { int value = ((PdfIntegerObject&)*length).value(); if (value > 0) { shared_ptr filter = dictionary["Filter"]; if (filter == PdfNameObject::nameObject("FlateDecode")) { dataStream.seek(1, false); ZLZDecompressor decompressor(value - 2); char buffer[2048]; while (true) { std::size_t size = decompressor.decompress(dataStream, buffer, 2048); if (size == 0) { break; } myData.append(buffer, size); } std::cerr << myData << "\n"; } else { myData.append(value, '\0'); myData[0] = ch; dataStream.read((char*)myData.data() + 1, value - 1); } } } /* shared_ptr filter = dictionary["Filter"]; if (!filter.isNull()) { switch (filter->type()) { default: break; case NAME: myFilters.push_back( (filter == PdfNameObject::nameObject("FlateDecode")) ? FLATE : UNKNOWN ); break; case ARRAY: { // TODO: process filters array } } } */ } PdfObject::Type PdfStreamObject::type() const { return STREAM; } enum PdfCharacterType { PDF_CHAR_REGULAR, PDF_CHAR_WHITESPACE, PDF_CHAR_DELIMITER }; static PdfCharacterType *PdfCharacterTypeTable = 0; void PdfObject::skipWhiteSpaces(ZLInputStream &stream, char &ch) { if (PdfCharacterTypeTable == 0) { PdfCharacterTypeTable = new PdfCharacterType[256]; for (int i = 0; i < 256; ++i) { PdfCharacterTypeTable[i] = PDF_CHAR_REGULAR; } PdfCharacterTypeTable[0] = PDF_CHAR_WHITESPACE; PdfCharacterTypeTable[9] = PDF_CHAR_WHITESPACE; PdfCharacterTypeTable[10] = PDF_CHAR_WHITESPACE; PdfCharacterTypeTable[12] = PDF_CHAR_WHITESPACE; PdfCharacterTypeTable[13] = PDF_CHAR_WHITESPACE; PdfCharacterTypeTable[32] = PDF_CHAR_WHITESPACE; PdfCharacterTypeTable['('] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable[')'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['<'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['>'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['['] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable[']'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['{'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['}'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['/'] = PDF_CHAR_DELIMITER; PdfCharacterTypeTable['%'] = PDF_CHAR_DELIMITER; } while ((PdfCharacterTypeTable[(unsigned char)ch] == PDF_CHAR_WHITESPACE) && (stream.read(&ch, 1) == 1)) { } } void PdfObject::readToken(ZLInputStream &stream, std::string &buffer, char &ch) { buffer.clear(); skipWhiteSpaces(stream, ch); while (PdfCharacterTypeTable[(unsigned char)ch] == PDF_CHAR_REGULAR) { buffer += ch; if (stream.read(&ch, 1) != 1) { break; } } } shared_ptr PdfObject::readObject(ZLInputStream &stream, char &ch) { skipWhiteSpaces(stream, ch); PdfObject::Type type = PdfObject::NIL; bool hexString = false; switch (ch) { case '(': hexString = false; type = PdfObject::STRING; break; case '<': stream.read(&ch, 1); hexString = true; type = (ch == '<') ? PdfObject::DICTIONARY : PdfObject::STRING; break; case '>': // end of dictionary stream.read(&ch, 1); if (ch == '>') { stream.read(&ch, 1); } return 0; case '/': type = PdfObject::NAME; break; case '[': type = PdfObject::ARRAY; break; case ']': // end of array stream.read(&ch, 1); return 0; case '+': case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': type = PdfObject::INTEGER_NUMBER; break; case 't': case 'f': type = PdfObject::BOOLEAN; break; } switch (type) { case PdfObject::DICTIONARY: { ch = 0; shared_ptr name; shared_ptr value; shared_ptr next; PdfDictionaryObject *dictionary = new PdfDictionaryObject(); while (true) { next = readObject(stream, ch); if (next.isNull()) { break; } PdfObject::Type oType = next->type(); if (oType == PdfObject::NAME) { name = next; value = readObject(stream, ch); if (value.isNull()) { break; } dictionary->setObject(name, value); } else if (oType == PdfObject::INTEGER_NUMBER) { if (value.isNull() || (value->type() != PdfObject::INTEGER_NUMBER)) { break; } skipWhiteSpaces(stream, ch); if (ch != 'R') { break; } const int number = ((PdfIntegerObject&)*value).value(); const int generation = ((PdfIntegerObject&)*next).value(); dictionary->setObject(name, new PdfObjectReference(number, generation)); value = 0; ch = 0; } else { break; } } std::string token; readToken(stream, token, ch); if (token == "stream") { shared_ptr d = dictionary; return new PdfStreamObject(*dictionary, stream); } else { return dictionary; } } case PdfObject::NAME: { std::string name; stream.read(&ch, 1); readToken(stream, name, ch); return PdfNameObject::nameObject(name); } case PdfObject::BOOLEAN: { std::string name; readToken(stream, name, ch); return (name == "true") ? PdfBooleanObject::TRUE() : PdfBooleanObject::FALSE(); } case PdfObject::INTEGER_NUMBER: { std::string str; if ((ch == '+') || (ch == '-')) { str += ch; stream.read(&ch, 1); } while ((ch >= '0') && (ch <= '9')) { str += ch; stream.read(&ch, 1); } return PdfIntegerObject::integerObject(atoi(str.c_str())); } case PdfObject::STRING: { std::string value; if (hexString) { char num[3]; num[2] = '\0'; while (ch != '>') { num[0] = ch; stream.read(num + 1, 1); value += (char)strtol(num, 0, 16); stream.read(&ch, 1); } ch = 0; } else { // TODO: implement } return new PdfStringObject(value); } case PdfObject::ARRAY: { PdfArrayObject *array = new PdfArrayObject(); ch = 0; while (true) { skipWhiteSpaces(stream, ch); if (ch == 'R') { const int size = array->size(); if ((size >= 2) && ((*array)[size - 1]->type() == PdfObject::INTEGER_NUMBER) && ((*array)[size - 2]->type() == PdfObject::INTEGER_NUMBER)) { const int generation = ((PdfIntegerObject&)*array->popLast()).value(); const int number = ((PdfIntegerObject&)*array->popLast()).value(); array->addObject(new PdfObjectReference(number, generation)); ch = 0; } } shared_ptr object = readObject(stream, ch); if (object.isNull()) { break; } array->addObject(object); } std::cerr << "PdfArrayObject " << array->size() << "\n"; return array; } default: break; } std::string buffer; stream.read(&ch, 1); while (PdfCharacterTypeTable[(unsigned char)ch] == PDF_CHAR_REGULAR) { buffer += ch; stream.read(&ch, 1); } std::cerr << "buffer = " << buffer << "\n"; return 0; }