From 35ffc256f3544f1ff53c2a9241b8b4bede5536bf Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 22 Mar 2020 18:19:52 +0100 Subject: [PATCH] Text annotation --- CodeGenerator/codegenerator.cpp | 5 +- CodeGenerator/codegenerator.h | 3 +- PdfExampleGenerator/pdfexamplesgenerator.cpp | 26 +- PdfForQtLib/sources/pdfannotation.h | 11 + PdfForQtLib/sources/pdfdocumentbuilder.cpp | 211 +++++++- PdfForQtLib/sources/pdfdocumentbuilder.h | 45 ++ generated_code_definition.xml | 517 +++++++++++++++++++ 7 files changed, 814 insertions(+), 4 deletions(-) diff --git a/CodeGenerator/codegenerator.cpp b/CodeGenerator/codegenerator.cpp index 7ded935..d11f796 100644 --- a/CodeGenerator/codegenerator.cpp +++ b/CodeGenerator/codegenerator.cpp @@ -1209,7 +1209,10 @@ void GeneratedPDFObject::generateSourceCodeImpl(QTextStream& stream, CodeGenerat if (pass == Pass::Enter) { stream << indent << "objectBuilder.beginArray();" << Qt::endl; - stream << indent << writeTo << getValue().trimmed() << ";" << Qt::endl; + for (const QString& arrayPart : getValue().trimmed().split(";")) + { + stream << indent << writeTo << arrayPart << ";" << Qt::endl; + } stream << indent << "objectBuilder.endArray();" << Qt::endl; } break; diff --git a/CodeGenerator/codegenerator.h b/CodeGenerator/codegenerator.h index f32ef6b..78ce5ab 100644 --- a/CodeGenerator/codegenerator.h +++ b/CodeGenerator/codegenerator.h @@ -144,7 +144,8 @@ public: _QPointF, _QRectF, _QColor, - _QVariant + _QVariant, + _TextAnnotationIcon }; Q_ENUM(DataType) diff --git a/PdfExampleGenerator/pdfexamplesgenerator.cpp b/PdfExampleGenerator/pdfexamplesgenerator.cpp index 9c74988..bfc1eb5 100644 --- a/PdfExampleGenerator/pdfexamplesgenerator.cpp +++ b/PdfExampleGenerator/pdfexamplesgenerator.cpp @@ -24,7 +24,31 @@ void PDFExamplesGenerator::generateAnnotationsExample() { pdf::PDFDocumentBuilder builder; - builder.appendPage(QRectF(0, 0, 400, 400)); + pdf::PDFObjectReference page1 = builder.appendPage(QRectF(0, 0, 400, 400)); + builder.createAnnotationText(page1, QRectF(50, 50, 24, 24), pdf::TextAnnotationIcon::Comment, "Title1", "Subject1", "Comment", false); + builder.createAnnotationText(page1, QRectF(50, 100, 24, 24), pdf::TextAnnotationIcon::Help, "Title1", "Subject1", "Help", false); + builder.createAnnotationText(page1, QRectF(50, 150, 24, 24), pdf::TextAnnotationIcon::Insert, "Title1", "Subject1", "Insert", false); + builder.createAnnotationText(page1, QRectF(50, 200, 24, 24), pdf::TextAnnotationIcon::Key, "Title1", "Subject1", "Key", false); + builder.createAnnotationText(page1, QRectF(50, 250, 24, 24), pdf::TextAnnotationIcon::NewParagraph, "Title1", "Subject1", "NewParagraph", false); + builder.createAnnotationText(page1, QRectF(50, 300, 24, 24), pdf::TextAnnotationIcon::Note, "Title1", "Subject1", "Note", false); + builder.createAnnotationText(page1, QRectF(50, 350, 24, 24), pdf::TextAnnotationIcon::Paragraph, "Title1", "Subject1", "Paragraph", false); + builder.createAnnotationText(page1, QRectF(250, 50, 24, 24), pdf::TextAnnotationIcon::Comment, "Title1", "Subject1", "Comment", true); + builder.createAnnotationText(page1, QRectF(250, 100, 24, 24), pdf::TextAnnotationIcon::Help, "Title1", "Subject1", "Help", true); + builder.createAnnotationText(page1, QRectF(250, 150, 24, 24), pdf::TextAnnotationIcon::Insert, "Title1", "Subject1", "Insert", true); + builder.createAnnotationText(page1, QRectF(250, 200, 24, 24), pdf::TextAnnotationIcon::Key, "Title1", "Subject1", "Key", true); + builder.createAnnotationText(page1, QRectF(250, 250, 24, 24), pdf::TextAnnotationIcon::NewParagraph, "Title1", "Subject1", "NewParagraph", true); + builder.createAnnotationText(page1, QRectF(250, 300, 24, 24), pdf::TextAnnotationIcon::Note, "Title1", "Subject1", "Note", true); + builder.createAnnotationText(page1, QRectF(250, 350, 24, 24), pdf::TextAnnotationIcon::Paragraph, "Title1", "Subject1", "Paragraph", true); + + pdf::PDFObjectReference page5 = builder.appendPage(QRectF(0, 0, 400, 400)); + builder.createAnnotationSquare(page5, QRectF(50, 50, 50, 50), 3.0, Qt::green, Qt::red, "Title1", "Subject1", "Contents - green filling, red boundary"); + builder.createAnnotationSquare(page5, QRectF(50, 150, 50, 50), 3.0, QColor(), Qt::red, "Title2", "Subject2", "Contents - red boundary"); + builder.createAnnotationSquare(page5, QRectF(50, 250, 50, 50), 3.0, Qt::green, QColor(), "Title3", "Subject3", "Contents - green filling"); + + pdf::PDFObjectReference page6 = builder.appendPage(QRectF(0, 0, 400, 400)); + builder.createAnnotationCircle(page6, QRectF(50, 50, 50, 50), 3.0, Qt::green, Qt::red, "Title1", "Subject1", "Contents - green filling, red boundary"); + builder.createAnnotationCircle(page6, QRectF(50, 150, 50, 50), 3.0, QColor(), Qt::red, "Title2", "Subject2", "Contents - red boundary"); + builder.createAnnotationCircle(page6, QRectF(50, 250, 50, 50), 3.0, Qt::green, QColor(), "Title3", "Subject3", "Contents - green filling"); // Write result to a file pdf::PDFDocument document = builder.build(); diff --git a/PdfForQtLib/sources/pdfannotation.h b/PdfForQtLib/sources/pdfannotation.h index 2887ee9..8211cab 100644 --- a/PdfForQtLib/sources/pdfannotation.h +++ b/PdfForQtLib/sources/pdfannotation.h @@ -508,6 +508,17 @@ private: PDFObject m_externalData; }; +enum class TextAnnotationIcon +{ + Comment, + Help, + Insert, + Key, + NewParagraph, + Note, + Paragraph +}; + /// Text annotation represents note attached to a specific point in the PDF /// document. It appears as icon, and it is not zoomed, or rotated (behaves /// as if flag NoZoom and NoRotate were set). When this annotation is opened, diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index 52a9223..f67b83f 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -64,6 +64,61 @@ void PDFObjectFactory::endDictionaryItem() std::get(dictionaryItem.object).addEntry(qMove(topItem.itemName), qMove(std::get(topItem.object))); } +PDFObjectFactory& PDFObjectFactory::operator<<(TextAnnotationIcon icon) +{ + switch (icon) + { + case TextAnnotationIcon::Comment: + { + *this << WrapName("Comment"); + break; + } + + case TextAnnotationIcon::Help: + { + *this << WrapName("Help"); + break; + } + + case TextAnnotationIcon::Insert: + { + *this << WrapName("Insert"); + break; + } + + case TextAnnotationIcon::Key: + { + *this << WrapName("Key"); + break; + } + + case TextAnnotationIcon::NewParagraph: + { + *this << WrapName("NewParagraph"); + break; + } + + case TextAnnotationIcon::Note: + { + *this << WrapName("Note"); + break; + } + + case TextAnnotationIcon::Paragraph: + { + *this << WrapName("Paragraph"); + break; + } + + default: + { + Q_ASSERT(false); + } + } + + return *this; +} + PDFObjectFactory& PDFObjectFactory::operator<<(WrapEmptyArray) { beginArray(); @@ -276,7 +331,9 @@ void PDFDocumentBuilder::appendTo(PDFObjectReference reference, PDFObject object QRectF PDFDocumentBuilder::getPopupWindowRect(const QRectF& rectangle) const { - return rectangle.translated(rectangle.width() * 1.25, 0); + QRectF rect = rectangle.translated(rectangle.width() * 1.25, 0); + rect.setSize(QSizeF(100, 100)); + return rect; } QString PDFDocumentBuilder::getProducerString() const @@ -375,7 +432,17 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationSquare(PDFObjectReference objectBuilder.endDictionaryItem(); objectBuilder.endDictionary(); PDFObject updateAnnotationPopup = objectBuilder.takeObject(); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Annots"); + objectBuilder.beginArray(); + objectBuilder << annotationObject; + objectBuilder << popupAnnotation; + objectBuilder.endArray(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject pageAnnots = objectBuilder.takeObject(); mergeTo(annotationObject, updateAnnotationPopup); + appendTo(page, pageAnnots); return annotationObject; } @@ -532,6 +599,148 @@ PDFObjectReference PDFDocumentBuilder::createCatalogPageTreeRoot() } +PDFObjectReference PDFDocumentBuilder::createAnnotationCircle(PDFObjectReference page, + QRectF rectangle, + PDFReal borderWidth, + QColor fillColor, + QColor strokeColor, + QString title, + QString subject, + QString contents) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Subtype"); + objectBuilder << WrapName("Circle"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Rect"); + objectBuilder << rectangle; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("F"); + objectBuilder << 4; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("P"); + objectBuilder << page; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("M"); + objectBuilder << WrapCurrentDateTime(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("CreationDate"); + objectBuilder << WrapCurrentDateTime(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Border"); + objectBuilder << std::initializer_list{ 0.0, 0.0, borderWidth }; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("C"); + objectBuilder << WrapAnnotationColor(strokeColor); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("IC"); + objectBuilder << WrapAnnotationColor(fillColor); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("T"); + objectBuilder << title; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Contents"); + objectBuilder << contents; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Subj"); + objectBuilder << subject; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference annotationObject = addObject(objectBuilder.takeObject()); + PDFObjectReference popupAnnotation = createAnnotationPopup(page, annotationObject, getPopupWindowRect(rectangle), false); + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Popup"); + objectBuilder << popupAnnotation; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updateAnnotationPopup = objectBuilder.takeObject(); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Annots"); + objectBuilder.beginArray(); + objectBuilder << annotationObject; + objectBuilder << popupAnnotation; + objectBuilder.endArray(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject pageAnnots = objectBuilder.takeObject(); + mergeTo(annotationObject, updateAnnotationPopup); + appendTo(page, pageAnnots); + return annotationObject; +} + + +PDFObjectReference PDFDocumentBuilder::createAnnotationText(PDFObjectReference page, + QRectF rectangle, + TextAnnotationIcon iconType, + QString title, + QString subject, + QString contents, + bool open) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Subtype"); + objectBuilder << WrapName("Text"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Rect"); + objectBuilder << rectangle; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Name"); + objectBuilder << iconType; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("F"); + objectBuilder << 4; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("P"); + objectBuilder << page; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("M"); + objectBuilder << WrapCurrentDateTime(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("CreationDate"); + objectBuilder << WrapCurrentDateTime(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("T"); + objectBuilder << title; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Contents"); + objectBuilder << contents; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Subj"); + objectBuilder << subject; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Open"); + objectBuilder << open; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference annotationObject = addObject(objectBuilder.takeObject()); + PDFObjectReference popupAnnotation = createAnnotationPopup(page, annotationObject, getPopupWindowRect(rectangle), false); + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Popup"); + objectBuilder << popupAnnotation; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject updateAnnotationPopup = objectBuilder.takeObject(); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Annots"); + objectBuilder.beginArray(); + objectBuilder << annotationObject; + objectBuilder << popupAnnotation; + objectBuilder.endArray(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject pageAnnots = objectBuilder.takeObject(); + mergeTo(annotationObject, updateAnnotationPopup); + appendTo(page, pageAnnots); + return annotationObject; +} + + /* END GENERATED CODE */ } // namespace pdf diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h index 39bbafa..371765b 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -20,6 +20,7 @@ #include "pdfobject.h" #include "pdfdocument.h" +#include "pdfannotation.h" namespace pdf { @@ -77,6 +78,7 @@ public: PDFObjectFactory& operator<<(WrapAnnotationColor color); PDFObjectFactory& operator<<(QString textString); PDFObjectFactory& operator<<(WrapEmptyArray); + PDFObjectFactory& operator<<(TextAnnotationIcon icon); /// Treat containers - write them as array template()))> @@ -244,6 +246,49 @@ public: PDFObjectReference createCatalogPageTreeRoot(); + /// Circle annotation displays ellipse (or circle). When opened, they display pop-up window containing + /// the text of associated note (and window title). Circle border/fill color can be defined, along with + /// border width. + /// \param page Page to which is annotation added + /// \param rectangle Area in which is circle/ellipse displayed + /// \param borderWidth Width of the border line of circle/ellipse + /// \param fillColor Fill color of rectangle (interior color). If you do not want to have area color filled, + /// then use invalid QColor. + /// \param strokeColor Stroke color (color of the rectangle border). If you do not want to have a + /// border, then use invalid QColor. + /// \param title Title (it is displayed as title of popup window) + /// \param subject Subject (short description of the subject being adressed by the annotation) + /// \param contents Contents (text displayed, for example, in the marked annotation dialog) + PDFObjectReference createAnnotationCircle(PDFObjectReference page, + QRectF rectangle, + PDFReal borderWidth, + QColor fillColor, + QColor strokeColor, + QString title, + QString subject, + QString contents); + + + /// Creates text annotation. Text annotation is "sticky note" attached to a point in the PDF document. + /// When closed, it is displayed as icon, if opened, widget appears with attached text. Text annotations + /// do not scale or rotate, they appear independent of zoom/rotate. So, they behave as if flags + /// NoZoom or NoRotate to the annotations are being set. + /// \param page Page to which is annotation added + /// \param rectangle Area in which is icon displayed + /// \param iconType Icon type + /// \param title Title (it is displayed as title of popup window) + /// \param subject Subject (short description of the subject being adressed by the annotation) + /// \param contents Contents (text displayed, for example, in the marked annotation dialog) + /// \param open Is annotation initiali displayed as opened? + PDFObjectReference createAnnotationText(PDFObjectReference page, + QRectF rectangle, + TextAnnotationIcon iconType, + QString title, + QString subject, + QString contents, + bool open); + + /* END GENERATED CODE */ private: diff --git a/generated_code_definition.xml b/generated_code_definition.xml index 6217e26..ad79e43 100644 --- a/generated_code_definition.xml +++ b/generated_code_definition.xml @@ -203,6 +203,38 @@ _PDFObject + + + + + + + + + + + + + + ArraySimple + annotationObject;popupAnnotation + + + Annots + DictionaryItemComplex + + + + + Dictionary + + + + CreateObject + pageAnnots + _PDFObject + + @@ -210,6 +242,7 @@ _void mergeTo(annotationObject, updateAnnotationPopup); +appendTo(page, pageAnnots); return annotationObject; @@ -725,5 +758,489 @@ return pageReference; Creates page tree root for the catalog. This function is only called when new document is being created. Do not call this function manually. _PDFObjectReference + + + + + + + + + + page + _PDFObjectReference + Page to which is annotation added + + + + + rectangle + _QRectF + Area in which is circle/ellipse displayed + + + + + borderWidth + _PDFReal + Width of the border line of circle/ellipse + + + + + fillColor + _QColor + Fill color of rectangle (interior color). If you do not want to have area color filled, then use invalid QColor. + + + + + strokeColor + _QColor + Stroke color (color of the rectangle border). If you do not want to have a border, then use invalid QColor. + + + + + title + _QString + Title (it is displayed as title of popup window) + + + + + subject + _QString + Subject (short description of the subject being adressed by the annotation) + + + + + contents + _QString + Contents (text displayed, for example, in the marked annotation dialog) + + + Parameters + + _void + + + + + + + + + + + + Subtype + DictionaryItemSimple + WrapName("Circle") + + + + + Rect + DictionaryItemSimple + rectangle + + + + + F + DictionaryItemSimple + 4 + + + + + P + DictionaryItemSimple + page + + + + + M + DictionaryItemSimple + WrapCurrentDateTime() + + + + + CreationDate + DictionaryItemSimple + WrapCurrentDateTime() + + + + + Border + DictionaryItemSimple + std::initializer_list<PDFReal>{ 0.0, 0.0, borderWidth } + + + + + C + DictionaryItemSimple + WrapAnnotationColor(strokeColor) + + + + + IC + DictionaryItemSimple + WrapAnnotationColor(fillColor) + + + + + T + DictionaryItemSimple + title + + + + + Contents + DictionaryItemSimple + contents + + + + + Subj + DictionaryItemSimple + subject + + + + Dictionary + + + + CreateObject + annotationObject + _PDFObjectReference + + + + + + Code + + _void + PDFObjectReference popupAnnotation = createAnnotationPopup(page, annotationObject, getPopupWindowRect(rectangle), false); + + + + + + + + + + + Popup + DictionaryItemSimple + popupAnnotation + + + Popup + Dictionary + popupAnnotation + + + CreateObject + updateAnnotationPopup + _PDFObject + + + + + + + + + + + + + + + + ArraySimple + annotationObject;popupAnnotation + + + Annots + DictionaryItemComplex + + + + + Dictionary + + + + CreateObject + pageAnnots + _PDFObject + + + + + + Code + + _void + mergeTo(annotationObject, updateAnnotationPopup); +appendTo(page, pageAnnots); +return annotationObject; + + + Annotations + createAnnotationCircle + Circle annotation displays ellipse (or circle). When opened, they display pop-up window containing the text of associated note (and window title). Circle border/fill color can be defined, along with border width. + _PDFObjectReference + + + + + + + + + + + page + _PDFObjectReference + Page to which is annotation added + + + + + rectangle + _QRectF + Area in which is icon displayed + + + + + iconType + _TextAnnotationIcon + Icon type + + + + + title + _QString + Title (it is displayed as title of popup window) + + + + + subject + _QString + Subject (short description of the subject being adressed by the annotation) + + + + + contents + _QString + Contents (text displayed, for example, in the marked annotation dialog) + + + + + open + _bool + Is annotation initiali displayed as opened? + + + Parameters + + _void + + + + + + + + + + + + Subtype + DictionaryItemSimple + WrapName("Text") + + + + + Rect + DictionaryItemSimple + rectangle + + + + + Name + DictionaryItemSimple + iconType + + + + + F + DictionaryItemSimple + 4 + + + + + P + DictionaryItemSimple + page + + + + + M + DictionaryItemSimple + WrapCurrentDateTime() + + + + + CreationDate + DictionaryItemSimple + WrapCurrentDateTime() + + + + + T + DictionaryItemSimple + title + + + + + Contents + DictionaryItemSimple + contents + + + + + Subj + DictionaryItemSimple + subject + + + + + Open + DictionaryItemSimple + open + + + + Dictionary + + + + CreateObject + annotationObject + _PDFObjectReference + + + + + + Code + + _void + PDFObjectReference popupAnnotation = createAnnotationPopup(page, annotationObject, getPopupWindowRect(rectangle), false); + + + + + + + + + + + Popup + DictionaryItemSimple + popupAnnotation + + + Popup + Dictionary + popupAnnotation + + + CreateObject + updateAnnotationPopup + _PDFObject + + + + + + + + + + + + + + + + ArraySimple + annotationObject;popupAnnotation + + + Annots + DictionaryItemComplex + + + + + Dictionary + + + + CreateObject + pageAnnots + _PDFObject + + + + + + Code + + _void + mergeTo(annotationObject, updateAnnotationPopup); +appendTo(page, pageAnnots); +return annotationObject; + + + Annotations + createAnnotationText + Creates text annotation. Text annotation is "sticky note" attached to a point in the PDF document. When closed, it is displayed as icon, if opened, widget appears with attached text. Text annotations do not scale or rotate, they appear independent of zoom/rotate. So, they behave as if flags NoZoom or NoRotate to the annotations are being set. + _PDFObjectReference +