From ce0d75f44f24a1f7e7de703f9aadc5e5752fdb79 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Thu, 24 Dec 2020 17:33:45 +0100 Subject: [PATCH] Annotation editation - more complex annotations --- Pdf4QtLib/sources/pdfannotation.cpp | 37 ++- Pdf4QtLib/sources/pdfannotation.h | 4 + Pdf4QtLib/sources/pdfobject.h | 3 + Pdf4QtLib/sources/pdfobjecteditormodel.cpp | 274 +++++++++++++++++++- Pdf4QtLib/sources/pdfobjecteditormodel.h | 38 ++- Pdf4QtLib/sources/pdfobjecteditorwidget.cpp | 31 ++- 6 files changed, 366 insertions(+), 21 deletions(-) diff --git a/Pdf4QtLib/sources/pdfannotation.cpp b/Pdf4QtLib/sources/pdfannotation.cpp index 0803033..4bfbeb5 100644 --- a/Pdf4QtLib/sources/pdfannotation.cpp +++ b/Pdf4QtLib/sources/pdfannotation.cpp @@ -924,21 +924,21 @@ PDFAnnotationQuadrilaterals PDFAnnotation::parseQuadrilaterals(const PDFObjectSt return PDFAnnotationQuadrilaterals(qMove(path), qMove(quadrilaterals)); } +constexpr const std::array, 10> lineEndings = { + std::pair{ AnnotationLineEnding::None, "None" }, + std::pair{ AnnotationLineEnding::Square, "Square" }, + std::pair{ AnnotationLineEnding::Circle, "Circle" }, + std::pair{ AnnotationLineEnding::Diamond, "Diamond" }, + std::pair{ AnnotationLineEnding::OpenArrow, "OpenArrow" }, + std::pair{ AnnotationLineEnding::ClosedArrow, "ClosedArrow" }, + std::pair{ AnnotationLineEnding::Butt, "Butt" }, + std::pair{ AnnotationLineEnding::ROpenArrow, "ROpenArrow" }, + std::pair{ AnnotationLineEnding::RClosedArrow, "RClosedArrow" }, + std::pair{ AnnotationLineEnding::Slash, "Slash" } +}; + AnnotationLineEnding PDFAnnotation::convertNameToLineEnding(const QByteArray& name) { - constexpr const std::array, 10> lineEndings = { - std::pair{ AnnotationLineEnding::None, "None" }, - std::pair{ AnnotationLineEnding::Square, "Square" }, - std::pair{ AnnotationLineEnding::Circle, "Circle" }, - std::pair{ AnnotationLineEnding::Diamond, "Diamond" }, - std::pair{ AnnotationLineEnding::OpenArrow, "OpenArrow" }, - std::pair{ AnnotationLineEnding::ClosedArrow, "ClosedArrow" }, - std::pair{ AnnotationLineEnding::Butt, "Butt" }, - std::pair{ AnnotationLineEnding::ROpenArrow, "ROpenArrow" }, - std::pair{ AnnotationLineEnding::RClosedArrow, "RClosedArrow" }, - std::pair{ AnnotationLineEnding::Slash, "Slash" } - }; - auto it = std::find_if(lineEndings.cbegin(), lineEndings.cend(), [&name](const auto& item) { return name == item.second; }); if (it != lineEndings.cend()) { @@ -948,6 +948,17 @@ AnnotationLineEnding PDFAnnotation::convertNameToLineEnding(const QByteArray& na return AnnotationLineEnding::None; } +QByteArray PDFAnnotation::convertLineEndingToName(AnnotationLineEnding lineEnding) +{ + auto it = std::find_if(lineEndings.cbegin(), lineEndings.cend(), [lineEnding](const auto& item) { return lineEnding == item.first; }); + if (it != lineEndings.cend()) + { + return it->second; + } + + return lineEndings.front().second; +} + QColor PDFAnnotation::getStrokeColor() const { return getDrawColorFromAnnotationColor(getColor(), getStrokeOpacity()); diff --git a/Pdf4QtLib/sources/pdfannotation.h b/Pdf4QtLib/sources/pdfannotation.h index ed145a4..6cad000 100644 --- a/Pdf4QtLib/sources/pdfannotation.h +++ b/Pdf4QtLib/sources/pdfannotation.h @@ -587,6 +587,10 @@ public: /// \param name Name of the line ending static AnnotationLineEnding convertNameToLineEnding(const QByteArray& name); + /// Converts line ending to name. + /// \param lineEnding Line ending + static QByteArray convertLineEndingToName(AnnotationLineEnding lineEnding); + /// Returns draw color from defined annotation color. If color is incorrectly /// defined, then black color is returned. /// \param color Color (can have 1, 3 and 4 components) diff --git a/Pdf4QtLib/sources/pdfobject.h b/Pdf4QtLib/sources/pdfobject.h index 2582410..d07d986 100644 --- a/Pdf4QtLib/sources/pdfobject.h +++ b/Pdf4QtLib/sources/pdfobject.h @@ -320,6 +320,9 @@ public: /// then it throws an exception. const PDFObject& getItem(size_t index) const { return m_objects.at(index); } + /// Sets item at the specified index. Index must be valid. + void setItem(PDFObject value, size_t index) { m_objects[index] = qMove(value); } + /// Returns size of the array (number of elements) size_t getCount() const { return m_objects.size(); } diff --git a/Pdf4QtLib/sources/pdfobjecteditormodel.cpp b/Pdf4QtLib/sources/pdfobjecteditormodel.cpp index 09609d7..bfa0830 100644 --- a/Pdf4QtLib/sources/pdfobjecteditormodel.cpp +++ b/Pdf4QtLib/sources/pdfobjecteditormodel.cpp @@ -74,6 +74,21 @@ bool PDFObjectEditorAbstractModel::queryAttribute(size_t index, Question questio case Question::IsMapped: return !attribute.attributeFlags.testFlag(PDFObjectEditorModelAttribute::Hidden) && attribute.type != ObjectEditorAttributeType::Constant; + case Question::IsVisible: + { + if (!queryAttribute(index, Question::IsMapped)) + { + return false; + } + + if (!attribute.attributeFlags.testFlag(PDFObjectEditorModelAttribute::HideInsteadOfDisable)) + { + return true; + } + + return queryAttribute(index, Question::HasAttribute); + } + case Question::HasAttribute: { // Check type flags @@ -98,6 +113,33 @@ bool PDFObjectEditorAbstractModel::queryAttribute(size_t index, Question questio return true; } + case Question::HasSimilarAttribute: + { + if (!queryAttribute(index, Question::HasAttribute)) + { + // Find similar attributes + if (queryAttribute(index, Question::IsPersisted)) + { + auto it = m_similarAttributes.find(index); + if (it != m_similarAttributes.cend()) + { + const std::vector& similarAttributes = it->second; + for (const size_t similarAttribute : similarAttributes) + { + if (queryAttribute(similarAttribute, Question::HasAttribute) && queryAttribute(similarAttribute, Question::IsPersisted)) + { + return true; + } + } + } + } + + return false; + } + + return true; + } + case Question::IsAttributeEditable: return queryAttribute(index, Question::HasAttribute) && !attribute.attributeFlags.testFlag(PDFObjectEditorModelAttribute::Readonly); @@ -158,7 +200,7 @@ std::vector PDFObjectEditorAbstractModel::getSelectorDependentAttributes return result; } -PDFObject PDFObjectEditorAbstractModel::getValue(size_t index) const +PDFObject PDFObjectEditorAbstractModel::getValue(size_t index, bool resolveArrayIndex) const { const QByteArrayList& dictionaryAttribute = m_attributes.at(index).dictionaryAttribute; if (dictionaryAttribute.isEmpty()) @@ -181,6 +223,22 @@ PDFObject PDFObjectEditorAbstractModel::getValue(size_t index) const } } + const size_t arrayIndex = m_attributes.at(index).arrayIndex; + if (arrayIndex && resolveArrayIndex) + { + PDFObject object = m_storage->getObject(dictionary->get(dictionaryAttribute.back())); + if (object.isArray()) + { + const PDFArray* arrayObject = object.getArray(); + if (arrayIndex <= arrayObject->getCount()) + { + return arrayObject->getItem(arrayIndex - 1); + } + } + + return PDFObject(); + } + return dictionary->get(dictionaryAttribute.back()); } @@ -218,7 +276,30 @@ PDFObject PDFObjectEditorAbstractModel::writeAttributeValueToObject(size_t attri } factory.beginDictionaryItem(dictionaryAttribute.back()); - factory << qMove(value); + size_t arrayIndex = m_attributes.at(attribute).arrayIndex; + if (arrayIndex) + { + PDFArray array; + PDFObject arrayObject = m_storage->getObject(getValue(attribute, false)); + if (arrayObject.isArray()) + { + array = *arrayObject.getArray(); + } + + --arrayIndex; + + while (arrayIndex >= array.getCount()) + { + array.appendItem(PDFObject()); + } + + array.setItem(qMove(value), arrayIndex); + factory << PDFObject::createArray(std::make_shared(qMove(array))); + } + else + { + factory << qMove(value); + } factory.endDictionaryItem(); for (int i = 0; i < pathDepth; ++i) @@ -241,6 +322,35 @@ QVariant PDFObjectEditorAbstractModel::getMaximumValue(size_t index) const return m_attributes.at(index).maxValue; } +void PDFObjectEditorAbstractModel::initialize() +{ + const size_t attributeCount = getAttributeCount(); + std::map> similarAttributes; + + for (size_t attribute = 0; attribute < attributeCount; ++attribute) + { + if (!queryAttribute(attribute, Question::IsPersisted)) + { + // Non-persisted attributes are skipped + continue; + } + + similarAttributes[m_attributes[attribute].dictionaryAttribute].push_back(attribute); + } + + for (const auto& similarAttributeItem : similarAttributes) + { + const std::vector& attributes = similarAttributeItem.second; + if (attributes.size() > 1) + { + for (const size_t attribute : attributes) + { + m_similarAttributes[attribute] = attributes; + } + } + } +} + void PDFObjectEditorAbstractModel::updateSelectorValues() { // Turn on selectors, which have some dependent attribute, @@ -255,7 +365,7 @@ void PDFObjectEditorAbstractModel::updateSelectorValues() bool hasPersistedAttribute = false; for (size_t dependentAttribute : getSelectorDependentAttributes(index)) { - if (!getValue(dependentAttribute).isNull()) + if (!getValue(dependentAttribute, true).isNull()) { hasPersistedAttribute = true; break; @@ -301,6 +411,36 @@ size_t PDFObjectEditorAbstractModel::createAttribute(ObjectEditorAttributeType t return index; } +size_t PDFObjectEditorAbstractModel::createAttribute(ObjectEditorAttributeType type, + QByteArrayList attributesName, + QString category, + QString subcategory, + QString name, + PDFObject defaultValue, + uint32_t typeFlags, + PDFObjectEditorModelAttribute::Flags flags) +{ + size_t index = m_attributes.size(); + + PDFObjectEditorModelAttribute attribute; + attribute.type = type; + attribute.dictionaryAttribute = qMove(attributesName); + attribute.category = qMove(category); + attribute.subcategory = qMove(subcategory); + attribute.name = qMove(name); + attribute.defaultValue = qMove(defaultValue); + attribute.typeFlags = typeFlags; + attribute.attributeFlags = flags; + m_attributes.emplace_back(qMove(attribute)); + + if (type == ObjectEditorAttributeType::Type) + { + m_typeAttribute = index; + } + + return index; +} + size_t PDFObjectEditorAbstractModel::createSelectorAttribute(QString category, QString subcategory, QString name) { return createAttribute(ObjectEditorAttributeType::Selector, QByteArray(), qMove(category), qMove(subcategory), qMove(name)); @@ -308,7 +448,7 @@ size_t PDFObjectEditorAbstractModel::createSelectorAttribute(QString category, Q uint32_t PDFObjectEditorAbstractModel::getCurrentTypeFlags() const { - PDFObject value = getValue(m_typeAttribute); + PDFObject value = getValue(m_typeAttribute, true); for (const PDFObjectEditorModelAttributeEnumItem& item : m_attributes.at(m_typeAttribute).enumItems) { @@ -381,6 +521,8 @@ PDFObjectEditorAnnotationsModel::PDFObjectEditorAnnotationsModel(QObject* parent size_t appearanceSelector = createSelectorAttribute(tr("General"), tr("Options"), tr("Modify appearance")); createAttribute(ObjectEditorAttributeType::Color, "C", tr("Appearance"), tr("Colors"), tr("Color"), getDefaultColor()); m_attributes.back().selectorAttribute = appearanceSelector; + createAttribute(ObjectEditorAttributeType::Color, "IC", tr("Appearance"), tr("Colors"), tr("Interior color"), getDefaultColor(), Line | Circle | Square | Polygon | PolyLine); + m_attributes.back().selectorAttribute = appearanceSelector; createAttribute(ObjectEditorAttributeType::ComboBox, "BM", tr("Appearance"), tr("Transparency"), tr("Blend mode"), PDFObject::createName("Normal")); m_attributes.back().selectorAttribute = appearanceSelector; @@ -403,6 +545,34 @@ PDFObjectEditorAnnotationsModel::PDFObjectEditorAnnotationsModel(QObject* parent createAttribute(ObjectEditorAttributeType::TextLine, "Lang", tr("General"), tr("General"), tr("Language")); + // Border style/effect + size_t borderSelector = createSelectorAttribute(tr("General"), tr("Options"), tr("Modify border")); + createAttribute(ObjectEditorAttributeType::Double, QByteArrayList() << "BS" << "W", tr("Border"), tr("Border Style"), tr("Width"), PDFObject::createReal(0.0), Link | Line | Circle | Square | Polygon | PolyLine); + m_attributes.back().selectorAttribute = borderSelector; + m_attributes.back().minValue = 0.0; + + createAttribute(ObjectEditorAttributeType::ComboBox, QByteArrayList() << "BS" << "S", tr("Border"), tr("Border Style"), tr("Style"), PDFObject::createName("S"), Link | Line | Circle | Square | Polygon | PolyLine); + PDFObjectEditorModelAttributeEnumItems borderStyleEnumItems; + borderStyleEnumItems.emplace_back(tr("Solid"), 1, PDFObject::createName("S")); + borderStyleEnumItems.emplace_back(tr("Dashed"), 2, PDFObject::createName("D")); + borderStyleEnumItems.emplace_back(tr("Beveled"), 3, PDFObject::createName("B")); + borderStyleEnumItems.emplace_back(tr("Inset"), 4, PDFObject::createName("I")); + borderStyleEnumItems.emplace_back(tr("Underline"), 5, PDFObject::createName("U")); + m_attributes.back().selectorAttribute = borderSelector; + m_attributes.back().enumItems = qMove(borderStyleEnumItems); + + createAttribute(ObjectEditorAttributeType::ComboBox, QByteArrayList() << "BE" << "S", tr("Border"), tr("Border Effect"), tr("Style"), PDFObject::createName("S"), FreeText | Circle | Square | Polygon); + PDFObjectEditorModelAttributeEnumItems borderEffectEnumItems; + borderEffectEnumItems.emplace_back(tr("Cloudy"), 1, PDFObject::createName("C")); + borderEffectEnumItems.emplace_back(tr("None"), 2, PDFObject::createName("S")); + m_attributes.back().selectorAttribute = borderSelector; + m_attributes.back().enumItems = qMove(borderEffectEnumItems); + + createAttribute(ObjectEditorAttributeType::Double, QByteArrayList() << "BE" << "I", tr("Border"), tr("Border Effect"), tr("Intensity"), PDFObject::createReal(0.0), FreeText | Circle | Square | Polygon); + m_attributes.back().selectorAttribute = borderSelector; + m_attributes.back().minValue = 0.0; + m_attributes.back().maxValue = 2.0; + // Sticky note annotation createAttribute(ObjectEditorAttributeType::ComboBox, "Name", tr("Sticky note"), tr("Sticky note"), tr("Type"), PDFObject::createName("Note"), Text); @@ -416,7 +586,101 @@ PDFObjectEditorAnnotationsModel::PDFObjectEditorAnnotationsModel(QObject* parent stickyNoteEnum.emplace_back(tr("Insert"), 64, PDFObject::createName("Insert")); m_attributes.back().enumItems = qMove(stickyNoteEnum); - createAttribute(ObjectEditorAttributeType::Boolean, "Name", tr("Sticky note"), tr("Sticky note"), tr("Open"), PDFObject::createBool(false), Text); + createAttribute(ObjectEditorAttributeType::Boolean, "Open", tr("Sticky note"), tr("Sticky note"), tr("Open"), PDFObject::createBool(false), Text); + + // Link annotation + createAttribute(ObjectEditorAttributeType::ComboBox, "H", tr("Link"), tr("Style"), tr("Highlight"), PDFObject::createName("I"), Link); + + PDFObjectEditorModelAttributeEnumItems linkHighlightEnumValues; + linkHighlightEnumValues.emplace_back(tr("None"), 0, PDFObject::createName("N")); + linkHighlightEnumValues.emplace_back(tr("Invert"), 2, PDFObject::createName("I")); + linkHighlightEnumValues.emplace_back(tr("Outline"), 4, PDFObject::createName("O")); + linkHighlightEnumValues.emplace_back(tr("Push"), 8, PDFObject::createName("P")); + m_attributes.back().enumItems = qMove(linkHighlightEnumValues); + + // Free text annotation + createQuaddingAttribute("Q", tr("Free text"), tr("Style"), tr("Alignment"), FreeText); + + createAttribute(ObjectEditorAttributeType::ComboBox, "IT", tr("Free text"), tr("Style"), tr("Intent"), PDFObject::createName("FreeText"), FreeText); + PDFObjectEditorModelAttributeEnumItems freeTextIntent; + freeTextIntent.emplace_back(tr("Free text"), 0, PDFObject::createName("FreeText")); + freeTextIntent.emplace_back(tr("Callout"), 1, PDFObject::createName("FreeTextCallout")); + freeTextIntent.emplace_back(tr("Typewriter"), 2, PDFObject::createName("FreeTextTypeWriter")); + m_attributes.back().enumItems = qMove(freeTextIntent); + + createLineEndingAttribute("LE", tr("Free text"), tr("Style"), tr("Callout line ending"), FreeText); + + // Line annotation + createLineEndingAttribute("LE", tr("Line"), tr("Style"), tr("Line start"), Line | PolyLine); + m_attributes.back().arrayIndex = 1; + createLineEndingAttribute("LE", tr("Line"), tr("Style"), tr("Line end"), Line | PolyLine); + m_attributes.back().arrayIndex = 2; + + createAttribute(ObjectEditorAttributeType::Double, "LL", tr("Line"), tr("Style"), tr("Leader line length"), PDFObject::createReal(0.0), Line, PDFObjectEditorModelAttribute::HideInsteadOfDisable); + m_attributes.back().minValue = 0.0; + + createAttribute(ObjectEditorAttributeType::Double, "LLE", tr("Line"), tr("Style"), tr("Leader line extension"), PDFObject::createReal(0.0), Line, PDFObjectEditorModelAttribute::HideInsteadOfDisable); + m_attributes.back().minValue = 0.0; + + createAttribute(ObjectEditorAttributeType::Double, "LLO", tr("Line"), tr("Style"), tr("Leader line offset"), PDFObject::createReal(0.0), Line, PDFObjectEditorModelAttribute::HideInsteadOfDisable); + m_attributes.back().minValue = 0.0; + + createAttribute(ObjectEditorAttributeType::ComboBox, "IT", tr("Line"), tr("Style"), tr("Intent"), PDFObject::createName("LineArrow"), Line, PDFObjectEditorModelAttribute::HideInsteadOfDisable); + PDFObjectEditorModelAttributeEnumItems lineIntent; + lineIntent.emplace_back(tr("Arrow"), 0, PDFObject::createName("LineArrow")); + lineIntent.emplace_back(tr("Dimension"), 1, PDFObject::createName("LineDimension")); + m_attributes.back().enumItems = qMove(lineIntent); + + createAttribute(ObjectEditorAttributeType::ComboBox, "IT", tr("Line"), tr("Style"), tr("Intent"), PDFObject(), Polygon | PolyLine, PDFObjectEditorModelAttribute::HideInsteadOfDisable); + PDFObjectEditorModelAttributeEnumItems polygonIntent; + polygonIntent.emplace_back(tr("None"), 0, PDFObject()); + polygonIntent.emplace_back(tr("Cloud"), 1, PDFObject::createName("PolygonCloud")); + polygonIntent.emplace_back(tr("Line dimension"), 2, PDFObject::createName("PolyLineDimension")); + polygonIntent.emplace_back(tr("Polygon dimension"), 3, PDFObject::createName("PolygonDimension")); + m_attributes.back().enumItems = qMove(polygonIntent); + + createAttribute(ObjectEditorAttributeType::Boolean, "Cap", tr("Line"), tr("Text"), tr("Caption"), PDFObject::createBool(false), Line); + + createAttribute(ObjectEditorAttributeType::ComboBox, "CP", tr("Line"), tr("Text"), tr("Caption position"), PDFObject::createName("Inline"), Line); + PDFObjectEditorModelAttributeEnumItems lineCaptionPosition; + lineCaptionPosition.emplace_back(tr("Inline"), 0, PDFObject::createName("Inline")); + lineCaptionPosition.emplace_back(tr("Top"), 1, PDFObject::createName("Top")); + m_attributes.back().enumItems = qMove(lineCaptionPosition); + + initialize(); +} + +size_t PDFObjectEditorAnnotationsModel::createQuaddingAttribute(QByteArray attributeName, QString category, QString subcategory, QString name, uint32_t typeFlags) +{ + size_t attribute = createAttribute(ObjectEditorAttributeType::ComboBox, qMove(attributeName), qMove(category), qMove(subcategory), qMove(name), PDFObject::createInteger(0), typeFlags); + + PDFObjectEditorModelAttributeEnumItems quaddingEnumValues; + quaddingEnumValues.emplace_back(tr("Left"), 0, PDFObject::createInteger(0)); + quaddingEnumValues.emplace_back(tr("Center"), 1, PDFObject::createInteger(1)); + quaddingEnumValues.emplace_back(tr("Right"), 2, PDFObject::createInteger(2)); + m_attributes.back().enumItems = qMove(quaddingEnumValues); + + return attribute; +} + +size_t PDFObjectEditorAnnotationsModel::createLineEndingAttribute(QByteArray attributeName, QString category, QString subcategory, QString name, uint32_t typeFlags) +{ + size_t attribute = createAttribute(ObjectEditorAttributeType::ComboBox, qMove(attributeName), qMove(category), qMove(subcategory), qMove(name), PDFObject::createInteger(0), typeFlags); + + PDFObjectEditorModelAttributeEnumItems lineEndingEnumValues; + lineEndingEnumValues.emplace_back(tr("None"), 0, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::None))); + lineEndingEnumValues.emplace_back(tr("Square"), 1, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::Square))); + lineEndingEnumValues.emplace_back(tr("Circle"), 2, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::Circle))); + lineEndingEnumValues.emplace_back(tr("Diamond"), 3, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::Diamond))); + lineEndingEnumValues.emplace_back(tr("Open arrow"), 4, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::OpenArrow))); + lineEndingEnumValues.emplace_back(tr("Closed arrow"), 5, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::ClosedArrow))); + lineEndingEnumValues.emplace_back(tr("Butt"), 6, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::Butt))); + lineEndingEnumValues.emplace_back(tr("Reversed open arrow"), 7, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::ROpenArrow))); + lineEndingEnumValues.emplace_back(tr("Reversed closed arrow"), 8, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::RClosedArrow))); + lineEndingEnumValues.emplace_back(tr("Slash"), 9, PDFObject::createName(PDFAnnotation::convertLineEndingToName(AnnotationLineEnding::Slash))); + m_attributes.back().enumItems = qMove(lineEndingEnumValues); + + return attribute; } } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfobjecteditormodel.h b/Pdf4QtLib/sources/pdfobjecteditormodel.h index 83da54d..9d54bba 100644 --- a/Pdf4QtLib/sources/pdfobjecteditormodel.h +++ b/Pdf4QtLib/sources/pdfobjecteditormodel.h @@ -98,6 +98,10 @@ struct PDFObjectEditorModelAttribute /// can select the attribute value. size_t selectorAttribute = 0; + /// If this value is nonzero, it marks that value is stored in the array, + /// with 1-based index arrayIndex. + size_t arrayIndex = 0; + QVariant minValue; QVariant maxValue; @@ -131,7 +135,9 @@ public: IsMapped, ///< Is attribute mapped in gui? IsSelector, ///< Is attribute's role a selector for other attributes? IsPersisted, ///< Is attribute persisted to edited object? + IsVisible, ///< Is attribute visible (perhaps disabled)? HasAttribute, ///< Does current object has given attribute? + HasSimilarAttribute, ///< Does current object has some attribute, which is persisted to same dictionary item as current attribute? IsAttributeEditable ///< Is current attribute editable (this implies previous flag) ? }; @@ -147,7 +153,13 @@ public: /// by this selector. std::vector getSelectorDependentAttributes(size_t selector) const; - PDFObject getValue(size_t index) const; + /// Returns value stored in the object. If array index of the attribute + /// is specified, and resolveArrayIndex is true, then value from the array + /// is retrieved. Otherwise, array itself is returned. + /// \param index Attribute index + /// \param resolveArrayIndex For array attribute, retrieve array item (true), or array itself (false) + PDFObject getValue(size_t index, bool resolveArrayIndex) const; + PDFObject getDefaultValue(size_t index) const; PDFObject getEditedObject() const { return m_editedObject; } void setEditedObject(PDFObject object); @@ -172,6 +184,7 @@ signals: void editedObjectChanged(); protected: + void initialize(); void updateSelectorValues(); size_t createAttribute(ObjectEditorAttributeType type, @@ -183,6 +196,15 @@ protected: uint32_t typeFlags = 0, PDFObjectEditorModelAttribute::Flags flags = PDFObjectEditorModelAttribute::None); + size_t createAttribute(ObjectEditorAttributeType type, + QByteArrayList attributesName, + QString category, + QString subcategory, + QString name, + PDFObject defaultValue = PDFObject(), + uint32_t typeFlags = 0, + PDFObjectEditorModelAttribute::Flags flags = PDFObjectEditorModelAttribute::None); + size_t createSelectorAttribute(QString category, QString subcategory, QString name); @@ -196,6 +218,7 @@ protected: PDFObject m_editedObject; const PDFObjectStorage* m_storage; size_t m_typeAttribute; + std::map> m_similarAttributes; }; class Pdf4QtLIBSHARED_EXPORT PDFObjectEditorAnnotationsModel : public PDFObjectEditorAbstractModel @@ -230,6 +253,19 @@ private: public: explicit PDFObjectEditorAnnotationsModel(QObject* parent); + +private: + size_t createQuaddingAttribute(QByteArray attributeName, + QString category, + QString subcategory, + QString name, + uint32_t typeFlags = 0); + + size_t createLineEndingAttribute(QByteArray attributeName, + QString category, + QString subcategory, + QString name, + uint32_t typeFlags = 0); }; } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfobjecteditorwidget.cpp b/Pdf4QtLib/sources/pdfobjecteditorwidget.cpp index 312473b..737d2b6 100644 --- a/Pdf4QtLib/sources/pdfobjecteditorwidget.cpp +++ b/Pdf4QtLib/sources/pdfobjecteditorwidget.cpp @@ -115,6 +115,7 @@ void PDFObjectEditorWidgetMapper::initialize(QTabWidget* tabWidget) for (Subcategory& subcategory : category.subcategories) { QGroupBox* groupBox = new QGroupBox(category.page); + groupBox->setTitle(subcategory.name); category.page->layout()->addWidget(groupBox); QGridLayout* layout = new QGridLayout(); @@ -159,7 +160,7 @@ void PDFObjectEditorWidgetMapper::loadWidgets() } else { - PDFObject object = m_model->getValue(attribute); + PDFObject object = m_model->getValue(attribute, true); if (object.isNull()) { object = m_model->getDefaultValue(attribute); @@ -282,7 +283,7 @@ void PDFObjectEditorWidgetMapper::onCommitRequested(size_t attribute) continue; } - if (!m_model->queryAttribute(i, PDFObjectEditorAbstractModel::Question::HasAttribute)) + if (!m_model->queryAttribute(i, PDFObjectEditorAbstractModel::Question::HasSimilarAttribute)) { object = m_model->writeAttributeValueToObject(i, object, PDFObject()); } @@ -583,7 +584,10 @@ void PDFObjectEditorMappedComboBoxAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_comboBox->setHidden(!isVisible); m_comboBox->setEnabled(enabled && !readonly); } @@ -619,7 +623,10 @@ void PDFObjectEditorMappedLineEditAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_lineEdit->setHidden(!isVisible); m_lineEdit->setEnabled(enabled); m_lineEdit->setReadOnly(readonly); } @@ -661,7 +668,10 @@ void PDFObjectEditorMappedTextBrowserAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_textBrowser->setHidden(!isVisible); m_textBrowser->setEnabled(enabled); m_textBrowser->setReadOnly(readonly); } @@ -692,7 +702,10 @@ void PDFObjectEditorMappedRectangleAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_pushButton->setHidden(!isVisible); m_pushButton->setEnabled(enabled && !readonly); } @@ -735,7 +748,10 @@ void PDFObjectEditorMappedDateTimeAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_dateTimeEdit->setHidden(!isVisible); m_dateTimeEdit->setEnabled(enabled); m_dateTimeEdit->setReadOnly(readonly); } @@ -786,9 +802,11 @@ void PDFObjectEditorMappedFlagsAdapter::update() const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); const bool enableCheckbox = enabled && !readonly; + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); for (const auto& item : m_flagCheckBoxes) { + item.second->setHidden(!isVisible); item.second->setEnabled(enableCheckbox); } } @@ -821,7 +839,10 @@ void PDFObjectEditorMappedCheckBoxAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_checkBox->setHidden(!isVisible); m_checkBox->setEnabled(enabled && !readonly); } @@ -855,7 +876,10 @@ void PDFObjectEditorMappedDoubleAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_spinBox->setHidden(!isVisible); m_spinBox->setEnabled(enabled); m_spinBox->setReadOnly(readonly); } @@ -926,7 +950,10 @@ void PDFObjectEditorMappedColorAdapter::update() { const bool enabled = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::HasAttribute); const bool readonly = !m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsAttributeEditable); + const bool isVisible = m_model->queryAttribute(m_attribute, PDFObjectEditorAbstractModel::Question::IsVisible); + m_label->setHidden(!isVisible); + m_comboBox->setHidden(!isVisible); m_comboBox->setEnabled(enabled && !readonly); }