File attachment annotation graphics

This commit is contained in:
Jakub Melka
2020-04-07 20:59:39 +02:00
parent 33472428e9
commit de1fa4a2da
7 changed files with 446 additions and 124 deletions

View File

@ -447,14 +447,14 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
annotation->m_fileSpecification = PDFFileSpecification::parse(storage, dictionary->get("FS"));
constexpr const std::array<std::pair<const char*, PDFFileAttachmentAnnotation::Icon>, 4> icons = {
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "Graph", PDFFileAttachmentAnnotation::Icon::Graph },
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "Paperclip", PDFFileAttachmentAnnotation::Icon::Paperclip },
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "PushPin", PDFFileAttachmentAnnotation::Icon::PushPin },
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "Tag", PDFFileAttachmentAnnotation::Icon::Tag }
constexpr const std::array<std::pair<const char*, FileAttachmentIcon>, 4> icons = {
std::pair<const char*, FileAttachmentIcon>{ "Graph", FileAttachmentIcon::Graph },
std::pair<const char*, FileAttachmentIcon>{ "Paperclip", FileAttachmentIcon::Paperclip },
std::pair<const char*, FileAttachmentIcon>{ "PushPin", FileAttachmentIcon::PushPin },
std::pair<const char*, FileAttachmentIcon>{ "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

View File

@ -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

View File

@ -219,6 +219,34 @@ void PDFObjectFactory::endDictionaryItem()
std::get<PDFDictionary>(dictionaryItem.object).addEntry(qMove(topItem.itemName), qMove(std::get<PDFObject>(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;

View File

@ -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<typename Container, typename ValueType = decltype(*std::begin(std::declval<Container>()))>
@ -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();