mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Editor plugin: SVG images
This commit is contained in:
@ -210,6 +210,9 @@ bool EditorPlugin::save()
|
|||||||
const pdf::PDFPage* page = m_document->getCatalog()->getPage(pageIndex);
|
const pdf::PDFPage* page = m_document->getCatalog()->getPage(pageIndex);
|
||||||
const pdf::PDFEditedPageContent& editedPageContent = m_editedPageContent.at(pageIndex);
|
const pdf::PDFEditedPageContent& editedPageContent = m_editedPageContent.at(pageIndex);
|
||||||
|
|
||||||
|
QRectF mediaBox = page->getMediaBox();
|
||||||
|
QRectF mediaBoxMM = page->getMediaBoxMM();
|
||||||
|
|
||||||
pdf::PDFPageContentEditorContentStreamBuilder contentStreamBuilder(m_document);
|
pdf::PDFPageContentEditorContentStreamBuilder contentStreamBuilder(m_document);
|
||||||
contentStreamBuilder.setFontDictionary(editedPageContent.getFontDictionary());
|
contentStreamBuilder.setFontDictionary(editedPageContent.getFontDictionary());
|
||||||
|
|
||||||
@ -290,8 +293,24 @@ bool EditorPlugin::save()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// It is probably an SVG image
|
// It is probably an SVG image
|
||||||
|
pdf::PDFContentEditorPaintDevice paintDevice(&contentStreamBuilder, mediaBox, mediaBoxMM);
|
||||||
|
QPainter painter(&paintDevice);
|
||||||
|
|
||||||
|
QList<pdf::PDFRenderError> errors;
|
||||||
|
pdf::PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex);
|
||||||
|
elementImage->drawPage(&painter, &m_scene, pageIndex, nullptr, textLayoutGetter, QTransform(), errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (elementTextBox)
|
||||||
|
{
|
||||||
|
pdf::PDFContentEditorPaintDevice paintDevice(&contentStreamBuilder, mediaBox, mediaBoxMM);
|
||||||
|
QPainter painter(&paintDevice);
|
||||||
|
|
||||||
|
QList<pdf::PDFRenderError> errors;
|
||||||
|
pdf::PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex);
|
||||||
|
elementTextBox->drawPage(&painter, &m_scene, pageIndex, nullptr, textLayoutGetter, QTransform(), errors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +168,19 @@ QPainter::CompositionMode PDFBlendModeInfo::getCompositionModeFromBlendMode(Blen
|
|||||||
return QPainter::CompositionMode_SourceOver;
|
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)
|
QString PDFBlendModeInfo::getBlendModeName(BlendMode mode)
|
||||||
{
|
{
|
||||||
for (const std::pair<const char*, BlendMode>& info : BLEND_MODE_INFOS)
|
for (const std::pair<const char*, BlendMode>& info : BLEND_MODE_INFOS)
|
||||||
|
@ -86,6 +86,9 @@ public:
|
|||||||
/// \param mode Blend mode
|
/// \param mode Blend mode
|
||||||
static QPainter::CompositionMode getCompositionModeFromBlendMode(BlendMode 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
|
/// Returns blend mode name
|
||||||
/// \param mode Blend mode
|
/// \param mode Blend mode
|
||||||
static QString getBlendModeName(BlendMode mode);
|
static QString getBlendModeName(BlendMode mode);
|
||||||
|
@ -24,10 +24,185 @@
|
|||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
#include <QPaintEngine>
|
||||||
|
|
||||||
namespace pdf
|
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) :
|
PDFPageContentEditorContentStreamBuilder::PDFPageContentEditorContentStreamBuilder(PDFDocument* document) :
|
||||||
m_document(document)
|
m_document(document)
|
||||||
{
|
{
|
||||||
@ -818,6 +993,26 @@ void PDFPageContentEditorContentStreamBuilder::writeStyledPath(const QPainterPat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
void PDFPageContentEditorContentStreamBuilder::writeImage(const QImage& image,
|
||||||
const QRectF& rectangle)
|
const QRectF& rectangle)
|
||||||
{
|
{
|
||||||
@ -849,6 +1044,14 @@ void PDFPageContentEditorContentStreamBuilder::writeImage(const QImage& image,
|
|||||||
stream << "Q" << Qt::endl;
|
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
|
bool PDFPageContentEditorContentStreamBuilder::isNeededToWriteCurrentTransformationMatrix() const
|
||||||
{
|
{
|
||||||
return !m_currentState.getCurrentTransformationMatrix().isIdentity();
|
return !m_currentState.getCurrentTransformationMatrix().isIdentity();
|
||||||
|
@ -20,9 +20,31 @@
|
|||||||
|
|
||||||
#include "pdfpagecontenteditorprocessor.h"
|
#include "pdfpagecontenteditorprocessor.h"
|
||||||
|
|
||||||
|
#include <QPaintDevice>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
class PDFPageContentElement;
|
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
|
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentEditorContentStreamBuilder
|
||||||
{
|
{
|
||||||
@ -48,7 +70,15 @@ public:
|
|||||||
bool isStroking,
|
bool isStroking,
|
||||||
bool isFilling);
|
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, const QRectF& rectangle);
|
||||||
|
void writeImage(const QImage& image, QTransform transform, const QRectF& rectangle);
|
||||||
|
|
||||||
|
const PDFPageContentProcessorState& getCurrentState() { return m_currentState; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isNeededToWriteCurrentTransformationMatrix() const;
|
bool isNeededToWriteCurrentTransformationMatrix() const;
|
||||||
|
Reference in New Issue
Block a user