mirror of https://github.com/JakubMelka/PDF4QT.git
Editor plugin: Make content stream
This commit is contained in:
parent
a40faf855d
commit
eeadf328b2
|
@ -18,6 +18,7 @@
|
||||||
#include "pdfpagecontenteditorprocessor.h"
|
#include "pdfpagecontenteditorprocessor.h"
|
||||||
|
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
|
@ -67,6 +68,7 @@ void PDFPageContentEditorProcessor::performInterceptInstruction(Operator current
|
||||||
if (m_contentElementText && !m_contentElementText->isEmpty())
|
if (m_contentElementText && !m_contentElementText->isEmpty())
|
||||||
{
|
{
|
||||||
m_contentElementText->setTextPath(std::move(m_textPath));
|
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_content.addContentElement(std::move(m_contentElementText));
|
||||||
}
|
}
|
||||||
m_contentElementText.reset();
|
m_contentElementText.reset();
|
||||||
|
@ -608,10 +610,12 @@ PDFEditedPageContentElementText::PDFEditedPageContentElementText(PDFPageContentP
|
||||||
PDFEditedPageContentElementText::PDFEditedPageContentElementText(PDFPageContentProcessorState state,
|
PDFEditedPageContentElementText::PDFEditedPageContentElementText(PDFPageContentProcessorState state,
|
||||||
std::vector<Item> items,
|
std::vector<Item> items,
|
||||||
QPainterPath textPath,
|
QPainterPath textPath,
|
||||||
QTransform transform) :
|
QTransform transform,
|
||||||
|
QString itemsAsText) :
|
||||||
PDFEditedPageContentElement(state, transform),
|
PDFEditedPageContentElement(state, transform),
|
||||||
m_items(std::move(items)),
|
m_items(std::move(items)),
|
||||||
m_textPath(std::move(textPath))
|
m_textPath(std::move(textPath)),
|
||||||
|
m_itemsAsText(itemsAsText)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -623,7 +627,7 @@ PDFEditedPageContentElement::Type PDFEditedPageContentElementText::getType() con
|
||||||
|
|
||||||
PDFEditedPageContentElementText* PDFEditedPageContentElementText::clone() const
|
PDFEditedPageContentElementText* PDFEditedPageContentElementText::clone() const
|
||||||
{
|
{
|
||||||
return new PDFEditedPageContentElementText(getState(), getItems(), getTextPath(), getTransform());
|
return new PDFEditedPageContentElementText(getState(), getItems(), getTextPath(), getTransform(), getItemsAsText());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFEditedPageContentElementText::addItem(Item item)
|
void PDFEditedPageContentElementText::addItem(Item item)
|
||||||
|
@ -656,14 +660,15 @@ void PDFEditedPageContentElementText::setTextPath(QPainterPath newTextPath)
|
||||||
m_textPath = newTextPath;
|
m_textPath = newTextPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PDFEditedPageContentElementText::getItemsAsText() const
|
QString PDFEditedPageContentElementText::createItemsAsText(const PDFPageContentProcessorState& initialState,
|
||||||
|
const std::vector<Item>& items)
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
|
|
||||||
PDFPageContentProcessorState state = getState();
|
PDFPageContentProcessorState state = initialState;
|
||||||
state.setStateFlags(PDFPageContentProcessorState::StateFlags());
|
state.setStateFlags(PDFPageContentProcessorState::StateFlags());
|
||||||
|
|
||||||
for (const Item& item : getItems())
|
for (const Item& item : items)
|
||||||
{
|
{
|
||||||
if (item.isText)
|
if (item.isText)
|
||||||
{
|
{
|
||||||
|
@ -760,6 +765,16 @@ QString PDFEditedPageContentElementText::getItemsAsText() const
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PDFEditedPageContentElementText::getItemsAsText() const
|
||||||
|
{
|
||||||
|
return m_itemsAsText;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFEditedPageContentElementText::setItemsAsText(const QString& newItemsAsText)
|
||||||
|
{
|
||||||
|
m_itemsAsText = newItemsAsText;
|
||||||
|
}
|
||||||
|
|
||||||
void PDFPageContentEditorContentStreamBuilder::writeStateDifference(const PDFPageContentProcessorState& state)
|
void PDFPageContentEditorContentStreamBuilder::writeStateDifference(const PDFPageContentProcessorState& state)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -771,12 +786,14 @@ void PDFPageContentEditorContentStreamBuilder::writeElement(const PDFEditedPageC
|
||||||
state.setCurrentTransformationMatrix(element->getTransform());
|
state.setCurrentTransformationMatrix(element->getTransform());
|
||||||
writeStateDifference(state);
|
writeStateDifference(state);
|
||||||
|
|
||||||
QDataStream stream(&m_outputContent, QDataStream::WriteOnly);
|
QTextStream stream(&m_outputContent, QDataStream::WriteOnly);
|
||||||
|
|
||||||
if (const PDFEditedPageContentElementImage* imageElement = element->asImage())
|
if (const PDFEditedPageContentElementImage* imageElement = element->asImage())
|
||||||
{
|
{
|
||||||
QImage image = imageElement->getImage();
|
QImage image = imageElement->getImage();
|
||||||
PDFObject imageObject = imageElement->getImageObject();
|
PDFObject imageObject = imageElement->getImageObject();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const PDFEditedPageContentElementPath* pathElement = element->asPath())
|
if (const PDFEditedPageContentElementPath* pathElement = element->asPath())
|
||||||
|
@ -787,10 +804,20 @@ void PDFPageContentEditorContentStreamBuilder::writeElement(const PDFEditedPageC
|
||||||
writePainterPath(stream, pathElement->getPath(), isStroking, isFilling);
|
writePainterPath(stream, pathElement->getPath(), isStroking, isFilling);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const PDFEditedPageContentElementText* textElement = element->asText())
|
||||||
|
{
|
||||||
|
QString text = textElement->getItemsAsText();
|
||||||
|
|
||||||
|
if (!text.isEmpty())
|
||||||
|
{
|
||||||
|
writeText(stream, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stream << Qt::endl;
|
stream << Qt::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPageContentEditorContentStreamBuilder::writePainterPath(QDataStream& stream,
|
void PDFPageContentEditorContentStreamBuilder::writePainterPath(QTextStream& stream,
|
||||||
const QPainterPath& path,
|
const QPainterPath& path,
|
||||||
bool isStroking,
|
bool isStroking,
|
||||||
bool isFilling)
|
bool isFilling)
|
||||||
|
@ -858,4 +885,217 @@ void PDFPageContentEditorContentStreamBuilder::writePainterPath(QDataStream& str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFPageContentEditorContentStreamBuilder::writeText(QTextStream& stream, const QString& text)
|
||||||
|
{
|
||||||
|
stream << "q BT" << Qt::endl;
|
||||||
|
|
||||||
|
QXmlStreamReader reader(text);
|
||||||
|
|
||||||
|
auto isCommand = [&reader](const char* tag) -> bool
|
||||||
|
{
|
||||||
|
QString tagString = reader.name().toString();
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
return tagString == "tr" && attributes.size() == 1 && attributes.hasAttribute("v");
|
||||||
|
};
|
||||||
|
|
||||||
|
while (!reader.atEnd() && !reader.hasError())
|
||||||
|
{
|
||||||
|
reader.readNext();
|
||||||
|
|
||||||
|
if (reader.isStartElement())
|
||||||
|
{
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
|
||||||
|
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;
|
||||||
|
QString v1 = attributes.value("font").toString();
|
||||||
|
PDFReal v2 = attributes.value("size").toDouble(&ok);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
addError(PDFTranslationContext::tr("Cannot convert text '%1' to number.").arg(attributes.value("size").toString()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.isCharacters())
|
||||||
|
{
|
||||||
|
QString characters = reader.text().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << "ET Q" << Qt::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFPageContentEditorContentStreamBuilder::addError(const QString& error)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
|
@ -142,7 +142,8 @@ public:
|
||||||
PDFEditedPageContentElementText(PDFPageContentProcessorState state,
|
PDFEditedPageContentElementText(PDFPageContentProcessorState state,
|
||||||
std::vector<Item> items,
|
std::vector<Item> items,
|
||||||
QPainterPath textPath,
|
QPainterPath textPath,
|
||||||
QTransform transform);
|
QTransform transform,
|
||||||
|
QString itemsAsText);
|
||||||
virtual ~PDFEditedPageContentElementText() = default;
|
virtual ~PDFEditedPageContentElementText() = default;
|
||||||
|
|
||||||
virtual Type getType() const override;
|
virtual Type getType() const override;
|
||||||
|
@ -160,11 +161,16 @@ public:
|
||||||
QPainterPath getTextPath() const;
|
QPainterPath getTextPath() const;
|
||||||
void setTextPath(QPainterPath newTextPath);
|
void setTextPath(QPainterPath newTextPath);
|
||||||
|
|
||||||
|
static QString createItemsAsText(const PDFPageContentProcessorState& initialState,
|
||||||
|
const std::vector<Item>& items);
|
||||||
|
|
||||||
QString getItemsAsText() const;
|
QString getItemsAsText() const;
|
||||||
|
void setItemsAsText(const QString& newItemsAsText);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Item> m_items;
|
std::vector<Item> m_items;
|
||||||
QPainterPath m_textPath;
|
QPainterPath m_textPath;
|
||||||
|
QString m_itemsAsText;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContent
|
class PDF4QTLIBCORESHARED_EXPORT PDFEditedPageContent
|
||||||
|
@ -213,10 +219,13 @@ public:
|
||||||
const QByteArray& getOutputContent() const;
|
const QByteArray& getOutputContent() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void writePainterPath(QDataStream& stream,
|
void writePainterPath(QTextStream& stream,
|
||||||
const QPainterPath& path,
|
const QPainterPath& path,
|
||||||
bool isStroking,
|
bool isStroking,
|
||||||
bool isFilling);
|
bool isFilling);
|
||||||
|
void writeText(QTextStream& stream, const QString& text);
|
||||||
|
|
||||||
|
void addError(const QString& error);
|
||||||
|
|
||||||
PDFDictionary m_fontDictionary;
|
PDFDictionary m_fontDictionary;
|
||||||
PDFDictionary m_xobjectDictionary;
|
PDFDictionary m_xobjectDictionary;
|
||||||
|
|
|
@ -88,6 +88,11 @@ void PDFPageContentEditorEditedItemSettings::saveToElement(PDFPageContentElement
|
||||||
imageElement->setImageObject(PDFObject());
|
imageElement->setImageObject(PDFObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PDFEditedPageContentElementText* textElement = editedElement->getElement()->asText())
|
||||||
|
{
|
||||||
|
textElement->setItemsAsText(ui->plainTextEdit->toPlainText());
|
||||||
|
}
|
||||||
|
|
||||||
PDFTransformationDecomposition decomposedTransformation;
|
PDFTransformationDecomposition decomposedTransformation;
|
||||||
decomposedTransformation.rotationAngle = ui->rotationAngleEdit->value();
|
decomposedTransformation.rotationAngle = ui->rotationAngleEdit->value();
|
||||||
decomposedTransformation.shearFactor = ui->shearFactorEdit->value();
|
decomposedTransformation.shearFactor = ui->shearFactorEdit->value();
|
||||||
|
|
Loading…
Reference in New Issue