mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-25 07:57:48 +01:00
Signature plugin: Electronic signature
This commit is contained in:
parent
b2a26ada19
commit
c6cfb4cdc8
@ -501,10 +501,11 @@ public:
|
|||||||
{
|
{
|
||||||
None = 0x0000, ///< No flag
|
None = 0x0000, ///< No flag
|
||||||
Reset = 0x0001, ///< Whole document content is changed (for example, new document is being set)
|
Reset = 0x0001, ///< Whole document content is changed (for example, new document is being set)
|
||||||
Annotation = 0x0002, ///< Annotations changed
|
PageContents = 0x0002, ///< Page contents changed (page graphics, not annotations)
|
||||||
FormField = 0x0004, ///< Form field content changed
|
Annotation = 0x0004, ///< Annotations changed
|
||||||
Authorization = 0x0008, ///< Authorization has changed (for example, old document is granted user access, but for new, owner access)
|
FormField = 0x0008, ///< Form field content changed
|
||||||
XFA_Pagination = 0x0010, ///< XFA pagination has been performed (this flag can be set only when Reset flag has been set and not any other flag)
|
Authorization = 0x0010, ///< Authorization has changed (for example, old document is granted user access, but for new, owner access)
|
||||||
|
XFA_Pagination = 0x0020, ///< XFA pagination has been performed (this flag can be set only when Reset flag has been set and not any other flag)
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_FLAGS(ModificationFlags, ModificationFlag)
|
Q_DECLARE_FLAGS(ModificationFlags, ModificationFlag)
|
||||||
@ -550,6 +551,7 @@ public:
|
|||||||
ModificationFlags getFlags() const { return m_flags; }
|
ModificationFlags getFlags() const { return m_flags; }
|
||||||
|
|
||||||
bool hasReset() const { return m_flags.testFlag(Reset); }
|
bool hasReset() const { return m_flags.testFlag(Reset); }
|
||||||
|
bool hasPageContentsChanged() const { return m_flags.testFlag(PageContents); }
|
||||||
bool hasFlag(ModificationFlag flag) const { return m_flags.testFlag(flag); }
|
bool hasFlag(ModificationFlag flag) const { return m_flags.testFlag(flag); }
|
||||||
|
|
||||||
operator PDFDocument*() const { return m_document; }
|
operator PDFDocument*() const { return m_document; }
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include "pdfobjectutils.h"
|
#include "pdfobjectutils.h"
|
||||||
#include "pdfnametreeloader.h"
|
#include "pdfnametreeloader.h"
|
||||||
#include "pdfdbgheap.h"
|
#include "pdfdbgheap.h"
|
||||||
|
#include "pdfparser.h"
|
||||||
|
#include "pdfstreamfilters.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@ -698,6 +700,11 @@ PDFDocument PDFDocumentBuilder::build()
|
|||||||
return PDFDocument(PDFObjectStorage(m_storage), m_version);
|
return PDFDocument(PDFObjectStorage(m_storage), m_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray PDFDocumentBuilder::getDecodedStream(const PDFStream* stream) const
|
||||||
|
{
|
||||||
|
return m_storage.getDecodedStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
std::array<PDFReal, 4> PDFDocumentBuilder::getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const
|
std::array<PDFReal, 4> PDFDocumentBuilder::getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const
|
||||||
{
|
{
|
||||||
return { qAbs(innerRect.left() - boundingRect.left()), qAbs(boundingRect.bottom() - innerRect.bottom()), qAbs(boundingRect.right() - innerRect.right()), qAbs(boundingRect.top() - innerRect.top()) };
|
return { qAbs(innerRect.left() - boundingRect.left()), qAbs(boundingRect.bottom() - innerRect.bottom()), qAbs(boundingRect.right() - innerRect.right()), qAbs(boundingRect.top() - innerRect.top()) };
|
||||||
@ -813,6 +820,10 @@ void PDFPageContentStreamBuilder::end(QPainter* painter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFObject oldResourcesObject = pageDictionary->get("Resources");
|
||||||
|
replaceResources(contentsReference, resourcesReference, oldResourcesObject);
|
||||||
|
m_documentBuilder->mergeTo(resourcesReference, m_documentBuilder->getObject(oldResourcesObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (m_mode)
|
switch (m_mode)
|
||||||
@ -848,6 +859,139 @@ void PDFPageContentStreamBuilder::end(QPainter* painter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFPageContentStreamBuilder::replaceResources(PDFObjectReference contentStreamReference,
|
||||||
|
PDFObjectReference resourcesReference,
|
||||||
|
PDFObject oldResources)
|
||||||
|
{
|
||||||
|
PDFObject newResources = m_documentBuilder->getObjectByReference(resourcesReference);
|
||||||
|
oldResources = m_documentBuilder->getObject(oldResources);
|
||||||
|
PDFObject contentStreamObject = m_documentBuilder->getObjectByReference(contentStreamReference);
|
||||||
|
|
||||||
|
std::vector<std::pair<QByteArray, QByteArray>> renamings;
|
||||||
|
|
||||||
|
if (oldResources.isDictionary() && newResources.isDictionary())
|
||||||
|
{
|
||||||
|
PDFObjectFactory renamedResourcesDictionary;
|
||||||
|
|
||||||
|
renamedResourcesDictionary.beginDictionary();
|
||||||
|
|
||||||
|
const PDFDictionary* oldResourcesDictionary = oldResources.getDictionary();
|
||||||
|
const PDFDictionary* newResourcesDictionary = newResources.getDictionary();
|
||||||
|
const size_t count = newResourcesDictionary->getCount();
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
const PDFInplaceOrMemoryString& key = newResourcesDictionary->getKey(i);
|
||||||
|
QByteArray keyString = key.getString();
|
||||||
|
|
||||||
|
// Process current resource key
|
||||||
|
renamedResourcesDictionary.beginDictionaryItem(keyString);
|
||||||
|
|
||||||
|
if (oldResourcesDictionary->hasKey(keyString))
|
||||||
|
{
|
||||||
|
const PDFObject& newResourcesSubdictionaryObject = m_documentBuilder->getObject(newResourcesDictionary->getValue(i));
|
||||||
|
const PDFObject& oldResourcesSubdictionaryObject = m_documentBuilder->getObject(oldResourcesDictionary->get(keyString));
|
||||||
|
if (oldResourcesSubdictionaryObject.isDictionary() && newResourcesSubdictionaryObject.isDictionary())
|
||||||
|
{
|
||||||
|
// Jakub Melka: Rename items, which are in both dictionaries
|
||||||
|
const PDFDictionary* oldSd = oldResourcesSubdictionaryObject.getDictionary();
|
||||||
|
const PDFDictionary* newSd = newResourcesSubdictionaryObject.getDictionary();
|
||||||
|
|
||||||
|
renamedResourcesDictionary.beginDictionary();
|
||||||
|
|
||||||
|
const size_t subcount = newSd->getCount();
|
||||||
|
for (size_t j = 0; j < subcount; ++j)
|
||||||
|
{
|
||||||
|
const PDFInplaceOrMemoryString& subkey = newSd->getKey(j);
|
||||||
|
QByteArray subkeyString = subkey.getString();
|
||||||
|
if (oldSd->hasKey(subkeyString))
|
||||||
|
{
|
||||||
|
// Jakub Melka: we must rename the item
|
||||||
|
QByteArray newSubkeyString = subkeyString;
|
||||||
|
PDFInteger k = 0;
|
||||||
|
while (oldSd->hasKey(newSubkeyString))
|
||||||
|
{
|
||||||
|
newSubkeyString = subkeyString + "_" + QByteArray::number(++k);
|
||||||
|
}
|
||||||
|
renamings.emplace_back(std::make_pair(subkeyString, newSubkeyString));
|
||||||
|
subkeyString = newSubkeyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
renamedResourcesDictionary.beginDictionaryItem(subkeyString);
|
||||||
|
renamedResourcesDictionary << newSd->getValue(j);
|
||||||
|
renamedResourcesDictionary.endDictionaryItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
renamedResourcesDictionary.endDictionary();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renamedResourcesDictionary << newResourcesDictionary->getValue(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renamedResourcesDictionary << newResourcesDictionary->getValue(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
renamedResourcesDictionary.endDictionaryItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
renamedResourcesDictionary.endDictionary();
|
||||||
|
m_documentBuilder->setObject(resourcesReference, renamedResourcesDictionary.takeObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentStreamObject.isStream())
|
||||||
|
{
|
||||||
|
QByteArray decodedStream = m_documentBuilder->getDecodedStream(contentStreamObject.getStream());
|
||||||
|
decodedStream = decodedStream.trimmed();
|
||||||
|
|
||||||
|
// Append save/restore state
|
||||||
|
if (!decodedStream.startsWith("q "))
|
||||||
|
{
|
||||||
|
decodedStream.prepend("q ");
|
||||||
|
decodedStream.append(" Q");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace all occurences in the stream
|
||||||
|
for (const auto& item : renamings)
|
||||||
|
{
|
||||||
|
QByteArray oldName = item.first;
|
||||||
|
QByteArray newName = item.second;
|
||||||
|
|
||||||
|
oldName.prepend('/');
|
||||||
|
newName.prepend('/');
|
||||||
|
|
||||||
|
int currentPos = 0;
|
||||||
|
int oldNameIndex = decodedStream.indexOf(oldName, currentPos);
|
||||||
|
while (oldNameIndex != -1)
|
||||||
|
{
|
||||||
|
const int whiteSpacePosition = oldNameIndex + oldName.size();
|
||||||
|
if (whiteSpacePosition < decodedStream.size() && !PDFLexicalAnalyzer::isWhitespace(decodedStream[whiteSpacePosition]))
|
||||||
|
{
|
||||||
|
currentPos = oldNameIndex + 1;
|
||||||
|
oldNameIndex = decodedStream.indexOf(oldName, currentPos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedStream.replace(oldNameIndex, oldName.length(), newName);
|
||||||
|
currentPos = oldNameIndex + 1;
|
||||||
|
oldNameIndex = decodedStream.indexOf(oldName, currentPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFArray array;
|
||||||
|
array.appendItem(PDFObject::createName("FlateDecode"));
|
||||||
|
|
||||||
|
// Compress the content stream
|
||||||
|
QByteArray compressedData = PDFFlateDecodeFilter::compress(decodedStream);
|
||||||
|
PDFDictionary updatedDictionary = *contentStreamObject.getStream()->getDictionary();
|
||||||
|
updatedDictionary.setEntry(PDFInplaceOrMemoryString("Length"), PDFObject::createInteger(compressedData.size()));
|
||||||
|
updatedDictionary.setEntry(PDFInplaceOrMemoryString("Filters"), PDFObject::createArray(std::make_shared<PDFArray>(qMove(array))));
|
||||||
|
PDFObject newContentStream = PDFObject::createStream(std::make_shared<PDFStream>(qMove(updatedDictionary), qMove(compressedData)));
|
||||||
|
m_documentBuilder->setObject(contentStreamReference, std::move(newContentStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PDFDocumentBuilder::updateAnnotationAppearanceStreams(PDFObjectReference annotationReference)
|
void PDFDocumentBuilder::updateAnnotationAppearanceStreams(PDFObjectReference annotationReference)
|
||||||
{
|
{
|
||||||
PDFAnnotationPtr annotation = PDFAnnotation::parse(&m_storage, annotationReference);
|
PDFAnnotationPtr annotation = PDFAnnotation::parse(&m_storage, annotationReference);
|
||||||
|
@ -295,6 +295,10 @@ public:
|
|||||||
void end(QPainter* painter);
|
void end(QPainter* painter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void replaceResources(PDFObjectReference contentStreamReference,
|
||||||
|
PDFObjectReference resourcesReference,
|
||||||
|
PDFObject oldResources);
|
||||||
|
|
||||||
PDFDocumentBuilder* m_documentBuilder;
|
PDFDocumentBuilder* m_documentBuilder;
|
||||||
PDFContentStreamBuilder* m_contentStreamBuilder;
|
PDFContentStreamBuilder* m_contentStreamBuilder;
|
||||||
PDFObjectReference m_pageReference;
|
PDFObjectReference m_pageReference;
|
||||||
@ -341,6 +345,11 @@ public:
|
|||||||
/// is returned (no exception is thrown).
|
/// is returned (no exception is thrown).
|
||||||
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
|
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
|
||||||
|
|
||||||
|
/// Returns the decoded stream. If stream data cannot be decoded,
|
||||||
|
/// then empty byte array is returned.
|
||||||
|
/// \param stream Stream to be decoded
|
||||||
|
QByteArray getDecodedStream(const PDFStream* stream) const;
|
||||||
|
|
||||||
/// Returns annotation bounding rectangle
|
/// Returns annotation bounding rectangle
|
||||||
std::array<PDFReal, 4> getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const;
|
std::array<PDFReal, 4> getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const;
|
||||||
|
|
||||||
@ -1526,6 +1535,7 @@ public:
|
|||||||
PDFModifiedDocument::ModificationFlags getFlags() const { return m_modificationFlags; }
|
PDFModifiedDocument::ModificationFlags getFlags() const { return m_modificationFlags; }
|
||||||
|
|
||||||
void markReset() { m_modificationFlags.setFlag(PDFModifiedDocument::Reset); }
|
void markReset() { m_modificationFlags.setFlag(PDFModifiedDocument::Reset); }
|
||||||
|
void markPageContentsChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::PageContents); }
|
||||||
void markAnnotationsChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::Annotation); }
|
void markAnnotationsChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::Annotation); }
|
||||||
void markFormFieldChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::FormField); }
|
void markFormFieldChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::FormField); }
|
||||||
void markXFAPagination() { m_modificationFlags.setFlag(PDFModifiedDocument::XFA_Pagination); }
|
void markXFAPagination() { m_modificationFlags.setFlag(PDFModifiedDocument::XFA_Pagination); }
|
||||||
|
@ -192,7 +192,10 @@ PDFOperationResult PDFDocumentWriter::write(const QString& fileName, const PDFDo
|
|||||||
PDFOperationResult result = write(&file, document);
|
PDFOperationResult result = write(&file, document);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
file.commit();
|
if (!file.commit())
|
||||||
|
{
|
||||||
|
return tr("File '%1' can't be opened for writing. %2").arg(fileName, file.errorString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -488,8 +488,8 @@ void PDFDrawWidgetProxy::setDocument(const PDFModifiedDocument& document)
|
|||||||
if (getDocument() != document)
|
if (getDocument() != document)
|
||||||
{
|
{
|
||||||
m_cacheClearTimer->stop();
|
m_cacheClearTimer->stop();
|
||||||
m_compiler->stop(document.hasReset());
|
m_compiler->stop(document.hasReset() || document.hasPageContentsChanged());
|
||||||
m_textLayoutCompiler->stop(document.hasReset());
|
m_textLayoutCompiler->stop(document.hasReset() || document.hasPageContentsChanged());
|
||||||
m_controller->setDocument(document);
|
m_controller->setDocument(document);
|
||||||
|
|
||||||
if (PDFOptionalContentActivity* optionalContentActivity = document.getOptionalContentActivity())
|
if (PDFOptionalContentActivity* optionalContentActivity = document.getOptionalContentActivity())
|
||||||
|
@ -1836,7 +1836,7 @@ void PDFFontCache::setDocument(const PDFModifiedDocument& document)
|
|||||||
|
|
||||||
// Jakub Melka: If document has not reset flag, then fonts of the
|
// Jakub Melka: If document has not reset flag, then fonts of the
|
||||||
// document remains the same. So it is not needed to clear font cache.
|
// document remains the same. So it is not needed to clear font cache.
|
||||||
if (document.hasReset())
|
if (document.hasReset() || document.hasPageContentsChanged())
|
||||||
{
|
{
|
||||||
m_fontCache.clear();
|
m_fontCache.clear();
|
||||||
m_realizedFontCache.clear();
|
m_realizedFontCache.clear();
|
||||||
|
@ -738,7 +738,7 @@ void PDFThumbnailsItemModel::setDocument(const PDFModifiedDocument& document)
|
|||||||
{
|
{
|
||||||
if (m_document != document)
|
if (m_document != document)
|
||||||
{
|
{
|
||||||
if (document.hasReset() || document.hasFlag(PDFModifiedDocument::Annotation))
|
if (document.hasReset() || document.hasPageContentsChanged() || document.hasFlag(PDFModifiedDocument::Annotation))
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_thumbnailCache.clear();
|
m_thumbnailCache.clear();
|
||||||
|
@ -2363,12 +2363,18 @@ void PDFPageContentElementTextBox::drawPage(QPainter* painter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
QRectF rect = getRectangle();
|
QRectF rect = getRectangle();
|
||||||
|
QFont font = getFont();
|
||||||
|
font.setHintingPreference(QFont::PreferNoHinting);
|
||||||
|
if (font.pointSizeF() > 0.0)
|
||||||
|
{
|
||||||
|
font.setPixelSize(qCeil(font.pointSizeF()));
|
||||||
|
}
|
||||||
|
|
||||||
PDFPainterStateGuard guard(painter);
|
PDFPainterStateGuard guard(painter);
|
||||||
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
||||||
painter->setPen(getPen());
|
painter->setPen(getPen());
|
||||||
painter->setBrush(getBrush());
|
painter->setBrush(getBrush());
|
||||||
painter->setFont(getFont());
|
painter->setFont(font);
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
painter->setClipRect(rect, Qt::IntersectClip);
|
painter->setClipRect(rect, Qt::IntersectClip);
|
||||||
painter->translate(rect.center());
|
painter->translate(rect.center());
|
||||||
|
@ -383,10 +383,9 @@ QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data,
|
|||||||
return predictor.apply(uncompress(data));
|
return predictor.apply(uncompress(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray PDFFlateDecodeFilter::recompress(const QByteArray& data)
|
QByteArray PDFFlateDecodeFilter::compress(const QByteArray& decompressedData)
|
||||||
{
|
{
|
||||||
QByteArray result;
|
QByteArray result;
|
||||||
QByteArray decompressedData = uncompress(data);
|
|
||||||
|
|
||||||
z_stream stream = { };
|
z_stream stream = { };
|
||||||
stream.next_in = const_cast<Bytef*>(convertByteArrayToUcharPtr(decompressedData));
|
stream.next_in = const_cast<Bytef*>(convertByteArrayToUcharPtr(decompressedData));
|
||||||
@ -431,13 +430,19 @@ QByteArray PDFFlateDecodeFilter::recompress(const QByteArray& data)
|
|||||||
errorMessage = PDFTranslationContext::tr("zlib code: %1").arg(error);
|
errorMessage = PDFTranslationContext::tr("zlib code: %1").arg(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw PDFException(PDFTranslationContext::tr("Error decompressing by flate method: %1").arg(errorMessage));
|
throw PDFException(PDFTranslationContext::tr("Error compressing by flate method: %1").arg(errorMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray PDFFlateDecodeFilter::recompress(const QByteArray& data)
|
||||||
|
{
|
||||||
|
QByteArray decompressedData = uncompress(data);
|
||||||
|
return compress(decompressedData);
|
||||||
|
}
|
||||||
|
|
||||||
PDFInteger PDFFlateDecodeFilter::getStreamDataLength(const QByteArray& data, PDFInteger offset) const
|
PDFInteger PDFFlateDecodeFilter::getStreamDataLength(const QByteArray& data, PDFInteger offset) const
|
||||||
{
|
{
|
||||||
if (offset < 0 || offset >= data.size())
|
if (offset < 0 || offset >= data.size())
|
||||||
|
@ -215,6 +215,10 @@ public:
|
|||||||
|
|
||||||
/// Recompresses data. So, first, data are decompressed, and then
|
/// Recompresses data. So, first, data are decompressed, and then
|
||||||
/// recompressed again with maximal compress ratio possible.
|
/// recompressed again with maximal compress ratio possible.
|
||||||
|
/// \param data Uncompressed data to be compressed
|
||||||
|
static QByteArray compress(const QByteArray& decompressedData);
|
||||||
|
|
||||||
|
/// Compress data with maximal compress ratio possible.
|
||||||
/// \param data Compressed data to be recompressed
|
/// \param data Compressed data to be recompressed
|
||||||
static QByteArray recompress(const QByteArray& data);
|
static QByteArray recompress(const QByteArray& data);
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ void PDFAdvancedFindWidget::setDocument(const pdf::PDFModifiedDocument& document
|
|||||||
|
|
||||||
// If document is not being reset, then page text should remain the same,
|
// If document is not being reset, then page text should remain the same,
|
||||||
// so, there is no need to clear the results.
|
// so, there is no need to clear the results.
|
||||||
if (document.hasReset())
|
if (document.hasReset() || document.hasPageContentsChanged())
|
||||||
{
|
{
|
||||||
m_findResults.clear();
|
m_findResults.clear();
|
||||||
updateUI();
|
updateUI();
|
||||||
|
@ -292,7 +292,9 @@ void SignaturePlugin::onSignElectronically()
|
|||||||
pdf::PDFTextLayoutGetter nullGetter(nullptr, pageIndex);
|
pdf::PDFTextLayoutGetter nullGetter(nullptr, pageIndex);
|
||||||
m_scene.drawPage(painter, pageIndex, nullptr, nullGetter, QMatrix(), errors);
|
m_scene.drawPage(painter, pageIndex, nullptr, nullGetter, QMatrix(), errors);
|
||||||
pageContentStreamBuilder.end(painter);
|
pageContentStreamBuilder.end(painter);
|
||||||
|
modifier.markPageContentsChanged();
|
||||||
}
|
}
|
||||||
|
m_scene.clear();
|
||||||
|
|
||||||
if (modifier.finalize())
|
if (modifier.finalize())
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user