diff --git a/PdfForQtLib/sources/pdfannotation.cpp b/PdfForQtLib/sources/pdfannotation.cpp index dad3a29..12627d5 100644 --- a/PdfForQtLib/sources/pdfannotation.cpp +++ b/PdfForQtLib/sources/pdfannotation.cpp @@ -209,6 +209,16 @@ std::vector PDFAnnotation::getDrawKeys(const PDFForm 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) { 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_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 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" || subtype == "Underline" || @@ -655,12 +725,21 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject result->m_color = loader.readNumberArrayFromDictionary(dictionary, "C"); result->m_structParent = loader.readIntegerFromDictionary(dictionary, "StructParent", 0); 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()) { markupAnnotation->m_windowTitle = loader.readTextStringFromDictionary(dictionary, "T", QString()); 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_creationDate = PDFEncoding::convertToDateTime(loader.readStringFromDictionary(dictionary, "CreationDate")); markupAnnotation->m_inReplyTo = loader.readReferenceFromDictionary(dictionary, "IRT"); @@ -737,7 +816,7 @@ AnnotationLineEnding PDFAnnotation::convertNameToLineEnding(const QByteArray& na QColor PDFAnnotation::getStrokeColor() const { - return getDrawColorFromAnnotationColor(getColor()); + return getDrawColorFromAnnotationColor(getColor(), getStrokeOpacity()); } QColor PDFAnnotation::getFillColor() const @@ -745,14 +824,14 @@ QColor PDFAnnotation::getFillColor() const return QColor(); } -QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector& color) +QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector& color, PDFReal opacity) { switch (color.size()) { case 1: { const PDFReal gray = color.back(); - return QColor::fromRgbF(gray, gray, gray, 1.0); + return QColor::fromRgbF(gray, gray, gray, opacity); } case 3: @@ -760,7 +839,7 @@ QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector const PDFReal r = color[0]; const PDFReal g = color[1]; const PDFReal b = color[2]; - return QColor::fromRgbF(r, g, b, 1.0); + return QColor::fromRgbF(r, g, b, opacity); } case 4: @@ -769,14 +848,16 @@ QColor PDFAnnotation::getDrawColorFromAnnotationColor(const std::vector const PDFReal m = color[1]; const PDFReal y = color[2]; const PDFReal k = color[3]; - return QColor::fromCmykF(c, m, y, k, 1.0); + return QColor::fromCmykF(c, m, y, k, opacity); } default: break; } - return QColor(Qt::black); + QColor black(Qt::black); + black.setAlphaF(opacity); + return black; } QPen PDFAnnotation::getPen() const @@ -1683,7 +1764,7 @@ void PDFWidgetAnnotationManager::createWidgetsForMarkupAnnotations(QWidget* pare scrollArea->setWidget(frameWidget); 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 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; painter.setPen(getPen()); painter.setBrush(getBrush()); + painter.setCompositionMode(getCompositionMode()); switch (getType()) { @@ -1791,12 +1873,7 @@ void PDFSimpleGeometryAnnotation::draw(AnnotationDrawParameters& parameters) con QColor PDFSimpleGeometryAnnotation::getFillColor() const { - QColor color = getDrawColorFromAnnotationColor(getInteriorColor()); - if (color.isValid()) - { - color.setAlphaF(getOpacity()); - } - return color; + return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity()); } bool PDFMarkupAnnotation::isReplyTo() const @@ -1804,26 +1881,6 @@ bool PDFMarkupAnnotation::isReplyTo() const 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 PDFTextAnnotation::getDrawKeys(const PDFFormManager* formManager) const { Q_UNUSED(formManager); @@ -1835,15 +1892,15 @@ std::vector PDFTextAnnotation::getDrawKeys(const PDF void PDFTextAnnotation::draw(AnnotationDrawParameters& parameters) const { - const PDFReal opacity = getOpacity(); - 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, opacity) : - QColor::fromRgbF(1.0, 0.0, 0.0, opacity); + QColor strokeColor = QColor::fromRgbF(0.0, 0.0, 0.0, getStrokeOpacity()); + QColor fillColor = (parameters.key.first == PDFAppeareanceStreams::Appearance::Normal) ? QColor::fromRgbF(1.0, 1.0, 0.0, getFillOpacity()) : + QColor::fromRgbF(1.0, 0.0, 0.0, getFillOpacity()); constexpr const PDFReal rectSize = 32.0; constexpr const PDFReal penWidth = 2.0; QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); QRectF rectangle = getRectangle(); rectangle.setSize(QSizeF(rectSize, rectSize)); @@ -1918,6 +1975,7 @@ void PDFLineAnnotation::draw(AnnotationDrawParameters& parameters) const } QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); painter.setPen(getPen()); painter.setBrush(getBrush()); @@ -1974,12 +2032,7 @@ void PDFLineAnnotation::draw(AnnotationDrawParameters& parameters) const QColor PDFLineAnnotation::getFillColor() const { - QColor color = getDrawColorFromAnnotationColor(getInteriorColor()); - if (color.isValid()) - { - color.setAlphaF(getOpacity()); - } - return color; + return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity()); } void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) const @@ -1991,6 +2044,7 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) } QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); painter.setPen(getPen()); painter.setBrush(getBrush()); @@ -1999,20 +2053,30 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) { case AnnotationType::Polygon: { - QPolygonF polygon; - polygon.reserve(int(m_vertices.size() + 1)); - for (const QPointF& point : m_vertices) + if (m_path.isEmpty()) { - 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; } @@ -2021,28 +2085,46 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) const PDFReal lineEndingSize = painter.pen().widthF() * 5.0; QPainterPath boundingPath; - const size_t pointCount = m_vertices.size(); - const size_t lastPoint = pointCount - 1; - for (size_t i = 1; i < pointCount; ++i) + if (m_path.isEmpty()) { - 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 - drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, getStartLineEnding(), AnnotationLineEnding::None, boundingPath, QPointF(), QString(), true); - } - else if (i == lastPoint) - { - // We draw last line - drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, AnnotationLineEnding::None, getEndLineEnding(), boundingPath, QPointF(), QString(), true); - } - else - { - QLineF line(m_vertices[i - 1], m_vertices[i]); - boundingPath.moveTo(line.p1()); - boundingPath.lineTo(line.p2()); - painter.drawLine(line); + if (i == 1) + { + // We draw first line + drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, getStartLineEnding(), AnnotationLineEnding::None, boundingPath, QPointF(), QString(), true); + } + else if (i == lastPoint) + { + // We draw last line + drawLine(LineGeometryInfo::create(QLineF(m_vertices[i - 1], m_vertices[i])), painter, lineEndingSize, AnnotationLineEnding::None, getEndLineEnding(), boundingPath, QPointF(), QString(), true); + } + else + { + QLineF line(m_vertices[i - 1], m_vertices[i]); + 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.adjust(-lineEndingSize, -lineEndingSize, lineEndingSize, lineEndingSize); @@ -2057,12 +2139,7 @@ void PDFPolygonalGeometryAnnotation::draw(AnnotationDrawParameters& parameters) QColor PDFPolygonalGeometryAnnotation::getFillColor() const { - QColor color = getDrawColorFromAnnotationColor(getInteriorColor()); - if (color.isValid()) - { - color.setAlphaF(getOpacity()); - } - return color; + return getDrawColorFromAnnotationColor(getInteriorColor(), getFillOpacity()); } PDFAnnotation::LineGeometryInfo PDFAnnotation::LineGeometryInfo::create(QLineF line) @@ -2088,6 +2165,120 @@ PDFAnnotation::LineGeometryInfo PDFAnnotation::LineGeometryInfo::create(QLineF l 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, QPainter& painter, PDFReal lineEndingSize, @@ -2120,112 +2311,6 @@ void PDFAnnotation::drawLine(const PDFAnnotation::LineGeometryInfo& info, 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 const PDFReal startOffset = getOffsetFromLineEnding(p1Ending); 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); } - drawLineEnding(info.transformedLine.p1(), p1Ending, false); - drawLineEnding(info.transformedLine.p2(), p2Ending, true); + drawLineEnding(&painter, info.transformedLine.p1(), lineEndingSize, arrowAxisLength, p1Ending, false, info.LCStoGCS, boundingPath); + drawLineEnding(&painter, info.transformedLine.p2(), lineEndingSize, arrowAxisLength, p2Ending, true, info.LCStoGCS, boundingPath); if (drawText && !textIsAboveLine) { @@ -2320,6 +2405,7 @@ void PDFHighlightAnnotation::draw(AnnotationDrawParameters& parameters) const } QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); parameters.boundingRectangle = m_highlightArea.getPath().boundingRect(); painter.setPen(getPen()); @@ -2450,6 +2536,7 @@ void PDFLinkAnnotation::draw(AnnotationDrawParameters& parameters) const case LinkHighlightMode::Push: { // Draw border + painter.setCompositionMode(getCompositionMode()); painter.setPen(getPen()); painter.setBrush(Qt::NoBrush); painter.drawPath(m_activationRegion.getPath()); @@ -2506,15 +2593,15 @@ PDFAnnotationDefaultAppearance PDFAnnotationDefaultAppearance::parse(const QByte } 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) { - 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) { - 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 { QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); parameters.boundingRectangle = getRectangle(); painter.setPen(getPen()); @@ -2628,6 +2716,7 @@ void PDFFreeTextAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFCaretAnnotation::draw(AnnotationDrawParameters& parameters) const { QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); parameters.boundingRectangle = getRectangle(); QRectF caretRect = getCaretRectangle(); @@ -2659,6 +2748,7 @@ void PDFInkAnnotation::draw(AnnotationDrawParameters& parameters) const painter.setPen(getPen()); painter.setBrush(getBrush()); + painter.setCompositionMode(getCompositionMode()); QPainterPath boundingPath; QPainterPath currentPath; @@ -2729,6 +2819,7 @@ void PDFInkAnnotation::draw(AnnotationDrawParameters& parameters) const void PDFStampAnnotation::draw(AnnotationDrawParameters& parameters) const { QPainter& painter = *parameters.painter; + painter.setCompositionMode(getCompositionMode()); QString text; QColor color(Qt::red); @@ -2882,7 +2973,8 @@ void PDFFileAttachmentAnnotation::draw(AnnotationDrawParameters& parameters) con break; } - drawCharacterSymbol(text, getOpacity(), parameters); + parameters.painter->setCompositionMode(getCompositionMode()); + drawCharacterSymbol(text, getStrokeOpacity(), parameters); } void PDFSoundAnnotation::draw(AnnotationDrawParameters& parameters) const @@ -2902,7 +2994,8 @@ void PDFSoundAnnotation::draw(AnnotationDrawParameters& parameters) const break; } - drawCharacterSymbol(text, getOpacity(), parameters); + parameters.painter->setCompositionMode(getCompositionMode()); + drawCharacterSymbol(text, getStrokeOpacity(), parameters); } const PDFAnnotationManager::PageAnnotation* PDFAnnotationManager::PageAnnotations::getPopupAnnotation(const PageAnnotation& pageAnnotation) const @@ -2979,6 +3072,7 @@ void PDFWidgetAnnotation::draw(AnnotationDrawParameters& parameters) const } PDFPainterStateGuard guard(parameters.painter); + parameters.painter->setCompositionMode(getCompositionMode()); const PDFFormFieldWidgetEditor* editor = parameters.formManager->getEditor(formField); if (editor && editor->isEditorDrawEnabled()) @@ -3174,7 +3268,7 @@ std::vector PDFWidgetAnnotation::getDrawKeys(co break; case PDFFormField::FieldType::Choice: - // TODO: Implement choice appearance + // Choices have always default appearance break; case PDFFormField::FieldType::Signature: diff --git a/PdfForQtLib/sources/pdfannotation.h b/PdfForQtLib/sources/pdfannotation.h index 655e8cd..3f17289 100644 --- a/PdfForQtLib/sources/pdfannotation.h +++ b/PdfForQtLib/sources/pdfannotation.h @@ -27,6 +27,7 @@ #include "pdfmeshqualitysettings.h" #include "pdfdocumentdrawinterface.h" #include "pdfrenderer.h" +#include "pdfblendfunction.h" #include #include @@ -527,6 +528,15 @@ public: const std::vector& getColor() const { return m_color; } PDFInteger getStructuralParent() const { return m_structParent; } 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. /// \param storage Object storage @@ -546,8 +556,11 @@ public: /// \param name Name of the line ending static AnnotationLineEnding convertNameToLineEnding(const QByteArray& name); - /// Returns draw color from defined annotation color - static QColor getDrawColorFromAnnotationColor(const std::vector& color); + /// Returns draw color from defined annotation color. If color is incorrectly + /// 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& color, PDFReal opacity); protected: virtual QColor getStrokeColor() const; @@ -580,6 +593,29 @@ protected: /// a brush, then empty brush is returned. 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 /// by its geometry information. Painter must be set to global coordinates. /// Bounding path is also updated, it is specified in global coordinates, @@ -626,6 +662,11 @@ private: std::vector m_color; ///< Color (for example, title bar of popup window), "C" entry PDFInteger m_structParent; ///< Structural parent identifier, "StructParent" 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 @@ -648,7 +689,6 @@ public: const QString& getWindowTitle() const { return m_windowTitle; } PDFObjectReference getPopupAnnotation() const { return m_popupAnnotation; } - PDFReal getOpacity() const { return m_opacity; } const QString& getRichTextString() const { return m_richTextString; } const QDateTime& getCreationDate() const { return m_creationDate; } PDFObjectReference getInReplyTo() const { return m_inReplyTo; } @@ -657,16 +697,11 @@ public: const QByteArray& getIntent() const { return m_intent; } const PDFObject& getExternalData() const { return m_externalData; } -protected: - virtual QColor getStrokeColor() const override; - virtual QColor getFillColor() const override; - private: friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference); QString m_windowTitle; PDFObjectReference m_popupAnnotation; - PDFReal m_opacity = 1.0; QString m_richTextString; QDateTime m_creationDate; PDFObjectReference m_inReplyTo; @@ -915,6 +950,7 @@ public: const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; } Intent getIntent() const { return m_intent; } const PDFObject& getMeasure() const { return m_measure; } + const QPainterPath& getPath() const { return m_path; } protected: virtual QColor getFillColor() const override; @@ -930,6 +966,7 @@ private: PDFAnnotationBorderEffect m_effect; Intent m_intent; PDFObject m_measure; + QPainterPath m_path; }; /// Annotation for text highlighting. Can highlight, underline, strikeout,