mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Free text and line annotations
This commit is contained in:
@@ -34,6 +34,8 @@ namespace pdf
|
||||
class PDFDocument;
|
||||
class PDFDrawWidgetProxy;
|
||||
|
||||
using TextAlignment = Qt::Alignment;
|
||||
|
||||
enum class AnnotationType
|
||||
{
|
||||
Invalid,
|
||||
|
@@ -64,6 +64,83 @@ void PDFObjectFactory::endDictionaryItem()
|
||||
std::get<PDFDictionary>(dictionaryItem.object).addEntry(qMove(topItem.itemName), qMove(std::get<PDFObject>(topItem.object)));
|
||||
}
|
||||
|
||||
PDFObjectFactory& PDFObjectFactory::operator<<(AnnotationLineEnding lineEnding)
|
||||
{
|
||||
switch (lineEnding)
|
||||
{
|
||||
case AnnotationLineEnding::Square:
|
||||
*this << WrapName("Square");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::Circle:
|
||||
*this << WrapName("Circle");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::Diamond:
|
||||
*this << WrapName("Diamond");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::OpenArrow:
|
||||
*this << WrapName("OpenArrow");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::ClosedArrow:
|
||||
*this << WrapName("ClosedArrow");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::Butt:
|
||||
*this << WrapName("Butt");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::ROpenArrow:
|
||||
*this << WrapName("ROpenArrow");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::RClosedArrow:
|
||||
*this << WrapName("RClosedArrow");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::Slash:
|
||||
*this << WrapName("Slash");
|
||||
break;
|
||||
|
||||
case AnnotationLineEnding::None:
|
||||
default:
|
||||
*this << WrapName("None");
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PDFObjectFactory& PDFObjectFactory::operator<<(WrapString string)
|
||||
{
|
||||
addObject(PDFObject::createString(std::make_shared<PDFString>(qMove(string.string))));
|
||||
return *this;
|
||||
}
|
||||
|
||||
PDFObjectFactory& PDFObjectFactory::operator<<(WrapFreeTextAlignment alignment)
|
||||
{
|
||||
if (alignment.alignment.testFlag(Qt::AlignLeft))
|
||||
{
|
||||
*this << 0;
|
||||
}
|
||||
else if (alignment.alignment.testFlag(Qt::AlignHCenter))
|
||||
{
|
||||
*this << 1;
|
||||
}
|
||||
else if (alignment.alignment.testFlag(Qt::AlignRight))
|
||||
{
|
||||
*this << 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default is left alignment
|
||||
*this << 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
PDFObjectFactory& PDFObjectFactory::operator<<(LinkHighlightMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
@@ -350,6 +427,11 @@ PDFDocument PDFDocumentBuilder::build()
|
||||
return PDFDocument(PDFObjectStorage(m_storage), m_version);
|
||||
}
|
||||
|
||||
std::array<PDFReal, 4> PDFDocumentBuilder::getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const
|
||||
{
|
||||
return { qAbs(innerRect.left() - boundingRect.left()), qAbs(boundingRect.bottom() - innerRect.bottom()), qAbs(boundingRect.right() - innerRect.right()), qAbs(boundingRect.top() - innerRect.top()) };
|
||||
}
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::addObject(PDFObject object)
|
||||
{
|
||||
return m_storage.addObject(PDFObjectManipulator::removeNullObjects(object));
|
||||
@@ -538,6 +620,244 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationCircle(PDFObjectReference
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("FreeText");
|
||||
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("T");
|
||||
objectBuilder << title;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Contents");
|
||||
objectBuilder << contents;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subj");
|
||||
objectBuilder << subject;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Q");
|
||||
objectBuilder << WrapFreeTextAlignment(textAlignment);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("DA");
|
||||
objectBuilder << WrapString("/Arial 10 Tf");
|
||||
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);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("FreeText");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << boundingRectangle;
|
||||
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("Q");
|
||||
objectBuilder << WrapFreeTextAlignment(textAlignment);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("DA");
|
||||
objectBuilder << WrapString("/Arial 10 Tf");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("RD");
|
||||
objectBuilder << getAnnotationReductionRectangle(boundingRectangle, textRectangle);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CL");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startPoint.x();
|
||||
objectBuilder << startPoint.y();
|
||||
objectBuilder << endPoint.x();
|
||||
objectBuilder << endPoint.y();
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("LE");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startLineType;
|
||||
objectBuilder << endLineType;
|
||||
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);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationLine(PDFObjectReference page,
|
||||
QRectF boundingRect,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
PDFReal lineWidth,
|
||||
QColor fillColor,
|
||||
QColor strokeColor,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Line");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << boundingRect;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("F");
|
||||
objectBuilder << 4;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("L");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startPoint.x();
|
||||
objectBuilder << startPoint.y();
|
||||
objectBuilder << endPoint.x();
|
||||
objectBuilder << endPoint.y();
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("LE");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startLineType;
|
||||
objectBuilder << endLineType;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("M");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Border");
|
||||
objectBuilder << std::initializer_list<PDFReal>{ 0.0, 0.0, lineWidth };
|
||||
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(boundingRect), 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::createAnnotationLink(PDFObjectReference page,
|
||||
QRectF linkRectangle,
|
||||
PDFObjectReference action,
|
||||
|
@@ -47,6 +47,28 @@ struct WrapAnnotationColor
|
||||
QColor color;
|
||||
};
|
||||
|
||||
struct WrapFreeTextAlignment
|
||||
{
|
||||
constexpr inline WrapFreeTextAlignment(Qt::Alignment alignment) :
|
||||
alignment(alignment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Qt::Alignment alignment;
|
||||
};
|
||||
|
||||
struct WrapString
|
||||
{
|
||||
WrapString(const char* string) :
|
||||
string(string)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QByteArray string;
|
||||
};
|
||||
|
||||
struct WrapCurrentDateTime { };
|
||||
struct WrapEmptyArray { };
|
||||
|
||||
@@ -80,6 +102,9 @@ public:
|
||||
PDFObjectFactory& operator<<(WrapEmptyArray);
|
||||
PDFObjectFactory& operator<<(TextAnnotationIcon icon);
|
||||
PDFObjectFactory& operator<<(LinkHighlightMode mode);
|
||||
PDFObjectFactory& operator<<(WrapFreeTextAlignment alignment);
|
||||
PDFObjectFactory& operator<<(WrapString string);
|
||||
PDFObjectFactory& operator<<(AnnotationLineEnding lineEnding);
|
||||
|
||||
/// Treat containers - write them as array
|
||||
template<typename Container, typename ValueType = decltype(*std::begin(std::declval<Container>()))>
|
||||
@@ -181,6 +206,9 @@ public:
|
||||
/// is returned (no exception is thrown).
|
||||
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
|
||||
|
||||
/// Returns annotation bounding rectangle
|
||||
std::array<PDFReal, 4> getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const;
|
||||
|
||||
/* START GENERATED CODE */
|
||||
|
||||
/// Appends a new page after last page.
|
||||
@@ -216,6 +244,82 @@ public:
|
||||
QString contents);
|
||||
|
||||
|
||||
/// 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.
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param rectangle Area in which is text displayed
|
||||
/// \param title Title
|
||||
/// \param subject Subject
|
||||
/// \param contents Contents (text displayed)
|
||||
/// \param textAlignment Text alignment. Only horizontal alignment flags are valid.
|
||||
PDFObjectReference createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment);
|
||||
|
||||
|
||||
/// 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. Specify
|
||||
/// start/end point parameters of this function to get callout line.
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param boundingRectangle Bounding rectangle of free text annotation. It must contain both
|
||||
/// callout line and text rectangle.
|
||||
/// \param textRectangle Rectangle with text, in absolute coordinates. They are then recomputed to
|
||||
/// match bounding rectangle.
|
||||
/// \param title Title
|
||||
/// \param subject Subject
|
||||
/// \param contents Contents (text displayed)
|
||||
/// \param textAlignment Text alignment. Only horizontal alignment flags are valid.
|
||||
/// \param startPoint Start point of callout line
|
||||
/// \param endPoint End point of callout line
|
||||
/// \param startLineType Line ending at the start point
|
||||
/// \param endLineType Line ending at the end point
|
||||
PDFObjectReference createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType);
|
||||
|
||||
|
||||
/// Line annotation represents straight line, or some more advanced graphics, such as dimension with
|
||||
/// text. Line annotations are markup annotations, so they can have popup window. Line endings can
|
||||
/// be specified.
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param boundingRect Line annotation bounding rectangle
|
||||
/// \param startPoint Line start
|
||||
/// \param endPoint Line end
|
||||
/// \param lineWidth Line width
|
||||
/// \param fillColor Fill color of line parts (for example, filled line endings)
|
||||
/// \param strokeColor Line stroke color
|
||||
/// \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 startLineType Start line ending type
|
||||
/// \param endLineType End line ending type
|
||||
PDFObjectReference createAnnotationLine(PDFObjectReference page,
|
||||
QRectF boundingRect,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
PDFReal lineWidth,
|
||||
QColor fillColor,
|
||||
QColor strokeColor,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType);
|
||||
|
||||
|
||||
/// Creates new link annotation. It usually represents clickable hypertext link. User can also specify
|
||||
/// action, which can be executed, for example, link can be also in the PDF document (link to some
|
||||
/// location in document).
|
||||
@@ -289,7 +393,7 @@ public:
|
||||
/// \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?
|
||||
/// \param open Is annotation initially displayed as opened?
|
||||
PDFObjectReference createAnnotationText(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
TextAnnotationIcon iconType,
|
||||
|
Reference in New Issue
Block a user