Spaces:
Running
Running
/* poppler-annotation.cc: qt interface to poppler | |
* Copyright (C) 2006, 2009, 2012-2015, 2018-2022 Albert Astals Cid <[email protected]> | |
* Copyright (C) 2006, 2008, 2010 Pino Toscano <[email protected]> | |
* Copyright (C) 2012, Guillermo A. Amaral B. <[email protected]> | |
* Copyright (C) 2012-2014 Fabio D'Urso <[email protected]> | |
* Copyright (C) 2012, 2015, Tobias Koenig <[email protected]> | |
* Copyright (C) 2018 Adam Reichold <[email protected]> | |
* Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <[email protected]>. Work sponsored by the LiMux project of the city of Munich | |
* Copyright (C) 2018 Intevation GmbH <[email protected]> | |
* Copyright (C) 2018 Dileep Sankhla <[email protected]> | |
* Copyright (C) 2018, 2019 Tobias Deiminger <[email protected]> | |
* Copyright (C) 2018 Carlos Garcia Campos <[email protected]> | |
* Copyright (C) 2020-2022 Oliver Sander <[email protected]> | |
* Copyright (C) 2020 Katarina Behrens <[email protected]> | |
* Copyright (C) 2020 Thorsten Behrens <[email protected]> | |
* Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <[email protected]>. Work sponsored by Technische Universität Dresden | |
* Copyright (C) 2021 Mahmoud Ahmed Khalil <[email protected]> | |
* Adapting code from | |
* Copyright (C) 2004 by Enrico Ros <[email protected]> | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; either version 2, or (at your option) | |
* any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. | |
*/ | |
// qt/kde includes | |
// local includes | |
// poppler includes | |
/* Almost all getters directly query the underlying poppler annotation, with | |
* the exceptions of link, file attachment, sound, movie and screen annotations, | |
* Whose data retrieval logic has not been moved yet. Their getters return | |
* static data set at creation time by findAnnotations | |
*/ | |
namespace Poppler { | |
// BEGIN AnnotationAppearancePrivate implementation | |
AnnotationAppearancePrivate::AnnotationAppearancePrivate(Annot *annot) | |
{ | |
if (annot) { | |
appearance = annot->getAppearance(); | |
} else { | |
appearance.setToNull(); | |
} | |
} | |
// END AnnotationAppearancePrivate implementation | |
// BEGIN AnnotationAppearance implementation | |
AnnotationAppearance::AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate) : d(annotationAppearancePrivate) { } | |
AnnotationAppearance::~AnnotationAppearance() | |
{ | |
delete d; | |
} | |
// END AnnotationAppearance implementation | |
// BEGIN Annotation implementation | |
AnnotationPrivate::AnnotationPrivate() : revisionScope(Annotation::Root), revisionType(Annotation::None), pdfAnnot(nullptr), pdfPage(nullptr), parentDoc(nullptr) { } | |
void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData) | |
{ | |
const int height = qimg.height(); | |
const int width = qimg.width(); | |
switch (bitsPerPixel) { | |
case 1: | |
for (int line = 0; line < height; line++) { | |
const char *lineData = reinterpret_cast<const char *>(qimg.scanLine(line)); | |
for (int offset = 0; offset < (width + 7) / 8; offset++) { | |
data->append(lineData[offset]); | |
} | |
} | |
break; | |
case 8: | |
case 24: | |
data->append((const char *)qimg.bits(), static_cast<int>(qimg.sizeInBytes())); | |
break; | |
case 32: | |
for (int line = 0; line < height; line++) { | |
const QRgb *lineData = reinterpret_cast<const QRgb *>(qimg.scanLine(line)); | |
for (int offset = 0; offset < width; offset++) { | |
char a = (char)qAlpha(lineData[offset]); | |
char r = (char)qRed(lineData[offset]); | |
char g = (char)qGreen(lineData[offset]); | |
char b = (char)qBlue(lineData[offset]); | |
data->append(r); | |
data->append(g); | |
data->append(b); | |
sMaskData->append(a); | |
} | |
} | |
break; | |
} | |
} | |
void AnnotationPrivate::addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type) | |
{ | |
/* Since ownership stays with the caller, create an alias of ann */ | |
revisions.append(ann->d_ptr->makeAlias()); | |
/* Set revision properties */ | |
revisionScope = scope; | |
revisionType = type; | |
} | |
AnnotationPrivate::~AnnotationPrivate() | |
{ | |
// Delete all children revisions | |
qDeleteAll(revisions); | |
// Release Annot object | |
if (pdfAnnot) { | |
pdfAnnot->decRefCnt(); | |
} | |
} | |
void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData *doc) | |
{ | |
if (pdfAnnot) { | |
error(errIO, -1, "Annotation is already tied"); | |
return; | |
} | |
pdfAnnot = ann; | |
pdfPage = page; | |
parentDoc = doc; | |
pdfAnnot->incRefCnt(); | |
} | |
/* This method is called when a new annotation is created, after pdfAnnot and | |
* pdfPage have been set */ | |
void AnnotationPrivate::flushBaseAnnotationProperties() | |
{ | |
Q_ASSERT(pdfPage); | |
Annotation *q = makeAlias(); // Setters are defined in the public class | |
// Since pdfAnnot has been set, this calls will write in the Annot object | |
q->setAuthor(author); | |
q->setContents(contents); | |
q->setUniqueName(uniqueName); | |
q->setModificationDate(modDate); | |
q->setCreationDate(creationDate); | |
q->setFlags(flags); | |
// q->setBoundary(boundary); -- already set by subclass-specific code | |
q->setStyle(style); | |
q->setPopup(popup); | |
// Flush revisions | |
foreach (Annotation *r, revisions) { | |
// TODO: Flush revision | |
delete r; // Object is no longer needed | |
} | |
delete q; | |
// Clear some members to save memory | |
author.clear(); | |
contents.clear(); | |
uniqueName.clear(); | |
revisions.clear(); | |
} | |
// Returns matrix to convert from user space coords (oriented according to the | |
// specified rotation) to normalized coords | |
static void fillNormalizationMTX(::Page *pdfPage, double MTX[6], int pageRotation) | |
{ | |
Q_ASSERT(pdfPage); | |
// build a normalized transform matrix for this page at 100% scale | |
GfxState *gfxState = new GfxState(72.0, 72.0, pdfPage->getCropBox(), pageRotation, true); | |
const double *gfxCTM = gfxState->getCTM(); | |
double w = pdfPage->getCropWidth(); | |
double h = pdfPage->getCropHeight(); | |
// Swap width and height if the page is rotated landscape or seascape | |
if (pageRotation == 90 || pageRotation == 270) { | |
double t = w; | |
w = h; | |
h = t; | |
} | |
for (int i = 0; i < 6; i += 2) { | |
MTX[i] = gfxCTM[i] / w; | |
MTX[i + 1] = gfxCTM[i + 1] / h; | |
} | |
delete gfxState; | |
} | |
// Returns matrix to convert from user space coords (i.e. those that are stored | |
// in the PDF file) to normalized coords (i.e. those that we expose to clients). | |
// This method also applies a rotation around the top-left corner if the | |
// FixedRotation flag is set. | |
void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const | |
{ | |
Q_ASSERT(pdfPage); | |
Q_ASSERT(pdfAnnot); | |
const int pageRotate = pdfPage->getRotate(); | |
if (pageRotate == 0 || (pdfAnnot->getFlags() & Annot::flagNoRotate) == 0) { | |
// Use the normalization matrix for this page's rotation | |
fillNormalizationMTX(pdfPage, MTX, pageRotate); | |
} else { | |
// Clients expect coordinates relative to this page's rotation, but | |
// FixedRotation annotations internally use unrotated coordinates: | |
// construct matrix to both normalize and rotate coordinates using the | |
// top-left corner as rotation pivot | |
double MTXnorm[6]; | |
fillNormalizationMTX(pdfPage, MTXnorm, pageRotate); | |
QTransform transform(MTXnorm[0], MTXnorm[1], MTXnorm[2], MTXnorm[3], MTXnorm[4], MTXnorm[5]); | |
transform.translate(+pdfAnnot->getXMin(), +pdfAnnot->getYMax()); | |
transform.rotate(pageRotate); | |
transform.translate(-pdfAnnot->getXMin(), -pdfAnnot->getYMax()); | |
MTX[0] = transform.m11(); | |
MTX[1] = transform.m12(); | |
MTX[2] = transform.m21(); | |
MTX[3] = transform.m22(); | |
MTX[4] = transform.dx(); | |
MTX[5] = transform.dy(); | |
} | |
} | |
QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const | |
{ | |
double swp, MTX[6]; | |
fillTransformationMTX(MTX); | |
QPointF p1, p2; | |
XPDFReader::transform(MTX, r.x1, r.y1, p1); | |
XPDFReader::transform(MTX, r.x2, r.y2, p2); | |
double tl_x = p1.x(); | |
double tl_y = p1.y(); | |
double br_x = p2.x(); | |
double br_y = p2.y(); | |
if (tl_x > br_x) { | |
swp = tl_x; | |
tl_x = br_x; | |
br_x = swp; | |
} | |
if (tl_y > br_y) { | |
swp = tl_y; | |
tl_y = br_y; | |
br_y = swp; | |
} | |
return QRectF(QPointF(tl_x, tl_y), QPointF(br_x, br_y)); | |
} | |
// This function converts a boundary QRectF in normalized coords to a | |
// PDFRectangle in user coords. If the FixedRotation flag is set, this function | |
// also applies a rotation around the top-left corner: it's the inverse of | |
// the transformation produced by fillTransformationMTX, but we can't use | |
// fillTransformationMTX here because it relies on the native annotation | |
// object's boundary rect to be already set up. | |
PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int rFlags) | |
{ | |
Q_ASSERT(pdfPage); | |
const int pageRotate = pdfPage->getRotate(); | |
double MTX[6]; | |
fillNormalizationMTX(pdfPage, MTX, pageRotate); | |
double tl_x, tl_y, br_x, br_y, swp; | |
XPDFReader::invTransform(MTX, r.topLeft(), tl_x, tl_y); | |
XPDFReader::invTransform(MTX, r.bottomRight(), br_x, br_y); | |
if (tl_x > br_x) { | |
swp = tl_x; | |
tl_x = br_x; | |
br_x = swp; | |
} | |
if (tl_y > br_y) { | |
swp = tl_y; | |
tl_y = br_y; | |
br_y = swp; | |
} | |
const int rotationFixUp = (rFlags & Annotation::FixedRotation) ? pageRotate : 0; | |
const double width = br_x - tl_x; | |
const double height = br_y - tl_y; | |
if (rotationFixUp == 0) { | |
return PDFRectangle(tl_x, tl_y, br_x, br_y); | |
} else if (rotationFixUp == 90) { | |
return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y); | |
} else if (rotationFixUp == 180) { | |
return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y); | |
} else { // rotationFixUp == 270 | |
return PDFRectangle(br_x, br_y - width, br_x + height, br_y); | |
} | |
} | |
PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFlags) const | |
{ | |
return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags); | |
} | |
AnnotPath *AnnotationPrivate::toAnnotPath(const QVector<QPointF> &list) const | |
{ | |
const int count = list.size(); | |
std::vector<AnnotCoord> ac; | |
ac.reserve(count); | |
double MTX[6]; | |
fillTransformationMTX(MTX); | |
foreach (const QPointF &p, list) { | |
double x, y; | |
XPDFReader::invTransform(MTX, p, x, y); | |
ac.emplace_back(x, y); | |
} | |
return new AnnotPath(std::move(ac)); | |
} | |
std::vector<std::unique_ptr<Annotation>> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet<Annotation::SubType> &subtypes, int parentID) | |
{ | |
Annots *annots = pdfPage->getAnnots(); | |
const bool wantTextAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AText); | |
const bool wantLineAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALine); | |
const bool wantGeomAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AGeom); | |
const bool wantHighlightAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AHighlight); | |
const bool wantStampAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AStamp); | |
const bool wantInkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AInk); | |
const bool wantLinkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALink); | |
const bool wantCaretAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ACaret); | |
const bool wantFileAttachmentAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AFileAttachment); | |
const bool wantSoundAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ASound); | |
const bool wantMovieAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AMovie); | |
const bool wantScreenAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AScreen); | |
const bool wantWidgetAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AWidget); | |
// Create Annotation objects and tie to their native Annot | |
std::vector<std::unique_ptr<Annotation>> res; | |
for (Annot *ann : annots->getAnnots()) { | |
if (!ann) { | |
error(errInternal, -1, "Annot is null"); | |
continue; | |
} | |
// Check parent annotation | |
AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(ann); | |
if (!markupann) { | |
// Assume it's a root annotation, and skip if user didn't request it | |
if (parentID != -1) { | |
continue; | |
} | |
} else if (markupann->getInReplyToID() != parentID) { | |
continue; | |
} | |
/* Create Annotation of the right subclass */ | |
std::unique_ptr<Annotation> annotation; | |
Annot::AnnotSubtype subType = ann->getType(); | |
switch (subType) { | |
case Annot::typeText: | |
if (!wantTextAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<TextAnnotation>(TextAnnotation::Linked); | |
break; | |
case Annot::typeFreeText: | |
if (!wantTextAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<TextAnnotation>(TextAnnotation::InPlace); | |
break; | |
case Annot::typeLine: | |
if (!wantLineAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<LineAnnotation>(LineAnnotation::StraightLine); | |
break; | |
case Annot::typePolygon: | |
case Annot::typePolyLine: | |
if (!wantLineAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<LineAnnotation>(LineAnnotation::Polyline); | |
break; | |
case Annot::typeSquare: | |
case Annot::typeCircle: | |
if (!wantGeomAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<GeomAnnotation>(); | |
break; | |
case Annot::typeHighlight: | |
case Annot::typeUnderline: | |
case Annot::typeSquiggly: | |
case Annot::typeStrikeOut: | |
if (!wantHighlightAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<HighlightAnnotation>(); | |
break; | |
case Annot::typeStamp: | |
if (!wantStampAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<StampAnnotation>(); | |
break; | |
case Annot::typeInk: | |
if (!wantInkAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<InkAnnotation>(); | |
break; | |
case Annot::typeLink: /* TODO: Move logic to getters */ | |
{ | |
if (!wantLinkAnnotations) { | |
continue; | |
} | |
// parse Link params | |
AnnotLink *linkann = static_cast<AnnotLink *>(ann); | |
LinkAnnotation *l = new LinkAnnotation(); | |
// -> hlMode | |
l->setLinkHighlightMode((LinkAnnotation::HighlightMode)linkann->getLinkEffect()); | |
// -> link region | |
// TODO | |
// reading link action | |
if (linkann->getAction()) { | |
std::unique_ptr<Link> popplerLink = PageData::convertLinkActionToLink(linkann->getAction(), doc, QRectF()); | |
if (popplerLink) { | |
l->setLinkDestination(std::move(popplerLink)); | |
} | |
} | |
annotation.reset(l); | |
break; | |
} | |
case Annot::typeCaret: | |
if (!wantCaretAnnotations) { | |
continue; | |
} | |
annotation = std::make_unique<CaretAnnotation>(); | |
break; | |
case Annot::typeFileAttachment: /* TODO: Move logic to getters */ | |
{ | |
if (!wantFileAttachmentAnnotations) { | |
continue; | |
} | |
AnnotFileAttachment *attachann = static_cast<AnnotFileAttachment *>(ann); | |
FileAttachmentAnnotation *f = new FileAttachmentAnnotation(); | |
// -> fileIcon | |
f->setFileIconName(QString::fromLatin1(attachann->getName()->c_str())); | |
// -> embeddedFile | |
auto filespec = std::make_unique<FileSpec>(attachann->getFile()); | |
f->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(std::move(filespec)))); | |
annotation.reset(f); | |
break; | |
} | |
case Annot::typeSound: /* TODO: Move logic to getters */ | |
{ | |
if (!wantSoundAnnotations) { | |
continue; | |
} | |
AnnotSound *soundann = static_cast<AnnotSound *>(ann); | |
SoundAnnotation *s = new SoundAnnotation(); | |
// -> soundIcon | |
s->setSoundIconName(QString::fromLatin1(soundann->getName()->c_str())); | |
// -> sound | |
s->setSound(new SoundObject(soundann->getSound())); | |
annotation.reset(s); | |
break; | |
} | |
case Annot::typeMovie: /* TODO: Move logic to getters */ | |
{ | |
if (!wantMovieAnnotations) { | |
continue; | |
} | |
AnnotMovie *movieann = static_cast<AnnotMovie *>(ann); | |
MovieAnnotation *m = new MovieAnnotation(); | |
// -> movie | |
MovieObject *movie = new MovieObject(movieann); | |
m->setMovie(movie); | |
// -> movieTitle | |
const GooString *movietitle = movieann->getTitle(); | |
if (movietitle) { | |
m->setMovieTitle(QString::fromLatin1(movietitle->c_str())); | |
} | |
annotation.reset(m); | |
break; | |
} | |
case Annot::typeScreen: { | |
if (!wantScreenAnnotations) { | |
continue; | |
} | |
AnnotScreen *screenann = static_cast<AnnotScreen *>(ann); | |
// TODO Support other link types than Link::Rendition in ScreenAnnotation | |
if (!screenann->getAction() || screenann->getAction()->getKind() != actionRendition) { | |
continue; | |
} | |
ScreenAnnotation *s = new ScreenAnnotation(); | |
// -> screen | |
std::unique_ptr<Link> popplerLink = PageData::convertLinkActionToLink(screenann->getAction(), doc, QRectF()); | |
s->setAction(static_cast<Poppler::LinkRendition *>(popplerLink.release())); | |
// -> screenTitle | |
const GooString *screentitle = screenann->getTitle(); | |
if (screentitle) { | |
s->setScreenTitle(UnicodeParsedString(screentitle)); | |
} | |
annotation.reset(s); | |
break; | |
} | |
case Annot::typePopup: | |
continue; // popups are parsed by Annotation's window() getter | |
case Annot::typeUnknown: | |
continue; // special case for ignoring unknown annotations | |
case Annot::typeWidget: | |
if (!wantWidgetAnnotations) { | |
continue; | |
} | |
annotation.reset(new WidgetAnnotation()); | |
break; | |
case Annot::typeRichMedia: { | |
const AnnotRichMedia *annotRichMedia = static_cast<AnnotRichMedia *>(ann); | |
RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation; | |
const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings(); | |
if (annotSettings) { | |
RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings; | |
if (annotSettings->getActivation()) { | |
RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation; | |
switch (annotSettings->getActivation()->getCondition()) { | |
case AnnotRichMedia::Activation::conditionPageOpened: | |
activation->setCondition(RichMediaAnnotation::Activation::PageOpened); | |
break; | |
case AnnotRichMedia::Activation::conditionPageVisible: | |
activation->setCondition(RichMediaAnnotation::Activation::PageVisible); | |
break; | |
case AnnotRichMedia::Activation::conditionUserAction: | |
activation->setCondition(RichMediaAnnotation::Activation::UserAction); | |
break; | |
} | |
settings->setActivation(activation); | |
} | |
if (annotSettings->getDeactivation()) { | |
RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation; | |
switch (annotSettings->getDeactivation()->getCondition()) { | |
case AnnotRichMedia::Deactivation::conditionPageClosed: | |
deactivation->setCondition(RichMediaAnnotation::Deactivation::PageClosed); | |
break; | |
case AnnotRichMedia::Deactivation::conditionPageInvisible: | |
deactivation->setCondition(RichMediaAnnotation::Deactivation::PageInvisible); | |
break; | |
case AnnotRichMedia::Deactivation::conditionUserAction: | |
deactivation->setCondition(RichMediaAnnotation::Deactivation::UserAction); | |
break; | |
} | |
settings->setDeactivation(deactivation); | |
} | |
richMediaAnnotation->setSettings(settings); | |
} | |
const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent(); | |
if (annotContent) { | |
RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content; | |
const int configurationsCount = annotContent->getConfigurationsCount(); | |
if (configurationsCount > 0) { | |
QList<RichMediaAnnotation::Configuration *> configurations; | |
for (int i = 0; i < configurationsCount; ++i) { | |
const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration(i); | |
if (!annotConfiguration) { | |
continue; | |
} | |
RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration; | |
if (annotConfiguration->getName()) { | |
configuration->setName(UnicodeParsedString(annotConfiguration->getName())); | |
} | |
switch (annotConfiguration->getType()) { | |
case AnnotRichMedia::Configuration::type3D: | |
configuration->setType(RichMediaAnnotation::Configuration::Type3D); | |
break; | |
case AnnotRichMedia::Configuration::typeFlash: | |
configuration->setType(RichMediaAnnotation::Configuration::TypeFlash); | |
break; | |
case AnnotRichMedia::Configuration::typeSound: | |
configuration->setType(RichMediaAnnotation::Configuration::TypeSound); | |
break; | |
case AnnotRichMedia::Configuration::typeVideo: | |
configuration->setType(RichMediaAnnotation::Configuration::TypeVideo); | |
break; | |
} | |
const int instancesCount = annotConfiguration->getInstancesCount(); | |
if (instancesCount > 0) { | |
QList<RichMediaAnnotation::Instance *> instances; | |
for (int j = 0; j < instancesCount; ++j) { | |
const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance(j); | |
if (!annotInstance) { | |
continue; | |
} | |
RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance; | |
switch (annotInstance->getType()) { | |
case AnnotRichMedia::Instance::type3D: | |
instance->setType(RichMediaAnnotation::Instance::Type3D); | |
break; | |
case AnnotRichMedia::Instance::typeFlash: | |
instance->setType(RichMediaAnnotation::Instance::TypeFlash); | |
break; | |
case AnnotRichMedia::Instance::typeSound: | |
instance->setType(RichMediaAnnotation::Instance::TypeSound); | |
break; | |
case AnnotRichMedia::Instance::typeVideo: | |
instance->setType(RichMediaAnnotation::Instance::TypeVideo); | |
break; | |
} | |
const AnnotRichMedia::Params *annotParams = annotInstance->getParams(); | |
if (annotParams) { | |
RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params; | |
if (annotParams->getFlashVars()) { | |
params->setFlashVars(UnicodeParsedString(annotParams->getFlashVars())); | |
} | |
instance->setParams(params); | |
} | |
instances.append(instance); | |
} | |
configuration->setInstances(instances); | |
} | |
configurations.append(configuration); | |
} | |
content->setConfigurations(configurations); | |
} | |
const int assetsCount = annotContent->getAssetsCount(); | |
if (assetsCount > 0) { | |
QList<RichMediaAnnotation::Asset *> assets; | |
for (int i = 0; i < assetsCount; ++i) { | |
const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset(i); | |
if (!annotAsset) { | |
continue; | |
} | |
RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset; | |
if (annotAsset->getName()) { | |
asset->setName(UnicodeParsedString(annotAsset->getName())); | |
} | |
auto fileSpec = std::make_unique<FileSpec>(annotAsset->getFileSpec()); | |
asset->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(std::move(fileSpec)))); | |
assets.append(asset); | |
} | |
content->setAssets(assets); | |
} | |
richMediaAnnotation->setContent(content); | |
} | |
annotation.reset(richMediaAnnotation); | |
break; | |
} | |
default: { | |
switch (subType) { | |
CASE_FOR_TYPE(PrinterMark) | |
CASE_FOR_TYPE(TrapNet) | |
CASE_FOR_TYPE(Watermark) | |
CASE_FOR_TYPE(3D) | |
default: | |
error(errUnimplemented, -1, "Annotation {0:d} not supported", subType); | |
} | |
continue; | |
} | |
} | |
annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc); | |
res.push_back(std::move(annotation)); | |
} | |
return res; | |
} | |
Ref AnnotationPrivate::pdfObjectReference() const | |
{ | |
if (pdfAnnot == nullptr) { | |
return Ref::INVALID(); | |
} | |
return pdfAnnot->getRef(); | |
} | |
std::unique_ptr<Link> AnnotationPrivate::additionalAction(Annotation::AdditionalActionType type) const | |
{ | |
if (pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget) { | |
return {}; | |
} | |
const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); | |
std::unique_ptr<::LinkAction> linkAction; | |
if (pdfAnnot->getType() == Annot::typeScreen) { | |
linkAction = static_cast<AnnotScreen *>(pdfAnnot)->getAdditionalAction(actionType); | |
} else { | |
linkAction = static_cast<AnnotWidget *>(pdfAnnot)->getAdditionalAction(actionType); | |
} | |
if (linkAction) { | |
return PageData::convertLinkActionToLink(linkAction.get(), parentDoc, QRectF()); | |
} | |
return {}; | |
} | |
void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann) | |
{ | |
if (ann->d_ptr->pdfAnnot != nullptr) { | |
error(errIO, -1, "Annotation is already tied"); | |
return; | |
} | |
// Unimplemented annotations can't be created by the user because their ctor | |
// is private. Therefore, createNativeAnnot will never return 0 | |
Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc); | |
Q_ASSERT(nativeAnnot); | |
if (ann->d_ptr->annotationAppearance.isStream()) { | |
nativeAnnot->setNewAppearance(ann->d_ptr->annotationAppearance.copy()); | |
} | |
pdfPage->addAnnot(nativeAnnot); | |
} | |
void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann) | |
{ | |
if (ann->d_ptr->pdfAnnot == nullptr) { | |
error(errIO, -1, "Annotation is not tied"); | |
return; | |
} | |
if (ann->d_ptr->pdfPage != pdfPage) { | |
error(errIO, -1, "Annotation doesn't belong to the specified page"); | |
return; | |
} | |
// Remove annotation | |
pdfPage->removeAnnot(ann->d_ptr->pdfAnnot); | |
// Destroy object | |
delete ann; | |
} | |
class TextAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
TextAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
void setDefaultAppearanceToNative(); | |
std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const; | |
// data fields | |
TextAnnotation::TextType textType; | |
QString textIcon; | |
std::optional<QFont> textFont; | |
QColor textColor = Qt::black; | |
TextAnnotation::InplaceAlignPosition inplaceAlign; | |
QVector<QPointF> inplaceCallout; | |
TextAnnotation::InplaceIntent inplaceIntent; | |
}; | |
class Annotation::Style::Private : public QSharedData | |
{ | |
public: | |
Private() : opacity(1.0), width(1.0), lineStyle(Solid), xCorners(0.0), yCorners(0.0), lineEffect(NoEffect), effectIntensity(1.0) | |
{ | |
dashArray.resize(1); | |
dashArray[0] = 3; | |
} | |
QColor color; | |
double opacity; | |
double width; | |
Annotation::LineStyle lineStyle; | |
double xCorners; | |
double yCorners; | |
QVector<double> dashArray; | |
Annotation::LineEffect lineEffect; | |
double effectIntensity; | |
}; | |
Annotation::Style::Style() : d(new Private) { } | |
Annotation::Style::Style(const Style &other) : d(other.d) { } | |
Annotation::Style &Annotation::Style::operator=(const Style &other) | |
{ | |
if (this != &other) { | |
d = other.d; | |
} | |
return *this; | |
} | |
Annotation::Style::~Style() { } | |
QColor Annotation::Style::color() const | |
{ | |
return d->color; | |
} | |
void Annotation::Style::setColor(const QColor &color) | |
{ | |
d->color = color; | |
} | |
double Annotation::Style::opacity() const | |
{ | |
return d->opacity; | |
} | |
void Annotation::Style::setOpacity(double opacity) | |
{ | |
d->opacity = opacity; | |
} | |
double Annotation::Style::width() const | |
{ | |
return d->width; | |
} | |
void Annotation::Style::setWidth(double width) | |
{ | |
d->width = width; | |
} | |
Annotation::LineStyle Annotation::Style::lineStyle() const | |
{ | |
return d->lineStyle; | |
} | |
void Annotation::Style::setLineStyle(Annotation::LineStyle style) | |
{ | |
d->lineStyle = style; | |
} | |
double Annotation::Style::xCorners() const | |
{ | |
return d->xCorners; | |
} | |
void Annotation::Style::setXCorners(double radius) | |
{ | |
d->xCorners = radius; | |
} | |
double Annotation::Style::yCorners() const | |
{ | |
return d->yCorners; | |
} | |
void Annotation::Style::setYCorners(double radius) | |
{ | |
d->yCorners = radius; | |
} | |
const QVector<double> &Annotation::Style::dashArray() const | |
{ | |
return d->dashArray; | |
} | |
void Annotation::Style::setDashArray(const QVector<double> &array) | |
{ | |
d->dashArray = array; | |
} | |
Annotation::LineEffect Annotation::Style::lineEffect() const | |
{ | |
return d->lineEffect; | |
} | |
void Annotation::Style::setLineEffect(Annotation::LineEffect effect) | |
{ | |
d->lineEffect = effect; | |
} | |
double Annotation::Style::effectIntensity() const | |
{ | |
return d->effectIntensity; | |
} | |
void Annotation::Style::setEffectIntensity(double intens) | |
{ | |
d->effectIntensity = intens; | |
} | |
class Annotation::Popup::Private : public QSharedData | |
{ | |
public: | |
Private() : flags(-1) { } | |
int flags; | |
QRectF geometry; | |
QString title; | |
QString summary; | |
QString text; | |
}; | |
Annotation::Popup::Popup() : d(new Private) { } | |
Annotation::Popup::Popup(const Popup &other) : d(other.d) { } | |
Annotation::Popup &Annotation::Popup::operator=(const Popup &other) | |
{ | |
if (this != &other) { | |
d = other.d; | |
} | |
return *this; | |
} | |
Annotation::Popup::~Popup() { } | |
int Annotation::Popup::flags() const | |
{ | |
return d->flags; | |
} | |
void Annotation::Popup::setFlags(int flags) | |
{ | |
d->flags = flags; | |
} | |
QRectF Annotation::Popup::geometry() const | |
{ | |
return d->geometry; | |
} | |
void Annotation::Popup::setGeometry(const QRectF &geom) | |
{ | |
d->geometry = geom; | |
} | |
QString Annotation::Popup::title() const | |
{ | |
return d->title; | |
} | |
void Annotation::Popup::setTitle(const QString &title) | |
{ | |
d->title = title; | |
} | |
QString Annotation::Popup::summary() const | |
{ | |
return d->summary; | |
} | |
void Annotation::Popup::setSummary(const QString &summary) | |
{ | |
d->summary = summary; | |
} | |
QString Annotation::Popup::text() const | |
{ | |
return d->text; | |
} | |
void Annotation::Popup::setText(const QString &text) | |
{ | |
d->text = text; | |
} | |
Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { } | |
Annotation::~Annotation() { } | |
QString Annotation::author() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->author; | |
} | |
const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot); | |
return markupann ? UnicodeParsedString(markupann->getLabel()) : QString(); | |
} | |
void Annotation::setAuthor(const QString &author) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->author = author; | |
return; | |
} | |
AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot); | |
if (markupann) { | |
markupann->setLabel(std::unique_ptr<GooString>(QStringToUnicodeGooString(author))); | |
} | |
} | |
QString Annotation::contents() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->contents; | |
} | |
return UnicodeParsedString(d->pdfAnnot->getContents()); | |
} | |
void Annotation::setContents(const QString &contents) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->contents = contents; | |
return; | |
} | |
d->pdfAnnot->setContents(std::unique_ptr<GooString>(QStringToUnicodeGooString(contents))); | |
TextAnnotationPrivate *textAnnotD = dynamic_cast<TextAnnotationPrivate *>(d); | |
if (textAnnotD) { | |
textAnnotD->setDefaultAppearanceToNative(); | |
} | |
} | |
QString Annotation::uniqueName() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->uniqueName; | |
} | |
return UnicodeParsedString(d->pdfAnnot->getName()); | |
} | |
void Annotation::setUniqueName(const QString &uniqueName) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->uniqueName = uniqueName; | |
return; | |
} | |
QByteArray ascii = uniqueName.toLatin1(); | |
GooString s(ascii.constData()); | |
d->pdfAnnot->setName(&s); | |
} | |
QDateTime Annotation::modificationDate() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->modDate; | |
} | |
if (d->pdfAnnot->getModified()) { | |
return convertDate(d->pdfAnnot->getModified()->c_str()); | |
} else { | |
return QDateTime(); | |
} | |
} | |
void Annotation::setModificationDate(const QDateTime &date) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->modDate = date; | |
return; | |
} | |
if (d->pdfAnnot) { | |
if (date.isValid()) { | |
const time_t t = date.toSecsSinceEpoch(); | |
GooString *s = timeToDateString(&t); | |
d->pdfAnnot->setModified(s); | |
delete s; | |
} else { | |
d->pdfAnnot->setModified(nullptr); | |
} | |
} | |
} | |
QDateTime Annotation::creationDate() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->creationDate; | |
} | |
const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot); | |
if (markupann && markupann->getDate()) { | |
return convertDate(markupann->getDate()->c_str()); | |
} | |
return modificationDate(); | |
} | |
void Annotation::setCreationDate(const QDateTime &date) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->creationDate = date; | |
return; | |
} | |
AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot); | |
if (markupann) { | |
if (date.isValid()) { | |
const time_t t = date.toSecsSinceEpoch(); | |
GooString *s = timeToDateString(&t); | |
markupann->setDate(s); | |
delete s; | |
} else { | |
markupann->setDate(nullptr); | |
} | |
} | |
} | |
static Annotation::Flags fromPdfFlags(int flags) | |
{ | |
Annotation::Flags qtflags; | |
if (flags & Annot::flagHidden) { | |
qtflags |= Annotation::Hidden; | |
} | |
if (flags & Annot::flagNoZoom) { | |
qtflags |= Annotation::FixedSize; | |
} | |
if (flags & Annot::flagNoRotate) { | |
qtflags |= Annotation::FixedRotation; | |
} | |
if (!(flags & Annot::flagPrint)) { | |
qtflags |= Annotation::DenyPrint; | |
} | |
if (flags & Annot::flagReadOnly) { | |
qtflags |= Annotation::DenyWrite; | |
qtflags |= Annotation::DenyDelete; | |
} | |
if (flags & Annot::flagLocked) { | |
qtflags |= Annotation::DenyDelete; | |
} | |
if (flags & Annot::flagToggleNoView) { | |
qtflags |= Annotation::ToggleHidingOnMouse; | |
} | |
return qtflags; | |
} | |
static int toPdfFlags(Annotation::Flags qtflags) | |
{ | |
int flags = 0; | |
if (qtflags & Annotation::Hidden) { | |
flags |= Annot::flagHidden; | |
} | |
if (qtflags & Annotation::FixedSize) { | |
flags |= Annot::flagNoZoom; | |
} | |
if (qtflags & Annotation::FixedRotation) { | |
flags |= Annot::flagNoRotate; | |
} | |
if (!(qtflags & Annotation::DenyPrint)) { | |
flags |= Annot::flagPrint; | |
} | |
if (qtflags & Annotation::DenyWrite) { | |
flags |= Annot::flagReadOnly; | |
} | |
if (qtflags & Annotation::DenyDelete) { | |
flags |= Annot::flagLocked; | |
} | |
if (qtflags & Annotation::ToggleHidingOnMouse) { | |
flags |= Annot::flagToggleNoView; | |
} | |
return flags; | |
} | |
Annotation::Flags Annotation::flags() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->flags; | |
} | |
return fromPdfFlags(d->pdfAnnot->getFlags()); | |
} | |
void Annotation::setFlags(Annotation::Flags flags) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->flags = flags; | |
return; | |
} | |
d->pdfAnnot->setFlags(toPdfFlags(flags)); | |
} | |
QRectF Annotation::boundary() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->boundary; | |
} | |
const PDFRectangle &rect = d->pdfAnnot->getRect(); | |
return d->fromPdfRectangle(rect); | |
} | |
void Annotation::setBoundary(const QRectF &boundary) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->boundary = boundary; | |
return; | |
} | |
const PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags()); | |
if (rect == d->pdfAnnot->getRect()) { | |
return; | |
} | |
d->pdfAnnot->setRect(&rect); | |
} | |
Annotation::Style Annotation::style() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->style; | |
} | |
Style s; | |
s.setColor(convertAnnotColor(d->pdfAnnot->getColor())); | |
const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot); | |
if (markupann) { | |
s.setOpacity(markupann->getOpacity()); | |
} | |
const AnnotBorder *border = d->pdfAnnot->getBorder(); | |
if (border) { | |
if (border->getType() == AnnotBorder::typeArray) { | |
const AnnotBorderArray *border_array = static_cast<const AnnotBorderArray *>(border); | |
s.setXCorners(border_array->getHorizontalCorner()); | |
s.setYCorners(border_array->getVerticalCorner()); | |
} | |
s.setWidth(border->getWidth()); | |
s.setLineStyle((Annotation::LineStyle)(1 << border->getStyle())); | |
const std::vector<double> &dashArray = border->getDash(); | |
s.setDashArray(QVector<double>(dashArray.begin(), dashArray.end())); | |
} | |
AnnotBorderEffect *border_effect; | |
switch (d->pdfAnnot->getType()) { | |
case Annot::typeFreeText: | |
border_effect = static_cast<AnnotFreeText *>(d->pdfAnnot)->getBorderEffect(); | |
break; | |
case Annot::typeSquare: | |
case Annot::typeCircle: | |
border_effect = static_cast<AnnotGeometry *>(d->pdfAnnot)->getBorderEffect(); | |
break; | |
default: | |
border_effect = nullptr; | |
} | |
if (border_effect) { | |
s.setLineEffect((Annotation::LineEffect)border_effect->getEffectType()); | |
s.setEffectIntensity(border_effect->getIntensity()); | |
} | |
return s; | |
} | |
void Annotation::setStyle(const Annotation::Style &style) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->style = style; | |
return; | |
} | |
d->pdfAnnot->setColor(convertQColor(style.color())); | |
AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot); | |
if (markupann) { | |
markupann->setOpacity(style.opacity()); | |
} | |
auto border = std::make_unique<AnnotBorderArray>(); | |
border->setWidth(style.width()); | |
border->setHorizontalCorner(style.xCorners()); | |
border->setVerticalCorner(style.yCorners()); | |
d->pdfAnnot->setBorder(std::move(border)); | |
} | |
Annotation::Popup Annotation::popup() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->popup; | |
} | |
Popup w; | |
AnnotPopup *popup = nullptr; | |
int flags = -1; // Not initialized | |
const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot); | |
if (markupann) { | |
popup = markupann->getPopup(); | |
w.setSummary(UnicodeParsedString(markupann->getSubject())); | |
} | |
if (popup) { | |
flags = fromPdfFlags(popup->getFlags()) & (Annotation::Hidden | Annotation::FixedSize | Annotation::FixedRotation); | |
if (!popup->getOpen()) { | |
flags |= Annotation::Hidden; | |
} | |
const PDFRectangle &rect = popup->getRect(); | |
w.setGeometry(d->fromPdfRectangle(rect)); | |
} | |
if (d->pdfAnnot->getType() == Annot::typeText) { | |
const AnnotText *textann = static_cast<const AnnotText *>(d->pdfAnnot); | |
// Text annotations default to same rect as annotation | |
if (flags == -1) { | |
flags = 0; | |
w.setGeometry(boundary()); | |
} | |
// If text is not 'opened', force window hiding. if the window | |
// was parsed from popup, the flag should already be set | |
if (!textann->getOpen() && flags != -1) { | |
flags |= Annotation::Hidden; | |
} | |
} | |
w.setFlags(flags); | |
return w; | |
} | |
void Annotation::setPopup(const Annotation::Popup &popup) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->popup = popup; | |
return; | |
} | |
AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot); | |
if (!markupann) | |
return; | |
// Create a new AnnotPopup and assign it to pdfAnnot | |
PDFRectangle rect = d->toPdfRectangle( popup.geometry() ); | |
AnnotPopup * p = new AnnotPopup( d->pdfPage->getDoc(), &rect ); | |
p->setOpen( !(popup.flags() & Annotation::Hidden) ); | |
if (!popup.summary().isEmpty()) | |
{ | |
GooString *s = QStringToUnicodeGooString(popup.summary()); | |
markupann->setLabel(s); | |
delete s; | |
} | |
markupann->setPopup(p); | |
} | |
Annotation::RevScope Annotation::revisionScope() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->revisionScope; | |
} | |
const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot); | |
if (markupann && markupann->isInReplyTo()) { | |
switch (markupann->getReplyTo()) { | |
case AnnotMarkup::replyTypeR: | |
return Annotation::Reply; | |
case AnnotMarkup::replyTypeGroup: | |
return Annotation::Group; | |
} | |
} | |
return Annotation::Root; // It's not a revision | |
} | |
Annotation::RevType Annotation::revisionType() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
return d->revisionType; | |
} | |
const AnnotText *textann = dynamic_cast<const AnnotText *>(d->pdfAnnot); | |
if (textann && textann->isInReplyTo()) { | |
switch (textann->getState()) { | |
case AnnotText::stateMarked: | |
return Annotation::Marked; | |
case AnnotText::stateUnmarked: | |
return Annotation::Unmarked; | |
case AnnotText::stateAccepted: | |
return Annotation::Accepted; | |
case AnnotText::stateRejected: | |
return Annotation::Rejected; | |
case AnnotText::stateCancelled: | |
return Annotation::Cancelled; | |
case AnnotText::stateCompleted: | |
return Annotation::Completed; | |
default: | |
break; | |
} | |
} | |
return Annotation::None; | |
} | |
std::vector<std::unique_ptr<Annotation>> Annotation::revisions() const | |
{ | |
Q_D(const Annotation); | |
if (!d->pdfAnnot) { | |
/* Return aliases, whose ownership goes to the caller */ | |
std::vector<std::unique_ptr<Annotation>> res; | |
foreach (Annotation *rev, d->revisions) | |
res.push_back(std::unique_ptr<Annotation>(rev->d_ptr->makeAlias())); | |
return res; | |
} | |
/* If the annotation doesn't live in a object on its own (eg bug51361), it | |
* has no ref, therefore it can't have revisions */ | |
if (!d->pdfAnnot->getHasRef()) { | |
return std::vector<std::unique_ptr<Annotation>>(); | |
} | |
return AnnotationPrivate::findAnnotations(d->pdfPage, d->parentDoc, QSet<Annotation::SubType>(), d->pdfAnnot->getId()); | |
} | |
std::unique_ptr<AnnotationAppearance> Annotation::annotationAppearance() const | |
{ | |
Q_D(const Annotation); | |
return std::make_unique<AnnotationAppearance>(new AnnotationAppearancePrivate(d->pdfAnnot)); | |
} | |
void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationAppearance) | |
{ | |
Q_D(Annotation); | |
if (!d->pdfAnnot) { | |
d->annotationAppearance = annotationAppearance.d->appearance.copy(); | |
return; | |
} | |
// Moving the appearance object using std::move would result | |
// in the object being completed moved from the AnnotationAppearancePrivate | |
// class. So, we'll not be able to retrieve the stamp's original AP stream | |
d->pdfAnnot->setNewAppearance(annotationAppearance.d->appearance.copy()); | |
} | |
// END Annotation implementation | |
/** TextAnnotation [Annotation] */ | |
TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(TextAnnotation::InplaceAlignLeft), inplaceIntent(TextAnnotation::Unknown) { } | |
Annotation *TextAnnotationPrivate::makeAlias() | |
{ | |
return new TextAnnotation(*this); | |
} | |
Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
// Setters are defined in the public class | |
TextAnnotation *q = static_cast<TextAnnotation *>(makeAlias()); | |
// Set page and contents | |
pdfPage = destPage; | |
parentDoc = doc; | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
if (textType == TextAnnotation::Linked) { | |
pdfAnnot = new AnnotText { destPage->getDoc(), &rect }; | |
} else { | |
const double pointSize = textFont ? textFont->pointSizeF() : AnnotFreeText::undefinedFontPtSize; | |
if (pointSize < 0) { | |
qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0"; | |
} | |
pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect }; | |
} | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setTextIcon(textIcon); | |
q->setInplaceAlign(inplaceAlign); | |
q->setCalloutPoints(inplaceCallout); | |
q->setInplaceIntent(inplaceIntent); | |
delete q; | |
inplaceCallout.clear(); // Free up memory | |
setDefaultAppearanceToNative(); | |
return pdfAnnot; | |
} | |
void TextAnnotationPrivate::setDefaultAppearanceToNative() | |
{ | |
if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { | |
AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(pdfAnnot); | |
const double pointSize = textFont ? textFont->pointSizeF() : AnnotFreeText::undefinedFontPtSize; | |
if (pointSize < 0) { | |
qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0"; | |
} | |
std::string fontName = "Invalid_font"; | |
if (textFont) { | |
Form *form = pdfPage->getDoc()->getCatalog()->getCreateForm(); | |
if (form) { | |
fontName = form->findFontInDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString()); | |
if (fontName.empty()) { | |
fontName = form->addFontToDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString()).fontName; | |
} | |
if (!fontName.empty()) { | |
form->ensureFontsForAllCharacters(pdfAnnot->getContents(), fontName); | |
} else { | |
fontName = "Invalid_font"; | |
} | |
} | |
} | |
DefaultAppearance da { { objName, fontName.c_str() }, pointSize, convertQColor(textColor) }; | |
ftextann->setDefaultAppearance(da); | |
} | |
} | |
std::unique_ptr<DefaultAppearance> TextAnnotationPrivate::getDefaultAppearanceFromNative() const | |
{ | |
if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { | |
AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(pdfAnnot); | |
return ftextann->getDefaultAppearance(); | |
} else { | |
return {}; | |
} | |
} | |
TextAnnotation::TextAnnotation(TextAnnotation::TextType type) : Annotation(*new TextAnnotationPrivate()) | |
{ | |
setTextType(type); | |
} | |
TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) : Annotation(dd) { } | |
TextAnnotation::~TextAnnotation() { } | |
Annotation::SubType TextAnnotation::subType() const | |
{ | |
return AText; | |
} | |
TextAnnotation::TextType TextAnnotation::textType() const | |
{ | |
Q_D(const TextAnnotation); | |
if (!d->pdfAnnot) { | |
return d->textType; | |
} | |
return d->pdfAnnot->getType() == Annot::typeText ? TextAnnotation::Linked : TextAnnotation::InPlace; | |
} | |
void TextAnnotation::setTextType(TextAnnotation::TextType type) | |
{ | |
Q_D(TextAnnotation); | |
if (!d->pdfAnnot) { | |
d->textType = type; | |
return; | |
} | |
// Type cannot be changed if annotation is already tied | |
qWarning() << "You can't change the type of a TextAnnotation that is already in a page"; | |
} | |
QString TextAnnotation::textIcon() const | |
{ | |
Q_D(const TextAnnotation); | |
if (!d->pdfAnnot) { | |
return d->textIcon; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeText) { | |
const AnnotText *textann = static_cast<const AnnotText *>(d->pdfAnnot); | |
return QString::fromLatin1(textann->getIcon()->c_str()); | |
} | |
return QString(); | |
} | |
void TextAnnotation::setTextIcon(const QString &icon) | |
{ | |
Q_D(TextAnnotation); | |
if (!d->pdfAnnot) { | |
d->textIcon = icon; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeText) { | |
AnnotText *textann = static_cast<AnnotText *>(d->pdfAnnot); | |
QByteArray encoded = icon.toLatin1(); | |
GooString s(encoded.constData()); | |
textann->setIcon(&s); | |
} | |
} | |
QFont TextAnnotation::textFont() const | |
{ | |
Q_D(const TextAnnotation); | |
if (d->textFont) { | |
return *d->textFont; | |
} | |
double fontSize { AnnotFreeText::undefinedFontPtSize }; | |
if (d->pdfAnnot->getType() == Annot::typeFreeText) { | |
std::unique_ptr<DefaultAppearance> da { d->getDefaultAppearanceFromNative() }; | |
if (da && da->getFontPtSize() > 0) { | |
fontSize = da->getFontPtSize(); | |
} | |
} | |
QFont font; | |
font.setPointSizeF(fontSize); | |
return font; | |
} | |
void TextAnnotation::setTextFont(const QFont &font) | |
{ | |
Q_D(TextAnnotation); | |
if (font == d->textFont) { | |
return; | |
} | |
d->textFont = font; | |
d->setDefaultAppearanceToNative(); | |
} | |
QColor TextAnnotation::textColor() const | |
{ | |
Q_D(const TextAnnotation); | |
if (!d->pdfAnnot) { | |
return d->textColor; | |
} | |
if (std::unique_ptr<DefaultAppearance> da { d->getDefaultAppearanceFromNative() }) { | |
return convertAnnotColor(da->getFontColor()); | |
} | |
return {}; | |
} | |
void TextAnnotation::setTextColor(const QColor &color) | |
{ | |
Q_D(TextAnnotation); | |
if (color == d->textColor) { | |
return; | |
} | |
d->textColor = color; | |
d->setDefaultAppearanceToNative(); | |
} | |
TextAnnotation::InplaceAlignPosition TextAnnotation::inplaceAlign() const | |
{ | |
Q_D(const TextAnnotation); | |
if (!d->pdfAnnot) { | |
return d->inplaceAlign; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeFreeText) { | |
const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot); | |
switch (ftextann->getQuadding()) { | |
case VariableTextQuadding::leftJustified: | |
return InplaceAlignLeft; | |
case VariableTextQuadding::centered: | |
return InplaceAlignCenter; | |
case VariableTextQuadding::rightJustified: | |
return InplaceAlignRight; | |
} | |
} | |
return InplaceAlignLeft; | |
} | |
static VariableTextQuadding alignToQuadding(TextAnnotation::InplaceAlignPosition align) | |
{ | |
switch (align) { | |
case TextAnnotation::InplaceAlignLeft: | |
return VariableTextQuadding::leftJustified; | |
case TextAnnotation::InplaceAlignCenter: | |
return VariableTextQuadding::centered; | |
case TextAnnotation::InplaceAlignRight: | |
return VariableTextQuadding::rightJustified; | |
} | |
return VariableTextQuadding::leftJustified; | |
} | |
void TextAnnotation::setInplaceAlign(InplaceAlignPosition align) | |
{ | |
Q_D(TextAnnotation); | |
if (!d->pdfAnnot) { | |
d->inplaceAlign = align; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeFreeText) { | |
AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot); | |
ftextann->setQuadding(alignToQuadding(align)); | |
} | |
} | |
QPointF TextAnnotation::calloutPoint(int id) const | |
{ | |
const QVector<QPointF> points = calloutPoints(); | |
if (id < 0 || id >= points.size()) { | |
return QPointF(); | |
} else { | |
return points[id]; | |
} | |
} | |
QVector<QPointF> TextAnnotation::calloutPoints() const | |
{ | |
Q_D(const TextAnnotation); | |
if (!d->pdfAnnot) { | |
return d->inplaceCallout; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeText) { | |
return QVector<QPointF>(); | |
} | |
const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot); | |
const AnnotCalloutLine *callout = ftextann->getCalloutLine(); | |
if (!callout) { | |
return QVector<QPointF>(); | |
} | |
double MTX[6]; | |
d->fillTransformationMTX(MTX); | |
const AnnotCalloutMultiLine *callout_v6 = dynamic_cast<const AnnotCalloutMultiLine *>(callout); | |
QVector<QPointF> res(callout_v6 ? 3 : 2); | |
XPDFReader::transform(MTX, callout->getX1(), callout->getY1(), res[0]); | |
XPDFReader::transform(MTX, callout->getX2(), callout->getY2(), res[1]); | |
if (callout_v6) { | |
XPDFReader::transform(MTX, callout_v6->getX3(), callout_v6->getY3(), res[2]); | |
} | |
return res; | |
} | |
void TextAnnotation::setCalloutPoints(const QVector<QPointF> &points) | |
{ | |
Q_D(TextAnnotation); | |
if (!d->pdfAnnot) { | |
d->inplaceCallout = points; | |
return; | |
} | |
if (d->pdfAnnot->getType() != Annot::typeFreeText) { | |
return; | |
} | |
AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot); | |
const int count = points.size(); | |
if (count == 0) { | |
ftextann->setCalloutLine(nullptr); | |
return; | |
} | |
if (count != 2 && count != 3) { | |
error(errSyntaxError, -1, "Expected zero, two or three points for callout"); | |
return; | |
} | |
AnnotCalloutLine *callout; | |
double x1, y1, x2, y2; | |
double MTX[6]; | |
d->fillTransformationMTX(MTX); | |
XPDFReader::invTransform(MTX, points[0], x1, y1); | |
XPDFReader::invTransform(MTX, points[1], x2, y2); | |
if (count == 3) { | |
double x3, y3; | |
XPDFReader::invTransform(MTX, points[2], x3, y3); | |
callout = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3); | |
} else { | |
callout = new AnnotCalloutLine(x1, y1, x2, y2); | |
} | |
ftextann->setCalloutLine(callout); | |
delete callout; | |
} | |
TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const | |
{ | |
Q_D(const TextAnnotation); | |
if (!d->pdfAnnot) { | |
return d->inplaceIntent; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeFreeText) { | |
const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot); | |
return (TextAnnotation::InplaceIntent)ftextann->getIntent(); | |
} | |
return TextAnnotation::Unknown; | |
} | |
void TextAnnotation::setInplaceIntent(TextAnnotation::InplaceIntent intent) | |
{ | |
Q_D(TextAnnotation); | |
if (!d->pdfAnnot) { | |
d->inplaceIntent = intent; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeFreeText) { | |
AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot); | |
ftextann->setIntent((AnnotFreeText::AnnotFreeTextIntent)intent); | |
} | |
} | |
/** LineAnnotation [Annotation] */ | |
class LineAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
LineAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields (note uses border for rendering style) | |
QVector<QPointF> linePoints; | |
LineAnnotation::TermStyle lineStartStyle; | |
LineAnnotation::TermStyle lineEndStyle; | |
bool lineClosed : 1; // (if true draw close shape) | |
bool lineShowCaption : 1; | |
LineAnnotation::LineType lineType; | |
QColor lineInnerColor; | |
double lineLeadingFwdPt; | |
double lineLeadingBackPt; | |
LineAnnotation::LineIntent lineIntent; | |
}; | |
LineAnnotationPrivate::LineAnnotationPrivate() | |
: AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) | |
{ | |
} | |
Annotation *LineAnnotationPrivate::makeAlias() | |
{ | |
return new LineAnnotation(*this); | |
} | |
Annot *LineAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
// Setters are defined in the public class | |
LineAnnotation *q = static_cast<LineAnnotation *>(makeAlias()); | |
// Set page and document | |
pdfPage = destPage; | |
parentDoc = doc; | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
if (lineType == LineAnnotation::StraightLine) { | |
pdfAnnot = new AnnotLine(doc->doc, &rect); | |
} else { | |
pdfAnnot = new AnnotPolygon(doc->doc, &rect, lineClosed ? Annot::typePolygon : Annot::typePolyLine); | |
} | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setLinePoints(linePoints); | |
q->setLineStartStyle(lineStartStyle); | |
q->setLineEndStyle(lineEndStyle); | |
q->setLineInnerColor(lineInnerColor); | |
q->setLineLeadingForwardPoint(lineLeadingFwdPt); | |
q->setLineLeadingBackPoint(lineLeadingBackPt); | |
q->setLineShowCaption(lineShowCaption); | |
q->setLineIntent(lineIntent); | |
delete q; | |
linePoints.clear(); // Free up memory | |
return pdfAnnot; | |
} | |
LineAnnotation::LineAnnotation(LineAnnotation::LineType type) : Annotation(*new LineAnnotationPrivate()) | |
{ | |
setLineType(type); | |
} | |
LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) : Annotation(dd) { } | |
LineAnnotation::~LineAnnotation() { } | |
Annotation::SubType LineAnnotation::subType() const | |
{ | |
return ALine; | |
} | |
LineAnnotation::LineType LineAnnotation::lineType() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineType; | |
} | |
return (d->pdfAnnot->getType() == Annot::typeLine) ? LineAnnotation::StraightLine : LineAnnotation::Polyline; | |
} | |
void LineAnnotation::setLineType(LineAnnotation::LineType type) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineType = type; | |
return; | |
} | |
// Type cannot be changed if annotation is already tied | |
qWarning() << "You can't change the type of a LineAnnotation that is already in a page"; | |
} | |
QVector<QPointF> LineAnnotation::linePoints() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->linePoints; | |
} | |
double MTX[6]; | |
d->fillTransformationMTX(MTX); | |
QVector<QPointF> res; | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
QPointF p; | |
XPDFReader::transform(MTX, lineann->getX1(), lineann->getY1(), p); | |
res.append(p); | |
XPDFReader::transform(MTX, lineann->getX2(), lineann->getY2(), p); | |
res.append(p); | |
} else { | |
const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot); | |
const AnnotPath *vertices = polyann->getVertices(); | |
for (int i = 0; i < vertices->getCoordsLength(); ++i) { | |
QPointF p; | |
XPDFReader::transform(MTX, vertices->getX(i), vertices->getY(i), p); | |
res.append(p); | |
} | |
} | |
return res; | |
} | |
void LineAnnotation::setLinePoints(const QVector<QPointF> &points) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->linePoints = points; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
if (points.size() != 2) { | |
error(errSyntaxError, -1, "Expected two points for a straight line"); | |
return; | |
} | |
double x1, y1, x2, y2; | |
double MTX[6]; | |
d->fillTransformationMTX(MTX); | |
XPDFReader::invTransform(MTX, points.first(), x1, y1); | |
XPDFReader::invTransform(MTX, points.last(), x2, y2); | |
lineann->setVertices(x1, y1, x2, y2); | |
} else { | |
AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot); | |
AnnotPath *p = d->toAnnotPath(points); | |
polyann->setVertices(p); | |
delete p; | |
} | |
} | |
LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineStartStyle; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
return (LineAnnotation::TermStyle)lineann->getStartStyle(); | |
} else { | |
const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot); | |
return (LineAnnotation::TermStyle)polyann->getStartStyle(); | |
} | |
} | |
void LineAnnotation::setLineStartStyle(LineAnnotation::TermStyle style) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineStartStyle = style; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle()); | |
} else { | |
AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot); | |
polyann->setStartEndStyle((AnnotLineEndingStyle)style, polyann->getEndStyle()); | |
} | |
} | |
LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineEndStyle; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
return (LineAnnotation::TermStyle)lineann->getEndStyle(); | |
} else { | |
const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot); | |
return (LineAnnotation::TermStyle)polyann->getEndStyle(); | |
} | |
} | |
void LineAnnotation::setLineEndStyle(LineAnnotation::TermStyle style) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineEndStyle = style; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style); | |
} else { | |
AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot); | |
polyann->setStartEndStyle(polyann->getStartStyle(), (AnnotLineEndingStyle)style); | |
} | |
} | |
bool LineAnnotation::isLineClosed() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineClosed; | |
} | |
return d->pdfAnnot->getType() == Annot::typePolygon; | |
} | |
void LineAnnotation::setLineClosed(bool closed) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineClosed = closed; | |
return; | |
} | |
if (d->pdfAnnot->getType() != Annot::typeLine) { | |
AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot); | |
// Set new subtype and switch intent if necessary | |
if (closed) { | |
polyann->setType(Annot::typePolygon); | |
if (polyann->getIntent() == AnnotPolygon::polylineDimension) { | |
polyann->setIntent(AnnotPolygon::polygonDimension); | |
} | |
} else { | |
polyann->setType(Annot::typePolyLine); | |
if (polyann->getIntent() == AnnotPolygon::polygonDimension) { | |
polyann->setIntent(AnnotPolygon::polylineDimension); | |
} | |
} | |
} | |
} | |
QColor LineAnnotation::lineInnerColor() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineInnerColor; | |
} | |
AnnotColor *c; | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
c = lineann->getInteriorColor(); | |
} else { | |
const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot); | |
c = polyann->getInteriorColor(); | |
} | |
return convertAnnotColor(c); | |
} | |
void LineAnnotation::setLineInnerColor(const QColor &color) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineInnerColor = color; | |
return; | |
} | |
auto c = convertQColor(color); | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setInteriorColor(std::move(c)); | |
} else { | |
AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot); | |
polyann->setInteriorColor(std::move(c)); | |
} | |
} | |
double LineAnnotation::lineLeadingForwardPoint() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineLeadingFwdPt; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
return lineann->getLeaderLineLength(); | |
} | |
return 0; | |
} | |
void LineAnnotation::setLineLeadingForwardPoint(double point) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineLeadingFwdPt = point; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setLeaderLineLength(point); | |
} | |
} | |
double LineAnnotation::lineLeadingBackPoint() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineLeadingBackPt; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
return lineann->getLeaderLineExtension(); | |
} | |
return 0; | |
} | |
void LineAnnotation::setLineLeadingBackPoint(double point) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineLeadingBackPt = point; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setLeaderLineExtension(point); | |
} | |
} | |
bool LineAnnotation::lineShowCaption() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineShowCaption; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
return lineann->getCaption(); | |
} | |
return false; | |
} | |
void LineAnnotation::setLineShowCaption(bool show) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineShowCaption = show; | |
return; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setCaption(show); | |
} | |
} | |
LineAnnotation::LineIntent LineAnnotation::lineIntent() const | |
{ | |
Q_D(const LineAnnotation); | |
if (!d->pdfAnnot) { | |
return d->lineIntent; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot); | |
return (LineAnnotation::LineIntent)(lineann->getIntent() + 1); | |
} else { | |
const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot); | |
if (polyann->getIntent() == AnnotPolygon::polygonCloud) { | |
return LineAnnotation::PolygonCloud; | |
} else { // AnnotPolygon::polylineDimension, AnnotPolygon::polygonDimension | |
return LineAnnotation::Dimension; | |
} | |
} | |
} | |
void LineAnnotation::setLineIntent(LineAnnotation::LineIntent intent) | |
{ | |
Q_D(LineAnnotation); | |
if (!d->pdfAnnot) { | |
d->lineIntent = intent; | |
return; | |
} | |
if (intent == LineAnnotation::Unknown) { | |
return; // Do not set (actually, it should clear the property) | |
} | |
if (d->pdfAnnot->getType() == Annot::typeLine) { | |
AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot); | |
lineann->setIntent((AnnotLine::AnnotLineIntent)(intent - 1)); | |
} else { | |
AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot); | |
if (intent == LineAnnotation::PolygonCloud) { | |
polyann->setIntent(AnnotPolygon::polygonCloud); | |
} else // LineAnnotation::Dimension | |
{ | |
if (d->pdfAnnot->getType() == Annot::typePolygon) { | |
polyann->setIntent(AnnotPolygon::polygonDimension); | |
} else { // Annot::typePolyLine | |
polyann->setIntent(AnnotPolygon::polylineDimension); | |
} | |
} | |
} | |
} | |
/** GeomAnnotation [Annotation] */ | |
class GeomAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
GeomAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields (note uses border for rendering style) | |
GeomAnnotation::GeomType geomType; | |
QColor geomInnerColor; | |
}; | |
GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { } | |
Annotation *GeomAnnotationPrivate::makeAlias() | |
{ | |
return new GeomAnnotation(*this); | |
} | |
Annot *GeomAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
// Setters are defined in the public class | |
GeomAnnotation *q = static_cast<GeomAnnotation *>(makeAlias()); | |
// Set page and document | |
pdfPage = destPage; | |
parentDoc = doc; | |
Annot::AnnotSubtype type; | |
if (geomType == GeomAnnotation::InscribedSquare) { | |
type = Annot::typeSquare; | |
} else { // GeomAnnotation::InscribedCircle | |
type = Annot::typeCircle; | |
} | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
pdfAnnot = new AnnotGeometry(destPage->getDoc(), &rect, type); | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setGeomInnerColor(geomInnerColor); | |
delete q; | |
return pdfAnnot; | |
} | |
GeomAnnotation::GeomAnnotation() : Annotation(*new GeomAnnotationPrivate()) { } | |
GeomAnnotation::GeomAnnotation(GeomAnnotationPrivate &dd) : Annotation(dd) { } | |
GeomAnnotation::~GeomAnnotation() { } | |
Annotation::SubType GeomAnnotation::subType() const | |
{ | |
return AGeom; | |
} | |
GeomAnnotation::GeomType GeomAnnotation::geomType() const | |
{ | |
Q_D(const GeomAnnotation); | |
if (!d->pdfAnnot) { | |
return d->geomType; | |
} | |
if (d->pdfAnnot->getType() == Annot::typeSquare) { | |
return GeomAnnotation::InscribedSquare; | |
} else { // Annot::typeCircle | |
return GeomAnnotation::InscribedCircle; | |
} | |
} | |
void GeomAnnotation::setGeomType(GeomAnnotation::GeomType type) | |
{ | |
Q_D(GeomAnnotation); | |
if (!d->pdfAnnot) { | |
d->geomType = type; | |
return; | |
} | |
AnnotGeometry *geomann = static_cast<AnnotGeometry *>(d->pdfAnnot); | |
if (type == GeomAnnotation::InscribedSquare) { | |
geomann->setType(Annot::typeSquare); | |
} else { // GeomAnnotation::InscribedCircle | |
geomann->setType(Annot::typeCircle); | |
} | |
} | |
QColor GeomAnnotation::geomInnerColor() const | |
{ | |
Q_D(const GeomAnnotation); | |
if (!d->pdfAnnot) { | |
return d->geomInnerColor; | |
} | |
const AnnotGeometry *geomann = static_cast<const AnnotGeometry *>(d->pdfAnnot); | |
return convertAnnotColor(geomann->getInteriorColor()); | |
} | |
void GeomAnnotation::setGeomInnerColor(const QColor &color) | |
{ | |
Q_D(GeomAnnotation); | |
if (!d->pdfAnnot) { | |
d->geomInnerColor = color; | |
return; | |
} | |
AnnotGeometry *geomann = static_cast<AnnotGeometry *>(d->pdfAnnot); | |
geomann->setInteriorColor(convertQColor(color)); | |
} | |
/** HighlightAnnotation [Annotation] */ | |
class HighlightAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
HighlightAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
HighlightAnnotation::HighlightType highlightType; | |
QList<HighlightAnnotation::Quad> highlightQuads; // not empty | |
// helpers | |
static Annot::AnnotSubtype toAnnotSubType(HighlightAnnotation::HighlightType type); | |
QList<HighlightAnnotation::Quad> fromQuadrilaterals(AnnotQuadrilaterals *quads) const; | |
AnnotQuadrilaterals *toQuadrilaterals(const QList<HighlightAnnotation::Quad> &quads) const; | |
}; | |
HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { } | |
Annotation *HighlightAnnotationPrivate::makeAlias() | |
{ | |
return new HighlightAnnotation(*this); | |
} | |
Annot::AnnotSubtype HighlightAnnotationPrivate::toAnnotSubType(HighlightAnnotation::HighlightType type) | |
{ | |
switch (type) { | |
default: // HighlightAnnotation::Highlight: | |
return Annot::typeHighlight; | |
case HighlightAnnotation::Underline: | |
return Annot::typeUnderline; | |
case HighlightAnnotation::Squiggly: | |
return Annot::typeSquiggly; | |
case HighlightAnnotation::StrikeOut: | |
return Annot::typeStrikeOut; | |
} | |
} | |
QList<HighlightAnnotation::Quad> HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const | |
{ | |
QList<HighlightAnnotation::Quad> quads; | |
if (!hlquads || !hlquads->getQuadrilateralsLength()) { | |
return quads; | |
} | |
const int quadsCount = hlquads->getQuadrilateralsLength(); | |
double MTX[6]; | |
fillTransformationMTX(MTX); | |
quads.reserve(quadsCount); | |
for (int q = 0; q < quadsCount; ++q) { | |
HighlightAnnotation::Quad quad; | |
XPDFReader::transform(MTX, hlquads->getX1(q), hlquads->getY1(q), quad.points[0]); | |
XPDFReader::transform(MTX, hlquads->getX2(q), hlquads->getY2(q), quad.points[1]); | |
XPDFReader::transform(MTX, hlquads->getX3(q), hlquads->getY3(q), quad.points[2]); | |
XPDFReader::transform(MTX, hlquads->getX4(q), hlquads->getY4(q), quad.points[3]); | |
// ### PDF1.6 specs says that point are in ccw order, but in fact | |
// points 3 and 4 are swapped in every PDF around! | |
QPointF tmpPoint = quad.points[2]; | |
quad.points[2] = quad.points[3]; | |
quad.points[3] = tmpPoint; | |
// initialize other properties and append quad | |
quad.capStart = true; // unlinked quads are always capped | |
quad.capEnd = true; // unlinked quads are always capped | |
quad.feather = 0.1; // default feather | |
quads.append(quad); | |
} | |
return quads; | |
} | |
AnnotQuadrilaterals *HighlightAnnotationPrivate::toQuadrilaterals(const QList<HighlightAnnotation::Quad> &quads) const | |
{ | |
const int count = quads.size(); | |
auto ac = std::make_unique<AnnotQuadrilaterals::AnnotQuadrilateral[]>(count); | |
double MTX[6]; | |
fillTransformationMTX(MTX); | |
int pos = 0; | |
foreach (const HighlightAnnotation::Quad &q, quads) { | |
double x1, y1, x2, y2, x3, y3, x4, y4; | |
XPDFReader::invTransform(MTX, q.points[0], x1, y1); | |
XPDFReader::invTransform(MTX, q.points[1], x2, y2); | |
// Swap points 3 and 4 (see HighlightAnnotationPrivate::fromQuadrilaterals) | |
XPDFReader::invTransform(MTX, q.points[3], x3, y3); | |
XPDFReader::invTransform(MTX, q.points[2], x4, y4); | |
ac[pos++] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4); | |
} | |
return new AnnotQuadrilaterals(std::move(ac), count); | |
} | |
Annot *HighlightAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
// Setters are defined in the public class | |
HighlightAnnotation *q = static_cast<HighlightAnnotation *>(makeAlias()); | |
// Set page and document | |
pdfPage = destPage; | |
parentDoc = doc; | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
pdfAnnot = new AnnotTextMarkup(destPage->getDoc(), &rect, toAnnotSubType(highlightType)); | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setHighlightQuads(highlightQuads); | |
highlightQuads.clear(); // Free up memory | |
delete q; | |
return pdfAnnot; | |
} | |
HighlightAnnotation::HighlightAnnotation() : Annotation(*new HighlightAnnotationPrivate()) { } | |
HighlightAnnotation::HighlightAnnotation(HighlightAnnotationPrivate &dd) : Annotation(dd) { } | |
HighlightAnnotation::~HighlightAnnotation() { } | |
Annotation::SubType HighlightAnnotation::subType() const | |
{ | |
return AHighlight; | |
} | |
HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const | |
{ | |
Q_D(const HighlightAnnotation); | |
if (!d->pdfAnnot) { | |
return d->highlightType; | |
} | |
Annot::AnnotSubtype subType = d->pdfAnnot->getType(); | |
if (subType == Annot::typeHighlight) { | |
return HighlightAnnotation::Highlight; | |
} else if (subType == Annot::typeUnderline) { | |
return HighlightAnnotation::Underline; | |
} else if (subType == Annot::typeSquiggly) { | |
return HighlightAnnotation::Squiggly; | |
} else { // Annot::typeStrikeOut | |
return HighlightAnnotation::StrikeOut; | |
} | |
} | |
void HighlightAnnotation::setHighlightType(HighlightAnnotation::HighlightType type) | |
{ | |
Q_D(HighlightAnnotation); | |
if (!d->pdfAnnot) { | |
d->highlightType = type; | |
return; | |
} | |
AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot); | |
hlann->setType(HighlightAnnotationPrivate::toAnnotSubType(type)); | |
} | |
QList<HighlightAnnotation::Quad> HighlightAnnotation::highlightQuads() const | |
{ | |
Q_D(const HighlightAnnotation); | |
if (!d->pdfAnnot) { | |
return d->highlightQuads; | |
} | |
const AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot); | |
return d->fromQuadrilaterals(hlann->getQuadrilaterals()); | |
} | |
void HighlightAnnotation::setHighlightQuads(const QList<HighlightAnnotation::Quad> &quads) | |
{ | |
Q_D(HighlightAnnotation); | |
if (!d->pdfAnnot) { | |
d->highlightQuads = quads; | |
return; | |
} | |
AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot); | |
AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads); | |
hlann->setQuadrilaterals(quadrilaterals); | |
delete quadrilaterals; | |
} | |
/** StampAnnotation [Annotation] */ | |
class StampAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
StampAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
AnnotStampImageHelper *convertQImageToAnnotStampImageHelper(const QImage &qimg); | |
// data fields | |
QString stampIconName; | |
QImage stampCustomImage; | |
}; | |
StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { } | |
Annotation *StampAnnotationPrivate::makeAlias() | |
{ | |
return new StampAnnotation(*this); | |
} | |
Annot *StampAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
StampAnnotation *q = static_cast<StampAnnotation *>(makeAlias()); | |
// Set page and document | |
pdfPage = destPage; | |
parentDoc = doc; | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
pdfAnnot = new AnnotStamp(destPage->getDoc(), &rect); | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setStampIconName(stampIconName); | |
q->setStampCustomImage(stampCustomImage); | |
delete q; | |
stampIconName.clear(); // Free up memory | |
return pdfAnnot; | |
} | |
AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHelper(const QImage &qimg) | |
{ | |
QImage convertedQImage = qimg; | |
QByteArray data; | |
QByteArray sMaskData; | |
const int width = convertedQImage.width(); | |
const int height = convertedQImage.height(); | |
int bitsPerComponent = 1; | |
ColorSpace colorSpace = ColorSpace::DeviceGray; | |
switch (convertedQImage.format()) { | |
case QImage::Format_MonoLSB: | |
if (!convertedQImage.allGray()) { | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
} else { | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_Mono); | |
} | |
break; | |
case QImage::Format_Mono: | |
if (!convertedQImage.allGray()) { | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
} | |
break; | |
case QImage::Format_RGB32: | |
case QImage::Format_ARGB32_Premultiplied: | |
case QImage::Format_ARGB8565_Premultiplied: | |
case QImage::Format_ARGB6666_Premultiplied: | |
case QImage::Format_ARGB8555_Premultiplied: | |
case QImage::Format_ARGB4444_Premultiplied: | |
case QImage::Format_Alpha8: | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32); | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
break; | |
case QImage::Format_RGBA8888: | |
case QImage::Format_RGBA8888_Premultiplied: | |
case QImage::Format_RGBX8888: | |
case QImage::Format_ARGB32: | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
break; | |
case QImage::Format_Grayscale8: | |
bitsPerComponent = 8; | |
break; | |
case QImage::Format_Grayscale16: | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_Grayscale8); | |
colorSpace = ColorSpace::DeviceGray; | |
bitsPerComponent = 8; | |
break; | |
case QImage::Format_RGB16: | |
case QImage::Format_RGB666: | |
case QImage::Format_RGB555: | |
case QImage::Format_RGB444: | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
break; | |
case QImage::Format_RGB888: | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
break; | |
default: | |
convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32); | |
colorSpace = ColorSpace::DeviceRGB; | |
bitsPerComponent = 8; | |
break; | |
} | |
getRawDataFromQImage(convertedQImage, convertedQImage.depth(), &data, &sMaskData); | |
AnnotStampImageHelper *annotImg; | |
if (sMaskData.size() > 0) { | |
AnnotStampImageHelper sMask(parentDoc->doc, width, height, ColorSpace::DeviceGray, 8, sMaskData.data(), sMaskData.size()); | |
annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.size(), sMask.getRef()); | |
} else { | |
annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.size()); | |
} | |
return annotImg; | |
} | |
StampAnnotation::StampAnnotation() : Annotation(*new StampAnnotationPrivate()) { } | |
StampAnnotation::StampAnnotation(StampAnnotationPrivate &dd) : Annotation(dd) { } | |
StampAnnotation::~StampAnnotation() { } | |
Annotation::SubType StampAnnotation::subType() const | |
{ | |
return AStamp; | |
} | |
QString StampAnnotation::stampIconName() const | |
{ | |
Q_D(const StampAnnotation); | |
if (!d->pdfAnnot) { | |
return d->stampIconName; | |
} | |
const AnnotStamp *stampann = static_cast<const AnnotStamp *>(d->pdfAnnot); | |
return QString::fromLatin1(stampann->getIcon()->c_str()); | |
} | |
void StampAnnotation::setStampIconName(const QString &name) | |
{ | |
Q_D(StampAnnotation); | |
if (!d->pdfAnnot) { | |
d->stampIconName = name; | |
return; | |
} | |
AnnotStamp *stampann = static_cast<AnnotStamp *>(d->pdfAnnot); | |
QByteArray encoded = name.toLatin1(); | |
GooString s(encoded.constData()); | |
stampann->setIcon(&s); | |
} | |
void StampAnnotation::setStampCustomImage(const QImage &image) | |
{ | |
if (image.isNull()) { | |
return; | |
} | |
Q_D(StampAnnotation); | |
if (!d->pdfAnnot) { | |
d->stampCustomImage = QImage(image); | |
return; | |
} | |
AnnotStamp *stampann = static_cast<AnnotStamp *>(d->pdfAnnot); | |
AnnotStampImageHelper *annotCustomImage = d->convertQImageToAnnotStampImageHelper(image); | |
stampann->setCustomImage(annotCustomImage); | |
} | |
/** InkAnnotation [Annotation] */ | |
class InkAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
InkAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
QList<QVector<QPointF>> inkPaths; | |
// helper | |
AnnotPath **toAnnotPaths(const QList<QVector<QPointF>> &paths); | |
}; | |
InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { } | |
Annotation *InkAnnotationPrivate::makeAlias() | |
{ | |
return new InkAnnotation(*this); | |
} | |
// Note: Caller is required to delete array elements and the array itself after use | |
AnnotPath **InkAnnotationPrivate::toAnnotPaths(const QList<QVector<QPointF>> &paths) | |
{ | |
const int pathsNumber = paths.size(); | |
AnnotPath **res = new AnnotPath *[pathsNumber]; | |
for (int i = 0; i < pathsNumber; ++i) { | |
res[i] = toAnnotPath(paths[i]); | |
} | |
return res; | |
} | |
Annot *InkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
// Setters are defined in the public class | |
InkAnnotation *q = static_cast<InkAnnotation *>(makeAlias()); | |
// Set page and document | |
pdfPage = destPage; | |
parentDoc = doc; | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
pdfAnnot = new AnnotInk(destPage->getDoc(), &rect); | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setInkPaths(inkPaths); | |
inkPaths.clear(); // Free up memory | |
delete q; | |
return pdfAnnot; | |
} | |
InkAnnotation::InkAnnotation() : Annotation(*new InkAnnotationPrivate()) { } | |
InkAnnotation::InkAnnotation(InkAnnotationPrivate &dd) : Annotation(dd) { } | |
InkAnnotation::~InkAnnotation() { } | |
Annotation::SubType InkAnnotation::subType() const | |
{ | |
return AInk; | |
} | |
QList<QVector<QPointF>> InkAnnotation::inkPaths() const | |
{ | |
Q_D(const InkAnnotation); | |
if (!d->pdfAnnot) { | |
return d->inkPaths; | |
} | |
const AnnotInk *inkann = static_cast<const AnnotInk *>(d->pdfAnnot); | |
const AnnotPath *const *paths = inkann->getInkList(); | |
if (!paths || !inkann->getInkListLength()) { | |
return {}; | |
} | |
double MTX[6]; | |
d->fillTransformationMTX(MTX); | |
const int pathsNumber = inkann->getInkListLength(); | |
QList<QVector<QPointF>> inkPaths; | |
inkPaths.reserve(pathsNumber); | |
for (int m = 0; m < pathsNumber; ++m) { | |
// transform each path in a list of normalized points .. | |
QVector<QPointF> localList; | |
const AnnotPath *path = paths[m]; | |
const int pointsNumber = path ? path->getCoordsLength() : 0; | |
for (int n = 0; n < pointsNumber; ++n) { | |
QPointF point; | |
XPDFReader::transform(MTX, path->getX(n), path->getY(n), point); | |
localList.append(point); | |
} | |
// ..and add it to the annotation | |
inkPaths.append(localList); | |
} | |
return inkPaths; | |
} | |
void InkAnnotation::setInkPaths(const QList<QVector<QPointF>> &paths) | |
{ | |
Q_D(InkAnnotation); | |
if (!d->pdfAnnot) { | |
d->inkPaths = paths; | |
return; | |
} | |
AnnotInk *inkann = static_cast<AnnotInk *>(d->pdfAnnot); | |
AnnotPath **annotpaths = d->toAnnotPaths(paths); | |
const int pathsNumber = paths.size(); | |
inkann->setInkList(annotpaths, pathsNumber); | |
for (int i = 0; i < pathsNumber; ++i) { | |
delete annotpaths[i]; | |
} | |
delete[] annotpaths; | |
} | |
/** LinkAnnotation [Annotation] */ | |
class LinkAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
LinkAnnotationPrivate(); | |
~LinkAnnotationPrivate() override; | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
std::unique_ptr<Link> linkDestination; | |
LinkAnnotation::HighlightMode linkHLMode; | |
QPointF linkRegion[4]; | |
}; | |
LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkHLMode(LinkAnnotation::Invert) { } | |
LinkAnnotationPrivate::~LinkAnnotationPrivate() { } | |
Annotation *LinkAnnotationPrivate::makeAlias() | |
{ | |
return new LinkAnnotation(*this); | |
} | |
Annot *LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
return nullptr; // Not implemented | |
} | |
LinkAnnotation::LinkAnnotation() : Annotation(*new LinkAnnotationPrivate()) { } | |
LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd) : Annotation(dd) { } | |
LinkAnnotation::~LinkAnnotation() { } | |
Annotation::SubType LinkAnnotation::subType() const | |
{ | |
return ALink; | |
} | |
Link *LinkAnnotation::linkDestination() const | |
{ | |
Q_D(const LinkAnnotation); | |
return d->linkDestination.get(); | |
} | |
void LinkAnnotation::setLinkDestination(std::unique_ptr<Link> &&link) | |
{ | |
Q_D(LinkAnnotation); | |
d->linkDestination = std::move(link); | |
} | |
LinkAnnotation::HighlightMode LinkAnnotation::linkHighlightMode() const | |
{ | |
Q_D(const LinkAnnotation); | |
return d->linkHLMode; | |
} | |
void LinkAnnotation::setLinkHighlightMode(LinkAnnotation::HighlightMode mode) | |
{ | |
Q_D(LinkAnnotation); | |
d->linkHLMode = mode; | |
} | |
QPointF LinkAnnotation::linkRegionPoint(int id) const | |
{ | |
if (id < 0 || id >= 4) { | |
return QPointF(); | |
} | |
Q_D(const LinkAnnotation); | |
return d->linkRegion[id]; | |
} | |
void LinkAnnotation::setLinkRegionPoint(int id, const QPointF point) | |
{ | |
if (id < 0 || id >= 4) { | |
return; | |
} | |
Q_D(LinkAnnotation); | |
d->linkRegion[id] = point; | |
} | |
/** CaretAnnotation [Annotation] */ | |
class CaretAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
CaretAnnotationPrivate(); | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
CaretAnnotation::CaretSymbol symbol; | |
}; | |
CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { } | |
Annotation *CaretAnnotationPrivate::makeAlias() | |
{ | |
return new CaretAnnotation(*this); | |
} | |
Annot *CaretAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
// Setters are defined in the public class | |
CaretAnnotation *q = static_cast<CaretAnnotation *>(makeAlias()); | |
// Set page and document | |
pdfPage = destPage; | |
parentDoc = doc; | |
// Set pdfAnnot | |
PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); | |
pdfAnnot = new AnnotCaret(destPage->getDoc(), &rect); | |
// Set properties | |
flushBaseAnnotationProperties(); | |
q->setCaretSymbol(symbol); | |
delete q; | |
return pdfAnnot; | |
} | |
CaretAnnotation::CaretAnnotation() : Annotation(*new CaretAnnotationPrivate()) { } | |
CaretAnnotation::CaretAnnotation(CaretAnnotationPrivate &dd) : Annotation(dd) { } | |
CaretAnnotation::~CaretAnnotation() { } | |
Annotation::SubType CaretAnnotation::subType() const | |
{ | |
return ACaret; | |
} | |
CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const | |
{ | |
Q_D(const CaretAnnotation); | |
if (!d->pdfAnnot) { | |
return d->symbol; | |
} | |
const AnnotCaret *caretann = static_cast<const AnnotCaret *>(d->pdfAnnot); | |
return (CaretAnnotation::CaretSymbol)caretann->getSymbol(); | |
} | |
void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol) | |
{ | |
Q_D(CaretAnnotation); | |
if (!d->pdfAnnot) { | |
d->symbol = symbol; | |
return; | |
} | |
AnnotCaret *caretann = static_cast<AnnotCaret *>(d->pdfAnnot); | |
caretann->setSymbol((AnnotCaret::AnnotCaretSymbol)symbol); | |
} | |
/** FileAttachmentAnnotation [Annotation] */ | |
class FileAttachmentAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
FileAttachmentAnnotationPrivate(); | |
~FileAttachmentAnnotationPrivate() override; | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
QString icon; | |
EmbeddedFile *embfile; | |
}; | |
FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { } | |
FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate() | |
{ | |
delete embfile; | |
} | |
Annotation *FileAttachmentAnnotationPrivate::makeAlias() | |
{ | |
return new FileAttachmentAnnotation(*this); | |
} | |
Annot *FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
return nullptr; // Not implemented | |
} | |
FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation(*new FileAttachmentAnnotationPrivate()) { } | |
FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd) : Annotation(dd) { } | |
FileAttachmentAnnotation::~FileAttachmentAnnotation() { } | |
Annotation::SubType FileAttachmentAnnotation::subType() const | |
{ | |
return AFileAttachment; | |
} | |
QString FileAttachmentAnnotation::fileIconName() const | |
{ | |
Q_D(const FileAttachmentAnnotation); | |
return d->icon; | |
} | |
void FileAttachmentAnnotation::setFileIconName(const QString &icon) | |
{ | |
Q_D(FileAttachmentAnnotation); | |
d->icon = icon; | |
} | |
EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const | |
{ | |
Q_D(const FileAttachmentAnnotation); | |
return d->embfile; | |
} | |
void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef) | |
{ | |
Q_D(FileAttachmentAnnotation); | |
d->embfile = ef; | |
} | |
/** SoundAnnotation [Annotation] */ | |
class SoundAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
SoundAnnotationPrivate(); | |
~SoundAnnotationPrivate() override; | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
QString icon; | |
SoundObject *sound; | |
}; | |
SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { } | |
SoundAnnotationPrivate::~SoundAnnotationPrivate() | |
{ | |
delete sound; | |
} | |
Annotation *SoundAnnotationPrivate::makeAlias() | |
{ | |
return new SoundAnnotation(*this); | |
} | |
Annot *SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
return nullptr; // Not implemented | |
} | |
SoundAnnotation::SoundAnnotation() : Annotation(*new SoundAnnotationPrivate()) { } | |
SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd) : Annotation(dd) { } | |
SoundAnnotation::~SoundAnnotation() { } | |
Annotation::SubType SoundAnnotation::subType() const | |
{ | |
return ASound; | |
} | |
QString SoundAnnotation::soundIconName() const | |
{ | |
Q_D(const SoundAnnotation); | |
return d->icon; | |
} | |
void SoundAnnotation::setSoundIconName(const QString &icon) | |
{ | |
Q_D(SoundAnnotation); | |
d->icon = icon; | |
} | |
SoundObject *SoundAnnotation::sound() const | |
{ | |
Q_D(const SoundAnnotation); | |
return d->sound; | |
} | |
void SoundAnnotation::setSound(SoundObject *s) | |
{ | |
Q_D(SoundAnnotation); | |
d->sound = s; | |
} | |
/** MovieAnnotation [Annotation] */ | |
class MovieAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
MovieAnnotationPrivate(); | |
~MovieAnnotationPrivate() override; | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
MovieObject *movie; | |
QString title; | |
}; | |
MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { } | |
MovieAnnotationPrivate::~MovieAnnotationPrivate() | |
{ | |
delete movie; | |
} | |
Annotation *MovieAnnotationPrivate::makeAlias() | |
{ | |
return new MovieAnnotation(*this); | |
} | |
Annot *MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
return nullptr; // Not implemented | |
} | |
MovieAnnotation::MovieAnnotation() : Annotation(*new MovieAnnotationPrivate()) { } | |
MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd) : Annotation(dd) { } | |
MovieAnnotation::~MovieAnnotation() { } | |
Annotation::SubType MovieAnnotation::subType() const | |
{ | |
return AMovie; | |
} | |
MovieObject *MovieAnnotation::movie() const | |
{ | |
Q_D(const MovieAnnotation); | |
return d->movie; | |
} | |
void MovieAnnotation::setMovie(MovieObject *movie) | |
{ | |
Q_D(MovieAnnotation); | |
d->movie = movie; | |
} | |
QString MovieAnnotation::movieTitle() const | |
{ | |
Q_D(const MovieAnnotation); | |
return d->title; | |
} | |
void MovieAnnotation::setMovieTitle(const QString &title) | |
{ | |
Q_D(MovieAnnotation); | |
d->title = title; | |
} | |
/** ScreenAnnotation [Annotation] */ | |
class ScreenAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
ScreenAnnotationPrivate(); | |
~ScreenAnnotationPrivate() override; | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
// data fields | |
LinkRendition *action; | |
QString title; | |
}; | |
ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { } | |
ScreenAnnotationPrivate::~ScreenAnnotationPrivate() | |
{ | |
delete action; | |
} | |
ScreenAnnotation::ScreenAnnotation(ScreenAnnotationPrivate &dd) : Annotation(dd) { } | |
Annotation *ScreenAnnotationPrivate::makeAlias() | |
{ | |
return new ScreenAnnotation(*this); | |
} | |
Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
return nullptr; // Not implemented | |
} | |
ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { } | |
ScreenAnnotation::~ScreenAnnotation() { } | |
Annotation::SubType ScreenAnnotation::subType() const | |
{ | |
return AScreen; | |
} | |
LinkRendition *ScreenAnnotation::action() const | |
{ | |
Q_D(const ScreenAnnotation); | |
return d->action; | |
} | |
void ScreenAnnotation::setAction(LinkRendition *action) | |
{ | |
Q_D(ScreenAnnotation); | |
d->action = action; | |
} | |
QString ScreenAnnotation::screenTitle() const | |
{ | |
Q_D(const ScreenAnnotation); | |
return d->title; | |
} | |
void ScreenAnnotation::setScreenTitle(const QString &title) | |
{ | |
Q_D(ScreenAnnotation); | |
d->title = title; | |
} | |
std::unique_ptr<Link> ScreenAnnotation::additionalAction(AdditionalActionType type) const | |
{ | |
Q_D(const ScreenAnnotation); | |
return d->additionalAction(type); | |
} | |
/** WidgetAnnotation [Annotation] */ | |
class WidgetAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
Annotation *makeAlias() override; | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; | |
}; | |
Annotation *WidgetAnnotationPrivate::makeAlias() | |
{ | |
return new WidgetAnnotation(*this); | |
} | |
Annot *WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) | |
{ | |
return nullptr; // Not implemented | |
} | |
WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) { } | |
WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { } | |
WidgetAnnotation::~WidgetAnnotation() { } | |
Annotation::SubType WidgetAnnotation::subType() const | |
{ | |
return AWidget; | |
} | |
std::unique_ptr<Link> WidgetAnnotation::additionalAction(AdditionalActionType type) const | |
{ | |
Q_D(const WidgetAnnotation); | |
return d->additionalAction(type); | |
} | |
/** RichMediaAnnotation [Annotation] */ | |
class RichMediaAnnotation::Params::Private | |
{ | |
public: | |
Private() { } | |
QString flashVars; | |
}; | |
RichMediaAnnotation::Params::Params() : d(new Private) { } | |
RichMediaAnnotation::Params::~Params() { } | |
void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars) | |
{ | |
d->flashVars = flashVars; | |
} | |
QString RichMediaAnnotation::Params::flashVars() const | |
{ | |
return d->flashVars; | |
} | |
class RichMediaAnnotation::Instance::Private | |
{ | |
public: | |
Private() : params(nullptr) { } | |
~Private() { delete params; } | |
Private(const Private &) = delete; | |
Private &operator=(const Private &) = delete; | |
RichMediaAnnotation::Instance::Type type; | |
RichMediaAnnotation::Params *params; | |
}; | |
RichMediaAnnotation::Instance::Instance() : d(new Private) { } | |
RichMediaAnnotation::Instance::~Instance() { } | |
void RichMediaAnnotation::Instance::setType(Type type) | |
{ | |
d->type = type; | |
} | |
RichMediaAnnotation::Instance::Type RichMediaAnnotation::Instance::type() const | |
{ | |
return d->type; | |
} | |
void RichMediaAnnotation::Instance::setParams(RichMediaAnnotation::Params *params) | |
{ | |
delete d->params; | |
d->params = params; | |
} | |
RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const | |
{ | |
return d->params; | |
} | |
class RichMediaAnnotation::Configuration::Private | |
{ | |
public: | |
Private() { } | |
~Private() | |
{ | |
qDeleteAll(instances); | |
instances.clear(); | |
} | |
Private(const Private &) = delete; | |
Private &operator=(const Private &) = delete; | |
RichMediaAnnotation::Configuration::Type type; | |
QString name; | |
QList<RichMediaAnnotation::Instance *> instances; | |
}; | |
RichMediaAnnotation::Configuration::Configuration() : d(new Private) { } | |
RichMediaAnnotation::Configuration::~Configuration() { } | |
void RichMediaAnnotation::Configuration::setType(Type type) | |
{ | |
d->type = type; | |
} | |
RichMediaAnnotation::Configuration::Type RichMediaAnnotation::Configuration::type() const | |
{ | |
return d->type; | |
} | |
void RichMediaAnnotation::Configuration::setName(const QString &name) | |
{ | |
d->name = name; | |
} | |
QString RichMediaAnnotation::Configuration::name() const | |
{ | |
return d->name; | |
} | |
void RichMediaAnnotation::Configuration::setInstances(const QList<RichMediaAnnotation::Instance *> &instances) | |
{ | |
qDeleteAll(d->instances); | |
d->instances.clear(); | |
d->instances = instances; | |
} | |
QList<RichMediaAnnotation::Instance *> RichMediaAnnotation::Configuration::instances() const | |
{ | |
return d->instances; | |
} | |
class RichMediaAnnotation::Asset::Private | |
{ | |
public: | |
Private() : embeddedFile(nullptr) { } | |
~Private() { delete embeddedFile; } | |
Private(const Private &) = delete; | |
Private &operator=(const Private &) = delete; | |
QString name; | |
EmbeddedFile *embeddedFile; | |
}; | |
RichMediaAnnotation::Asset::Asset() : d(new Private) { } | |
RichMediaAnnotation::Asset::~Asset() { } | |
void RichMediaAnnotation::Asset::setName(const QString &name) | |
{ | |
d->name = name; | |
} | |
QString RichMediaAnnotation::Asset::name() const | |
{ | |
return d->name; | |
} | |
void RichMediaAnnotation::Asset::setEmbeddedFile(EmbeddedFile *embeddedFile) | |
{ | |
delete d->embeddedFile; | |
d->embeddedFile = embeddedFile; | |
} | |
EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const | |
{ | |
return d->embeddedFile; | |
} | |
class RichMediaAnnotation::Content::Private | |
{ | |
public: | |
Private() { } | |
~Private() | |
{ | |
qDeleteAll(configurations); | |
configurations.clear(); | |
qDeleteAll(assets); | |
assets.clear(); | |
} | |
Private(const Private &) = delete; | |
Private &operator=(const Private &) = delete; | |
QList<RichMediaAnnotation::Configuration *> configurations; | |
QList<RichMediaAnnotation::Asset *> assets; | |
}; | |
RichMediaAnnotation::Content::Content() : d(new Private) { } | |
RichMediaAnnotation::Content::~Content() { } | |
void RichMediaAnnotation::Content::setConfigurations(const QList<RichMediaAnnotation::Configuration *> &configurations) | |
{ | |
qDeleteAll(d->configurations); | |
d->configurations.clear(); | |
d->configurations = configurations; | |
} | |
QList<RichMediaAnnotation::Configuration *> RichMediaAnnotation::Content::configurations() const | |
{ | |
return d->configurations; | |
} | |
void RichMediaAnnotation::Content::setAssets(const QList<RichMediaAnnotation::Asset *> &assets) | |
{ | |
qDeleteAll(d->assets); | |
d->assets.clear(); | |
d->assets = assets; | |
} | |
QList<RichMediaAnnotation::Asset *> RichMediaAnnotation::Content::assets() const | |
{ | |
return d->assets; | |
} | |
class RichMediaAnnotation::Activation::Private | |
{ | |
public: | |
Private() : condition(RichMediaAnnotation::Activation::UserAction) { } | |
RichMediaAnnotation::Activation::Condition condition; | |
}; | |
RichMediaAnnotation::Activation::Activation() : d(new Private) { } | |
RichMediaAnnotation::Activation::~Activation() { } | |
void RichMediaAnnotation::Activation::setCondition(Condition condition) | |
{ | |
d->condition = condition; | |
} | |
RichMediaAnnotation::Activation::Condition RichMediaAnnotation::Activation::condition() const | |
{ | |
return d->condition; | |
} | |
class RichMediaAnnotation::Deactivation::Private : public QSharedData | |
{ | |
public: | |
Private() : condition(RichMediaAnnotation::Deactivation::UserAction) { } | |
RichMediaAnnotation::Deactivation::Condition condition; | |
}; | |
RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { } | |
RichMediaAnnotation::Deactivation::~Deactivation() { } | |
void RichMediaAnnotation::Deactivation::setCondition(Condition condition) | |
{ | |
d->condition = condition; | |
} | |
RichMediaAnnotation::Deactivation::Condition RichMediaAnnotation::Deactivation::condition() const | |
{ | |
return d->condition; | |
} | |
class RichMediaAnnotation::Settings::Private : public QSharedData | |
{ | |
public: | |
Private() : activation(nullptr), deactivation(nullptr) { } | |
RichMediaAnnotation::Activation *activation; | |
RichMediaAnnotation::Deactivation *deactivation; | |
}; | |
RichMediaAnnotation::Settings::Settings() : d(new Private) { } | |
RichMediaAnnotation::Settings::~Settings() { } | |
void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation) | |
{ | |
delete d->activation; | |
d->activation = activation; | |
} | |
RichMediaAnnotation::Activation *RichMediaAnnotation::Settings::activation() const | |
{ | |
return d->activation; | |
} | |
void RichMediaAnnotation::Settings::setDeactivation(RichMediaAnnotation::Deactivation *deactivation) | |
{ | |
delete d->deactivation; | |
d->deactivation = deactivation; | |
} | |
RichMediaAnnotation::Deactivation *RichMediaAnnotation::Settings::deactivation() const | |
{ | |
return d->deactivation; | |
} | |
class RichMediaAnnotationPrivate : public AnnotationPrivate | |
{ | |
public: | |
RichMediaAnnotationPrivate() : settings(nullptr), content(nullptr) { } | |
~RichMediaAnnotationPrivate() override; | |
Annotation *makeAlias() override { return new RichMediaAnnotation(*this); } | |
Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override | |
{ | |
Q_UNUSED(destPage); | |
Q_UNUSED(doc); | |
return nullptr; | |
} | |
RichMediaAnnotation::Settings *settings; | |
RichMediaAnnotation::Content *content; | |
}; | |
RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate() | |
{ | |
delete settings; | |
delete content; | |
} | |
RichMediaAnnotation::RichMediaAnnotation() : Annotation(*new RichMediaAnnotationPrivate()) { } | |
RichMediaAnnotation::RichMediaAnnotation(RichMediaAnnotationPrivate &dd) : Annotation(dd) { } | |
RichMediaAnnotation::~RichMediaAnnotation() { } | |
Annotation::SubType RichMediaAnnotation::subType() const | |
{ | |
return ARichMedia; | |
} | |
void RichMediaAnnotation::setSettings(RichMediaAnnotation::Settings *settings) | |
{ | |
Q_D(RichMediaAnnotation); | |
delete d->settings; | |
d->settings = settings; | |
} | |
RichMediaAnnotation::Settings *RichMediaAnnotation::settings() const | |
{ | |
Q_D(const RichMediaAnnotation); | |
return d->settings; | |
} | |
void RichMediaAnnotation::setContent(RichMediaAnnotation::Content *content) | |
{ | |
Q_D(RichMediaAnnotation); | |
delete d->content; | |
d->content = content; | |
} | |
RichMediaAnnotation::Content *RichMediaAnnotation::content() const | |
{ | |
Q_D(const RichMediaAnnotation); | |
return d->content; | |
} | |
// BEGIN utility annotation functions | |
QColor convertAnnotColor(const AnnotColor *color) | |
{ | |
if (!color) { | |
return QColor(); | |
} | |
QColor newcolor; | |
const double *color_data = color->getValues(); | |
switch (color->getSpace()) { | |
case AnnotColor::colorTransparent: // = 0, | |
newcolor = Qt::transparent; | |
break; | |
case AnnotColor::colorGray: // = 1, | |
newcolor.setRgbF(color_data[0], color_data[0], color_data[0]); | |
break; | |
case AnnotColor::colorRGB: // = 3, | |
newcolor.setRgbF(color_data[0], color_data[1], color_data[2]); | |
break; | |
case AnnotColor::colorCMYK: // = 4 | |
newcolor.setCmykF(color_data[0], color_data[1], color_data[2], color_data[3]); | |
break; | |
} | |
return newcolor; | |
} | |
std::unique_ptr<AnnotColor> convertQColor(const QColor &c) | |
{ | |
if (c.alpha() == 0) { | |
return {}; // Transparent | |
} | |
switch (c.spec()) { | |
case QColor::Rgb: | |
case QColor::Hsl: | |
case QColor::Hsv: | |
return std::make_unique<AnnotColor>(c.redF(), c.greenF(), c.blueF()); | |
case QColor::Cmyk: | |
return std::make_unique<AnnotColor>(c.cyanF(), c.magentaF(), c.yellowF(), c.blackF()); | |
case QColor::Invalid: | |
default: | |
return {}; | |
} | |
} | |
// END utility annotation functions | |
} | |