diff --git a/Pdf4QtDiff/utils.cpp b/Pdf4QtDiff/utils.cpp index 30ca2401..c436911e 100644 --- a/Pdf4QtDiff/utils.cpp +++ b/Pdf4QtDiff/utils.cpp @@ -302,6 +302,7 @@ void DifferencesDrawInterface::drawPage(QPainter* painter, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -329,7 +330,7 @@ void DifferencesDrawInterface::drawPage(QPainter* painter, const auto& item = *it; if (item.first == leftPageIndex) { - QColor color = getColorForIndex(i); + QColor color = convertor.convert(getColorForIndex(i), false, true); drawRectangle(painter, pagePointToDevicePointMatrix, item.second, color); drawMarker(painter, pagePointToDevicePointMatrix, item.second, color, true); } @@ -352,7 +353,7 @@ void DifferencesDrawInterface::drawPage(QPainter* painter, const auto& item = *it; if (item.first == rightPageIndex) { - QColor color = getColorForIndex(i); + QColor color = convertor.convert(getColorForIndex(i), false, true); drawRectangle(painter, pagePointToDevicePointMatrix, item.second, color); drawMarker(painter, pagePointToDevicePointMatrix, item.second, color, false); } @@ -388,7 +389,7 @@ void DifferencesDrawInterface::drawPage(QPainter* painter, break; } - QColor color = getColorForIndex(*pageMoveIndex); + QColor color = convertor.convert(getColorForIndex(*pageMoveIndex), false, true); QPointF targetPoint = pagePointToDevicePointMatrix.map(QPointF(5, 5)); pdf::PDFPainterHelper::drawBubble(painter, targetPoint.toPoint(), color, text, Qt::AlignRight | Qt::AlignTop); } diff --git a/Pdf4QtDiff/utils.h b/Pdf4QtDiff/utils.h index 8709db53..6f438691 100644 --- a/Pdf4QtDiff/utils.h +++ b/Pdf4QtDiff/utils.h @@ -93,6 +93,7 @@ class DifferencesDrawInterface : public pdf::IDocumentDrawInterface const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const override; virtual void drawPostRendering(QPainter* painter, QRect rect) const override; diff --git a/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.cpp b/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.cpp index 9eb57767..43e3f4ac 100644 --- a/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.cpp +++ b/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.cpp @@ -20,6 +20,7 @@ #include "pdfwidgettool.h" #include "pdfutils.h" #include "pdfwidgetutils.h" +#include "pdfcms.h" #include "audiobookcreator.h" #include @@ -170,6 +171,7 @@ void AudioBookPlugin::drawPage(QPainter* painter, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -208,8 +210,8 @@ void AudioBookPlugin::drawPage(QPainter* painter, fillColor.setAlphaF(0.2f); pen.setColor(strokeColor); - painter->setPen(pen); - painter->setBrush(QBrush(fillColor)); + painter->setPen(convertor.convert(pen)); + painter->setBrush(convertor.convert(QBrush(fillColor))); QPainterPath path; path.addRect(boundingRect); diff --git a/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.h b/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.h index 1f547521..6b38b293 100644 --- a/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.h +++ b/Pdf4QtEditorPlugins/AudioBookPlugin/audiobookplugin.h @@ -53,6 +53,7 @@ class AudioBookPlugin : public pdf::PDFPlugin, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const override; // IDrawWidgetInputInterface interface diff --git a/Pdf4QtEditorPlugins/CMakeLists.txt b/Pdf4QtEditorPlugins/CMakeLists.txt index a1ef10dd..177f01cf 100644 --- a/Pdf4QtEditorPlugins/CMakeLists.txt +++ b/Pdf4QtEditorPlugins/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 Jakub Melka +# Copyright (C) 2022-2023 Jakub Melka # # This file is part of PDF4QT. # @@ -28,3 +28,4 @@ add_subdirectory(OutputPreviewPlugin) add_subdirectory(RedactPlugin) add_subdirectory(SignaturePlugin) add_subdirectory(SoftProofingPlugin) +add_subdirectory(EditorPlugin) diff --git a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.cpp b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.cpp index 43a3be21..8a72b2f8 100644 --- a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.cpp +++ b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.cpp @@ -18,6 +18,7 @@ #include "dimensionsplugin.h" #include "pdfdrawwidget.h" #include "pdfwidgetutils.h" +#include "pdfcms.h" #include "settingsdialog.h" #include @@ -149,6 +150,7 @@ void DimensionsPlugin::drawPage(QPainter* painter, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -196,7 +198,7 @@ void DimensionsPlugin::drawPage(QPainter* painter, QPointF unitVector = unitVectorLine.p2() - unitVectorLine.p1(); qreal extensionLineSize = pdf::PDFWidgetUtils::scaleDPI_y(painter->device(), 5); - painter->setPen(Qt::black); + painter->setPen(convertor.convert(QColor(Qt::black), false, true)); painter->drawLine(line); QLineF extensionLineLeft(p1 - unitVector * extensionLineSize, p1 + unitVector * extensionLineSize); @@ -237,14 +239,14 @@ void DimensionsPlugin::drawPage(QPainter* painter, QColor brushColor = Qt::black; brushColor.setAlphaF(0.1f); - painter->setPen(qMove(pen)); - painter->setBrush(QBrush(brushColor, isArea ? Qt::SolidPattern : Qt::DiagCrossPattern)); + painter->setPen(convertor.convert(pen)); + painter->setBrush(convertor.convert(QBrush(brushColor, isArea ? Qt::SolidPattern : Qt::DiagCrossPattern))); painter->setTransform(QTransform(pagePointToDevicePointMatrix), true); painter->drawPolygon(polygon.data(), int(polygon.size()), Qt::OddEvenFill); painter->restore(); - QPen penPoint(Qt::black); + QPen penPoint(convertor.convert(QColor(Qt::black), false, true)); penPoint.setCapStyle(Qt::RoundCap); penPoint.setWidthF(pointSize); painter->setPen(penPoint); @@ -287,7 +289,7 @@ void DimensionsPlugin::drawPage(QPainter* painter, line1.setLength(maxLength); line2.setLength(maxLength); - QPen pen(Qt::black); + QPen pen(convertor.convert(QColor(Qt::black), false, true)); pen.setWidthF(lineSize); painter->setPen(qMove(pen)); @@ -301,7 +303,7 @@ void DimensionsPlugin::drawPage(QPainter* painter, rect.translate(line1.p1()); painter->drawArc(rect, startAngle - angleLength, angleLength); - QPen penPoint(Qt::black); + QPen penPoint(convertor.convert(QColor(Qt::black), false, true)); penPoint.setCapStyle(Qt::RoundCap); penPoint.setWidthF(pointSize); painter->setPen(penPoint); diff --git a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.h b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.h index 7d5c2382..bd005235 100644 --- a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.h +++ b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensionsplugin.h @@ -47,6 +47,7 @@ class DimensionsPlugin : public pdf::PDFPlugin, public pdf::IDocumentDrawInterfa const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const override; private: diff --git a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.cpp b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.cpp index d2af61e2..2dc4848c 100644 --- a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.cpp +++ b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.cpp @@ -18,6 +18,7 @@ #include "dimensiontool.h" #include "pdfwidgetutils.h" #include "pdfdrawwidget.h" +#include "pdfcms.h" #include @@ -41,6 +42,7 @@ void DimensionTool::drawPage(QPainter* painter, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -59,7 +61,7 @@ void DimensionTool::drawPage(QPainter* painter, return; } - painter->setPen(Qt::black); + painter->setPen(convertor.convert(QColor(Qt::black), false, true)); const std::vector& points = m_pickTool->getPickedPoints(); for (size_t i = 1; i < points.size(); ++i) { diff --git a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.h b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.h index 5d9752d0..e2112f12 100644 --- a/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.h +++ b/Pdf4QtEditorPlugins/DimensionsPlugin/dimensiontool.h @@ -111,6 +111,7 @@ class DimensionTool : public pdf::PDFWidgetTool const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const override; signals: diff --git a/Pdf4QtEditorPlugins/EditorPlugin/CMakeLists.txt b/Pdf4QtEditorPlugins/EditorPlugin/CMakeLists.txt new file mode 100644 index 00000000..3e38b820 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (C) 2023 Jakub Melka +# +# This file is part of PDF4QT. +# +# PDF4QT is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# with the written consent of the copyright owner, any later version. +# +# PDF4QT 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PDF4QT. If not, see . + +add_library(EditorPlugin SHARED + editorplugin.cpp + icons.qrc +) + +target_link_libraries(EditorPlugin PRIVATE Pdf4QtLibCore Pdf4QtLibWidgets Qt6::Core Qt6::Gui Qt6::Widgets) + +set_target_properties(EditorPlugin PROPERTIES + VERSION ${PDF4QT_VERSION} + SOVERSION ${PDF4QT_VERSION} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_PLUGINS_DIR} + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_PLUGINS_DIR}) + +install(TARGETS EditorPlugin RUNTIME DESTINATION ${PDF4QT_PLUGINS_DIR} LIBRARY DESTINATION ${PDF4QT_PLUGINS_DIR}) + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/EditorPlugin.json b/Pdf4QtEditorPlugins/EditorPlugin/EditorPlugin.json new file mode 100644 index 00000000..cc513643 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/EditorPlugin.json @@ -0,0 +1,7 @@ +{ + "Name" : "Editor", + "Author" : "Jakub Melka", + "Version" : "1.0.0", + "License" : "LGPL v3", + "Description" : "Edit content of PDF document." +} diff --git a/Pdf4QtEditorPlugins/EditorPlugin/accept-mark.svg b/Pdf4QtEditorPlugins/EditorPlugin/accept-mark.svg new file mode 100644 index 00000000..d577d968 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/accept-mark.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/activate.svg b/Pdf4QtEditorPlugins/EditorPlugin/activate.svg new file mode 100644 index 00000000..d8fb5d0a --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/activate.svg @@ -0,0 +1,58 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/clear.svg b/Pdf4QtEditorPlugins/EditorPlugin/clear.svg new file mode 100644 index 00000000..9fbb320a --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/clear.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-dot.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-dot.svg new file mode 100644 index 00000000..97427e17 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-dot.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-freehand-curve.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-freehand-curve.svg new file mode 100644 index 00000000..3b8e3b4b --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-freehand-curve.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-horizontal-line.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-horizontal-line.svg new file mode 100644 index 00000000..34272e6d --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-horizontal-line.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-line.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-line.svg new file mode 100644 index 00000000..ff400fbd --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-line.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-no-mark.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-no-mark.svg new file mode 100644 index 00000000..a5f20ea1 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-no-mark.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-rectangle.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-rectangle.svg new file mode 100644 index 00000000..5b3c056d --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-rectangle.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-rounded-rectangle.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-rounded-rectangle.svg new file mode 100644 index 00000000..397628a2 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-rounded-rectangle.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-svg-image.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-svg-image.svg new file mode 100644 index 00000000..0d46519b --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-svg-image.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-text.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-text.svg new file mode 100644 index 00000000..8e4173ab --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-text.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-vertical-line.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-vertical-line.svg new file mode 100644 index 00000000..6b4239fb --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-vertical-line.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/create-yes-mark.svg b/Pdf4QtEditorPlugins/EditorPlugin/create-yes-mark.svg new file mode 100644 index 00000000..d9c53c82 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/create-yes-mark.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/editorplugin.cpp b/Pdf4QtEditorPlugins/EditorPlugin/editorplugin.cpp new file mode 100644 index 00000000..24cd5042 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/editorplugin.cpp @@ -0,0 +1,835 @@ +// Copyright (C) 2023-2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "editorplugin.h" +#include "pdfdrawwidget.h" +#include "pdfutils.h" +#include "pdfpagecontenteditorwidget.h" +#include "pdfpagecontenteditorstylesettings.h" +#include "pdfdocumentbuilder.h" +#include "pdfcertificatemanagerdialog.h" +#include "pdfdocumentwriter.h" +#include "pdfpagecontenteditorprocessor.h" +#include "pdfpagecontenteditorcontentstreambuilder.h" +#include "pdfstreamfilters.h" +#include "pdfoptimizer.h" + +#include +#include +#include +#include +#include + +namespace pdfplugin +{ + +EditorPlugin::EditorPlugin() : + pdf::PDFPlugin(nullptr), + m_actions({ }), + m_tools({ }), + m_editorWidget(nullptr), + m_scene(nullptr), + m_sceneSelectionChangeEnabled(true), + m_isSaving(false) +{ + m_scene.setIsPageContentDrawSuppressed(true); +} + +// TODO: When text is edited, old text remains + +void EditorPlugin::setWidget(pdf::PDFWidget* widget) +{ + Q_ASSERT(!m_widget); + + BaseClass::setWidget(widget); + + QAction* activateAction = new QAction(QIcon(":/pdfplugins/editorplugin/activate.svg"), tr("&Edit page content"), this); + QAction* createTextAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-text.svg"), tr("Create &Text Label"), this); + QAction* createFreehandCurveAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-freehand-curve.svg"), tr("Create &Freehand Curve"), this); + QAction* createAcceptMarkAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-yes-mark.svg"), tr("Create &Accept Mark"), this); + QAction* createRejectMarkAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-no-mark.svg"), tr("Create &Reject Mark"), this); + QAction* createRectangleAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-rectangle.svg"), tr("Create R&ectangle"), this); + QAction* createRoundedRectangleAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-rounded-rectangle.svg"), tr("&Create Rounded Rectangle"), this); + QAction* createHorizontalLineAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-horizontal-line.svg"), tr("Create &Horizontal Line"), this); + QAction* createVerticalLineAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-vertical-line.svg"), tr("Create &Vertical Line"), this); + QAction* createLineAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-line.svg"), tr("Create L&ine"), this); + QAction* createDotAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-dot.svg"), tr("Create &Dot"), this); + QAction* createSvgImageAction = new QAction(QIcon(":/pdfplugins/editorplugin/create-svg-image.svg"), tr("Create &SVG Image"), this); + QAction* clearAction = new QAction(QIcon(":/pdfplugins/editorplugin/clear.svg"), tr("Clear A&ll Graphics"), this); + QAction* certificatesAction = new QAction(QIcon(":/pdfplugins/editorplugin/certificates.svg"), tr("Certificates &Manager"), this); + + activateAction->setObjectName("editortool_activateAction"); + createTextAction->setObjectName("editortool_createTextAction"); + createFreehandCurveAction->setObjectName("editortool_createFreehandCurveAction"); + createAcceptMarkAction->setObjectName("editortool_createAcceptMarkAction"); + createRejectMarkAction->setObjectName("editortool_createRejectMarkAction"); + createRectangleAction->setObjectName("editortool_createRectangleAction"); + createRoundedRectangleAction->setObjectName("editortool_createRoundedRectangleAction"); + createHorizontalLineAction->setObjectName("editortool_createHorizontalLineAction"); + createVerticalLineAction->setObjectName("editortool_createVerticalLineAction"); + createLineAction->setObjectName("editortool_createLineAction"); + createDotAction->setObjectName("editortool_createDotAction"); + createSvgImageAction->setObjectName("editortool_createSvgImageAction"); + clearAction->setObjectName("editortool_clearAction"); + certificatesAction->setObjectName("editortool_certificatesAction"); + + activateAction->setCheckable(true); + createTextAction->setCheckable(true); + createFreehandCurveAction->setCheckable(true); + createAcceptMarkAction->setCheckable(true); + createRejectMarkAction->setCheckable(true); + createRectangleAction->setCheckable(true); + createRoundedRectangleAction->setCheckable(true); + createHorizontalLineAction->setCheckable(true); + createVerticalLineAction->setCheckable(true); + createLineAction->setCheckable(true); + createDotAction->setCheckable(true); + createSvgImageAction->setCheckable(true); + + m_actions[Activate] = activateAction; + m_actions[Text] = createTextAction; + m_actions[FreehandCurve] = createFreehandCurveAction; + m_actions[AcceptMark] = createAcceptMarkAction; + m_actions[RejectMark] = createRejectMarkAction; + m_actions[Rectangle] = createRectangleAction; + m_actions[RoundedRectangle] = createRoundedRectangleAction; + m_actions[HorizontalLine] = createHorizontalLineAction; + m_actions[VerticalLine] = createVerticalLineAction; + m_actions[Line] = createLineAction; + m_actions[Dot] = createDotAction; + m_actions[SvgImage] = createSvgImageAction; + m_actions[Clear] = clearAction; + + QFile acceptMarkFile(":/pdfplugins/editorplugin/accept-mark.svg"); + QByteArray acceptMarkContent; + if (acceptMarkFile.open(QFile::ReadOnly)) + { + acceptMarkContent = acceptMarkFile.readAll(); + acceptMarkFile.close(); + } + + QFile rejectMarkFile(":/pdfplugins/editorplugin/reject-mark.svg"); + QByteArray rejectMarkContent; + if (rejectMarkFile.open(QFile::ReadOnly)) + { + rejectMarkContent = rejectMarkFile.readAll(); + rejectMarkFile.close(); + } + + m_tools[TextTool] = new pdf::PDFCreatePCElementTextTool(widget->getDrawWidgetProxy(), &m_scene, createTextAction, this); + m_tools[FreehandCurveTool] = new pdf::PDFCreatePCElementFreehandCurveTool(widget->getDrawWidgetProxy(), &m_scene, createFreehandCurveAction, this); + m_tools[AcceptMarkTool] = new pdf::PDFCreatePCElementImageTool(widget->getDrawWidgetProxy(), &m_scene, createAcceptMarkAction, acceptMarkContent, false, this); + m_tools[RejectMarkTool] = new pdf::PDFCreatePCElementImageTool(widget->getDrawWidgetProxy(), &m_scene, createRejectMarkAction, rejectMarkContent, false, this); + m_tools[RectangleTool] = new pdf::PDFCreatePCElementRectangleTool(widget->getDrawWidgetProxy(), &m_scene, createRectangleAction, false, this); + m_tools[RoundedRectangleTool] = new pdf::PDFCreatePCElementRectangleTool(widget->getDrawWidgetProxy(), &m_scene, createRoundedRectangleAction, true, this); + m_tools[HorizontalLineTool] = new pdf::PDFCreatePCElementLineTool(widget->getDrawWidgetProxy(), &m_scene, createHorizontalLineAction, true, false, this); + m_tools[VerticalLineTool] = new pdf::PDFCreatePCElementLineTool(widget->getDrawWidgetProxy(), &m_scene, createVerticalLineAction, false, true, this); + m_tools[LineTool] = new pdf::PDFCreatePCElementLineTool(widget->getDrawWidgetProxy(), &m_scene, createLineAction, false, false, this); + m_tools[DotTool] = new pdf::PDFCreatePCElementDotTool(widget->getDrawWidgetProxy(), &m_scene, createDotAction, this); + m_tools[ImageTool] = new pdf::PDFCreatePCElementImageTool(widget->getDrawWidgetProxy(), &m_scene, createSvgImageAction, QByteArray(), true, this); + + pdf::PDFToolManager* toolManager = widget->getToolManager(); + for (pdf::PDFWidgetTool* tool : m_tools) + { + toolManager->addTool(tool); + connect(tool, &pdf::PDFWidgetTool::toolActivityChanged, this, &EditorPlugin::onToolActivityChanged); + } + + m_widget->addInputInterface(&m_scene); + m_widget->getDrawWidgetProxy()->registerDrawInterface(&m_scene); + m_scene.setWidget(m_widget); + connect(&m_scene, &pdf::PDFPageContentScene::sceneChanged, this, &EditorPlugin::onSceneChanged); + connect(&m_scene, &pdf::PDFPageContentScene::selectionChanged, this, &EditorPlugin::onSceneSelectionChanged); + connect(&m_scene, &pdf::PDFPageContentScene::editElementRequest, this, &EditorPlugin::onSceneEditElement); + connect(clearAction, &QAction::triggered, &m_scene, &pdf::PDFPageContentScene::clear); + connect(activateAction, &QAction::triggered, this, &EditorPlugin::onSetActive); + connect(m_widget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::drawSpaceChanged, this, &EditorPlugin::onDrawSpaceChanged); + connect(m_widget, &pdf::PDFWidget::sceneActivityChanged, this, &EditorPlugin::onSceneActivityChanged); + + updateActions(); +} + +void EditorPlugin::setDocument(const pdf::PDFModifiedDocument& document) +{ + BaseClass::setDocument(document); + + if (document.hasReset()) + { + setActive(false); + updateActions(); + } +} + +std::vector EditorPlugin::getActions() const +{ + std::vector result; + + result.push_back(m_actions[Activate]); + + return result; +} + +QString EditorPlugin::getPluginMenuName() const +{ + return tr("Edi&tor"); +} + +bool EditorPlugin::updatePageContent(pdf::PDFInteger pageIndex, + const std::vector& elements, + pdf::PDFDocumentBuilder* builder) +{ + pdf::PDFColorConvertor convertor; + const pdf::PDFPage* page = m_document->getCatalog()->getPage(pageIndex); + const pdf::PDFEditedPageContent& editedPageContent = m_editedPageContent.at(pageIndex); + + QRectF mediaBox = page->getMediaBox(); + QRectF mediaBoxMM = page->getMediaBoxMM(); + + pdf::PDFPageContentEditorContentStreamBuilder contentStreamBuilder(m_document); + contentStreamBuilder.setFontDictionary(editedPageContent.getFontDictionary()); + + for (const pdf::PDFPageContentElement* element : elements) + { + const pdf::PDFPageContentElementEdited* editedElement = element->asElementEdited(); + const pdf::PDFPageContentElementRectangle* elementRectangle = element->asElementRectangle(); + const pdf::PDFPageContentElementLine* elementLine = element->asElementLine(); + const pdf::PDFPageContentElementDot* elementDot = element->asElementDot(); + const pdf::PDFPageContentElementFreehandCurve* elementFreehandCurve = element->asElementFreehandCurve(); + const pdf::PDFPageContentImageElement* elementImage = element->asElementImage(); + const pdf::PDFPageContentElementTextBox* elementTextBox = element->asElementTextBox(); + + if (editedElement) + { + contentStreamBuilder.writeEditedElement(editedElement->getElement()); + } + + if (elementRectangle) + { + QRectF rect = elementRectangle->getRectangle(); + + QPainterPath path; + if (elementRectangle->isRounded()) + { + qreal radius = qMin(rect.width(), rect.height()) * 0.25; + path.addRoundedRect(rect, radius, radius, Qt::AbsoluteSize); + } + else + { + path.addRect(rect); + } + + const bool stroke = elementRectangle->getPen().style() != Qt::NoPen; + const bool fill = elementRectangle->getBrush().style() != Qt::NoBrush; + contentStreamBuilder.writeStyledPath(path, elementRectangle->getPen(), elementRectangle->getBrush(), stroke, fill); + } + + if (elementLine) + { + QLineF line = elementLine->getLine(); + QPainterPath path; + path.moveTo(line.p1()); + path.lineTo(line.p2()); + + contentStreamBuilder.writeStyledPath(path, elementLine->getPen(), elementLine->getBrush(), true, false); + } + + if (elementDot) + { + QPen pen = elementDot->getPen(); + const qreal radius = pen.widthF() * 0.5; + + QPainterPath path; + path.addEllipse(elementDot->getPoint(), radius, radius); + + contentStreamBuilder.writeStyledPath(path, Qt::NoPen, QBrush(pen.color()), false, true); + } + + if (elementFreehandCurve) + { + QPainterPath path = elementFreehandCurve->getCurve(); + contentStreamBuilder.writeStyledPath(path, elementFreehandCurve->getPen(), elementFreehandCurve->getBrush(), true, false); + } + + if (elementImage) + { + QImage image = elementImage->getImage(); + if (!image.isNull()) + { + contentStreamBuilder.writeImage(image, elementImage->getRectangle()); + } + else + { + // It is probably an SVG image + pdf::PDFContentEditorPaintDevice paintDevice(&contentStreamBuilder, mediaBox, mediaBoxMM); + QPainter painter(&paintDevice); + + QList errors; + pdf::PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex); + elementImage->drawPage(&painter, &m_scene, pageIndex, nullptr, textLayoutGetter, QTransform(), convertor, errors); + } + } + + if (elementTextBox) + { + pdf::PDFContentEditorPaintDevice paintDevice(&contentStreamBuilder, mediaBox, mediaBoxMM); + QPainter painter(&paintDevice); + + QList errors; + pdf::PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex); + elementTextBox->drawPage(&painter, &m_scene, pageIndex, nullptr, textLayoutGetter, QTransform(), convertor, errors); + } + } + + QStringList errors = contentStreamBuilder.getErrors(); + contentStreamBuilder.clearErrors(); + + if (!errors.empty()) + { + const int errorCount = errors.size(); + if (errors.size() > 3) + { + errors.resize(3); + } + + QString message = tr("Errors (%2) occured while creating content stream on page %3.
%1").arg(errors.join("
")).arg(errorCount).arg(pageIndex + 1); + if (QMessageBox::question(m_dataExchangeInterface->getMainWindow(), tr("Error"), message, QMessageBox::Abort, QMessageBox::Ignore) == QMessageBox::Abort) + { + return false; + } + } + + pdf::PDFDictionary fontDictionary = contentStreamBuilder.getFontDictionary(); + pdf::PDFDictionary xobjectDictionary = contentStreamBuilder.getXObjectDictionary(); + pdf::PDFDictionary graphicStateDictionary = contentStreamBuilder.getGraphicStateDictionary(); + + builder->replaceObjectsByReferences(fontDictionary); + builder->replaceObjectsByReferences(xobjectDictionary); + builder->replaceObjectsByReferences(graphicStateDictionary); + + pdf::PDFArray array; + array.appendItem(pdf::PDFObject::createName("FlateDecode")); + + // Compress the content stream + QByteArray compressedData = pdf::PDFFlateDecodeFilter::compress(contentStreamBuilder.getOutputContent()); + pdf::PDFDictionary contentDictionary; + contentDictionary.setEntry(pdf::PDFInplaceOrMemoryString("Length"), pdf::PDFObject::createInteger(compressedData.size())); + contentDictionary.setEntry(pdf::PDFInplaceOrMemoryString("Filter"), pdf::PDFObject::createArray(std::make_shared(qMove(array)))); + pdf::PDFObject contentObject = pdf::PDFObject::createStream(std::make_shared(qMove(contentDictionary), qMove(compressedData))); + + pdf::PDFObject pageObject = builder->getObjectByReference(page->getPageReference()); + + pdf::PDFObjectFactory factory; + factory.beginDictionary(); + factory.beginDictionaryItem("Resources"); + factory.beginDictionary(); + + if (!fontDictionary.isEmpty()) + { + factory.beginDictionaryItem("Font"); + factory << fontDictionary; + factory.endDictionaryItem(); + } + + if (!xobjectDictionary.isEmpty()) + { + factory.beginDictionaryItem("XObject"); + factory << xobjectDictionary; + factory.endDictionaryItem(); + } + + if (!graphicStateDictionary.isEmpty()) + { + factory.beginDictionaryItem("ExtGState"); + factory << graphicStateDictionary; + factory.endDictionaryItem(); + } + + factory.endDictionary(); + factory.endDictionaryItem(); + + factory.beginDictionaryItem("Contents"); + factory << builder->addObject(std::move(contentObject)); + factory.endDictionaryItem(); + + factory.endDictionary(); + + pageObject = pdf::PDFObjectManipulator::merge(pageObject, factory.takeObject(), pdf::PDFObjectManipulator::RemoveNullObjects); + builder->setObject(page->getPageReference(), std::move(pageObject)); + + return true; +} + +bool EditorPlugin::save() +{ + pdf::PDFTemporaryValueChange guard(&m_isSaving, true); + + auto answer = QMessageBox::question(m_dataExchangeInterface->getMainWindow(), tr("Confirm Changes"), tr("The changes to the page content will be written to the document. Do you want to continue?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel); + + if (answer == QMessageBox::Cancel) + { + return false; + } + + if (answer == QMessageBox::Yes) + { + pdf::PDFDocumentModifier modifier(m_document); + pdf::PDFDocumentBuilder* builder = modifier.getBuilder(); + + std::set pageIndices; + for (const auto& item : m_editedPageContent) + { + pageIndices.insert(item.first); + } + + std::map> elementsByPage = m_scene.getElementsByPage(); + for (pdf::PDFInteger pageIndex : pageIndices) + { + if (m_editedPageContent.count(pageIndex) == 0) + { + continue; + } + + std::vector elements; + auto it = elementsByPage.find(pageIndex); + if (it != elementsByPage.cend()) + { + elements = std::move(it->second); + } + + if (!updatePageContent(pageIndex, elements, builder)) + { + return false; + } + + modifier.markReset(); + } + + m_scene.clear(); + m_editedPageContent.clear(); + + if (modifier.finalize()) + { + pdf::PDFDocument document = *modifier.getDocument(); + pdf::PDFOptimizer optimizer(pdf::PDFOptimizer::DereferenceSimpleObjects | + pdf::PDFOptimizer::RemoveNullObjects | + pdf::PDFOptimizer::RemoveUnusedObjects | + pdf::PDFOptimizer::MergeIdenticalObjects | + pdf::PDFOptimizer::ShrinkObjectStorage, nullptr); + optimizer.setDocument(&document); + optimizer.optimize(); + document = optimizer.takeOptimizedDocument(); + + Q_EMIT m_widget->getToolManager()->documentModified(pdf::PDFModifiedDocument(pdf::PDFDocumentPointer(new pdf::PDFDocument(std::move(document))), nullptr, modifier.getFlags())); + } + } + + return true; +} + +void EditorPlugin::onSceneActivityChanged() +{ + updateActions(); +} + +void EditorPlugin::onSceneChanged(bool graphicsOnly) +{ + if (!graphicsOnly) + { + updateActions(); + } + + if (m_editorWidget) + { + m_editorWidget->updateItemsInListWidget(); + } + + updateGraphics(); +} + +void EditorPlugin::onSceneSelectionChanged() +{ + if (m_editorWidget && m_sceneSelectionChangeEnabled) + { + m_editorWidget->setSelection(m_scene.getSelectedElementIds()); + } +} + +void EditorPlugin::onWidgetSelectionChanged() +{ + Q_ASSERT(m_editorWidget); + + pdf::PDFTemporaryValueChange guard(&m_sceneSelectionChangeEnabled, false); + m_scene.setSelectedElementIds(m_editorWidget->getSelection()); +} + +pdf::PDFWidgetTool* EditorPlugin::getActiveTool() +{ + for (pdf::PDFWidgetTool* currentTool : m_tools) + { + if (currentTool->isActive()) + { + return currentTool; + } + } + + return nullptr; +} + +void EditorPlugin::onToolActivityChanged() +{ + if (m_editorWidget) + { + pdf::PDFWidgetTool* activeTool = getActiveTool(); + + const pdf::PDFPageContentElement* element = nullptr; + pdf::PDFCreatePCElementTool* tool = qobject_cast(activeTool); + if (tool) + { + element = tool->getElement(); + } + + m_editorWidget->loadStyleFromElement(element); + } +} + +void EditorPlugin::onSceneEditElement(const std::set& elements) +{ + if (elements.empty()) + { + return; + } + + pdf::PDFPageContentElement* element = nullptr; + for (pdf::PDFInteger id : elements) + { + element = m_scene.getElementById(id); + if (element) + { + break; + } + } + + if (!element) + { + return; + } + + std::unique_ptr clonedElement(element->clone()); + if (pdf::PDFPageContentEditorStyleSettings::showEditElementStyleDialog(m_dataExchangeInterface->getMainWindow(), clonedElement.get())) + { + if (clonedElement->asElementEdited()) + { + pdf::PDFPageContentElementEdited* editedElement = dynamic_cast(clonedElement.get()); + if (editedElement->getElement()->asText()) + { + if (!updateTextElement(editedElement)) + { + return; + } + } + } + + m_scene.replaceElement(clonedElement.release()); + updateGraphics(); + } +} + +void EditorPlugin::onSceneEditSingleElement(pdf::PDFInteger elementId) +{ + onSceneEditElement({ elementId }); +} + +void EditorPlugin::onPenChanged(const QPen& pen) +{ + if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) + { + activeTool->setPen(pen); + } +} + +void EditorPlugin::onBrushChanged(const QBrush& brush) +{ + if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) + { + activeTool->setBrush(brush); + } +} + +void EditorPlugin::onFontChanged(const QFont& font) +{ + if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) + { + activeTool->setFont(font); + } +} + +void EditorPlugin::onAlignmentChanged(Qt::Alignment alignment) +{ + if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) + { + activeTool->setAlignment(alignment); + } +} + +void EditorPlugin::onTextAngleChanged(pdf::PDFReal angle) +{ + if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) + { + activeTool->setTextAngle(angle); + } +} + +void EditorPlugin::setActive(bool active) +{ + if (m_scene.isActive() != active) + { + // Abort active tool, if we are deactivating the plugin + if (!active) + { + if (pdf::PDFWidgetTool* tool = m_widget->getToolManager()->getActiveTool()) + { + auto it = std::find(m_tools.cbegin(), m_tools.cend(), tool); + if (it != m_tools.cend()) + { + m_widget->getToolManager()->setActiveTool(nullptr); + } + } + } + + m_scene.setActive(active); + if (!active) + { + m_scene.clear(); + m_editedPageContent.clear(); + } + else + { + updateDockWidget(); + updateEditedPages(); + } + + m_actions[Activate]->setChecked(active); + updateActions(); + + // If editor is not active, remove the widget + if (m_editorWidget && !active) + { + delete m_editorWidget; + m_editorWidget = nullptr; + } + } +} + +void EditorPlugin::onSetActive(bool active) +{ + if (m_scene.isActive() && !active && !save()) + { + updateActions(); + m_actions[Activate]->setChecked(true); + return; + } + + setActive(active); +} + +void EditorPlugin::updateActions() +{ + m_actions[Activate]->setEnabled(m_document); + + if (!m_scene.isActive() || !m_document) + { + // Inactive scene - disable all except activate action + for (QAction* action : m_actions) + { + if (action == m_actions[Activate]) + { + action->setEnabled(m_widget && !m_widget->isAnySceneActive(&m_scene)); + continue; + } + + action->setEnabled(false); + } + + return; + } + + const bool isSceneNonempty = !m_scene.isEmpty(); + + // Tool actions + for (auto actionId : { Text, FreehandCurve, AcceptMark, RejectMark, + Rectangle, RoundedRectangle, HorizontalLine, + VerticalLine, Line, Dot, SvgImage }) + { + m_actions[actionId]->setEnabled(true); + } + + // Clear action + QAction* clearAction = m_actions[Clear]; + clearAction->setEnabled(isSceneNonempty); +} + +void EditorPlugin::updateGraphics() +{ + if (m_widget) + { + m_widget->getDrawWidget()->getWidget()->update(); + } +} + +void EditorPlugin::updateDockWidget() +{ + if (m_editorWidget) + { + return; + } + + m_editorWidget = new pdf::PDFPageContentEditorWidget(m_dataExchangeInterface->getMainWindow()); + m_editorWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + m_dataExchangeInterface->getMainWindow()->addDockWidget(Qt::RightDockWidgetArea, m_editorWidget, Qt::Vertical); + m_editorWidget->setFloating(false); + m_editorWidget->setWindowTitle(tr("Editor Toolbox")); + m_editorWidget->setScene(&m_scene); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::operationTriggered, &m_scene, &pdf::PDFPageContentScene::performOperation); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::itemSelectionChangedByUser, this, &EditorPlugin::onWidgetSelectionChanged); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::editElementRequest, this, &EditorPlugin::onSceneEditSingleElement); + + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignTop))->setIcon(QIcon(":/resources/pce-align-top.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignCenterVertically))->setIcon(QIcon(":/resources/pce-align-v-center.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignBottom))->setIcon(QIcon(":/resources/pce-align-bottom.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignLeft))->setIcon(QIcon(":/resources/pce-align-left.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignCenterHorizontally))->setIcon(QIcon(":/resources/pce-align-h-center.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignRight))->setIcon(QIcon(":/resources/pce-align-right.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::SetSameHeight))->setIcon(QIcon(":/resources/pce-same-height.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::SetSameWidth))->setIcon(QIcon(":/resources/pce-same-width.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::SetSameSize))->setIcon(QIcon(":/resources/pce-same-size.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::CenterHorizontally))->setIcon(QIcon(":/resources/pce-center-h.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::CenterVertically))->setIcon(QIcon(":/resources/pce-center-v.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::CenterHorAndVert))->setIcon(QIcon(":/resources/pce-center-vh.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::LayoutVertically))->setIcon(QIcon(":/resources/pce-layout-v.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::LayoutHorizontally))->setIcon(QIcon(":/resources/pce-layout-h.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::LayoutForm))->setIcon(QIcon(":/resources/pce-layout-form.svg")); + m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::LayoutGrid))->setIcon(QIcon(":/resources/pce-layout-grid.svg")); + + for (QAction* action : m_actions) + { + m_editorWidget->addAction(action); + } + + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::penChanged, this, &EditorPlugin::onPenChanged); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::brushChanged, this, &EditorPlugin::onBrushChanged); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::fontChanged, this, &EditorPlugin::onFontChanged); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::alignmentChanged, this, &EditorPlugin::onAlignmentChanged); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::textAngleChanged, this, &EditorPlugin::onTextAngleChanged); +} + +void EditorPlugin::updateEditedPages() +{ + if (!m_scene.isActive() || m_isSaving) + { + // Editor is not active or we are saving the document + return; + } + + std::vector currentPages = m_widget->getDrawWidget()->getCurrentPages(); + for (pdf::PDFInteger pageIndex : currentPages) + { + if (m_editedPageContent.count(pageIndex)) + { + continue; + } + + const pdf::PDFPage* page = m_document->getCatalog()->getPage(pageIndex); + auto cms = m_widget->getDrawWidgetProxy()->getCMSManager()->getCurrentCMS(); + pdf::PDFPageContentEditorProcessor processor(page, + m_document, + m_widget->getDrawWidgetProxy()->getFontCache(), + cms.data(), + m_widget->getDrawWidgetProxy()->getOptionalContentActivity(), + QTransform(), + pdf::PDFMeshQualitySettings()); + + QList errors = processor.processContents(); + Q_UNUSED(errors); + + m_editedPageContent[pageIndex] = processor.takeEditedPageContent(); + + size_t elementCount = m_editedPageContent[pageIndex].getElementCount(); + for (size_t i = 0; i < elementCount; ++i) + { + pdf::PDFEditedPageContentElement* element = m_editedPageContent[pageIndex].getElement(i); + pdf::PDFPageContentElementEdited* editedElement = new pdf::PDFPageContentElementEdited(element); + editedElement->setPageIndex(pageIndex); + m_scene.addElement(editedElement); + } + } +} + +bool EditorPlugin::updateTextElement(pdf::PDFPageContentElementEdited* element) +{ + pdf::PDFPageContentElementEdited* elementEdited = dynamic_cast(element); + pdf::PDFEditedPageContentElementText* targetTextElement = elementEdited->getElement()->asText(); + + if (!targetTextElement) + { + return false; + } + + pdf::PDFDocumentModifier modifier(m_document); + pdf::PDFDocumentBuilder* builder = modifier.getBuilder(); + + if (!updatePageContent(element->getPageIndex(), { element }, builder)) + { + return false; + } + + if (modifier.finalize()) + { + pdf::PDFDocument* document = modifier.getDocument().get(); + + const pdf::PDFPage* page = document->getCatalog()->getPage(element->getPageIndex()); + auto cms = m_widget->getDrawWidgetProxy()->getCMSManager()->getCurrentCMS(); + pdf::PDFFontCache fontCache(64, 64); + pdf::PDFOptionalContentActivity activity(document, pdf::OCUsage::View, nullptr); + fontCache.setDocument(pdf::PDFModifiedDocument(document, &activity)); + pdf::PDFPageContentEditorProcessor processor(page, document, &fontCache, cms.data(), &activity, QTransform(), pdf::PDFMeshQualitySettings()); + + QList errors = processor.processContents(); + Q_UNUSED(errors); + + pdf::PDFEditedPageContent content = processor.takeEditedPageContent(); + if (content.getElementCount() == 1) + { + pdf::PDFEditedPageContentElement* sourceElement = content.getElement(0); + pdf::PDFEditedPageContentElementText* sourceElementText = sourceElement->asText(); + targetTextElement->setState(sourceElementText->getState()); + targetTextElement->setTextPath(sourceElementText->getTextPath()); + } + else + { + return false; + } + } + + return true; +} + +void EditorPlugin::onDrawSpaceChanged() +{ + updateEditedPages(); +} + +} diff --git a/Pdf4QtEditorPlugins/EditorPlugin/editorplugin.h b/Pdf4QtEditorPlugins/EditorPlugin/editorplugin.h new file mode 100644 index 00000000..394c1285 --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/editorplugin.h @@ -0,0 +1,136 @@ +// Copyright (C) 2023 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef EDITORSPLUGIN_H +#define EDITORSPLUGIN_H + +#include "pdfplugin.h" +#include "pdfpagecontentelements.h" +#include "pdfpagecontenteditortools.h" +#include "pdfpagecontenteditorprocessor.h" + +#include + +namespace pdf +{ +class PDFPageContentEditorWidget; +} + +namespace pdfplugin +{ + +class EditorPlugin : public pdf::PDFPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "PDF4QT.EditorPlugin" FILE "EditorPlugin.json") + +private: + using BaseClass = pdf::PDFPlugin; + +public: + EditorPlugin(); + + virtual void setWidget(pdf::PDFWidget* widget) override; + virtual void setDocument(const pdf::PDFModifiedDocument& document) override; + virtual std::vector getActions() const override; + virtual QString getPluginMenuName() const override; + + bool save(); + +private: + void onSceneActivityChanged(); + void onSceneChanged(bool graphicsOnly); + void onSceneSelectionChanged(); + void onWidgetSelectionChanged(); + void onToolActivityChanged(); + void onSceneEditElement(const std::set& elements); + void onSceneEditSingleElement(pdf::PDFInteger elementId); + + void onPenChanged(const QPen& pen); + void onBrushChanged(const QBrush& brush); + void onFontChanged(const QFont& font); + void onAlignmentChanged(Qt::Alignment alignment); + void onTextAngleChanged(pdf::PDFReal angle); + + enum Action + { + // Activate action + Activate, + + // Create graphics actions + Text, + FreehandCurve, + AcceptMark, + RejectMark, + Rectangle, + RoundedRectangle, + HorizontalLine, + VerticalLine, + Line, + Dot, + SvgImage, + Clear, + + LastAction + }; + + enum Tools + { + TextTool, + FreehandCurveTool, + AcceptMarkTool, + RejectMarkTool, + RectangleTool, + RoundedRectangleTool, + HorizontalLineTool, + VerticalLineTool, + LineTool, + DotTool, + ImageTool, + LastTool + }; + + void setActive(bool active); + void onSetActive(bool active); + + void updateActions(); + void updateGraphics(); + void updateDockWidget(); + void updateEditedPages(); + + bool updatePageContent(pdf::PDFInteger pageIndex, + const std::vector& elements, + pdf::PDFDocumentBuilder* builder); + bool updateTextElement(pdf::PDFPageContentElementEdited* element); + + void onDrawSpaceChanged(); + + pdf::PDFWidgetTool* getActiveTool(); + + std::array m_actions; + std::array m_tools; + pdf::PDFPageContentEditorWidget* m_editorWidget; + + pdf::PDFPageContentScene m_scene; + std::map m_editedPageContent; + bool m_sceneSelectionChangeEnabled; + bool m_isSaving; +}; + +} // namespace pdfplugin + +#endif // EDITORSPLUGIN_H diff --git a/Pdf4QtEditorPlugins/EditorPlugin/icons.qrc b/Pdf4QtEditorPlugins/EditorPlugin/icons.qrc new file mode 100644 index 00000000..51cb074b --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/icons.qrc @@ -0,0 +1,19 @@ + + + accept-mark.svg + reject-mark.svg + activate.svg + clear.svg + create-dot.svg + create-freehand-curve.svg + create-horizontal-line.svg + create-line.svg + create-no-mark.svg + create-rectangle.svg + create-rounded-rectangle.svg + create-svg-image.svg + create-text.svg + create-vertical-line.svg + create-yes-mark.svg + + diff --git a/Pdf4QtEditorPlugins/EditorPlugin/reject-mark.svg b/Pdf4QtEditorPlugins/EditorPlugin/reject-mark.svg new file mode 100644 index 00000000..017bbf4a --- /dev/null +++ b/Pdf4QtEditorPlugins/EditorPlugin/reject-mark.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.cpp b/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.cpp index 8745e83b..ead65855 100644 --- a/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.cpp +++ b/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.cpp @@ -162,6 +162,7 @@ void SignaturePlugin::setWidget(pdf::PDFWidget* widget) connect(signElectronicallyAction, &QAction::triggered, this, &SignaturePlugin::onSignElectronically); connect(signDigitallyAction, &QAction::triggered, this, &SignaturePlugin::onSignDigitally); connect(certificatesAction, &QAction::triggered, this, &SignaturePlugin::onOpenCertificatesManager); + connect(m_widget, &pdf::PDFWidget::sceneActivityChanged, this, &SignaturePlugin::onSceneActivityChanged); updateActions(); } @@ -288,6 +289,8 @@ void SignaturePlugin::onSignElectronically() Q_ASSERT(m_document); Q_ASSERT(!m_scene.isEmpty()); + pdf::PDFColorConvertor convertor; + if (QMessageBox::question(m_dataExchangeInterface->getMainWindow(), tr("Confirm Signature"), tr("Document will be signed electronically. Do you want to continue?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { pdf::PDFDocumentModifier modifier(m_document); @@ -302,7 +305,7 @@ void SignaturePlugin::onSignElectronically() QPainter* painter = pageContentStreamBuilder.begin(page->getPageReference()); QList errors; pdf::PDFTextLayoutGetter nullGetter(nullptr, pageIndex); - m_scene.drawElements(painter, pageIndex, nullGetter, QTransform(), nullptr, errors); + m_scene.drawElements(painter, pageIndex, nullGetter, QTransform(), nullptr, convertor, errors); pageContentStreamBuilder.end(painter); modifier.markPageContentsChanged(); } @@ -369,12 +372,13 @@ void SignaturePlugin::onSignDigitally() Q_ASSERT(!m_scene.isEmpty()); const pdf::PDFInteger pageIndex = *m_scene.getPageIndices().begin(); const pdf::PDFPage* page = catalog->getPage(pageIndex); + pdf::PDFColorConvertor convertor; pdf::PDFContentStreamBuilder contentBuilder(page->getMediaBox().size(), pdf::PDFContentStreamBuilder::CoordinateSystem::PDF); QPainter* painter = contentBuilder.begin(); QList errors; pdf::PDFTextLayoutGetter nullGetter(nullptr, pageIndex); - m_scene.drawPage(painter, pageIndex, nullptr, nullGetter, QTransform(), errors); + m_scene.drawPage(painter, pageIndex, nullptr, nullGetter, QTransform(), convertor, errors); pdf::PDFContentStreamBuilder::ContentStream contentStream = contentBuilder.end(painter); QRectF boundingRect = m_scene.getBoundingBox(pageIndex); @@ -505,6 +509,11 @@ void SignaturePlugin::onOpenCertificatesManager() dialog.exec(); } +void SignaturePlugin::onSceneActivityChanged() +{ + updateActions(); +} + void SignaturePlugin::onPenChanged(const QPen& pen) { if (pdf::PDFCreatePCElementTool* activeTool = qobject_cast(getActiveTool())) @@ -574,6 +583,13 @@ void SignaturePlugin::setActive(bool active) m_actions[Activate]->setChecked(active); updateActions(); + + // If editor is not active, remove the widget + if (m_editorWidget && !active) + { + delete m_editorWidget; + m_editorWidget = nullptr; + } } } @@ -586,6 +602,11 @@ void SignaturePlugin::updateActions() // Inactive scene - disable all except activate action and certificates for (QAction* action : m_actions) { + if (action == m_actions[Activate]) + { + action->setEnabled(m_widget && !m_widget->isAnySceneActive(&m_scene)); + } + if (action == m_actions[Activate] || action == m_actions[Certificates]) { @@ -642,6 +663,7 @@ void SignaturePlugin::updateDockWidget() m_editorWidget->setScene(&m_scene); connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::operationTriggered, &m_scene, &pdf::PDFPageContentScene::performOperation); connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::itemSelectionChangedByUser, this, &SignaturePlugin::onWidgetSelectionChanged); + connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::editElementRequest, this, &SignaturePlugin::onSceneEditSingleElement); m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignTop))->setIcon(QIcon(":/resources/pce-align-top.svg")); m_editorWidget->getToolButtonForOperation(static_cast(pdf::PDFPageContentElementManipulator::Operation::AlignCenterVertically))->setIcon(QIcon(":/resources/pce-align-v-center.svg")); @@ -672,4 +694,9 @@ void SignaturePlugin::updateDockWidget() connect(m_editorWidget, &pdf::PDFPageContentEditorWidget::textAngleChanged, this, &SignaturePlugin::onTextAngleChanged); } +void SignaturePlugin::onSceneEditSingleElement(pdf::PDFInteger elementId) +{ + onSceneEditElement({ elementId }); +} + } diff --git a/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.h b/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.h index ab9d2252..45167c38 100644 --- a/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.h +++ b/Pdf4QtEditorPlugins/SignaturePlugin/signatureplugin.h @@ -57,12 +57,14 @@ class SignaturePlugin : public pdf::PDFPlugin void onSignElectronically(); void onSignDigitally(); void onOpenCertificatesManager(); + void onSceneActivityChanged(); void onPenChanged(const QPen& pen); void onBrushChanged(const QBrush& brush); void onFontChanged(const QFont& font); void onAlignmentChanged(Qt::Alignment alignment); void onTextAngleChanged(pdf::PDFReal angle); + void onSceneEditSingleElement(pdf::PDFInteger elementId); enum Action { diff --git a/Pdf4QtLibCore/CMakeLists.txt b/Pdf4QtLibCore/CMakeLists.txt index 48274d83..e930b234 100644 --- a/Pdf4QtLibCore/CMakeLists.txt +++ b/Pdf4QtLibCore/CMakeLists.txt @@ -148,6 +148,10 @@ add_library(Pdf4QtLibCore SHARED sources/pdfcertificatestore.cpp sources/pdfblpainter.h sources/pdfblpainter.cpp + sources/pdfpagecontenteditorprocessor.h + sources/pdfpagecontenteditorprocessor.cpp + sources/pdfpagecontenteditorcontentstreambuilder.h + sources/pdfpagecontenteditorcontentstreambuilder.cpp ) include(GenerateExportHeader) diff --git a/Pdf4QtLibCore/sources/pdfblendfunction.cpp b/Pdf4QtLibCore/sources/pdfblendfunction.cpp index af1343a0..f16fa951 100644 --- a/Pdf4QtLibCore/sources/pdfblendfunction.cpp +++ b/Pdf4QtLibCore/sources/pdfblendfunction.cpp @@ -168,6 +168,19 @@ QPainter::CompositionMode PDFBlendModeInfo::getCompositionModeFromBlendMode(Blen return QPainter::CompositionMode_SourceOver; } +BlendMode PDFBlendModeInfo::getBlendModeFromCompositionMode(QPainter::CompositionMode mode) +{ + for (BlendMode blendMode : getBlendModes()) + { + if (mode == getCompositionModeFromBlendMode(blendMode)) + { + return blendMode; + } + } + + return BlendMode::Normal; +} + QString PDFBlendModeInfo::getBlendModeName(BlendMode mode) { for (const std::pair& info : BLEND_MODE_INFOS) diff --git a/Pdf4QtLibCore/sources/pdfblendfunction.h b/Pdf4QtLibCore/sources/pdfblendfunction.h index 3f6ede50..590c2133 100644 --- a/Pdf4QtLibCore/sources/pdfblendfunction.h +++ b/Pdf4QtLibCore/sources/pdfblendfunction.h @@ -86,6 +86,9 @@ class PDFBlendModeInfo /// \param mode Blend mode static QPainter::CompositionMode getCompositionModeFromBlendMode(BlendMode mode); + /// Returns blend mode from QPainter's composition mode. + static BlendMode getBlendModeFromCompositionMode(QPainter::CompositionMode mode); + /// Returns blend mode name /// \param mode Blend mode static QString getBlendModeName(BlendMode mode); diff --git a/Pdf4QtLibCore/sources/pdfblpainter.cpp b/Pdf4QtLibCore/sources/pdfblpainter.cpp index 8ae39e97..567a076d 100644 --- a/Pdf4QtLibCore/sources/pdfblpainter.cpp +++ b/Pdf4QtLibCore/sources/pdfblpainter.cpp @@ -971,6 +971,14 @@ void PDFBLPaintEngine::setBLPen(BLContext& context, const QPen& pen) break; } + case Qt::CustomDashLine: + { + auto dashPattern = pen.dashPattern(); + strokeOptions.dashArray.assignData(dashPattern.data(), dashPattern.size()); + strokeOptions.dashOffset = pen.dashOffset(); + break; + } + default: break; } diff --git a/Pdf4QtLibCore/sources/pdfcolorconvertor.cpp b/Pdf4QtLibCore/sources/pdfcolorconvertor.cpp index c15aa7f2..befa8154 100644 --- a/Pdf4QtLibCore/sources/pdfcolorconvertor.cpp +++ b/Pdf4QtLibCore/sources/pdfcolorconvertor.cpp @@ -225,6 +225,20 @@ QColor PDFColorConvertor::getForegroundColor() const return m_foregroundColor; } +QPen PDFColorConvertor::convert(const QPen& pen, bool background, bool foreground) const +{ + QPen newPen = pen; + newPen.setColor(convert(pen.color(), background, foreground)); + return newPen; +} + +QBrush PDFColorConvertor::convert(const QBrush& brush, bool background, bool foreground) const +{ + QBrush newBrush = brush; + newBrush.setColor(convert(brush.color(), background, foreground)); + return newBrush; +} + QColor PDFColorConvertor::getBackgroundColor() const { return m_backgroundColor; diff --git a/Pdf4QtLibCore/sources/pdfcolorconvertor.h b/Pdf4QtLibCore/sources/pdfcolorconvertor.h index 5364251d..a6fa5a8b 100644 --- a/Pdf4QtLibCore/sources/pdfcolorconvertor.h +++ b/Pdf4QtLibCore/sources/pdfcolorconvertor.h @@ -20,6 +20,8 @@ #include "pdfglobal.h" +#include +#include #include #include @@ -103,6 +105,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFColorConvertor QColor getBackgroundColor() const; QColor getForegroundColor() const; + QPen convert(const QPen& pen, bool background = false, bool foreground = true) const; + QBrush convert(const QBrush& brush, bool background = false, bool foreground = true) const; + private: /// Correct lightness using sigmoid function /// \return Adjusted lightness normalized in range [0.0, 1.0] diff --git a/Pdf4QtLibCore/sources/pdfdocumentbuilder.cpp b/Pdf4QtLibCore/sources/pdfdocumentbuilder.cpp index 3f679bfc..be73df72 100644 --- a/Pdf4QtLibCore/sources/pdfdocumentbuilder.cpp +++ b/Pdf4QtLibCore/sources/pdfdocumentbuilder.cpp @@ -285,6 +285,12 @@ PDFObjectFactory& PDFObjectFactory::operator<<(AnnotationBorderStyle style) return *this; } +PDFObjectFactory& PDFObjectFactory::operator<<(PDFDictionary dictionary) +{ + *this << PDFObject::createDictionary(std::make_shared(std::move(dictionary))); + return *this; +} + PDFObjectFactory& PDFObjectFactory::operator<<(const QDateTime& dateTime) { addObject(PDFObject::createString(PDFEncoding::convertDateTimeToString(dateTime))); @@ -708,6 +714,19 @@ PDFDocument PDFDocumentBuilder::build() return PDFDocument(PDFObjectStorage(m_storage), m_version, QByteArray()); } +void PDFDocumentBuilder::replaceObjectsByReferences(PDFDictionary& dictionary) +{ + for (size_t i = 0; i < dictionary.getCount(); ++i) + { + const PDFObject& object = dictionary.getValue(i); + if (!object.isReference()) + { + auto key = dictionary.getKey(i); + dictionary.setEntry(key, PDFObject::createReference(addObject(object))); + } + } +} + QByteArray PDFDocumentBuilder::getDecodedStream(const PDFStream* stream) const { return m_storage.getDecodedStream(stream); diff --git a/Pdf4QtLibCore/sources/pdfdocumentbuilder.h b/Pdf4QtLibCore/sources/pdfdocumentbuilder.h index 3a5de42f..276e4b70 100644 --- a/Pdf4QtLibCore/sources/pdfdocumentbuilder.h +++ b/Pdf4QtLibCore/sources/pdfdocumentbuilder.h @@ -133,6 +133,7 @@ class PDF4QTLIBCORESHARED_EXPORT PDFObjectFactory PDFObjectFactory& operator<<(const PDFDestination& destination); PDFObjectFactory& operator<<(PageRotation pageRotation); PDFObjectFactory& operator<<(PDFFormSubmitFlags flags); + PDFObjectFactory& operator<<(PDFDictionary dictionary); /// Treat containers - write them as array template()))> @@ -343,6 +344,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFDocumentBuilder /// if document being built was invalid. PDFDocument build(); + /// Replaces all objects by references in the dictionary + void replaceObjectsByReferences(PDFDictionary& dictionary); + /// If object is reference, the dereference attempt is performed /// and object is returned. If it is not a reference, then self /// is returned. If dereference attempt fails, then null object diff --git a/Pdf4QtLibCore/sources/pdffont.cpp b/Pdf4QtLibCore/sources/pdffont.cpp index d4618493..164e711b 100644 --- a/Pdf4QtLibCore/sources/pdffont.cpp +++ b/Pdf4QtLibCore/sources/pdffont.cpp @@ -564,9 +564,10 @@ QString PDFSystemFontInfoStorage::getFontPostscriptName(QString fontName) return fontName.remove(QChar(' ')).remove(QChar('-')).remove(QChar(',')).trimmed(); } -PDFFont::PDFFont(CIDSystemInfo CIDSystemInfo, FontDescriptor fontDescriptor) : +PDFFont::PDFFont(CIDSystemInfo CIDSystemInfo, QByteArray fontId, FontDescriptor fontDescriptor) : m_CIDSystemInfo(qMove(CIDSystemInfo)), - m_fontDescriptor(qMove(fontDescriptor)) + m_fontDescriptor(qMove(fontDescriptor)), + m_fontId(qMove(fontId)) { } @@ -749,7 +750,7 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ if (glyphIndex) { const Glyph& glyph = getGlyph(glyphIndex); - textSequence.items.emplace_back(&glyph.glyph, (*encoding)[static_cast(byteArray[i])], glyph.advance); + textSequence.items.emplace_back(&glyph.glyph, (*encoding)[static_cast(byteArray[i])], glyph.advance, static_cast(byteArray[i])); } else { @@ -757,7 +758,7 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ if (glyphWidth > 0) { const QPainterPath* nullpath = nullptr; - textSequence.items.emplace_back(nullpath, QChar(), glyphWidth * m_pixelSize * FONT_WIDTH_MULTIPLIER); + textSequence.items.emplace_back(nullpath, QChar(), glyphWidth * m_pixelSize * FONT_WIDTH_MULTIPLIER, static_cast(byteArray[i])); } } } @@ -784,7 +785,7 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ { QChar character = toUnicode->getToUnicode(cid); const Glyph& glyph = getGlyph(glyphIndex); - textSequence.items.emplace_back(&glyph.glyph, character, glyph.advance); + textSequence.items.emplace_back(&glyph.glyph, character, glyph.advance, cid); } else { @@ -799,7 +800,7 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ // We do not multiply advance with font size and FONT_WIDTH_MULTIPLIER, because in the code, // "advance" is treated as in font space. const QPainterPath* nullpath = nullptr; - textSequence.items.emplace_back(nullpath, QChar(), -glyphWidth); + textSequence.items.emplace_back(nullpath, QChar(), -glyphWidth, cid); } } } @@ -1284,7 +1285,53 @@ CIDSystemInfo PDFFont::readCIDSystemInfo(const PDFObject& cidSystemInfoObject, c return cidSystemInfo; } -PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* document) +QByteArray PDFFont::getFontId() const +{ + return m_fontId; +} + +PDFEncodedText PDFFont::encodeText(const QString& text) const +{ + PDFEncodedText result; + result.isValid = true; + + const PDFFontCMap* cmap = getCMap(); + const PDFFontCMap* toUnicode = getToUnicode(); + + if (!cmap || !toUnicode) + { + result.errorString = PDFTranslationContext::tr("Invalid font encoding."); + return result; + } + + for (const QChar& character : text) + { + CID cid = toUnicode->getFromUnicode(character); + if (cid != CID()) + { + QByteArray encoded = cmap->encode(cid); + if (!encoded.isEmpty()) + { + result.encodedText.append(encoded); + result.errorString += "_"; + } + else + { + result.isValid = false; + result.errorString += character; + } + } + else + { + result.isValid = false; + result.errorString += character; + } + } + + return result; +} + +PDFFontPointer PDFFont::createFont(const PDFObject& object, QByteArray fontId, const PDFDocument* document) { const PDFObject& dereferencedFontDictionary = document->getObject(object); if (!dereferencedFontDictionary.isDictionary()) @@ -1763,7 +1810,7 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d toUnicodeCMap = PDFFontCMap::createFromData(decodedStream); } - return PDFFontPointer(new PDFType0Font(qMove(cidSystemInfo), qMove(fontDescriptor), qMove(cmap), qMove(toUnicodeCMap), qMove(cidToGidMapper), defaultWidth, qMove(advances))); + return PDFFontPointer(new PDFType0Font(qMove(cidSystemInfo), qMove(fontId), qMove(fontDescriptor), qMove(cmap), qMove(toUnicodeCMap), qMove(cidToGidMapper), defaultWidth, qMove(advances))); } case FontType::Type3: @@ -1853,7 +1900,7 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d } std::vector widthsF3 = fontLoader.readNumberArrayFromDictionary(fontDictionary, "Widths"); - return PDFFontPointer(new PDFType3Font(qMove(fontDescriptor), firstCharF3, lastCharF3, fontMatrix, qMove(characterContentStreams), qMove(widthsF3), document->getObject(fontDictionary->get("Resources")), qMove(toUnicodeCMap))); + return PDFFontPointer(new PDFType3Font(qMove(fontDescriptor), qMove(fontId), firstCharF3, lastCharF3, fontMatrix, qMove(characterContentStreams), qMove(widthsF3), document->getObject(fontDictionary->get("Resources")), qMove(toUnicodeCMap))); } default: @@ -1867,10 +1914,10 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d { case FontType::Type1: case FontType::MMType1: - return PDFFontPointer(new PDFType1Font(fontType, qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont, glyphIndexArray)); + return PDFFontPointer(new PDFType1Font(fontType, qMove(fontId), qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont, glyphIndexArray)); case FontType::TrueType: - return PDFFontPointer(new PDFTrueTypeFont(qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, glyphIndexArray)); + return PDFFontPointer(new PDFTrueTypeFont(qMove(cidSystemInfo), qMove(fontId), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, glyphIndexArray)); default: { @@ -1883,6 +1930,7 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d } PDFSimpleFont::PDFSimpleFont(CIDSystemInfo cidSystemInfo, + QByteArray fontId, FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, @@ -1892,7 +1940,7 @@ PDFSimpleFont::PDFSimpleFont(CIDSystemInfo cidSystemInfo, PDFEncoding::Encoding encodingType, encoding::EncodingTable encoding, GlyphIndices glyphIndices) : - PDFFont(qMove(cidSystemInfo), qMove(fontDescriptor)), + PDFFont(qMove(cidSystemInfo), qMove(fontId), qMove(fontDescriptor)), m_name(qMove(name)), m_baseFont(qMove(baseFont)), m_firstChar(firstChar), @@ -1922,6 +1970,45 @@ PDFInteger PDFSimpleFont::getGlyphAdvance(size_t index) const return 0; } +PDFEncodedText PDFSimpleFont::encodeText(const QString& text) const +{ + PDFEncodedText result; + result.isValid = true; + + const encoding::EncodingTable* encodingTable = getEncoding(); + + for (const QChar& character : text) + { + ushort unicode = character.unicode(); + unsigned char converted = 0; + + bool isFound = false; + for (size_t i = 0; i < encodingTable->size(); ++i) + { + if (unicode == (*encodingTable)[static_cast(i)] && + m_glyphIndices[i] != GID()) + { + isFound = true; + converted = static_cast(i); + break; + } + } + + if (isFound) + { + result.encodedText.append(static_cast(converted)); + result.errorString += "_"; + } + else + { + result.isValid = false; + result.errorString += character; + } + } + + return result; +} + void PDFSimpleFont::dumpFontToTreeItem(ITreeFactory* treeFactory) const { BaseClass::dumpFontToTreeItem(treeFactory); @@ -1976,6 +2063,7 @@ void PDFSimpleFont::dumpFontToTreeItem(ITreeFactory* treeFactory) const } PDFType1Font::PDFType1Font(FontType fontType, + QByteArray fontId, CIDSystemInfo cidSystemInfo, FontDescriptor fontDescriptor, QByteArray name, @@ -1987,7 +2075,7 @@ PDFType1Font::PDFType1Font(FontType fontType, encoding::EncodingTable encoding, StandardFontType standardFontType, GlyphIndices glyphIndices) : - PDFSimpleFont(qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding, glyphIndices), + PDFSimpleFont(qMove(cidSystemInfo), qMove(fontId), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding, glyphIndices), m_fontType(fontType), m_standardFontType(standardFontType) { @@ -2068,7 +2156,7 @@ void PDFFontCache::setDocument(const PDFModifiedDocument& document) } } -PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject) const +PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject, const QByteArray& fontId) const { if (fontObject.isReference()) { @@ -2081,7 +2169,7 @@ PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject) const if (it == m_fontCache.cend()) { // We must create the font - PDFFontPointer font = PDFFont::createFont(fontObject, m_document); + PDFFontPointer font = PDFFont::createFont(fontObject, fontId, m_document); if (m_fontCacheShrinkDisabledObjects.empty() && m_fontCache.size() >= m_fontCacheLimit) { @@ -2096,7 +2184,7 @@ PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject) const else { // Object is not a reference. Create font directly and return it. - return PDFFont::createFont(fontObject, m_document); + return PDFFont::createFont(fontObject, fontId, m_document); } } @@ -2488,6 +2576,35 @@ std::vector PDFFontCMap::interpret(const QByteArray& byteArray) const return result; } +QByteArray PDFFontCMap::encode(CID cid) const +{ + QByteArray byteArray; + + for (const auto& entry : m_entries) + { + unsigned int minPossibleValue = entry.from + entry.cid; + unsigned int maxPossibleValue = entry.to + entry.cid; + + if (cid >= minPossibleValue && cid <= maxPossibleValue) + { + // Calculate the original value from cid + unsigned int value = cid - entry.cid + entry.from; + + byteArray.reserve(entry.byteCount); + + // Construct byte array for this value based on the entry's byteCount + for (int i = entry.byteCount - 1; i >= 0; --i) + { + byteArray.append(static_cast((value >> (8 * i)) & 0xFF)); + } + + break; + } + } + + return byteArray; +} + QChar PDFFontCMap::getToUnicode(CID cid) const { if (isValid()) @@ -2504,6 +2621,29 @@ QChar PDFFontCMap::getToUnicode(CID cid) const return QChar(); } +CID PDFFontCMap::getFromUnicode(QChar character) const +{ + if (!character.isNull()) + { + char16_t ucs4 = character.unicode(); + const CID unicodeCID = ucs4; + + for (const Entry& entry : m_entries) + { + const CID minUnicodeCID = entry.cid; + const CID maxUnicodeCID = (entry.to - entry.from) + entry.cid; + + if (unicodeCID >= minUnicodeCID && unicodeCID <= maxUnicodeCID) + { + const CID cid = unicodeCID + entry.from - entry.cid; + return cid; + } + } + } + + return CID(); +} + PDFFontCMap::PDFFontCMap(Entries&& entries, bool vertical) : m_entries(qMove(entries)), m_maxKeyLength(0), @@ -2611,6 +2751,7 @@ PDFReal PDFType0Font::getGlyphAdvance(CID cid) const } PDFType3Font::PDFType3Font(FontDescriptor fontDescriptor, + QByteArray fontId, int firstCharacterIndex, int lastCharacterIndex, QTransform fontMatrix, @@ -2618,7 +2759,7 @@ PDFType3Font::PDFType3Font(FontDescriptor fontDescriptor, std::vector&& widths, const PDFObject& resources, PDFFontCMap toUnicode) : - PDFFont(CIDSystemInfo(), qMove(fontDescriptor)), + PDFFont(CIDSystemInfo(), qMove(fontId), qMove(fontDescriptor)), m_firstCharacterIndex(firstCharacterIndex), m_lastCharacterIndex(lastCharacterIndex), m_fontMatrix(fontMatrix), @@ -2680,7 +2821,7 @@ void PDFRealizedType3FontImpl::fillTextSequence(const QByteArray& byteArray, Tex if (contentStream) { - textSequence.items.emplace_back(contentStream, character, width); + textSequence.items.emplace_back(contentStream, character, width, index); } else { diff --git a/Pdf4QtLibCore/sources/pdffont.h b/Pdf4QtLibCore/sources/pdffont.h index 1b12fb84..716ff814 100644 --- a/Pdf4QtLibCore/sources/pdffont.h +++ b/Pdf4QtLibCore/sources/pdffont.h @@ -70,9 +70,9 @@ class ITreeFactory struct TextSequenceItem { inline explicit TextSequenceItem() = default; - inline explicit TextSequenceItem(const QPainterPath* glyph, QChar character, PDFReal advance) : glyph(glyph), character(character), advance(advance) { } + inline explicit TextSequenceItem(const QPainterPath* glyph, QChar character, PDFReal advance, CID cid) : glyph(glyph), character(character), advance(advance), cid(cid) { } inline explicit TextSequenceItem(PDFReal advance) : character(), advance(advance) { } - inline explicit TextSequenceItem(const QByteArray* characterContentStream, QChar character, PDFReal advance) : characterContentStream(characterContentStream), character(character), advance(advance) { } + inline explicit TextSequenceItem(const QByteArray* characterContentStream, QChar character, PDFReal advance, uint cid) : characterContentStream(characterContentStream), character(character), advance(advance), cid(cid) { } inline bool isContentStream() const { return characterContentStream; } inline bool isCharacter() const { return glyph; } @@ -83,6 +83,7 @@ struct TextSequenceItem const QByteArray* characterContentStream = nullptr; QChar character; PDFReal advance = 0; + CID cid = 0; }; struct TextSequence @@ -290,11 +291,18 @@ class PDF4QTLIBCORESHARED_EXPORT PDFRealizedFont IRealizedFontImpl* m_impl; }; +struct PDFEncodedText +{ + QByteArray encodedText; + QString errorString; + bool isValid = false; +}; + /// Base class representing font in the PDF file class PDF4QTLIBCORESHARED_EXPORT PDFFont { public: - explicit PDFFont(CIDSystemInfo CIDSystemInfo, FontDescriptor fontDescriptor); + explicit PDFFont(CIDSystemInfo CIDSystemInfo, QByteArray fontId, FontDescriptor fontDescriptor); virtual ~PDFFont() = default; /// Returns the font type @@ -317,8 +325,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFFont /// Creates font from the object. If font can't be created, exception is thrown. /// \param object Font dictionary + /// \param fontId Font ID /// \param document Document - static PDFFontPointer createFont(const PDFObject& object, const PDFDocument* document); + static PDFFontPointer createFont(const PDFObject& object, QByteArray fontId, const PDFDocument* document); /// Tries to read font descriptor from the object /// \param fontDescriptorObject Font descriptor dictionary @@ -330,9 +339,16 @@ class PDF4QTLIBCORESHARED_EXPORT PDFFont /// \param document Document static CIDSystemInfo readCIDSystemInfo(const PDFObject& cidSystemInfoObject, const PDFDocument* document); + /// Returns font id from the font dictionary + QByteArray getFontId() const; + + /// Encodes text into font encoding + virtual PDFEncodedText encodeText(const QString& text) const; + protected: CIDSystemInfo m_CIDSystemInfo; FontDescriptor m_fontDescriptor; + QByteArray m_fontId; }; /// Simple font, see PDF reference 1.7, chapter 5.5. Simple fonts have encoding table, @@ -343,6 +359,7 @@ class PDFSimpleFont : public PDFFont public: explicit PDFSimpleFont(CIDSystemInfo cidSystemInfo, + QByteArray fontId, FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, @@ -361,6 +378,8 @@ class PDFSimpleFont : public PDFFont /// Returns the glyph advance (or zero, if glyph advance is invalid) PDFInteger getGlyphAdvance(size_t index) const; + virtual PDFEncodedText encodeText(const QString& text) const override; + virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const override; protected: @@ -380,6 +399,7 @@ class PDFType1Font : public PDFSimpleFont public: explicit PDFType1Font(FontType fontType, + QByteArray fontId, CIDSystemInfo cidSystemInfo, FontDescriptor fontDescriptor, QByteArray name, @@ -433,7 +453,8 @@ class PDF4QTLIBCORESHARED_EXPORT PDFFontCache /// Retrieves font from the cache. If font can't be accessed or created, /// then exception is thrown. /// \param fontObject Font object - PDFFontPointer getFont(const PDFObject& fontObject) const; + /// \param fontId Font identification in resource dictionary + PDFFontPointer getFont(const PDFObject& fontObject, const QByteArray& fontId) const; /// Retrieves realized font from the cache. If realized font can't be accessed or created, /// then exception is thrown. @@ -547,9 +568,15 @@ class PDF4QTLIBCORESHARED_EXPORT PDFFontCMap /// Converts byte array to array of CIDs std::vector interpret(const QByteArray& byteArray) const; + /// Encodes character to byte array + QByteArray encode(CID cid) const; + /// Converts CID to QChar, use only on ToUnicode CMaps QChar getToUnicode(CID cid) const; + /// Converts QChar to CID, use only on ToUnicode CMaps + CID getFromUnicode(QChar character) const; + private: struct Entry @@ -599,6 +626,7 @@ class PDFType3Font : public PDFFont { public: explicit PDFType3Font(FontDescriptor fontDescriptor, + QByteArray fontId, int firstCharacterIndex, int lastCharacterIndex, QTransform fontMatrix, @@ -640,8 +668,15 @@ class PDFType3Font : public PDFFont class PDFType0Font : public PDFFont { public: - explicit inline PDFType0Font(CIDSystemInfo cidSystemInfo, FontDescriptor fontDescriptor, PDFFontCMap cmap, PDFFontCMap toUnicode, PDFCIDtoGIDMapper mapper, PDFReal defaultAdvance, std::unordered_map advances) : - PDFFont(qMove(cidSystemInfo), qMove(fontDescriptor)), + explicit inline PDFType0Font(CIDSystemInfo cidSystemInfo, + QByteArray fontId, + FontDescriptor fontDescriptor, + PDFFontCMap cmap, + PDFFontCMap toUnicode, + PDFCIDtoGIDMapper mapper, + PDFReal defaultAdvance, + std::unordered_map advances) : + PDFFont(qMove(cidSystemInfo), qMove(fontId), qMove(fontDescriptor)), m_cmap(qMove(cmap)), m_toUnicode(qMove(toUnicode)), m_mapper(qMove(mapper)), diff --git a/Pdf4QtLibCore/sources/pdfobject.cpp b/Pdf4QtLibCore/sources/pdfobject.cpp index eb009e14..de892067 100644 --- a/Pdf4QtLibCore/sources/pdfobject.cpp +++ b/Pdf4QtLibCore/sources/pdfobject.cpp @@ -246,8 +246,13 @@ void PDFArray::optimize() bool PDFDictionary::equals(const PDFObjectContent* other) const { Q_ASSERT(dynamic_cast(other)); - const PDFDictionary* otherStream = static_cast(other); - return m_dictionary == otherStream->m_dictionary; + const PDFDictionary* otherDictionary = static_cast(other); + return m_dictionary == otherDictionary->m_dictionary; +} + +bool PDFDictionary::operator==(const PDFDictionary& other) const +{ + return m_dictionary == other.m_dictionary; } const PDFObject& PDFDictionary::get(const QByteArray& key) const diff --git a/Pdf4QtLibCore/sources/pdfobject.h b/Pdf4QtLibCore/sources/pdfobject.h index b6f3f018..a55a27c3 100644 --- a/Pdf4QtLibCore/sources/pdfobject.h +++ b/Pdf4QtLibCore/sources/pdfobject.h @@ -364,6 +364,8 @@ class PDF4QTLIBCORESHARED_EXPORT PDFDictionary : public PDFObjectContent virtual bool equals(const PDFObjectContent* other) const override; + bool operator==(const PDFDictionary&other) const; + /// Returns object for the key. If key is not found in the dictionary, /// then valid reference to the null object is returned. /// \param key Key @@ -425,6 +427,8 @@ class PDF4QTLIBCORESHARED_EXPORT PDFDictionary : public PDFObjectContent /// Removes null objects from dictionary void removeNullObjects(); + bool isEmpty() const { return getCount() == 0; } + /// Optimizes the dictionary for memory consumption virtual void optimize() override; diff --git a/Pdf4QtLibCore/sources/pdfpagecontenteditorcontentstreambuilder.cpp b/Pdf4QtLibCore/sources/pdfpagecontenteditorcontentstreambuilder.cpp new file mode 100644 index 00000000..86dd3d48 --- /dev/null +++ b/Pdf4QtLibCore/sources/pdfpagecontenteditorcontentstreambuilder.cpp @@ -0,0 +1,1074 @@ +// Copyright (C) 2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "pdfpagecontenteditorcontentstreambuilder.h" +#include "pdfdocumentbuilder.h" +#include "pdfobject.h" +#include "pdfstreamfilters.h" +#include "pdfpainterutils.h" + +#include +#include +#include +#include + +namespace pdf +{ + +class PDFContentEditorPaintEngine : public QPaintEngine +{ +public: + PDFContentEditorPaintEngine(PDFPageContentEditorContentStreamBuilder* builder) : + QPaintEngine(PrimitiveTransform | AlphaBlend | PorterDuff | PainterPaths | ConstantOpacity | BlendModes | PaintOutsidePaintEvent), + m_builder(builder) + { + + } + + virtual Type type() const override { return User; } + + virtual bool begin(QPaintDevice*) override; + virtual bool end() override; + + virtual void updateState(const QPaintEngineState& state) override; + virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) override; + + virtual void drawPath(const QPainterPath& path); + virtual void drawPolygon(const QPointF* points, int pointCount, PolygonDrawMode mode); + +private: + PDFPageContentProcessorState m_state; + PDFPageContentEditorContentStreamBuilder* m_builder = nullptr; + bool m_isFillActive = false; + bool m_isStrokeActive = false; +}; + +bool PDFContentEditorPaintEngine::begin(QPaintDevice*) +{ + return !isActive(); +} + +bool PDFContentEditorPaintEngine::end() +{ + return true; +} + +void PDFContentEditorPaintEngine::updateState(const QPaintEngineState& newState) +{ + QPaintEngine::DirtyFlags stateFlags = newState.state(); + + if (stateFlags.testFlag(QPaintEngine::DirtyPen)) + { + PDFPainterHelper::applyPenToGraphicState(&m_state, newState.pen()); + m_isStrokeActive = newState.pen().style() != Qt::NoPen; + } + + if (stateFlags.testFlag(QPaintEngine::DirtyBrush)) + { + PDFPainterHelper::applyBrushToGraphicState(&m_state, newState.brush()); + m_isFillActive = newState.brush().style() != Qt::NoBrush; + } + + if (stateFlags.testFlag(QPaintEngine::DirtyTransform)) + { + m_state.setCurrentTransformationMatrix(newState.transform()); + } + + if (stateFlags.testFlag(QPaintEngine::DirtyCompositionMode)) + { + m_state.setBlendMode(PDFBlendModeInfo::getBlendModeFromCompositionMode(newState.compositionMode())); + } + + if (stateFlags.testFlag(QPaintEngine::DirtyOpacity)) + { + m_state.setAlphaFilling(newState.opacity()); + m_state.setAlphaStroking(newState.opacity()); + } +} + +void PDFContentEditorPaintEngine::drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) +{ + QPixmap pixmap = pm.copy(sr.toRect()); + m_builder->writeImage(pixmap.toImage(), m_state.getCurrentTransformationMatrix(), r); +} + +void PDFContentEditorPaintEngine::drawPath(const QPainterPath& path) +{ + m_builder->writeStyledPath(path, m_state, m_isStrokeActive, m_isFillActive); +} + +void PDFContentEditorPaintEngine::drawPolygon(const QPointF* points, + int pointCount, + PolygonDrawMode mode) +{ + bool isStroking = m_isStrokeActive; + bool isFilling = m_isFillActive && mode != PolylineMode; + + QPolygonF polygon; + for (int i = 0; i < pointCount; ++i) + { + polygon << points[i]; + } + + QPainterPath path; + path.addPolygon(polygon); + + Qt::FillRule fillRule = Qt::OddEvenFill; + switch (mode) + { + case QPaintEngine::OddEvenMode: + fillRule = Qt::OddEvenFill; + break; + case QPaintEngine::WindingMode: + fillRule = Qt::WindingFill; + break; + case QPaintEngine::ConvexMode: + break; + case QPaintEngine::PolylineMode: + break; + } + + path.setFillRule(fillRule); + + m_builder->writeStyledPath(path, m_state, isStroking, isFilling); +} + +PDFContentEditorPaintDevice::PDFContentEditorPaintDevice(PDFPageContentEditorContentStreamBuilder* builder, QRectF mediaRect, QRectF mediaRectMM) : + m_paintEngine(new PDFContentEditorPaintEngine(builder)), + m_mediaRect(mediaRect), + m_mediaRectMM(mediaRectMM) +{ + +} + +int PDFContentEditorPaintDevice::metric(PaintDeviceMetric metric) const +{ + switch (metric) + { + case QPaintDevice::PdmWidth: + return m_mediaRect.width(); + case QPaintDevice::PdmHeight: + return m_mediaRect.height(); + case QPaintDevice::PdmWidthMM: + return m_mediaRectMM.width(); + case QPaintDevice::PdmHeightMM: + return m_mediaRectMM.height(); + case QPaintDevice::PdmNumColors: + return INT_MAX; + case QPaintDevice::PdmDepth: + return 8; + case QPaintDevice::PdmDpiX: + case QPaintDevice::PdmPhysicalDpiX: + return m_mediaRect.width() * 25.4 / m_mediaRectMM.width(); + case QPaintDevice::PdmDpiY: + case QPaintDevice::PdmPhysicalDpiY: + return m_mediaRect.height() * 25.4 / m_mediaRectMM.height(); + case QPaintDevice::PdmDevicePixelRatio: + case QPaintDevice::PdmDevicePixelRatioScaled: + return 1.0; + default: + Q_ASSERT(false); + break; + } + + return 0; +} + +PDFContentEditorPaintDevice::~PDFContentEditorPaintDevice() +{ + delete m_paintEngine; +} + +int PDFContentEditorPaintDevice::devType() const +{ + return QInternal::Picture; +} + +QPaintEngine* PDFContentEditorPaintDevice::paintEngine() const +{ + return m_paintEngine; +} + +PDFPageContentEditorContentStreamBuilder::PDFPageContentEditorContentStreamBuilder(PDFDocument* document) : + m_document(document) +{ + +} + +void PDFPageContentEditorContentStreamBuilder::writeStateDifference(QTextStream& stream, const PDFPageContentProcessorState& state) +{ + m_currentState.setState(state); + + auto stateFlags = m_currentState.getStateFlags(); + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateLineWidth)) + { + stream << m_currentState.getLineWidth() << " w" << Qt::endl; + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateLineCapStyle)) + { + stream << PDFPageContentProcessor::convertPenCapStyleToLineCap(m_currentState.getLineCapStyle()) << " J" << Qt::endl; + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateLineJoinStyle)) + { + stream << PDFPageContentProcessor::convertPenJoinStyleToLineJoin(m_currentState.getLineJoinStyle()) << " j" << Qt::endl; + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateMitterLimit)) + { + stream << m_currentState.getMitterLimit() << " M" << Qt::endl; + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateLineDashPattern)) + { + const PDFLineDashPattern& dashPattern = m_currentState.getLineDashPattern(); + + if (dashPattern.isSolid()) + { + stream << "[] 0 d" << Qt::endl; + } + else + { + stream << "[ "; + + for (PDFReal arrayItem : dashPattern.getDashArray()) + { + stream << arrayItem << " "; + } + + stream << " ] " << dashPattern.getDashOffset() << " d" << Qt::endl; + } + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateRenderingIntent)) + { + switch (m_currentState.getRenderingIntent()) + { + case pdf::RenderingIntent::Perceptual: + stream << "/Perceptual ri" << Qt::endl; + break; + case pdf::RenderingIntent::AbsoluteColorimetric: + stream << "/AbsoluteColorimetric ri" << Qt::endl; + break; + case pdf::RenderingIntent::RelativeColorimetric: + stream << "/RelativeColorimetric ri" << Qt::endl; + break; + case pdf::RenderingIntent::Saturation: + stream << "/Saturation ri" << Qt::endl; + break; + + default: + break; + } + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateMitterLimit)) + { + stream << m_currentState.getMitterLimit() << " M" << Qt::endl; + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateFlatness)) + { + stream << m_currentState.getFlatness() << " i" << Qt::endl; + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateStrokeColor) || + stateFlags.testFlag(PDFPageContentProcessorState::StateStrokeColorSpace)) + { + QColor color = m_currentState.getStrokeColor(); + const PDFAbstractColorSpace* strokeColorSpace = m_currentState.getStrokeColorSpace(); + if (strokeColorSpace && strokeColorSpace->getColorSpace() == PDFAbstractColorSpace::ColorSpace::DeviceGray) + { + stream << qGray(color.rgb()) / 255.0 << " G" << Qt::endl; + } + else if (strokeColorSpace && strokeColorSpace->getColorSpace() == PDFAbstractColorSpace::ColorSpace::DeviceCMYK) + { + const PDFColor& strokeColorOriginal = m_currentState.getStrokeColorOriginal(); + if (strokeColorOriginal.size() >= 4) + { + stream << strokeColorOriginal[0] << " " << strokeColorOriginal[1] << " " << strokeColorOriginal[2] << " " << strokeColorOriginal[3] << " K" << Qt::endl; + } + } + else + { + stream << color.redF() << " " << color.greenF() << " " << color.blueF() << " RG" << Qt::endl; + } + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateFillColor) || + stateFlags.testFlag(PDFPageContentProcessorState::StateFillColorSpace)) + { + QColor color = m_currentState.getFillColor(); + const PDFAbstractColorSpace* fillColorSpace = m_currentState.getFillColorSpace(); + if (fillColorSpace && fillColorSpace->getColorSpace() == PDFAbstractColorSpace::ColorSpace::DeviceGray) + { + stream << qGray(color.rgb()) / 255.0 << " g" << Qt::endl; + } + else if (fillColorSpace && fillColorSpace->getColorSpace() == PDFAbstractColorSpace::ColorSpace::DeviceCMYK) + { + const PDFColor& fillColor = m_currentState.getFillColorOriginal(); + if (fillColor.size() >= 4) + { + stream << fillColor[0] << " " << fillColor[1] << " " << fillColor[2] << " " << fillColor[3] << " k" << Qt::endl; + } + } + else + { + stream << color.redF() << " " << color.greenF() << " " << color.blueF() << " rg" << Qt::endl; + } + } + + m_currentState.setStateFlags(PDFPageContentProcessorState::StateFlags()); + + + PDFObjectFactory stateDictionary; + stateDictionary.beginDictionary(); + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateSmoothness)) + { + stateDictionary.beginDictionaryItem("SM"); + stateDictionary << m_currentState.getSmoothness(); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateAlphaStroking)) + { + stateDictionary.beginDictionaryItem("CA"); + stateDictionary << m_currentState.getAlphaStroking(); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateAlphaFilling)) + { + stateDictionary.beginDictionaryItem("ca"); + stateDictionary << m_currentState.getAlphaFilling(); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateAlphaIsShape)) + { + stateDictionary.beginDictionaryItem("AIS"); + stateDictionary << m_currentState.getAlphaIsShape(); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateTextKnockout)) + { + stateDictionary.beginDictionaryItem("TK"); + stateDictionary << m_currentState.getTextKnockout(); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateStrokeAdjustment)) + { + stateDictionary.beginDictionaryItem("SA"); + stateDictionary << m_currentState.getStrokeAdjustment(); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateBlendMode)) + { + QString blendModeName = PDFBlendModeInfo::getBlendModeName(m_currentState.getBlendMode()); + + stateDictionary.beginDictionaryItem("BM"); + stateDictionary << WrapName(blendModeName.toLatin1()); + stateDictionary.endDictionaryItem(); + } + + if (stateFlags.testFlag(PDFPageContentProcessorState::StateOverprint)) + { + PDFOverprintMode overprintMode = m_currentState.getOverprintMode(); + + stateDictionary.beginDictionaryItem("OPM"); + stateDictionary << overprintMode.overprintMode; + stateDictionary.endDictionaryItem(); + + stateDictionary.beginDictionaryItem("OP"); + stateDictionary << overprintMode.overprintStroking; + stateDictionary.endDictionaryItem(); + + stateDictionary.beginDictionaryItem("op"); + stateDictionary << overprintMode.overprintFilling; + stateDictionary.endDictionaryItem(); + } + + stateDictionary.endDictionary(); + PDFObject stateObject = stateDictionary.takeObject(); + + const PDFDictionary* dictionary = m_document->getDictionaryFromObject(stateObject); + if (dictionary && dictionary->getCount() > 0) + { + // Apply state + QByteArray key; + + for (size_t i = 0; i < m_graphicStateDictionary.getCount(); ++i) + { + const PDFDictionary* currentDictionary = m_document->getDictionaryFromObject(m_graphicStateDictionary.getValue(i)); + if (*currentDictionary == *dictionary) + { + key = m_graphicStateDictionary.getKey(i).getString(); + break; + } + } + + if (key.isEmpty()) + { + int i = 0; + while (true) + { + QByteArray currentKey = QString("s%1").arg(++i).toLatin1(); + if (!m_graphicStateDictionary.hasKey(currentKey)) + { + m_graphicStateDictionary.addEntry(PDFInplaceOrMemoryString(currentKey), std::move(stateObject)); + key = currentKey; + break; + } + } + } + + stream << "/" << key << " gs" << Qt::endl; + } +} + +void PDFPageContentEditorContentStreamBuilder::writeEditedElement(const PDFEditedPageContentElement* element) +{ + PDFPageContentProcessorState state = element->getState(); + state.setCurrentTransformationMatrix(element->getTransform()); + + QTextStream stream(&m_outputContent, QDataStream::WriteOnly | QDataStream::Append); + writeStateDifference(stream, state); + + bool isNeededToWriteCurrentTransformationMatrix = this->isNeededToWriteCurrentTransformationMatrix(); + if (isNeededToWriteCurrentTransformationMatrix) + { + stream << "q" << Qt::endl; + writeCurrentTransformationMatrix(stream); + } + + if (const PDFEditedPageContentElementImage* imageElement = element->asImage()) + { + QImage image = imageElement->getImage(); + + writeImage(stream, image); + } + + if (const PDFEditedPageContentElementPath* pathElement = element->asPath()) + { + const bool isStroking = pathElement->getStrokePath(); + const bool isFilling = pathElement->getFillPath(); + + writePainterPath(stream, pathElement->getPath(), isStroking, isFilling); + } + + if (const PDFEditedPageContentElementText* textElement = element->asText()) + { + QString text = textElement->getItemsAsText(); + + if (!text.isEmpty()) + { + writeText(stream, text); + } + } + + if (isNeededToWriteCurrentTransformationMatrix) + { + stream << "Q" << Qt::endl; + } +} + +const QByteArray& PDFPageContentEditorContentStreamBuilder::getOutputContent() const +{ + return m_outputContent; +} + +void PDFPageContentEditorContentStreamBuilder::writePainterPath(QTextStream& stream, + const QPainterPath& path, + bool isStroking, + bool isFilling) +{ + const int elementCount = path.elementCount(); + + for (int i = 0; i < elementCount; ++i) + { + QPainterPath::Element element = path.elementAt(i); + + switch (element.type) + { + case QPainterPath::MoveToElement: + stream << element.x << " " << element.y << " m" << Qt::endl; + break; + + case QPainterPath::LineToElement: + stream << element.x << " " << element.y << " l" << Qt::endl; + break; + + case QPainterPath::CurveToElement: + stream << element.x << " " << element.y << " "; + ++i; + + while (i < elementCount) + { + QPainterPath::Element currentElement = path.elementAt(i); + + if (currentElement.type == QPainterPath::CurveToDataElement) + { + ++i; + stream << currentElement.x << " " << currentElement.y << " "; + } + else + { + --i; + break; + } + } + stream << " c" << Qt::endl; + break; + + case QPainterPath::CurveToDataElement: + stream << element.x << " " << element.y << " "; + break; + + default: + break; + } + } + + if (isStroking && !isFilling) + { + stream << "S" << Qt::endl; + } + else if (isStroking || isFilling) + { + switch (path.fillRule()) + { + case Qt::OddEvenFill: + if (isFilling && isStroking) + { + stream << "B*" << Qt::endl; + } + else + { + stream << "f*" << Qt::endl; + } + break; + case Qt::WindingFill: + if (isFilling && isStroking) + { + stream << "B" << Qt::endl; + } + else + { + stream << "f" << Qt::endl; + } + break; + default: + break; + } + } + else + { + stream << "n" << Qt::endl; + } +} + +void PDFPageContentEditorContentStreamBuilder::writeText(QTextStream& stream, const QString& text) +{ + stream << "q BT" << Qt::endl; + + QString xml = QString("%1").arg(text); + + QXmlStreamReader reader(xml); + m_textFont = m_currentState.getTextFont(); + + while (!reader.atEnd() && !reader.hasError()) + { + reader.readNext(); + + switch (reader.tokenType()) + { + case QXmlStreamReader::NoToken: + break; + + case QXmlStreamReader::Invalid: + addError(PDFTranslationContext::tr("Invalid XML text.")); + break; + + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::EndElement: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::ProcessingInstruction: + case QXmlStreamReader::EntityReference: + break; + + case QXmlStreamReader::StartElement: + writeTextCommand(stream, reader); + break; + + case QXmlStreamReader::Characters: + { + QString characters = reader.text().toString(); + + if (m_textFont) + { + PDFEncodedText encodedText = m_textFont->encodeText(characters); + + if (!encodedText.encodedText.isEmpty()) + { + stream << "<" << encodedText.encodedText.toHex() << "> Tj" << Qt::endl; + } + + if (!encodedText.isValid) + { + addError(PDFTranslationContext::tr("Error during converting text to font encoding. Some characters were not converted: '%1'.").arg(encodedText.errorString)); + } + } + else + { + addError(PDFTranslationContext::tr("Text font not defined!")); + } + break; + } + + default: + Q_ASSERT(false); + break; + } + } + + stream << "ET Q" << Qt::endl; +} + +void PDFPageContentEditorContentStreamBuilder::writeTextCommand(QTextStream& stream, const QXmlStreamReader& reader) +{ + QXmlStreamAttributes attributes = reader.attributes(); + + auto isCommand = [&reader](const char* tag) -> bool + { + QString tagString = reader.name().toString(); + QXmlStreamAttributes attributes = reader.attributes(); + return tagString == QLatin1String(tag) && attributes.size() == 1 && attributes.hasAttribute("v"); + }; + + if (reader.name().toString() == "doc") + { + return; + } + + if (isCommand("tr")) + { + const QXmlStreamAttribute& attribute = attributes.front(); + bool ok = false; + const int textRenderingMode = attribute.value().toInt(&ok); + if (!ok || textRenderingMode < 0 || textRenderingMode > 7) + { + addError(PDFTranslationContext::tr("Invalid rendering mode '%1'. Valid values are 0-7.").arg(textRenderingMode)); + } + else + { + stream << textRenderingMode << " Tr" << Qt::endl; + } + } + else if (isCommand("ts")) + { + const QXmlStreamAttribute& attribute = attributes.front(); + bool ok = false; + const double textRise = attribute.value().toDouble(&ok); + + if (!ok) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attribute.value().toString())); + } + else + { + stream << textRise << " Ts" << Qt::endl; + } + } + else if (isCommand("tc")) + { + const QXmlStreamAttribute& attribute = attributes.front(); + bool ok = false; + const double textCharacterSpacing = attribute.value().toDouble(&ok); + + if (!ok) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attribute.value().toString())); + } + else + { + stream << textCharacterSpacing << " Tc" << Qt::endl; + } + } + else if (isCommand("tw")) + { + const QXmlStreamAttribute& attribute = attributes.front(); + bool ok = false; + const double textWordSpacing = attribute.value().toDouble(&ok); + + if (!ok) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attribute.value().toString())); + } + else + { + stream << textWordSpacing << " Tw" << Qt::endl; + } + } + else if (isCommand("tl")) + { + const QXmlStreamAttribute& attribute = attributes.front(); + bool ok = false; + const double textLeading = attribute.value().toDouble(&ok); + + if (!ok) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attribute.value().toString())); + } + else + { + stream << textLeading << " TL" << Qt::endl; + } + } + else if (isCommand("tz")) + { + const QXmlStreamAttribute& attribute = attributes.front(); + bool ok = false; + const PDFReal textScaling = attribute.value().toDouble(&ok); + + if (!ok) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attribute.value().toString())); + } + else + { + stream << textScaling << " Tz" << Qt::endl; + } + } + else if (reader.name().toString() == "tf") + { + if (attributes.hasAttribute("font") && attributes.hasAttribute("size")) + { + bool ok = false; + QByteArray v1 = attributes.value("font").toString().toLatin1(); + PDFReal v2 = attributes.value("size").toDouble(&ok); + + if (!ok) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attributes.value("size").toString())); + } + else + { + v1 = selectFont(v1); + stream << "/" << v1 << " " << v2 << " Tf" << Qt::endl; + } + } + else + { + addError(PDFTranslationContext::tr("Text font command requires two attributes - font and size.")); + } + } + else if (reader.name().toString() == "tpos") + { + if (attributes.hasAttribute("x") && attributes.hasAttribute("y")) + { + bool ok1 = false; + bool ok2 = false; + PDFReal v1 = attributes.value("x").toDouble(&ok1); + PDFReal v2 = attributes.value("y").toDouble(&ok2); + + if (!ok1) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attributes.value("x").toString())); + } + else if (!ok2) + { + addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attributes.value("y").toString())); + } + else + { + stream << v1 << " " << v2 << " Td" << Qt::endl; + } + } + else + { + addError(PDFTranslationContext::tr("Text translation command requires two attributes - x and y.")); + } + } + else if (reader.name().toString() == "tmatrix") + { + if (attributes.hasAttribute("m11") && attributes.hasAttribute("m12") && + attributes.hasAttribute("m21") && attributes.hasAttribute("m22") && + attributes.hasAttribute("x") && attributes.hasAttribute("y")) + { + bool ok1 = false; + bool ok2 = false; + bool ok3 = false; + bool ok4 = false; + bool ok5 = false; + bool ok6 = false; + PDFReal m11 = attributes.value("m11").toDouble(&ok1); + PDFReal m12 = attributes.value("m12").toDouble(&ok2); + PDFReal m21 = attributes.value("m21").toDouble(&ok3); + PDFReal m22 = attributes.value("m22").toDouble(&ok4); + PDFReal x = attributes.value("x").toDouble(&ok5); + PDFReal y = attributes.value("y").toDouble(&ok6); + + if (!ok1 || !ok2 || !ok3 || !ok4 || !ok5 | !ok6) + { + addError(PDFTranslationContext::tr("Invalid text matrix parameters.")); + } + else + { + stream << m11 << " " << m12 << " " << m21 << " " << m22 << " " << x << " " << y << " Tm" << Qt::endl; + } + } + else + { + addError(PDFTranslationContext::tr("Set text matrix command requires six elements - m11, m12, m21, m22, x, y.")); + } + } + else + { + addError(PDFTranslationContext::tr("Invalid command '%1'.").arg(reader.name().toString())); + } +} + +void PDFPageContentEditorContentStreamBuilder::writeImage(QTextStream& stream, const QImage& image) +{ + QByteArray key; + + int i = 0; + while (true) + { + QByteArray currentKey = QString("Im%1").arg(++i).toLatin1(); + if (!m_xobjectDictionary.hasKey(currentKey)) + { + PDFArray array; + array.appendItem(PDFObject::createName("FlateDecode")); + + QImage codedImage = image; + codedImage = codedImage.convertToFormat(QImage::Format_RGB888); + + QByteArray decodedStream; + QBuffer buffer(&decodedStream); + if (buffer.open(QIODevice::WriteOnly)) + { + int width = codedImage.width(); + int bytesPerLine = codedImage.bytesPerLine(); + + for (int scanLineIndex = 0; scanLineIndex < codedImage.height(); ++scanLineIndex) + { + const uchar* scanline = codedImage.constScanLine(scanLineIndex); + buffer.write((const char*)scanline, qMin(3 * width, bytesPerLine)); + } + + buffer.close(); + } + + // Compress the content stream + QByteArray compressedData = PDFFlateDecodeFilter::compress(decodedStream); + PDFDictionary imageDictionary; + imageDictionary.setEntry(PDFInplaceOrMemoryString("Subtype"), PDFObject::createName("Image")); + imageDictionary.setEntry(PDFInplaceOrMemoryString("Width"), PDFObject::createInteger(image.width())); + imageDictionary.setEntry(PDFInplaceOrMemoryString("Height"), PDFObject::createInteger(image.height())); + imageDictionary.setEntry(PDFInplaceOrMemoryString("Predictor"), PDFObject::createInteger(1)); + imageDictionary.setEntry(PDFInplaceOrMemoryString("ColorSpace"), PDFObject::createName("DeviceRGB")); + imageDictionary.setEntry(PDFInplaceOrMemoryString("BitsPerComponent"), PDFObject::createInteger(8)); + imageDictionary.setEntry(PDFInplaceOrMemoryString("Length"), PDFObject::createInteger(compressedData.size())); + imageDictionary.setEntry(PDFInplaceOrMemoryString("Filter"), PDFObject::createArray(std::make_shared(qMove(array)))); + PDFObject imageObject = PDFObject::createStream(std::make_shared(qMove(imageDictionary), qMove(compressedData))); + + m_xobjectDictionary.addEntry(PDFInplaceOrMemoryString(currentKey), std::move(imageObject)); + key = currentKey; + break; + } + } + + stream << "/" << key << " Do" << Qt::endl; +} + +QByteArray PDFPageContentEditorContentStreamBuilder::selectFont(const QByteArray& font) +{ + m_textFont = nullptr; + + PDFObject fontObject = m_fontDictionary.get(font); + if (!fontObject.isNull()) + { + try + { + m_textFont = PDFFont::createFont(fontObject, font, m_document); + } + catch (const PDFException&) + { + addError(PDFTranslationContext::tr("Font '%1' is invalid.").arg(QString::fromLatin1(font))); + } + } + + if (!m_textFont) + { + QByteArray defaultFontKey = "PDF4QT_DefFnt"; + if (!m_fontDictionary.hasKey(defaultFontKey)) + { + PDFObjectFactory defaultFontFactory; + + defaultFontFactory.beginDictionary(); + defaultFontFactory.beginDictionaryItem("Type"); + defaultFontFactory << WrapName("Font"); + defaultFontFactory.endDictionaryItem(); + defaultFontFactory.beginDictionaryItem("Subtype"); + defaultFontFactory << WrapName("Type1"); + defaultFontFactory.endDictionaryItem(); + defaultFontFactory.beginDictionaryItem("BaseFont"); + defaultFontFactory << WrapName("Helvetica"); + defaultFontFactory.endDictionaryItem(); + defaultFontFactory.beginDictionaryItem("Encoding"); + defaultFontFactory << WrapName("WinAnsiEncoding"); + defaultFontFactory.endDictionaryItem(); + defaultFontFactory.endDictionary(); + + m_fontDictionary.setEntry(PDFInplaceOrMemoryString(defaultFontKey), defaultFontFactory.takeObject()); + } + + m_textFont = PDFFont::createFont(fontObject, font, m_document); + return defaultFontKey; + } + + return font; +} + +void PDFPageContentEditorContentStreamBuilder::addError(const QString& error) +{ + m_errors << error; +} + +void PDFPageContentEditorContentStreamBuilder::setFontDictionary(const PDFDictionary& newFontDictionary) +{ + m_fontDictionary = newFontDictionary; +} + +void PDFPageContentEditorContentStreamBuilder::writeStyledPath(const QPainterPath& path, + const QPen& pen, + const QBrush& brush, + bool isStroking, + bool isFilling) +{ + PDFPageContentProcessorState newState = m_currentState; + newState.setCurrentTransformationMatrix(QTransform()); + + PDFPainterHelper::applyPenToGraphicState(&newState, pen); + PDFPainterHelper::applyBrushToGraphicState(&newState, brush); + + QTextStream stream(&m_outputContent, QDataStream::WriteOnly | QDataStream::Append); + writeStateDifference(stream, newState); + + bool isNeededToWriteCurrentTransformationMatrix = this->isNeededToWriteCurrentTransformationMatrix(); + if (isNeededToWriteCurrentTransformationMatrix) + { + stream << "q" << Qt::endl; + writeCurrentTransformationMatrix(stream); + } + + writePainterPath(stream, path, isStroking, isFilling); + + if (isNeededToWriteCurrentTransformationMatrix) + { + stream << "Q" << Qt::endl; + } +} + +void PDFPageContentEditorContentStreamBuilder::writeStyledPath(const QPainterPath& path, const PDFPageContentProcessorState& state, bool isStroking, bool isFilling) +{ + QTextStream stream(&m_outputContent, QDataStream::WriteOnly | QDataStream::Append); + writeStateDifference(stream, state); + + bool isNeededToWriteCurrentTransformationMatrix = this->isNeededToWriteCurrentTransformationMatrix(); + if (isNeededToWriteCurrentTransformationMatrix) + { + stream << "q" << Qt::endl; + writeCurrentTransformationMatrix(stream); + } + + writePainterPath(stream, path, isStroking, isFilling); + + if (isNeededToWriteCurrentTransformationMatrix) + { + stream << "Q" << Qt::endl; + } +} + +void PDFPageContentEditorContentStreamBuilder::writeImage(const QImage& image, + const QRectF& rectangle) +{ + QTextStream stream(&m_outputContent, QDataStream::WriteOnly | QDataStream::Append); + + stream << "q" << Qt::endl; + if (isNeededToWriteCurrentTransformationMatrix()) + { + writeCurrentTransformationMatrix(stream); + } + + QSizeF rectangleSize = QSizeF(image.size()).scaled(rectangle.size(), Qt::KeepAspectRatio); + QRectF transformedRectangle(QPointF(), rectangleSize); + transformedRectangle.moveCenter(rectangle.center()); + + QTransform imageTransform(transformedRectangle.width(), 0, 0, transformedRectangle.height(), transformedRectangle.left(), transformedRectangle.top()); + + PDFReal m11 = imageTransform.m11(); + PDFReal m12 = imageTransform.m12(); + PDFReal m21 = imageTransform.m21(); + PDFReal m22 = imageTransform.m22(); + PDFReal x = imageTransform.dx(); + PDFReal y = imageTransform.dy(); + + stream << m11 << " " << m12 << " " << m21 << " " << m22 << " " << x << " " << y << " cm" << Qt::endl; + + writeImage(stream, image); + + stream << "Q" << Qt::endl; +} + +void PDFPageContentEditorContentStreamBuilder::writeImage(const QImage& image, QTransform transform, const QRectF& rectangle) +{ + QTransform oldTransform = m_currentState.getCurrentTransformationMatrix(); + m_currentState.setCurrentTransformationMatrix(transform); + writeImage(image, rectangle); + m_currentState.setCurrentTransformationMatrix(oldTransform); +} + +bool PDFPageContentEditorContentStreamBuilder::isNeededToWriteCurrentTransformationMatrix() const +{ + return !m_currentState.getCurrentTransformationMatrix().isIdentity(); +} + +void PDFPageContentEditorContentStreamBuilder::writeCurrentTransformationMatrix(QTextStream& stream) +{ + QTransform transform = m_currentState.getCurrentTransformationMatrix(); + + PDFReal m11 = transform.m11(); + PDFReal m12 = transform.m12(); + PDFReal m21 = transform.m21(); + PDFReal m22 = transform.m22(); + PDFReal x = transform.dx(); + PDFReal y = transform.dy(); + + stream << m11 << " " << m12 << " " << m21 << " " << m22 << " " << x << " " << y << " cm" << Qt::endl; +} + +} // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdfpagecontenteditorcontentstreambuilder.h b/Pdf4QtLibCore/sources/pdfpagecontenteditorcontentstreambuilder.h new file mode 100644 index 00000000..9ee58e38 --- /dev/null +++ b/Pdf4QtLibCore/sources/pdfpagecontenteditorcontentstreambuilder.h @@ -0,0 +1,114 @@ +// Copyright (C) 2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef PDFPAGECONTENTEDITORCONTENTSTREAMBUILDER_H +#define PDFPAGECONTENTEDITORCONTENTSTREAMBUILDER_H + +#include "pdfpagecontenteditorprocessor.h" + +#include + +namespace pdf +{ +class PDFPageContentElement; +class PDFContentEditorPaintEngine; +class PDFPageContentEditorContentStreamBuilder; + +class PDF4QTLIBCORESHARED_EXPORT PDFContentEditorPaintDevice : public QPaintDevice +{ +public: + PDFContentEditorPaintDevice(PDFPageContentEditorContentStreamBuilder* builder, QRectF mediaRect, QRectF mediaRectMM); + virtual ~PDFContentEditorPaintDevice() override; + + virtual int devType() const override; + virtual QPaintEngine* paintEngine() const override; + +protected: + virtual int metric(PaintDeviceMetric metric) const override; + +private: + PDFContentEditorPaintEngine* m_paintEngine; + QRectF m_mediaRect; + QRectF m_mediaRectMM; +}; + +class PDF4QTLIBCORESHARED_EXPORT PDFPageContentEditorContentStreamBuilder +{ +public: + PDFPageContentEditorContentStreamBuilder(PDFDocument* document); + + void writeEditedElement(const PDFEditedPageContentElement* element); + + const QByteArray& getOutputContent() const; + + const PDFDictionary& getFontDictionary() const { return m_fontDictionary; } + const PDFDictionary& getXObjectDictionary() const { return m_xobjectDictionary; } + const PDFDictionary& getGraphicStateDictionary() const { return m_graphicStateDictionary; } + + void setFontDictionary(const PDFDictionary& newFontDictionary); + + const QStringList& getErrors() const { return m_errors; } + void clearErrors() { m_errors.clear(); } + + void writeStyledPath(const QPainterPath& path, + const QPen& pen, + const QBrush& brush, + bool isStroking, + bool isFilling); + + void writeStyledPath(const QPainterPath& path, + const PDFPageContentProcessorState& state, + bool isStroking, + bool isFilling); + + void writeImage(const QImage& image, const QRectF& rectangle); + void writeImage(const QImage& image, QTransform transform, const QRectF& rectangle); + + const PDFPageContentProcessorState& getCurrentState() { return m_currentState; } + +private: + bool isNeededToWriteCurrentTransformationMatrix() const; + + void writeCurrentTransformationMatrix(QTextStream& stream); + void writeStateDifference(QTextStream& stream, const PDFPageContentProcessorState& state); + + void writePainterPath(QTextStream& stream, + const QPainterPath& path, + bool isStroking, + bool isFilling); + + void writeText(QTextStream& stream, const QString& text); + void writeTextCommand(QTextStream& stream, const QXmlStreamReader& reader); + + void writeImage(QTextStream& stream, const QImage& image); + + QByteArray selectFont(const QByteArray& font); + void addError(const QString& error); + + PDFDocument* m_document = nullptr; + PDFDictionary m_fontDictionary; + PDFDictionary m_xobjectDictionary; + PDFDictionary m_graphicStateDictionary; + QByteArray m_outputContent; + PDFPageContentProcessorState m_currentState; + PDFFontPointer m_textFont; + QStringList m_errors; +}; + +} // namespace pdf + +#endif // PDFPAGECONTENTEDITORCONTENTSTREAMBUILDER_H diff --git a/Pdf4QtLibCore/sources/pdfpagecontenteditorprocessor.cpp b/Pdf4QtLibCore/sources/pdfpagecontenteditorprocessor.cpp new file mode 100644 index 00000000..956c3bca --- /dev/null +++ b/Pdf4QtLibCore/sources/pdfpagecontenteditorprocessor.cpp @@ -0,0 +1,792 @@ +// Copyright (C) 2023-2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "pdfpagecontenteditorprocessor.h" + +namespace pdf +{ + +PDFPageContentEditorProcessor::PDFPageContentEditorProcessor(const PDFPage* page, + const PDFDocument* document, + const PDFFontCache* fontCache, + const PDFCMS* CMS, + const PDFOptionalContentActivity* optionalContentActivity, + QTransform pagePointToDevicePointMatrix, + const PDFMeshQualitySettings& meshQualitySettings) : + BaseClass(page, document, fontCache, CMS, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings) +{ + m_clippingPaths.push(QPainterPath()); + + if (auto fontDictionary = getFontDictionary()) + { + m_content.setFontDictionary(*fontDictionary); + } + + if (auto xObjectDictionary = getXObjectDictionary()) + { + m_content.setXObjectDictionary(*xObjectDictionary); + } +} + +const PDFEditedPageContent& PDFPageContentEditorProcessor::getEditedPageContent() const +{ + return m_content; +} + +PDFEditedPageContent PDFPageContentEditorProcessor::takeEditedPageContent() +{ + return std::move(m_content); +} + +void PDFPageContentEditorProcessor::performInterceptInstruction(Operator currentOperator, + ProcessOrder processOrder, + const QByteArray& operatorAsText) +{ + BaseClass::performInterceptInstruction(currentOperator, processOrder, operatorAsText); + + if (processOrder == ProcessOrder::BeforeOperation) + { + if (currentOperator == Operator::TextBegin && !isTextProcessing()) + { + m_contentElementText.reset(new PDFEditedPageContentElementText(*getGraphicState(), getGraphicState()->getCurrentTransformationMatrix())); + } + } + else + { + if (currentOperator == Operator::TextEnd && !isTextProcessing()) + { + if (m_contentElementText) + { + m_contentElementText->optimize(); + + if (!m_contentElementText->isEmpty()) + { + m_contentElementText->setTextPath(std::move(m_textPath)); + m_contentElementText->setItemsAsText(PDFEditedPageContentElementText::createItemsAsText(m_contentElementText->getState(), m_contentElementText->getItems())); + m_content.addContentElement(std::move(m_contentElementText)); + } + } + m_contentElementText.reset(); + m_textPath = QPainterPath(); + } + } +} + +void PDFPageContentEditorProcessor::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) +{ + BaseClass::performPathPainting(path, stroke, fill, text, fillRule); + + if (path.isEmpty()) + { + return; + } + + if (text) + { + m_textPath.addPath(path); + } + else + { + m_content.addContentPath(*getGraphicState(), path, stroke, fill); + } +} + +void PDFPageContentEditorProcessor::performUpdateGraphicsState(const PDFPageContentProcessorState& state) +{ + BaseClass::performUpdateGraphicsState(state); + + if (isTextProcessing() && m_contentElementText) + { + PDFEditedPageContentElementText::Item item; + item.isUpdateGraphicState = true; + item.state = state; + + m_contentElementText->addItem(item); + } +} + +void PDFPageContentEditorProcessor::performProcessTextSequence(const TextSequence& textSequence, ProcessOrder order) +{ + BaseClass::performProcessTextSequence(textSequence, order); + + if (order == ProcessOrder::BeforeOperation) + { + PDFEditedPageContentElementText::Item item; + item.isText = true; + item.textSequence = textSequence; + + m_contentElementText->addItem(item); + } +} + +bool PDFPageContentEditorProcessor::performOriginalImagePainting(const PDFImage& image, const PDFStream* stream) +{ + BaseClass::performOriginalImagePainting(image, stream); + + PDFObject imageObject = PDFObject::createStream(std::make_shared(*stream)); + m_content.addContentImage(*getGraphicState(), std::move(imageObject), QImage()); + + return false; +} + +void PDFPageContentEditorProcessor::performImagePainting(const QImage& image) +{ + BaseClass::performImagePainting(image); + + PDFEditedPageContentElement* backElement = m_content.getBackElement(); + Q_ASSERT(backElement); + + PDFEditedPageContentElementImage* imageElement = backElement->asImage(); + imageElement->setImage(image); +} + +void PDFPageContentEditorProcessor::performSaveGraphicState(ProcessOrder order) +{ + BaseClass::performSaveGraphicState(order); + + if (order == ProcessOrder::BeforeOperation) + { + m_clippingPaths.push(m_clippingPaths.top()); + } +} + +void PDFPageContentEditorProcessor::performRestoreGraphicState(ProcessOrder order) +{ + BaseClass::performRestoreGraphicState(order); + + if (order == ProcessOrder::AfterOperation) + { + m_clippingPaths.pop(); + } +} + +void PDFPageContentEditorProcessor::performClipping(const QPainterPath& path, Qt::FillRule fillRule) +{ + BaseClass::performClipping(path, fillRule); + + if (m_clippingPaths.top().isEmpty()) + { + m_clippingPaths.top() = path; + } + else + { + m_clippingPaths.top() = m_clippingPaths.top().intersected(path); + } +} + +bool PDFPageContentEditorProcessor::isContentKindSuppressed(ContentKind kind) const +{ + switch (kind) + { + case ContentKind::Shading: + case ContentKind::Tiling: + return true; + + default: + break; + } + + return false; +} + +QString PDFEditedPageContent::getOperatorToString(PDFPageContentProcessor::Operator operatorValue) +{ + switch (operatorValue) + { + case pdf::PDFPageContentProcessor::Operator::SetLineWidth: + return "set_line_width"; + case pdf::PDFPageContentProcessor::Operator::SetLineCap: + return "set_line_cap"; + case pdf::PDFPageContentProcessor::Operator::SetLineJoin: + return "set_line_join"; + case pdf::PDFPageContentProcessor::Operator::SetMitterLimit: + return "set_mitter_limit"; + case pdf::PDFPageContentProcessor::Operator::SetLineDashPattern: + return "set_line_dash_pattern"; + case pdf::PDFPageContentProcessor::Operator::SetRenderingIntent: + return "set_rendering_intent"; + case pdf::PDFPageContentProcessor::Operator::SetFlatness: + return "set_flatness"; + case pdf::PDFPageContentProcessor::Operator::SetGraphicState: + return "set_graphic_state"; + case pdf::PDFPageContentProcessor::Operator::SaveGraphicState: + return "save"; + case pdf::PDFPageContentProcessor::Operator::RestoreGraphicState: + return "restore"; + case pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix: + return "set_cm"; + case pdf::PDFPageContentProcessor::Operator::MoveCurrentPoint: + return "move_to"; + case pdf::PDFPageContentProcessor::Operator::LineTo: + return "line_to"; + case pdf::PDFPageContentProcessor::Operator::Bezier123To: + return "cubic123_to"; + case pdf::PDFPageContentProcessor::Operator::Bezier23To: + return "cubic23_to"; + case pdf::PDFPageContentProcessor::Operator::Bezier13To: + return "cubic13_to"; + case pdf::PDFPageContentProcessor::Operator::EndSubpath: + return "close_path"; + case pdf::PDFPageContentProcessor::Operator::Rectangle: + return "rect"; + case pdf::PDFPageContentProcessor::Operator::PathStroke: + return "path_stroke"; + case pdf::PDFPageContentProcessor::Operator::PathCloseStroke: + return "path_close_and_stroke"; + case pdf::PDFPageContentProcessor::Operator::PathFillWinding: + return "path_fill_winding"; + case pdf::PDFPageContentProcessor::Operator::PathFillWinding2: + return "path_fill_winding"; + case pdf::PDFPageContentProcessor::Operator::PathFillEvenOdd: + return "path_fill_even_odd"; + case pdf::PDFPageContentProcessor::Operator::PathFillStrokeWinding: + return "path_fill_stroke_winding"; + case pdf::PDFPageContentProcessor::Operator::PathFillStrokeEvenOdd: + return "path_fill_stroke_even_odd"; + case pdf::PDFPageContentProcessor::Operator::PathCloseFillStrokeWinding: + return "path_close_fill_stroke_winding"; + case pdf::PDFPageContentProcessor::Operator::PathCloseFillStrokeEvenOdd: + return "path_close_fill_stroke_even_odd"; + case pdf::PDFPageContentProcessor::Operator::PathClear: + return "path_clear"; + case pdf::PDFPageContentProcessor::Operator::ClipWinding: + return "clip_winding"; + case pdf::PDFPageContentProcessor::Operator::ClipEvenOdd: + return "clip_even_odd"; + case pdf::PDFPageContentProcessor::Operator::TextBegin: + return "text_begin"; + case pdf::PDFPageContentProcessor::Operator::TextEnd: + return "text_end"; + case pdf::PDFPageContentProcessor::Operator::TextSetCharacterSpacing: + return "set_char_spacing"; + case pdf::PDFPageContentProcessor::Operator::TextSetWordSpacing: + return "set_word_spacing"; + case pdf::PDFPageContentProcessor::Operator::TextSetHorizontalScale: + return "set_hor_scale"; + case pdf::PDFPageContentProcessor::Operator::TextSetLeading: + return "set_leading"; + case pdf::PDFPageContentProcessor::Operator::TextSetFontAndFontSize: + return "set_font"; + case pdf::PDFPageContentProcessor::Operator::TextSetRenderMode: + return "set_text_render_mode"; + case pdf::PDFPageContentProcessor::Operator::TextSetRise: + return "set_text_rise"; + case pdf::PDFPageContentProcessor::Operator::TextMoveByOffset: + return "text_move_by_offset"; + case pdf::PDFPageContentProcessor::Operator::TextSetLeadingAndMoveByOffset: + return "text_set_leading_and_move_by_offset"; + case pdf::PDFPageContentProcessor::Operator::TextSetMatrix: + return "text_set_matrix"; + case pdf::PDFPageContentProcessor::Operator::TextMoveByLeading: + return "text_move_by_leading"; + case pdf::PDFPageContentProcessor::Operator::TextShowTextString: + return "text_show_string"; + case pdf::PDFPageContentProcessor::Operator::TextShowTextIndividualSpacing: + return "text_show_string_with_spacing"; + case pdf::PDFPageContentProcessor::Operator::TextNextLineShowText: + return "text_next_line_and_show_text"; + case pdf::PDFPageContentProcessor::Operator::TextSetSpacingAndShowText: + return "text_set_spacing_and_show_text"; + case pdf::PDFPageContentProcessor::Operator::Type3FontSetOffset: + return "text_t3_set_offset"; + case pdf::PDFPageContentProcessor::Operator::Type3FontSetOffsetAndBB: + return "text_t3_set_offset_and_bb"; + case pdf::PDFPageContentProcessor::Operator::ColorSetStrokingColorSpace: + return "set_stroke_color_space"; + case pdf::PDFPageContentProcessor::Operator::ColorSetFillingColorSpace: + return "set_filling_color_space"; + case pdf::PDFPageContentProcessor::Operator::ColorSetStrokingColor: + return "set_stroke_color"; + case pdf::PDFPageContentProcessor::Operator::ColorSetStrokingColorN: + return "set_stroke_color_n"; + case pdf::PDFPageContentProcessor::Operator::ColorSetFillingColor: + return "set_filling_color"; + case pdf::PDFPageContentProcessor::Operator::ColorSetFillingColorN: + return "set_filling_color_n"; + case pdf::PDFPageContentProcessor::Operator::ColorSetDeviceGrayStroking: + return "set_stroke_gray_cs"; + case pdf::PDFPageContentProcessor::Operator::ColorSetDeviceGrayFilling: + return "set_filling_gray_cs"; + case pdf::PDFPageContentProcessor::Operator::ColorSetDeviceRGBStroking: + return "set_stroke_rgb_cs"; + case pdf::PDFPageContentProcessor::Operator::ColorSetDeviceRGBFilling: + return "set_filling_rgb_cs"; + case pdf::PDFPageContentProcessor::Operator::ColorSetDeviceCMYKStroking: + return "set_stroke_cmyk_cs"; + case pdf::PDFPageContentProcessor::Operator::ColorSetDeviceCMYKFilling: + return "set_filling_cmyk_cs"; + case pdf::PDFPageContentProcessor::Operator::ShadingPaintShape: + return "shading_paint"; + case pdf::PDFPageContentProcessor::Operator::InlineImageBegin: + return "ib"; + case pdf::PDFPageContentProcessor::Operator::InlineImageData: + return "id"; + case pdf::PDFPageContentProcessor::Operator::InlineImageEnd: + return "ie"; + case pdf::PDFPageContentProcessor::Operator::PaintXObject: + return "paint_object"; + case pdf::PDFPageContentProcessor::Operator::MarkedContentPoint: + return "mc_point"; + case pdf::PDFPageContentProcessor::Operator::MarkedContentPointWithProperties: + return "mc_point_prop"; + case pdf::PDFPageContentProcessor::Operator::MarkedContentBegin: + return "mc_begin"; + case pdf::PDFPageContentProcessor::Operator::MarkedContentBeginWithProperties: + return "mc_begin_prop"; + case pdf::PDFPageContentProcessor::Operator::MarkedContentEnd: + return "mc_end"; + case pdf::PDFPageContentProcessor::Operator::CompatibilityBegin: + return "compat_begin"; + case pdf::PDFPageContentProcessor::Operator::CompatibilityEnd: + return "compat_end"; + + default: + break; + } + + return QString(); +} + +QString PDFEditedPageContent::getOperandName(PDFPageContentProcessor::Operator operatorValue, int operandIndex) +{ + static const std::map, QString> operands = + { + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetLineWidth, 0), "lineWidth" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetLineCap, 0), "lineCap" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetLineJoin, 0), "lineJoin" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetMitterLimit, 0), "mitterLimit" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetRenderingIntent, 0), "renderingIntent" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetFlatness, 0), "flatness" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::SetGraphicState, 0), "graphicState" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix, 0), "a" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix, 1), "b" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix, 2), "c" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix, 3), "d" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix, 4), "e" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::AdjustCurrentTransformationMatrix, 5), "f" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::MoveCurrentPoint, 0), "x" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::MoveCurrentPoint, 1), "y" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::LineTo, 0), "x" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::LineTo, 1), "y" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier123To, 0), "x1" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier123To, 1), "y1" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier123To, 2), "x2" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier123To, 3), "y2" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier123To, 4), "x3" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier123To, 5), "y3" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier23To, 0), "x2" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier23To, 1), "y2" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier23To, 2), "x3" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier23To, 3), "y3" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier13To, 0), "x1" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier13To, 1), "y1" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier13To, 2), "x3" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Bezier13To, 3), "y3" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Rectangle, 0), "x" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Rectangle, 1), "y" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Rectangle, 2), "width" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::Rectangle, 3), "height" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetCharacterSpacing, 0), "charSpacing" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetWordSpacing, 0), "wordSpacing" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetHorizontalScale, 0), "scale" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetLeading, 0), "leading" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetFontAndFontSize, 0), "font" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetFontAndFontSize, 1), "fontSize" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetRenderMode, 0), "renderMode" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetRise, 0), "rise" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextMoveByOffset, 0), "tx" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextMoveByOffset, 1), "ty" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetLeadingAndMoveByOffset, 0), "tx" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetLeadingAndMoveByOffset, 1), "ty" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetMatrix, 0), "a" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetMatrix, 1), "b" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetMatrix, 2), "c" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetMatrix, 3), "d" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetMatrix, 4), "e" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetMatrix, 5), "f" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextShowTextString, 0), "string" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextNextLineShowText, 0), "string" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextShowTextIndividualSpacing, 0), "wSpacing" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextShowTextIndividualSpacing, 1), "chSpacing" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextShowTextIndividualSpacing, 2), "string" }, + { std::make_pair(pdf::PDFPageContentProcessor::Operator::TextSetSpacingAndShowText, 0), "string" }, + }; + + auto it = operands.find(std::make_pair(operatorValue, operandIndex)); + if (it != operands.cend()) + { + return it->second; + } + + return QString("op%1").arg(operandIndex); +} + +void PDFEditedPageContent::addContentPath(PDFPageContentProcessorState state, QPainterPath path, bool strokePath, bool fillPath) +{ + QTransform transform = state.getCurrentTransformationMatrix(); + m_contentElements.emplace_back(new PDFEditedPageContentElementPath(std::move(state), std::move(path), strokePath, fillPath, transform)); +} + +void PDFEditedPageContent::addContentImage(PDFPageContentProcessorState state, PDFObject imageObject, QImage image) +{ + QTransform transform = state.getCurrentTransformationMatrix(); + m_contentElements.emplace_back(new PDFEditedPageContentElementImage(std::move(state), std::move(imageObject), std::move(image), transform)); +} + +void PDFEditedPageContent::addContentElement(std::unique_ptr element) +{ + m_contentElements.emplace_back(std::move(element)); +} + +PDFEditedPageContentElement* PDFEditedPageContent::getBackElement() const +{ + if (m_contentElements.empty()) + { + return nullptr; + } + + return m_contentElements.back().get(); +} + +PDFDictionary PDFEditedPageContent::getFontDictionary() const +{ + return m_fontDictionary; +} + +void PDFEditedPageContent::setFontDictionary(const PDFDictionary& newFontDictionary) +{ + m_fontDictionary = newFontDictionary; +} + +PDFDictionary PDFEditedPageContent::getXObjectDictionary() const +{ + return m_xobjectDictionary; +} + +void PDFEditedPageContent::setXObjectDictionary(const PDFDictionary& newXobjectDictionary) +{ + m_xobjectDictionary = newXobjectDictionary; +} + +PDFEditedPageContentElement::PDFEditedPageContentElement(PDFPageContentProcessorState state, QTransform transform) : + m_state(std::move(state)), + m_transform(transform) +{ + +} + +const PDFPageContentProcessorState& PDFEditedPageContentElement::getState() const +{ + return m_state; +} + +void PDFEditedPageContentElement::setState(const PDFPageContentProcessorState& newState) +{ + m_state = newState; +} + +QTransform PDFEditedPageContentElement::getTransform() const +{ + return m_transform; +} + +void PDFEditedPageContentElement::setTransform(const QTransform& newTransform) +{ + m_transform = newTransform; +} + +PDFEditedPageContentElementPath::PDFEditedPageContentElementPath(PDFPageContentProcessorState state, QPainterPath path, bool strokePath, bool fillPath, QTransform transform) : + PDFEditedPageContentElement(std::move(state), transform), + m_path(std::move(path)), + m_strokePath(strokePath), + m_fillPath(fillPath) +{ + +} + +PDFEditedPageContentElement::Type PDFEditedPageContentElementPath::getType() const +{ + return Type::Path; +} + +PDFEditedPageContentElementPath* PDFEditedPageContentElementPath::clone() const +{ + return new PDFEditedPageContentElementPath(getState(), getPath(), getStrokePath(), getFillPath(), getTransform()); +} + +QRectF PDFEditedPageContentElementPath::getBoundingBox() const +{ + QPainterPath mappedPath = getTransform().map(m_path); + return mappedPath.boundingRect(); +} + +QPainterPath PDFEditedPageContentElementPath::getPath() const +{ + return m_path; +} + +void PDFEditedPageContentElementPath::setPath(QPainterPath newPath) +{ + m_path = newPath; +} + +bool PDFEditedPageContentElementPath::getStrokePath() const +{ + return m_strokePath; +} + +void PDFEditedPageContentElementPath::setStrokePath(bool newStrokePath) +{ + m_strokePath = newStrokePath; +} + +bool PDFEditedPageContentElementPath::getFillPath() const +{ + return m_fillPath; +} + +void PDFEditedPageContentElementPath::setFillPath(bool newFillPath) +{ + m_fillPath = newFillPath; +} + +PDFEditedPageContentElementImage::PDFEditedPageContentElementImage(PDFPageContentProcessorState state, PDFObject imageObject, QImage image, QTransform transform) : + PDFEditedPageContentElement(std::move(state), transform), + m_imageObject(std::move(imageObject)), + m_image(std::move(image)) +{ + +} + +PDFEditedPageContentElement::Type PDFEditedPageContentElementImage::getType() const +{ + return PDFEditedPageContentElement::Type::Image; +} + +PDFEditedPageContentElementImage* PDFEditedPageContentElementImage::clone() const +{ + return new PDFEditedPageContentElementImage(getState(), getImageObject(), getImage(), getTransform()); +} + +QRectF PDFEditedPageContentElementImage::getBoundingBox() const +{ + return getTransform().mapRect(QRectF(0, 0, 1, 1)); +} + +PDFObject PDFEditedPageContentElementImage::getImageObject() const +{ + return m_imageObject; +} + +void PDFEditedPageContentElementImage::setImageObject(const PDFObject& newImageObject) +{ + m_imageObject = newImageObject; +} + +QImage PDFEditedPageContentElementImage::getImage() const +{ + return m_image; +} + +void PDFEditedPageContentElementImage::setImage(const QImage& newImage) +{ + m_image = newImage; +} + +PDFEditedPageContentElementText::PDFEditedPageContentElementText(PDFPageContentProcessorState state, QTransform transform) : + PDFEditedPageContentElement(state, transform) +{ + +} + +PDFEditedPageContentElementText::PDFEditedPageContentElementText(PDFPageContentProcessorState state, + std::vector items, + QPainterPath textPath, + QTransform transform, + QString itemsAsText) : + PDFEditedPageContentElement(state, transform), + m_items(std::move(items)), + m_textPath(std::move(textPath)), + m_itemsAsText(itemsAsText) +{ + +} + +PDFEditedPageContentElement::Type PDFEditedPageContentElementText::getType() const +{ + return Type::Text; +} + +PDFEditedPageContentElementText* PDFEditedPageContentElementText::clone() const +{ + return new PDFEditedPageContentElementText(getState(), getItems(), getTextPath(), getTransform(), getItemsAsText()); +} + +void PDFEditedPageContentElementText::addItem(Item item) +{ + m_items.emplace_back(std::move(item)); +} + +const std::vector& PDFEditedPageContentElementText::getItems() const +{ + return m_items; +} + +void PDFEditedPageContentElementText::setItems(const std::vector& newItems) +{ + m_items = newItems; +} + +QRectF PDFEditedPageContentElementText::getBoundingBox() const +{ + return getTransform().mapRect(m_textPath.boundingRect()); +} + +QPainterPath PDFEditedPageContentElementText::getTextPath() const +{ + return m_textPath; +} + +void PDFEditedPageContentElementText::setTextPath(QPainterPath newTextPath) +{ + m_textPath = newTextPath; +} + +QString PDFEditedPageContentElementText::createItemsAsText(const PDFPageContentProcessorState& initialState, + const std::vector& items) +{ + QString text; + + PDFPageContentProcessorState state = initialState; + state.setStateFlags(PDFPageContentProcessorState::StateFlags()); + + for (const Item& item : items) + { + if (item.isText) + { + for (const TextSequenceItem& textItem : item.textSequence.items) + { + if (textItem.isCharacter()) + { + if (!textItem.character.isNull()) + { + text += QString(textItem.character).toHtmlEscaped(); + } + else if (textItem.isAdvance()) + { + text += QString("").arg(textItem.advance); + } + else if (textItem.cid != 0) + { + text += QString("").arg(textItem.cid); + } + } + } + } + else if (item.isUpdateGraphicState) + { + PDFPageContentProcessorState newState = state; + newState.setStateFlags(PDFPageContentProcessorState::StateFlags()); + + newState.setState(item.state); + PDFPageContentProcessorState::StateFlags flags = newState.getStateFlags(); + + if (flags.testFlag(PDFPageContentProcessorState::StateTextRenderingMode)) + { + text += QString("").arg(int(newState.getTextRenderingMode())); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextRise)) + { + text += QString("").arg(newState.getTextRise()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextCharacterSpacing)) + { + text += QString("").arg(newState.getTextCharacterSpacing()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextWordSpacing)) + { + text += QString("").arg(newState.getTextWordSpacing()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextLeading)) + { + text += QString("").arg(newState.getTextLeading()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextHorizontalScaling)) + { + text += QString("").arg(newState.getTextHorizontalScaling()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextKnockout)) + { + text += QString("").arg(newState.getTextKnockout()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextFont) || + flags.testFlag(PDFPageContentProcessorState::StateTextFontSize)) + { + text += QString("").arg(newState.getTextFont()->getFontId()).arg(newState.getTextFontSize()); + } + + if (flags.testFlag(PDFPageContentProcessorState::StateTextMatrix)) + { + QTransform transform = newState.getTextMatrix(); + + qreal x = transform.dx(); + qreal y = transform.dy(); + + if (transform.isTranslating()) + { + text += QString("").arg(x).arg(y); + } + else + { + text += QString("").arg(transform.m11()).arg(transform.m12()).arg(transform.m21()).arg(transform.m22()).arg(x).arg(y); + } + } + + state = newState; + state.setStateFlags(PDFPageContentProcessorState::StateFlags()); + } + } + + return text; +} + +QString PDFEditedPageContentElementText::getItemsAsText() const +{ + return m_itemsAsText; +} + +void PDFEditedPageContentElementText::setItemsAsText(const QString& newItemsAsText) +{ + m_itemsAsText = newItemsAsText; +} + +void PDFEditedPageContentElementText::optimize() +{ + while (!m_items.empty() && !m_items.back().isText) + { + m_items.pop_back(); + } +} + +} // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdfpagecontenteditorprocessor.h b/Pdf4QtLibCore/sources/pdfpagecontenteditorprocessor.h new file mode 100644 index 00000000..a26fd813 --- /dev/null +++ b/Pdf4QtLibCore/sources/pdfpagecontenteditorprocessor.h @@ -0,0 +1,252 @@ +// Copyright (C) 2023-2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef PDFPAGECONTENTEDITORPROCESSOR_H +#define PDFPAGECONTENTEDITORPROCESSOR_H + +#include "pdfpagecontentprocessor.h" + +#include + +class QXmlStreamReader; + +namespace pdf +{ + +class PDFEditedPageContentElementPath; +class PDFEditedPageContentElementText; +class PDFEditedPageContentElementImage; + +class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContentElement +{ +public: + PDFEditedPageContentElement() = default; + PDFEditedPageContentElement(PDFPageContentProcessorState state, QTransform transform); + virtual ~PDFEditedPageContentElement() = default; + + enum class Type + { + Path, + Text, + Image + }; + + virtual Type getType() const = 0; + virtual PDFEditedPageContentElement* clone() const = 0; + + virtual PDFEditedPageContentElementPath* asPath() { return nullptr; } + virtual const PDFEditedPageContentElementPath* asPath() const { return nullptr; } + + virtual PDFEditedPageContentElementText* asText() { return nullptr; } + virtual const PDFEditedPageContentElementText* asText() const { return nullptr; } + + virtual PDFEditedPageContentElementImage* asImage() { return nullptr; } + virtual const PDFEditedPageContentElementImage* asImage() const { return nullptr; } + + const PDFPageContentProcessorState& getState() const; + void setState(const PDFPageContentProcessorState& newState); + + virtual QRectF getBoundingBox() const = 0; + + QTransform getTransform() const; + void setTransform(const QTransform& newTransform); + +protected: + PDFPageContentProcessorState m_state; + QTransform m_transform; +}; + +class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContentElementPath : public PDFEditedPageContentElement +{ +public: + PDFEditedPageContentElementPath(PDFPageContentProcessorState state, + QPainterPath path, + bool strokePath, + bool fillPath, + QTransform transform); + virtual ~PDFEditedPageContentElementPath() = default; + + virtual Type getType() const override; + virtual PDFEditedPageContentElementPath* clone() const override; + virtual PDFEditedPageContentElementPath* asPath() override { return this; } + virtual const PDFEditedPageContentElementPath* asPath() const override { return this; } + virtual QRectF getBoundingBox() const override; + + QPainterPath getPath() const; + void setPath(QPainterPath newPath); + + bool getStrokePath() const; + void setStrokePath(bool newStrokePath); + + bool getFillPath() const; + void setFillPath(bool newFillPath); + +private: + QPainterPath m_path; + bool m_strokePath; + bool m_fillPath; +}; + +class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContentElementImage : public PDFEditedPageContentElement +{ +public: + PDFEditedPageContentElementImage(PDFPageContentProcessorState state, + PDFObject imageObject, + QImage image, + QTransform transform); + virtual ~PDFEditedPageContentElementImage() = default; + + virtual Type getType() const override; + virtual PDFEditedPageContentElementImage* clone() const override; + virtual PDFEditedPageContentElementImage* asImage() override { return this; } + virtual const PDFEditedPageContentElementImage* asImage() const override { return this; } + virtual QRectF getBoundingBox() const override; + + PDFObject getImageObject() const; + void setImageObject(const PDFObject& newImageObject); + + QImage getImage() const; + void setImage(const QImage& newImage); + +private: + PDFObject m_imageObject; + QImage m_image; +}; + +class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContentElementText : public PDFEditedPageContentElement +{ +public: + + struct Item + { + bool isUpdateGraphicState = false; + bool isText = false; + TextSequence textSequence; + + PDFPageContentProcessorState state; + }; + + PDFEditedPageContentElementText(PDFPageContentProcessorState state, QTransform transform); + PDFEditedPageContentElementText(PDFPageContentProcessorState state, + std::vector items, + QPainterPath textPath, + QTransform transform, + QString itemsAsText); + virtual ~PDFEditedPageContentElementText() = default; + + virtual Type getType() const override; + virtual PDFEditedPageContentElementText* clone() const override; + virtual PDFEditedPageContentElementText* asText() override { return this; } + virtual const PDFEditedPageContentElementText* asText() const override { return this; } + virtual QRectF getBoundingBox() const override; + + void addItem(Item item); + const std::vector& getItems() const; + void setItems(const std::vector& newItems); + + bool isEmpty() const { return m_items.empty(); } + + QPainterPath getTextPath() const; + void setTextPath(QPainterPath newTextPath); + + static QString createItemsAsText(const PDFPageContentProcessorState& initialState, + const std::vector& items); + + QString getItemsAsText() const; + void setItemsAsText(const QString& newItemsAsText); + + void optimize(); + +private: + std::vector m_items; + QPainterPath m_textPath; + QString m_itemsAsText; +}; + +class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContent +{ +public: + PDFEditedPageContent() = default; + PDFEditedPageContent(const PDFEditedPageContent&) = delete; + PDFEditedPageContent(PDFEditedPageContent&&) = default; + + PDFEditedPageContent& operator=(const PDFEditedPageContent&) = delete; + PDFEditedPageContent& operator=(PDFEditedPageContent&&) = default; + + static QString getOperatorToString(PDFPageContentProcessor::Operator operatorValue); + static QString getOperandName(PDFPageContentProcessor::Operator operatorValue, int operandIndex); + + void addContentPath(PDFPageContentProcessorState state, QPainterPath path, bool strokePath, bool fillPath); + void addContentImage(PDFPageContentProcessorState state, PDFObject imageObject, QImage image); + void addContentClipping(PDFPageContentProcessorState state, QPainterPath path); + void addContentElement(std::unique_ptr element); + + std::size_t getElementCount() const { return m_contentElements.size(); } + PDFEditedPageContentElement* getElement(size_t index) const { return m_contentElements.at(index).get(); } + + PDFEditedPageContentElement* getBackElement() const; + + PDFDictionary getFontDictionary() const; + void setFontDictionary(const PDFDictionary& newFontDictionary); + + PDFDictionary getXObjectDictionary() const; + void setXObjectDictionary(const PDFDictionary& newXobjectDictionary); + +private: + std::vector> m_contentElements; + PDFDictionary m_fontDictionary; + PDFDictionary m_xobjectDictionary; +}; + +class PDF4QTLIBCORESHARED_EXPORT PDFPageContentEditorProcessor : public PDFPageContentProcessor +{ + using BaseClass = PDFPageContentProcessor; + +public: + PDFPageContentEditorProcessor(const PDFPage* page, + const PDFDocument* document, + const PDFFontCache* fontCache, + const PDFCMS* CMS, + const PDFOptionalContentActivity* optionalContentActivity, + QTransform pagePointToDevicePointMatrix, + const PDFMeshQualitySettings& meshQualitySettings); + + const PDFEditedPageContent& getEditedPageContent() const; + PDFEditedPageContent takeEditedPageContent(); + +protected: + virtual void performInterceptInstruction(Operator currentOperator, ProcessOrder processOrder, const QByteArray& operatorAsText) override; + virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) override; + virtual bool isContentKindSuppressed(ContentKind kind) const override; + virtual bool performOriginalImagePainting(const PDFImage& image, const PDFStream* stream) override; + virtual void performImagePainting(const QImage& image) override; + virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule) override; + virtual void performSaveGraphicState(ProcessOrder order) override; + virtual void performRestoreGraphicState(ProcessOrder order) override; + virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override; + virtual void performProcessTextSequence(const TextSequence& textSequence, ProcessOrder order) override; + +private: + PDFEditedPageContent m_content; + std::stack m_clippingPaths; + std::unique_ptr m_contentElementText; + QPainterPath m_textPath; +}; + +} // namespace pdf + +#endif // PDFPAGECONTENTEDITORPROCESSOR_H diff --git a/Pdf4QtLibCore/sources/pdfpagecontentprocessor.cpp b/Pdf4QtLibCore/sources/pdfpagecontentprocessor.cpp index 5f4c6029..7eb2029c 100644 --- a/Pdf4QtLibCore/sources/pdfpagecontentprocessor.cpp +++ b/Pdf4QtLibCore/sources/pdfpagecontentprocessor.cpp @@ -23,6 +23,7 @@ #include "pdfexecutionpolicy.h" #include "pdfstreamfilters.h" +#include #include #include @@ -390,9 +391,10 @@ void PDFPageContentProcessor::performClipping(const QPainterPath& path, Qt::Fill Q_UNUSED(fillRule); } -bool PDFPageContentProcessor::performOriginalImagePainting(const PDFImage& image) +bool PDFPageContentProcessor::performOriginalImagePainting(const PDFImage& image, const PDFStream* stream) { Q_UNUSED(image); + Q_UNUSED(stream); return false; } @@ -485,6 +487,12 @@ void PDFPageContentProcessor::performTextEnd(ProcessOrder order) Q_UNUSED(order); } +void PDFPageContentProcessor::performProcessTextSequence(const TextSequence& textSequence, ProcessOrder order) +{ + Q_UNUSED(textSequence); + Q_UNUSED(order); +} + bool PDFPageContentProcessor::isContentKindSuppressed(ContentKind kind) const { Q_UNUSED(kind); @@ -528,6 +536,15 @@ PDFPageContentProcessor::PDFTransparencyGroup PDFPageContentProcessor::parseTran return group; } +void PDFPageContentProcessor::performInterceptInstruction(Operator currentOperator, + ProcessOrder processOrder, + const QByteArray& operatorAsText) +{ + Q_UNUSED(currentOperator); + Q_UNUSED(processOrder); + Q_UNUSED(operatorAsText); +} + void PDFPageContentProcessor::processContent(const QByteArray& content) { PDFLexicalAnalyzer parser(content.constBegin(), content.constEnd()); @@ -738,6 +755,12 @@ void PDFPageContentProcessor::processForm(const QTransform& matrix, const QByteArray& content, PDFInteger formStructuralParent) { + if (isContentKindSuppressed(ContentKind::Forms)) + { + // Process of forms is suppressed + return; + } + PDFPageContentProcessorStateGuard guard(this); PDFTemporaryValueChange structuralParentChangeGuard(&m_structuralParentKey, formStructuralParent); @@ -1129,6 +1152,9 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command) } } + performInterceptInstruction(op, ProcessOrder::BeforeOperation, command); + auto callInterceptInstAtEnd = qScopeGuard([&, this](){ performInterceptInstruction(op, ProcessOrder::AfterOperation, command); }); + switch (op) { case Operator::SetLineWidth: @@ -1855,6 +1881,11 @@ bool PDFPageContentProcessor::isProcessingCancelled() const return m_operationControl && m_operationControl->isOperationCancelled(); } +bool PDFPageContentProcessor::isTextProcessing() const +{ + return m_textBeginEndState > 0; +} + void PDFPageContentProcessor::reportRenderErrorOnce(RenderErrorType type, QString message) { if (!m_onceReportedErrors.count(message)) @@ -2718,7 +2749,7 @@ void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFOperandName font { try { - PDFFontPointer font = m_fontCache->getFont(m_fontDictionary->get(fontName.name)); + PDFFontPointer font = m_fontCache->getFont(m_fontDictionary->get(fontName.name), fontName.name); m_graphicState.setTextFont(qMove(font)); m_graphicState.setTextFontSize(fontSize); @@ -2973,7 +3004,7 @@ void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream) PDFImage pdfImage = PDFImage::createImage(m_document, stream, qMove(colorSpace), false, m_graphicState.getRenderingIntent(), this); - if (!performOriginalImagePainting(pdfImage)) + if (!performOriginalImagePainting(pdfImage, stream)) { QImage image = pdfImage.getImage(m_CMS, this, m_operationControl); @@ -3007,6 +3038,12 @@ void PDFPageContentProcessor::reportWarningAboutColorOperatorsInUTP() void PDFPageContentProcessor::processForm(const PDFStream* stream) { + if (isContentKindSuppressed(ContentKind::Forms)) + { + // Process of forms is suppressed + return; + } + PDFDocumentDataLoaderDecorator loader(getDocument()); const PDFDictionary* streamDictionary = stream->getDictionary(); @@ -3165,6 +3202,8 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence) return; } + performProcessTextSequence(textSequence, ProcessOrder::BeforeOperation); + const PDFRealizedFontPointer& font = getRealizedFont(); if (font) { @@ -3340,6 +3379,8 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence) { throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid font, text can't be printed.")); } + + performProcessTextSequence(textSequence, ProcessOrder::AfterOperation); } PDFRealizedFontPointer PDFPageContentProcessor::getRealizedFontImpl() @@ -3415,7 +3456,7 @@ bool PDFPageContentProcessor::isContentSuppressedByOC(PDFObjectReference ocgOrOc return false; } -PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() : +PDFPageContentProcessorState::PDFPageContentProcessorState() : m_currentTransformationMatrix(), m_strokeColorSpace(), m_fillColorSpace(), @@ -3456,55 +3497,60 @@ PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorSt m_strokeColorOriginal = m_fillColorOriginal; } -PDFPageContentProcessor::PDFPageContentProcessorState::~PDFPageContentProcessorState() -{ - -} - -PDFPageContentProcessor::PDFPageContentProcessorState& PDFPageContentProcessor::PDFPageContentProcessorState::operator=(const PDFPageContentProcessor::PDFPageContentProcessorState& other) -{ - setCurrentTransformationMatrix(other.getCurrentTransformationMatrix()); - setStrokeColorSpace(other.m_strokeColorSpace); - setFillColorSpace(other.m_fillColorSpace); - setStrokeColor(other.getStrokeColor(), other.getStrokeColorOriginal()); - setFillColor(other.getFillColor(), other.getFillColorOriginal()); - setLineWidth(other.getLineWidth()); - setLineCapStyle(other.getLineCapStyle()); - setLineJoinStyle(other.getLineJoinStyle()); - setMitterLimit(other.getMitterLimit()); - setLineDashPattern(other.getLineDashPattern()); - setRenderingIntentName(other.getRenderingIntentName()); - setFlatness(other.getFlatness()); - setSmoothness(other.getSmoothness()); - setTextCharacterSpacing(other.getTextCharacterSpacing()); - setTextWordSpacing(other.getTextWordSpacing()); - setTextHorizontalScaling(other.getTextHorizontalScaling()); - setTextLeading(other.getTextLeading()); - setTextFont(other.getTextFont()); - setTextFontSize(other.getTextFontSize()); - setTextRenderingMode(other.getTextRenderingMode()); - setTextRise(other.getTextRise()); - setTextKnockout(other.getTextKnockout()); - setTextMatrix(other.getTextMatrix()); - setTextLineMatrix(other.getTextLineMatrix()); - setAlphaStroking(other.getAlphaStroking()); - setAlphaFilling(other.getAlphaFilling()); - setBlendMode(other.getBlendMode()); - setRenderingIntent(other.getRenderingIntent()); - setOverprintMode(other.getOverprintMode()); - setAlphaIsShape(other.getAlphaIsShape()); - setStrokeAdjustment(other.getStrokeAdjustment()); - setSoftMask(other.getSoftMask()); - setBlackPointCompensationMode(other.getBlackPointCompensationMode()); - setBlackGenerationFunction(other.getBlackGenerationFunction()); - setUndercolorRemovalFunction(other.getUndercolorRemovalFunction()); - setTransferFunction(other.getTransferFunction()); - setHalftone(other.getHalftone()); - setHalftoneOrigin(other.getHalftoneOrigin()); +PDFPageContentProcessorState::~PDFPageContentProcessorState() +{ + +} + +PDFPageContentProcessorState& PDFPageContentProcessorState::operator=(const PDFPageContentProcessorState& other) +{ + setState(other); return *this; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setCurrentTransformationMatrix(const QTransform& currentTransformationMatrix) +void PDFPageContentProcessorState::setState(const PDFPageContentProcessorState& state) +{ + setCurrentTransformationMatrix(state.getCurrentTransformationMatrix()); + setStrokeColorSpace(state.m_strokeColorSpace); + setFillColorSpace(state.m_fillColorSpace); + setStrokeColor(state.getStrokeColor(), state.getStrokeColorOriginal()); + setFillColor(state.getFillColor(), state.getFillColorOriginal()); + setLineWidth(state.getLineWidth()); + setLineCapStyle(state.getLineCapStyle()); + setLineJoinStyle(state.getLineJoinStyle()); + setMitterLimit(state.getMitterLimit()); + setLineDashPattern(state.getLineDashPattern()); + setRenderingIntentName(state.getRenderingIntentName()); + setFlatness(state.getFlatness()); + setSmoothness(state.getSmoothness()); + setTextCharacterSpacing(state.getTextCharacterSpacing()); + setTextWordSpacing(state.getTextWordSpacing()); + setTextHorizontalScaling(state.getTextHorizontalScaling()); + setTextLeading(state.getTextLeading()); + setTextFont(state.getTextFont()); + setTextFontSize(state.getTextFontSize()); + setTextRenderingMode(state.getTextRenderingMode()); + setTextRise(state.getTextRise()); + setTextKnockout(state.getTextKnockout()); + setTextMatrix(state.getTextMatrix()); + setTextLineMatrix(state.getTextLineMatrix()); + setAlphaStroking(state.getAlphaStroking()); + setAlphaFilling(state.getAlphaFilling()); + setBlendMode(state.getBlendMode()); + setRenderingIntent(state.getRenderingIntent()); + setOverprintMode(state.getOverprintMode()); + setAlphaIsShape(state.getAlphaIsShape()); + setStrokeAdjustment(state.getStrokeAdjustment()); + setSoftMask(state.getSoftMask()); + setBlackPointCompensationMode(state.getBlackPointCompensationMode()); + setBlackGenerationFunction(state.getBlackGenerationFunction()); + setUndercolorRemovalFunction(state.getUndercolorRemovalFunction()); + setTransferFunction(state.getTransferFunction()); + setHalftone(state.getHalftone()); + setHalftoneOrigin(state.getHalftoneOrigin()); +} + +void PDFPageContentProcessorState::setCurrentTransformationMatrix(const QTransform& currentTransformationMatrix) { if (m_currentTransformationMatrix != currentTransformationMatrix) { @@ -3513,7 +3559,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setCurrentTransforma } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeColorSpace(const QSharedPointer& strokeColorSpace) +void PDFPageContentProcessorState::setStrokeColorSpace(const QSharedPointer& strokeColorSpace) { if (m_strokeColorSpace != strokeColorSpace) { @@ -3522,7 +3568,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeColorSpace( } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setFillColorSpace(const QSharedPointer& fillColorSpace) +void PDFPageContentProcessorState::setFillColorSpace(const QSharedPointer& fillColorSpace) { if (m_fillColorSpace != fillColorSpace) { @@ -3531,7 +3577,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setFillColorSpace(co } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeColor(const QColor& strokeColor, const PDFColor& originalColor) +void PDFPageContentProcessorState::setStrokeColor(const QColor& strokeColor, const PDFColor& originalColor) { if (m_strokeColor != strokeColor || m_strokeColorOriginal != originalColor) { @@ -3541,7 +3587,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeColor(const } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setFillColor(const QColor& fillColor, const PDFColor& originalColor) +void PDFPageContentProcessorState::setFillColor(const QColor& fillColor, const PDFColor& originalColor) { if (m_fillColor != fillColor || m_fillColorOriginal != originalColor) { @@ -3551,7 +3597,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setFillColor(const Q } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setLineWidth(PDFReal lineWidth) +void PDFPageContentProcessorState::setLineWidth(PDFReal lineWidth) { if (m_lineWidth != lineWidth) { @@ -3560,7 +3606,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setLineWidth(PDFReal } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setLineCapStyle(Qt::PenCapStyle lineCapStyle) +void PDFPageContentProcessorState::setLineCapStyle(Qt::PenCapStyle lineCapStyle) { if (m_lineCapStyle != lineCapStyle) { @@ -3569,7 +3615,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setLineCapStyle(Qt:: } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle) +void PDFPageContentProcessorState::setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle) { if (m_lineJoinStyle != lineJoinStyle) { @@ -3578,7 +3624,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setLineJoinStyle(Qt: } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setMitterLimit(PDFReal mitterLimit) +void PDFPageContentProcessorState::setMitterLimit(PDFReal mitterLimit) { if (m_mitterLimit != mitterLimit) { @@ -3587,7 +3633,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setMitterLimit(PDFRe } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setLineDashPattern(PDFLineDashPattern pattern) +void PDFPageContentProcessorState::setLineDashPattern(PDFLineDashPattern pattern) { if (m_lineDashPattern != pattern) { @@ -3596,7 +3642,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setLineDashPattern(P } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setRenderingIntentName(const QByteArray& renderingIntentName) +void PDFPageContentProcessorState::setRenderingIntentName(const QByteArray& renderingIntentName) { if (m_renderingIntentName != renderingIntentName) { @@ -3605,7 +3651,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setRenderingIntentNa } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setFlatness(PDFReal flatness) +void PDFPageContentProcessorState::setFlatness(PDFReal flatness) { if (m_flatness != flatness) { @@ -3614,7 +3660,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setFlatness(PDFReal } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setSmoothness(PDFReal smoothness) +void PDFPageContentProcessorState::setSmoothness(PDFReal smoothness) { if (m_smoothness != smoothness) { @@ -3623,7 +3669,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setSmoothness(PDFRea } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextLeading(PDFReal textLeading) +void PDFPageContentProcessorState::setTextLeading(PDFReal textLeading) { if (m_textLeading != textLeading) { @@ -3632,7 +3678,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextLeading(PDFRe } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextFontSize(PDFReal textFontSize) +void PDFPageContentProcessorState::setTextFontSize(PDFReal textFontSize) { if (m_textFontSize != textFontSize) { @@ -3641,7 +3687,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextFontSize(PDFR } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextKnockout(bool textKnockout) +void PDFPageContentProcessorState::setTextKnockout(bool textKnockout) { if (m_textKnockout != textKnockout) { @@ -3650,7 +3696,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextKnockout(bool } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextLineMatrix(const QTransform& textLineMatrix) +void PDFPageContentProcessorState::setTextLineMatrix(const QTransform& textLineMatrix) { if (m_textLineMatrix != textLineMatrix) { @@ -3659,7 +3705,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextLineMatrix(co } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setAlphaStroking(PDFReal alpha) +void PDFPageContentProcessorState::setAlphaStroking(PDFReal alpha) { if (m_alphaStroking != alpha) { @@ -3668,7 +3714,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setAlphaStroking(PDF } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setAlphaFilling(PDFReal alpha) +void PDFPageContentProcessorState::setAlphaFilling(PDFReal alpha) { if (m_alphaFilling != alpha) { @@ -3677,7 +3723,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setAlphaFilling(PDFR } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setBlendMode(BlendMode mode) +void PDFPageContentProcessorState::setBlendMode(BlendMode mode) { if (m_blendMode != mode) { @@ -3686,7 +3732,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setBlendMode(BlendMo } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setRenderingIntent(RenderingIntent renderingIntent) +void PDFPageContentProcessorState::setRenderingIntent(RenderingIntent renderingIntent) { if (m_renderingIntent != renderingIntent) { @@ -3695,21 +3741,21 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setRenderingIntent(R } } -QColor PDFPageContentProcessor::PDFPageContentProcessorState::getStrokeColorWithAlpha() const +QColor PDFPageContentProcessorState::getStrokeColorWithAlpha() const { QColor color = getStrokeColor(); color.setAlphaF(m_alphaStroking); return color; } -QColor PDFPageContentProcessor::PDFPageContentProcessorState::getFillColorWithAlpha() const +QColor PDFPageContentProcessorState::getFillColorWithAlpha() const { QColor color = getFillColor(); color.setAlphaF(m_alphaFilling); return color; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setOverprintMode(PDFOverprintMode overprintMode) +void PDFPageContentProcessorState::setOverprintMode(PDFOverprintMode overprintMode) { if (m_overprintMode != overprintMode) { @@ -3718,7 +3764,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setOverprintMode(PDF } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setAlphaIsShape(bool alphaIsShape) +void PDFPageContentProcessorState::setAlphaIsShape(bool alphaIsShape) { if (m_alphaIsShape != alphaIsShape) { @@ -3727,12 +3773,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setAlphaIsShape(bool } } -bool PDFPageContentProcessor::PDFPageContentProcessorState::getStrokeAdjustment() const +bool PDFPageContentProcessorState::getStrokeAdjustment() const { return m_strokeAdjustment; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeAdjustment(bool strokeAdjustment) +void PDFPageContentProcessorState::setStrokeAdjustment(bool strokeAdjustment) { if (m_strokeAdjustment != strokeAdjustment) { @@ -3741,12 +3787,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeAdjustment( } } -const PDFDictionary* PDFPageContentProcessor::PDFPageContentProcessorState::getSoftMask() const +const PDFDictionary* PDFPageContentProcessorState::getSoftMask() const { return m_softMask; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setSoftMask(const PDFDictionary* softMask) +void PDFPageContentProcessorState::setSoftMask(const PDFDictionary* softMask) { if (m_softMask != softMask) { @@ -3755,12 +3801,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setSoftMask(const PD } } -BlackPointCompensationMode PDFPageContentProcessor::PDFPageContentProcessorState::getBlackPointCompensationMode() const +BlackPointCompensationMode PDFPageContentProcessorState::getBlackPointCompensationMode() const { return m_blackPointCompensationMode; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setBlackPointCompensationMode(BlackPointCompensationMode blackPointCompensationMode) +void PDFPageContentProcessorState::setBlackPointCompensationMode(BlackPointCompensationMode blackPointCompensationMode) { if (m_blackPointCompensationMode != blackPointCompensationMode) { @@ -3769,12 +3815,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setBlackPointCompens } } -PDFObject PDFPageContentProcessor::PDFPageContentProcessorState::getHalftone() const +PDFObject PDFPageContentProcessorState::getHalftone() const { return m_halftone; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setHalftone(const PDFObject& halftone) +void PDFPageContentProcessorState::setHalftone(const PDFObject& halftone) { if (m_halftone != halftone) { @@ -3783,12 +3829,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setHalftone(const PD } } -QPointF PDFPageContentProcessor::PDFPageContentProcessorState::getHalftoneOrigin() const +QPointF PDFPageContentProcessorState::getHalftoneOrigin() const { return m_halftoneOrigin; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setHalftoneOrigin(const QPointF& halftoneOrigin) +void PDFPageContentProcessorState::setHalftoneOrigin(const QPointF& halftoneOrigin) { if (m_halftoneOrigin != halftoneOrigin) { @@ -3797,12 +3843,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setHalftoneOrigin(co } } -PDFObject PDFPageContentProcessor::PDFPageContentProcessorState::getTransferFunction() const +PDFObject PDFPageContentProcessorState::getTransferFunction() const { return m_transferFunction; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTransferFunction(const PDFObject& transferFunction) +void PDFPageContentProcessorState::setTransferFunction(const PDFObject& transferFunction) { if (m_transferFunction != transferFunction) { @@ -3811,12 +3857,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTransferFunction( } } -PDFObject PDFPageContentProcessor::PDFPageContentProcessorState::getUndercolorRemovalFunction() const +PDFObject PDFPageContentProcessorState::getUndercolorRemovalFunction() const { return m_undercolorRemovalFunction; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setUndercolorRemovalFunction(const PDFObject& undercolorRemovalFunction) +void PDFPageContentProcessorState::setUndercolorRemovalFunction(const PDFObject& undercolorRemovalFunction) { if (m_undercolorRemovalFunction != undercolorRemovalFunction) { @@ -3825,12 +3871,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setUndercolorRemoval } } -PDFObject PDFPageContentProcessor::PDFPageContentProcessorState::getBlackGenerationFunction() const +PDFObject PDFPageContentProcessorState::getBlackGenerationFunction() const { return m_blackGenerationFunction; } -void PDFPageContentProcessor::PDFPageContentProcessorState::setBlackGenerationFunction(const PDFObject& blackGenerationFunction) +void PDFPageContentProcessorState::setBlackGenerationFunction(const PDFObject& blackGenerationFunction) { if (m_blackGenerationFunction != blackGenerationFunction) { @@ -3839,7 +3885,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setBlackGenerationFu } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextMatrix(const QTransform& textMatrix) +void PDFPageContentProcessorState::setTextMatrix(const QTransform& textMatrix) { if (m_textMatrix != textMatrix) { @@ -3848,7 +3894,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextMatrix(const } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextRise(PDFReal textRise) +void PDFPageContentProcessorState::setTextRise(PDFReal textRise) { if (m_textRise != textRise) { @@ -3857,7 +3903,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextRise(PDFReal } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextRenderingMode(TextRenderingMode textRenderingMode) +void PDFPageContentProcessorState::setTextRenderingMode(TextRenderingMode textRenderingMode) { if (m_textRenderingMode != textRenderingMode) { @@ -3866,7 +3912,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextRenderingMode } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextFont(const PDFFontPointer& textFont) +void PDFPageContentProcessorState::setTextFont(const PDFFontPointer& textFont) { if (m_textFont != textFont) { @@ -3875,7 +3921,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextFont(const PD } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextHorizontalScaling(PDFReal textHorizontalScaling) +void PDFPageContentProcessorState::setTextHorizontalScaling(PDFReal textHorizontalScaling) { if (m_textHorizontalScaling != textHorizontalScaling) { @@ -3884,7 +3930,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextHorizontalSca } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextWordSpacing(PDFReal textWordSpacing) +void PDFPageContentProcessorState::setTextWordSpacing(PDFReal textWordSpacing) { if (m_textWordSpacing != textWordSpacing) { @@ -3893,7 +3939,7 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextWordSpacing(P } } -void PDFPageContentProcessor::PDFPageContentProcessorState::setTextCharacterSpacing(PDFReal textCharacterSpacing) +void PDFPageContentProcessorState::setTextCharacterSpacing(PDFReal textCharacterSpacing) { if (m_textCharacterSpacing != textCharacterSpacing) { diff --git a/Pdf4QtLibCore/sources/pdfpagecontentprocessor.h b/Pdf4QtLibCore/sources/pdfpagecontentprocessor.h index 559074e9..aa0a4a7f 100644 --- a/Pdf4QtLibCore/sources/pdfpagecontentprocessor.h +++ b/Pdf4QtLibCore/sources/pdfpagecontentprocessor.h @@ -48,6 +48,7 @@ class PDFOptionalContentActivity; static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState"; + class PDFLineDashPattern { public: @@ -79,6 +80,253 @@ class PDFLineDashPattern PDFReal m_dashOffset = 0.0; }; +struct PDFOverprintMode +{ + bool overprintStroking = false; + bool overprintFilling = false; + int overprintMode = 0; + + inline bool operator==(const PDFOverprintMode& other) const + { + return std::tie(overprintStroking, overprintFilling, overprintMode) == std::tie(other.overprintStroking, other.overprintFilling, other.overprintMode); + } + inline bool operator!=(const PDFOverprintMode& other) const + { + return !(*this == other); + } +}; + +/// Represents graphic state of the PDF (holding current graphic state parameters). +/// Please see PDF Reference 1.7, Chapter 4.3 "Graphic State" +class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessorState +{ +public: + explicit PDFPageContentProcessorState(); + ~PDFPageContentProcessorState(); + + PDFPageContentProcessorState(const PDFPageContentProcessorState&) = default; + PDFPageContentProcessorState(PDFPageContentProcessorState&&) = default; + + PDFPageContentProcessorState& operator=(PDFPageContentProcessorState&&) = delete; + PDFPageContentProcessorState& operator=(const PDFPageContentProcessorState& other); + + enum StateFlag : uint64_t + { + StateUnchanged = 0x0000000000000000, + StateCurrentTransformationMatrix = 0x0000000000000001, + StateStrokeColorSpace = 0x0000000000000002, + StateFillColorSpace = 0x0000000000000004, + StateStrokeColor = 0x0000000000000008, + StateFillColor = 0x0000000000000010, + StateLineWidth = 0x0000000000000020, + StateLineCapStyle = 0x0000000000000040, + StateLineJoinStyle = 0x0000000000000080, + StateMitterLimit = 0x0000000000000100, + StateLineDashPattern = 0x0000000000000200, + StateRenderingIntentName = 0x0000000000000400, + StateFlatness = 0x0000000000000800, + StateSmoothness = 0x0000000000001000, + StateTextMatrix = 0x0000000000002000, + StateTextLineMatrix = 0x0000000000004000, + StateTextCharacterSpacing = 0x0000000000008000, + StateTextWordSpacing = 0x0000000000010000, + StateTextHorizontalScaling = 0x0000000000020000, + StateTextLeading = 0x0000000000040000, + StateTextFont = 0x0000000000080000, + StateTextFontSize = 0x0000000000100000, + StateTextRenderingMode = 0x0000000000200000, + StateTextRise = 0x0000000000400000, + StateTextKnockout = 0x0000000000800000, + StateAlphaStroking = 0x0000000001000000, + StateAlphaFilling = 0x0000000002000000, + StateBlendMode = 0x0000000004000000, + StateRenderingIntent = 0x0000000008000000, + StateOverprint = 0x0000000010000000, + StateAlphaIsShape = 0x0000000020000000, + StateStrokeAdjustment = 0x0000000040000000, + StateSoftMask = 0x0000000080000000, + StateBlackPointCompensation = 0x0000000100000000, + StateBlackGenerationFunction = 0x0000000200000000, + StateUndercolorRemovalFunction = 0x0000000400000000, + StateTransferFunction = 0x0000000800000000, + StateHalftone = 0x0000001000000000, + StateHalftoneOrigin = 0x0000002000000000, + StateAll = 0xFFFFFFFFFFFFFFFF + }; + + using StateFlags = PDFFlags; + + void setState(const PDFPageContentProcessorState& state); + + const QTransform& getCurrentTransformationMatrix() const { return m_currentTransformationMatrix; } + void setCurrentTransformationMatrix(const QTransform& currentTransformationMatrix); + + const PDFAbstractColorSpace* getStrokeColorSpace() const { return m_strokeColorSpace.data(); } + void setStrokeColorSpace(const QSharedPointer& strokeColorSpace); + + const PDFAbstractColorSpace* getFillColorSpace() const { return m_fillColorSpace.data(); } + void setFillColorSpace(const QSharedPointer& fillColorSpace); + + const QColor& getStrokeColor() const { return m_strokeColor; } + const PDFColor& getStrokeColorOriginal() const { return m_strokeColorOriginal; } + void setStrokeColor(const QColor& strokeColor, const PDFColor& originalColor); + + const QColor& getFillColor() const { return m_fillColor; } + const PDFColor& getFillColorOriginal() const { return m_fillColorOriginal; } + void setFillColor(const QColor& fillColor, const PDFColor& originalColor); + + PDFReal getLineWidth() const { return m_lineWidth; } + void setLineWidth(PDFReal lineWidth); + + Qt::PenCapStyle getLineCapStyle() const { return m_lineCapStyle; } + void setLineCapStyle(Qt::PenCapStyle lineCapStyle); + + Qt::PenJoinStyle getLineJoinStyle() const { return m_lineJoinStyle; } + void setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle); + + PDFReal getMitterLimit() const { return m_mitterLimit; } + void setMitterLimit(PDFReal mitterLimit); + + const PDFLineDashPattern& getLineDashPattern() const { return m_lineDashPattern; } + void setLineDashPattern(PDFLineDashPattern pattern); + + const QByteArray& getRenderingIntentName() const { return m_renderingIntentName; } + void setRenderingIntentName(const QByteArray& renderingIntentName); + + PDFReal getFlatness() const { return m_flatness; } + void setFlatness(PDFReal flatness); + + PDFReal getSmoothness() const { return m_smoothness; } + void setSmoothness(PDFReal smoothness); + + StateFlags getStateFlags() const { return m_stateFlags; } + void setStateFlags(StateFlags stateFlags) { m_stateFlags = stateFlags; } + + PDFReal getTextCharacterSpacing() const { return m_textCharacterSpacing; } + void setTextCharacterSpacing(PDFReal textCharacterSpacing); + + PDFReal getTextWordSpacing() const { return m_textWordSpacing; } + void setTextWordSpacing(PDFReal textWordSpacing); + + PDFReal getTextHorizontalScaling() const { return m_textHorizontalScaling; } + void setTextHorizontalScaling(PDFReal textHorizontalScaling); + + PDFReal getTextLeading() const { return m_textLeading; } + void setTextLeading(PDFReal textLeading); + + const PDFFontPointer& getTextFont() const { return m_textFont; } + void setTextFont(const PDFFontPointer& textFont); + + PDFReal getTextFontSize() const { return m_textFontSize; } + void setTextFontSize(PDFReal textFontSize); + + TextRenderingMode getTextRenderingMode() const { return m_textRenderingMode; } + void setTextRenderingMode(TextRenderingMode textRenderingMode); + + PDFReal getTextRise() const { return m_textRise; } + void setTextRise(PDFReal textRise); + + bool getTextKnockout() const { return m_textKnockout; } + void setTextKnockout(bool textKnockout); + + const QTransform& getTextMatrix() const { return m_textMatrix; } + void setTextMatrix(const QTransform& textMatrix); + + const QTransform& getTextLineMatrix() const { return m_textLineMatrix; } + void setTextLineMatrix(const QTransform& textLineMatrix); + + PDFReal getAlphaStroking() const { return m_alphaStroking; } + void setAlphaStroking(PDFReal alpha); + + PDFReal getAlphaFilling() const { return m_alphaFilling; } + void setAlphaFilling(PDFReal alpha); + + BlendMode getBlendMode() const { return m_blendMode; } + void setBlendMode(BlendMode mode); + + RenderingIntent getRenderingIntent() const { return m_renderingIntent; } + void setRenderingIntent(RenderingIntent renderingIntent); + + /// Returns stroke color with alpha channel + QColor getStrokeColorWithAlpha() const; + + /// Returns fill color with alpha channel + QColor getFillColorWithAlpha() const; + + PDFOverprintMode getOverprintMode() const { return m_overprintMode; } + void setOverprintMode(PDFOverprintMode overprintMode); + + bool getAlphaIsShape() const { return m_alphaIsShape; } + void setAlphaIsShape(bool alphaIsShape); + + bool getStrokeAdjustment() const; + void setStrokeAdjustment(bool strokeAdjustment); + + const PDFDictionary* getSoftMask() const; + void setSoftMask(const PDFDictionary* softMask); + + BlackPointCompensationMode getBlackPointCompensationMode() const; + void setBlackPointCompensationMode(BlackPointCompensationMode blackPointCompensationMode); + + PDFObject getBlackGenerationFunction() const; + void setBlackGenerationFunction(const PDFObject& blackGenerationFunction); + + PDFObject getUndercolorRemovalFunction() const; + void setUndercolorRemovalFunction(const PDFObject& undercolorRemovalFunction); + + PDFObject getTransferFunction() const; + void setTransferFunction(const PDFObject& transferFunction); + + PDFObject getHalftone() const; + void setHalftone(const PDFObject& halftone); + + QPointF getHalftoneOrigin() const; + void setHalftoneOrigin(const QPointF& halftoneOrigin); + +private: + QTransform m_currentTransformationMatrix; + PDFColorSpacePointer m_strokeColorSpace; + PDFColorSpacePointer m_fillColorSpace; + QColor m_strokeColor; + PDFColor m_strokeColorOriginal; + QColor m_fillColor; + PDFColor m_fillColorOriginal; + PDFReal m_lineWidth; + Qt::PenCapStyle m_lineCapStyle; + Qt::PenJoinStyle m_lineJoinStyle; + PDFReal m_mitterLimit; + PDFLineDashPattern m_lineDashPattern; + QByteArray m_renderingIntentName; + PDFReal m_flatness; + PDFReal m_smoothness; + PDFReal m_textCharacterSpacing; // T_c + PDFReal m_textWordSpacing; // T_w + PDFReal m_textHorizontalScaling; // T_h, percentage + PDFReal m_textLeading; // T_l + PDFFontPointer m_textFont; // Text font + PDFReal m_textFontSize; // T_fs + TextRenderingMode m_textRenderingMode; // Text rendering mode + PDFReal m_textRise; // T_rise + bool m_textKnockout; + QTransform m_textMatrix; + QTransform m_textLineMatrix; + PDFReal m_alphaStroking; + PDFReal m_alphaFilling; + BlendMode m_blendMode; + RenderingIntent m_renderingIntent; + PDFOverprintMode m_overprintMode; + bool m_alphaIsShape; + bool m_strokeAdjustment; + const PDFDictionary* m_softMask; + BlackPointCompensationMode m_blackPointCompensationMode; + PDFObject m_blackGenerationFunction; + PDFObject m_undercolorRemovalFunction; + PDFObject m_transferFunction; + PDFObject m_halftone; + QPointF m_halftoneOrigin; + StateFlags m_stateFlags; +}; + /// Process the contents of the page. class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderErrorReporter { @@ -253,6 +501,29 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError /// Returns true, if page content processing is being cancelled bool isProcessingCancelled() const; + /// Returns true, if we are in a text processing + bool isTextProcessing() const; + + + /// Converts PDF line cap to Qt's pen cap style. Function always succeeds, + /// if invalid \p lineCap occurs, then some valid pen cap style is returned. + /// \param lineCap PDF Line cap style (see PDF Reference 1.7, values can be 0, 1, and 2) + static Qt::PenCapStyle convertLineCapToPenCapStyle(PDFInteger lineCap); + + /// Converts Qt's pen cap style to PDF's line cap style (defined in the PDF Reference) + /// \param penCapStyle Qt's pen cap style to be converted + static PDFInteger convertPenCapStyleToLineCap(Qt::PenCapStyle penCapStyle); + + /// Converts PDF line join to Qt's pen join style. Function always succeeds, + /// if invalid \p lineJoin occurs, then some valid pen join style is returned. + /// \param lineJoin PDF Line join style (see PDF Reference 1.7, values can be 0, 1, and 2) + static Qt::PenJoinStyle convertLineJoinToPenJoinStyle(PDFInteger lineJoin); + + /// Converts Qt's pen join style to PDF's line join style (defined in the PDF Reference) + /// \param penJoinStyle Qt's pen join style to be converted + static PDFInteger convertPenJoinStyleToLineJoin(Qt::PenJoinStyle penJoinStyle); + + protected: struct PDFTransparencyGroup @@ -296,257 +567,21 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError friend class PDFPageContentProcessor; }; - struct PDFOverprintMode - { - bool overprintStroking = false; - bool overprintFilling = false; - int overprintMode = 0; - - inline bool operator==(const PDFOverprintMode& other) const - { - return std::tie(overprintStroking, overprintFilling, overprintMode) == std::tie(other.overprintStroking, other.overprintFilling, other.overprintMode); - } - inline bool operator!=(const PDFOverprintMode& other) const - { - return !(*this == other); - } - }; - - /// Represents graphic state of the PDF (holding current graphic state parameters). - /// Please see PDF Reference 1.7, Chapter 4.3 "Graphic State" - class PDFPageContentProcessorState - { - public: - explicit PDFPageContentProcessorState(); - ~PDFPageContentProcessorState(); - - PDFPageContentProcessorState(const PDFPageContentProcessorState&) = default; - PDFPageContentProcessorState(PDFPageContentProcessorState&&) = default; - - PDFPageContentProcessorState& operator=(PDFPageContentProcessorState&&) = delete; - PDFPageContentProcessorState& operator=(const PDFPageContentProcessorState& other); - - enum StateFlag : uint64_t - { - StateUnchanged = 0x0000000000000000, - StateCurrentTransformationMatrix = 0x0000000000000001, - StateStrokeColorSpace = 0x0000000000000002, - StateFillColorSpace = 0x0000000000000004, - StateStrokeColor = 0x0000000000000008, - StateFillColor = 0x0000000000000010, - StateLineWidth = 0x0000000000000020, - StateLineCapStyle = 0x0000000000000040, - StateLineJoinStyle = 0x0000000000000080, - StateMitterLimit = 0x0000000000000100, - StateLineDashPattern = 0x0000000000000200, - StateRenderingIntentName = 0x0000000000000400, - StateFlatness = 0x0000000000000800, - StateSmoothness = 0x0000000000001000, - StateTextMatrix = 0x0000000000002000, - StateTextLineMatrix = 0x0000000000004000, - StateTextCharacterSpacing = 0x0000000000008000, - StateTextWordSpacing = 0x0000000000010000, - StateTextHorizontalScaling = 0x0000000000020000, - StateTextLeading = 0x0000000000040000, - StateTextFont = 0x0000000000080000, - StateTextFontSize = 0x0000000000100000, - StateTextRenderingMode = 0x0000000000200000, - StateTextRise = 0x0000000000400000, - StateTextKnockout = 0x0000000000800000, - StateAlphaStroking = 0x0000000001000000, - StateAlphaFilling = 0x0000000002000000, - StateBlendMode = 0x0000000004000000, - StateRenderingIntent = 0x0000000008000000, - StateOverprint = 0x0000000010000000, - StateAlphaIsShape = 0x0000000020000000, - StateStrokeAdjustment = 0x0000000040000000, - StateSoftMask = 0x0000000080000000, - StateBlackPointCompensation = 0x0000000100000000, - StateBlackGenerationFunction = 0x0000000200000000, - StateUndercolorRemovalFunction = 0x0000000400000000, - StateTransferFunction = 0x0000000800000000, - StateHalftone = 0x0000001000000000, - StateHalftoneOrigin = 0x0000002000000000, - StateAll = 0xFFFFFFFFFFFFFFFF - }; - - using StateFlags = PDFFlags; - - const QTransform& getCurrentTransformationMatrix() const { return m_currentTransformationMatrix; } - void setCurrentTransformationMatrix(const QTransform& currentTransformationMatrix); - - const PDFAbstractColorSpace* getStrokeColorSpace() const { return m_strokeColorSpace.data(); } - void setStrokeColorSpace(const QSharedPointer& strokeColorSpace); - - const PDFAbstractColorSpace* getFillColorSpace() const { return m_fillColorSpace.data(); } - void setFillColorSpace(const QSharedPointer& fillColorSpace); - - const QColor& getStrokeColor() const { return m_strokeColor; } - const PDFColor& getStrokeColorOriginal() const { return m_strokeColorOriginal; } - void setStrokeColor(const QColor& strokeColor, const PDFColor& originalColor); - - const QColor& getFillColor() const { return m_fillColor; } - const PDFColor& getFillColorOriginal() const { return m_fillColorOriginal; } - void setFillColor(const QColor& fillColor, const PDFColor& originalColor); - - PDFReal getLineWidth() const { return m_lineWidth; } - void setLineWidth(PDFReal lineWidth); - - Qt::PenCapStyle getLineCapStyle() const { return m_lineCapStyle; } - void setLineCapStyle(Qt::PenCapStyle lineCapStyle); - - Qt::PenJoinStyle getLineJoinStyle() const { return m_lineJoinStyle; } - void setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle); - - PDFReal getMitterLimit() const { return m_mitterLimit; } - void setMitterLimit(PDFReal mitterLimit); - - const PDFLineDashPattern& getLineDashPattern() const { return m_lineDashPattern; } - void setLineDashPattern(PDFLineDashPattern pattern); - - const QByteArray& getRenderingIntentName() const { return m_renderingIntentName; } - void setRenderingIntentName(const QByteArray& renderingIntentName); - - PDFReal getFlatness() const { return m_flatness; } - void setFlatness(PDFReal flatness); - - PDFReal getSmoothness() const { return m_smoothness; } - void setSmoothness(PDFReal smoothness); - - StateFlags getStateFlags() const { return m_stateFlags; } - void setStateFlags(StateFlags stateFlags) { m_stateFlags = stateFlags; } - - PDFReal getTextCharacterSpacing() const { return m_textCharacterSpacing; } - void setTextCharacterSpacing(PDFReal textCharacterSpacing); - - PDFReal getTextWordSpacing() const { return m_textWordSpacing; } - void setTextWordSpacing(PDFReal textWordSpacing); - - PDFReal getTextHorizontalScaling() const { return m_textHorizontalScaling; } - void setTextHorizontalScaling(PDFReal textHorizontalScaling); - - PDFReal getTextLeading() const { return m_textLeading; } - void setTextLeading(PDFReal textLeading); - - const PDFFontPointer& getTextFont() const { return m_textFont; } - void setTextFont(const PDFFontPointer& textFont); - - PDFReal getTextFontSize() const { return m_textFontSize; } - void setTextFontSize(PDFReal textFontSize); - - TextRenderingMode getTextRenderingMode() const { return m_textRenderingMode; } - void setTextRenderingMode(TextRenderingMode textRenderingMode); - - PDFReal getTextRise() const { return m_textRise; } - void setTextRise(PDFReal textRise); - - bool getTextKnockout() const { return m_textKnockout; } - void setTextKnockout(bool textKnockout); - - const QTransform& getTextMatrix() const { return m_textMatrix; } - void setTextMatrix(const QTransform& textMatrix); - - const QTransform& getTextLineMatrix() const { return m_textLineMatrix; } - void setTextLineMatrix(const QTransform& textLineMatrix); - - PDFReal getAlphaStroking() const { return m_alphaStroking; } - void setAlphaStroking(PDFReal alpha); - - PDFReal getAlphaFilling() const { return m_alphaFilling; } - void setAlphaFilling(PDFReal alpha); - - BlendMode getBlendMode() const { return m_blendMode; } - void setBlendMode(BlendMode mode); - - RenderingIntent getRenderingIntent() const { return m_renderingIntent; } - void setRenderingIntent(RenderingIntent renderingIntent); - - /// Returns stroke color with alpha channel - QColor getStrokeColorWithAlpha() const; - - /// Returns fill color with alpha channel - QColor getFillColorWithAlpha() const; - - PDFOverprintMode getOverprintMode() const { return m_overprintMode; } - void setOverprintMode(PDFOverprintMode overprintMode); - - bool getAlphaIsShape() const { return m_alphaIsShape; } - void setAlphaIsShape(bool alphaIsShape); - - bool getStrokeAdjustment() const; - void setStrokeAdjustment(bool strokeAdjustment); - - const PDFDictionary* getSoftMask() const; - void setSoftMask(const PDFDictionary* softMask); - - BlackPointCompensationMode getBlackPointCompensationMode() const; - void setBlackPointCompensationMode(BlackPointCompensationMode blackPointCompensationMode); - - PDFObject getBlackGenerationFunction() const; - void setBlackGenerationFunction(const PDFObject& blackGenerationFunction); - - PDFObject getUndercolorRemovalFunction() const; - void setUndercolorRemovalFunction(const PDFObject& undercolorRemovalFunction); - - PDFObject getTransferFunction() const; - void setTransferFunction(const PDFObject& transferFunction); - - PDFObject getHalftone() const; - void setHalftone(const PDFObject& halftone); - - QPointF getHalftoneOrigin() const; - void setHalftoneOrigin(const QPointF& halftoneOrigin); - - private: - QTransform m_currentTransformationMatrix; - PDFColorSpacePointer m_strokeColorSpace; - PDFColorSpacePointer m_fillColorSpace; - QColor m_strokeColor; - PDFColor m_strokeColorOriginal; - QColor m_fillColor; - PDFColor m_fillColorOriginal; - PDFReal m_lineWidth; - Qt::PenCapStyle m_lineCapStyle; - Qt::PenJoinStyle m_lineJoinStyle; - PDFReal m_mitterLimit; - PDFLineDashPattern m_lineDashPattern; - QByteArray m_renderingIntentName; - PDFReal m_flatness; - PDFReal m_smoothness; - PDFReal m_textCharacterSpacing; // T_c - PDFReal m_textWordSpacing; // T_w - PDFReal m_textHorizontalScaling; // T_h, percentage - PDFReal m_textLeading; // T_l - PDFFontPointer m_textFont; // Text font - PDFReal m_textFontSize; // T_fs - TextRenderingMode m_textRenderingMode; // Text rendering mode - PDFReal m_textRise; // T_rise - bool m_textKnockout; - QTransform m_textMatrix; - QTransform m_textLineMatrix; - PDFReal m_alphaStroking; - PDFReal m_alphaFilling; - BlendMode m_blendMode; - RenderingIntent m_renderingIntent; - PDFOverprintMode m_overprintMode; - bool m_alphaIsShape; - bool m_strokeAdjustment; - const PDFDictionary* m_softMask; - BlackPointCompensationMode m_blackPointCompensationMode; - PDFObject m_blackGenerationFunction; - PDFObject m_undercolorRemovalFunction; - PDFObject m_transferFunction; - PDFObject m_halftone; - QPointF m_halftoneOrigin; - StateFlags m_stateFlags; - }; - enum class ProcessOrder { BeforeOperation, AfterOperation }; + /// This function is used, when we directly want to intercept content + /// stream instructions and operands. + /// \param currentOperator Current operator + /// \param processOrder Mark before/after instruction is executed + /// \param operatorAsText Operator converted to text + virtual void performInterceptInstruction(Operator currentOperator, + ProcessOrder processOrder, + const QByteArray& operatorAsText); + /// This function has to be implemented in the client drawing implementation, it should /// draw the path according to the parameters. /// \param path Path, which should be drawn (can be emtpy - in that case nothing happens) @@ -575,8 +610,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError /// original image, it should return true, so no conversion to QImage occurs, /// which can be performance bottleneck. /// \param image Image + /// \param stream Stream, from which image originated /// \returns true, if image is successfully processed - virtual bool performOriginalImagePainting(const PDFImage& image); + virtual bool performOriginalImagePainting(const PDFImage& image, const PDFStream* stream); /// This function has to be implemented in the client drawing implementation, it should /// draw the image. @@ -645,6 +681,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError /// Implement to respond to text end operator virtual void performTextEnd(ProcessOrder order); + /// Implement to respond to text sequence processing + virtual void performProcessTextSequence(const TextSequence& textSequence, ProcessOrder order); + enum class ContentKind { Shapes, ///< General shapes (they can be also shaded / tiled) @@ -652,6 +691,7 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError Images, ///< Images Shading, ///< Shading Tiling, ///< Tiling + Forms, ///< Forms }; /// Override this function to disable particular content type (for example @@ -706,6 +746,9 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError /// Returns optional content activity const PDFOptionalContentActivity* getOptionalContentActivity() const { return m_optionalContentActivity; } + /// Returns operand for current operator + const PDFFlatArray& getOperands() const { return m_operands; } + class PDF4QTLIBCORESHARED_EXPORT PDFTransparencyGroupGuard { public: @@ -719,6 +762,14 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError /// Process form using form stream void processForm(const PDFStream* stream); + const PDFDictionary* getColorSpaceDictionary() const { return m_colorSpaceDictionary; } + const PDFDictionary* getFontDictionary() const { return m_fontDictionary; } + const PDFDictionary* getXObjectDictionary() const { return m_xobjectDictionary; } + const PDFDictionary* getExtendedGraphicStateDictionary() const { return m_extendedGraphicStateDictionary; } + const PDFDictionary* getPropertiesDictionary() const { return m_propertiesDictionary; } + const PDFDictionary* getShadingDictionary() const { return m_shadingDictionary; } + const PDFDictionary* getPatternDictionary() const { return m_patternDictionary; } + private: /// Initializes the resources dictionaries void initDictionaries(const PDFObject& resourcesObject); @@ -857,8 +908,6 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError template inline QColor getColorFromColorSpace(const PDFAbstractColorSpace* colorSpace, Operands... operands) { - - constexpr const size_t operandCount = sizeof...(Operands); const size_t colorSpaceComponentCount = colorSpace->getColorComponentCount(); if (operandCount == colorSpaceComponentCount) @@ -871,24 +920,6 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderError } } - /// Converts PDF line cap to Qt's pen cap style. Function always succeeds, - /// if invalid \p lineCap occurs, then some valid pen cap style is returned. - /// \param lineCap PDF Line cap style (see PDF Reference 1.7, values can be 0, 1, and 2) - static Qt::PenCapStyle convertLineCapToPenCapStyle(PDFInteger lineCap); - - /// Convers Qt's pen cap style to PDF's line cap style (defined in the PDF Reference) - /// \param penCapStyle Qt's pen cap style to be converted - static PDFInteger convertPenCapStyleToLineCap(Qt::PenCapStyle penCapStyle); - - /// Converts PDF line join to Qt's pen join style. Function always succeeds, - /// if invalid \p lineJoin occurs, then some valid pen join style is returned. - /// \param lineJoin PDF Line join style (see PDF Reference 1.7, values can be 0, 1, and 2) - static Qt::PenJoinStyle convertLineJoinToPenJoinStyle(PDFInteger lineJoin); - - /// Convers Qt's pen join style to PDF's line join style (defined in the PDF Reference) - /// \param penJoinStyle Qt's pen join style to be converted - static PDFInteger convertPenJoinStyleToLineJoin(Qt::PenJoinStyle penJoinStyle); - // General graphic state w, J, j, M, d, ri, i, gs void operatorSetLineWidth(PDFReal lineWidth); ///< w, sets the line width void operatorSetLineCap(PDFInteger lineCap); ///< J, sets the line cap diff --git a/Pdf4QtLibCore/sources/pdfpainter.cpp b/Pdf4QtLibCore/sources/pdfpainter.cpp index 00d23d13..02eecb74 100644 --- a/Pdf4QtLibCore/sources/pdfpainter.cpp +++ b/Pdf4QtLibCore/sources/pdfpainter.cpp @@ -114,56 +114,12 @@ bool PDFPainterBase::isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) QPen PDFPainterBase::getCurrentPenImpl() const { - const PDFPageContentProcessorState* graphicState = getGraphicState(); - QColor color = graphicState->getStrokeColor(); - if (color.isValid()) - { - color.setAlphaF(getEffectiveStrokingAlpha()); - const PDFReal lineWidth = graphicState->getLineWidth(); - Qt::PenCapStyle penCapStyle = graphicState->getLineCapStyle(); - Qt::PenJoinStyle penJoinStyle = graphicState->getLineJoinStyle(); - const PDFLineDashPattern& lineDashPattern = graphicState->getLineDashPattern(); - const PDFReal mitterLimit = graphicState->getMitterLimit(); - - QPen pen(color); - - pen.setWidthF(lineWidth); - pen.setCapStyle(penCapStyle); - pen.setJoinStyle(penJoinStyle); - pen.setMiterLimit(mitterLimit); - - if (lineDashPattern.isSolid()) - { - pen.setStyle(Qt::SolidLine); - } - else - { - pen.setStyle(Qt::CustomDashLine); - pen.setDashPattern(lineDashPattern.createForQPen(pen.widthF())); - pen.setDashOffset(lineDashPattern.getDashOffset()); - } - - return pen; - } - else - { - return QPen(Qt::NoPen); - } + return PDFPainterHelper::createPenFromState(getGraphicState(), getEffectiveStrokingAlpha()); } QBrush PDFPainterBase::getCurrentBrushImpl() const { - const PDFPageContentProcessorState* graphicState = getGraphicState(); - QColor color = graphicState->getFillColor(); - if (color.isValid()) - { - color.setAlphaF(getEffectiveFillingAlpha()); - return QBrush(color, Qt::SolidPattern); - } - else - { - return QBrush(Qt::NoBrush); - } + return PDFPainterHelper::createBrushFromState(getGraphicState(), getEffectiveFillingAlpha()); } PDFReal PDFPainterBase::getEffectiveStrokingAlpha() const diff --git a/Pdf4QtLibCore/sources/pdfpainterutils.cpp b/Pdf4QtLibCore/sources/pdfpainterutils.cpp index 5ff65bf6..80c3b3b1 100644 --- a/Pdf4QtLibCore/sources/pdfpainterutils.cpp +++ b/Pdf4QtLibCore/sources/pdfpainterutils.cpp @@ -16,6 +16,7 @@ // along with PDF4QT. If not, see . #include "pdfpainterutils.h" +#include "pdfpagecontentprocessor.h" #include #include @@ -64,5 +65,171 @@ QRect PDFPainterHelper::drawBubble(QPainter* painter, QPoint point, QColor color return rectangle; } +QPen PDFPainterHelper::createPenFromState(const PDFPageContentProcessorState* graphicState, double alpha) +{ + QColor color = graphicState->getStrokeColor(); + if (color.isValid()) + { + color.setAlphaF(alpha); + const PDFReal lineWidth = graphicState->getLineWidth(); + Qt::PenCapStyle penCapStyle = graphicState->getLineCapStyle(); + Qt::PenJoinStyle penJoinStyle = graphicState->getLineJoinStyle(); + const PDFLineDashPattern& lineDashPattern = graphicState->getLineDashPattern(); + const PDFReal mitterLimit = graphicState->getMitterLimit(); + + QPen pen(color); + + pen.setWidthF(lineWidth); + pen.setCapStyle(penCapStyle); + pen.setJoinStyle(penJoinStyle); + pen.setMiterLimit(mitterLimit); + + if (lineDashPattern.isSolid()) + { + pen.setStyle(Qt::SolidLine); + } + else + { + pen.setStyle(Qt::CustomDashLine); + pen.setDashPattern(lineDashPattern.createForQPen(pen.widthF())); + pen.setDashOffset(lineDashPattern.getDashOffset()); + } + + return pen; + } + else + { + return QPen(Qt::NoPen); + } +} + +QBrush PDFPainterHelper::createBrushFromState(const PDFPageContentProcessorState* graphicState, double alpha) +{ + QColor color = graphicState->getFillColor(); + if (color.isValid()) + { + color.setAlphaF(alpha); + return QBrush(color, Qt::SolidPattern); + } + else + { + return QBrush(Qt::NoBrush); + } +} + +void PDFPainterHelper::applyPenToGraphicState(PDFPageContentProcessorState* graphicState, const QPen& pen) +{ + if (pen.style() != Qt::NoPen) + { + graphicState->setLineWidth(pen.widthF()); + graphicState->setLineCapStyle(pen.capStyle()); + graphicState->setLineJoinStyle(pen.joinStyle()); + graphicState->setMitterLimit(pen.miterLimit()); + + QColor color = pen.color(); + + graphicState->setAlphaStroking(color.alphaF()); + + const PDFAbstractColorSpace* strokeColorSpace = graphicState->getStrokeColorSpace(); + if (!strokeColorSpace || strokeColorSpace->getColorSpace() != PDFAbstractColorSpace::ColorSpace::DeviceRGB) + { + graphicState->setStrokeColorSpace(QSharedPointer(new PDFDeviceRGBColorSpace())); + } + graphicState->setStrokeColor(color, PDFColor(color.redF(), color.greenF(), color.blueF())); + + if (pen.style() == Qt::SolidLine) + { + graphicState->setLineDashPattern(PDFLineDashPattern()); + } + else + { + PDFLineDashPattern lineDashPattern; + QList penPattern = pen.dashPattern(); + PDFReal penWidth = pen.widthF(); + + std::vector dashArray; + for (qreal value : penPattern) + { + dashArray.push_back(value * penWidth); + } + + lineDashPattern.setDashArray(std::move(dashArray)); + lineDashPattern.setDashOffset(pen.dashOffset()); + graphicState->setLineDashPattern(std::move(lineDashPattern)); + } + } +} + +void PDFPainterHelper::applyBrushToGraphicState(PDFPageContentProcessorState* graphicState, const QBrush& brush) +{ + if (brush.style() != Qt::NoBrush) + { + QColor color = brush.color(); + + graphicState->setAlphaFilling(color.alphaF()); + + const PDFAbstractColorSpace* fillColorSpace = graphicState->getFillColorSpace(); + if (!fillColorSpace || fillColorSpace->getColorSpace() != PDFAbstractColorSpace::ColorSpace::DeviceRGB) + { + graphicState->setFillColorSpace(QSharedPointer(new PDFDeviceRGBColorSpace())); + } + graphicState->setFillColor(color, PDFColor(color.redF(), color.greenF(), color.blueF())); + } +} + +PDFTransformationDecomposition PDFPainterHelper::decomposeTransform(const QTransform& transform) +{ + PDFTransformationDecomposition result; + + const qreal m11 = transform.m11(); + const qreal m12 = transform.m12(); + const qreal m21 = transform.m21(); + const qreal m22 = transform.m22(); + + const qreal dx = transform.dx(); + const qreal dy = transform.dy(); + + const qreal sx = std::sqrt(m11 * m11 + m21 * m21); + const qreal phi = std::atan2(m21, m11); + const qreal msy = m12 * std::cos(phi) + m22 * std::sin(phi); + const qreal sy = -m12 * std::sin(phi) + m22 * std::cos(phi); + + result.rotationAngle = phi; + result.scaleX = sx; + result.scaleY = sy; + + if (!qFuzzyIsNull(sy)) + { + result.shearFactor = msy / sy; + } + else + { + result.shearFactor = 0.0; + } + + result.translateX = dx; + result.translateY = dy; + + return result; +} + +QTransform PDFPainterHelper::composeTransform(const PDFTransformationDecomposition& decomposition) +{ + const qreal s = std::sin(decomposition.rotationAngle); + const qreal c = std::cos(decomposition.rotationAngle); + + const qreal m = decomposition.shearFactor; + const qreal sx = decomposition.scaleX; + const qreal sy = decomposition.scaleY; + const qreal dx = decomposition.translateX; + const qreal dy = decomposition.translateY; + + const qreal m11 = sx * c; + const qreal m12 = sy * m * c - sy * s; + const qreal m21 = sx * s; + const qreal m22 = sy * m * s + sy * c; + + return QTransform(m11, m12, m21, m22, dx, dy); +} } // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdfpainterutils.h b/Pdf4QtLibCore/sources/pdfpainterutils.h index ecef6e10..008a5edf 100644 --- a/Pdf4QtLibCore/sources/pdfpainterutils.h +++ b/Pdf4QtLibCore/sources/pdfpainterutils.h @@ -24,6 +24,7 @@ namespace pdf { +class PDFPageContentProcessorState; /// RAII wrapper for painter save/restore class PDFPainterStateGuard @@ -44,6 +45,16 @@ class PDFPainterStateGuard QPainter* m_painter; }; +struct PDFTransformationDecomposition +{ + double rotationAngle = 0.0; + double shearFactor = 0.0; + double scaleX = 0.0; + double scaleY = 0.0; + double translateX = 0.0; + double translateY = 0.0; +}; + class PDF4QTLIBCORESHARED_EXPORT PDFPainterHelper { public: @@ -55,6 +66,21 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPainterHelper /// \param text Text inside the bubble /// \param alignment Bubble alignment relative to the bubble position point static QRect drawBubble(QPainter* painter, QPoint point, QColor color, QString text, Qt::Alignment alignment); + + /// Creates pen from painter graphicState + static QPen createPenFromState(const PDFPageContentProcessorState* graphicState, double alpha); + + /// Creates brush from painter graphicState + static QBrush createBrushFromState(const PDFPageContentProcessorState* graphicState, double alpha); + + static void applyPenToGraphicState(PDFPageContentProcessorState* graphicState, const QPen& pen); + static void applyBrushToGraphicState(PDFPageContentProcessorState* graphicState, const QBrush& brush); + + /// Decompose transform + static PDFTransformationDecomposition decomposeTransform(const QTransform& transform); + + /// Compose transform + static QTransform composeTransform(const PDFTransformationDecomposition& decomposition); }; } // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdftextlayout.cpp b/Pdf4QtLibCore/sources/pdftextlayout.cpp index 2cd0be5a..9a62701e 100644 --- a/Pdf4QtLibCore/sources/pdftextlayout.cpp +++ b/Pdf4QtLibCore/sources/pdftextlayout.cpp @@ -18,6 +18,7 @@ #include "pdftextlayout.h" #include "pdfutils.h" #include "pdfexecutionpolicy.h" +#include "pdfcms.h" #include #include @@ -1509,7 +1510,11 @@ PDFTextLayout PDFTextLayoutStorageGetter::getTextLayoutImpl() const return m_storage ? m_storage->getTextLayout(m_pageIndex) : PDFTextLayout(); } -void PDFTextSelectionPainter::draw(QPainter* painter, PDFInteger pageIndex, PDFTextLayoutGetter& textLayoutGetter, const QTransform& matrix) +void PDFTextSelectionPainter::draw(QPainter* painter, + PDFInteger pageIndex, + PDFTextLayoutGetter& textLayoutGetter, + const QTransform& matrix, + const PDFColorConvertor& convertor) { Q_ASSERT(painter); @@ -1548,8 +1553,8 @@ void PDFTextSelectionPainter::draw(QPainter* painter, PDFInteger pageIndex, PDFT QColor brushColor = item.color; brushColor.setAlphaF(SELECTION_ALPHA); - painter->setPen(penColor); - painter->setBrush(QBrush(brushColor, Qt::SolidPattern)); + painter->setPen(convertor.convert(QPen(penColor))); + painter->setBrush(convertor.convert(QBrush(brushColor, Qt::SolidPattern))); painter->drawPath(path); } diff --git a/Pdf4QtLibCore/sources/pdftextlayout.h b/Pdf4QtLibCore/sources/pdftextlayout.h index e63a6203..b0cdc7e3 100644 --- a/Pdf4QtLibCore/sources/pdftextlayout.h +++ b/Pdf4QtLibCore/sources/pdftextlayout.h @@ -35,6 +35,7 @@ namespace pdf class PDFTextLayout; class PDFTextLayoutStorage; struct PDFCharacterPointer; +class PDFColorConvertor; struct PDFTextCharacterInfo { @@ -522,7 +523,7 @@ class PDF4QTLIBCORESHARED_EXPORT PDFTextSelectionPainter /// \param pageIndex Page index /// \param textLayoutGetter Text layout getter /// \param matrix Matrix which translates from page space to device space - void draw(QPainter* painter, PDFInteger pageIndex, PDFTextLayoutGetter& textLayoutGetter, const QTransform& matrix); + void draw(QPainter* painter, PDFInteger pageIndex, PDFTextLayoutGetter& textLayoutGetter, const QTransform& matrix, const PDFColorConvertor& convertor); /// Prepares geometry for text selection drawing, using text layout and matrix. If current text selection /// doesn't contain items from active page, then text layout is not accessed. diff --git a/Pdf4QtLibCore/sources/pdftransparencyrenderer.cpp b/Pdf4QtLibCore/sources/pdftransparencyrenderer.cpp index d7882e43..5a06ef33 100644 --- a/Pdf4QtLibCore/sources/pdftransparencyrenderer.cpp +++ b/Pdf4QtLibCore/sources/pdftransparencyrenderer.cpp @@ -2615,8 +2615,10 @@ void PDFTransparencyRenderer::performTextEnd(ProcessOrder order) } } -bool PDFTransparencyRenderer::performOriginalImagePainting(const PDFImage& image) +bool PDFTransparencyRenderer::performOriginalImagePainting(const PDFImage& image, const PDFStream* stream) { + Q_UNUSED(stream); + PDFFloatBitmap texture = getImage(image); if (m_settings.flags.testFlag(PDFTransparencyRendererSettings::SmoothImageTransformation) && image.isInterpolated()) diff --git a/Pdf4QtLibCore/sources/pdftransparencyrenderer.h b/Pdf4QtLibCore/sources/pdftransparencyrenderer.h index 43e71226..5eeb5f95 100644 --- a/Pdf4QtLibCore/sources/pdftransparencyrenderer.h +++ b/Pdf4QtLibCore/sources/pdftransparencyrenderer.h @@ -703,7 +703,7 @@ class PDF4QTLIBCORESHARED_EXPORT PDFTransparencyRenderer : public PDFPageContent virtual void performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup) override; virtual void performTextBegin(ProcessOrder order) override; virtual void performTextEnd(ProcessOrder order) override; - virtual bool performOriginalImagePainting(const PDFImage& image) override; + virtual bool performOriginalImagePainting(const PDFImage& image, const PDFStream* stream) override; virtual void performImagePainting(const QImage& image) override; virtual void performMeshPainting(const PDFMesh& mesh) override; diff --git a/Pdf4QtLibGui/pdfadvancedfindwidget.cpp b/Pdf4QtLibGui/pdfadvancedfindwidget.cpp index 671aa11e..cbcfed77 100644 --- a/Pdf4QtLibGui/pdfadvancedfindwidget.cpp +++ b/Pdf4QtLibGui/pdfadvancedfindwidget.cpp @@ -18,6 +18,7 @@ #include "pdfadvancedfindwidget.h" #include "ui_pdfadvancedfindwidget.h" +#include "pdfcms.h" #include "pdfcompiler.h" #include "pdfdocument.h" #include "pdfdrawspacecontroller.h" @@ -196,6 +197,7 @@ void PDFAdvancedFindWidget::drawPage(QPainter* painter, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -203,7 +205,7 @@ void PDFAdvancedFindWidget::drawPage(QPainter* painter, const pdf::PDFTextSelection& textSelection = getTextSelection(); pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection); - textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix); + textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, convertor); } void PDFAdvancedFindWidget::performSearch() diff --git a/Pdf4QtLibGui/pdfadvancedfindwidget.h b/Pdf4QtLibGui/pdfadvancedfindwidget.h index b8170d22..c2253b1b 100644 --- a/Pdf4QtLibGui/pdfadvancedfindwidget.h +++ b/Pdf4QtLibGui/pdfadvancedfindwidget.h @@ -54,6 +54,7 @@ class PDFAdvancedFindWidget : public QWidget, public pdf::IDocumentDrawInterface const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const override; void setDocument(const pdf::PDFModifiedDocument& document); diff --git a/Pdf4QtLibGui/pdfdocumentpropertiesdialog.cpp b/Pdf4QtLibGui/pdfdocumentpropertiesdialog.cpp index e3b7bcbc..0beb5f55 100644 --- a/Pdf4QtLibGui/pdfdocumentpropertiesdialog.cpp +++ b/Pdf4QtLibGui/pdfdocumentpropertiesdialog.cpp @@ -331,7 +331,7 @@ void PDFDocumentPropertiesDialog::initializeFonts(const pdf::PDFDocument* docume try { - if (pdf::PDFFontPointer font = pdf::PDFFont::createFont(object, document)) + if (pdf::PDFFontPointer font = pdf::PDFFont::createFont(object, fontsDictionary->getKey(i).getString(), document)) { pdf::PDFRenderErrorReporterDummy dummyReporter; pdf::PDFRealizedFontPointer realizedFont = pdf::PDFRealizedFont::createRealizedFont(font, 8.0, &dummyReporter); diff --git a/Pdf4QtLibWidgets/CMakeLists.txt b/Pdf4QtLibWidgets/CMakeLists.txt index 591ce207..db2fcdd0 100644 --- a/Pdf4QtLibWidgets/CMakeLists.txt +++ b/Pdf4QtLibWidgets/CMakeLists.txt @@ -65,6 +65,7 @@ add_library(Pdf4QtLibWidgets SHARED sources/pdfwidgetsglobal.h sources/pdfcertificatelisthelper.h sources/pdfcertificatelisthelper.cpp + sources/pdfpagecontenteditorediteditemsettings.h sources/pdfpagecontenteditorediteditemsettings.cpp sources/pdfpagecontenteditorediteditemsettings.ui ) include(GenerateExportHeader) diff --git a/Pdf4QtLibWidgets/sources/pdfadvancedtools.cpp b/Pdf4QtLibWidgets/sources/pdfadvancedtools.cpp index b6cdbdb9..6348280d 100644 --- a/Pdf4QtLibWidgets/sources/pdfadvancedtools.cpp +++ b/Pdf4QtLibWidgets/sources/pdfadvancedtools.cpp @@ -461,6 +461,7 @@ void PDFCreateLineTypeTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(pageIndex); @@ -468,7 +469,7 @@ void PDFCreateLineTypeTool::drawPage(QPainter* painter, Q_UNUSED(layoutGetter); Q_UNUSED(errors); - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_pickTool->getPageIndex()) { @@ -485,8 +486,8 @@ void PDFCreateLineTypeTool::drawPage(QPainter* painter, painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - QPen pen(m_strokeColor); - QBrush brush(m_fillColor, Qt::SolidPattern); + QPen pen = convertor.convert(QPen(m_strokeColor)); + QBrush brush = convertor.convert(QBrush(m_fillColor, Qt::SolidPattern)); pen.setWidthF(m_penWidth); painter->setPen(qMove(pen)); painter->setBrush(qMove(brush)); @@ -592,9 +593,10 @@ void PDFCreateEllipseTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_pickTool->getPageIndex()) { @@ -611,8 +613,8 @@ void PDFCreateEllipseTool::drawPage(QPainter* painter, painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - QPen pen(m_strokeColor); - QBrush brush(m_fillColor, Qt::SolidPattern); + QPen pen = convertor.convert(QPen(m_strokeColor)); + QBrush brush = convertor.convert(QBrush(m_fillColor, Qt::SolidPattern)); pen.setWidthF(m_penWidth); painter->setPen(qMove(pen)); painter->setBrush(qMove(brush)); @@ -672,9 +674,10 @@ void PDFCreateFreehandCurveTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_pageIndex || m_pickedPoints.empty()) { @@ -683,7 +686,7 @@ void PDFCreateFreehandCurveTool::drawPage(QPainter* painter, painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - QPen pen(m_strokeColor); + QPen pen = convertor.convert(QPen(m_strokeColor)); pen.setWidthF(m_penWidth); painter->setPen(qMove(pen)); painter->setRenderHint(QPainter::Antialiasing); @@ -833,6 +836,7 @@ void PDFCreateStampTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -854,7 +858,7 @@ void PDFCreateStampTool::drawPage(QPainter* painter, parameters.painter = painter; parameters.annotation = const_cast(&m_stampAnnotation); parameters.key.first = PDFAppeareanceStreams::Appearance::Normal; - parameters.colorConvertor = getProxy()->getCMSManager()->getColorConvertor(); + parameters.colorConvertor = convertor; PDFRenderer::applyFeaturesToColorConvertor(getProxy()->getFeatures(), parameters.colorConvertor); m_stampAnnotation.draw(parameters); @@ -933,13 +937,15 @@ void PDFCreateHighlightTextTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(errors); + Q_UNUSED(convertor); pdf::PDFTextSelectionPainter textSelectionPainter(&m_textSelection); - textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix); + textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, convertor); } void PDFCreateHighlightTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event) @@ -1173,13 +1179,15 @@ void PDFCreateRedactTextTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(errors); + Q_UNUSED(convertor); pdf::PDFTextSelectionPainter textSelectionPainter(&m_textSelection); - textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix); + textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, convertor); } void PDFCreateRedactTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event) diff --git a/Pdf4QtLibWidgets/sources/pdfadvancedtools.h b/Pdf4QtLibWidgets/sources/pdfadvancedtools.h index 856daff5..8a0a3d14 100644 --- a/Pdf4QtLibWidgets/sources/pdfadvancedtools.h +++ b/Pdf4QtLibWidgets/sources/pdfadvancedtools.h @@ -130,10 +130,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreateLineTypeTool : public PDFCreateAnno virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override; virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override; - virtual void drawPage(QPainter* painter, PDFInteger pageIndex, + virtual void drawPage(QPainter* painter, + PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; PDFReal getPenWidth() const; @@ -176,6 +178,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreateEllipseTool : public PDFCreateAnnot const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; PDFReal getPenWidth() const; @@ -211,6 +214,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreateFreehandCurveTool : public PDFCreat const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; @@ -249,6 +253,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreateStampTool : public PDFWidgetTool const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override; @@ -289,6 +294,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreateHighlightTextTool : public PDFWidge const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; @@ -353,6 +359,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreateRedactTextTool : public PDFWidgetTo const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; diff --git a/Pdf4QtLibWidgets/sources/pdfdocumentdrawinterface.h b/Pdf4QtLibWidgets/sources/pdfdocumentdrawinterface.h index 01d41905..9dd97fea 100644 --- a/Pdf4QtLibWidgets/sources/pdfdocumentdrawinterface.h +++ b/Pdf4QtLibWidgets/sources/pdfdocumentdrawinterface.h @@ -31,6 +31,7 @@ class QWheelEvent; namespace pdf { +class PDFColorConvertor; class PDFPrecompiledPage; class PDFTextLayoutGetter; @@ -47,18 +48,24 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT IDocumentDrawInterface /// \param compiledPage Compiled page /// \param layoutGetter Layout getter /// \param pagePointToDevicePointMatrix Matrix mapping page space to device point space + /// \param convertor Color convertor /// \param[out] errors Output parameter - rendering errors virtual void drawPage(QPainter* painter, pdf::PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const; /// Performs drawing of additional graphics after all pages are drawn onto the painter. /// \param painter Painter /// \param rect Draw rectangle (usually viewport rectangle of the pdf widget) virtual void drawPostRendering(QPainter* painter, QRect rect) const; + + /// Returns true if drawing of the page content should be suppressed. + /// This is used for special purposes, such as rendering edited page content. + virtual bool isPageContentDrawSuppressed() const; }; /// Input interface for handling events. Implementations should react on these events, diff --git a/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp b/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp index 845030da..6261f9d4 100644 --- a/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp +++ b/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp @@ -784,6 +784,11 @@ void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect, PDFRenderer::F // Use current paper color (it can be a bit different from white) QColor paperColor = getPaperColor(); + // Color management system + PDFCMSPointer cms = getCMSManager()->getCurrentCMS(); + PDFColorConvertor convertor = cms->getColorConvertor(); + PDFRenderer::applyFeaturesToColorConvertor(features, convertor); + // Iterate trough pages and display them on the painter device for (const LayoutItem& item : m_layout.items) { @@ -809,9 +814,19 @@ void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect, PDFRenderer::F const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex); QTransform matrix = QTransform(createPagePointToDevicePointMatrix(page, placedRect)) * baseMatrix; - compiledPage->draw(painter, page->getCropBox(), matrix, features, groupInfo.transparency); PDFTextLayoutGetter layoutGetter = m_textLayoutCompiler->getTextLayoutLazy(item.pageIndex); + bool isPageContentDrawSuppressed = false; + for (IDocumentDrawInterface* drawInterface : m_drawInterfaces) + { + isPageContentDrawSuppressed = isPageContentDrawSuppressed || drawInterface->isPageContentDrawSuppressed(); + } + + if (!isPageContentDrawSuppressed) + { + compiledPage->draw(painter, page->getCropBox(), matrix, features, groupInfo.transparency); + } + // Draw text blocks/text lines, if it is enabled if (features.testFlag(PDFRenderer::DebugTextBlocks)) { @@ -869,7 +884,7 @@ void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect, PDFRenderer::F for (IDocumentDrawInterface* drawInterface : m_drawInterfaces) { painter->save(); - drawInterface->drawPage(painter, item.pageIndex, compiledPage, layoutGetter, matrix, drawInterfaceErrors); + drawInterface->drawPage(painter, item.pageIndex, compiledPage, layoutGetter, matrix, convertor, drawInterfaceErrors); painter->restore(); } } @@ -1613,6 +1628,7 @@ void IDocumentDrawInterface::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const pdf::PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(painter); @@ -1620,6 +1636,7 @@ void IDocumentDrawInterface::drawPage(QPainter* painter, Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(pagePointToDevicePointMatrix); + Q_UNUSED(convertor); Q_UNUSED(errors); } @@ -1629,4 +1646,9 @@ void IDocumentDrawInterface::drawPostRendering(QPainter* painter, QRect rect) co Q_UNUSED(rect); } +bool IDocumentDrawInterface::isPageContentDrawSuppressed() const +{ + return false; +} + } // namespace pdf diff --git a/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp b/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp index bddd386a..186f7dcb 100644 --- a/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp +++ b/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp @@ -23,6 +23,7 @@ #include "pdfwidgetannotation.h" #include "pdfwidgetformmanager.h" #include "pdfblpainter.h" +#include "pdfpagecontentelements.h" #include #include @@ -145,6 +146,11 @@ void PDFWidget::onPageImageChanged(bool all, const std::vector& page } } +void PDFWidget::onSceneActiveStateChanged(bool) +{ + Q_EMIT sceneActivityChanged(); +} + void PDFWidget::removeInputInterface(IDrawWidgetInputInterface* inputInterface) { auto it = std::find(m_inputInterfaces.begin(), m_inputInterfaces.end(), inputInterface); @@ -152,6 +158,17 @@ void PDFWidget::removeInputInterface(IDrawWidgetInputInterface* inputInterface) { m_inputInterfaces.erase(it); } + + PDFPageContentScene* scene = dynamic_cast(inputInterface); + if (scene) + { + auto itScene = std::find(m_scenes.begin(), m_scenes.end(), inputInterface); + if (itScene != m_scenes.end()) + { + m_scenes.erase(itScene); + disconnect(scene, &PDFPageContentScene::sceneActiveStateChanged, this, &PDFWidget::onSceneActiveStateChanged); + } + } } void PDFWidget::addInputInterface(IDrawWidgetInputInterface* inputInterface) @@ -160,9 +177,29 @@ void PDFWidget::addInputInterface(IDrawWidgetInputInterface* inputInterface) { m_inputInterfaces.push_back(inputInterface); std::sort(m_inputInterfaces.begin(), m_inputInterfaces.end(), IDrawWidgetInputInterface::Comparator()); + + PDFPageContentScene* scene = dynamic_cast(inputInterface); + if (scene) + { + m_scenes.push_back(scene); + connect(scene, &PDFPageContentScene::sceneActiveStateChanged, this, &PDFWidget::onSceneActiveStateChanged); + } } } +bool PDFWidget::isAnySceneActive(PDFPageContentScene* sceneToSkip) const +{ + for (PDFPageContentScene* scene : m_scenes) + { + if (scene->isActive() && scene != sceneToSkip) + { + return true; + } + } + + return false; +} + PDFWidgetFormManager* PDFWidget::getFormManager() const { return m_formManager; diff --git a/Pdf4QtLibWidgets/sources/pdfdrawwidget.h b/Pdf4QtLibWidgets/sources/pdfdrawwidget.h index da864bd6..d27eacb8 100644 --- a/Pdf4QtLibWidgets/sources/pdfdrawwidget.h +++ b/Pdf4QtLibWidgets/sources/pdfdrawwidget.h @@ -38,6 +38,7 @@ class PDFDrawWidgetProxy; class PDFModifiedDocument; class PDFWidgetAnnotationManager; class IDrawWidgetInputInterface; +class PDFPageContentScene; class IDrawWidget { @@ -105,12 +106,17 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFWidget : public QWidget void removeInputInterface(IDrawWidgetInputInterface* inputInterface); void addInputInterface(IDrawWidgetInputInterface* inputInterface); + /// Returns true, if any scene is active + bool isAnySceneActive(PDFPageContentScene* sceneToSkip) const; + signals: + void sceneActivityChanged(); void pageRenderingErrorsChanged(pdf::PDFInteger pageIndex, int errorsCount); private: void onRenderingError(PDFInteger pageIndex, const QList& errors); void onPageImageChanged(bool all, const std::vector& pages); + void onSceneActiveStateChanged(bool); const PDFCMSManager* m_cmsManager; PDFToolManager* m_toolManager; @@ -122,6 +128,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFWidget : public QWidget PDFDrawWidgetProxy* m_proxy; PageRenderingErrors m_pageRenderingErrors; std::vector m_inputInterfaces; + std::vector m_scenes; RendererEngine m_rendererEngine; }; diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.cpp b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.cpp new file mode 100644 index 00000000..7c9fe6bb --- /dev/null +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.cpp @@ -0,0 +1,476 @@ +// Copyright (C) 2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "pdfpagecontenteditorediteditemsettings.h" +#include "ui_pdfpagecontenteditorediteditemsettings.h" + +#include "pdfpagecontentelements.h" +#include "pdfpagecontenteditorprocessor.h" +#include "pdfwidgetutils.h" +#include "pdfpainterutils.h" + +#include +#include +#include +#include +#include + +namespace pdf +{ + +PDFPageContentEditorEditedItemSettings::PDFPageContentEditorEditedItemSettings(QWidget* parent) : + QWidget(parent), + ui(new Ui::PDFPageContentEditorEditedItemSettings) +{ + ui->setupUi(this); + connect(ui->loadImageButton, &QPushButton::clicked, this, &PDFPageContentEditorEditedItemSettings::selectImage); + + for (const QString& colorName : QColor::colorNames()) + { + QColor color(colorName); + QIcon icon = getIconForColor(color); + + ui->penColorCombo->addItem(icon, colorName, color); + ui->brushColorCombo->addItem(icon, colorName, color); + } + + ui->penStyleCombo->addItem(tr("Solid"), int(Qt::SolidLine)); + ui->penStyleCombo->addItem(tr("Dashed"), int(Qt::DashLine)); + ui->penStyleCombo->addItem(tr("Dotted"), int(Qt::DotLine)); + ui->penStyleCombo->addItem(tr("Dash-dot"), int(Qt::DashDotLine)); + ui->penStyleCombo->addItem(tr("Dash-dot-dot"), int(Qt::DashDotDotLine)); + ui->penStyleCombo->addItem(tr("Custom"), int(Qt::CustomDashLine)); + ui->brushStyleCombo->addItem(tr("Solid"), int(Qt::SolidPattern)); + + connect(ui->selectPenColorButton, &QToolButton::clicked, this, &PDFPageContentEditorEditedItemSettings::onSelectPenColorButtonClicked); + connect(ui->selectBrushColorButton, &QToolButton::clicked, this, &PDFPageContentEditorEditedItemSettings::onSelectBrushColorButtonClicked); + connect(ui->penWidthEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PDFPageContentEditorEditedItemSettings::onPenWidthChanged); + connect(ui->penStyleCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onPenStyleChanged); + connect(ui->brushStyleCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onBrushStyleChanged); + connect(ui->penColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorEditedItemSettings::onPenColorComboTextChanged); + connect(ui->penColorCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onPenColorComboIndexChanged); + connect(ui->brushColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorEditedItemSettings::onBrushColorComboTextChanged); + connect(ui->brushColorCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorEditedItemSettings::onBrushColorComboIndexChanged); +} + +QIcon PDFPageContentEditorEditedItemSettings::getIconForColor(QColor color) const +{ + QIcon icon; + + QSize iconSize = PDFWidgetUtils::scaleDPI(this, QSize(16, 16)); + + QPixmap pixmap(iconSize.width(), iconSize.height()); + pixmap.fill(color); + icon.addPixmap(pixmap); + + return icon; +} + +PDFPageContentEditorEditedItemSettings::~PDFPageContentEditorEditedItemSettings() +{ + delete ui; +} + +void PDFPageContentEditorEditedItemSettings::loadFromElement(PDFPageContentElementEdited* editedElement) +{ + const PDFEditedPageContentElement* contentElement = editedElement->getElement(); + + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->imageTab)); + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->textTab)); + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->styleTab)); + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->transformationTab)); + + if (const PDFEditedPageContentElementImage* imageElement = contentElement->asImage()) + { + ui->tabWidget->addTab(ui->imageTab, tr("Image")); + m_image = imageElement->getImage(); + setImage(imageElement->getImage()); + } + + if (PDFEditedPageContentElementText* textElement = editedElement->getElement()->asText()) + { + ui->tabWidget->addTab(ui->textTab, tr("Text")); + QString text = textElement->getItemsAsText(); + ui->plainTextEdit->setPlainText(text); + } + + if (editedElement->getElement()->asText() || editedElement->getElement()->asPath()) + { + ui->tabWidget->addTab(ui->styleTab, tr("Style")); + } + + QTransform matrix = editedElement->getElement()->getTransform(); + PDFTransformationDecomposition decomposedTransformation = PDFPainterHelper::decomposeTransform(matrix); + + ui->rotationAngleEdit->setValue(qRadiansToDegrees(decomposedTransformation.rotationAngle)); + ui->scaleInXEdit->setValue(decomposedTransformation.scaleX); + ui->scaleInYEdit->setValue(decomposedTransformation.scaleY); + ui->shearFactorEdit->setValue(decomposedTransformation.shearFactor); + ui->translateInXEdit->setValue(decomposedTransformation.translateX); + ui->translateInYEdit->setValue(decomposedTransformation.translateY); + + ui->tabWidget->addTab(ui->transformationTab, tr("Transformation")); + + // Style + const PDFEditedPageContentElement* element = editedElement->getElement(); + + StyleFeatures features = None; + + if (element->asPath()) + { + features.setFlag(Pen); + features.setFlag(PenColor); + features.setFlag(Brush); + features.setFlag(StrokeFill); + } + + if (element->asText()) + { + features.setFlag(PenColor); + } + + const bool hasPen = features.testFlag(Pen); + const bool hasPenColor = features.testFlag(PenColor); + const bool hasBrush = features.testFlag(Brush); + const bool hasStrokeFill = features.testFlag(StrokeFill); + + ui->penWidthEdit->setEnabled(hasPen); + ui->penWidthLabel->setEnabled(hasPen); + + ui->penStyleCombo->setEnabled(hasPen); + ui->penStyleLabel->setEnabled(hasPen); + + ui->penColorCombo->setEnabled(hasPenColor); + ui->penColorLabel->setEnabled(hasPenColor); + ui->selectPenColorButton->setEnabled(hasPenColor); + + ui->brushStyleLabel->setEnabled(hasBrush); + ui->brushStyleCombo->setEnabled(hasBrush); + + ui->brushColorCombo->setEnabled(hasBrush); + ui->brushColorLabel->setEnabled(hasBrush); + ui->selectBrushColorButton->setEnabled(hasBrush); + + ui->strokePathCheckBox->setEnabled(hasStrokeFill); + ui->fillPathCheckBox->setEnabled(hasStrokeFill); + + if (const PDFEditedPageContentElementPath* pathElement = element->asPath()) + { + ui->strokePathCheckBox->setChecked(pathElement->getStrokePath()); + ui->fillPathCheckBox->setChecked(pathElement->getFillPath()); + } + + const PDFPageContentProcessorState& graphicState = element->getState(); + + QPen pen = pdf::PDFPainterHelper::createPenFromState(&graphicState, graphicState.getAlphaStroking()); + QBrush brush = pdf::PDFPainterHelper::createBrushFromState(&graphicState, graphicState.getAlphaFilling()); + + if (element->asText()) + { + pen.setColor(brush.color()); + } + + setPen(pen, true); + setBrush(brush, true); +} + +void PDFPageContentEditorEditedItemSettings::setPen(const QPen& pen, bool forceUpdate) +{ + if (m_pen != pen || forceUpdate) + { + m_pen = pen; + ui->penWidthEdit->setValue(pen.widthF()); + ui->penStyleCombo->setCurrentIndex(ui->penStyleCombo->findData(int(pen.style()))); + setColorToComboBox(ui->penColorCombo, pen.color()); + } +} + +void PDFPageContentEditorEditedItemSettings::setBrush(const QBrush& brush, bool forceUpdate) +{ + if (m_brush != brush || forceUpdate) + { + m_brush = brush; + ui->brushStyleCombo->setCurrentIndex(ui->brushStyleCombo->findData(int(brush.style()))); + setColorToComboBox(ui->brushColorCombo, brush.color()); + } +} + +void PDFPageContentEditorEditedItemSettings::setColorToComboBox(QComboBox* comboBox, QColor color) +{ + if (!color.isValid()) + { + return; + } + + QString name = color.name(QColor::HexArgb); + + int index = comboBox->findData(color, Qt::UserRole, Qt::MatchExactly); + + if (index == -1) + { + // Jakub Melka: try to find text (color name) + index = comboBox->findText(name); + } + + if (index != -1) + { + comboBox->setCurrentIndex(index); + } + else + { + comboBox->addItem(getIconForColor(color), name, color); + comboBox->setCurrentIndex(comboBox->count() - 1); + } +} + +void PDFPageContentEditorEditedItemSettings::saveToElement(PDFPageContentElementEdited* editedElement) +{ + if (PDFEditedPageContentElementImage* imageElement = editedElement->getElement()->asImage()) + { + imageElement->setImage(m_image); + imageElement->setImageObject(PDFObject()); + } + + if (PDFEditedPageContentElementText* textElement = editedElement->getElement()->asText()) + { + textElement->setItemsAsText(ui->plainTextEdit->toPlainText()); + } + + if (PDFEditedPageContentElementPath* pathElement = editedElement->getElement()->asPath()) + { + pathElement->setStrokePath(ui->strokePathCheckBox->isChecked()); + pathElement->setFillPath(ui->fillPathCheckBox->isChecked()); + } + + PDFTransformationDecomposition decomposedTransformation; + decomposedTransformation.rotationAngle = ui->rotationAngleEdit->value(); + decomposedTransformation.shearFactor = ui->shearFactorEdit->value(); + decomposedTransformation.scaleX = ui->scaleInXEdit->value(); + decomposedTransformation.scaleY = ui->scaleInYEdit->value(); + decomposedTransformation.translateX = ui->translateInXEdit->value(); + decomposedTransformation.translateY = ui->translateInYEdit->value(); + + QTransform transform = PDFPainterHelper::composeTransform(decomposedTransformation); + editedElement->getElement()->setTransform(transform); + + if (editedElement->getElement()->asPath()) + { + PDFPageContentProcessorState graphicState = editedElement->getElement()->getState(); + + PDFPainterHelper::applyPenToGraphicState(&graphicState, m_pen); + PDFPainterHelper::applyBrushToGraphicState(&graphicState, m_brush); + + editedElement->getElement()->setState(graphicState); + } + + if (editedElement->getElement()->asText()) + { + PDFPageContentProcessorState graphicState = editedElement->getElement()->getState(); + + QBrush brush(m_pen.color(), Qt::SolidPattern); + PDFPainterHelper::applyBrushToGraphicState(&graphicState, brush); + + editedElement->getElement()->setState(graphicState); + } +} + +static int PDF_gcd(int a, int b) +{ + if (b == 0) + { + return a; + } + + return PDF_gcd(b, a % b); +} + +void PDFPageContentEditorEditedItemSettings::setImage(QImage image) +{ + QSize imageSize = QSize(200, 200); + + int width = image.width(); + int height = image.height(); + + int n = width; + int d = height; + + int divisor = PDF_gcd(n, d); + if (divisor > 1) + { + n /= divisor; + d /= divisor; + } + + ui->imageWidthEdit->setText(QString("%1 px").arg(width)); + ui->imageHeightEdit->setText(QString("%1 px").arg(height)); + ui->imageRatioEdit->setText(QString("%1 : %2").arg(n).arg(d)); + + image.setDevicePixelRatio(this->devicePixelRatioF()); + image = image.scaled(imageSize * this->devicePixelRatioF(), Qt::KeepAspectRatio); + + ui->imageLabel->setPixmap(QPixmap::fromImage(image)); + ui->imageLabel->setFixedSize(PDFWidgetUtils::scaleDPI(this, imageSize)); +} + +void PDFPageContentEditorEditedItemSettings::selectImage() +{ + QString imageDirectory; + + QStringList pictureDirectiories = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); + if (!pictureDirectiories.isEmpty()) + { + imageDirectory = pictureDirectiories.last(); + } + else + { + imageDirectory = QDir::currentPath(); + } + + QList mimeTypes = QImageReader::supportedMimeTypes(); + QStringList mimeTypeFilters; + for (const QByteArray& mimeType : mimeTypes) + { + mimeTypeFilters.append(mimeType); + } + + QFileDialog dialog(this, tr("Select Image")); + dialog.setDirectory(imageDirectory); + dialog.setMimeTypeFilters(mimeTypeFilters); + dialog.selectMimeTypeFilter("image/svg+xml"); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::ExistingFile); + + if (dialog.exec() == QFileDialog::Accepted) + { + QString fileName = dialog.selectedFiles().constFirst(); + QImageReader reader(fileName); + QImage image = reader.read(); + + if (!image.isNull()) + { + setImage(image); + m_image = std::move(image); + } + } +} + +void PDFPageContentEditorEditedItemSettings::setPenColor(QColor color) +{ + if (color.isValid()) + { + m_pen.setColor(color); + setColorToComboBox(ui->penColorCombo, color); + } +} + +void PDFPageContentEditorEditedItemSettings::onSelectPenColorButtonClicked() +{ + QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Pen"), QColorDialog::ShowAlphaChannel); + setPenColor(color); +} + +void PDFPageContentEditorEditedItemSettings::setBrushColor(QColor color) +{ + if (color.isValid() && m_brush.color() != color) + { + m_brush.setColor(color); + setColorToComboBox(ui->brushColorCombo, color); + } +} + +const QBrush& PDFPageContentEditorEditedItemSettings::getBrush() const +{ + return m_brush; +} + +const QPen& PDFPageContentEditorEditedItemSettings::getPen() const +{ + return m_pen; +} + +void PDFPageContentEditorEditedItemSettings::onSelectBrushColorButtonClicked() +{ + QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Brush"), QColorDialog::ShowAlphaChannel); + setBrushColor(color); +} + +void PDFPageContentEditorEditedItemSettings::onPenWidthChanged(double value) +{ + m_pen.setWidthF(value); +} + +void PDFPageContentEditorEditedItemSettings::onPenStyleChanged() +{ + Qt::PenStyle penStyle = static_cast(ui->penStyleCombo->currentData().toInt()); + m_pen.setStyle(penStyle); +} + +void PDFPageContentEditorEditedItemSettings::onBrushStyleChanged() +{ + Qt::BrushStyle brushStyle = static_cast(ui->brushStyleCombo->currentData().toInt()); + m_brush.setStyle(brushStyle); +} + +void PDFPageContentEditorEditedItemSettings::onPenColorComboTextChanged() +{ + QColor color(ui->penColorCombo->currentText()); + if (color.isValid()) + { + setColorToComboBox(ui->penColorCombo, color); + m_pen.setColor(color); + } + else if (ui->penColorCombo->currentIndex() != -1) + { + ui->penColorCombo->setEditText(ui->penColorCombo->itemText(ui->penColorCombo->currentIndex())); + } +} + +void PDFPageContentEditorEditedItemSettings::onPenColorComboIndexChanged() +{ + const int index = ui->penColorCombo->currentIndex(); + QColor color = ui->penColorCombo->itemData(index, Qt::UserRole).value(); + if (color.isValid() && m_pen.color() != color) + { + m_pen.setColor(color); + } +} + +void PDFPageContentEditorEditedItemSettings::onBrushColorComboTextChanged() +{ + QColor color(ui->brushColorCombo->currentText()); + if (color.isValid()) + { + setColorToComboBox(ui->brushColorCombo, color); + m_brush.setColor(color); + } + else if (ui->brushColorCombo->currentIndex() != -1) + { + ui->brushColorCombo->setEditText(ui->brushColorCombo->itemText(ui->brushColorCombo->currentIndex())); + } +} + +void PDFPageContentEditorEditedItemSettings::onBrushColorComboIndexChanged() +{ + const int index = ui->brushColorCombo->currentIndex(); + QColor color = ui->brushColorCombo->itemData(index, Qt::UserRole).value(); + if (color.isValid()) + { + m_brush.setColor(color); + } +} + +} // namespace pdf diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.h b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.h new file mode 100644 index 00000000..19a02487 --- /dev/null +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.h @@ -0,0 +1,91 @@ +// Copyright (C) 2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef PDFPAGECONTENTEDITOREDITEDITEMSETTINGS_H +#define PDFPAGECONTENTEDITOREDITEDITEMSETTINGS_H + +#include +#include +#include + +class QComboBox; + +namespace Ui +{ +class PDFPageContentEditorEditedItemSettings; +} + +namespace pdf +{ +class PDFPageContentElementEdited; + +class PDFPageContentEditorEditedItemSettings : public QWidget +{ + Q_OBJECT + +public: + explicit PDFPageContentEditorEditedItemSettings(QWidget* parent); + virtual ~PDFPageContentEditorEditedItemSettings() override; + + void loadFromElement(PDFPageContentElementEdited* editedElement); + void saveToElement(PDFPageContentElementEdited* editedElement); + + void setPen(const QPen& pen, bool forceUpdate); + void setBrush(const QBrush& brush, bool forceUpdate); + + const QPen& getPen() const; + const QBrush& getBrush() const; + +private: + void onSelectPenColorButtonClicked(); + void onSelectBrushColorButtonClicked(); + void onPenWidthChanged(double value); + void onPenStyleChanged(); + void onBrushStyleChanged(); + void onPenColorComboTextChanged(); + void onPenColorComboIndexChanged(); + void onBrushColorComboTextChanged(); + void onBrushColorComboIndexChanged(); + + void setImage(QImage image); + void selectImage(); + + enum StyleFeature + { + None = 0, + Pen = 1 << 0, + PenColor = 1 << 1, + Brush = 1 << 2, + StrokeFill = 1 << 3, + }; + Q_DECLARE_FLAGS(StyleFeatures, StyleFeature) + + void setColorToComboBox(QComboBox* comboBox, QColor color); + QIcon getIconForColor(QColor color) const; + + void setPenColor(QColor color); + void setBrushColor(QColor color); + + Ui::PDFPageContentEditorEditedItemSettings* ui; + QImage m_image; + QPen m_pen; + QBrush m_brush; +}; + +} // namespace pdf + +#endif // PDFPAGECONTENTEDITOREDITEDITEMSETTINGS_H diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.ui b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.ui new file mode 100644 index 00000000..790879ff --- /dev/null +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorediteditemsettings.ui @@ -0,0 +1,421 @@ + + + PDFPageContentEditorEditedItemSettings + + + + 0 + 0 + 571 + 461 + + + + Dialog + + + + + + 0 + + + + Text + + + + + + Text Content + + + + + + + + + + + + + Image + + + + + + Image Content + + + + + + + + Height + + + + + + + Width + + + + + + + true + + + + + + + true + + + + + + + Ratio + + + + + + + true + + + + + + + + + true + + + + + + + + + + + + Load Image + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + Style + + + + + + ... + + + + + + + + + + Pen Style + + + + + + + Pen Color + + + + + + + ... + + + + + + + + + + Brush Style + + + + + + + Pen Width + + + + + + + Stroke path + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Brush Color + + + + + + + true + + + + + + + true + + + + + + + Fill path + + + + + + + + Transformation + + + + + + + + Translation in Y + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + Translation in X + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + Scale in X + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + Scale in Y + + + + + + + Shear factor + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p>Rotation angle</p></body></html> + + + + + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + φ + + + + + + + <html><head/><body><p>s<span style=" vertical-align:sub;">x</span></p></body></html> + + + + + + + <html><head/><body><p>s<span style=" vertical-align:sub;">y</span></p></body></html> + + + + + + + m + + + + + + + <html><head/><body><p>t<span style=" vertical-align:sub;">x</span></p></body></html> + + + + + + + <html><head/><body><p>t<span style=" vertical-align:sub;">y</span></p></body></html> + + + + + + + + + + + + + + + accept() + reject() + + diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorstylesettings.cpp b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorstylesettings.cpp index 8b88ee2f..c7bbb1ee 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorstylesettings.cpp +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorstylesettings.cpp @@ -20,6 +20,7 @@ #include "pdfwidgetutils.h" #include "pdfpagecontentelements.h" +#include "pdfpagecontenteditorediteditemsettings.h" #include #include @@ -38,7 +39,7 @@ PDFPageContentEditorStyleSettings::PDFPageContentEditorStyleSettings(QWidget* pa { ui->setupUi(this); - for (QString colorName : QColor::colorNames()) + for (const QString& colorName : QColor::colorNames()) { QColor color(colorName); QIcon icon = getIconForColor(color); @@ -263,59 +264,88 @@ bool PDFPageContentEditorStyleSettings::showEditElementStyleDialog(QWidget* pare dialog.setWindowTitle(tr("Edit Item")); dialog.setLayout(new QVBoxLayout()); - QTextEdit* textEdit = nullptr; - PDFPageContentStyledElement* styledElement = dynamic_cast(element); - PDFPageContentElementTextBox* textElement = dynamic_cast(element); - if (textElement) + PDFPageContentEditorStyleSettings* appearanceWidget = nullptr; + PDFPageContentElementEdited* editedElement = dynamic_cast(element); + if (editedElement) { - QGroupBox* contentGroupBox = new QGroupBox(&dialog); - textEdit = new QTextEdit(textElement->getText(), contentGroupBox); - textEdit->setFont(textElement->getFont()); - textEdit->setAlignment(textElement->getAlignment()); - textEdit->setTextColor(textElement->getPen().color()); - contentGroupBox->setTitle(tr("Content")); - contentGroupBox->setLayout(new QVBoxLayout()); - contentGroupBox->layout()->addWidget(textEdit); - dialog.layout()->addWidget(contentGroupBox); - } + PDFPageContentEditorEditedItemSettings* widget = new PDFPageContentEditorEditedItemSettings(&dialog); + dialog.layout()->addWidget(widget); - PDFPageContentEditorStyleSettings* appearanceWidget = new PDFPageContentEditorStyleSettings(&dialog); - appearanceWidget->loadFromElement(element, true); - if (textEdit) - { - connect(appearanceWidget, &PDFPageContentEditorStyleSettings::alignmentChanged, textEdit, &QTextEdit::setAlignment); - connect(appearanceWidget, &PDFPageContentEditorStyleSettings::fontChanged, textEdit, &QTextEdit::setFont); - connect(appearanceWidget, &PDFPageContentEditorStyleSettings::penChanged, textEdit, [textEdit](const QPen& pen) { textEdit->setTextColor(pen.color()); }); - } + QDialogButtonBox* dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); + connect(dialogButtonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + connect(dialogButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + dialog.layout()->addWidget(dialogButtonBox); - QGroupBox* appearanceGroupBox = new QGroupBox(&dialog); - appearanceGroupBox->setTitle(tr("Appearance")); - appearanceGroupBox->setLayout(new QVBoxLayout()); - appearanceGroupBox->layout()->addWidget(appearanceWidget); - dialog.layout()->addWidget(appearanceGroupBox); + pdf::PDFWidgetUtils::style(&dialog); - QDialogButtonBox* dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); - connect(dialogButtonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); - connect(dialogButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - dialog.layout()->addWidget(dialogButtonBox); + widget->loadFromElement(editedElement); - if (dialog.exec() == QDialog::Accepted) - { - if (styledElement) + if (dialog.exec() == QDialog::Accepted) { - styledElement->setPen(appearanceWidget->getPen()); - styledElement->setBrush(appearanceWidget->getBrush()); + widget->saveToElement(editedElement); + return true; } + } + else + { + QTextEdit* textEdit = nullptr; + PDFPageContentStyledElement* styledElement = dynamic_cast(element); + PDFPageContentElementTextBox* textElement = dynamic_cast(element); if (textElement) { - textElement->setText(textEdit->toPlainText()); - textElement->setFont(appearanceWidget->getFont()); - textElement->setAlignment(appearanceWidget->getAlignment()); - textElement->setAngle(appearanceWidget->getTextAngle()); + QGroupBox* contentGroupBox = new QGroupBox(&dialog); + textEdit = new QTextEdit(textElement->getText(), contentGroupBox); + textEdit->setFont(textElement->getFont()); + textEdit->setAlignment(textElement->getAlignment()); + textEdit->setTextColor(textElement->getPen().color()); + contentGroupBox->setTitle(tr("Content")); + contentGroupBox->setLayout(new QVBoxLayout()); + contentGroupBox->layout()->addWidget(textEdit); + dialog.layout()->addWidget(contentGroupBox); } - return true; + appearanceWidget = new PDFPageContentEditorStyleSettings(&dialog); + appearanceWidget->loadFromElement(element, true); + + if (textEdit) + { + connect(appearanceWidget, &PDFPageContentEditorStyleSettings::alignmentChanged, textEdit, &QTextEdit::setAlignment); + connect(appearanceWidget, &PDFPageContentEditorStyleSettings::fontChanged, textEdit, &QTextEdit::setFont); + connect(appearanceWidget, &PDFPageContentEditorStyleSettings::penChanged, textEdit, [textEdit](const QPen& pen) { textEdit->setTextColor(pen.color()); }); + } + + QGroupBox* appearanceGroupBox = new QGroupBox(&dialog); + appearanceGroupBox->setTitle(tr("Appearance")); + appearanceGroupBox->setLayout(new QVBoxLayout()); + appearanceGroupBox->layout()->addWidget(appearanceWidget); + dialog.layout()->addWidget(appearanceGroupBox); + + QDialogButtonBox* dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); + connect(dialogButtonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + connect(dialogButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + dialog.layout()->addWidget(dialogButtonBox); + + pdf::PDFWidgetUtils::style(&dialog); + + if (dialog.exec() == QDialog::Accepted) + { + if (styledElement) + { + styledElement->setPen(appearanceWidget->getPen()); + styledElement->setBrush(appearanceWidget->getBrush()); + } + + if (textElement) + { + textElement->setText(textEdit->toPlainText()); + textElement->setFont(appearanceWidget->getFont()); + textElement->setAlignment(appearanceWidget->getAlignment()); + textElement->setAngle(appearanceWidget->getTextAngle()); + } + + return true; + } } return false; diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.cpp b/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.cpp index 79539c22..eade8545 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.cpp +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.cpp @@ -148,9 +148,10 @@ void PDFCreatePCElementRectangleTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_pickTool->getPageIndex()) { @@ -166,7 +167,7 @@ void PDFCreatePCElementRectangleTool::drawPage(QPainter* painter, m_element->setPageIndex(pageIndex); m_element->setRectangle(rectangle); - m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + m_element->drawPage(painter, m_scene, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } const PDFPageContentElement* PDFCreatePCElementRectangleTool::getElement() const @@ -242,9 +243,10 @@ void PDFCreatePCElementLineTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_pickTool->getPageIndex() || !m_startPoint) { @@ -262,7 +264,7 @@ void PDFCreatePCElementLineTool::drawPage(QPainter* painter, m_element->setLine(line); } - m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + m_element->drawPage(painter, m_scene, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } const PDFPageContentElement* PDFCreatePCElementLineTool::getElement() const @@ -335,13 +337,14 @@ PDFCreatePCElementImageTool::~PDFCreatePCElementImageTool() } void PDFCreatePCElementImageTool::drawPage(QPainter* painter, - PDFInteger pageIndex, - const PDFPrecompiledPage* compiledPage, - PDFTextLayoutGetter& layoutGetter, - const QTransform& pagePointToDevicePointMatrix, - QList& errors) const + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, + QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_pickTool->getPageIndex()) { @@ -361,12 +364,12 @@ void PDFCreatePCElementImageTool::drawPage(QPainter* painter, PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); painter->setRenderHint(QPainter::Antialiasing); - painter->setPen(Qt::DotLine); + painter->setPen(convertor.convert(QPen(Qt::DotLine))); painter->setBrush(Qt::NoBrush); painter->drawRect(rectangle); } - m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + m_element->drawPage(painter, m_scene, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } const PDFPageContentElement* PDFCreatePCElementImageTool::getElement() const @@ -486,17 +489,18 @@ void PDFCreatePCElementDotTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); QPointF point = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint()); PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); painter->setRenderHint(QPainter::Antialiasing); - painter->setPen(m_element->getPen()); - painter->setBrush(m_element->getBrush()); + painter->setPen(convertor.convert(m_element->getPen())); + painter->setBrush(convertor.convert(m_element->getBrush())); painter->drawPoint(point); } @@ -547,16 +551,17 @@ void PDFCreatePCElementFreehandCurveTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_element->getPageIndex() || m_element->isEmpty()) { return; } - m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + m_element->drawPage(painter, m_scene, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } const PDFPageContentElement* PDFCreatePCElementFreehandCurveTool::getElement() const @@ -691,9 +696,10 @@ void PDFCreatePCElementTextTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (pageIndex != m_element->getPageIndex()) { @@ -707,7 +713,7 @@ void PDFCreatePCElementTextTool::drawPage(QPainter* painter, parameters.painter = painter; parameters.boundingRectangle = m_element->getRectangle(); parameters.key.first = PDFAppeareanceStreams::Appearance::Normal; - parameters.colorConvertor = getProxy()->getCMSManager()->getColorConvertor(); + parameters.colorConvertor = convertor; PDFRenderer::applyFeaturesToColorConvertor(getProxy()->getFeatures(), parameters.colorConvertor); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.h b/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.h index 4b0e9712..ecec0f33 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.h +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditortools.h @@ -79,6 +79,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreatePCElementRectangleTool : public PDF const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual const PDFPageContentElement* getElement() const override; @@ -113,6 +114,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreatePCElementImageTool : public PDFCrea const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual const PDFPageContentElement* getElement() const override; @@ -153,6 +155,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreatePCElementLineTool : public PDFCreat const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual const PDFPageContentElement* getElement() const override; @@ -187,6 +190,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreatePCElementDotTool : public PDFCreate const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual const PDFPageContentElement* getElement() const override; @@ -219,6 +223,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreatePCElementFreehandCurveTool : public const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual const PDFPageContentElement* getElement() const override; @@ -257,6 +262,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFCreatePCElementTextTool : public PDFCreat const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual const PDFPageContentElement* getElement() const override; diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.cpp b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.cpp index d3088ffe..57cb2a82 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.cpp +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.cpp @@ -85,6 +85,7 @@ PDFPageContentEditorWidget::PDFPageContentEditorWidget(QWidget* parent) : connect(&m_actionMapper, &QSignalMapper::mappedObject, this, &PDFPageContentEditorWidget::onActionTriggerRequest); connect(&m_operationMapper, &QSignalMapper::mappedInt, this, &PDFPageContentEditorWidget::operationTriggered); connect(ui->itemsListWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PDFPageContentEditorWidget::onItemSelectionChanged); + connect(ui->itemsListWidget, &QListWidget::itemDoubleClicked, this, &PDFPageContentEditorWidget::onItemDoubleClicked); connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::penChanged, this, &PDFPageContentEditorWidget::penChanged); connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::brushChanged, this, &PDFPageContentEditorWidget::brushChanged); @@ -225,6 +226,12 @@ void PDFPageContentEditorWidget::onItemSelectionChanged() } } +void PDFPageContentEditorWidget::onItemDoubleClicked(QListWidgetItem* item) +{ + const PDFInteger elementId = item->data(Qt::UserRole).toLongLong(); + Q_EMIT editElementRequest(elementId); +} + PDFPageContentScene* PDFPageContentEditorWidget::scene() const { return m_scene; diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.h b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.h index 7f7e3953..cf706610 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.h +++ b/Pdf4QtLibWidgets/sources/pdfpagecontenteditorwidget.h @@ -27,6 +27,7 @@ #include class QToolButton; +class QListWidgetItem; namespace Ui { @@ -74,11 +75,13 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentEditorWidget : public QDockWid void fontChanged(const QFont& font); void alignmentChanged(Qt::Alignment alignment); void textAngleChanged(pdf::PDFReal angle); + void editElementRequest(pdf::PDFInteger elementId); private: void onActionTriggerRequest(QObject* actionObject); void onActionChanged(); void onItemSelectionChanged(); + void onItemDoubleClicked(QListWidgetItem* item); Ui::PDFPageContentEditorWidget* ui; PDFPageContentEditorStyleSettings* m_settingsWidget; diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontentelements.cpp b/Pdf4QtLibWidgets/sources/pdfpagecontentelements.cpp index e81440db..17d7d46a 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontentelements.cpp +++ b/Pdf4QtLibWidgets/sources/pdfpagecontentelements.cpp @@ -21,6 +21,8 @@ #include "pdfdrawspacecontroller.h" #include "pdfwidgetutils.h" #include "pdfutils.h" +#include "pdfcms.h" +#include "pdfpagecontenteditorprocessor.h" #include #include @@ -29,10 +31,16 @@ #include #include #include +#include namespace pdf { +PDFPageContentElement::~PDFPageContentElement() +{ + +} + PDFInteger PDFPageContentElement::getPageIndex() const { return m_pageIndex; @@ -61,6 +69,7 @@ Qt::CursorShape PDFPageContentElement::getCursorShapeForManipulationMode(uint mo case Pt1: case Pt2: case Translate: + case Select: return Qt::ArrowCursor; case Top: @@ -154,6 +163,7 @@ void PDFPageContentElement::performRectangleManipulation(QRectF& rectangle, switch (mode) { case None: + case Select: break; case Translate: @@ -267,15 +277,18 @@ void PDFPageContentElementRectangle::setRectangle(const QRectF& newRectangle) } void PDFPageContentElementRectangle::drawPage(QPainter* painter, + const pdf::PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); + Q_UNUSED(scene); if (pageIndex != getPageIndex()) { @@ -284,8 +297,8 @@ void PDFPageContentElementRectangle::drawPage(QPainter* painter, PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - painter->setPen(getPen()); - painter->setBrush(getBrush()); + painter->setPen(convertor.convert(getPen())); + painter->setBrush(convertor.convert(getBrush())); painter->setRenderHint(QPainter::Antialiasing); QRectF rect = getRectangle(); @@ -330,6 +343,7 @@ PDFPageContentScene::PDFPageContentScene(QObject* parent) : QObject(parent), m_firstFreeId(1), m_isActive(false), + m_isPageContentDrawSuppressed(false), m_widget(nullptr), m_manipulator(this, nullptr) { @@ -380,6 +394,7 @@ void PDFPageContentScene::clear() { m_manipulator.reset(); m_elements.clear(); + m_firstFreeId = 1; Q_EMIT sceneChanged(false); } } @@ -639,11 +654,17 @@ int PDFPageContentScene::getInputPriority() const return ToolPriority + 1; } +bool PDFPageContentScene::isPageContentDrawSuppressed() const +{ + return isActive() && m_isPageContentDrawSuppressed; +} + void PDFPageContentScene::drawElements(QPainter* painter, PDFInteger pageIndex, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, const PDFPrecompiledPage* compiledPage, + const PDFColorConvertor& convertor, QList& errors) const { for (const auto& element : m_elements) @@ -653,7 +674,7 @@ void PDFPageContentScene::drawElements(QPainter* painter, continue; } - element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + element->drawPage(painter, this, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } } @@ -662,6 +683,7 @@ void PDFPageContentScene::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { if (!m_isActive) @@ -669,8 +691,8 @@ void PDFPageContentScene::drawPage(QPainter* painter, return; } - drawElements(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, compiledPage, errors); - m_manipulator.drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + drawElements(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, compiledPage, convertor, errors); + m_manipulator.drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } PDFPageContentScene::MouseEventInfo PDFPageContentScene::getMouseEventInfo(QWidget* widget, QPoint point) @@ -820,6 +842,11 @@ void PDFPageContentScene::onSelectionChanged() Q_EMIT selectionChanged(); } +void PDFPageContentScene::setIsPageContentDrawSuppressed(bool newIsPageContentDrawSuppressed) +{ + m_isPageContentDrawSuppressed = newIsPageContentDrawSuppressed; +} + PDFWidget* PDFPageContentScene::widget() const { return m_widget; @@ -848,6 +875,7 @@ void PDFPageContentScene::setActive(bool newIsActive) } Q_EMIT sceneChanged(false); + Q_EMIT sceneActiveStateChanged(newIsActive); } } @@ -890,6 +918,18 @@ std::set PDFPageContentScene::getPageIndices() const return result; } +std::map> PDFPageContentScene::getElementsByPage() const +{ + std::map> result; + + for (const auto& elementHandle : m_elements) + { + result[elementHandle->getPageIndex()].push_back(elementHandle.get()); + } + + return result; +} + QRectF PDFPageContentScene::getBoundingBox(PDFInteger pageIndex) const { QRectF rect; @@ -950,15 +990,18 @@ PDFPageContentElementLine* PDFPageContentElementLine::clone() const } void PDFPageContentElementLine::drawPage(QPainter* painter, + const pdf::PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); + Q_UNUSED(scene); if (pageIndex != getPageIndex()) { @@ -967,8 +1010,8 @@ void PDFPageContentElementLine::drawPage(QPainter* painter, PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - painter->setPen(getPen()); - painter->setBrush(getBrush()); + painter->setPen(convertor.convert(getPen())); + painter->setBrush(convertor.convert(getBrush())); painter->setRenderHint(QPainter::Antialiasing); painter->drawLine(getLine()); @@ -1132,15 +1175,18 @@ PDFPageContentImageElement* PDFPageContentImageElement::clone() const } void PDFPageContentImageElement::drawPage(QPainter* painter, - PDFInteger pageIndex, - const PDFPrecompiledPage* compiledPage, - PDFTextLayoutGetter& layoutGetter, - const QTransform& pagePointToDevicePointMatrix, - QList& errors) const + const pdf::PDFPageContentScene* scene, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, + QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); + Q_UNUSED(scene); if (pageIndex != getPageIndex() || !getRectangle().isValid()) { @@ -1185,7 +1231,8 @@ void PDFPageContentImageElement::drawPage(QPainter* painter, painter->scale(1.0, -1.0); targetRenderBox.moveTopLeft(QPointF(0, 0)); - painter->drawImage(targetRenderBox, m_image); + QImage image = convertor.convert(image); + painter->drawImage(targetRenderBox, image); } } @@ -1224,7 +1271,22 @@ void PDFPageContentImageElement::setContent(const QByteArray& newContent) if (m_content != newContent) { m_content = newContent; - if (!m_renderer->load(m_content)) + + m_renderer = std::make_unique(); + + QXmlStreamReader xml(m_content); + while (!xml.atEnd() && !xml.hasError()) + { + xml.readNext(); + } + + bool isSvgLoaded = false; + if (!xml.hasError()) + { + isSvgLoaded = m_renderer->load(m_content); + } + + if (!isSvgLoaded) { QByteArray imageData = m_content; QBuffer buffer(&imageData); @@ -1259,15 +1321,18 @@ PDFPageContentElementDot* PDFPageContentElementDot::clone() const } void PDFPageContentElementDot::drawPage(QPainter* painter, + const pdf::PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); + Q_UNUSED(scene); if (pageIndex != getPageIndex()) { @@ -1277,8 +1342,8 @@ void PDFPageContentElementDot::drawPage(QPainter* painter, PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); painter->setRenderHint(QPainter::Antialiasing); - painter->setPen(getPen()); - painter->setBrush(getBrush()); + painter->setPen(convertor.convert(getPen())); + painter->setBrush(convertor.convert(getBrush())); painter->drawPoint(m_point); } @@ -1347,15 +1412,18 @@ PDFPageContentElementFreehandCurve* PDFPageContentElementFreehandCurve::clone() } void PDFPageContentElementFreehandCurve::drawPage(QPainter* painter, + const pdf::PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); + Q_UNUSED(scene); if (pageIndex != getPageIndex()) { @@ -1364,8 +1432,8 @@ void PDFPageContentElementFreehandCurve::drawPage(QPainter* painter, PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - painter->setPen(getPen()); - painter->setBrush(getBrush()); + painter->setPen(convertor.convert(getPen())); + painter->setBrush(convertor.convert(getBrush())); painter->setRenderHint(QPainter::Antialiasing); painter->drawPath(getCurve()); @@ -2266,6 +2334,7 @@ void PDFPageContentElementManipulator::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { // Draw selection @@ -2291,8 +2360,8 @@ void PDFPageContentElementManipulator::drawPage(QPainter* painter, QBrush brush(Qt::SolidPattern); brush.setColor(QColor::fromRgbF(1.0f, 1.0f, 0.0f, 0.2f)); - painter->setPen(std::move(pen)); - painter->setBrush(std::move(brush)); + painter->setPen(convertor.convert(pen)); + painter->setBrush(convertor.convert(brush)); selectionPath = pagePointToDevicePointMatrix.map(selectionPath); painter->drawPath(selectionPath); @@ -2307,7 +2376,7 @@ void PDFPageContentElementManipulator::drawPage(QPainter* painter, for (const auto& manipulatedElement : m_manipulatedElements) { - manipulatedElement->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + manipulatedElement->drawPage(painter, m_scene, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); } } } @@ -2372,15 +2441,18 @@ PDFPageContentElementTextBox* PDFPageContentElementTextBox::clone() const } void PDFPageContentElementTextBox::drawPage(QPainter* painter, + const pdf::PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); + Q_UNUSED(scene); if (pageIndex != getPageIndex()) { @@ -2397,8 +2469,8 @@ void PDFPageContentElementTextBox::drawPage(QPainter* painter, PDFPainterStateGuard guard(painter); painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); - painter->setPen(getPen()); - painter->setBrush(getBrush()); + painter->setPen(convertor.convert(getPen())); + painter->setBrush(convertor.convert(getBrush())); painter->setFont(font); painter->setRenderHint(QPainter::Antialiasing); painter->setClipRect(rect, Qt::IntersectClip); @@ -2533,4 +2605,153 @@ void PDFPageContentElementTextBox::setAlignment(const Qt::Alignment& newAlignmen m_alignment = newAlignment; } +PDFPageContentElementEdited::PDFPageContentElementEdited(const PDFEditedPageContentElement* element) : + m_element(element->clone()) +{ + +} + +PDFPageContentElementEdited::~PDFPageContentElementEdited() +{ + +} + +PDFPageContentElementEdited* PDFPageContentElementEdited::clone() const +{ + PDFPageContentElementEdited* copy = new PDFPageContentElementEdited(m_element.get()); + copy->setElementId(getElementId()); + copy->setPageIndex(getPageIndex()); + return copy; +} + +void PDFPageContentElementEdited::drawPage(QPainter* painter, + const pdf::PDFPageContentScene* scene, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, + QList& errors) const +{ + PDFPainterStateGuard guard(painter); + + Q_UNUSED(pageIndex); + Q_UNUSED(compiledPage); + Q_UNUSED(layoutGetter); + Q_UNUSED(errors); + + painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true); + painter->setPen(convertor.convert(QPen(Qt::SolidLine))); + painter->setBrush(Qt::NoBrush); + painter->setFont(QApplication::font()); + painter->setRenderHint(QPainter::Antialiasing); + + const pdf::PDFPage* page = scene->getDocument()->getCatalog()->getPage(pageIndex); + QRectF mediaBox = page->getMediaBox(); + if (mediaBox.isValid()) + { + QPainterPath path; + path.addPolygon(mediaBox); + + painter->setClipPath(path, Qt::IntersectClip); + } + + painter->setTransform(m_element->getTransform(), true); + + if (const PDFEditedPageContentElementImage* imageElement = m_element->asImage()) + { + QRectF rect(0, 0, 1, 1); + painter->translate(rect.bottomLeft()); + painter->scale(1.0, -1.0); + + QRect transformedRect(0.0, 0.0, rect.width(), rect.height()); + + QImage image = convertor.convert(imageElement->getImage()); + painter->fillRect(transformedRect, Qt::white); + painter->drawImage(transformedRect, image); + } + + if (const PDFEditedPageContentElementPath* pathElement = m_element->asPath()) + { + const PDFPageContentProcessorState& state = m_element->getState(); + QPen pen = convertor.convert(pdf::PDFPainterHelper::createPenFromState(&state, state.getAlphaStroking())); + QBrush brush = convertor.convert(pdf::PDFPainterHelper::createBrushFromState(&state, state.getAlphaFilling())); + painter->setPen(pathElement->getStrokePath() ? pen : QPen(Qt::NoPen)); + painter->setBrush(pathElement->getFillPath() ? brush : QBrush(Qt::NoBrush)); + painter->drawPath(pathElement->getPath()); + } + + if (const PDFEditedPageContentElementText* textElement = m_element->asText()) + { + const PDFPageContentProcessorState& state = m_element->getState(); + painter->setBrush(convertor.convert(pdf::PDFPainterHelper::createBrushFromState(&state, state.getAlphaFilling()))); + painter->fillPath(textElement->getTextPath(), painter->brush()); + } +} + +uint PDFPageContentElementEdited::getManipulationMode(const QPointF& point, PDFReal snapPointDistanceThreshold) const +{ + Q_UNUSED(point); + Q_UNUSED(snapPointDistanceThreshold); + + if (getBoundingBox().contains(point)) + { + return Translate; + } + + return None; +} + +void PDFPageContentElementEdited::performManipulation(uint mode, const QPointF& offset) +{ + switch (mode) + { + case None: + break; + + case Translate: + { + QTransform transform = m_element->getTransform(); + QTransform updatedTransform(transform.m11(), transform.m12(), transform.m21(), transform.m22(), transform.dx() + offset.x(), transform.dy() + offset.y()); + m_element->setTransform(updatedTransform); + break; + } + + default: + Q_ASSERT(false); + break; + } +} + +QRectF PDFPageContentElementEdited::getBoundingBox() const +{ + return m_element->getBoundingBox(); +} + +void PDFPageContentElementEdited::setSize(QSizeF size) +{ + Q_UNUSED(size); +} + +QString PDFPageContentElementEdited::getDescription() const +{ + if (m_element->asImage()) + { + return formatDescription(PDFTranslationContext::tr("Image")); + } + + if (m_element->asText()) + { + return formatDescription(PDFTranslationContext::tr("Text")); + } + + if (m_element->asPath()) + { + return formatDescription(PDFTranslationContext::tr("Path")); + } + + return formatDescription(PDFTranslationContext::tr("Unknown")); +} + } // namespace pdf + diff --git a/Pdf4QtLibWidgets/sources/pdfpagecontentelements.h b/Pdf4QtLibWidgets/sources/pdfpagecontentelements.h index a3943499..ad3554b1 100644 --- a/Pdf4QtLibWidgets/sources/pdfpagecontentelements.h +++ b/Pdf4QtLibWidgets/sources/pdfpagecontentelements.h @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Jakub Melka +// Copyright (C) 2022-2024 Jakub Melka // // This file is part of PDF4QT. // @@ -34,23 +34,34 @@ class QSvgRenderer; namespace pdf { +class PDFCMS; class PDFWidget; class PDFDocument; class PDFPageContentScene; +class PDFEditedPageContentElement; +class PDFPageContentElementEdited; +class PDFPageContentElementRectangle; +class PDFPageContentElementLine; +class PDFPageContentElementDot; +class PDFPageContentElementFreehandCurve; +class PDFPageContentImageElement; +class PDFPageContentElementTextBox; class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElement { public: explicit PDFPageContentElement() = default; - virtual ~PDFPageContentElement() = default; + virtual ~PDFPageContentElement(); virtual PDFPageContentElement* clone() const = 0; virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const = 0; /// Returns manipulation mode. If manipulation mode is zero, then element @@ -89,6 +100,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElement enum ManipulationModes : uint { None = 0, + Select, Translate, Top, Left, @@ -102,6 +114,14 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElement Pt2 }; + virtual const PDFPageContentElementEdited* asElementEdited() const { return nullptr; } + virtual const PDFPageContentElementRectangle* asElementRectangle() const { return nullptr; } + virtual const PDFPageContentElementLine* asElementLine() const { return nullptr; } + virtual const PDFPageContentElementDot* asElementDot() const { return nullptr ; } + virtual const PDFPageContentElementFreehandCurve* asElementFreehandCurve() const { return nullptr; } + virtual const PDFPageContentImageElement* asElementImage() const { return nullptr; } + virtual const PDFPageContentElementTextBox* asElementTextBox() const { return nullptr; } + protected: uint getRectangleManipulationMode(const QRectF& rectangle, const QPointF& point, @@ -150,10 +170,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementRectangle : public PDFP void setRectangle(const QRectF& newRectangle); virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual uint getManipulationMode(const QPointF& point, @@ -163,6 +185,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementRectangle : public PDFP virtual QRectF getBoundingBox() const override; virtual void setSize(QSizeF size) override; virtual QString getDescription() const override; + virtual const PDFPageContentElementRectangle* asElementRectangle() const override { return this; } private: bool m_rounded = false; @@ -184,10 +207,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementLine : public PDFPageCo }; virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual uint getManipulationMode(const QPointF& point, @@ -197,6 +222,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementLine : public PDFPageCo virtual QRectF getBoundingBox() const override; virtual void setSize(QSizeF size) override; virtual QString getDescription() const override; + virtual const PDFPageContentElementLine* asElementLine() const override { return this; } LineGeometry getGeometry() const; void setGeometry(LineGeometry newGeometry); @@ -217,10 +243,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementDot : public PDFPageCon virtual PDFPageContentElementDot* clone() const override; virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual uint getManipulationMode(const QPointF& point, @@ -230,6 +258,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementDot : public PDFPageCon virtual QRectF getBoundingBox() const override; virtual void setSize(QSizeF size) override; virtual QString getDescription() const override; + virtual const PDFPageContentElementDot* asElementDot() const override { return this; } QPointF getPoint() const; void setPoint(QPointF newPoint); @@ -246,10 +275,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementFreehandCurve : public virtual PDFPageContentElementFreehandCurve* clone() const override; virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual uint getManipulationMode(const QPointF& point, @@ -259,6 +290,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementFreehandCurve : public virtual QRectF getBoundingBox() const override; virtual void setSize(QSizeF size); virtual QString getDescription() const override; + virtual const PDFPageContentElementFreehandCurve* asElementFreehandCurve() const override { return this; } QPainterPath getCurve() const; void setCurve(QPainterPath newCurve); @@ -281,10 +313,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentImageElement : public PDFPageC virtual PDFPageContentImageElement* clone() const override; virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual uint getManipulationMode(const QPointF& point, @@ -294,6 +328,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentImageElement : public PDFPageC virtual QRectF getBoundingBox() const override; virtual void setSize(QSizeF size); virtual QString getDescription() const override; + virtual const PDFPageContentImageElement* asElementImage() const override { return this; } const QByteArray& getContent() const; void setContent(const QByteArray& newContent); @@ -301,6 +336,9 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentImageElement : public PDFPageC const QRectF& getRectangle() const; void setRectangle(const QRectF& newRectangle); + const QSvgRenderer* getRenderer() const { return m_renderer.get(); } + const QImage& getImage() const { return m_image; } + private: QRectF m_rectangle; QByteArray m_content; @@ -319,10 +357,12 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementTextBox : public PDFPag void setRectangle(const QRectF& newRectangle) { m_rectangle = newRectangle; } virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual uint getManipulationMode(const QPointF& point, @@ -332,6 +372,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementTextBox : public PDFPag virtual QRectF getBoundingBox() const override; virtual void setSize(QSizeF size) override; virtual QString getDescription() const override; + virtual const PDFPageContentElementTextBox* asElementTextBox() const override { return this; } const QString& getText() const; void setText(const QString& newText); @@ -353,6 +394,35 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementTextBox : public PDFPag Qt::Alignment m_alignment = Qt::AlignCenter; }; +class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementEdited : public PDFPageContentElement +{ +public: + PDFPageContentElementEdited(const PDFEditedPageContentElement* element); + virtual ~PDFPageContentElementEdited(); + + virtual PDFPageContentElementEdited* clone() const override; + virtual void drawPage(QPainter* painter, + const PDFPageContentScene* scene, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, + QList& errors) const override; + virtual uint getManipulationMode(const QPointF& point, PDFReal snapPointDistanceThreshold) const override; + virtual void performManipulation(uint mode, const QPointF& offset) override; + virtual QRectF getBoundingBox() const override; + virtual void setSize(QSizeF size) override; + virtual QString getDescription() const override; + virtual const PDFPageContentElementEdited* asElementEdited() const { return this; } + + const PDFEditedPageContentElement* getElement() const { return m_element.get(); } + PDFEditedPageContentElement* getElement() { return m_element.get(); } + +private: + std::unique_ptr m_element; +}; + class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementManipulator : public QObject { Q_OBJECT @@ -442,6 +512,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementManipulator : public QO const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const; /// Returns bounding box of whole selection @@ -466,8 +537,8 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentElementManipulator : public QO }; class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentScene : public QObject, - public IDocumentDrawInterface, - public IDrawWidgetInputInterface + public IDocumentDrawInterface, + public IDrawWidgetInputInterface { Q_OBJECT @@ -509,6 +580,8 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentScene : public QObject, /// Returns set of involved pages std::set getPageIndices() const; + std::map> getElementsByPage() const; + /// Returns bounding box of elements on page QRectF getBoundingBox(PDFInteger pageIndex) const; @@ -538,12 +611,14 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentScene : public QObject, virtual QString getTooltip() const override; virtual const std::optional& getCursor() const override; virtual int getInputPriority() const override; + virtual bool isPageContentDrawSuppressed() const; virtual void drawPage(QPainter* painter, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; PDFWidget* widget() const; @@ -554,8 +629,11 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentScene : public QObject, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, const PDFPrecompiledPage* compiledPage, + const PDFColorConvertor& convertor, QList& errors) const; + void setIsPageContentDrawSuppressed(bool newIsPageContentDrawSuppressed); + signals: /// This signal is emitted when scene has changed (including graphics) void sceneChanged(bool graphicsOnly); @@ -565,6 +643,8 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentScene : public QObject, /// Request to edit the elements void editElementRequest(const std::set& elements); + void sceneActiveStateChanged(bool activated); + private: struct MouseEventInfo @@ -614,6 +694,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPageContentScene : public QObject, PDFInteger m_firstFreeId; bool m_isActive; + bool m_isPageContentDrawSuppressed; PDFWidget* m_widget; std::vector> m_elements; std::optional m_cursor; diff --git a/Pdf4QtLibWidgets/sources/pdftexteditpseudowidget.cpp b/Pdf4QtLibWidgets/sources/pdftexteditpseudowidget.cpp index 1cb7e394..7346d648 100644 --- a/Pdf4QtLibWidgets/sources/pdftexteditpseudowidget.cpp +++ b/Pdf4QtLibWidgets/sources/pdftexteditpseudowidget.cpp @@ -645,14 +645,14 @@ void PDFTextEditPseudowidget::draw(AnnotationDrawParameters& parameters, bool ed if (edit) { pdf::PDFPainterStateGuard guard2(painter); - painter->setPen(parameters.colorConvertor.convert(Qt::black, false, true)); + painter->setPen(parameters.colorConvertor.convert(QColor(Qt::black), false, true)); painter->setBrush(Qt::NoBrush); painter->drawRect(parameters.boundingRectangle); } painter->setClipRect(parameters.boundingRectangle, Qt::IntersectClip); painter->setWorldTransform(QTransform(createTextBoxTransformMatrix(edit)), true); - painter->setPen(parameters.colorConvertor.convert(Qt::black, false, true)); + painter->setPen(parameters.colorConvertor.convert(QColor(Qt::black), false, true)); if (isComb()) { diff --git a/Pdf4QtLibWidgets/sources/pdfwidgetformmanager.cpp b/Pdf4QtLibWidgets/sources/pdfwidgetformmanager.cpp index 941fcc8b..a2a861c0 100644 --- a/Pdf4QtLibWidgets/sources/pdfwidgetformmanager.cpp +++ b/Pdf4QtLibWidgets/sources/pdfwidgetformmanager.cpp @@ -1344,7 +1344,7 @@ void PDFFormFieldComboBoxEditor::draw(AnnotationDrawParameters& parameters, bool AnnotationDrawParameters listBoxParameters = parameters; listBoxParameters.boundingRectangle = m_listBoxPopupRectangle; - QColor color = parameters.colorConvertor.convert(Qt::white, true, false); + QColor color = parameters.colorConvertor.convert(QColor(Qt::white), true, false); listBoxParameters.painter->fillRect(listBoxParameters.boundingRectangle, color); m_listBox.draw(listBoxParameters, true); @@ -1751,7 +1751,7 @@ void PDFListBoxPseudowidget::draw(AnnotationDrawParameters& parameters, bool edi if (edit) { pdf::PDFPainterStateGuard guard2(painter); - painter->setPen(parameters.colorConvertor.convert(Qt::black, false, true)); + painter->setPen(parameters.colorConvertor.convert(QColor(Qt::black), false, true)); painter->setBrush(Qt::NoBrush); painter->drawRect(parameters.boundingRectangle); } diff --git a/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp b/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp index e1de00de..a1782029 100644 --- a/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp +++ b/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp @@ -20,6 +20,7 @@ #include "pdfcompiler.h" #include "pdfwidgetutils.h" #include "pdfpainterutils.h" +#include "pdfcms.h" #include #include @@ -346,6 +347,7 @@ void PDFFindTextTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -353,7 +355,7 @@ void PDFFindTextTool::drawPage(QPainter* painter, const pdf::PDFTextSelection& textSelection = getTextSelection(); pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection); - textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix); + textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, convertor); } void PDFFindTextTool::clearResults() @@ -713,13 +715,14 @@ void PDFSelectTextTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(errors); pdf::PDFTextSelectionPainter textSelectionPainter(&m_textSelection); - textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix); + textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix, convertor); } void PDFSelectTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event) @@ -1305,6 +1308,7 @@ void PDFPickTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { Q_UNUSED(compiledPage); @@ -1330,14 +1334,14 @@ void PDFPickTool::drawPage(QPainter* painter, QRect selectionRectangle(xMin, yMin, xMax - xMin, yMax - yMin); if (selectionRectangle.isValid()) { - painter->fillRect(selectionRectangle, m_selectionRectangleColor); + painter->fillRect(selectionRectangle, convertor.convert(m_selectionRectangleColor, false, true)); } } if (m_mode == Mode::Images && m_snapper.getSnappedImage()) { const PDFSnapper::ViewportSnapImage* snappedImage = m_snapper.getSnappedImage(); - painter->fillPath(snappedImage->viewportPath, m_selectionRectangleColor); + painter->fillPath(snappedImage->viewportPath, convertor.convert(m_selectionRectangleColor, false, true)); } } @@ -1644,9 +1648,10 @@ void PDFSelectTableTool::drawPage(QPainter* painter, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const { - BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); + BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, convertor, errors); if (isTablePicked() && pageIndex == m_pageIndex) { @@ -1658,8 +1663,8 @@ void PDFSelectTableTool::drawPage(QPainter* painter, QPen pen(Qt::SolidLine); pen.setWidthF(lineWidth); - painter->setPen(std::move(pen)); - painter->setBrush(QBrush(color)); + painter->setPen(convertor.convert(pen)); + painter->setBrush(convertor.convert(QBrush(color))); painter->drawRect(rectangle); for (const PDFReal columnPosition : m_horizontalBreaks) diff --git a/Pdf4QtLibWidgets/sources/pdfwidgettool.h b/Pdf4QtLibWidgets/sources/pdfwidgettool.h index b6b3ba36..2b644a81 100644 --- a/Pdf4QtLibWidgets/sources/pdfwidgettool.h +++ b/Pdf4QtLibWidgets/sources/pdfwidgettool.h @@ -188,6 +188,7 @@ class PDFFindTextTool : public PDFWidgetTool const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; protected: @@ -265,6 +266,7 @@ class PDFSelectTextTool : public PDFWidgetTool const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; @@ -360,6 +362,7 @@ class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPickTool : public PDFWidgetTool const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void drawPostRendering(QPainter* painter, QRect rect) const override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; @@ -428,6 +431,7 @@ class PDFSelectTableTool : public PDFWidgetTool const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, + const PDFColorConvertor& convertor, QList& errors) const override; virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override; diff --git a/PdfTool/pdftoolinfofonts.cpp b/PdfTool/pdftoolinfofonts.cpp index 61339bf6..00049c41 100644 --- a/PdfTool/pdftoolinfofonts.cpp +++ b/PdfTool/pdftoolinfofonts.cpp @@ -120,7 +120,7 @@ int PDFToolInfoFonts::execute(const PDFToolOptions& options) try { - if (pdf::PDFFontPointer font = pdf::PDFFont::createFont(object, &document)) + if (pdf::PDFFontPointer font = pdf::PDFFont::createFont(object, fontsDictionary->getKey(i).getString(), &document)) { pdf::PDFRenderErrorReporterDummy dummyReporter; pdf::PDFRealizedFontPointer realizedFont = pdf::PDFRealizedFont::createRealizedFont(font, 8.0, &dummyReporter);