// Copyright (C) 2018-2020 Jakub Melka // // This file is part of PdfForQt. // // PdfForQt is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // PdfForQt 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with PDFForQt. If not, see . #include "pdfobject.h" #include "pdfvisitor.h" namespace pdf { QByteArray PDFObject::getString() const { const PDFObjectContentPointer& objectContent = std::get(m_data); Q_ASSERT(dynamic_cast(objectContent.get())); const PDFString* string = static_cast(objectContent.get()); return string->getString(); } const PDFDictionary* PDFObject::getDictionary() const { const PDFObjectContentPointer& objectContent = std::get(m_data); Q_ASSERT(dynamic_cast(objectContent.get())); return static_cast(objectContent.get()); } const PDFString* PDFObject::getStringObject() const { const PDFObjectContentPointer& objectContent = std::get(m_data); Q_ASSERT(dynamic_cast(objectContent.get())); return static_cast(objectContent.get()); } const PDFStream* PDFObject::getStream() const { const PDFObjectContentPointer& objectContent = std::get(m_data); Q_ASSERT(dynamic_cast(objectContent.get())); return static_cast(objectContent.get()); } const PDFArray* PDFObject::getArray() const { const PDFObjectContentPointer& objectContent = std::get(m_data); Q_ASSERT(dynamic_cast(objectContent.get())); return static_cast(objectContent.get()); } bool PDFObject::operator==(const PDFObject &other) const { if (m_type == other.m_type) { Q_ASSERT(std::holds_alternative(m_data) == std::holds_alternative(other.m_data)); // If we have content object defined, then use its equal operator, // otherwise use default compare operator. The only problem with // default compare operator can occur, when we have a double // with NaN value. Then operator == can return false, even if // values are "equal" (NaN == NaN returns false) if (std::holds_alternative(m_data)) { Q_ASSERT(std::get(m_data)); return std::get(m_data)->equals(std::get(other.m_data).get()); } return m_data == other.m_data; } return false; } void PDFObject::accept(PDFAbstractVisitor* visitor) const { switch (m_type) { case Type::Null: visitor->visitNull(); break; case Type::Bool: visitor->visitBool(getBool()); break; case Type::Int: visitor->visitInt(getInteger()); break; case Type::Real: visitor->visitReal(getReal()); break; case Type::String: visitor->visitString(getStringObject()); break; case Type::Name: visitor->visitName(getStringObject()); break; case Type::Array: visitor->visitArray(getArray()); break; case Type::Dictionary: visitor->visitDictionary(getDictionary()); break; case Type::Stream: visitor->visitStream(getStream()); break; case Type::Reference: visitor->visitReference(getReference()); break; default: Q_ASSERT(false); } } bool PDFString::equals(const PDFObjectContent* other) const { Q_ASSERT(dynamic_cast(other)); const PDFString* otherString = static_cast(other); return m_string == otherString->m_string; } void PDFString::setString(const QByteArray& string) { m_string = string; } void PDFString::optimize() { m_string.shrink_to_fit(); } bool PDFArray::equals(const PDFObjectContent* other) const { Q_ASSERT(dynamic_cast(other)); const PDFArray* otherArray = static_cast(other); return m_objects == otherArray->m_objects; } void PDFArray::appendItem(PDFObject object) { m_objects.push_back(std::move(object)); } void PDFArray::optimize() { m_objects.shrink_to_fit(); } bool PDFDictionary::equals(const PDFObjectContent* other) const { Q_ASSERT(dynamic_cast(other)); const PDFDictionary* otherStream = static_cast(other); return m_dictionary == otherStream->m_dictionary; } const PDFObject& PDFDictionary::get(const QByteArray& key) const { auto it = find(key); if (it != m_dictionary.cend()) { return it->second; } else { static PDFObject dummy; return dummy; } } const PDFObject& PDFDictionary::get(const char* key) const { auto it = find(key); if (it != m_dictionary.cend()) { return it->second; } else { static PDFObject dummy; return dummy; } } void PDFDictionary::setEntry(const QByteArray& key, PDFObject&& value) { auto it = find(key); if (it != m_dictionary.end()) { it->second = qMove(value); } else { addEntry(QByteArray(key), qMove(value)); } } void PDFDictionary::removeNullObjects() { m_dictionary.erase(std::remove_if(m_dictionary.begin(), m_dictionary.end(), [](const DictionaryEntry& entry) { return entry.second.isNull(); }), m_dictionary.end()); m_dictionary.shrink_to_fit(); } void PDFDictionary::optimize() { m_dictionary.shrink_to_fit(); for (DictionaryEntry& entry : m_dictionary) { entry.first.shrink_to_fit(); } } std::vector::const_iterator PDFDictionary::find(const QByteArray& key) const { return std::find_if(m_dictionary.cbegin(), m_dictionary.cend(), [&key](const DictionaryEntry& entry) { return entry.first == key; }); } std::vector::iterator PDFDictionary::find(const QByteArray& key) { return std::find_if(m_dictionary.begin(), m_dictionary.end(), [&key](const DictionaryEntry& entry) { return entry.first == key; }); } std::vector::const_iterator PDFDictionary::find(const char* key) const { return std::find_if(m_dictionary.cbegin(), m_dictionary.cend(), [&key](const DictionaryEntry& entry) { return entry.first == key; }); } bool PDFStream::equals(const PDFObjectContent* other) const { Q_ASSERT(dynamic_cast(other)); const PDFStream* otherStream = static_cast(other); return m_dictionary.equals(&otherStream->m_dictionary) && m_content == otherStream->m_content; } PDFObject PDFObjectManipulator::merge(PDFObject left, PDFObject right, MergeFlags flags) { const bool leftHasDictionary = left.isDictionary() || left.isStream(); if (left.getType() != right.getType() && (leftHasDictionary != right.isDictionary())) { return right; } if (left.isStream()) { Q_ASSERT(right.isDictionary() || right.isStream()); const PDFStream* leftStream = left.getStream(); const PDFStream* rightStream = right.isStream() ? right.getStream() : nullptr; PDFDictionary targetDictionary = *leftStream->getDictionary(); const PDFDictionary& sourceDictionary = rightStream ? *rightStream->getDictionary() : *right.getDictionary(); for (size_t i = 0, count = sourceDictionary.getCount(); i < count; ++i) { const QByteArray& key = sourceDictionary.getKey(i); PDFObject value = merge(targetDictionary.get(key), sourceDictionary.getValue(i), flags); targetDictionary.setEntry(key, qMove(value)); } if (flags.testFlag(RemoveNullObjects)) { targetDictionary.removeNullObjects(); } return PDFObject::createStream(std::make_shared(qMove(targetDictionary), QByteArray(rightStream ? *rightStream->getContent() : *leftStream->getContent()))); } if (left.isDictionary()) { Q_ASSERT(right.isDictionary()); PDFDictionary targetDictionary = *left.getDictionary(); const PDFDictionary& sourceDictionary = *right.getDictionary(); for (size_t i = 0, count = sourceDictionary.getCount(); i < count; ++i) { const QByteArray& key = sourceDictionary.getKey(i); PDFObject value = merge(targetDictionary.get(key), sourceDictionary.getValue(i), flags); targetDictionary.setEntry(key, qMove(value)); } if (flags.testFlag(RemoveNullObjects)) { targetDictionary.removeNullObjects(); } return PDFObject::createDictionary(std::make_shared(qMove(targetDictionary))); } else if (left.isArray() && flags.testFlag(ConcatenateArrays)) { // Concatenate arrays const PDFArray* leftArray = left.getArray(); const PDFArray* rightArray = right.getArray(); std::vector objects; objects.reserve(leftArray->getCount() + rightArray->getCount()); for (size_t i = 0, count = leftArray->getCount(); i < count; ++i) { objects.emplace_back(leftArray->getItem(i)); } for (size_t i = 0, count = rightArray->getCount(); i < count; ++i) { objects.emplace_back(rightArray->getItem(i)); } return PDFObject::createArray(std::make_shared(qMove(objects))); } return right; } PDFObject PDFObjectManipulator::removeNullObjects(PDFObject object) { return merge(object, object, RemoveNullObjects); } } // namespace pdf