From de1fa4a2daaf0e48fb058e42d3c04b597c91f50b Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Tue, 7 Apr 2020 20:59:39 +0200 Subject: [PATCH] File attachment annotation graphics --- CodeGenerator/codegenerator.h | 1 + PdfExampleGenerator/pdfexamplesgenerator.cpp | 6 + PdfForQtLib/sources/pdfannotation.cpp | 57 +++- PdfForQtLib/sources/pdfannotation.h | 21 +- PdfForQtLib/sources/pdfdocumentbuilder.cpp | 176 ++++++++---- PdfForQtLib/sources/pdfdocumentbuilder.h | 36 ++- generated_code_definition.xml | 273 +++++++++++++++---- 7 files changed, 446 insertions(+), 124 deletions(-) diff --git a/CodeGenerator/codegenerator.h b/CodeGenerator/codegenerator.h index 3c00b75..393351c 100644 --- a/CodeGenerator/codegenerator.h +++ b/CodeGenerator/codegenerator.h @@ -154,6 +154,7 @@ public: _TextAlignment, _AnnotationLineEnding, _AnnotationBorderStyle, + _FileAttachmentIcon, _Stamp }; Q_ENUM(DataType) diff --git a/PdfExampleGenerator/pdfexamplesgenerator.cpp b/PdfExampleGenerator/pdfexamplesgenerator.cpp index 6ca8d0f..11f0c61 100644 --- a/PdfExampleGenerator/pdfexamplesgenerator.cpp +++ b/PdfExampleGenerator/pdfexamplesgenerator.cpp @@ -290,6 +290,12 @@ void PDFExamplesGenerator::generateAnnotationsExample() builder.createAnnotationStamp(page14, QRectF(210, 300, 100, 50), pdf::Stamp::Sold, "Title", "Subject", "Contents"); builder.createAnnotationStamp(page14, QRectF(210, 350, 100, 50), pdf::Stamp::TopSecret, "Title", "Subject", "Contents"); + pdf::PDFObjectReference page15 = builder.appendPage(QRectF(0, 0, 400, 400)); + builder.createAnnotationFileAttachment(page15, QPointF(50, 150), pdf::PDFObjectReference(), pdf::FileAttachmentIcon::PushPin, "Title", "File description"); + builder.createAnnotationFileAttachment(page15, QPointF(50, 350), pdf::PDFObjectReference(), pdf::FileAttachmentIcon::Graph, "Title", "File description"); + builder.createAnnotationFileAttachment(page15, QPointF(250, 150), pdf::PDFObjectReference(), pdf::FileAttachmentIcon::Paperclip, "Title", "File description"); + builder.createAnnotationFileAttachment(page15, QPointF(250, 350), pdf::PDFObjectReference(), pdf::FileAttachmentIcon::Tag, "Title", "File description"); + // Write result to a file pdf::PDFDocument document = builder.build(); pdf::PDFDocumentWriter writer(nullptr); diff --git a/PdfForQtLib/sources/pdfannotation.cpp b/PdfForQtLib/sources/pdfannotation.cpp index 01a8464..47ef442 100644 --- a/PdfForQtLib/sources/pdfannotation.cpp +++ b/PdfForQtLib/sources/pdfannotation.cpp @@ -447,14 +447,14 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject annotation->m_fileSpecification = PDFFileSpecification::parse(storage, dictionary->get("FS")); - constexpr const std::array, 4> icons = { - std::pair{ "Graph", PDFFileAttachmentAnnotation::Icon::Graph }, - std::pair{ "Paperclip", PDFFileAttachmentAnnotation::Icon::Paperclip }, - std::pair{ "PushPin", PDFFileAttachmentAnnotation::Icon::PushPin }, - std::pair{ "Tag", PDFFileAttachmentAnnotation::Icon::Tag } + constexpr const std::array, 4> icons = { + std::pair{ "Graph", FileAttachmentIcon::Graph }, + std::pair{ "Paperclip", FileAttachmentIcon::Paperclip }, + std::pair{ "PushPin", FileAttachmentIcon::PushPin }, + std::pair{ "Tag", FileAttachmentIcon::Tag } }; - annotation->m_icon = loader.readEnumByName(dictionary->get("Name"), icons.begin(), icons.end(), PDFFileAttachmentAnnotation::Icon::PushPin); + annotation->m_icon = loader.readEnumByName(dictionary->get("Name"), icons.begin(), icons.end(), FileAttachmentIcon::PushPin); } else if (subtype == "Sound") { @@ -2121,4 +2121,49 @@ void PDFStampAnnotation::draw(AnnotationDrawParameters& parameters) const parameters.boundingRectangle.adjust(-penWidth, -penWidth, penWidth, penWidth); } +void PDFFileAttachmentAnnotation::draw(AnnotationDrawParameters& parameters) const +{ + QString text = "?"; + switch (getIcon()) + { + case FileAttachmentIcon::Graph: + text = QString::fromUtf16(u"\U0001F4C8"); + break; + case FileAttachmentIcon::Paperclip: + text = QString::fromUtf16(u"\U0001F4CE"); + break; + case FileAttachmentIcon::PushPin: + text = QString::fromUtf16(u"\U0001F4CC"); + break; + case FileAttachmentIcon::Tag: + text = QString::fromUtf16(u"\U0001F3F7"); + break; + + default: + Q_ASSERT(false); + break; + } + + const PDFReal opacity = getOpacity(); + QColor strokeColor = QColor::fromRgbF(0.0, 0.0, 0.0, opacity); + + constexpr const PDFReal rectSize = 24.0; + QPainter& painter = *parameters.painter; + QRectF rectangle = getRectangle(); + rectangle.setSize(QSizeF(rectSize, rectSize)); + + QFont font = QApplication::font(); + font.setPixelSize(16.0); + + QPainterPath textPath; + textPath.addText(0.0, 0.0, font, text); + textPath = QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0).map(textPath); + QRectF textBoundingRect = textPath.boundingRect(); + QPointF offset = rectangle.center() - textBoundingRect.center(); + textPath.translate(offset); + painter.fillPath(textPath, QBrush(strokeColor, Qt::SolidPattern)); + + parameters.boundingRectangle = rectangle; +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfannotation.h b/PdfForQtLib/sources/pdfannotation.h index 55cfcca..2b1cc14 100644 --- a/PdfForQtLib/sources/pdfannotation.h +++ b/PdfForQtLib/sources/pdfannotation.h @@ -1001,6 +1001,14 @@ private: bool m_opened = false; }; +enum class FileAttachmentIcon +{ + Graph, + Paperclip, + PushPin, + Tag +}; + /// File attachment annotation contains reference to (embedded or external) file. /// So it is a link to specified file. Activating annotation enables user to view /// or store attached file in the filesystem. @@ -1009,24 +1017,17 @@ class PDFFileAttachmentAnnotation : public PDFMarkupAnnotation public: inline explicit PDFFileAttachmentAnnotation() = default; - enum class Icon - { - Graph, - Paperclip, - PushPin, - Tag - }; - virtual AnnotationType getType() const override { return AnnotationType::FileAttachment; } + virtual void draw(AnnotationDrawParameters& parameters) const override; const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; } - Icon getIcon() const { return m_icon; } + FileAttachmentIcon getIcon() const { return m_icon; } private: friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject object); PDFFileSpecification m_fileSpecification; - Icon m_icon = Icon::PushPin; + FileAttachmentIcon m_icon = FileAttachmentIcon::PushPin; }; /// Sound annotation contains sound, which is played, when diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index 8939019..4b0ea2f 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -219,6 +219,34 @@ void PDFObjectFactory::endDictionaryItem() std::get(dictionaryItem.object).addEntry(qMove(topItem.itemName), qMove(std::get(topItem.object))); } +PDFObjectFactory& PDFObjectFactory::operator<<(FileAttachmentIcon icon) +{ + switch (icon) + { + case FileAttachmentIcon::Graph: + *this << WrapName("Graph"); + break; + + case FileAttachmentIcon::Paperclip: + *this << WrapName("Paperclip"); + break; + + case FileAttachmentIcon::PushPin: + *this << WrapName("PushPin"); + break; + + case FileAttachmentIcon::Tag: + *this << WrapName("Tag"); + break; + + default: + Q_ASSERT(false); + break; + } + + return *this; +} + PDFObjectFactory& PDFObjectFactory::operator<<(Stamp stamp) { switch (stamp) @@ -1310,6 +1338,56 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationCircle(PDFObjectReference } +PDFObjectReference PDFDocumentBuilder::createAnnotationFileAttachment(PDFObjectReference page, + QPointF position, + PDFObjectReference fileSpecification, + FileAttachmentIcon icon, + QString title, + QString description) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Type"); + objectBuilder << WrapName("Annot"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Subtype"); + objectBuilder << WrapName("FileAttachment"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("P"); + objectBuilder << page; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Rect"); + objectBuilder << QRectF(position, QSizeF(32, 32)); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("FS"); + objectBuilder << fileSpecification; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Name"); + objectBuilder << icon; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("T"); + objectBuilder << title; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Contents"); + objectBuilder << description; + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference annotationReference = addObject(objectBuilder.takeObject()); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Annots"); + objectBuilder.beginArray(); + objectBuilder << annotationReference; + objectBuilder.endArray(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject pageAnnots = objectBuilder.takeObject(); + appendTo(page, pageAnnots); + updateAnnotationAppearanceStreams(annotationReference); + return annotationReference; +} + + PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReference page, QRectF rectangle, QString title, @@ -2638,6 +2716,55 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationText(PDFObjectReference p } +PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectReference page, + QRectF rectangle, + QColor color) +{ + PDFObjectFactory objectBuilder; + + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Type"); + objectBuilder << WrapName("Annot"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Subtype"); + objectBuilder << WrapName("Underline"); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("Rect"); + objectBuilder << rectangle; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("P"); + objectBuilder << page; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("CreationDate"); + objectBuilder << WrapCurrentDateTime(); + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("C"); + objectBuilder << color; + objectBuilder.endDictionaryItem(); + objectBuilder.beginDictionaryItem("QuadPoints"); + objectBuilder.beginArray(); + objectBuilder << rectangle.bottomLeft(); + objectBuilder << rectangle.bottomRight(); + objectBuilder << rectangle.topLeft(); + objectBuilder << rectangle.topRight(); + objectBuilder.endArray(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObjectReference annotationObject = addObject(objectBuilder.takeObject()); + objectBuilder.beginDictionary(); + objectBuilder.beginDictionaryItem("Annots"); + objectBuilder.beginArray(); + objectBuilder << annotationObject; + objectBuilder.endArray(); + objectBuilder.endDictionaryItem(); + objectBuilder.endDictionary(); + PDFObject pageAnnots = objectBuilder.takeObject(); + appendTo(page, pageAnnots); + updateAnnotationAppearanceStreams(annotationObject); + return annotationObject; +} + + PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectReference page, QRectF rectangle, QColor color, @@ -2702,55 +2829,6 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectRefere } -PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectReference page, - QRectF rectangle, - QColor color) -{ - PDFObjectFactory objectBuilder; - - objectBuilder.beginDictionary(); - objectBuilder.beginDictionaryItem("Type"); - objectBuilder << WrapName("Annot"); - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("Subtype"); - objectBuilder << WrapName("Underline"); - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("Rect"); - objectBuilder << rectangle; - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("P"); - objectBuilder << page; - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("CreationDate"); - objectBuilder << WrapCurrentDateTime(); - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("C"); - objectBuilder << color; - objectBuilder.endDictionaryItem(); - objectBuilder.beginDictionaryItem("QuadPoints"); - objectBuilder.beginArray(); - objectBuilder << rectangle.bottomLeft(); - objectBuilder << rectangle.bottomRight(); - objectBuilder << rectangle.topLeft(); - objectBuilder << rectangle.topRight(); - objectBuilder.endArray(); - objectBuilder.endDictionaryItem(); - objectBuilder.endDictionary(); - PDFObjectReference annotationObject = addObject(objectBuilder.takeObject()); - objectBuilder.beginDictionary(); - objectBuilder.beginDictionaryItem("Annots"); - objectBuilder.beginArray(); - objectBuilder << annotationObject; - objectBuilder.endArray(); - objectBuilder.endDictionaryItem(); - objectBuilder.endDictionary(); - PDFObject pageAnnots = objectBuilder.takeObject(); - appendTo(page, pageAnnots); - updateAnnotationAppearanceStreams(annotationObject); - return annotationObject; -} - - PDFObjectReference PDFDocumentBuilder::createCatalog() { PDFObjectFactory objectBuilder; diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h index ff09335..7bf2ed4 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -112,6 +112,7 @@ public: PDFObjectFactory& operator<<(AnnotationBorderStyle style); PDFObjectFactory& operator<<(const PDFObject& object); PDFObjectFactory& operator<<(Stamp stamp); + PDFObjectFactory& operator<<(FileAttachmentIcon icon); /// Treat containers - write them as array template()))> @@ -331,6 +332,21 @@ public: QString contents); + /// Creates a new file attachment annotation. This annotation needs file specification as parameter. + /// \param page Page to which is annotation added + /// \param position Position + /// \param fileSpecification File specification + /// \param icon Icon + /// \param title Title + /// \param description Description + PDFObjectReference createAnnotationFileAttachment(PDFObjectReference page, + QPointF position, + PDFObjectReference fileSpecification, + FileAttachmentIcon icon, + QString title, + QString description); + + /// Free text annotation displays text directly on a page. Text appears directly on the page, in the /// same way, as standard text in PDF document. Free text annotations are usually used to comment /// the document. Free text annotation can also have callout line, with, or without a knee. @@ -742,6 +758,16 @@ public: bool open); + /// Text markup annotation is used to underline text. It is a markup annotation, so it can contain + /// window to be opened (and commented). + /// \param page Page to which is annotation added + /// \param rectangle Area in which is markup displayed + /// \param color Color + PDFObjectReference createAnnotationUnderline(PDFObjectReference page, + QRectF rectangle, + QColor color); + + /// Text markup annotation is used to underline text. It is a markup annotation, so it can contain /// window to be opened (and commented). /// \param page Page to which is annotation added @@ -758,16 +784,6 @@ public: QString contents); - /// Text markup annotation is used to underline text. It is a markup annotation, so it can contain - /// window to be opened (and commented). - /// \param page Page to which is annotation added - /// \param rectangle Area in which is markup displayed - /// \param color Color - PDFObjectReference createAnnotationUnderline(PDFObjectReference page, - QRectF rectangle, - QColor color); - - /// Creates empty catalog. This function is used, when a new document is being created. Do not call /// this function manually. PDFObjectReference createCatalog(); diff --git a/generated_code_definition.xml b/generated_code_definition.xml index 53e1c85..989bea1 100644 --- a/generated_code_definition.xml +++ b/generated_code_definition.xml @@ -617,6 +617,181 @@ return annotationObject; Circle annotation displays ellipse (or circle). Circle border/fill color can be defined, along with border width. Popup annotation can be attached to this annotation. _PDFObjectReference + + + + + + + + + + page + _PDFObjectReference + Page to which is annotation added + + + + + position + _QPointF + Position + + + + + fileSpecification + _PDFObjectReference + File specification + + + + + icon + _FileAttachmentIcon + Icon + + + + + title + _QString + Title + + + + + description + _QString + Description + + + Parameters + + _void + + + + + + + + + + + + Type + DictionaryItemSimple + WrapName("Annot") + + + + + Subtype + DictionaryItemSimple + WrapName("FileAttachment") + + + + + P + DictionaryItemSimple + page + + + + + Rect + DictionaryItemSimple + QRectF(position, QSizeF(32, 32)) + + + + + FS + DictionaryItemSimple + fileSpecification + + + + + Name + DictionaryItemSimple + icon + + + + + T + DictionaryItemSimple + title + + + + + Contents + DictionaryItemSimple + description + + + + Dictionary + + + + CreateObject + annotationReference + _PDFObjectReference + + + + + + + + + + + + + + + + ArraySimple + annotationReference + + + Annots + DictionaryItemComplex + + + + + Dictionary + + + + CreateObject + pageAnnots + _PDFObject + + + + + + Code + + _void + appendTo(page, pageAnnots); +updateAnnotationAppearanceStreams(annotationReference); +return annotationReference; + + + Annotations + createAnnotationFileAttachment + Creates a new file attachment annotation. This annotation needs file specification as parameter. + _PDFObjectReference + @@ -5044,27 +5219,6 @@ return annotationObject; _QColor Color - - - - title - _QString - Title - - - - - subject - _QString - Subject - - - - - contents - _QString - Contents - Parameters @@ -5105,13 +5259,6 @@ return annotationObject; DictionaryItemSimple page - - - - M - DictionaryItemSimple - WrapCurrentDateTime() - @@ -5126,27 +5273,6 @@ return annotationObject; DictionaryItemSimple color - - - - T - DictionaryItemSimple - title - - - - - Contents - DictionaryItemSimple - contents - - - - - Subj - DictionaryItemSimple - subject - @@ -5248,6 +5374,27 @@ return annotationObject; _QColor Color + + + + title + _QString + Title + + + + + subject + _QString + Subject + + + + + contents + _QString + Contents + Parameters @@ -5288,6 +5435,13 @@ return annotationObject; DictionaryItemSimple page + + + + M + DictionaryItemSimple + WrapCurrentDateTime() + @@ -5302,6 +5456,27 @@ return annotationObject; DictionaryItemSimple color + + + + T + DictionaryItemSimple + title + + + + + Contents + DictionaryItemSimple + contents + + + + + Subj + DictionaryItemSimple + subject +