mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Redact annotation, some fixes
This commit is contained in:
@ -219,6 +219,71 @@ QPainter::CompositionMode PDFAnnotation::getCompositionMode() const
|
|||||||
return PDFBlendModeInfo::getCompositionModeFromBlendMode(BlendMode::Normal);
|
return PDFBlendModeInfo::getCompositionModeFromBlendMode(BlendMode::Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPainterPath PDFAnnotation::parsePath(const PDFObjectStorage* storage, const PDFDictionary* dictionary, bool closePath)
|
||||||
|
{
|
||||||
|
QPainterPath path;
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(storage);
|
||||||
|
PDFObject pathObject = storage->getObject(dictionary->get("Path"));
|
||||||
|
if (pathObject.isArray())
|
||||||
|
{
|
||||||
|
for (const PDFObject& pathItemObject : *pathObject.getArray())
|
||||||
|
{
|
||||||
|
std::vector<PDFReal> pathItem = loader.readNumberArray(pathItemObject);
|
||||||
|
switch (pathItem.size())
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
QPointF point(pathItem[0], pathItem[1]);
|
||||||
|
if (path.isEmpty())
|
||||||
|
{
|
||||||
|
path.moveTo(point);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path.lineTo(point);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
if (path.isEmpty())
|
||||||
|
{
|
||||||
|
// First path item must be 'Move to' command
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.quadTo(pathItem[0], pathItem[1], pathItem[2], pathItem[3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
{
|
||||||
|
if (path.isEmpty())
|
||||||
|
{
|
||||||
|
// First path item must be 'Move to' command
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.cubicTo(pathItem[0], pathItem[1], pathItem[2], pathItem[3], pathItem[4], pathItem[5]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closePath)
|
||||||
|
{
|
||||||
|
path.closeSubpath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference)
|
PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference)
|
||||||
{
|
{
|
||||||
PDFObject object = storage->getObjectByReference(reference);
|
PDFObject object = storage->getObjectByReference(reference);
|
||||||
@ -384,66 +449,7 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
|
|||||||
|
|
||||||
annotation->m_intent = loader.readEnumByName(dictionary->get("IT"), intents.begin(), intents.end(), PDFPolygonalGeometryAnnotation::Intent::None);
|
annotation->m_intent = loader.readEnumByName(dictionary->get("IT"), intents.begin(), intents.end(), PDFPolygonalGeometryAnnotation::Intent::None);
|
||||||
annotation->m_measure = storage->getObject(dictionary->get("Measure"));
|
annotation->m_measure = storage->getObject(dictionary->get("Measure"));
|
||||||
|
annotation->m_path = parsePath(storage, dictionary, subtype == "Polygon");
|
||||||
PDFObject pathObject = storage->getObject(dictionary->get("Path"));
|
|
||||||
if (pathObject.isArray())
|
|
||||||
{
|
|
||||||
QPainterPath path;
|
|
||||||
for (const PDFObject& pathItemObject : *pathObject.getArray())
|
|
||||||
{
|
|
||||||
std::vector<PDFReal> pathItem = loader.readNumberArray(pathItemObject);
|
|
||||||
switch (pathItem.size())
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
QPointF point(pathItem[0], pathItem[1]);
|
|
||||||
if (path.isEmpty())
|
|
||||||
{
|
|
||||||
path.moveTo(point);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
path.lineTo(point);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
{
|
|
||||||
if (path.isEmpty())
|
|
||||||
{
|
|
||||||
// First path item must be 'Move to' command
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
path.quadTo(pathItem[0], pathItem[1], pathItem[2], pathItem[3]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
{
|
|
||||||
if (path.isEmpty())
|
|
||||||
{
|
|
||||||
// First path item must be 'Move to' command
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
path.cubicTo(pathItem[0], pathItem[1], pathItem[2], pathItem[3], pathItem[4], pathItem[5]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subtype == "Polygon")
|
|
||||||
{
|
|
||||||
path.closeSubpath();
|
|
||||||
}
|
|
||||||
|
|
||||||
annotation->m_path = qMove(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (subtype == "Highlight" ||
|
else if (subtype == "Highlight" ||
|
||||||
subtype == "Underline" ||
|
subtype == "Underline" ||
|
||||||
@ -523,6 +529,9 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
|
|||||||
PDFInkAnnotation* annotation = new PDFInkAnnotation();
|
PDFInkAnnotation* annotation = new PDFInkAnnotation();
|
||||||
result.reset(annotation);
|
result.reset(annotation);
|
||||||
|
|
||||||
|
annotation->m_inkPath = parsePath(storage, dictionary, false);
|
||||||
|
if (annotation->m_inkPath.isEmpty())
|
||||||
|
{
|
||||||
PDFObject inkList = storage->getObject(dictionary->get("InkList"));
|
PDFObject inkList = storage->getObject(dictionary->get("InkList"));
|
||||||
if (inkList.isArray())
|
if (inkList.isArray())
|
||||||
{
|
{
|
||||||
@ -548,6 +557,7 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (subtype == "Popup")
|
else if (subtype == "Popup")
|
||||||
{
|
{
|
||||||
PDFPopupAnnotation* annotation = new PDFPopupAnnotation();
|
PDFPopupAnnotation* annotation = new PDFPopupAnnotation();
|
||||||
@ -571,6 +581,19 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
|
|||||||
|
|
||||||
annotation->m_icon = loader.readEnumByName(dictionary->get("Name"), icons.begin(), icons.end(), FileAttachmentIcon::PushPin);
|
annotation->m_icon = loader.readEnumByName(dictionary->get("Name"), icons.begin(), icons.end(), FileAttachmentIcon::PushPin);
|
||||||
}
|
}
|
||||||
|
else if (subtype == "Redact")
|
||||||
|
{
|
||||||
|
PDFRedactAnnotation* annotation = new PDFRedactAnnotation();
|
||||||
|
result.reset(annotation);
|
||||||
|
|
||||||
|
annotation->m_redactionRegion = parseQuadrilaterals(storage, dictionary->get("QuadPoints"), annotationsRectangle);
|
||||||
|
annotation->m_interiorColor = loader.readNumberArrayFromDictionary(dictionary, "IC");
|
||||||
|
annotation->m_overlayForm = dictionary->get("RO");
|
||||||
|
annotation->m_overlayText = loader.readTextStringFromDictionary(dictionary, "OverlayText", QString());
|
||||||
|
annotation->m_repeat = loader.readBooleanFromDictionary(dictionary, "Repeat", false);
|
||||||
|
annotation->m_defaultAppearance = loader.readStringFromDictionary(dictionary, "DA");
|
||||||
|
annotation->m_justification = loader.readIntegerFromDictionary(dictionary, "Q", 0);
|
||||||
|
}
|
||||||
else if (subtype == "Sound")
|
else if (subtype == "Sound")
|
||||||
{
|
{
|
||||||
PDFSoundAnnotation* annotation = new PDFSoundAnnotation();
|
PDFSoundAnnotation* annotation = new PDFSoundAnnotation();
|
||||||
@ -654,6 +677,11 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
|
|||||||
annotation->m_relativeVerticalOffset = loader.readNumberFromDictionary(fixedPrintDictionary, "V", 0.0);
|
annotation->m_relativeVerticalOffset = loader.readNumberFromDictionary(fixedPrintDictionary, "V", 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (subtype == "Projection")
|
||||||
|
{
|
||||||
|
PDFProjectionAnnotation* annotation = new PDFProjectionAnnotation();
|
||||||
|
result.reset(annotation);
|
||||||
|
}
|
||||||
else if (subtype == "3D")
|
else if (subtype == "3D")
|
||||||
{
|
{
|
||||||
PDF3DAnnotation* annotation = new PDF3DAnnotation();
|
PDF3DAnnotation* annotation = new PDF3DAnnotation();
|
||||||
@ -2761,6 +2789,7 @@ void PDFInkAnnotation::draw(AnnotationDrawParameters& parameters) const
|
|||||||
QPainterPath boundingPath;
|
QPainterPath boundingPath;
|
||||||
QPainterPath currentPath;
|
QPainterPath currentPath;
|
||||||
const int elementCount = path.elementCount();
|
const int elementCount = path.elementCount();
|
||||||
|
bool hasSpline = false;
|
||||||
for (int i = 0; i < elementCount; ++i)
|
for (int i = 0; i < elementCount; ++i)
|
||||||
{
|
{
|
||||||
QPainterPath::Element element = path.elementAt(i);
|
QPainterPath::Element element = path.elementAt(i);
|
||||||
@ -2805,10 +2834,21 @@ void PDFInkAnnotation::draw(AnnotationDrawParameters& parameters) const
|
|||||||
|
|
||||||
case QPainterPath::CurveToElement:
|
case QPainterPath::CurveToElement:
|
||||||
case QPainterPath::CurveToDataElement:
|
case QPainterPath::CurveToDataElement:
|
||||||
|
hasSpline = true;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Jakub Melka: If we have a spline, then we don't do anything...
|
||||||
|
// Just copy the spline path.
|
||||||
|
if (hasSpline)
|
||||||
|
{
|
||||||
|
currentPath = path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the path
|
// Reset the path
|
||||||
@ -3292,4 +3332,29 @@ std::vector<pdf::PDFAppeareanceStreams::Key> PDFWidgetAnnotation::getDrawKeys(co
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFRedactAnnotation::draw(AnnotationDrawParameters& parameters) const
|
||||||
|
{
|
||||||
|
if (m_redactionRegion.isEmpty())
|
||||||
|
{
|
||||||
|
// Jakub Melka: do not draw empty redact area
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainter& painter = *parameters.painter;
|
||||||
|
painter.setCompositionMode(getCompositionMode());
|
||||||
|
parameters.boundingRectangle = m_redactionRegion.getPath().boundingRect();
|
||||||
|
|
||||||
|
painter.setPen(getPen());
|
||||||
|
painter.setBrush(getBrush());
|
||||||
|
painter.drawPath(m_redactionRegion.getPath());
|
||||||
|
|
||||||
|
const qreal penWidth = painter.pen().widthF();
|
||||||
|
parameters.boundingRectangle.adjust(-penWidth, -penWidth, penWidth, penWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PDFRedactAnnotation::getFillColor() const
|
||||||
|
{
|
||||||
|
return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -79,6 +79,8 @@ enum class AnnotationType
|
|||||||
PrinterMark,
|
PrinterMark,
|
||||||
TrapNet,
|
TrapNet,
|
||||||
Watermark,
|
Watermark,
|
||||||
|
Redact,
|
||||||
|
Projection,
|
||||||
_3D,
|
_3D,
|
||||||
RichMedia
|
RichMedia
|
||||||
};
|
};
|
||||||
@ -647,6 +649,12 @@ protected:
|
|||||||
/// \param parameters Draw parameters
|
/// \param parameters Draw parameters
|
||||||
void drawCharacterSymbol(QString text, PDFReal opacity, AnnotationDrawParameters& parameters) const;
|
void drawCharacterSymbol(QString text, PDFReal opacity, AnnotationDrawParameters& parameters) const;
|
||||||
|
|
||||||
|
/// Parses path. If path is incorrect, empty path is returned.
|
||||||
|
/// \param storage Storage
|
||||||
|
/// \param dictionary Annotation's dictionary
|
||||||
|
/// \param closePath Close path when finishing?
|
||||||
|
static QPainterPath parsePath(const PDFObjectStorage* storage, const PDFDictionary* dictionary, bool closePath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PDFObjectReference m_selfReference; ///< Reference to self
|
PDFObjectReference m_selfReference; ///< Reference to self
|
||||||
QRectF m_rectangle; ///< Annotation rectangle, in page coordinates, "Rect" entry
|
QRectF m_rectangle; ///< Annotation rectangle, in page coordinates, "Rect" entry
|
||||||
@ -1274,6 +1282,50 @@ private:
|
|||||||
PDFReal m_relativeVerticalOffset = 0.0;
|
PDFReal m_relativeVerticalOffset = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Redaction annotation represents content selection, which should
|
||||||
|
/// be removed from the document.
|
||||||
|
class PDFRedactAnnotation : public PDFMarkupAnnotation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDFRedactAnnotation() = default;
|
||||||
|
|
||||||
|
virtual AnnotationType getType() const override { return AnnotationType::Redact; }
|
||||||
|
virtual void draw(AnnotationDrawParameters& parameters) const override;
|
||||||
|
|
||||||
|
const PDFAnnotationQuadrilaterals& getRedactionRegion() const { return m_redactionRegion; }
|
||||||
|
const std::vector<PDFReal>& getInteriorColor() const { return m_interiorColor; }
|
||||||
|
const PDFObject& getOverlay() const { return m_overlayForm; }
|
||||||
|
const QString& getOverlayText() const { return m_overlayText; }
|
||||||
|
bool isRepeat() const { return m_repeat; }
|
||||||
|
const QByteArray& getDefaultAppearance() const { return m_defaultAppearance; }
|
||||||
|
PDFInteger getJustification() const { return m_justification; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QColor getFillColor() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
|
||||||
|
|
||||||
|
PDFAnnotationQuadrilaterals m_redactionRegion;
|
||||||
|
std::vector<PDFReal> m_interiorColor;
|
||||||
|
PDFObject m_overlayForm; ///< Overlay form object
|
||||||
|
QString m_overlayText;
|
||||||
|
bool m_repeat = false;
|
||||||
|
QByteArray m_defaultAppearance;
|
||||||
|
PDFInteger m_justification = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFProjectionAnnotation : public PDFMarkupAnnotation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDFProjectionAnnotation() = default;
|
||||||
|
|
||||||
|
virtual AnnotationType getType() const override { return AnnotationType::Projection; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
|
||||||
|
};
|
||||||
|
|
||||||
/// 3D annotations represents 3D scene, which can be viewed in the application.
|
/// 3D annotations represents 3D scene, which can be viewed in the application.
|
||||||
class PDF3DAnnotation : public PDFAnnotation
|
class PDF3DAnnotation : public PDFAnnotation
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user