Annotations refinement according to PDF 2.0 specification

This commit is contained in:
Jakub Melka 2020-08-30 17:15:50 +02:00
parent 210356f426
commit ae311a31ae
2 changed files with 330 additions and 199 deletions

View File

@ -209,6 +209,16 @@ std::vector<PDFAppeareanceStreams::Key> PDFAnnotation::getDrawKeys(const PDFForm
return { PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Normal, QByteArray() } }; return { PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Normal, QByteArray() } };
} }
QPainter::CompositionMode PDFAnnotation::getCompositionMode() const
{
if (PDFBlendModeInfo::isSupportedByQt(getBlendMode()))
{
return PDFBlendModeInfo::getCompositionModeFromBlendMode(getBlendMode());
}
return PDFBlendModeInfo::getCompositionModeFromBlendMode(BlendMode::Normal);
}
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);
@ -374,6 +384,66 @@ 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"));
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" ||
@ -655,12 +725,21 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
result->m_color = loader.readNumberArrayFromDictionary(dictionary, "C"); result->m_color = loader.readNumberArrayFromDictionary(dictionary, "C");
result->m_structParent = loader.readIntegerFromDictionary(dictionary, "StructParent", 0); result->m_structParent = loader.readIntegerFromDictionary(dictionary, "StructParent", 0);
result->m_optionalContentReference = loader.readReferenceFromDictionary(dictionary, "OC"); result->m_optionalContentReference = loader.readReferenceFromDictionary(dictionary, "OC");
result->m_strokingOpacity = loader.readNumberFromDictionary(dictionary, "CA", 1.0);
result->m_fillingOpacity = loader.readNumberFromDictionary(dictionary, "ca", result->m_strokingOpacity);
result->m_blendMode = PDFBlendModeInfo::getBlendMode(loader.readNameFromDictionary(dictionary, "BM"));
if (result->m_blendMode == BlendMode::Invalid)
{
result->m_blendMode = BlendMode::Normal;
}
result->m_language = loader.readTextStringFromDictionary(dictionary, "Lang", QString());
if (PDFMarkupAnnotation* markupAnnotation = result->asMarkupAnnotation()) if (PDFMarkupAnnotation* markupAnnotation = result->asMarkupAnnotation())
{ {
markupAnnotation->m_windowTitle = loader.readTextStringFromDictionary(dictionary, "T", QString()); markupAnnotation->m_windowTitle = loader.readTextStringFromDictionary(dictionary, "T", QString());
markupAnnotation->m_popupAnnotation = loader.readReferenceFromDictionary(dictionary, "Popup"); markupAnnotation->m_popupAnnotation = loader.readReferenceFromDictionary(dictionary, "Popup");
markupAnnotation->m_opacity = loader.readNumberFromDictionary(dictionary, "CA", 1.0);
markupAnnotation->m_richTextString = loader.readTextStringFromDictionary(dictionary, "RC", QString()); markupAnnotation->m_richTextString = loader.readTextStringFromDictionary(dictionary, "RC", QString());
markupAnnotation->m_creationDate = PDFEncoding::convertToDateTime(loader.readStringFromDictionary(dictionary, "CreationDate")); markupAnnotation->m_creationDate = PDFEncoding::convertToDateTime(loader.readStringFromDictionary(dictionary, "CreationDate"));
markupAnnotation->m_inReplyTo = loader.readReferenceFromDictionary(dictionary, "IRT"); markupAnnotation->m_inReplyTo = loader.readReferenceFromDictionary(dictionary, "IRT");
@ -737,7 +816,7 @@ AnnotationLineEnding PDFAnnotation::convertNameToLineEnding(const QByteArray& na
QColor PDFAnnotation::getStrokeColor() const QColor PDFAnnotation::getStrokeColor() const
{ {
return getDrawColorFromAnnotationColor(getColor()); return getDrawColorFromAnnotationColor(getColor(), getStrokeOpacity());
} }
QColor PDFAnnotation::getFillColor() const QColor PDFAnnotation::getFillColor() const
@ -745,14 +824,14 @@ QColor PDFAnnotation::getFillColor() const
return QColor(); return QColor();
} }
QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector<PDFReal>& color) QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector<PDFReal>& color, PDFReal opacity)
{ {
switch (color.size()) switch (color.size())
{ {
case 1: case 1:
{ {
const PDFReal gray = color.back(); const PDFReal gray = color.back();
return QColor::fromRgbF(gray, gray, gray, 1.0); return QColor::fromRgbF(gray, gray, gray, opacity);
} }
case 3: case 3:
@ -760,7 +839,7 @@ QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector<PDFReal>
const PDFReal r = color[0]; const PDFReal r = color[0];
const PDFReal g = color[1]; const PDFReal g = color[1];
const PDFReal b = color[2]; const PDFReal b = color[2];
return QColor::fromRgbF(r, g, b, 1.0); return QColor::fromRgbF(r, g, b, opacity);
} }
case 4: case 4:
@ -769,14 +848,16 @@ QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector<PDFReal>
const PDFReal m = color[1]; const PDFReal m = color[1];
const PDFReal y = color[2]; const PDFReal y = color[2];
const PDFReal k = color[3]; const PDFReal k = color[3];
return QColor::fromCmykF(c, m, y, k, 1.0); return QColor::fromCmykF(c, m, y, k, opacity);
} }
default: default:
break; break;
} }
return QColor(Qt::black); QColor black(Qt::black);
black.setAlphaF(opacity);
return black;
} }
QPen PDFAnnotation::getPen() const QPen PDFAnnotation::getPen() const
@ -1683,7 +1764,7 @@ void PDFWidgetAnnotationManager::createWidgetsForMarkupAnnotations(QWidget* pare
scrollArea->setWidget(frameWidget); scrollArea->setWidget(frameWidget);
const PDFMarkupAnnotation* markupMainAnnotation = pageAnnotation.annotation->asMarkupAnnotation(); const PDFMarkupAnnotation* markupMainAnnotation = pageAnnotation.annotation->asMarkupAnnotation();
QColor color = markupMainAnnotation->getDrawColorFromAnnotationColor(markupMainAnnotation->getColor()); QColor color = markupMainAnnotation->getDrawColorFromAnnotationColor(markupMainAnnotation->getColor(), 1.0);
QColor titleColor = QColor::fromHslF(color.hueF(), color.saturationF(), 0.2, 1.0); QColor titleColor = QColor::fromHslF(color.hueF(), color.saturationF(), 0.2, 1.0);
QColor backgroundColor = QColor::fromHslF(color.hueF(), color.saturationF(), 0.9, 1.0); QColor backgroundColor = QColor::fromHslF(color.hueF(), color.saturationF(), 0.9, 1.0);
@ -1749,6 +1830,7 @@ void PDFSimpleGeometryAnnotation::draw(AnnotationDrawParameters& parameters) con
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setPen(getPen()); painter.setPen(getPen());
painter.setBrush(getBrush()); painter.setBrush(getBrush());
painter.setCompositionMode(getCompositionMode());
switch (getType()) switch (getType())
{ {
@ -1791,12 +1873,7 @@ void PDFSimpleGeometryAnnotation::draw(AnnotationDrawParameters& parameters) con
QColor PDFSimpleGeometryAnnotation::getFillColor() const QColor PDFSimpleGeometryAnnotation::getFillColor() const
{ {
QColor color = getDrawColorFromAnnotationColor(getInteriorColor()); return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity());
if (color.isValid())
{
color.setAlphaF(getOpacity());
}
return color;
} }
bool PDFMarkupAnnotation::isReplyTo() const bool PDFMarkupAnnotation::isReplyTo() const
@ -1804,26 +1881,6 @@ bool PDFMarkupAnnotation::isReplyTo() const
return m_inReplyTo.isValid() && m_replyType == ReplyType::Reply; return m_inReplyTo.isValid() && m_replyType == ReplyType::Reply;
} }
QColor PDFMarkupAnnotation::getStrokeColor() const
{
QColor color = PDFAnnotation::getStrokeColor();
if (color.isValid())
{
color.setAlphaF(m_opacity);
}
return color;
}
QColor PDFMarkupAnnotation::getFillColor() const
{
QColor color = PDFAnnotation::getFillColor();
if (color.isValid())
{
color.setAlphaF(m_opacity);
}
return color;
}
std::vector<PDFAppeareanceStreams::Key> PDFTextAnnotation::getDrawKeys(const PDFFormManager* formManager) const std::vector<PDFAppeareanceStreams::Key> PDFTextAnnotation::getDrawKeys(const PDFFormManager* formManager) const
{ {
Q_UNUSED(formManager); Q_UNUSED(formManager);
@ -1835,15 +1892,15 @@ std::vector<PDFAppeareanceStreams::Key> PDFTextAnnotation::getDrawKeys(const PDF
void PDFTextAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFTextAnnotation::draw(AnnotationDrawParameters& parameters) const
{ {
const PDFReal opacity = getOpacity(); QColor strokeColor = QColor::fromRgbF(0.0, 0.0, 0.0, getStrokeOpacity());
QColor strokeColor = QColor::fromRgbF(0.0, 0.0, 0.0, opacity); QColor fillColor = (parameters.key.first == PDFAppeareanceStreams::Appearance::Normal) ? QColor::fromRgbF(1.0, 1.0, 0.0, getFillOpacity()) :
QColor fillColor = (parameters.key.first == PDFAppeareanceStreams::Appearance::Normal) ? QColor::fromRgbF(1.0, 1.0, 0.0, opacity) : QColor::fromRgbF(1.0, 0.0, 0.0, getFillOpacity());
QColor::fromRgbF(1.0, 0.0, 0.0, opacity);
constexpr const PDFReal rectSize = 32.0; constexpr const PDFReal rectSize = 32.0;
constexpr const PDFReal penWidth = 2.0; constexpr const PDFReal penWidth = 2.0;
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
QRectF rectangle = getRectangle(); QRectF rectangle = getRectangle();
rectangle.setSize(QSizeF(rectSize, rectSize)); rectangle.setSize(QSizeF(rectSize, rectSize));
@ -1918,6 +1975,7 @@ void PDFLineAnnotation::draw(AnnotationDrawParameters& parameters) const
} }
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
painter.setPen(getPen()); painter.setPen(getPen());
painter.setBrush(getBrush()); painter.setBrush(getBrush());
@ -1974,12 +2032,7 @@ void PDFLineAnnotation::draw(AnnotationDrawParameters& parameters) const
QColor PDFLineAnnotation::getFillColor() const QColor PDFLineAnnotation::getFillColor() const
{ {
QColor color = getDrawColorFromAnnotationColor(getInteriorColor()); return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity());
if (color.isValid())
{
color.setAlphaF(getOpacity());
}
return color;
} }
void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) const
@ -1991,6 +2044,7 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters)
} }
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
painter.setPen(getPen()); painter.setPen(getPen());
painter.setBrush(getBrush()); painter.setBrush(getBrush());
@ -1999,20 +2053,30 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters)
{ {
case AnnotationType::Polygon: case AnnotationType::Polygon:
{ {
QPolygonF polygon; if (m_path.isEmpty())
polygon.reserve(int(m_vertices.size() + 1));
for (const QPointF& point : m_vertices)
{ {
polygon << point; QPolygonF polygon;
polygon.reserve(int(m_vertices.size() + 1));
for (const QPointF& point : m_vertices)
{
polygon << point;
}
if (!polygon.isClosed())
{
polygon << m_vertices.front();
}
painter.drawPolygon(polygon, Qt::OddEvenFill);
parameters.boundingRectangle = polygon.boundingRect();
parameters.boundingRectangle.adjust(-penWidth, -penWidth, penWidth, penWidth);
} }
if (!polygon.isClosed()) else
{ {
polygon << m_vertices.front(); painter.drawPath(m_path);
parameters.boundingRectangle = m_path.boundingRect();
parameters.boundingRectangle.adjust(-penWidth, -penWidth, penWidth, penWidth);
} }
painter.drawPolygon(polygon, Qt::OddEvenFill);
parameters.boundingRectangle = polygon.boundingRect();
parameters.boundingRectangle.adjust(-penWidth, -penWidth, penWidth, penWidth);
break; break;
} }
@ -2021,28 +2085,46 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters)
const PDFReal lineEndingSize = painter.pen().widthF() * 5.0; const PDFReal lineEndingSize = painter.pen().widthF() * 5.0;
QPainterPath boundingPath; QPainterPath boundingPath;
const size_t pointCount = m_vertices.size(); if (m_path.isEmpty())
const size_t lastPoint = pointCount - 1;
for (size_t i = 1; i < pointCount; ++i)
{ {
if (i == 1) const size_t pointCount = m_vertices.size();
const size_t lastPoint = pointCount - 1;
for (size_t i = 1; i < pointCount; ++i)
{ {
// We draw first line if (i == 1)
drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, getStartLineEnding(), AnnotationLineEnding::None, boundingPath, QPointF(), QString(), true); {
} // We draw first line
else if (i == lastPoint) drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, getStartLineEnding(), AnnotationLineEnding::None, boundingPath, QPointF(), QString(), true);
{ }
// We draw last line else if (i == lastPoint)
drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, AnnotationLineEnding::None, getEndLineEnding(), boundingPath, QPointF(), QString(), true); {
} // We draw last line
else drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, AnnotationLineEnding::None, getEndLineEnding(), boundingPath, QPointF(), QString(), true);
{ }
QLineF line(m_vertices[i - 1], m_vertices[i]); else
boundingPath.moveTo(line.p1()); {
boundingPath.lineTo(line.p2()); QLineF line(m_vertices[i - 1], m_vertices[i]);
painter.drawLine(line); boundingPath.moveTo(line.p1());
boundingPath.lineTo(line.p2());
painter.drawLine(line);
}
} }
} }
else
{
const PDFReal angle = 30;
const PDFReal lineEndingHalfSize = lineEndingSize * 0.5;
const PDFReal arrowAxisLength = lineEndingHalfSize / qTan(qDegreesToRadians(angle));
boundingPath = m_path;
painter.drawPath(m_path);
QMatrix LCStoGCS_start = LineGeometryInfo::create(QLineF(m_path.pointAtPercent(0.00), m_path.pointAtPercent(0.01))).LCStoGCS;
QMatrix LCStoGCS_end = LineGeometryInfo::create(QLineF(m_path.pointAtPercent(0.99), m_path.pointAtPercent(1.00))).LCStoGCS;
drawLineEnding(&painter, m_path.pointAtPercent(0), lineEndingSize, arrowAxisLength, getStartLineEnding(), false, LCStoGCS_start, boundingPath);
drawLineEnding(&painter, m_path.pointAtPercent(1), lineEndingSize, arrowAxisLength, getEndLineEnding(), true, LCStoGCS_end, boundingPath);
}
parameters.boundingRectangle = boundingPath.boundingRect(); parameters.boundingRectangle = boundingPath.boundingRect();
parameters.boundingRectangle.adjust(-lineEndingSize, -lineEndingSize, lineEndingSize, lineEndingSize); parameters.boundingRectangle.adjust(-lineEndingSize, -lineEndingSize, lineEndingSize, lineEndingSize);
@ -2057,12 +2139,7 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters)
QColor PDFPolygonalGeometryAnnotation::getFillColor() const QColor PDFPolygonalGeometryAnnotation::getFillColor() const
{ {
QColor color = getDrawColorFromAnnotationColor(getInteriorColor()); return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity());
if (color.isValid())
{
color.setAlphaF(getOpacity());
}
return color;
} }
PDFAnnotation::LineGeometryInfo PDFAnnotation::LineGeometryInfo::create(QLineF line) PDFAnnotation::LineGeometryInfo PDFAnnotation::LineGeometryInfo::create(QLineF line)
@ -2088,6 +2165,120 @@ PDFAnnotation::LineGeometryInfo PDFAnnotation::LineGeometryInfo::create(QLineF l
return result; return result;
} }
void PDFAnnotation::drawLineEnding(QPainter* painter,
QPointF point,
PDFReal lineEndingSize,
PDFReal arrowAxisLength,
AnnotationLineEnding ending,
bool flipAxis,
const QMatrix& LCStoGCS,
QPainterPath& boundingPath) const
{
QPainterPath path;
const PDFReal lineEndingHalfSize = lineEndingSize * 0.5;
switch (ending)
{
case AnnotationLineEnding::None:
break;
case AnnotationLineEnding::Square:
{
path.addRect(-lineEndingHalfSize, -lineEndingHalfSize, lineEndingSize, lineEndingSize);
break;
}
case AnnotationLineEnding::Circle:
{
path.addEllipse(QPointF(0, 0), lineEndingHalfSize, lineEndingHalfSize);
break;
}
case AnnotationLineEnding::Diamond:
{
path.moveTo(0.0, -lineEndingHalfSize);
path.lineTo(lineEndingHalfSize, 0.0);
path.lineTo(0.0, +lineEndingHalfSize);
path.lineTo(-lineEndingHalfSize, 0.0);
path.closeSubpath();
break;
}
case AnnotationLineEnding::OpenArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(arrowAxisLength, lineEndingHalfSize);
path.moveTo(0.0, 0.0);
path.lineTo(arrowAxisLength, -lineEndingHalfSize);
break;
}
case AnnotationLineEnding::ClosedArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(arrowAxisLength, lineEndingHalfSize);
path.lineTo(arrowAxisLength, -lineEndingHalfSize);
path.closeSubpath();
break;
}
case AnnotationLineEnding::Butt:
{
path.moveTo(0.0, -lineEndingHalfSize);
path.lineTo(0.0, lineEndingHalfSize);
break;
}
case AnnotationLineEnding::ROpenArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(-arrowAxisLength, lineEndingHalfSize);
path.moveTo(0.0, 0.0);
path.lineTo(-arrowAxisLength, -lineEndingHalfSize);
break;
}
case AnnotationLineEnding::RClosedArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(-arrowAxisLength, lineEndingHalfSize);
path.lineTo(-arrowAxisLength, -lineEndingHalfSize);
path.closeSubpath();
break;
}
case AnnotationLineEnding::Slash:
{
const PDFReal angle = 60;
const PDFReal lineEndingHalfSizeForSlash = lineEndingSize * 0.5;
const PDFReal slashAxisLength = lineEndingHalfSizeForSlash / qTan(qDegreesToRadians(angle));
path.moveTo(-slashAxisLength, -lineEndingHalfSizeForSlash);
path.lineTo(slashAxisLength, lineEndingHalfSizeForSlash);
break;
}
default:
break;
}
if (!path.isEmpty())
{
// Flip the x-axis (we are drawing endpoint)
if (flipAxis && ending != AnnotationLineEnding::Slash)
{
QMatrix matrix;
matrix.scale(-1.0, 1.0);
path = matrix.map(path);
}
path.translate(point);
path = LCStoGCS.map(path);
painter->drawPath(path);
boundingPath.addPath(path);
}
}
void PDFAnnotation::drawLine(const PDFAnnotation::LineGeometryInfo& info, void PDFAnnotation::drawLine(const PDFAnnotation::LineGeometryInfo& info,
QPainter& painter, QPainter& painter,
PDFReal lineEndingSize, PDFReal lineEndingSize,
@ -2120,112 +2311,6 @@ void PDFAnnotation::drawLine(const PDFAnnotation::LineGeometryInfo& info,
return 0.0; return 0.0;
}; };
auto drawLineEnding = [&](QPointF point, AnnotationLineEnding ending, bool flipAxis)
{
QPainterPath path;
switch (ending)
{
case AnnotationLineEnding::None:
break;
case AnnotationLineEnding::Square:
{
path.addRect(-lineEndingHalfSize, -lineEndingHalfSize, lineEndingSize, lineEndingSize);
break;
}
case AnnotationLineEnding::Circle:
{
path.addEllipse(QPointF(0, 0), lineEndingHalfSize, lineEndingHalfSize);
break;
}
case AnnotationLineEnding::Diamond:
{
path.moveTo(0.0, -lineEndingHalfSize);
path.lineTo(lineEndingHalfSize, 0.0);
path.lineTo(0.0, +lineEndingHalfSize);
path.lineTo(-lineEndingHalfSize, 0.0);
path.closeSubpath();
break;
}
case AnnotationLineEnding::OpenArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(arrowAxisLength, lineEndingHalfSize);
path.moveTo(0.0, 0.0);
path.lineTo(arrowAxisLength, -lineEndingHalfSize);
break;
}
case AnnotationLineEnding::ClosedArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(arrowAxisLength, lineEndingHalfSize);
path.lineTo(arrowAxisLength, -lineEndingHalfSize);
path.closeSubpath();
break;
}
case AnnotationLineEnding::Butt:
{
path.moveTo(0.0, -lineEndingHalfSize);
path.lineTo(0.0, lineEndingHalfSize);
break;
}
case AnnotationLineEnding::ROpenArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(-arrowAxisLength, lineEndingHalfSize);
path.moveTo(0.0, 0.0);
path.lineTo(-arrowAxisLength, -lineEndingHalfSize);
break;
}
case AnnotationLineEnding::RClosedArrow:
{
path.moveTo(0.0, 0.0);
path.lineTo(-arrowAxisLength, lineEndingHalfSize);
path.lineTo(-arrowAxisLength, -lineEndingHalfSize);
path.closeSubpath();
break;
}
case AnnotationLineEnding::Slash:
{
const PDFReal angle = 60;
const PDFReal lineEndingHalfSize = lineEndingSize * 0.5;
const PDFReal slashAxisLength = lineEndingHalfSize / qTan(qDegreesToRadians(angle));
path.moveTo(-slashAxisLength, -lineEndingHalfSize);
path.lineTo(slashAxisLength, lineEndingHalfSize);
break;
}
default:
break;
}
if (!path.isEmpty())
{
// Flip the x-axis (we are drawing endpoint)
if (flipAxis && ending != AnnotationLineEnding::Slash)
{
QMatrix matrix;
matrix.scale(-1.0, 1.0);
path = matrix.map(path);
}
path.translate(point);
path = info.LCStoGCS.map(path);
painter.drawPath(path);
boundingPath.addPath(path);
}
};
// Remove the offset from start/end // Remove the offset from start/end
const PDFReal startOffset = getOffsetFromLineEnding(p1Ending); const PDFReal startOffset = getOffsetFromLineEnding(p1Ending);
const PDFReal endOffset = getOffsetFromLineEnding(p2Ending); const PDFReal endOffset = getOffsetFromLineEnding(p2Ending);
@ -2250,8 +2335,8 @@ void PDFAnnotation::drawLine(const PDFAnnotation::LineGeometryInfo& info,
textPath = QMatrix(1, 0, 0, -1, 0, 0).map(textPath); textPath = QMatrix(1, 0, 0, -1, 0, 0).map(textPath);
} }
drawLineEnding(info.transformedLine.p1(), p1Ending, false); drawLineEnding(&painter, info.transformedLine.p1(), lineEndingSize, arrowAxisLength, p1Ending, false, info.LCStoGCS, boundingPath);
drawLineEnding(info.transformedLine.p2(), p2Ending, true); drawLineEnding(&painter, info.transformedLine.p2(), lineEndingSize, arrowAxisLength, p2Ending, true, info.LCStoGCS, boundingPath);
if (drawText && !textIsAboveLine) if (drawText && !textIsAboveLine)
{ {
@ -2320,6 +2405,7 @@ void PDFHighlightAnnotation::draw(AnnotationDrawParameters& parameters) const
} }
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
parameters.boundingRectangle = m_highlightArea.getPath().boundingRect(); parameters.boundingRectangle = m_highlightArea.getPath().boundingRect();
painter.setPen(getPen()); painter.setPen(getPen());
@ -2450,6 +2536,7 @@ void PDFLinkAnnotation::draw(AnnotationDrawParameters& parameters) const
case LinkHighlightMode::Push: case LinkHighlightMode::Push:
{ {
// Draw border // Draw border
painter.setCompositionMode(getCompositionMode());
painter.setPen(getPen()); painter.setPen(getPen());
painter.setBrush(Qt::NoBrush); painter.setBrush(Qt::NoBrush);
painter.drawPath(m_activationRegion.getPath()); painter.drawPath(m_activationRegion.getPath());
@ -2506,15 +2593,15 @@ PDFAnnotationDefaultAppearance PDFAnnotationDefaultAppearance::parse(const QByte
} }
else if (command == "g" && i >= 1) else if (command == "g" && i >= 1)
{ {
result.m_fontColor = PDFAnnotation::getDrawColorFromAnnotationColor({ readNumber(i - 1) }); result.m_fontColor = PDFAnnotation::getDrawColorFromAnnotationColor({ readNumber(i - 1) }, 1.0);
} }
else if (command == "rg" && i >= 3) else if (command == "rg" && i >= 3)
{ {
result.m_fontColor = PDFAnnotation::getDrawColorFromAnnotationColor({ readNumber(i - 3), readNumber(i - 2), readNumber(i - 1) }); result.m_fontColor = PDFAnnotation::getDrawColorFromAnnotationColor({ readNumber(i - 3), readNumber(i - 2), readNumber(i - 1) }, 1.0);
} }
else if (command == "k" && i >= 4) else if (command == "k" && i >= 4)
{ {
result.m_fontColor = PDFAnnotation::getDrawColorFromAnnotationColor({ readNumber(i - 4), readNumber(i - 3), readNumber(i - 2), readNumber(i - 1) }); result.m_fontColor = PDFAnnotation::getDrawColorFromAnnotationColor({ readNumber(i - 4), readNumber(i - 3), readNumber(i - 2), readNumber(i - 1) }, 1.0);
} }
} }
} }
@ -2525,6 +2612,7 @@ PDFAnnotationDefaultAppearance PDFAnnotationDefaultAppearance::parse(const QByte
void PDFFreeTextAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFFreeTextAnnotation::draw(AnnotationDrawParameters& parameters) const
{ {
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
parameters.boundingRectangle = getRectangle(); parameters.boundingRectangle = getRectangle();
painter.setPen(getPen()); painter.setPen(getPen());
@ -2628,6 +2716,7 @@ void PDFFreeTextAnnotation::draw(AnnotationDrawParameters& parameters) const
void PDFCaretAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFCaretAnnotation::draw(AnnotationDrawParameters& parameters) const
{ {
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
parameters.boundingRectangle = getRectangle(); parameters.boundingRectangle = getRectangle();
QRectF caretRect = getCaretRectangle(); QRectF caretRect = getCaretRectangle();
@ -2659,6 +2748,7 @@ void PDFInkAnnotation::draw(AnnotationDrawParameters& parameters) const
painter.setPen(getPen()); painter.setPen(getPen());
painter.setBrush(getBrush()); painter.setBrush(getBrush());
painter.setCompositionMode(getCompositionMode());
QPainterPath boundingPath; QPainterPath boundingPath;
QPainterPath currentPath; QPainterPath currentPath;
@ -2729,6 +2819,7 @@ void PDFInkAnnotation::draw(AnnotationDrawParameters& parameters) const
void PDFStampAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFStampAnnotation::draw(AnnotationDrawParameters& parameters) const
{ {
QPainter& painter = *parameters.painter; QPainter& painter = *parameters.painter;
painter.setCompositionMode(getCompositionMode());
QString text; QString text;
QColor color(Qt::red); QColor color(Qt::red);
@ -2882,7 +2973,8 @@ void PDFFileAttachmentAnnotation::draw(AnnotationDrawParameters& parameters) con
break; break;
} }
drawCharacterSymbol(text, getOpacity(), parameters); parameters.painter->setCompositionMode(getCompositionMode());
drawCharacterSymbol(text, getStrokeOpacity(), parameters);
} }
void PDFSoundAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFSoundAnnotation::draw(AnnotationDrawParameters& parameters) const
@ -2902,7 +2994,8 @@ void PDFSoundAnnotation::draw(AnnotationDrawParameters& parameters) const
break; break;
} }
drawCharacterSymbol(text, getOpacity(), parameters); parameters.painter->setCompositionMode(getCompositionMode());
drawCharacterSymbol(text, getStrokeOpacity(), parameters);
} }
const PDFAnnotationManager::PageAnnotation* PDFAnnotationManager::PageAnnotations::getPopupAnnotation(const PageAnnotation& pageAnnotation) const const PDFAnnotationManager::PageAnnotation* PDFAnnotationManager::PageAnnotations::getPopupAnnotation(const PageAnnotation& pageAnnotation) const
@ -2979,6 +3072,7 @@ void PDFWidgetAnnotation::draw(AnnotationDrawParameters& parameters) const
} }
PDFPainterStateGuard guard(parameters.painter); PDFPainterStateGuard guard(parameters.painter);
parameters.painter->setCompositionMode(getCompositionMode());
const PDFFormFieldWidgetEditor* editor = parameters.formManager->getEditor(formField); const PDFFormFieldWidgetEditor* editor = parameters.formManager->getEditor(formField);
if (editor && editor->isEditorDrawEnabled()) if (editor && editor->isEditorDrawEnabled())
@ -3174,7 +3268,7 @@ std::vector<pdf::PDFAppeareanceStreams::Key> PDFWidgetAnnotation::getDrawKeys(co
break; break;
case PDFFormField::FieldType::Choice: case PDFFormField::FieldType::Choice:
// TODO: Implement choice appearance // Choices have always default appearance
break; break;
case PDFFormField::FieldType::Signature: case PDFFormField::FieldType::Signature:

View File

@ -27,6 +27,7 @@
#include "pdfmeshqualitysettings.h" #include "pdfmeshqualitysettings.h"
#include "pdfdocumentdrawinterface.h" #include "pdfdocumentdrawinterface.h"
#include "pdfrenderer.h" #include "pdfrenderer.h"
#include "pdfblendfunction.h"
#include <QCursor> #include <QCursor>
#include <QPainterPath> #include <QPainterPath>
@ -527,6 +528,15 @@ public:
const std::vector<PDFReal>& getColor() const { return m_color; } const std::vector<PDFReal>& getColor() const { return m_color; }
PDFInteger getStructuralParent() const { return m_structParent; } PDFInteger getStructuralParent() const { return m_structParent; }
PDFObjectReference getOptionalContent() const { return m_optionalContentReference; } PDFObjectReference getOptionalContent() const { return m_optionalContentReference; }
const PDFObject& getAssociatedFiles() const { return m_associatedFiles; }
PDFReal getFillOpacity() const { return m_fillingOpacity; }
PDFReal getStrokeOpacity() const { return m_strokingOpacity; }
BlendMode getBlendMode() const { return m_blendMode; }
const QString& getLanguage() const { return m_language; }
/// Returns current composition mode. If blend mode is not supported by Qt,
/// then normal composition mode is returned.
QPainter::CompositionMode getCompositionMode() const;
/// Parses annotation from the object. If error occurs, then nullptr is returned. /// Parses annotation from the object. If error occurs, then nullptr is returned.
/// \param storage Object storage /// \param storage Object storage
@ -546,8 +556,11 @@ public:
/// \param name Name of the line ending /// \param name Name of the line ending
static AnnotationLineEnding convertNameToLineEnding(const QByteArray& name); static AnnotationLineEnding convertNameToLineEnding(const QByteArray& name);
/// Returns draw color from defined annotation color /// Returns draw color from defined annotation color. If color is incorrectly
static QColor getDrawColorFromAnnotationColor(const std::vector<PDFReal>& color); /// defined, then black color is returned.
/// \param color Color (can have 1, 3 and 4 components)
/// \param opacity Opacity
static QColor getDrawColorFromAnnotationColor(const std::vector<PDFReal>& color, PDFReal opacity);
protected: protected:
virtual QColor getStrokeColor() const; virtual QColor getStrokeColor() const;
@ -580,6 +593,29 @@ protected:
/// a brush, then empty brush is returned. /// a brush, then empty brush is returned.
QBrush getBrush() const; QBrush getBrush() const;
/// Draw line ending at given point, using parameters. Line ending appearance
/// is constructed given parameters \p lineEndingSize, \p arrowAxisLength
/// and \p ending. Parameter \p flipAxis controls, if we are drawing at the
/// start (false), or at the end (true) of the line. User can specify matrix,
/// which maps from local coordinate system of the line to the global coordinate
/// system. Also, bouding path is updated.
/// \param painter Painter
/// \param point Point, at which line ending is being drawn
/// \param lineEndingSize Line ending size
/// \param arrowAxisLength Length of the arrow
/// \param ending Type of ending
/// \param flipAxis Flip axis to draw end of line?
/// \param LCStoGCS Transformation from local coordinate system of line to global coordinate system
/// \pram boundingPath Bounding path to be updated
void drawLineEnding(QPainter* painter,
QPointF point,
PDFReal lineEndingSize,
PDFReal arrowAxisLength,
AnnotationLineEnding ending,
bool flipAxis,
const QMatrix& LCStoGCS,
QPainterPath& boundingPath) const;
/// Draw line using given parameters and painter. Line is specified /// Draw line using given parameters and painter. Line is specified
/// by its geometry information. Painter must be set to global coordinates. /// by its geometry information. Painter must be set to global coordinates.
/// Bounding path is also updated, it is specified in global coordinates, /// Bounding path is also updated, it is specified in global coordinates,
@ -626,6 +662,11 @@ private:
std::vector<PDFReal> m_color; ///< Color (for example, title bar of popup window), "C" entry std::vector<PDFReal> m_color; ///< Color (for example, title bar of popup window), "C" entry
PDFInteger m_structParent; ///< Structural parent identifier, "StructParent" entry PDFInteger m_structParent; ///< Structural parent identifier, "StructParent" entry
PDFObjectReference m_optionalContentReference; ///< Reference to optional content, "OC" entry PDFObjectReference m_optionalContentReference; ///< Reference to optional content, "OC" entry
PDFObject m_associatedFiles;
PDFReal m_fillingOpacity = 1.0;
PDFReal m_strokingOpacity = 1.0;
BlendMode m_blendMode = BlendMode::Normal;
QString m_language;
}; };
/// Markup annotation object, used to mark up contents of PDF documents. Markup annotations /// Markup annotation object, used to mark up contents of PDF documents. Markup annotations
@ -648,7 +689,6 @@ public:
const QString& getWindowTitle() const { return m_windowTitle; } const QString& getWindowTitle() const { return m_windowTitle; }
PDFObjectReference getPopupAnnotation() const { return m_popupAnnotation; } PDFObjectReference getPopupAnnotation() const { return m_popupAnnotation; }
PDFReal getOpacity() const { return m_opacity; }
const QString& getRichTextString() const { return m_richTextString; } const QString& getRichTextString() const { return m_richTextString; }
const QDateTime& getCreationDate() const { return m_creationDate; } const QDateTime& getCreationDate() const { return m_creationDate; }
PDFObjectReference getInReplyTo() const { return m_inReplyTo; } PDFObjectReference getInReplyTo() const { return m_inReplyTo; }
@ -657,16 +697,11 @@ public:
const QByteArray& getIntent() const { return m_intent; } const QByteArray& getIntent() const { return m_intent; }
const PDFObject& getExternalData() const { return m_externalData; } const PDFObject& getExternalData() const { return m_externalData; }
protected:
virtual QColor getStrokeColor() const override;
virtual QColor getFillColor() const override;
private: private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference); friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
QString m_windowTitle; QString m_windowTitle;
PDFObjectReference m_popupAnnotation; PDFObjectReference m_popupAnnotation;
PDFReal m_opacity = 1.0;
QString m_richTextString; QString m_richTextString;
QDateTime m_creationDate; QDateTime m_creationDate;
PDFObjectReference m_inReplyTo; PDFObjectReference m_inReplyTo;
@ -915,6 +950,7 @@ public:
const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; } const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; }
Intent getIntent() const { return m_intent; } Intent getIntent() const { return m_intent; }
const PDFObject& getMeasure() const { return m_measure; } const PDFObject& getMeasure() const { return m_measure; }
const QPainterPath& getPath() const { return m_path; }
protected: protected:
virtual QColor getFillColor() const override; virtual QColor getFillColor() const override;
@ -930,6 +966,7 @@ private:
PDFAnnotationBorderEffect m_effect; PDFAnnotationBorderEffect m_effect;
Intent m_intent; Intent m_intent;
PDFObject m_measure; PDFObject m_measure;
QPainterPath m_path;
}; };
/// Annotation for text highlighting. Can highlight, underline, strikeout, /// Annotation for text highlighting. Can highlight, underline, strikeout,